Shellchain experiment

Basic experiment docs

Download and install

https://github.com/USTB-InternetSecurityLab/Shellchain

项目代码量17余万行,基于multichain开发,最近的分支: springboot-contract

VSCode配置config.json举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/src/multichaind",
"args": ["shellchain"],
"stopAtEntry": false,
"cwd": "${workspaceRoot}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb"
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

Experiment 1: 单机实验

start

创建一条新链,这里的链名为chain1:

1
wsd@ubuntu_1:~$ shellchain-util create chain1

./src/shellchain-util create chain1

查看链初始化数据,启动链

1
2
wsd@ubuntu_1:~$ cat ~/.shellchain/chain1/params.dat
wsd@ubuntu_1:~$ shellchaind chain1 -daemon

shellchaind chain1 -daemon

关闭链
shellchain-cli chain1 stop

Raw transactions: 原始交易

查看有授权资格的账户并创建一笔用于转账的资产:

1
2
listpermissions issue
issue 15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7 asset1 100 0.1

(If multiple addresses are displayed, use the getaddresses true command to see which addresses belong to the local wallet.)

返回资产创建的交易id
list accounts of issue permission

创建新的地址用于转账:

1
getnewaddress

给予新地址交易权限:

1
grant 1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L receive,send

返回交易授权地址
newaddress

接下来我们看一看交易格式:gettransaction c20cc3707c00402ba1365a49c373483b5ae55e42055e7717d65d61bfc77243df

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
"amount" : 0.00000000,
"fee" : 0.00000000,
"confirmations" : 6,
"blockhash" : "001ceed2e7cc61a635a4bfa81a091d53d38695b5f5068102c9d5cf8513afa95c",
"blockindex" : 1,
"blocktime" : 1599743510,
"txid" : "c20cc3707c00402ba1365a49c373483b5ae55e42055e7717d65d61bfc77243df",
"walletconflicts" : [
],
"valid" : true,
"time" : 1599743493,
"timereceived" : 1599743493,
"details" : [
{
"account" : "",
"address" : "1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L",
"category" : "send",
"amount" : 0.00000000,
"vout" : 0,
"fee" : 0.00000000
},
{
"account" : "",
"address" : "15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7",
"category" : "send",
"amount" : 0.00000000,
"vout" : 1,
"fee" : 0.00000000
},
{
"account" : "",
"address" : "1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L",
"category" : "receive",
"amount" : 0.00000000,
"vout" : 0
},
{
"account" : "",
"address" : "15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7",
"category" : "receive",
"amount" : 0.00000000,
"vout" : 1
}
],
"hex" : "0100000001013e4affbd34ee58e69c34f910ba77f9bf363577fb93bbe1a2189ed656ce2640020000006a4730440220095458a6032aa1d445500b3a1321f4e4ee58c00d267b89b1c2b3d0bb427b36cc02202b4f3020dd094055a83cf859ebe0621c2c64f2ac015cf16ea331de55e168d7a7012103be0316aba83fd1a42cee503b706072c1d0db81990d3ad5589ad56eeb407d489affffffff0200000000000000002f76a9145457615fc1c97532c1927ad664d49f373c6d6e6d88ac1473706b700600000000000000ffffffff05265a5f7500000000000000001976a9142236a285f892a2049c22dacae972fb0ecb4e2a7688ac00000000027b7d00000000"
}
1
./src/multichain-cli chain1 decoderawtransaction 0100000001013e4affbd34ee58e69c34f910ba77f9bf363577fb93bbe1a2189ed656ce2640020000006a4730440220095458a6032aa1d445500b3a1321f4e4ee58c00d267b89b1c2b3d0bb427b36cc02202b4f3020dd094055a83cf859ebe0621c2c64f2ac015cf16ea331de55e168d7a7012103be0316aba83fd1a42cee503b706072c1d0db81990d3ad5589ad56eeb407d489affffffff0200000000000000002f76a9145457615fc1c97532c1927ad664d49f373c6d6e6d88ac1473706b700600000000000000ffffffff05265a5f7500000000000000001976a9142236a285f892a2049c22dacae972fb0ecb4e2a7688ac00000000027b7d00000000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
"txid" : "c20cc3707c00402ba1365a49c373483b5ae55e42055e7717d65d61bfc77243df",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "4026ce56d69e18a2e1bb93fb773536bff977ba10f9349ce658ee34bdff4a3e01",
"vout" : 2,
"scriptSig" : {
"asm" : "30440220095458a6032aa1d445500b3a1321f4e4ee58c00d267b89b1c2b3d0bb427b36cc02202b4f3020dd094055a83cf859ebe0621c2c64f2ac015cf16ea331de55e168d7a701 03be0316aba83fd1a42cee503b706072c1d0db81990d3ad5589ad56eeb407d489a",
"hex" : "4730440220095458a6032aa1d445500b3a1321f4e4ee58c00d267b89b1c2b3d0bb427b36cc02202b4f3020dd094055a83cf859ebe0621c2c64f2ac015cf16ea331de55e168d7a7012103be0316aba83fd1a42cee503b706072c1d0db81990d3ad5589ad56eeb407d489a"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.00000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 5457615fc1c97532c1927ad664d49f373c6d6e6d OP_EQUALVERIFY OP_CHECKSIG 73706b700600000000000000ffffffff05265a5f OP_DROP",
"hex" : "76a9145457615fc1c97532c1927ad664d49f373c6d6e6d88ac1473706b700600000000000000ffffffff05265a5f75",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L"
]
},
"assets" : [
],
"permissions" : [
{
"for" : null,
"connect" : false,
"send" : true,
"receive" : true,
"create" : false,
"issue" : false,
"mine" : false,
"admin" : false,
"activate" : false,
"startblock" : 0,
"endblock" : 4294967295,
"timestamp" : 1599743493
}
],
"items" : [
]
},
{
"value" : 0.00000000,
"n" : 1,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 2236a285f892a2049c22dacae972fb0ecb4e2a76 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9142236a285f892a2049c22dacae972fb0ecb4e2a7688ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7"
]
},
"assets" : [
],
"permissions" : [
],
"items" : [
]
}
],
"data" : [
],
"nType" : 0
}

./src/multichain-cli chain1 getwallettransaction 20cc3707c00402ba1365a49c373483b5ae55e42055e7717d65d61bfc77243df

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"balance" : {
"amount" : 0.00000000,
"assets" : [
]
},
"myaddresses" : [
"1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L",
"15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7"
],
"addresses" : [
],
"permissions" : [
{
"for" : null,
"connect" : false,
"send" : true,
"receive" : true,
"create" : false,
"issue" : false,
"mine" : false,
"admin" : false,
"activate" : false,
"startblock" : 0,
"endblock" : 4294967295,
"timestamp" : 1599743493,
"addresses" : [
"1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L"
]
}
],
"items" : [
],
"data" : [
],
"confirmations" : 11,
"blockhash" : "001ceed2e7cc61a635a4bfa81a091d53d38695b5f5068102c9d5cf8513afa95c",
"blockindex" : 1,
"blocktime" : 1599743510,
"txid" : "c20cc3707c00402ba1365a49c373483b5ae55e42055e7717d65d61bfc77243df",
"valid" : true,
"time" : 1599743493,
"timereceived" : 1599743493
}

列出该地址下 0 到 9999 范围内未被使用的资产(UTXO):

1
listunspent 0 9999 '["15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7"]'

listunspent.png

获取资产所在的交易id以及vout序号,通过 gettxout 命令再次检查:
gettxout.png

现在可以通过命令行创建一个转账20单位asset1的原始交易:

1
createrawtransaction '[{"txid":"4026ce56d69e18a2e1bb93fb773536bff977ba10f9349ce658ee34bdff4a3e01","vout":0}]' '{"1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L":{"asset1":20}}'

可以使用 decoderawtransaction 对得到的hex串进行解析。

由于每个交易的txin段和txout段要求的交易数量总数应该相等,在转出一笔20单位的out之后还需要补充一笔80单位的out字段:

1
appendrawchange [hex-blob] 15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7

执行成功后得到一串更长的hex,解析此串可以得到完整的txin、txout字段。可以继续添加一些16进制信息(如 abcd1234 )作为data字段:

1
appendrawdata [paste-longer-hex-blob] abcd1234

Round-robin consensus

Definition for Round-robin consensus: Multiple nodes play a key role in validating and voting for transactions, doesn’t depend on a single participant for block validation.

In this secion we’ll start collaborative block validation between the nodes. Note that, in the case of a permissioned Multichain blockchain, consensus is based on block signature and a customizable round-robin consensus scheme, rather than proof-of-work as in bitcoin.

1
wsd@ubuntu_1:~$ multichain-cli chain1 grant 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 mine

(Even though the permission is called mine note that there is no real “mining” taking place, in the sense of proof-of-work.)

1
wsd@ubuntu_2:~$ multichain-cli chain1 listpermissions mine

list-mine-permissions

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 setruntimeparam miningturnover 1
wsd@ubuntu_2:~$ multichain-cli chain1 setruntimeparam miningturnover 1

Now wait for a couple of minutes, so that a few blocks are added. (This assumes you left the block time on the default of 15 seconds.) Check the creators of the last few blocks:

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 listblocks -10
wsd@ubuntu_2:~$ multichain-cli chain1 listblocks -10

The address of the validator of each block is in the miner field of each element of the response.

Experiment 2: 双机实验

1
wsd@ubuntu_2:~$ multichaind chain1@192.168.122.168:2911

second server first try

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 grant 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 connect
wsd@ubuntu_2:~$ multichaind chain1 -daemon

second try is ok after server 1 grants the previlege

In case that the interactive mode of multichain-cli doesn’t support backspace, you can wrap the cli command with multichain-cli chain1.

Streams: 流

流可以用来存储和传递一般信息

1
create stream stream1 true

发布一个内容项上去,口令为 key1 (注意内容格式必须为16进制串,不然会报错:-8,Item data should be hexadecimal string)

1
publish stream1 key1 1234abcd

返回的是交易ID

检查该链上所有的流

1
liststreams

至少可以看到stream1和默认的root两个流

订阅stream1:

1
2
3
liststreamitems stream1
subscribe stream1
liststreamitems stream1

继续发布一些内容项到流:

1
2
publish stream1 key1 1234567890
publish stream1 key1 abcdef

获取流中的内容

1
liststreamitems stream1 # should show 3 items

You should see 3 items in the output because the first server has published two strings to key1, and published one string to key2.

1
wsd@ubuntu_1:~$ multichain-cli chain1 liststreamkeys stream1 # 2 keys

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 liststreamkeyitems stream1 key1 # 2 items with the key
wsd@ubuntu_1:~$ multichain-cli chain1 liststreampublishers stream1 # 2 publishers: first server and second server

two-publishers

1
wsd@ubuntu_1:~$ multichain-cli chain1 liststreampublisheritems stream1 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 # two items by this publisher

Assets

1
wsd@ubuntu_1:~$ multichain-cli chain1 listpermissions issue

permissions-to-create-assets

copy the address in the list so that an asset with 1000 units can be created on this node.

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 issue 1LWwPw3x9ocBkqTLkFGdqJqK3y6S8T3aLc7ZMg asset1 1000 0.01
wsd@ubuntu_1:~$ multichain-cli chain1 listassets

On both servers, you can verify asset1.
asset1

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 gettotalbalances
wsd@ubuntu_2:~$ multichain-cli chain1 gettotalbalances

server-1-balance
server-2-balance

If the addresses are not enough( <= 2), run getnewaddress to create new address for receiving assets:
getnewaddress
(If multiple addresses are displayed, use the getaddresses true command to see which addresses belong to the local wallet.)

Now we’re going to build a new transaction which sends some of the newly created assets from the current holding addresses to the newly created addresses:

1
./src/multichain-cli chain1 listunspent 0 9999 '["15dCVacyUcLD22yX3EeoYAecfwqdRZhfedTPT7"]'

Now try to send 50 units of the assets from first server’s wallet to second server’s.

1
wsd@ubuntu_1:~$ multichain-cli chain1 sendasset 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 asset1 100

An error showing ‘Destination address doesn’t have receive permission’ will appear. So it’s time to add receive permissions

1
wsd@ubuntu_1:~$ multichain-cli chain1 grant 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 receive

try sending asset again

1
wsd@ubuntu_1:~$ multichain-cli chain1 sendasset 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 asset1 100

send-asset

Now check the asset balances on each server, including transactions with zero confirmations.

balance-on-first-server
balance-on-second-server

You can also view the transaction on each node and see how it affected their balances:

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 listwallettransactions 1
wsd@ubuntu_2:~$ multichain-cli chain1 listwallettransactions 1

Issuing assets

发布资产

Combining assets and streams: 流与资产的结合

本部分将创建一笔125单位的转账交易,并写入附带信息到流中。

1
wsd@ubuntu_1:~$ multichain-cli chain1 sendwithdata 1R6jUNxTqx6bJetkv5ZkLXLub1nsNwC95gurf6 '{"asset1":125}' '{"for":"stream1","key":"transfer","data":"486921"}'

transaction-id

Now this transaction can be examined on the second server as below:

1
wsd@ubuntu_2:~$ multichain-cli chain1 getwallettransaction 60e98d2eb2133e1757af056e99207255a00f0d4845e6451e319774dafe20b560

In the output from this command, you should see the balance field showing the incoming “qty” is 125 units of asset1 and the items field containing the stream item that was added. You can also view this item directly within the stream:

1
2
wsd@ubuntu_1:~$ multichain-cli chain1 liststreamitems stream1
wsd@ubuntu_2:~$ multichain-cli chain1 liststreamitems stream1

list-streamitem-stream1-on-first-server
list-streamitem-stream1-on-second-server

源码阅读

发布交易的函数是 SendMoneyToSeveralAddresses(addresses, nAmount,wtx, lpScript, CScript(),addresses,txInfo.formatTx(),txInfo.ntype); 几个参数的解释分别为,接收方地址列表addresses,转账数额nAmount,接收交易指针wtx,脚本解释器指针lpScript,新建脚本解释器对象CScript(),发送方地址列表addresses,传入的交易信息txInfo.formatTx(),传入的交易类型txInfo.ntype

获取交易ID: string txid= wtx.GetHash().GetHex();
获取userResult:txInfo.userResult.c_str()

//获取使用方地址,并判断该地址是否存在以及拥有接收权力
vector<CTxDestination> addresses;
contractInformation txInfo = contractInformation();
//交易的部分参数初始化
CAmount nAmount=0;
CWalletTx wtx;
mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3;
lpScript->Clear();

更新multichain源仓库代码
shellchain.conf文件名的产生写在 chainparams/buildgenesis.cpp 里的 mc_GenerateConfFiles 函数中

rpc/rpchelp.cpp 里添加帮助文档
rpc/rpclist.cpp 里添加了cli命令和函数名的对应
rpc/rpcrawtransaction.cpp TxToJSON函数添加自定义交易字段到json的解析,在decoderawtransaction函数中会用到
rpc/rpcserver.h 中添加对于自定义交易函数的声明
rpc/rpcwalletutils.cpp SendMoneyToSeveralAddresses 中添加对 opcustom 的脚本解释器检查:

1
2
3
4
5
6
7
std:vector<CScript> scriptOpReturns;
if(scriptOpReturn.size()){
scriptOpReturns.push_back(scriptOpReturn);
}
if(scriptOpCustom.size()){
scriptOpReturns.push_back(scriptOpCustom);
}

rpc/rpcwalletsend.cpp 添加主体客体注册rpc函数和发送自定义交易函数
rpc/rpcclient.cpp vRPCConvertParams 中添加需要字符串转json的参数index,方法的第一个参数index为0

script/script.cpp GetOpName中和script.h opcodetype中 加入状态码 OP_CUSTOMDATA
script/standard.cpp IsStandardCustomData 加入对自定义脚本的检查

secp256k1/ecdsa_impl.h 中添加对于 256 位椭圆曲线的加解密函数,头文件在 ecdsa.h 中
secp256k1/ecmult_impl.h 中添加椭圆曲线点乘的操作
secp256k1/secp256k1.c 中添加加解密函数
secp256k1/tests.c 添加加解密测试

utils/serialize.h 中添加对于json参数的序列化和反序列化 stream 操作
utils/util.cpp 中添加对于 .shellchain 目录的创建

wallet/wallet_ismine.h 中定义 enum customdatatype
wallet/walletcoins.cpp 中BuildAssetTransaction函数、CreateAssetGroupingTransaction函数添加生成交易是自定义字段的处理

wallet/wallettxs.cpp 第 2051 行 for(i=0;i<(int)tx.vout.size();i++) // Checking that tx has long OP_RETURN,此处可能需要添加对op_custom的支持,避免字段过大对性能的影响
其中CustomTxNotify函数没搞清功能是啥

net/rest.cpp 负责提供rest接口

structs/amount.h CAmount 类型即 int64_t类型

阅读 createrawtransaction 1416-1576

交易的序列化存储与反序列化恢复

序列化采用将对象写入流的方式,在bitcoinconsensus.cpp 中定义了用于序列化的流 TxInputStream,仅在函数bitcoinconsensus_verify_script 中调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/** A class that deserializes a single CTransaction one time. */
class TxInputStream
{
public:
TxInputStream(int nTypeIn, int nVersionIn, const unsigned char *txTo, size_t txToLen) :
m_type(nTypeIn),
m_version(nVersionIn),
m_data(txTo),
m_remaining(txToLen)
{}

TxInputStream& read(char* pch, size_t nSize)
{
if (nSize > m_remaining)
throw std::ios_base::failure(std::string(__func__) + ": end of data");

if (pch == NULL)
throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer");

if (m_data == NULL)
throw std::ios_base::failure(std::string(__func__) + ": bad source buffer");

memcpy(pch, m_data, nSize);
m_remaining -= nSize;
m_data += nSize;
return *this;
}

template<typename T>
TxInputStream& operator>>(T& obj)
{
::Unserialize(*this, obj, m_type, m_version);
return *this;
}

private:
const int m_type;
const int m_version;
const unsigned char* m_data;
size_t m_remaining;
};

nTypeIn 取值类型为 enum{SER_NETWORK=1,SER_DISK=2,SER_GETHASH=4,} 中的一种,用于具体实现某种序列化操作

RPC请求接收

首先,server在启动的时候调用了 StartRPCThreads(string& strError) 函数,在该函数中调用

1
RPCListen(acceptor, *rpc_io_service, *rpc_ssl_context, fUseSSL);

RPCListen 的函数体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template <typename Protocol>
static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol> > acceptor,
io_service& service,
ssl::context& context,
const bool fUseSSL)
{
boost::shared_ptr< AcceptedConnectionImpl<Protocol> > conn(new AcceptedConnectionImpl<Protocol>(service, context, fUseSSL));

acceptor->async_accept(
conn->sslStream.lowest_layer(),
conn->peer,
boost::bind(&RPCAcceptHandler<Protocol>,
acceptor,
boost::ref(service),
boost::ref(context),
fUseSSL,
conn,
_1));
}

该函数接收泛型Protocol,用在acceptor中,
而调用该函数的accepter声明只有一处,位于 rpcserver.cppStartRPCThreads 函数中,声明如下:

1
boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service));

boost::shared_ptr 是一个可以共享所有权的智能指针
ip::tcp::acceptor 重命名于 boost::asio::basic_socket_acceptorboost::asio::ip::tcp
所以泛型Protocol在这里就是boost::asio::ip::tcp

接下来重点解释一下 acceptor->async_accept()函数。
该函数用来异步接收一个rpc请求到socket,并获取远程tcp端口。函数声明为:

1
2
3
4
template <typename Executor1, BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code)) AcceptHandler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(AcceptHandler,void (boost::system::error_code)) async_accept(basic_socket<protocol_type, Executor1>& peer,
endpoint_type& peer_endpoint,
BOOST_ASIO_MOVE_ARG(AcceptHandler) handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))

其中第一个参数 peer 就是连接将被接收进的socket,第二个参数 peer_endpoint 指与远程tcp连接对应的端口对象,第三个参数 handler 指接收完成后对应调用的函数对象。
boost::bind 返回值就是一个函数对象,第一个参数是函数声明指针,后面的对应该函数调用参数。
RPCAcceptHandler 接收6个参数,前5个参数在这里都确定了,第6个参数 error_code 每次实际调用都不同,这里 _1 表示实际调用时的参数列表的第一个参数,即传递进来的 error_code。

查看 LogPrintf

进入chain的存储目录

1
tail -f debug.log

在配置文件中添加自定义配置

读取配置文件的代码写在 utils/utilwrapper.cppmc_ReadConfigFile 函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 通过一个for循环从streamConfig的文件流中不断读取key=val的配置
for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
{
// Don't overwrite existing settings so command line settings override bitcoin.conf

string strKey = it->string_key;
strKey.erase(std::remove(strKey.begin(), strKey.end(), '-'), strKey.end()); // 删除key中的-
strKey.erase(std::remove(strKey.begin(), strKey.end(), '_'), strKey.end()); // 删除key中的_

strKey = string(prefix) + strKey; // key前面添加 -
if ((*mapSettingsRet).count(strKey) == 0)
{
(*mapSettingsRet)[strKey] = it->value[0];
// interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set)
mc_InterpretNegativeSetting(strKey, *mapSettingsRet);
}
if(mapMultiSettingsRet)
{
(*mapMultiSettingsRet)[strKey].push_back(it->value[0]);
}
}

配置内容写入 mapSettingsRetmapMultiSettingsRet

DIY:魔改代码

自定义交易格式

主要步骤:

  1. 定义合约字段 MutableContractInfo,继承于 CObjectInfo
  2. 添加编译选项,编译测试
  3. 在 transaction.h 的交易定义中添加 contract_in, contract_out 字段类型
    添加字段的序列化与反序列化操作,在rpcutils.h中定义,在rpcutils.cpp中实现
  4. 在发送交易的链条中添加合约字段和交易类型字段

1.定义合约字段

contracts文件夹中新建 contractinfo.h

contractinfo.hcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef _CONTRACT_INFO_H
#define _CONTRACT_INFO_H

#include "json/json_spirit.h"
#include "core/main.h"

struct MutableContractInfo
{
int ntype; // 合约状态类型码 enum::customdatatype
std::string address; // 合约地址 40 bytes
std::string lastTxid; // 上次合约执行结果所在交易号
std::string partySig; // 动作执行方签名
std::string actionName; // 动作名称
std::string actionParams; // 动作参数
std::string partyPubkey; // 动作执行方公钥
std::string contractData; // 合约执行结果
std::string hashcode;

ContractInfo() : ntype(-1) {}
ContractInfo(std::string str_txid);
ContractInfo(const CObjectInfo& tx);
void check(std::string& error);
Object formatTx();
};

#endif //_CONTRACT_INFO_H

合约地址储存在BerkeleyDB中,其中记录发布方账户地址fromAddress,每次执行合约的交易号及它们的索引,合约名称、SPESC代码、Java程序代码

在 contractinfo.cc 中编写声明的函数实现

2. 添加编译选项

在 src/Makefile.am 中的 libbitcoin_wallet_a_SOURCES 选项中添加 contracts/contractinfo.cpp, contracts/contractinfo.h
通过 autogen.sh, configure CFLAGS="-g -O0" CXXFLAGS="-g -O0" JFALGS="-g -O0" FFLAGS="-g -O0" CPPFLAGS="-g -O0" --enable-debug, make 三步进行测试

3. 添加交易自定义字段

在 CTransaction 和 CMutableTransaction 下添加定义

由于根据公钥是可以获得地址的,所以out端记录合约公钥

4. 修改发送交易的调用函数参数

SendMoneyToSeveralAddress 广播交易 > walletcoins.cpp 的 BuildAssetTransaction 函数中创建交易字段 vin_contract 和 vout_contract

5. BuildAssetTransaction

通过获取json串中的 contract_in 和 contract_out 字段,在临时交易txNew中添加ContractIn和ContractOut
交易大概长下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"txid" : "91c743e88e50be7b36843a108b2316af9d549a9fb7c94fa320d5bd3e5d28dfa2",
"version" : 1,
"locktime" : 0,
"contract_in" : {
"lastTxid" : "4026ce56d69e18a2e1bb93fb773536bff977ba10f9349ce658ee34bdff4a3e01",
"partySig" : "733c3bd75...be1d598470bd",
"actionName" : "Delegate",
"actionParams" : ["Platform", "OrderTicket"],
"invokeTerms" : [1]
},
"contract_out" : {
"contractData" : "004e3981e...92451a855511c49",
"contractPubkey" : "02b95dc6db0...9fdc3cd2af"
},
"vin" : [
...
],
"vout" : [
...
],
"nType" : 100
}

矿工接收请求后

  1. 接收到缓冲池MemoryPool中,重写 AcceptToMemoryPool 函数,添加
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #ifdef __ENABLE_CONTRACT
    if(tx.nType > 99 && tx.nType < 200) {
    if(!AcceptContract(tx,reason))
    {
    return state.DoS(0,
    error("AcceptToMemoryPool: : AcceptContract failed %s : %s", hash.ToString(),reason),
    REJECT_NONSTANDARD, reason);
    }
    }
    #endif

在 protocol/multichaintx.cpp 中添加 AcceptContract 函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
bool AcceptContract(const CTransaction& tx, string& reason)
{
//contract field check
MutableContractInfo info = MutableContractInfo(tx.info);
// if((reason = info.GetContractError()) != "") {
// return false;
// }
string txid = tx.GetHash().GetHex();
switch(tx.nType)
{
case CONTRACT_DEPLOY:
{
ContractsDBRow contracts = ContractsDBRow();
// contracts.setAddress(info.address.c_str(),info.address.length());
// contracts.setLatestCodeId(txid.data(),txid.size());
// contracts.setState(1);
// contracts.setName(info.contractName.data(),info.contractName.size());
mc_gState->m_Contracts->InsertItem(contracts);
break;
}
case CONTRACT_INIT:
{
mc_gState->m_Contracts->updateLatestExeId(info.address.data(), txid.data());
break;
}
case CONTRACT_EXECUTE:
{
mc_gState->m_Contracts->updateLatestExeId(info.address.data(), txid.data());
break;
}
// case CONTRACT_UPDATE:
// {
// mc_gState->m_Contracts->updateLatestCodeId(info.address.data(), txid.data());
// break;
// }
// case CONTRACT_DESTROY:
// {
// mc_gState->m_Contracts->updateState(info.address.data(), 0);
// break;
// }
default:
{
break;
}
}
return true;
}
  1. 拓展交易检查函数 CheckInputs

6. 拓展交易检查函数

main.cpp 下的 CheckInputs 函数

7. 编写合约数据库

合约数据库以contractAddress作为key,
存储了发布方address,每次执行的执行方各交易号,SPESC合约信息,Java程序代码。
分布式环境下,合约代码可利用multichain stream功能传递。
合约数据库使用BerkeleyDB,是可以和Java端调用的BerkeleyDB结合的(非JE版本可互通,JE版本仅用于Java端),

8. 编写 rpc 接口函数支持

编写 rpccontract.cc

9. 添加对于 rawtransaction 的解析支持

rpcrawtransaction.cpp 中的 TxToJSON 函数中添加对于自定义交易的转json串功能

1
2


workflow.png

再运行一次命令
./multichain-cli shellchain executecontract 1Ln9Tp1FCnKwSHw75BYBs74fyvp4LoaxzcHiqP addT 10

1
2
3
4
5
6
7
8
9
10
11
12
{
"systemInput":{
"contractData":"aced00057372000f637573746f6d2e436f6e74726163745e7698d0be7e2a1a02000249000574696d65734c00046e616d657400124c6a6176612f6c616e672f537472696e673b7872001d7368656c6c436861696e2e5368656c6c436861696e436f6e7472616374e96530aec4a7108902000078700000000a7400263141633177415a41336a554833537964735435444b67697a41774e6f364d6e42437871365732",
"fromAddress":"1Ac1wAZA3jUH3SydsT5DKgizAwNo6MnBCxq6W2",
"funName":"addT"
},
"contractParams":{
"signature":"",
"partyForm":"",
"userParams":["10"]
}
}

添加自定义接口

主要步骤:

比如现在我现在添加4个有关合约的自定义接口:sendcontract, sendcontractfrom, getcontract, getcontractfrom
rpcserver.h 里声明

1
2
3
4
5
extern json_spirit::Value sendcontract(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendcontractfrom(const json_spirit::Array& params, bool fHelp);

extern json_spirit::Value getcontract(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getcontractfrom(const json_spirit::Array& params, bool fHelp);

rpclist.cpp 里定义map的cli命令

如果需要json转移,则在 rpcclient.cpp 中的 vRPCConvertParams 中定义

接口测试

1
sendcontract '[{"txid":"4026ce56d69e18a2e1bb93fb773536bff977ba10f9349ce658ee34bdff4a3e01","vout":0}]' '{"1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L":{"asset1":20}}' '{"contractid":"abcd1234", "action":{"name":"publishservice","args":[1]}, "state":{"data":"somedata"}}'

根据公钥 pkey 获取地址:

1
2
3
info.fromAddress = CBitcoinAddress(pkey.GetID()).ToString();
fromaddresses=ParseAddresses(info.fromAddress,false,true);
addresses.push_back(fromaddresses[0]);

根据地址获取公钥脚本:

GetScriptForDestination

根据 scriptPubKey 获取 pubkey

CMutableTransaction 转 CWalletTx

walletcoins.cpp 1808行: static_cast<CTransaction>(&wtxNew) = CTransaction(txNew);

walletcoins.cpp 2743行 CreateAndCommitOptimizeTransaction 可以利用一下

测试createcontract

1
2
3
4
5
6
7
8
9
10
11
12
wsd@ubuntu-16-LTS-Server:~/JoToChain$ ./src/multichain-cli contractchain createcontract contract_name ~/contract_path
{"method":"createcontract","params":["contract_name","/home/wsd/contract_path"],"id":"68243892-1600005106","chain_name":"contractchain"}

{
"address" : "1DpHe374npo7eotqMpcWT3DRW7m3Gdt577QrMZ",
"txid" : "91c743e88e50be7b36843a108b2316af9d549a9fb7c94fa320d5bd3e5d28dfa2"
}

{
"address" : "1P3xtepz7jubi9bgXuUkPFmq1aKwajoxjPPDbt",
"txid" : "37a6156e39f50219b9868817be80ddb5425f10afe0c0878b7ea2103acc1240de"
}

Commit 过程中报错:Error: The transaction was rejected: 16: ConnectInputs failed: wrong-contract-deploy-output-information
报错: CommitTransaction() : Error: Transaction not valid: 16: ConnectInputs failed: contract-deploy-input: address or actionName is null。考虑原因可能是CreateTransaction的过程中没有加入contractInfo

walletcoins.cpp 1564行:txNew.info.add(Others); 存疑。是否有存在的必要?

multichaintx.cpp 里的 AcceptContract 函数用于 main.cpp 中,作用是再次检查合约格式,并加入到合约数据库内存池中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
./src/multichain-cli contractchain gettransaction 91c743e88e50be7b36843a108b2316af9d549a9fb7c94fa320d5bd3e5d28dfa2
{
"amount" : 0.00000000,
"fee" : 0.00000000,
"confirmations" : 4,
"blockhash" : "008abd8b88150757c1f2bdd4cd6ccdc7c829c698d0a084bcbff9685df729b890",
"blockindex" : 1,
"blocktime" : 1600222168,
"txid" : "91c743e88e50be7b36843a108b2316af9d549a9fb7c94fa320d5bd3e5d28dfa2",
"walletconflicts" : [
],
"valid" : true,
"time" : 1600222168,
"timereceived" : 1600222168,
"details" : [
{
"account" : "",
"address" : "1DpHe374npo7eotqMpcWT3DRW7m3Gdt577QrMZ",
"category" : "send",
"amount" : 0.00000000,
"vout" : 0,
"fee" : 0.00000000
}
],
"hex" : "..."
}

解析hex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
./src/multichain-cli contractchain decoderawtransaction 010000000172e4398b34b340c2d369c53bab6bd81bb3b2f2312ce5ffdfdab510216296f0b6000000006a473044022014153ed4e56ca6d2eeed9a58357fca9f35b3e39b70a949b771b7b8f752fdbab202206c51bf6af4120f1c32430bd10fe17b034d5f7fc7eabb66bcf4b39a6ed5d143530121027f51366ead7860f7a76ec288dcefa2c1af3b1e649f548a2d3999e73da1f5f6c0ffffffff737b2261646472657373223a2231447048653337346e706f37656f74714d7063575433445257376d3347647435373751724d5a222c2274786964223a22222c22616374696f6e4e616d65223a225f696e6974222c22616374696f6e506172616d73223a22222c227061727479536967223a22227d0200000000000000001976a9145ed1c347df506af7d9cb66877bc2b2f58789909288ac027b7d00000000000000001976a914a06beee2be7d64b78fe0af0fa3e22ac64a85ec3888ac667b22636f6e747261637444617461223a22222c2270617274795075626b6579223a22303262393564633664623037333363336264373562653164353938343730626439666463336330303465333938316539323435316138353535313163343964326166227d0000000064000000

{
"txid" : "91c743e88e50be7b36843a108b2316af9d549a9fb7c94fa320d5bd3e5d28dfa2",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "b6f096622110b5dadfffe52c31f2b2b31bd86bab3bc569d3c240b3348b39e472",
"vout" : 0,
"scriptSig" : {
"asm" : "3044022014153ed4e56ca6d2eeed9a58357fca9f35b3e39b70a949b771b7b8f752fdbab202206c51bf6af4120f1c32430bd10fe17b034d5f7fc7eabb66bcf4b39a6ed5d1435301 027f51366ead7860f7a76ec288dcefa2c1af3b1e649f548a2d3999e73da1f5f6c0",
"hex" : "473044022014153ed4e56ca6d2eeed9a58357fca9f35b3e39b70a949b771b7b8f752fdbab202206c51bf6af4120f1c32430bd10fe17b034d5f7fc7eabb66bcf4b39a6ed5d143530121027f51366ead7860f7a76ec288dcefa2c1af3b1e649f548a2d3999e73da1f5f6c0"
},
"vin_contract" : {
"address" : "1DpHe374npo7eotqMpcWT3DRW7m3Gdt577QrMZ",
"txid" : "",
"actionName" : "_init",
"actionParams" : "",
"partySig" : ""
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.00000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 5ed1c347df506af7d9cb66877bc2b2f587899092 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9145ed1c347df506af7d9cb66877bc2b2f58789909288ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1DpHe374npo7eotqMpcWT3DRW7m3Gdt577QrMZ"
]
}
},
{
"value" : 0.00000000,
"n" : 1,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 a06beee2be7d64b78fe0af0fa3e22ac64a85ec38 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914a06beee2be7d64b78fe0af0fa3e22ac64a85ec3888ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1NgYPRT6AQjSgRsiShStsiWsbfPQ15sugh3bXS"
]
},
"vout_contract" : {
"contractData" : "",
"partyPubkey" : "02b95dc6db0733c3bd75be1d598470bd9fdc3c004e3981e92451a855511c49d2af"
}
}
],
"nType" : 100
}

测试 callcontract

1
callcontract '{"address":"1XK3LDVyRStHaXThmJP1awHsrm2u8ovS8DXMbe", "txid":"78a50477408c82831c941e576fd41b55e7e7a73e7388375e6d576004ea12f98d", "partySig":"sign_partyA", "actionName":"publishservice", "actionParams":"service A, 1"}' '{"contractData":"abcdef1234567890", "partyPubkey":"partyA"}' '[{"txid":"4026ce56d69e18a2e1bb93fb773536bff977ba10f9349ce658ee34bdff4a3e01","vout":0}]' '{"1CQ9gPjQLUnebtZh73hZv7uNDx1ihtQRUxVt5L":{"asset1":20}}'
1
callcontract '{"address":"1DpHe374npo7eotqMpcWT3DRW7m3Gdt577QrMZ", "txid":"91c743e88e50be7b36843a108b2316af9d549a9fb7c94fa320d5bd3e5d28dfa2", "partySig":"sign_partyA", "actionName":"publishservice", "actionParams":"service A, 1"}' '{"contractData":"abcdef1234567890", "partyPubkey":"partyA"}'

报错:Insufficient funds
Coin selection: No unspent outputs are available. Please send a transaction, with zero amount, to this node or address first and wait for its confirmation.

报错原因在 wallet/wallet.cpp SelectMultiChainCoins函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int preferred_row=in_preferred_row;
for(int attempt=0;attempt<2;attempt++)
{
if((SelectMultiChainCoinsMinConf(nTargetValue, 1, 6, vCoins, in_map, in_amounts, in_selected_row, in_asset_row, preferred_row, setCoinsRet, nValueRet) ||
SelectMultiChainCoinsMinConf(nTargetValue, 1, 1, vCoins, in_map, in_amounts, in_selected_row, in_asset_row, preferred_row, setCoinsRet, nValueRet) ||
(bSpendZeroConfChange && SelectMultiChainCoinsMinConf(nTargetValue, 0, 1, vCoins, in_map, in_amounts, in_selected_row, in_asset_row, preferred_row, setCoinsRet, nValueRet))))
{
return true;
}
if(in_preferred_row == 0)
{
return false;
}
preferred_row=0;
}

return false;

该循环进行了两次attemp都没有返回true,最终返回了false

函数调用栈:
wallet/wallet.cpp 3124
wallet/walletcoins.cpp 1370
wallet/walletcoins.cpp 2187
wallet/walletcoins.cpp 2412
wallet/wallet.cpp 2691
rpc/rpcsmartcontract.cpp 376

原因:传入的 scriptPubkeys 为空?
callcontractfrom 函数中加入合约公钥 scriptPubKeys.push_back(GetScriptForDestination(CBitcoinAddress(contractInfo.address).Get()));

wallet.cpp 2841行中会对in、out字段顺序进行洗牌算法,所以检测合约字段还应该使用 size()>0 的方式,用 index=size()-1 不靠谱。

setFromAddresses 里没有填充值

测试成功结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
"txid" : "0d33b3e18ed1bcbfabce46e10e7ec4ce60a7d3a7c686e1ff58b94c22005bbd9d",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "37a6156e39f50219b9868817be80ddb5425f10afe0c0878b7ea2103acc1240de",
"vout" : 1,
"scriptSig" : {
"asm" : "3045022100de1bfae39330545ca5f52455b203dde11292ff9e759425803fb429191ef7690802205d86108f9c43ce5923b78c03d4f35ac7234eca5ef6ce2cb1be26034a1fd7b6b001 027f51366ead7860f7a76ec288dcefa2c1af3b1e649f548a2d3999e73da1f5f6c0",
"hex" : "483045022100de1bfae39330545ca5f52455b203dde11292ff9e759425803fb429191ef7690802205d86108f9c43ce5923b78c03d4f35ac7234eca5ef6ce2cb1be26034a1fd7b6b00121027f51366ead7860f7a76ec288dcefa2c1af3b1e649f548a2d3999e73da1f5f6c0"
},
"vin_contract" : {
"address" : "1P3xtepz7jubi9bgXuUkPFmq1aKwajoxjPPDbt",
"txid" : "37a6156e39f50219b9868817be80ddb5425f10afe0c0878b7ea2103acc1240de",
"actionName" : "publishservice",
"actionParams" : "service A, 1",
"partySig" : "sign_partyA"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.00000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 a32788bf5f608fe791c5104773d0caa1711b12ef OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914a32788bf5f608fe791c5104773d0caa1711b12ef88ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1P3xtepz7jubi9bgXuUkPFmq1aKwajoxjPPDbt"
]
}
},
{
"value" : 0.00000000,
"n" : 1,
"scriptPubKey" : {
"asm" : "OP_CUSTOMDATA 0061626364656631323334353637383930",
"hex" : "ba110061626364656631323334353637383930",
"type" : "nulldata"
}
},
{
"value" : 0.00000000,
"n" : 2,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 a06beee2be7d64b78fe0af0fa3e22ac64a85ec38 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914a06beee2be7d64b78fe0af0fa3e22ac64a85ec3888ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1NgYPRT6AQjSgRsiShStsiWsbfPQ15sugh3bXS"
]
},
"vout_contract" : {
"contractData" : "abcdef1234567890",
"partyPubkey" : "partyA"
}
}
],
"nType" : 4294967295
}

验证结点的合约可通过stream订阅通知

系列测试

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
./src/multichain-cli contractchain createcontract test_name1 test_path1
{
"address" : "1J4f7mRLLbTUcq7qCdBepp14vKPGi1wUib7BgM",
"txid" : "7665e9cd7aaa1e38682a36872d04f80b09c7a00f15c27f8775e44b6029325a6a"
}


./src/multichain-cli contractchain callcontract '{"address":"1J4f7mRLLbTUcq7qCdBepp14vKPGi1wUib7BgM", "txid":"7665e9cd7aaa1e38682a36872d04f80b09c7a00f15c27f8775e44b6029325a6a", "partySig":"sign_partyA", "actionName":"publishservice", "actionParams":"service A, 1"}' '{"contractData":"abcdef1234567890", "partyPubkey":"partyA"}'

c01076277d5e9af42b23e94d4f13e637dd828842fb315606b02175ed63031f64

./src/multichain-cli contractchain grant 1J4f7mRLLbTUcq7qCdBepp14vKPGi1wUib7BgM receive,send

abc4698943ce8dc7ebaacab56ea3e1836b857acce3ea858dcd704c31814a6f82

./src/multichain-cli contractchain issue 1NgYPRT6AQjSgRsiShStsiWsbfPQ15sugh3bXS money 100 0.01

b0aa46fb512ea4845596dbf6847cda43415e89479c80100427f59702b14bd161


./src/multichain-cli contractchain callcontract '{"address":"1J4f7mRLLbTUcq7qCdBepp14vKPGi1wUib7BgM", "txid":"c01076277d5e9af42b23e94d4f13e637dd828842fb315606b02175ed63031f64", "partySig":"sign_partyA", "actionName":"payContract", "actionParams":"10"}' '{"contractData":"1234567890abcdef", "partyPubkey":"partyA"}' '[{"txid":"b0aa46fb512ea4845596dbf6847cda43415e89479c80100427f59702b14bd161","vout":0}]' '{"1J4f7mRLLbTUcq7qCdBepp14vKPGi1wUib7BgM":{"money":10}}'

e769977d0cf2d272c34ab75e93705795634447acfb7b3552c124b1d4b45e15db

2

  1. 创建合约

    1
    2
    3
    4
    5
    6
    ./src/multichain-cli contractchain createcontract test_name2 test_path2

    {
    "address" : "144Z8njQaUejZTviipyNaP5MirxVgYidW73xmK",
    "txid" : "b13c36a69a49d413978e1a76ceaae77a40c2b909751b3877cb27b6151346990a"
    }
  2. 初始化合约

    1
    2
    3
    ./src/multichain-cli contractchain callcontract '{"address":"144Z8njQaUejZTviipyNaP5MirxVgYidW73xmK", "txid":"b13c36a69a49d413978e1a76ceaae77a40c2b909751b3877cb27b6151346990a", "partySig":"sign_partyA", "actionName":"publishservice", "actionParams":"service A, 1"}' '{"contractData":"abcdef1234567890", "partyPubkey":"partyA"}'

    e14b40448c7961509f5a00abe366a804ba02e7b704d67d1d13f98a4aad61ea27
  3. 创建资产

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    ./src/multichain-cli contractchain listpermissions issue

    [
    {
    "address" : "1NgYPRT6AQjSgRsiShStsiWsbfPQ15sugh3bXS",
    "for" : null,
    "type" : "issue",
    "startblock" : 0,
    "endblock" : 4294967295
    }
    ]

    ./src/multichain-cli contractchain issue 1NgYPRT6AQjSgRsiShStsiWsbfPQ15sugh3bXS money2 100 0.01

    1298afdfd8146c2e95cbbbc524f7d69e4540a8ccc27079ab08a61c53ee924d93
  4. 合约转移资产

    1
    2
    3
    4
    5
    6
    7
    ./src/multichain-cli contractchain grant 144Z8njQaUejZTviipyNaP5MirxVgYidW73xmK receive,send

    8a7c066b680b2620671605f6de5e9560c050c65c2fdfeb21fa9eda513c23d71c

    ./src/multichain-cli contractchain callcontract '{"address":"144Z8njQaUejZTviipyNaP5MirxVgYidW73xmK", "txid":"e14b40448c7961509f5a00abe366a804ba02e7b704d67d1d13f98a4aad61ea27", "partySig":"sign_partyA", "actionName":"payContract", "actionParams":"30"}' '{"contractData":"1234567890abcdef", "partyPubkey":"partyA"}' '[{"txid":"1298afdfd8146c2e95cbbbc524f7d69e4540a8ccc27079ab08a61c53ee924d93","vout":0}]' '{"144Z8njQaUejZTviipyNaP5MirxVgYidW73xmK":{"money2":30}}'

    df6a461259f37ffff4d9625bae73c06d393aea11a738b6cfe02a796b81f2abd7
PAT 真题 | 1148 Werewolf - Simple version PAT 真题 | 1057 Stack

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×