记录一下搭建一个以太坊私有网络环境的过程,方便以后的开发
我这里采用的是Geth客户端,在geth.ethereum.org网站上有详细的文档介绍,这里主要是按照官网的教程来操作。
安装
我是Ubuntu的环境,执行以下命令来安装
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum
设置私有网络
首先需要设置一个network id以便和其他以太坊网络隔绝开。以太坊主网络的network id是1,我这里设置为15
其次是选择共识算法,有proof-of-work和proof-of-authority两种,其中proof-of-work是现在主要的算法,另一种我理解还是处于实验性质的,因此还是先选择proof-of-work
我们可以先创建一个账号
geth account new --datadir data
运行之后就会显示一个账号的地址,例如我得到的地址是0xbDfa9d9C5656a2e4f09d53768DA17044AfE4F4d7
之后就是创建一个创世block,这也是我这个区块链开始的第一个block。这个block是通过genesis.json文件来进行配置,官网的例子如下:
{
"config": {
"chainId": 15,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"ethash": {}
},
"difficulty": "1",
"gasLimit": "8000000",
"alloc": {
"bDfa9d9C5656a2e4f09d53768DA17044AfE4F4d7": { "balance": "300000" }
}
}
这里面chainId代表network id,difficulty的值代表挖矿的难度,因为是私有网络,这里设置一个最小值即可。alloc里面代表要给哪些账号分配初始的以太币,这里是分配给我刚才创建的账号300000个wei,wei是以太坊的最小单位,1个以太币等于10^18个wei。
在我的系统中新建一个blockchain的目录,里面建一个data子目录,把genesis.json文件放在blockchain目录中,运行以下命令来初始化Geth数据库,使用这个创世block的配置文件来新建一个区块链节点。
geth init --datadir data genesis.json
创建之后我们就可以启动这个节点了,运行以下命令
geth --datadir data --networkid 15
从运行输出的结果里面我们可以看到IPC endpoint ended这句话,我们可以打开一个新的命令行终端,通过IPC URL来连接并启动javascript控制台
geth attach data/geth.ipc
挖矿
进入到Geth控制台之后,我们可以试一下挖矿到我们现有的这个账号。以下命令显示解锁账号,然后设置coinbase,之后就可以挖矿了。minser.start可以指定用多少个CPU线程来挖矿。
personal.unlockAccount(eth.accounts[0])
miner.setEtherbase(eth.accounts[0])
miner.start(1)
观察之前启动区块链节点的命令行终端输出的日志,会看到类似commint new mining work, mined potential block之类的信息,这就表示挖矿的任务在进行并成功挖到矿了。如果我们想停止挖矿,可以输入miner.stop()来终止。
过一段时间后,我们就可以查看挖到多少矿了。
web3.fromWei(eth.getBalance(eth.accounts[0]))
交易
现在我们可以创建一个新的account,以便转账一些以太币到这个账号上。
personal.newAccount()
得到的新的账号的地址是0x3312521d5892d45eb23b38c95375d9376dca8e0b
然后就可以转一些钱到这个新的账号
personal.unlockAccount(eth.accounts[0])
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:1e18,data:utils.toHex('This is a test')})
完成之后我们会得到交易的Hash值,"0xa69ae61b029a9e2ea46a075872e079829f770dbf94801586ad157c266010d9a5"
这时候查看eth.accounts[1]的余额还是0,因为没有人挖矿,所以交易没有被确认写入到区块链账簿里面。我们可以挖一下矿,过一会儿再查看就能看到余额会变为1个以太币了。
我们可以查看刚才的交易
eth.getTransaction('0xa69ae61b029a9e2ea46a075872e079829f770dbf94801586ad157c266010d9a5')
智能合约
以太坊最令人兴奋的特性就是支持智能合约。现在我们可以创建一个智能合约并部署到网络上了。
以下是一个简单的智能合约的代码:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
为了能在python中编译solidity语言编写的合约,需要安装以下的包
pip install py-solc-x
之后我们可以通过web3.py这个库来进行操作
1.首先是连接到我们的以太坊节点,如果连接成功就会打印True
from web3 import Web3
w3 = Web3(Web3.IPCProvider('data/geth.ipc'))
print(w3.isConnected())
2.查看当前的accounts,并查询余额,单位为ether
for account in w3.eth.accounts:
print("Account:{}, Balance:{} ether".format(account, Web3.fromWei(w3.eth.get_balance(account), 'ether')))
3.设置一个默认账号并解锁,后续将用这个账号来部署合约
w3.eth.default_account=w3.eth.accounts[0]
w3.geth.personal.unlockAccount(w3.eth.accounts[0], password)
4.编译并部署合约
import solcx
from solcx import compile_source
solcx.install_solc()
def compile_source_file(file_path):
with open(file_path, 'r') as f:
source = f.read()
return compile_source(source)
def deploy_contract(w3, contract_interface):
tx_hash = w3.eth.contract(
abi=contract_interface['abi'],
bytecode=contract_interface['bin']).constructor().transact()
return tx_hash
contract_source_path = 'storage.sol'
compiled_sol = compile_source_file('storage.sol')
contract_id, contract_interface = compiled_sol.popitem()
transcation_hash = deploy_contract(w3, contract_interface)
5.开启挖矿,过一段时间之后查询合约的地址
w3.geth.miner.start(1)
#Wait some time
contract_address = w3.eth.get_transaction_receipt(transaction_hash)['contractAddress']
6.拿到合约地址,表示合约已经记录在区块链账簿上了。实例化合约并调用store方法来存储一个数值,过一段时间等矿工执行并记录到账簿之后,即可调用retrieve方法来获取刚才存储的数值
contract_instance = w3.eth.contract(address=contract_address, abi=contract_interface['abi'])
contract_instance.functions.store(101).transact()
#Wait some time
contract_instance.functions.retrieve().call()