智能合约的部署与执行过程之代码解析

智能合约的部署与执行过程之代码解析:http://blockgeek.org/t/topic/1623
一、编写合约
合约示例,一个简单的做加法的合约

pragma solidity ^0.4.7;
contract addcontract {
        function add(uint a,uint b) returns(uint d) {
                      return a + b;
        }
 }

二、编译合约
通过remix编译可以获得abi与data如下:
abi =[{“constant”:false,“inputs”:[{“name”:“a”,“type”:“uint256”},{“name”:“b”,“type”:“uint256”}],“name”:“add”,“outputs”:[{“name”:“d”,“type”:“uint256”}],“payable”:false,“stateMutability”:“nonpayable”,“type”:“function”}]
data=“0x6080604052348015600f57600080fd5b50609d8061001e6000396000f300608060405260043610603e5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663771602f781146043575b600080fd5b348015604e57600080fd5b50605b600435602435606d565b60408051918252519081900360200190f35b01905600a165627a7a72305820f6266436f5e17e3985b58d99a115b831979da26bec9abec3f8db4082fe369fa10029”

三、部署合约
addContract = hpb.contract(abi)
addcontract = addContract.new({from:hpb.coinbase,data:data,gas:4700000}) ,返回交易hash:“0x7a9e50ee550d909f9d8fbdc849dc2bddcdd2485ffafb5eeb52c1ebbff68d1802”
至此,可以在txpool中看到有一条交易,挖矿打包后,合约部署成功
可通过addcontract.address查看合约地址,
也可通过交易hash查看:hpb.getTransactionReceipt(“0x7a9e50ee550d909f9d8fbdc849dc2bddcdd2485ffafb5eeb52c1ebbff68d1802”)

4、调用合约
合约部署完毕,调用合约
myContract = addContract.at(addcontract.address) // 或者这里直接使用地址
myContract.add.sendTransaction(1,2,{from:hpb.coinbase}) //返回交易hash :“0x4298e32d61c4a2447d862295cefdef0bc89490df65dff06f20a57c1962d7d84e”
此时可以看到txpool中有一条交易。
另外也可以通过myContract.add.call(1,2)来直接调用合约,但不产生交易,而是直接返回结果3。

五、代码分析
那么问题就来了,通过sendTransaction接口发送的交易并没有返回结果,那能找到该交易产生的结果吗?
下面从代码角度看一下通过sendTransaction接口执行的1+2后的结果在哪里。
直接看合约call的部分代码evm.go中的call
1、先判余额是否是否满足本次转账要求,满足的话先进行转账,根据input然后执行合约run
2、在run中,重点在for循环中,先通过op = contract.GetOp(pc) 获取操作码,操作码及其对应函数的定义在jump_table.go中
3、然后通过operation := in.cfg.JumpTable[op]获取对应的操作函数,并进行stack的校验
4、然后res, err := operation.execute(&pc, in.evm, contract, mem, stack) 获取操作的返回结果,res存储了返回结果
5、如果当前操作的operation.returns是true则将res存储到in.returnData中。
6、没有错误的情况继续下一个操作,转到第2步

通过上面的代码运行可以,返回的结果在res中,而这个结果在交易中是不进行保存的。所以通过sendTransaction发起的交易是无法查询结果的。
因此在实际应用中,通常是使用sendTransaction去设置变量的值并打包进区块中,然后通过call来调用进行相应变量的数据处理并返回结果。

六、合约示例
根据上述总结,比如合约是这样的

pragma solidity ^0.4.25;
contract mytest{
    uint public a;
    function seta(uint ina) public{
        a = ina;
    }
    
    function geta()public returns (uint b){
        return a;
    }
}

在实际合约调用的时候可以通过mytest.seta.sendTransaction(123,{from:coinbase})对a进行赋值,然后通过mytest.geta.call()来获取a的值

七、合约操作码
合约在执行的时候既然是根据操作码进行执行的,那从哪里获取操作码呢?
回到在remix编译合约的时候,查看ASSEMBLY 部分的内容,即汇编代码就是上面描述的操作码;
其中code部分的操作码是在合约部署的时候运行的,即合约部署过程;
data部分的操作码是在调用合约的时候运行的,即合约执行过程。
代码中操作码及其对应函数的定义在jump_table.go中。

你可能感兴趣的:(区块链)