以太坊合约部署总结

一、基本说明
以太坊目前还处于频繁更新的阶段,后续相关接口可能会有变化。当前本文对应以太坊的0.2x.x版本。

二、合约编译
编写好合约代码之后,需要将合约编译为字节码,并为了后续调用以及供其他人调用,还需要保存下ABI( Application Binary Interface)。不管是本地安装了solidify使用solc编译,还是在线编译( https://remix.ethereum.org ),都会得到两个数据,abi和bin,bin即为按16进制输出的二进制代码。

关于包含library库的合约的编译说明
如果你的合约使用了libraries,按正常方式编译后会发现字节码中会包含类似“__LibraryName________”的字符串。这是因为,在编译的时候编译器还不知道你所使用的库的地址,所以这里提供了占位字符串。对于这种情况,需要我们人工指定库部署在链上的地址。
也就是:
1、部署合约前,需要先部署好需要用到的库(其实每个库也都是一个合约,只是它比较特殊而已),获取库地址。
2、编译合约时,可以通过指定 --libraries “LibraryName:libraryaddress” 参数来提供库地址,多个“库名:库地址”对之间可以用逗号或空格分隔,如
--libraries "IterableMapping:0xf912c00b0c9ec43c0171a42c276e445f7a186f69”;库地址的前导0x可有可无。
3、当然编译时不指定--libraries参数也可以,这样可以在编译完成后,人工将占位字符串替换为库地址。这种操作下,库地址就是不包含0x的了,如用“f912c00b0c9ec43c0171a42c276e445f7a186f69”完整替换占位字符串。

三、合约部署
合约部署有两种方式,通过web3控制台部署和编程通过RPC部署。
(一)web3控制台部署合约
注意:部署前需要解锁账户。以下假定账户已经解锁。
1、指定abi
形式:
abi = web3.eth.contract(abijson)
例如:
abi = web3.eth.contract([{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function”}])


2、使用new方法部署合约
形式:
abi.new([constructorParam1] [, constructorParam2], {data: '0x12345...', from: myAccount, gas: 1000000},callback);

合约构造函数参数,依序写在new方法的前面部分,若没有构造函数参数,就不用传。之后大括号{}括起来的是合约数据,data即为编译好的bin,from为创建合约的账户,其他相关字段与发送Transaction的字段相同,但是创建合约不能带to字段;最后部分为部署合约的回调函数,可在回调函数中做部署完成后的处理。

示例:
co = abi.new(
   {
     from: web3.eth.accounts[0], 
     data: '0x6060604052341561000f57600080fd5b60d38061001d6000396000f3006060604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c14606e575b600080fd5b3415605857600080fd5b606c60048080359060200190919050506094565b005b3415607857600080fd5b607e609e565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203f073b26521b71d3298f38d48d6d13cea0331654cc49f4754094cdb870e20e5b0029', 
     gas: '4700000'
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
    }
 })


部署成功并被打包到区块中之后,将会看到类似如下输出:
Contract mined! address: 0x3e789ab8b916317ee64f33d2b03b44e2d41ade4b transactionHash: 0xc70ac1d1afeb622abda6215e767123a1c38edeba5ec6c635b02266879fabc185

同时,该合约被赋值给了变量co,可以使用co查看合约的信息、调用合约的函数。

若没有提供回调函数,可以通过自行查看合约的transactionHash和address属性来查看状态
co.transactionHash // 创建合约的交易哈希
co.address // 合约账户地址,一开始是未定义的,但后续(部署成功后)会被自动填充。

3、与合约交互
对已部署在链上的合约,可以与之进行交互。
在上述步骤部署好合约后,可以直接通过变量co进行交互。
若是重启了节点或与其他人部署的合约进行交互,可以使用 at方法获取已部署在链上的合约,如下:
abi = web3.eth.contract([{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function”}])

co = abi.at("0x3e789ab8b916317ee64f33d2b03b44e2d41ade4b”)

合约函数调用,有两种方式:sendTransaction和call,其中前者是真正发起交易更改合约存储状态的,结果会持久反映到区块链中。call只在本地执行。详见 http://ethdoc.cn/contracts-and-transactions/contracts.html#id17 的“合约交互”部分
当使用 call 时,函数会在本地的虚拟机(EVM)上执行,调用的返回值就是函数的返回值。这种方式的调用不会被纪录在区块链中,因此也不会改变合约的内部状态,这种方式被称为常量函数调用。这种调用方式不需要手续费。

形式:
co.functionName.Call(arg0,arg1,{from:account})
co.functionName.SendTransaction(arg0,arg1,{from:account})
对于不需要参数的call调用,可以简化为
co.functionName()

示例:
co.set.call(100,{from:"0x55b3f3f1ec65973d1f5cdb515fb9c761d3122179"})

co.set.sendTransaction(100,{from:"0x55b3f3f1ec65973d1f5cdb515fb9c761d3122179"});

co.get.call()
co.get()

说明:合约的public状态变量,会自动提供getter方法,例如如下的合约:
contract Token {
    /// total amount of tokens
    uint256 public totalSupply ;
……
}
会自动提供一个名为totalSupply的无参数function。

(二)以RPC方式编程部署合约及于合约交互
1、使用RPC方式编程部署合约,主要使用的RPC接口是sendTransaction。
对于部署合约,可以使用 personal_sendTransaction或eth_sendTransaction,前者可以提供密码参数,从而不需要账户先解锁,后者需要账户先解锁。
注意:对于与合约交互,只能使用eth_sendTransaction,对于需要改变合约状态的调用,需要先解锁账户。

合约部署时,transaction对象的data字段为编译好的合约字节码,to字段留空,其他字段含义与发送普通交易相同。(to字段留空表示创建合约,data字段有内容就会启动虚拟机执行)。

基本说明参考: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendtransaction
需要注意的是:
对于构造函数需要传入参数的合约部署,需要将参数进行编码,将编码后的数据直接跟在合约代码(编译后的16进制表示的字节码)后面。合约参数编码方式,参考: https://solidity-cn.readthedocs.io/zh/develop/abi-spec.html#abi
但是不需要构造函数的函数签名哈希的前四个字节。也就是直接考虑参数编码即可。

2、与合约交互
使用eth_sendTransaction与合约进行交互,transaction的to字段为合约地址,data字段为函数签名及参数的编码结果。
编码方式参考: https://solidity-cn.readthedocs.io/zh/develop/abi-spec.html#

注意:计算函数签名的哈希时,参数类型如果涉及到类型别名,则需要使用其权威类型代替。
例如以下函数
function insert( uint k, uint v) public returns ( uint)
{
    ……
}
待计算哈希的函数签名应该为" insert(uint256,uint256)
别名与权威表示列表如下:

别名 权威表示
byte
bytes1
uint
uint256
int
int256
ufixed
ufixed128x19
fixed
fixed128x19
另:address和uint160表现相同,但是在计算函数签名时直接使用address,如”balanceOf(address)”。编码时是左边补0。
var addr common.Address = common.HexToAddress("0x71af77518da8ee1e152068ea4727d1041d71b813”)
addrBytes := addr.Hash().Bytes()

你可能感兴趣的:(区块链-以太坊)