2019-03-14 通过rpc发布和调用以太坊合约

2022-05-13 08:31:39 浏览数 (1)

这里研究一下通过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"

0 人点赞