智能合约原始交易
在本章中,我们将使用JSON RPC部署一个智能合约,并与之进行交互。同样,我们也可以看到实现QTUM智能合约平台的底层原始交易。
下面为一个具有单个存储变量的合约的示例:
pragma solidity ^0.4.18;
contract SimpleStore {
function SimpleStore(uint _value) public {
value = _value;
}
function set(uint newValue) public {
value = newValue;
}
function get() public constant returns (uint) {
return value;
}
uint value;
}
创建合约
将合约编译为字节码:
solc --optimize --bin SimpleStore.sol
======= SimpleStore.sol:SimpleStore =======
Binary:
608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b200029
为了创建该合约,我们需要创建一个特殊的交易,该交易使用上述字节码作为交易的payload数据。
合约的构造函数需要一个用于初始化的值_value
:
function SimpleStore(uint _value) public {
value = _value;
}
使用1
作为初始化值。所有的构造函数参数都需要进行ABI编码。数字1
的ABI编码值为一个32字节的十六进制字符串:
0000000000000000000000000000000000000000000000000000000000000001
我们如何传递ABI编码的构造函数参数呢?很简单,只需将它附加到字节码的末尾。因此,创建一个智能合约的交易payload应该遵循以下结构:
<bytecode><constructor parameters>
创建一个合约的RPC命令是createcontract
。将编译器输出的字节码和构造函数参数组合在一起,并调用该命令:
qcli createcontract \
608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001
{
"txid": "4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68",
"sender": "qZ2kTyK4vLvGSngtYynmRdfd8n9UBFNwMv",
"hash160": "a9b6e7e59974d92b949a9c9aa8eb420f73518364",
"address": "ecb30fa67545211d0e4b859213872442cd9d37c5"
}
CREATECONTRACT的原始交易
为了深入了解createcontract
调用,下面我们检查用于创建交易的底层UTXO。首先,获取该交易id相关的原始交易数据:
qcli getrawtransaction 4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68
02000000019b96d26a797a9952dbd8d81400de9143e30cc73ae2d83fe6d3bcc46dc665ebb5000000004847304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d01feffffff02107851a3d10100001976a91441d6ac4feec70a7b28c59c23e92be5ea2a15879a88ac0000000000000000fd1e01010403a0252601284d1201608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001c1fe010000
然后,对该数据进行解析:
qcli decoderawtransaction \
02000000019b96d26a797a9952dbd8d81400de9143e30cc73ae2d83fe6d3bcc46dc665ebb5000000004847304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d01feffffff02107851a3d10100001976a91441d6ac4feec70a7b28c59c23e92be5ea2a15879a88ac0000000000000000fd1e01010403a0252601284d1201608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001c1fe010000
输出为:
{
"txid": "4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68",
"hash": "4caa93a015ecf4e9fafd84c7cda2ad4897068777f29a1b1e1804e50553990c68",
"size": 454,
"vsize": 454,
"version": 2,
"locktime": 510,
"vin": [
{
"txid": "b5eb65c66dc4bcd3e63fd8e23ac70ce34391de0014d8d8db52997a796ad2969b",
"vout": 0,
"scriptSig": {
"asm": "304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d[ALL]",
"hex": "47304402207601cc15aa2abc535de1673e1fe07d902d7970d2ea46892a5dc7dd7fbd394f5002201a6dd6dba6fe73f065cd2b2906d8b0632cfee68622835de23a8af6e73f7f968d01"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 19998.99818000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 41d6ac4feec70a7b28c59c23e92be5ea2a15879a OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a91441d6ac4feec70a7b28c59c23e92be5ea2a15879a88ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"qPZWFFMrHtxsaop1wJKh6SCnnurMFjPexC"
]
}
},
{
"value": 0.00000000,
"n": 1,
"scriptPubKey": {
"asm": "4 2500000 40 608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001 OP_CREATE",
"hex": "010403a0252601284d1201608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001c1",
"type": "create"
}
}
]
}
- 第一个vout为找回的零钱,需要返回给发送方钱包
- 第二个vout为一段OP_CREATE脚本
OP_CREATE是QTUM为支持智能合约平台而添加的一个新指令。它在栈上有4个参数,如下所示:
# VM version
4
# gas limit
2500000
# gas price
40
# bytecode + constructor params
608060405234801561001057600080fd5b506040516020806100f2833981016040525160005560bf806100336000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058202926444d9654f866e083cd06b8bfddde21cecfc614d9a03045246423c47f9b2000290000000000000000000000000000000000000000000000000000000000000001
# create instruction
OP_CREATE
使用Solar部署合约
我们再次使用solar部署SimpleStore
合约。它完全可以完成我们刚刚所做的事情:
solar deploy SimpleStore.sol '[1]'
🚀 All contracts confirmed
deployed SimpleStore.sol => cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4
与合约进行交互
与合约进行交互的方法有两种。
callcontract
- 合约method的只读查询。sendtocontract
- 创建一个会改变合约状态的交易。
上述两种方法都会调用合约的method。参数需要为ABI编码的形式,我们将使用solar
来创建发送给底层RPC命令的“data payload”。
调用合约
调用一个合约,我们要对调用的method进行ABI编码,并将它发送给callcontract
RPC调用。
调用get
method,该method返回SimpleStore合约的当前值
solar encode SimpleStore.sol get
6d4ce63c
callcontract需要使用合约地址,以及ABI编码的调用的method:
qcli callcontract cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 \
6d4ce63c
结果如下:
{
"address": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
"executionResult": {
"gasUsed": 21678,
"excepted": "None",
"newAddress": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
"output": "0000000000000000000000000000000000000000000000000000000000000001",
"codeDeposit": 0,
"gasRefunded": 0,
"depositSize": 0,
"gasForDeposit": 0
},
"transactionReceipt": {
"stateRoot": "88407020db33e8aca4ac47b16940fb31f9942bf8ff734e68156e899992465eda",
"gasUsed": 21678,
"bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"log": [
]
}
}
可以看到,上述结果中的output
项返回了我们用于构造该合约的值。
发送一个合约交易
现在,我们使用sendtocontract
来创建一个可以改变SimpleStore合约状态的交易。
对set(2)
进行编码:
solar encode SimpleStore.sol set '[2]'
60fe47b10000000000000000000000000000000000000000000000000000000000000002
然后,使用合约地址以及ABI编码的method进行sendtocontract
RPC调用:
qcli sendtocontract cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 \
60fe47b10000000000000000000000000000000000000000000000000000000000000002
返回的交易id为:
{
"txid": "f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139",
"sender": "qKNc7xc44W9teoxoWLUaxeJNq1967JaUoD",
"hash160": "13e67f7e74b6e81a841292ebeaabfc7333d3e1cb"
}
一旦交易已经被确认,我们就可以再次调用callcontract获取新的值:
qcli callcontract cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 \
6d4ce63c
{
"address": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
"executionResult": {
"gasUsed": 21678,
"excepted": "None",
"newAddress": "cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4",
"output": "0000000000000000000000000000000000000000000000000000000000000002",
"codeDeposit": 0,
"gasRefunded": 0,
"depositSize": 0,
"gasForDeposit": 0
},
"transactionReceipt": {
"stateRoot": "c4c0aac17cdde49eb2ae303941c8721c7e4b2f394d27e15aec4f15778e4cd087",
"gasUsed": 21678,
"bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"log": [
]
}
}
value
存储变量确实已经被修改了。
SendToContract交易
最后,我们来检查实现sendtocontract
的原始交易:
qcli getrawtransaction f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139
0200000001c919f89ddefa4bc09ad83330f993360fc419c9c91ef1560ede7f2df4f680eca6010000006a47304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b012102f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30ffeffffff02c0348d9bd10100001976a91458a1b81dbdeedd166bb166f528bb861048baf9b288ac00000000000000004301040390d00301282460fe47b1000000000000000000000000000000000000000000000000000000000000000214cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4c221020000
对该数据进行解析:
qcli decoderawtransaction 0200000001c919f89ddefa4bc09ad83330f993360fc419c9c91ef1560ede7f2df4f680eca6010000006a47304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b012102f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30ffeffffff02c0348d9bd10100001976a91458a1b81dbdeedd166bb166f528bb861048baf9b288ac00000000000000004301040390d00301282460fe47b1000000000000000000000000000000000000000000000000000000000000000214cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4c221020000
解析出来的交易数据如下:
{
"txid": "f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139",
"hash": "f6e934db283949a91a3f134246f48c52a7c8d580db36e998fa838d8c10c3b139",
"size": 267,
"vsize": 267,
"version": 2,
"locktime": 545,
"vin": [
{
"txid": "a6ec80f6f42d7fde0e56f11ec9c919c40f3693f93033d89ac04bfade9df819c9",
"vout": 1,
"scriptSig": {
"asm": "304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b[ALL] 02f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30f",
"hex": "47304402207401aa5c7f4fddb9e3917c26a383fe250cecda61b51f722ce2fbc2c47bcb1aa40220585badcf06a9b77156d54d5bd5f5d30e9606c340012e9e5097d52c2bd8e1839b012102f0d1abe6acc590a400c3719a562299637c55e45e56d4c75658e07d263ab8b30f"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 19997.69515200,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 58a1b81dbdeedd166bb166f528bb861048baf9b2 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a91458a1b81dbdeedd166bb166f528bb861048baf9b288ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"qRe2MMp8cyzXs7WpCGZKvpdrJJYyoKm9Lg"
]
}
},
{
"value": 0.00000000,
"n": 1,
"scriptPubKey": {
"asm": "4 250000 40 60fe47b10000000000000000000000000000000000000000000000000000000000000002 cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4 OP_CALL",
"hex": "01040390d00301282460fe47b1000000000000000000000000000000000000000000000000000000000000000214cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4c2",
"type": "call"
}
}
]
}
第二个vout为调用智能合约的脚本。它的结构和用于创建合约的OP_CREATE脚本非常相似。其脚本如下:
# VM version
4
# gas limit
250000
# gas price
40
# ABI-encoded method invokation
60fe47b10000000000000000000000000000000000000000000000000000000000000002
# address of contract
cd20af1f2d6ac4173f9464030e7cef40bf9cb7c4
OP_CALL