这里研究一下通过rpc发布和调用以太坊合约的过程(windows10环境)
1、准备工作
1.1、启动geth,创建一个专门用来发布合约的账户
参考2018-11-22 Debug以太坊go-ethereum实战 启动geth,并attach一个geth执行创建账户的命令:
代码语言:javascript复制geth --datadir "D:gopathsrcgithub.comethereumgeth-data0" --networkid 1314 --nodiscover --rpcaddr "127.0.0.1" --rpcport 8545 --rpc --rpcapi "db,eth,net,web3" --port 30001 --ipcpath geth1.ipc console --allow-insecure-unlock
新建一个终端
代码语言:javascript复制geth attach ipc:\.pipegeth1.ipc
personal.newAccount("123")
获得账户地址:"0x80284209f81e620ce62e40cd969906ed07b87c50" 给其转账10个eth
代码语言:javascript复制personal.unlockAccount(eth.accounts[0])
eth.sendTransaction({from:eth.accounts[0],to:"0x80284209f81e620ce62e40cd969906ed07b87c50",value:web3.toWei(10,"ether")})
miner.start(1);admin.sleepBlocks(1);miner.stop();
eth.getBalance("0x80284209f81e620ce62e40cd969906ed07b87c50")
获得账户余额:10000000000000000000
1.2、安装nodejs需要的库
代码语言:javascript复制cd temp
mkdir web3test
cd web3test
npm install keythereum
npm install ethereumjs-tx
1.3、还需要安装postman来执行rpc
2、发布合约
发布合约使用的rpc是eth_sendRawTransaction,但是参数是签名字符串,这里需要用nodejs代码实现签名 合约代码如下:
代码语言:javascript复制pragma solidity ^0.4.25;
contract helloworld {
string content;
function setContent(string memory _str) public {
content = _str;
}
function getContent() view public returns (string memory){
return content;
}
}
通过remix获取web3的js
代码语言:javascript复制var helloworldContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"_str","type":"string"}],"name":"setContent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getContent","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]);
var helloworld = helloworldContract.new(
{
from: web3.eth.accounts[0],
data: '0x608060405234801561001057600080fd5b506102d7806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633be8fd6a1461005157806359016c79146100ba575b600080fd5b34801561005d57600080fd5b506100b8600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061014a565b005b3480156100c657600080fd5b506100cf610164565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010f5780820151818401526020810190506100f4565b50505050905090810190601f16801561013c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060009080519060200190610160929190610206565b5050565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101fc5780601f106101d1576101008083540402835291602001916101fc565b820191906000526020600020905b8154815290600101906020018083116101df57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a7230582063b1c8ec79802b75b1777bd30256814a628ddb908967a3dbd2597a032f52257c0029',
gas: '4700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' contract.address ' transactionHash: ' contract.transactionHash);
}
})
构造eth_sendTransaction,仅需要改使用上面的data填充下面的data,
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 171,
"method": "eth_sendTransaction",
"params": [
{
"data": "0x608060405234801561001057600080fd5b506102d7806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633be8fd6a1461005157806359016c79146100ba575b600080fd5b34801561005d57600080fd5b506100b8600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061014a565b005b3480156100c657600080fd5b506100cf610164565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010f5780820151818401526020810190506100f4565b50505050905090810190601f16801561013c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060009080519060200190610160929190610206565b5050565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101fc5780601f106101d1576101008083540402835291602001916101fc565b820191906000526020600020905b8154815290600101906020018083116101df57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a7230582063b1c8ec79802b75b1777bd30256814a628ddb908967a3dbd2597a032f52257c0029",
"from": "0x80284209f81e620ce62e40cd969906ed07b87c50",
"gas": "0x47b760"
}
]
}
放在postman中执行,post给http://127.0.0.1:8545,注意设置body为raw,选择JSON(application) 获得执行结果
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 171,
"error": {
"code": -32000,
"message": "authentication needed: password or unlock"
}
}
需要先去geth出解锁
代码语言:javascript复制personal.unlockAccount("0x80284209f81e620ce62e40cd969906ed07b87c50")
然后重新执行,获得结果
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 171,
"result": "0xcd38e4c93cb9ce1971f25ba488c7c8f6600840d569ce7d3307604bf0ca0d6eb6"
}
由于这个发起了交易,还需要去geth出挖矿,才能执行完成
代码语言:javascript复制miner.start(1);admin.sleepBlocks(1);miner.stop();
查询执行结果:
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 2192787296,
"method": "eth_getTransactionReceipt",
"params": [
"0xcd38e4c93cb9ce1971f25ba488c7c8f6600840d569ce7d3307604bf0ca0d6eb6"
]
}
返回:
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 2192787296,
"result": {
"blockHash": "0xe01ebc2eb6214c8c82f20331fd6dbba34608afe7c6dd3e9772d81be75fb4bebf",
"blockNumber": "0x1872",
"contractAddress": "0xb9c4e02ab322fefb26ec422b07d9cdacb9036245",
"cumulativeGasUsed": "0x3c19a",
"from": "0x80284209f81e620ce62e40cd969906ed07b87c50",
"gasUsed": "0x3c19a",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"root": "0x5e4af4ef2e96abf728e3fabee5dcdda6ddbc25f484938ed110a446fa1df12474",
"to": null,
"transactionHash": "0xcd38e4c93cb9ce1971f25ba488c7c8f6600840d569ce7d3307604bf0ca0d6eb6",
"transactionIndex": "0x0"
}
}
获得合约地址:0xb9c4e02ab322fefb26ec422b07d9cdacb9036245
3、调用合约
3.1、调用设置函数
由于合约函数调用需要编码,这里使用myencode.js编码,其中的abi依然来自于remix
代码语言:javascript复制const Web3 = require('web3');
const web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");
// example of encode a function call
let abi = {
"constant": false,
"inputs": [
{
"name": "_str",
"type": "string"
}
],
"name": "setContent",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
};
let funCall = web3.eth.abi.encodeFunctionCall(abi, ['123']);
console.log(funCall);
获得: 0x3be8fd6a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033132330000000000000000000000000000000000000000000000000000000000
构造eth_sendTransaction,只需要用这个返回值替换data,to替换为合约地址
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 171,
"method": "eth_sendTransaction",
"params": [
{
"data": "0x3be8fd6a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033132330000000000000000000000000000000000000000000000000000000000",
"from": "0x80284209f81e620ce62e40cd969906ed07b87c50",
"to": "0xb9c4e02ab322fefb26ec422b07d9cdacb9036245",
"gas": "0x47b760"
}
]
}
获得结果:
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 171,
"result": "0x58e6e328d48b3c66481b631ec9015e88a247bd36a9565643203b27d0f7bc2c85"
}
由于这个发起了交易,还需要去geth出挖矿,才能执行完成
代码语言:javascript复制miner.start(1);admin.sleepBlocks(1);miner.stop();
3.2、调用读取函数
直接调用eth_call就可以了,下面的data来自于remix的detail里面的
代码语言:javascript复制FUNCTIONHASHES
{
"59016c79": "getContent()",
"3be8fd6a": "setContent(string)"
}
组合eth_call
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 1114,
"method": "eth_call",
"params": [
{
"data": "0x59016c79",
"to": "0xb9c4e02ab322fefb26ec422b07d9cdacb9036245"
},
"latest"
]
}
返回:
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 1114,
"result": "0x"
}
比较奇怪,居然无法读出来 使用eth_getStorageAt可以读出
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 1115,
"method": "eth_getStorageAt",
"params": [
"0xb9c4e02ab322fefb26ec422b07d9cdacb9036245",
"0x0",
"latest"
]
}
返回:
代码语言:javascript复制{
"jsonrpc": "2.0",
"id": 1115,
"result": "0x3132330000000000000000000000000000000000000000000000000000000006"
}
在geth下也可以读取到:
代码语言:javascript复制// 合约ABI
var abi=[{"constant":false,"inputs":[{"name":"_str","type":"string"}],"name":"setContent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getContent","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}];
// 合约地址
var address = "0xb9c4e02ab322fefb26ec422b07d9cdacb9036245";
// 通过ABI和地址获取已部署的合约对象
var helloworld = web3.eth.contract(abi).at(address);
helloworld.getContent()
返回:
代码语言:javascript复制"123"
或者直接调用:
代码语言:javascript复制eth.call({to: "0xb9c4e02ab322fefb26ec422b07d9cdacb9036245", data: "0x59016c79"})
也能够返回结果:
代码语言:javascript复制"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033132330000000000000000000000000000000000000000000000000000000000"