前面在 从零开始编写智能合约-使用traffle套件 中 使用 truffle
套件从零开始搭建环境, 编写、部署、测试和调用智能合约, 这次我们只使用一些命令行工具来进行"手工打造"
配套代码: https://github.com/yinhui1984/stepByStepSmartContract
创建测试链
还是以ETH为例, 使用 Go Ethereum
https://geth.ethereum.org 来创建 使用POW
共识算法的一个简单私有链.
安装geth
官方教程 https://geth.ethereum.org/docs/install-and-build/installing-geth
或者自己下载安装包进行安装 https://geth.ethereum.org/downloads/
安装完成后, 运行一下version表示成功
1
2
3
4
5
6
7
8
|
OSX MP16 ~/Downloads/privateNetwork ❯ geth version
Geth
Version: 1.10.16-stable
Architecture: amd64
Go Version: go1.17.6
Operating System: darwin
GOPATH=
GOROOT=go
|
创建一个账户
下面的创世文件
中的coinbase
会用到这个账户, 用来接收挖矿的收入
为方便起见, 将账户密码写到password.txt , 当然这是不安全的行为.
1
|
echo 123456 > password.txt
|
新建账户:
1
|
geth --datadir ./data/ account new --password ./password.txt
|
其中的datadir
是数据存储目录, 后面的数据文件都将存到这个目录中
1
2
3
4
5
6
7
8
9
10
11
12
|
OSX MP16 ~/Downloads/privateNetwork ❯ geth --datadir ./data/ account new --password ./password.txt
INFO [07-01|10:10:34.007] Maximum peer count ETH=50 LES=0 total=50
Your new key was generated
Public address of the key: 0x79dF0D3c23f22370881dC92aF524A1D5E52e3552
Path of the secret key file: data/keystore/UTC--2022-07-01T02-10-34.008109000Z--79df0d3c23f22370881dc92af524a1d5e52e3552
- You can share your public address with anyone. Others need it to interact with you.
- You must NEVER share the secret key with anyone! The key controls access to your funds!
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
|
打印内容中的 0x79dF0D3c23f22370881dC92aF524A1D5E52e3552
是公钥, 也是我们的账户地址
找一个网络ID
chainId
或者network ID
用于隔离以太坊对等网络。只有当两个对等点使用相同的创世块和网络 ID 时,区块链节点之间才会发生连接。使用 –networkid 命令行选项设置 geth 使用的网络 ID。
以太网主网 ID 为 1。如果您提供与主网不同的自定义网络 ID,您的节点将不会连接到其他节点并形成专用网络。如果您打算在 Internet 上连接到您的私有链,最好选择一个尚未使用的网络 ID。您可以在 https://chainid.network 找到由社区运行的以太坊网络注册表。 这里我们只是在本地运行, 随便使用一个ID就可以了, 比如1235
编写"创世文件"
一个新的区块链在生成第一个区块, 也就是创世块
的时候, 需要从创世文件
来进行生成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
{
"nonce": "0x0000000000000042",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x00",
"gasLimit": "0x8000000",
"difficulty": "0x0400",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x79dF0D3c23f22370881dC92aF524A1D5E52e3552",
"alloc": {
"0x79dF0D3c23f22370881dC92aF524A1D5E52e3552":
{
"balance": "1000000000000000000000"
}
},
"config": {
"chainId": 1235,
"homesteadBlock": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0
}
}
|
关于创世文件中的各个字段的含义, 后面会有一篇专门的博客来讲
注意: 将coinbase
以及 alloc
中的地址修改为上面创建账户时你所得到的地址
初始化区块链
1
|
geth --datadir ./data init ./genesis.json
|
输出
1
2
3
4
5
6
7
8
9
10
11
|
INFO [07-01|10:50:50.915] Maximum peer count ETH=50 LES=0 total=50
INFO [07-01|10:50:50.919] Set global gas cap cap=50,000,000
INFO [07-01|10:50:50.919] Allocated cache and file handles database=/Users/zhouyinhui/Downloads/privateNetwork/data/geth/chaindata cache=16.00MiB handles=16
INFO [07-01|10:50:51.007] Writing custom genesis block
INFO [07-01|10:50:51.010] Persisted trie from memory database nodes=0 size=0.00B time="21.005µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [07-01|10:50:51.011] Successfully wrote genesis state database=chaindata hash=0ca0fb..b61b69
INFO [07-01|10:50:51.011] Allocated cache and file handles database=/Users/zhouyinhui/Downloads/privateNetwork/data/geth/lightchaindata cache=16.00MiB handles=16
INFO [07-01|10:50:51.090] Writing custom genesis block
INFO [07-01|10:50:51.090] Persisted trie from memory database nodes=0 size=0.00B time="2.71µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [07-01|10:50:51.091] Successfully wrote genesis state database=lightchaindata hash=0ca0fb..b61b69
OSX MP16 ~/Downloads/privateNetwork ❯
|
生成的文件在 ./data/geth
下
运行区块链
1
|
geth --datadir ./data/ --networkid 1235 --port 8545 --http --http.api 'admin,eth,miner,net,txpool,personal,web3' --mine --allow-insecure-unlock --unlock '0x79dF0D3c23f22370881dC92aF524A1D5E52e3552' --password password.txt
|
--datadir
数据文件目录
--networkid
网络ID
--port
RPC端口
--http
启用http-rpc
服务
--http.api
提供那些API
--mine
启用挖矿
--allow-insecure-unlock
当账户相关的RPC被http暴露时,允许不安全的账户解锁
--unlock
解锁账户 (多个账户用逗号分割)
--password
解锁账户用到的密码文件
成功运行后, 会看到很多打印信息, 其中值得注意的是下面这两行, 一个是ipc, 一个是http server, 我们会使用他们来和我们的区块链进行通讯
1
2
|
INFO [07-01|11:08:58.245] IPC endpoint opened url=/Users/zhouyinhui/Downloads/privateNetwork/data/geth.ipc
INFO [07-01|11:08:58.245] HTTP server started endpoint=127.0.0.1:8545 prefix= cors= vhosts=localhost
|
另外一条是 我们作为区块链中的对等节点PEER NODE
时的信息, 这里我们还用不到
1
|
Started P2P networking self=enode://de2537c5f309be45bcb2d011cd172138db40295f74a288b07e03635e2194e7c2987cf97c9d295dc502053d9f7adda11052597c7f1bc5a5ec3d8182777138a0f1@127.0.0.1:8545
|
请保持区块链在后台运行, 后面的代码都需要用到
与测试链进行交互
attach
到测试链, 就可以启动一个js console, 和truffle console
功能一样
方式1, 通过IPC进行attach
1
|
geth attach /Users/zhouyinhui/Downloads/privateNetwork/data/geth.ipc
|
1
2
3
4
5
6
7
8
9
|
OSX MP16 ~/blockchain/private_net/tempchain ❯ geth attach /Users/zhouyinhui/Downloads/privateNetwork/data/geth.ipc
Welcome to the Geth JavaScript console!
....
To exit, press ctrl-d or type exit
> eth.chainId()
"0x4d3"
>
|
"0x4d3"
也就是 我们的 1235
其中 *.ipc 可以使用相对路径
方式2, 通过http服务进行attach
1
|
geth attach http://127.0.0.1:8545
|
一键生成测试链
上面那些繁琐的步骤可以搞成一个脚本, 一键生成, 这样来得更方便
脚本在这里: https://github.com/yinhui1984/stepByStepSmartContract/blob/main/autogen/autoGenChain.py
(直接将代码贴到这里会导致GitHub Pages failed to build your site
😂)
编写智能合约
我们还是使用 上篇博客中的智能合约
1
2
3
|
mkdir ./contracts
cd ./contracts
touch Calculator.sol
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Calculator {
function add(int a, int b) public pure returns (int) {
return a + b;
}
function subtract(int a, int b) public pure returns (int) {
return a - b;
}
}
|
编译智能合约
如果你的电脑上没有solc
, 请事先安装, 它是solidity的编译器, 官方教程: https://docs.soliditylang.org/en/v0.8.15/installing-solidity.html#installing-solidity
编译:
1
|
solc --bin --abi -o ./build ./Calculator.sol
|
--bin
生成16进制文件
--abi
生成abi文件
-o
输出目录
可以写一个Makefile
来干这个事情:
1
2
3
4
|
all:
solc --bin --abi --overwrite -o ./build ./Calculator.sol
clean:
rm -rf ./build/*
|
1
2
3
4
5
6
7
|
OSX MP16 ~/Downloads/privateNetwork/contracts ❯ tree
.
├── Calculator.sol
├── Makefile
└── build
├── Calculator.abi
└── Calculator.bin
|
部署智能合约
新建一个node.js 项目deploy
1
2
3
4
|
mkdir -p deploy/
cd deploy/
npm init
touch index.js
|
安装web3.js
在index.js
中加入如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
const fs = require('fs');
const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');
const bytecode = fs.readFileSync('../build/Calculator.bin').toString();
const abi = JSON.parse(fs.readFileSync('../build/Calculator.abi').toString());
(async function () {
const accounts = await web3.eth.getAccounts();
const calculator = new web3.eth.Contract(abi);
calculator.deploy({
data: bytecode
}).send({
from: accounts[0],
}).then((deployment) => {
console.log('Contract was deployed at the following address:');
console.log(deployment.options.address);
}).catch((err) => {
console.error(err);
});
})();
|
注意, 上面的部署代码没有使用账户的私钥, 是因为在前面的步骤中, 账户已经解锁了
在部署之前需要先启动挖矿
1
2
3
4
5
6
7
8
|
OSX MP16 ~/Downloads/pri/mychain ❯ ./attach.sh
...
> miner.start()
null
> eth.blockNumber
26
|
部署:
1
2
3
|
OSX MP16 ~/Downloads/pri/c/d/deploy_js ❯ node ./index.js
Contract was deployed at the following address:
0x1F55d61D5Aa8eC92FD1Da0f19FaC9D552A29DBBF
|
部署成功, 停掉挖矿, 否则电脑巨烫…
调用智能合约
调用智能合约就和 从零开始编写智能合约-使用truffle套件中的一样了, 可以跳转到那里进行参考