智能合约原始交易

在本章中,我们将使用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

与合约进行交互

与合约进行交互的方法有两种。

  1. callcontract - 合约method的只读查询。
  2. sendtocontract - 创建一个会改变合约状态的交易。

上述两种方法都会调用合约的method。参数需要为ABI编码的形式,我们将使用solar来创建发送给底层RPC命令的“data payload”。

调用合约

调用一个合约,我们要对调用的method进行ABI编码,并将它发送给callcontractRPC调用。

调用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进行sendtocontractRPC调用:

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

results matching ""

    No results matching ""