本文将用最通俗的方式,帮你搞懂智能合约的两种常见对接方式,横向对比Go、Java、Rust、Python、JavaScript等语言的实现差异,并带你深入思考:DApp的本质是什么?智能合约在其中究竟扮演什么角色?
这篇文章将带你深入理解智能合约的两种主流对接方式,了解Go、Java、Rust、Python、JavaScript语言在对接智能合约时的不同实现路径,更重要的是,它将引导你思考:为什么有这么多语言可以对接智能合约?背后反映了什么样的开发生态?它们如何共同构建出一个完整的DApp世界?
智能合约是一种自动执行、不可篡改的计算机协议,运行在区块链上。它们由一组预定义的规则和条件组成,一旦这些条件被满足,合约会自动执行相关的操作,无需中介或人工干预。
关键特点:
对接前
怎么对接?通常使用合约的ABI 和 合约地址 来创建合约实例,通过调用实例的方法来调用合约中各个函数或状态变量等;
怎么创建合约实例?有两种方式:
对接端的编程语言包括:Go/Java/Rust/Python/JS,大部分都包含上面两种绑定方式,但静态绑定的特定工具各有不同。
语言 | 工具库 | 功能说明 |
---|---|---|
Go | go-ethereum(Geth) | 使用 abigen 命令将 ABI 文件生成绑定代码,并实例化调用合约 |
Java | Web3j | 使用web3j generate 命令将 ABI 文件生成绑定代码,并实例化调用合约 |
Rust | ethers-rs | 使用 ethers-rs 的 abigen 命令将 ABI 文件生成绑定代码,并实例化调用合约 |
Python | web3.py | 直接加载 ABI 文件并创建合约实例 调用合约 |
JS/TS | web3.js / ethers.js | 直接加载 ABI 文件并创建合约实例 调用合约 |
以对接Counter合约为例,合约中有变量number,函数setNumber(uint)和increment(),ABI文件名=Counter.abi.json。
下面介绍各种语言创建合约实例的伪代码:
client, err := ethclient.Dial(RPC_URL) // 连接客户端
address := common.HexToAddress("0xYourContractAddress") // 合约地址
abiData, err := ioutil.ReadFile("Counter.abi.json") // 读取 ABI 文件
parsedABI, err := abi.JSON(strings.NewReader(string(abiData)))
contract := bind.NewBoundContract(address, parsedABI, client, client, client)// 使用 ABI 创建合约实例
tx, err := contract.Transact(nil, "setNumber", big.NewInt(42)) // 调用 setNumber 函数
使用 abigen
工具生成绑定代码 counter.go
举例:abigen --abi=Counter.abi.json --pkg=counter --out=Counter.go
import "your_project/counter" // 导入绑定代码Counter.go
client, err := ethclient.Dial(RPC_URL) // 连接客户端
address := common.HexToAddress("0xYourContractAddress") // 合约地址
contract, err := counter.NewCounter(address, client) // 使用 NewCounter 创建合约实例
tx, err := contract.SetNumber(big.NewInt(42)) // 调用 setNumber 函数
Web3j web3 = Web3j.build(new HttpService(RPC_URL)); // 初始化 Web3j
String abi = new String(Files.readAllBytes(Paths.get("Counter.abi.json"))); // 从 ABI 文件加载合约
String contractAddress = "0xYourContractAddress"; // 合约地址
Counter contract = Counter.load(contractAddress, web3, Credentials.create("PRIVATE_KEY"), new DefaultGasProvider()); // 创建合约实例
contract.setNumber(new BigInteger("42")).send(); // 调用 setNumber 函数
使用 web3j
工具将 ABI 文件生成绑定代码 Counter 类。
举例:web3j generate solidity -a=Counter.abi.json -o=src/main/java -p=com.example.contract
import com.example.contract.Counter; // 导入Counter类
Web3j web3 = Web3j.build(new HttpService(RPC_URL)); // 初始化 Web3j
Counter contract = Counter.load("0xYourContractAddress", web3, credentials, new DefaultGasProvider()); // 使用Counter类来与合约交互
contract.setNumber(BigInteger.valueOf(42)).send(); // 调用 setNumber 方法
const ABI: &str = include_str!("../Counter.abi.json"); // 将 ABI 文件包含进来
const CONTRACT_ADDRESS: &str = "0xYourContractAddress"; // 合约地址
let provider = Provider::<Http>::try_from(RPC_URL)?; // 连接客户端
let contract = Contract::new(CONTRACT_ADDRESS.parse(), ABI, provider); // 创建合约实例
let tx = contract.method::<_, H256>("setNumber", 42)?.send().await?; // 调用 setNumber 函数
使用 ethers-rs
的 abigen
工具生成绑定代码contract.rs
举例:ethers abigen --abi Counter.abi --out src/contract --lang rust
use counter::Counter; // 导入绑定代码counter.rs
let provider = Provider::<Http>::try_from(RPC_URL)?; // 连接客户端
const CONTRACT_ADDRESS: &str = "0xYourContractAddress"; // 合约地址
let contract: Counter<Provider<Http>> = Counter::new(CONTRACT_ADDRESS.parse()?, provider); // 创建合约实例
let tx = contract.set_number(42).send().await?; // 调用 setNumber 函数
在 Python 中,静态绑定和动态加载的差异并不明显, 都通过读取 ABI 文件并创建合约实例来进行交互;
w3 = Web3(Web3.HTTPProvider(RPC_URL)) # 连接客户端
contract_address = "0xYourContractAddress" # 合约地址
with open('Counter.abi.json') as f: # 加载ABI文件
abi = json.load(f)
contract = w3.eth.contract(address=contract_address, abi=abi) · # 创建合约实例
tx = contract.functions.setNumber(42).transact({'from': w3.eth.accounts[0]}) # 调用 setNumber 函数
JavaScript 中的静态绑定和动态加载的方式是相似的,都是读取 ABI 文件并生成合约实例;
JavaScript 交互智能合约的库有 Web3.js 或 ethers.js ,下面以Web3.js为例
const Web3 = require('web3'); // 导入 Web3.js 库
const web3 = new Web3(RPC_URL); // 连接客户端,web3是小写
const abi = JSON.parse(fs.readFileSync('Counter.abi.json', 'utf-8')); // 读取 ABI 文件
const contract = new web3.eth.Contract(abi, '0xYourContractAddress'); // 创建合约实例
contract.methods.setNumber(42).send({ from: userAddress }); // 调用 setNumber 方法
首要概念: 区块链(如以太坊) + 智能合约 + 前端界面 = 去中心化应用(DApp)
这是构建一个去中心化应用的核心。区块链提供去中心化的数据库,智能合约负责处理区块链上的业务逻辑,前端界面提供用户交互界面。
当业务逻辑变得复杂时,单靠智能合约可能无法满足需求。于是,后端服务(如Go/Java/Rust/Python)就成了桥梁,负责处理复杂的逻辑、数据存储以及优化与区块链的交互。
概念演变:区块链 + 智能合约 + 后端服务(Go/Java/Rust/Python) + 前端界面(JS) = 去中心化应用(DApp)
结论: 合约对接语言之所以有多种,主要是因为不同语言有不同的用途和优势。
首要概念: 区块链(如以太坊) + 智能合约 + 前端界面 = 去中心化应用(DApp)
概念演变:区块链 + 智能合约 + 后端服务(Go/Java/Rust/Python) + 前端界面(JS) = 去中心化应用(DApp)
在上面的概念中,为什么需要智能合约,为什么不直接和区块链这个去中心化数据库交互呢?
结论:区块链本身是一个去中心化的数据库,用于记录数据和交易,但它并不具备处理复杂逻辑的能力。智能合约是区块链上的程序,它能够定义和自动执行规则、条件和操作,保证这些操作在没有中介的情况下公平、公正地执行。
举例说明:
假设你想创建一个去中心化的借贷平台,用户可以存入资金并赚取利息,其他用户可以借款,并支付利息;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!