个人记录,很多操作参考了这篇文章与其他的,主要是为了给自己一条特别适合自己工作环境的私链。

关于环境

用的geth,安装啥的我觉得不用多说了,资料太多

init

创世区块

在非go目录下,新建一个,比如我在~/PersonWork/bychain,目录下来一个gensis.json,创世区块!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"config": {
"chainId": 23333, //反正别是1,1是以太坊主链
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0
},
"alloc": {}, //可以初始化账户,但是觉得没必要
"coinbase": "0x0000000000000000000000000000000000000000", //默认收钱账户,就是默认矿工账户
"difficulty": "0x400", //区块挖掘难度
"extraData": "0x", //不能是0x0,会报错,就是0x,不是我写错了
"gasLimit": "0x2fefd8", //gas limit
"nonce": "0xdeadbeefdeadbeaf",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}

注意!

config参数请加上byzantiumBlock、constantinopleBlock、petersburgBlock,因为其实以太坊本身就要经历这四个阶段,如果不加的话,在0.5.5版本的solc之后,就会出问题,因为EIP 145:EVM 中的按位移动(bitwise shifting)指令,是constantinopleBlock版本的 ,这个SHR吧,从0.5.5版本开始,就被用来跟在CALLDATALOAD后面,(通过右移)用来取不同位数,所以,你要是在搭建私链的时候,很可能被坑,你的执行到SHR指令戛然而止Orz,从外部表现来看就是:“gas required exceeds allowance or always failing transaction”,当然这个Error大部分时候和这个问题没关系。

image-20191118210909048

初始化命令

1
geth --datadir data0 init genesis.json

命令的主体是 geth init,表示初始化区块链,命令可以带有选项和参数,其中 --datadir 选项后面跟一个目录名,这里为 data0,表示指定数据存放目录为 data0,genesis.json 是 init 命令的参数。初始化完成data0里面就会有初始化数据

运行节点

1
2
3
4
5
6
# 节点1
geth --identity "TestNode0" --rpc --rpcport "8545" --rpccorsdomain "*" --datadir data0 --port "30303" --nodiscover --allow-insecure-unlock console

# 节点2
geth --datadir data1 init gensis.json
geth --identity "TestNode1" --rpc --rpcport "8546" --rpccorsdomain "*" --datadir data1 --port "30304" --nodiscover --allow-insecure-unlock console

上面命令的主体是 geth console,表示启动节点并进入交互式控制台。
各选项含义如下:

  • –identity:指定节点 ID;
  • –rpc:表示开启 HTTP-RPC 服务;
  • –rpcport:指定 HTTP-RPC 服务监听端口号(默认为 8545);
  • –rpccorsdomain:允许rpc跨的域 (browser enforced)
  • –datadir:指定区块链数据的存储位置;
  • –port:指定和其他节点连接所用的端口号(默认为 30303);
  • –nodiscover:关闭节点发现机制,防止加入有同样初始配置的陌生节点。

一个节点可以有多个账户,默认0账户收钱(就上面定义的),每次生成一个account,这个节点下面就会在文件夹keystone下生成一个文件,钥匙文件

钥匙文件

每个账户都由一对钥匙定义,一个私钥和一个公钥。 账户以地址为索引,地址由公钥衍生而来,取公钥的最后 20个字节。每对私钥 /地址都编码在一个钥匙文件里。钥匙文件是JSON文本文件,可以用任何文本编辑器打开和浏览。钥匙文件的关键部分,账户私钥,通常用你创建帐户时设置的密码进行加密。钥匙文件可以在以太坊节点数据目录的keystore子目录下找到。

source: https://blog.csdn.net/wzygis/article/details/73480112

一旦进入console了,这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:

  • eth:包含一些跟操作区块链相关的方法;
  • net:包含一些查看p2p网络状态的方法;
  • admin:包含一些与管理节点相关的方法;
  • miner:包含启动&停止挖矿的一些方法;
  • personal:主要包含一些管理账户的方法;
  • txpool:包含一些查看交易内存池的方法;
  • web3:包含了以上对象,还包含一些单位换算的方法。

常用命令有,后面希望有更详细的一些:

  • personal.newAccount():创建账户;
  • personal.unlockAccount():解锁账户;
  • eth.accounts:枚举系统中的账户;
  • eth.getBalance():查看账户余额,返回值的单位是 Wei(Wei 是以太坊中最小货币面额单位,类似比特币中的,1 ether = 10^18 Wei);
  • eth.blockNumber:列出区块总数;
  • eth.getTransaction(交易hash):获取交易;
  • eth.getBlock():获取区块;
  • miner.start():开始挖矿;
  • miner.stop():停止挖矿;
  • web3.fromWei():Wei 换算成以太币;
  • web3.toWei():以太币换算成 Wei;
  • txpool.status:交易池中的状态;
  • admin.addPeer():连接到其他节点;

这些命令支持 Tab 键自动补全

节点网络

启动前添加

1
2
3
4
5
6
7
# 进入节点目录下的geth文件夹
> cd data0/geth
> gedit static-nodes.json # 没有的话新建即可
[
"enode://xxxxx@[127.0.0.1]:30304"
]
# 最后正常启动即可

启动时添加

1
2
# 节点连接:启动节点时添加
> geth --bootnodes enode://pubkey1@ip1:port1,enode://pubkey2@ip2:port2 --identity "TestNode0" --rpc --rpcport "8545" --datadir data0 --port "30303" --nodiscover --allow-insecure-unlock console

启动后添加

1
2
3
4
5
6
7
8
9
10
11
# 节点1:获取节点1信息
> admin.nodeInfo.enode
"enode://略去数据@127.0.0.1:23332?discport=0"
# 节点2:查看节点2 peer数量
> net.peerCount
0
> admin.addPeer("enode://略去数据@127.0.0.1:23332?discport=0")

# 节点2的peer增加
> net.peerCount
1

如果出现admin.addPeer()返回true但是实际节点没有添加成功的例子,也即新增节点不生效,像这样:

1
2
3
4
5
> admin.addPeer("enode://c4586276391b3c88ec23889d1bc825d0c7d69bd5765d4545686f835608068b8dc48799d2686a04ea0f9e17aed099bf9b56935679fa6493e9b17151624a320714@172.16.0.17:30303")
true
> admin.peers
[]

查看节点全信息,查找原因

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
> admin.nodeInfo
{
...
ip: "::", #这个ip应该不能是『::』如果是这个,在启动节点的时候去掉 --nodiscover选项
listenAddr: "[::]:30306",
name: "Geth/TestNode1/v1.9.0-unstable/darwin-amd64/go1.12.5",
ports: {
discovery: 0,
listener: 30306
},
protocols: {
eth: {
config: {
chainId: xxx, # 这个值要一致,如果不一致是无法添加成功的
...
homesteadBlock: 0
},
difficulty: 1575808,
genesis: "0x62adc01b295219d7a4e1dade7648bf622f04e2b74aaaea3edfa3ad187ef59eed",
head: "0x43dc6aad82f2f7c208034ee103471e27a14c59e1cf07c2f124d345a377198c3c",
network: 1
}
}
}

智能合约

发布

对于这样一个demo:

calc.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity >=0.4.22 <0.6.0;
//This is a calc you input to number and we will get the result
contract calc{
uint lastresult;
function ADD(uint _a,uint _b) public returns(uint){
uint c = _a + _b;
lastresult = c;
return c;
}
function getLastResult() public view returns(uint){
return lastresult;
}
}

geth智能合约发布需要bytecode和abi

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
26
27
28
29
30
31
32
33
34
35
36
# In geth console
> bytecode="0x---" #bin数据
> abi=[{...}] #abi数据
> myContract = eth.contract(abi)

# 评估gas消耗,决定gas写多少
> web3.eth.estimateGas({data: bytecode})

# 解锁节点以发布交易
> personal.unlockAccount(eth.accounts[0],'password',1000)

> contractInstance = myContract.new({data: bytecode gas: 1000000, from: eth.accounts[0]}, 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)
}
})

# 查看是否在交易池中
eth.getBlock("pending", true).transactions

#节点3,挖矿
> miner.start(1);admin.sleepBlocks(1);miner.stop()

# 检查合约是否部署成功
> eth.getCode(contractInstance.address)

# Success Info eg:
> Contract mined! Address: 0x22f72b784e8a076849154cc497ccb2c45560a140 [object Object]
> Contract mined! Address: 0xc966d0f4b171025b65598b22f3c3f4e84f2f8717 [object Object]

Demo

可使用remix改善发布这个步骤,虽然改版了,但是差不多;注意gas可能会超过范围哦,修改下

geth客户端发布-web3deploy

Demo展示

智能合约的访问

当获取合约实例之后(比如 testInstance),在geth console中可以通过三种方法调用合约方法(比如testFunc)

  • testInstance.testFunc.sendTransaction();
  • testInstance.testFunc();
  • testInstance.testFunc.call();

本文将讲解这三种调用方法的区别

  • testInstance.testFunc.sendTransaction(); 会创建一个交易,调用之后会返回一个交易hash值,它会广播到网络,等待矿工打包, 它会消耗gas。
  • testInstance.testFunc.call(); 它完全是一个本地调用,不会向区块链网络广播任何东西,它的返回值完全取决于 testFunc 方法的代码,不会消耗gas
  • testInstance.testFunc(); 它会比较特殊,由于有constant标识的方法不会修改状态变量,所以它不会被编译器执行。所以,如果testFunc() 有constant标识,它并不会被编译器执行,web3.js会执行call()的本地操作。相反如果没有constant标识,会执行sendTransaction()操作。
1
2
3
4
5
6
7
8
9
// 得到address的智能合约实例
var contractInstance = web3.eth.contract(abi).at(address);

// 本地:
contractInstance.ADD.call(6,7)
//13

// 变成交易
contractInstance.ADD.sendTransaction(7,6,{from:eth.accounts[0]})

demo

看下这个交易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
eth.getTransaction("0xe9b9747a0e1cda6e4f46644e39e54cc2b7a78983311bddc654299d692217df72")
{
blockHash: "0x2023067033d263a3f893b059466d80fb235a6ca6fd8c33e32ee430d8bfec7029",
blockNumber: 109,
from: "0xc2ac34ff98de5040b6e4d8adc70cf5f70e09d217",
gas: 42029,
gasPrice: 1000000000,
hash: "0xe9b9747a0e1cda6e4f46644e39e54cc2b7a78983311bddc654299d692217df72",
input: "0xa87de9cc00000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000006",
nonce: 2,
r: "0xxxx",
s: "0xxxx",
to: "0xdac4e5626827029298266c244440419ff79455a9",
transactionIndex: 0,
v: "0x3ac",
value: 0
}

这里我想到了个问题,要是我没有abi怎么办,查了下资料,这个问题比较棘手。。。

监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取事件对象
var myEvent = metacoin.Transfer();
// 监听事件,监听到事件后会执行回调函数
myEvent.watch(function(err, result) {
if (!err) {
console.log(result);
} else {
console.log(err);
}
myEvent.stopWatching();
});
//————————————————
//版权声明:本文为CSDN博主「今夕不惑」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/MisshqZzz/article/details/77834856

API

更多:https://web3js.readthedocs.io

节点

admin.addPeer()

连接节点,两个节点要要指定相同的 chainID

admin.nodeInfo.enode

知道节点信息

admin.peers

可以查看连接到的其他节点信息,

net.peerCount

可以查看已连接到的节点数量。

eth.syncing

节点同步;当CurrentBlock大于等于HighestBlock时会返回false,也就是同步完成之后,再执行eth.syncing()函数会返回false。

如果同步没完成,则显示以下字段:

  • startingBlock:开始同步的起始区块编号;
  • currentBlock:当前正在导入的区块编号;
  • highestBlock:通过所链接的节点获得的当前最高的区块高度;
  • pulledStates:当前已经拉取的状态条目数;
  • knownStates:当前已知的待拉取的总状态条目数;

账户

personal.newAccount()

创建账户,输入两遍密码即可;可以创建好多个,按照创建顺序,0,1,2,…账户,用eth.accounts可以枚举有多少,以及地址是啥

eth.accounts()

枚举该节点的所有账户,[“0x嘻嘻嘻嘻嘻嘻嘻”,“0x发发发发发”]

eth.coinbase

挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:

eth.getBalance(eth.accounts[0])

查看账户余额,可以单位换算以下,见下

web3.fromWei(eth.getBalance(eth.accounts[1]),‘ether’)

以Wei为单位查看余额

web3.eth.personal.unlockAccount(address, password, unlockDuraction [, callback])

解锁账户,转账挖矿都需要解锁账户,如果解锁账户碰到这个问题"error-account-unlock-with-http-access-is-forbidden",在启动geth console的时候可以加上参数"–allow-insecure-unlock";

unlockDuration的时间应该是秒

https://ethereum.stackexchange.com/questions/69435/error-account-unlock-with-http-access-is-forbidden-when-unlock-an-account-in-ge

miner.setEtherbase()

将其他账户设置成coinbase,比如miner.setEtherbase(eth.accounts[1])

挖矿

miner.start(1)

其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。

miner.stop()

不解释

miner.start(1);admin.sleepBlocks(1);miner.stop();

一次只挖一个块

区块

eth.blockNumber

区块数量

eth.getBlock( i )

查看区块i的信息

eth.getBlock(“pengding/[number]”).gasLimit

Gas Limit就是一次交易中Gas的可用上限,在你提交交易之前,需要为交易设定一个Gas用量的上限。这个值一般写在gensis.json文件里,如果想提高gas,带上--targetgaslimit xxxxx

交易

eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})

发送交易,这是一个简单的转账交易,后面有合约创建和合约交互

txpool.status

展示交易池

eth.getBlock(“pending”, true).transactions

查看当前待确认交易

eth.getBlockTransactionCount(“pending”);

查看当前待确认交易个数

eth.getTransaction(“0x交易hash”)

查看交易信息

eth.estimatesGas({data: bytecode})

评估bytecode的gas消耗

单位换算

web3.toWei(5,‘ether’)

参考:

以太坊私链搭建

以太坊(Ethereum)私链建立 、合约编译、部署完全教程(1)

深入解析以太坊中调用合约的三种方法

在geth客户端调用已部署的智能合约