一、为什么用到私有链?
在以太坊的共有链上部署智能合约、发起交易需要花费以太币。而通过修改配置,可以在本机搭建一套以太坊私有链,因为与公有链没关系,既不用同步公有链庞大的数据,也不用花钱购买以太币,很好地满足了智能合约开发和测试的要求,开发好的智能合约也可以很容易地切换接口部署到以太坊公有链上。
二、开源工具和语言
1、brewMacOS包管理器
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
2、安装Go
和ethereum
liyuechun:Downloads yuechunli$ brew install go
liyuechun:Downloads yuechunli$ brew tap ethereum/ethereum
liyuechun:Downloads yuechunli$ brew install ethereum
3、geth运行以太坊节点
liyuechun:Downloads yuechunli$ git clone https://github.com/ethereum/go-ethereum
liyuechun:Downloads yuechunli$ cd go-ethereum
liyuechun:Downloads yuechunli$ make geth
4、Solidity以太坊智能合约语言
brew install solidity
备注:安装时间可能有点长,请耐心等待… 备注:安装时间可能有点长,请耐心等待… 备注:安装时间可能有点长,请耐心等待…
如果碰见下面的错误,请移步:http://blog.csdn.net/Sico2Sico/article/details/71082130
The GitHub credentials in the macOS keychain may be invalid.
Clear them with:
printf "protocol=https\nhost=github.com\n" | git credential-osxkeychain erase
Or create a personal access token:
https://github.com/settings/tokens/new?scopes=gist,public_repo&description=Homebrew
三、建立私链
1. 创建一个文件夹来存储你的私链数据
liyuechun:1015 yuechunli$ mkdir privatechain
liyuechun:1015 yuechunli$ pwd
/Users/liyuechun/Desktop/1015
liyuechun:1015 yuechunli$ ls
privatechain
liyuechun:1015 yuechunli$
2. 使用geth
来加载
geth --networkid 123 --dev --datadir data1 --rpc --rpcaddr 192.168.1.102 --rpcport 8989 --port 3000 --allow-insecure-unlock
各选项含义如下:
--identity:
指定节点 ID;--rpc:
表示开启 HTTP-RPC 服务;--rpcaddr:
HTTP-RPC 服务ip地址;--rpcport:
指定 HTTP-RPC 服务监听端口号(默认为 8545);--datadir:
指定区块链数据的存储位置;--port:
指定和其他节点连接所用的端口号(默认为 30303);--nodiscover:
关闭节点发现机制,防止加入有同样初始配置的陌生节点。--allow-insecure-unlock:
允许解锁。
执行上面的命令,你应该能看到下面的信息:
INFO [10-15 03:14:50] IPC endpoint opened: /Users/liyuechun/Desktop/1015/privchain/geth.ipc INFO [10-15 03:14:50] HTTP endpoint opened: http://127.0.0.1:8545
如果你切换到data1
文件夹里面,你会看到geth
, geth.ipc
, 和 keystore
。
liyuechun:1015 yuechunli$ cd data1/
liyuechun:data1 yuechunli$ ls
geth geth.ipc keystore
liyuechun:data1 yuechunli$
- 保持节点的运行,不要关闭终端,重新打开一个终端,使用
geth attach
连接节点,并且打开geth console
liyc1215:Desktop liyuechun$ cd privatechain/
liyc1215:privatechain liyuechun$ ls
data1
liyc1215:privatechain liyuechun$ cd data1/
liyc1215:data1 liyuechun$ ls
geth geth.ipc keystore
liyc1215:data1 liyuechun$ geth attach ipc:./geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.9.16-stable/darwin-amd64/go1.14.4
coinbase: 0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba
at block: 0 (Thu Jan 01 1970 08:00:00 GMT+0800 (CST))
datadir: /Users/liyuechun/Desktop/privatechain/data1
modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0
> personal.listAccounts
["0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba"]
>
这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:
eth:
包含一些跟操作区块链相关的方法;net:
包含一些查看p2p网络状态的方法;admin:
包含一些与管理节点相关的方法;miner:
包含启动&停止挖矿的一些方法;personal:
主要包含一些管理账户的方法;txpool:
包含一些查看交易内存池的方法;web3:
包含了以上对象,还包含一些单位换算的方法。
3. 相关api命令
查看账户
> personal.listAccounts
["0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba"]
>
创建账户
> personal.newAccount('liyuechun')
"0x3125e07931e2ab1486c60e296b93d1a5b4b2567a"
PS:里面的liyuechun
是你账户的密码,输入你自己喜欢的密码。
查看账户
> personal.listAccounts
["0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba", "0x3125e07931e2ab1486c60e296b93d1a5b4b2567a"]
>
4. web3参考文档
- https://web3js.readthedocs.io/en/v1.2.9/web3-eth.html#accounts
- https://ethereumbuilders.gitbooks.io/guide/content/en/ethereum_javascript_api.html
- https://web3py.readthedocs.io/en/stable/web3.miner.html
> web3.eth.accounts
["0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba", "0x3125e07931e2ab1486c60e296b93d1a5b4b2567a"]
> web3.eth.coinbase
"0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba"
>
5. 编写智能合约代码
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract HelloWorld {
string public greet;
function setGreeting(string memory greeting) public{
greet = greeting;
}
function hi() public pure returns(string memory) {
return "HelloWorld";
}
}
6. 获取智能合约字节码和abi
代码拷贝到https://remix.ethereum.org,编译,然后拷贝字节码和ABI。
- 字节码
608060405234801561001057600080fd5b506103d7806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063a413686214610046578063a99dca3f14610101578063cfae321714610184575b600080fd5b6100ff6004803603602081101561005c57600080fd5b810190808035906020019064010000000081111561007957600080fd5b82018360208201111561008b57600080fd5b803590602001918460018302840111640100000000831117156100ad57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610207565b005b610109610221565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561014957808201518184015260208101905061012e565b50505050905090810190601f1680156101765780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61018c61025e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101cc5780820151818401526020810190506101b1565b50505050905090810190601f1680156101f95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061021d9291906102fc565b5050565b60606040518060400160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102f45780601f106102c9576101008083540402835291602001916102f4565b820191906000526020600020905b8154815290600101906020018083116102d757829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061033d57805160ff191683800117855561036b565b8280016001018555821561036b579182015b8281111561036a57825182559160200191906001019061034f565b5b509050610378919061037c565b5090565b61039e91905b8082111561039a576000816000905550600101610382565b5090565b9056fea264697066735822122097b464d91714dce638c1cf5fd703125d2617a7d3417399c767b632b8b59110ee64736f6c63430006060033
- ABI
[
{
"inputs": [
],
"name": "greet",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
],
"name": "hi",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "greeting",
"type": "string"
}
],
"name": "setGreeting",
"outputs": [
],
"stateMutability": "nonpayable",
"type": "function"
}
]
7. 在bejson中转义成字符串
[{\"inputs\":[],\"name\":\"greet\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"hi\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"greeting\",\"type\":\"string\"}],\"name\":\"setGreeting\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]
7. 通过abi创建合约对象
> var abi = JSON.parse('[{\"inputs\":[],\"name\":\"greet\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"hi\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"greeting\",\"type\":\"string\"}],\"name\":\"setGreeting\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]')
> var myContract = web3.eth.contract(abi)
8. 检查coinbase账号余额
> account1 = web3.eth.coinbase
"0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba"
> web3.eth.getBalance(account1)
1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77
>
9. 解锁coinbase账号,我们通过coinbase账号来付费部署合约
coinbase
账号默认密码为空。
> personal.unlockAccount(account1, "")
true
>
10. 预估手续费
> bytecode = "608060405234801561001057600080fd5b506103d7806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063a413686214610046578063a99dca3f14610101578063cfae321714610184575b600080fd5b6100ff6004803603602081101561005c57600080fd5b810190808035906020019064010000000081111561007957600080fd5b82018360208201111561008b57600080fd5b803590602001918460018302840111640100000000831117156100ad57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610207565b005b610109610221565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561014957808201518184015260208101905061012e565b50505050905090810190601f1680156101765780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61018c61025e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101cc5780820151818401526020810190506101b1565b50505050905090810190601f1680156101f95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061021d9291906102fc565b5050565b60606040518060400160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102f45780601f106102c9576101008083540402835291602001916102f4565b820191906000526020600020905b8154815290600101906020018083116102d757829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061033d57805160ff191683800117855561036b565b8280016001018555821561036b579182015b8281111561036a57825182559160200191906001019061034f565b5b509050610378919061037c565b5090565b61039e91905b8082111561039a576000816000905550600101610382565b5090565b9056fea264697066735822122097b464d91714dce638c1cf5fd703125d2617a7d3417399c767b632b8b59110ee64736f6c63430006060033"
> web3.eth.estimateGas({data: bytecode})
Error: invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go struct field CallArgs.data of type hexutil.Bytes
at web3.js:6347:37(47)
at web3.js:5081:62(37)
at <eval>:1:21(7)
> bytecode = "0x608060405234801561001057600080fd5b506103d7806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063a413686214610046578063a99dca3f14610101578063cfae321714610184575b600080fd5b6100ff6004803603602081101561005c57600080fd5b810190808035906020019064010000000081111561007957600080fd5b82018360208201111561008b57600080fd5b803590602001918460018302840111640100000000831117156100ad57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610207565b005b610109610221565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561014957808201518184015260208101905061012e565b50505050905090810190601f1680156101765780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61018c61025e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101cc5780820151818401526020810190506101b1565b50505050905090810190601f1680156101f95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061021d9291906102fc565b5050565b60606040518060400160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102f45780601f106102c9576101008083540402835291602001916102f4565b820191906000526020600020905b8154815290600101906020018083116102d757829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061033d57805160ff191683800117855561036b565b8280016001018555821561036b579182015b8281111561036a57825182559160200191906001019061034f565b5b509050610378919061037c565b5090565b61039e91905b8082111561039a576000816000905550600101610382565b5090565b9056fea264697066735822122097b464d91714dce638c1cf5fd703125d2617a7d3417399c767b632b8b59110ee64736f6c63430006060033"
> web3.eth.estimateGas({data: bytecode})
265238
>
备注:字节码前面需要添加0x
。手续费大概为265238
gas
。
11. 部署合约,为了方便理解,设置一个回调函数
> contractInstance = myContract.new({data: bytecode,gas: 1000000, from: account1}, function(e, contract){
if(!e){
if(!contract.address){
console.log("Contract transaction send: Transaction Hash: "+contract.transactionHash+" waiting to be mined...");
}else{
console.log("Contract mined! Address: "+contract.address);
console.log(contract);
}
}else{
console.log(e)
}
})
如下所示:合约创建成功。
Contract transaction send: Transaction Hash: 0xae19a2404b80cd83466f2aac17e016fe9a84229bfadfe6b80417792b46bfaf33 waiting to be mined...
{
abi: [{
inputs: [],
name: "greet",
outputs: [{...}],
stateMutability: "view",
type: "function"
}, {
inputs: [{...}],
name: "setGreeting",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}],
address: undefined,
transactionHash: "0xae19a2404b80cd83466f2aac17e016fe9a84229bfadfe6b80417792b46bfaf33"
}
> Contract mined! Address: 0x47ad8096a084b00e464450b6c7f85954e2e12a6b
[object Object]
12. 检查合约是否部署成功
> contractInstance
{
abi: [{
inputs: [],
name: "greet",
outputs: [{...}],
stateMutability: "view",
type: "function"
}, {
inputs: [{...}],
name: "setGreeting",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}],
address: "0x47ad8096a084b00e464450b6c7f85954e2e12a6b",
transactionHash: "0xae19a2404b80cd83466f2aac17e016fe9a84229bfadfe6b80417792b46bfaf33",
allEvents: function(),
greet: function(),
setGreeting: function()
}
>
13. 调用合约方法
> contractInstance.setGreeting("liyuechun",{from:account1});
"0x4426849ae95fee4d7795d91329a1e1a9e45ea3af8668f751b3b716ae1a01e8eb"
14. 查询交易信息
> eth.getTransactionReceipt("0x4426849ae95fee4d7795d91329a1e1a9e45ea3af8668f751b3b716ae1a01e8eb")
{
blockHash: "0x6c6b97309e7d8a7f0a1a8b6a5e58d573cf00311d63ee88588e1c853113af5ebf",
blockNumber: 22,
contractAddress: null,
cumulativeGasUsed: 43264,
from: "0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba",
gasUsed: 43264,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: "0x510ef229f55776ec439aa6167523ccd0c5b88cd7",
transactionHash: "0x4426849ae95fee4d7795d91329a1e1a9e45ea3af8668f751b3b716ae1a01e8eb",
transactionIndex: 0
}
15. 转账
> web3.eth.getBalance(web3.eth.accounts[0]);
1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77
> web3.eth.getBalance(web3.eth.accounts[1]);
0
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(1,"ether")})
"0x3c7983c2976191aec473952ea3587dfad3d60009c9db37dbf4277ed9ad700788"
> web3.eth.getBalance(web3.eth.accounts[1]);
1000000000000000000
> web3.eth.getBalance(web3.eth.accounts[0]);
1.15792089237316195423570985008687907853269984665640564039456584007913129639927e+77
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(10,"ether")})
"0x38e5199cc3b9dec6e33e7783fe5833bbc5f12292b6bec63f49e3d5bb0166c01f"
> web3.eth.getBalance(web3.eth.accounts[1]);
11000000000000000000
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(10,"ether")})
"0x2a877b5c1942d16456f41391e8ab36f12d5d8caf6f7de6f76a854eb498cc1e53"
> web3.eth.getBalance(web3.eth.accounts[1]);
21000000000000000000
> web3.fromWei(web3.eth.getBalance(eth.accounts[1]), 'ether')
21
16. 查看转账信息
> eth.getTransactionReceipt("0x2a877b5c1942d16456f41391e8ab36f12d5d8caf6f7de6f76a854eb498cc1e53")
{
blockHash: "0x136dee4f228cfa46903584b095e471062adc528aa500d17e973718b68ebe9cb5",
blockNumber: 25,
contractAddress: null,
cumulativeGasUsed: 21000,
from: "0xa4ebe3ac6a5d625acc62c1cb3a0e20071489efba",
gasUsed: 21000,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: "0x3125e07931e2ab1486c60e296b93d1a5b4b2567a",
transactionHash: "0x2a877b5c1942d16456f41391e8ab36f12d5d8caf6f7de6f76a854eb498cc1e53",
transactionIndex: 0
}
>