以太坊智能合约部署与交互
时间: 2018-05-01来源:OSCHINA
前景提要
「深度学习福利」大神带你进阶工程师,立即查看>>>
启动容器来执行geth命令 root @ubu-blockchain2:~# docker run -i blockchain101/ethereum-geth: 1.6 .5 geth attach http:// 45.32 .252 .88 : 8201 Welcome to the Geth JavaScript console! instance : Geth /01/v1.6.5-stable/linux-amd64/go1.8 coinbase: 0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a at block: 6064 ( Wed , 02 Aug 2017 01:13:50 UTC ) datadir: /ethcluster/779977/data/01 modules: admin:1.0 eth:1.0 net:1.0 rpc:1.0 web3:1.0
查看我们的账户和余额 eth .getBalance (eth .accounts [ 0 ]) 11000000000000000000 eth .getBalance (eth .accounts [ 1 ]) 0 eth .accounts [ 0 ] "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a" > eth .accounts [ 1 ] "0xe82e2f0a5abd8774767b9751659976f9c4f59181"
发起一笔交易 eth .sendTransaction ({from: eth.accounts[ 0 ],to:eth.accounts[ 1 ],value:web3. toWei( 3 , 'ether' ) }) "0 x0075da712d26aea17d6647035107f509e13eaf3d113c1577db14d4cc4216caec "
查看交易细节 > eth .getTransaction ("0 x0075da712d26aea17d6647035107f509e13eaf3d113c1577db14d4cc4216caec ") { blockHash: "0x3115703894dc6015c96ef4de3e5615f416498ca1f985902b38cd70e27dab8871" , blockNumber: 1250 , from: "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a" , gas: 90000 , gasPrice: 18000000000 , hash: "0x0075da712d26aea17d6647035107f509e13eaf3d113c1577db14d4cc4216caec" , input: "0x" , nonce: 0 , r: "0x2aef2c1fa03a0fa4172d21e3383d8c0431d45ec855b9d16efdd5eb2de90c414c" , s: "0xc4d530fb7902bf509fe56bfbea4861bf6cc16791afc9c9103c1a18f77407d1f" , to: "0xe82e2f0a5abd8774767b9751659976f9c4f59181" , transactionIndex: 0 , v: "0x17cdb6" , value: 3000000000000000000 } > eth .getBalance ( eth .accounts [1] ) 3000000000000000000
验证用户0的余额 > eth .getBalance (eth .accounts [ 0 ]) 7999622000000000000
编写一个简单的合约 contract Sample { uint public value ; function Sample( uint v){ value =v; } function set ( uint v){ value =v; } function get () constant returns ( uint ){ return value ; } }
在 remix 网页编译得到ABI接口和合约的二进制代码、 abi=[{ "constant" : true , "inputs" :[], "name" : "value" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" : false , "type" : "function" },{ "constant" : false , "inputs" :[{ "name" : "v" , "type" : "uint256" }], "name" : "set" , "outputs" :[], "payable" : false , "type" : "function" },{ "constant" : true , "inputs" :[], "name" : "get" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" : false , "type" : "function" },{ "inputs" :[{ "name" : "v" , "type" : "uint256" }], "payable" : false , "type" : "constructor" }] [{ constant: true , inputs: [], name: "value" , outputs: [{ name: "" , type: "uint256" }], payable: false , type: "function" }, { constant: false , inputs: [{ name: "v" , type: "uint256" }], name: "set" , outputs: [], payable: false , type: "function" }, { constant: true , inputs: [], name: "get" , outputs: [{ name: "" , type: "uint256" }], payable: false , type: "function" }, { inputs: [{ name: "v" , type: "uint256" }], payable: false , type: "constructor" }]
需要使用eth.contract来定义一个合约类 > sample=eth.contract(abi) { abi: [{ constant: true , inputs: [], name: "value" , outputs: [{...}], payable: false , type : "function" }, { constant: false , inputs: [{...}], name: "set" , outputs: [], payable: false , type : "function" }, { constant: true , inputs: [], name: "get" , outputs: [{...}], payable: false , type : "function" }, { inputs: [{...}], payable: false , type : "constructor" }], eth: { accounts: [ "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a" , "0xe82e2f0a5abd8774767b9751659976f9c4f59181" ], blockNumber: 6225 , coinbase: "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a" , compile: { lll: function () , serpent: function () , solidity: function () }, defaultAccount: undefined, defaultBlock: "latest" , gasPrice: 18000000000 , hashrate: 0 , mining: false , pendingTransactions: [], protocolVersion: "0x3f" , syncing: false , call: function () , contract: function (abi) , estimateGas: function () , filter: function (fil, callback) , getAccounts: function (callback) , getBalance: function () , getBlock: function () , getBlockNumber: function (callback) , getBlockTransactionCount: function () , getBlockUncleCount: function () , getCode: function () , getCoinbase: function (callback) , getCompilers: function () , getGasPrice: function (callback) , getHashrate: function (callback) , getMining: function (callback) , getPendingTransactions: function (callback) , getProtocolVersion: function (callback) , getRawTransaction: function () , getRawTransactionFromBlock: function () , getStorageAt: function () , getSyncing: function (callback) , getTransaction: function () , getTransactionCount: function () , getTransactionFromBlock: function () , getTransactionReceipt: function () , getUncle: function () , getWork: function () , iban: function (iban) , icapNamereg: function () , isSyncing: function (callback) , namereg: function () , resend: function () , sendIBANTransaction: function () , sendRawTransaction: function () , sendTransaction: function () , sign: function () , signTransaction: function () , submitTransaction: function () , submitWork: function () }, at: function (address, callback) , getData: function () , new: function () }
合约的二进制代码赋值给SampleHEX方便使用 SampleHEX= "0x6060604052341561000c57fe5b60405160208061013a833981016040528080519060200190919050505b806000819055505b505b60f9806100416000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f24514604e57806360fe47b11460715780636d4ce63c14608e575bfe5b3415605557fe5b605b60b1565b6040518082815260200191505060405180910390f35b3415607857fe5b608c600480803590602001909190505060b7565b005b3415609557fe5b609b60c2565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b600060005490505b905600a165627a7a72305820208c8101070c8ba5a9b32db2bf4b8062a9ba50bc2869c39ac2297938756540e80029"
把合约代码部署上链 > thesample=sample.new( 1 ,{from:eth.accounts[ 0 ],data:SampleHEX,gas: 3000000 }) { abi: [{ constant: true, inputs: [], name: "value" , outputs: [{ ... }], payable: false, type: "function" }, { constant: false, inputs: [{ ... }], name: "set" , outputs: [], payable: false, type: "function" }, { constant: true, inputs: [], name: "get" , outputs: [{ ... }], payable: false, type: "function" }, { inputs: [{ ... }], payable: false, type: "constructor" }], address: undefined, transactionHash: "0xee74bcb4461c9712ec9aca96a5a3a4c3c64be1213854d519fc8e5432b554f7a1" }
查看交易细节 > samplerecpt=eth.getTransactionReceipt( "0xee74bcb4461c9712ec9aca96a5a3a4c3c64be1213854d519fc8e5432b554f7a1" ) { blockHash: "0xddba16545af882835fb9a69a0e5f3b9287c61664837d5ea0068b38575cb665c5" , blockNumber: 6246 , contractAddress: "0x7504fa9d64ab290844b82660d43b310f8fba0276" , cumulativeGasUsed: 141836 , from : "0x4c57e7e9c2f728046ddc6e96052056a241bdbd0a" , gasUsed: 141836 , logs: [], logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" , root: "0xd1093ecaca9cc0d10e82a533a15feccedf7ff5c79fb3ebd9366ec0b35dbef478" , to : null , transactionHash: "0xee74bcb4461c9712ec9aca96a5a3a4c3c64be1213854d519fc8e5432b554f7a1" , transactionIndex: 0 }
合约命名 > samplecontract=sample.at( "0x7504fa9d64ab290844b82660d43b310f8fba0276" ) { abi: [{ constant: true, inputs: [], name: "value" , outputs: [{ ... }], payable: false, type: "function" }, { constant: false, inputs: [{ ... }], name: "set" , outputs: [], payable: false, type: "function" }, { constant: true, inputs: [], name: "get" , outputs: [{ ... }], payable: false, type: "function" }, { inputs: [{ ... }], payable: false, type: "constructor" }], address: "0x7504fa9d64ab290844b82660d43b310f8fba0276" , transactionHash: null, allEvents: function (), get: function (), set: function (), value: function () }

合约查看功能函数get(),然后调用set()函数,再get()查看时已经改变了 samplecontract .get .call () 1 > samplecontract .set .sendTransaction ( 9 , {from:eth .accounts [ 0 ], gas: 3000000 }) "0x822ee6fb4caceb7e844c533f7f3bc57806f7cb3676fb3066eb848cca46b2f38a" > samplecontract .get .call () 9
我们再打开一个终端,打开cluster1的peer02的控制台,直接at到上一个终端部署的智能合约地址并进行set操作 root @ubu - blockchain2: ~ /ethereum-docker/ethereum -docker/ethereum-testnet-docker/dockercomposefiles # docker run -i blockchain101/ethereum-geth:1.6.5 geth attach http://45.32.252.88:9201 Welcome to the Geth JavaScript console! > abi=[{ "constant" :true , "inputs" :[] , "name" : "value" , "outputs" : [{ "name" : "" , "type" : "uint256" }], "payable" :false , "type" : "function" },{ "constant" :false , "inputs" : [{ "name" : "v" , "type" : "uint256" }], "name" : "set" , "outputs" :[] , "payable" :false , "type" : "function" },{ "constant" :true , "inputs" :[] , "name" : "get" , "outputs" : [{ "name" : "" , "type" : "uint256" }], "payable" :false , "type" : "function" },{ "inputs" : [{ "name" : "v" , "type" : "uint256" }], "payable" :false , "type" : "constructor" }] > sample=eth.contract(abi) SampleHEX = "0x6060604052341561000c57fe5b60405160208061013a833981016040528080519060200190919050505b806000819055505b505b60f9806100416000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f24514604e57806360fe47b11460715780636d4ce63c14608e575bfe5b3415605557fe5b605b60b1565b6040518082815260200191505060405180910390f35b3415607857fe5b608c600480803590602001909190505060b7565b005b3415609557fe5b609b60c2565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b600060005490505b905600a165627a7a72305820208c8101070c8ba5a9b32db2bf4b8062a9ba50bc2869c39ac2297938756540e80029"
直接把合约地址赋值并进行set操作 samplecontract=sample .at ( "0x7504fa9d64ab290844b82660d43b310f8fba0276" ) > samplecontract .get .call () 9
简单的方法来了。智能合约的部署需要编译,这里用在线编译:
https://ethereum.github.io/browser-solidity/#version=soljson-v0.4.14+commit.c2215d46.js
修改编译好的abi和对象名称: 这里在网上找了个代币的只能合约,可以进行充值、转账和查询,issue 函数可以向充值以太到合约账户,transfer 函数可以向其他账号发送token,getBalance 函数可以获取某个账号的token余额,代码如下: pragma solidity ^ 0.4 .2 ; contract Token { address issuer; mapping (address => uint) balances; event Issue(address account, uint amount); event Transfer(address from, address to,uint amount); function Token () { issuer = msg.sender; } function issue (address account, uintamount) { if (msg.sender != issuer) throw ; balances[account] += amount; } function transfer (address to, uint amount) { if (balances[msg.sender] < amount) throw ; balances[msg.sender] -= amount; balances[to] += amount; Transfer(msg.sender, to, amount); } function getBalance (address account) constant returns (uint) { return balances[account]; } } 修改编译好的gas和对象名称: varbrowser_untitled_sol_tokenContract =web3.eth.contract([{ "constant" : false , "inputs" :[{ "name" : "account" , "type" : "address" },{ "name" : "amount" , "type" : "uint256" }], "name" : "issue" , "outputs" :[], "payable" : false , "type" : "function" },{ "constant" : false , "inputs" :[{ "name" : "to" , "type" : "address" },{ "name" : "amount" , "type" : "uint256" }], "name" : "transfer" , "outputs" :[], "payable" : false , "type" : "function" },{ "constant" : true , "inputs" :[{ "name" : "account" , "type" : "address" }], "name" : "getBalance" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" : false , "type" : "function" },{ "inputs" :[], "payable" : false , "type" : "constructor" },{ "anonymous" : false , "inputs" :[{ "indexed" : false , "name" : "account" , "type" : "address" },{ "indexed" : false , "name" : "amount" , "type" : "uint256" }], "name" : "Issue" , "type" : "event" },{ "anonymous" : false , "inputs" :[{ "indexed" : false , "name" : "from" , "type" : "address" },{ "indexed" : false , "name" : "to" , "type" : "address" },{ "indexed" : false , "name" : "amount" , "type" : "uint256" }], "name" : "Transfer" , "type" : "event" }]); var token =browser_untitled_sol_tokenContract. new ( { from: web3.eth.accounts[ 0 ], data: '0x6060604052341561000f57600080fd5b5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6103d2806100616000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063867904b414610054578063a9059cbb14610096578063f8b2cb4f146100d8575b600080fd5b341561005f57600080fd5b610094600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610125565b005b34156100a157600080fd5b6100d6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101d2565b005b34156100e357600080fd5b61010f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061035c565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561018057600080fd5b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b5050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561021e57600080fd5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055507fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef338383604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a15b5050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b9190505600a165627a7a723058204afe007a03446d43d13ac892e6dba9d032f540a11ff427d26c22560727cbea2f0029' , gas: '4300000' }, function (e, contract) { console.log(e, contract); if ( typeof contract.address !== 'undefined' ) { console.log( 'Contract mined! address:' + contract.address + ' transactionHash: ' + contract.transactionHash); } })
此时输入合约部署的实例a_demotypes, 可以看到a_demotypes的详情。
控制台调用 > a_demotypes { abi: [{ constant: false, inputs: [{ ... }], name: "f" , outputs: [{ ... }], payable: false, type: "function" }], address: "0x54ed7a5f5a63ddada3bfe83b3e632adabaa5fc2f" , transactionHash: "0x69cde62bcd6458e14f40497f4840f422911d63f5dea2b3a9833e6810db64a1c9" , allEvents: function (), f: function () }
这里重点就是address表示的是合约的地址,你会发现这个和账号的信息结果一样,其实你也可以把这个合约地址看做是一个账号地址,后面我们外部调用到的就是这个合约地址。 充值 token .issue .sendTransaction ( eth .accounts [0] ,100, {from: eth.accounts[ 0 ] }); 发送 token token .transfer ( eth .accounts [1] , 30, {from: eth.accounts[ 0 ] }) 查看余额 token .getBalance () 控制台调用就不多说,和 Java 对象调用一样,直接调用即可
外部接口与智能合约交互
以太坊对外提供的有很多接口JSON RPC接口,web3接口,这里我们用JSON RPC接口。
相关API: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendrawtransaction
合约交互的原理
合约的交互都是一次交易,而我们要做的就是把要调用的方法和参数按照api规定的以参数的形式向区块请求一次交易,ethereum接收到我们的请求后通过解析传递的参数来执行相关的合约代码。
RPC接口给我们提供了俩个方法: eth_sendTransaction和eth_call。 eth_sendTransaction Createsnew message call transaction or a contract creation, if the data field containscode. Parameters Object - The transaction object from : DATA, 20 Bytes - The address the transaction is send from . to : DATA, 20 Bytes - ( optional when creating new contract) The address the transaction is directed to . gas: QUANTITY - ( optional , default : 90000 ) Integer of the gas provided for the transaction execution. It will return unused gas. gasPrice: QUANTITY - ( optional , default : To -Be-Determined) Integer of the gasPrice used for each paid gas value: QUANTITY - ( optional ) Integer of the value send with this transaction data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI nonce: QUANTITY - ( optional ) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.
可以看到,如果我们创建的为合约时,我们只需要from,to(文档上写的是可选的,但是实际操作中没有to为null的话合约不能正常执行,建议还是加上,这个值就是前面我们部署合约后生成的合约address),data。Data的属性的值具体可以看Contract ABI。这里大概说下:
Data的值相对来说不是固定的,具体怎么生成与合约的参数类型,参数数量都有关联。这里我们以部署的token合约的三个方法为例:
充值issue (address account, uint amount)
这个方法有俩个参数,address充值账号,uint充值数量。
根据Contract ABI,data值应该为方法名的sha3的前8个字节+参数的64字节,不够前面补充为0。
这里方法名并不是issue (address account, uint amount)而是issue(address,uint256)的sha3值。 web3 .sha 3( "issue(address,uint256)" ) "0x867904b44b606800f6f10498e11292d04ea19bfc7fe4bc0f1695aa516381f73d"

我们往第一个账号充值10,这里的数据不是以太币,而是我们自己创建的代币。 eth .accounts [ 0 ] "0x0cc9684af605bae10de801218321c1336bb62946"

将10转换为16进制为 000000000000000000000000000000000000000000000000000000000000000 a

那么data的数据为: 0x867904b4000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb000000000000000000000000000000000000000000000000000000000000000a

那么最后我们调用eth_sendTransaction方法传递参数JSON对象为: { from: 0 xfdf57e81872562a6112656f961944ce821fdf7eb, to: 0 x7fe133950fc010ce41322d88f64f1918b9abb3a3, data: 0 x867904b4000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb000000000000000000000000000000000000000000000000000000000000000a }

返回:此交易的hash值,此时该交易还没有执行,只是创建,还需要矿工通过挖矿才能完成。
调用接口方法: JsonRpcHttpClient client = newJsonRpcHttpClient( new URL(“http: //127.0.0.1:8545”)); Object result = client.invoke(methodName, params , Object. class );

通过控制台查看第一个账号已有代币: token .getbalance (eth .accounts [ 0 ])

执行调用接口代码。返回交易hash值: 0x2013764d1c3fea680f9015353497aa5f9f8d61580a3bd0524b3613b34329c095

此时控制台输入:

交易和充值一样,需要注意的是代币转出账号为from属性的值,代币转入账号为data属性里的值,to对应的是合约地址。
eth_call Executes anew message call immediately without creating a transaction on the block chain. Parameters Object - The transaction call object from : DATA, 20 Bytes - (optional) The address the transaction is sent from . to : DATA, 20 Bytes - The address the transaction is directed to . gas: QUANTITY - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions. gasPrice: QUANTITY - (optional) Integer of the gasPrice used for each paid gas value: QUANTITY - (optional) Integer of the value send with this transaction data: DATA - (optional) Hash of the method signature and encoded parameters. For details seeEthereum Contract ABI QUANTITY|TAG - integer block number, or the string "latest" , "earliest" or "pending" , see the default block parameter
这个方法返回一条信息给我们,相当于数据库的查询功能,参数也是三个,from,to,data,数据格式都是一样的。
查询getBalance(address account)
查询方法hash码: web3 .sha 3( "getBalance(address)" ) "0xf8b2cb4f3943230388faeee074f1503714bff212640051caba01411868d14ae3"
查询我们上一步充值的账号,那么传递的参数data为: 0xf8b2cb4f000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb
eth_call方法最后参数为: { from: 0 xfdf57e81872562a6112656f961944ce821fdf7eb, to: 0 x7fe133950fc010ce41322d88f64f1918b9abb3a3, data: 0 xf8b2cb4f000000000000000000000000fdf57e81872562a6112656f961944ce821fdf7eb }
注意,这个方法需要俩参数,处理一个JSONobject外,还有一个字符串参数,这俩可以为“”或者”latest”, “earliest” or “pending”
调用接口返回一个16进制字符串:
0x0000000000000000000000000000000000000000000000000000000000000071就是该账号的代币数量,转换为十进制为:113,与控制查询一致。
这就是一个智能合约的交互过程。是不是很简单啊。
转自:https://blog.csdn.net/ddffr/article/details/76549320
安利两个实战教程:
1. 适合区块链新手的以太坊DApp开发
2. 用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

热门排行