主页 > 如何退出imtoken > 手写一个以太坊智能合约

手写一个以太坊智能合约

如何退出imtoken 2023-03-02 06:13:46

\> geth --rpc --dev --mine --minerthreads 1 --unlock 0 console 2>>geth.log
 
  • 1

这将在 :8545 上启动 HTTP RPC 接口。

注意:geth 支持 CORS,请参阅 --rpccorsdomain 标志以获取更多信息。

我们可以通过使用 curl 检索 coinbase 地址和余额来证明接口正在工作。 请注意,这些示例中的数据在您的本地节点上会有所不同。 如果您想试验这些参数以太坊合约教程,请根据需要替换所需的参数。

\> curl --data '{"jsonrpc":"2.0","method":"eth_coinbase", "id":1}' localhost:8545
{"id":1,"jsonrpc":"2.0","result":["0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"]}
> curl --data '{"jsonrpc":"2.0","method":"eth_getBalance", "params": ["0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"], "id":2}' localhost:8545
{"id":2,"jsonrpc":"2.0","result":"0x1639e49bba16280000"}
 
  • 1
  • 2
  • 3
  • 4

还记得我说过数字是十六进制编码的吗? 在这种情况下,余额以 Wei 中的十六进制字符串形式返回。 如果你希望余额以数字以太为单位,你可以从控制台使用 web3,示例如下:

\> web3.fromWei("0x1639e49bba16280000", "ether")
"410"
 
  • 1
  • 2

现在我们在私有开发链上有了一些以太币,我们可以部署合约了。 第一步是验证 solidity 编译器是否可用。 您可以使用 eth_getCompilers RPC 方法来检索可用的编译器。 示例如下:

\> curl --data '{"jsonrpc":"2.0","method": "eth_getCompilers", "id": 3}' localhost:8545
{"id":3,"jsonrpc":"2.0","result":["Solidity"]}
 
  • 1
  • 2

我们可以看到solidity编译器是可用的。

下一步是将 Multiply7 合约编译成可以发送到以太坊虚拟机的字节码以太坊合约教程,例如:

\> curl --data '{"jsonrpc":"2.0","method": "eth_compileSolidity", "params": ["contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input
{"id":4,"jsonrpc":"2.0","result":{"Multiply7":{"code":"0x6060604052605f8060106000396000f360606040
 
  • 1
  • 2

现在我们已经编译了代码,我们需要决定部署它需要花费多少 gas。 RPC 接口有 eth_estimateGas 方法,它会给我们一个估计的数量,如下所示:

\> curl --data '{"jsonrpc":"2.0","method": "eth_estimateGas", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "data": "0x6060604052605f8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b60586004356007810260609081526000907f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da90602090a15060070290565b5060206060f3"}], "id": 5}' localhost:8545
{"id":5,"jsonrpc":"2.0","result":"0xb8a9"}
 
  • 1
  • 2

最后部署合约。

\> curl --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "gas": "0xb8a9", "data": "0x6060604052605f8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b60586004356007810260609081526000907f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da90602090a15060070290565b5060206060f3"}], "id": 6}' localhost:8545
{"id":6,"jsonrpc":"2.0","result":"0x3a90b5face52c4c5f30d507ccf51b0209ca628c6824d0532bcd6283df7c08
 
  • 1
  • 2

交易被节点接受,并返回交易哈希表。 我们可以使用这个哈希表来跟踪交易。

下一步是决定在哪里部署合约。 每个执行的交易都会创建一个收据。 这个rec​​eption包含交易的各种信息,比如交易包含在哪个区块中,以太坊虚拟机消耗了多少gas。 如果交易创建了合约,它还将包含合约地址。 我们可以使用eth_getTransactionReceipt RPC方法来获取回执,示例如下:

\> curl --data '{"jsonrpc":"2.0","method": "eth_getTransactionReceipt", "params": ["0x3a90b5face52c4c5f30d507ccf51b0209ca628c6824d0532bcd6283df7c08a7c"], "id": 7}' localhost:8545
{"id":7,"jsonrpc":"2.0","result":{"transactionHash":"0x3a90b5face52c4c5f30d507ccf51b0209ca628c682
 
  • 1
  • 2

如您所见,合约创建于 0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d。 如果你得到零而不是接收,它还没有被包含在块中。 此时,请检查您的矿机是否正在运行,然后重试。

与智能合约交互

现在合约已部署,我们可以与其进行交互。 有两种交互方式,发送交易或调用。 在本节的示例中,交易将被发送到合约的 multiply 方法。

在我们的示例中,需要指定 from、to 和 data 参数。 from是我们账户的公网地址,to是合约地址,Data参数稍微复杂一点,它包括指定调用哪个方法和哪个参数的payload。 这就是 ABI 发挥作用的地方,ABI 指定如何为以太坊虚拟机指定和编码数据。

有效负载字节是函数选择器,指定要调用的方法。 它采用 Keccak 哈希的前 4 个字节,覆盖函数名称参数类型,并将其编码为十六进制。 multiply 函数接受一个参数。 举例如下:

\> web3.sha3("multiply(uint256)").substring(0, 8)
"c6888fa1"
 
  • 1
  • 2

下一步是对参数进行编码。 我们只有一个 unit256,假设提供的值为 6。 ABI有一章规定了uint字节的编码方法,如下:

int: enc(X) is the big-endian two’s complement encoding of X, padded on the higher-oder (left) side with 0xff for negative X and with zero 字节s for positive X such that the length is a multiple of 32 bytes.
 
  • 1

它将编码为

00000000000000000000000000000000000000000000000000000000000000006。

结合特征选择器和编码参数,数据变为 0xc6888fa10000000000000000000000000000000000000000000000000000000000000006。

让我们试试看:

\> curl --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "to": "0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d", "data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}], "id": 8}' localhost:8545
{"id":8,"jsonrpc":"2.0","result":"0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869
 
  • 1
  • 2

由于我们发送了交易,因此返回了一个交易哈希表。 如果接收到搜索,可以看到一些新的内容,如下:

{
blockHash: "0xbf0a347307b8c63dd8c1d3d7cbdc0b463e6e7c9bf0a35be40393588242f01d55",
blockNumber: 268,
contractAddress: null,
cumulativeGasUsed: 22631,
gasUsed: 22631,
logs: [{
address: "0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d",
blockHash: "0xbf0a347307b8c63dd8c1d3d7cbdc0b463e6e7c9bf0a35be40393588242f01d55",
blockNumber: 268,
data: "0x000000000000000000000000000000000000000000000000000000000000002a",
logIndex: 0,
topics: ["0x24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"],
transactionHash: "0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869d74",
transactionIndex: 0
}],
transactionHash: "0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869d74",
transactionIndex: 0
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

接收包含一个日志。 日志由以太坊虚拟机在交易执行时生成,包括收据。 如果我们查看 Multiply 函数,我们可以看到打印事件是在输入计数为 7 时引发的。由于打印事件的参数是 uint256,它可以根据 ABI 规则进行编码,这为我们提供了预期的结果十进制 42。

\> web3.sha3("Print(uint256)")
"24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"
 
  • 1
  • 2

这只是对一些最常见任务的简要介绍。 在 RPC wiki 页面上查看可用 RPC 方法的完整列表。

Web3.js

如前例所示,使用 JSON-RPC 接口相当繁琐且容易出错,尤其是在处理 ABI 时。 Web3.js 是一个 Javascript 库,其目标是提供更友好的界面并减少出错的机会。

使用 web3 部署 Multiply7 合约如下所示:

var source = 'contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input
var compiled = web3.eth.compile.solidity(source);
var code = compiled.Multiply7.code;
var abi = compiled.Multiply7.info.abiDefinition;
web3.eth.contract(abi).new({from: "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", data: code}, function (err, contract) {
if (!err && contract.address)
console.log("deployed on:", contract.address);
}
);
deployed on: 0x0ab60714033847ad7f0677cc7514db48313976e2
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

加载已部署的合约并发送交易:

var source = 'contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input
var compiled = web3.eth.compile.solidity(source);
var Multiply7 = web3.eth.contract(compiled.Multiply7.info.abiDefinition);
var multi = Multiply7.at("0x0ab60714033847ad7f0677cc7514db48313976e2")
multi.multiply.sendTransaction(6, {from: "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"})
 
  • 1
  • 2
  • 3
  • 4
  • 5

注册将在打印事件创建日志时调用的回调。

multi.Print(function(err, data) { console.log(JSON.stringify(data)) })
{"address":"0x0ab60714033847ad7f0677cc7514db48313976e2","args": {"":"21"},"blockHash":"0x259c7dc0
 
  • 1
  • 2