转账与收款¶
听到这个有人又要兴奋了,收钱了!怎么操作呢?
读者回忆仔细回忆的话,第 3 章 交易是驱动力 我们简述了与以太坊区块链互动的唯一方式就是发送交易!无论是简单的转账,还是复杂的智能合约创建/调用,都是包含在交易内发送给以太坊网络的。本小节将带领读者亲自进行一次简单的转账交易。
若读者的Geth节点已经停止的话,请用如下命令重启节点,这次我们不需要它有带console控制台互动功能。请读者替换正确的地址为挖矿奖励地址。
1 2 3 4 5 6 | cd ether-test
geth --datadir ./db/ --rpc --rpcaddr=127.0.0.1 --rpcport 8545 --rpccorsdomain "*" \
--rpcapi "eth,net,web3,personal,admin,shh,txpool,debug,miner" \
--nodiscover --maxpeers 30 --networkid 198989 --port 30303 \
--mine --minerthreads 1 \
--etherbase "0x53dc408a8fa060fd3b72b30ca312f4b3f3232f4f"
|
转账
请确保你的Geth节点尚在运行,我们新开命令行窗口执行attach命令依附到正在运行中的Geth节点:
1 2 | cd ether-test
geth --datadir ./db attach ipc:./db/geth.ipc
|
在控制台上我们让节点暂停挖矿。
1 2 | > miner.stop()
true
|
同样在控制台上我们解锁的转账方的账户,输入解锁密码,以及解锁时长 300秒
。
1 2 | > personal.unlockAccount(eth.accounts[0], '123', 300)
true
|
Note
解锁账户的真正解锁的对象是存储于硬盘的keystore文件。 用户输入密码解锁后将私钥解出,暂存于内存。
在使用签名过后或者解锁时长到达规定后再从内存中移除私钥。这是设计上安全性的考量。不签名直接发送交易,将导致交易异常,无法成功发出。
好,接下来我们发送一笔 10
个以太币的转账,转账接收方是另一个我们持有的账户。
1 2 3 4 5 6 7 8 9 | > eth.sendTransaction(
... {
...... from: eth.accounts[0],
...... to: eth.accounts[1],
...... value: web3.toWei(10, 'ether')
....}
..)
"0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed"
|
交易发送成功!我们取回了一个长长的哈希值:
0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed
这就是交易的哈希值(Transaction Hash, TxHash),这个值能够唯一索引到一笔交易。
查看转账状态
之前我们已经命令挖矿停止。此时我们可以暂停查看全网状态。让我们深入研究此时刚发出去的交易处在什么阶段。
1 2 3 4 5 | > txpool.status
{
pending: 1,
queued: 0
}
|
此时交易尚处在 待打包 (pending)的状态,它的具体内容又是什么呢?我们用交易的哈希值来查询一下它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | > eth.getTransaction("0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed")
{
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
blockNumber: null,
from: "0x53dc408a8fa060fd3b72b30ca312f4b3f3232f4f",
gas: 90000,
gasPrice: 18000000000,
hash: "0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed",
input: "0x",
nonce: 1,
r: "0x9b372d58eb5acce93a8aa742ce3af179576c17208835eaf356c270db24df448c",
s: "0x5990b41a4f0f8103f4786f785e47f797b83428c96c251ee89bc10cef1d143b35",
to: "0x6c8f6c9d9f8d63503fc10977db48cd92a15d34ff",
transactionIndex: 0,
v: "0x7d9",
value: 10000000000000000000
}
|
读者如果回忆 交易的样子 ,就会发现一些非常眼熟的字段名,from/to
字段指明了交易的发送方和接收方; gas/gasPrice
指明了交易方愿意支付的交易费;value
指明了本次转账的以太币金额(wei
为单位);由于我们不是调用合约的交易,所以 input
所代表的数据区域是空的 0x
; nonce
是账户曾经发出过的交易笔数的值;因为尚未被打包入区块,所以 blockHash/blockNumber
都是 0
或者不确定状态。
让矿工运行!让这条交易被打包吧!
1 2 | > miner.start(1); admin.sleepBlocks(1); miner.stop();
true
|
以上代码将会让挖矿节点重启挖矿,稍等片刻,并在出块1个以后暂停挖矿。停止后区块链再次停止出块,给我们一个机会再观察一下交易池。
1 2 3 4 5 | > txpool.status
{
pending: 0,
queued: 0
}
|
交易已经成功被矿工捕获并打包,我们再查看一下交易状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | >eth.getTransaction("0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed")
{
blockHash: "0xa24f29bab9117a48ef6a83588dfecbb991ff2605dcfffffd3d486edb5a3bfe23",
blockNumber: 1450,
from: "0x53dc408a8fa060fd3b72b30ca312f4b3f3232f4f",
gas: 90000,
gasPrice: 18000000000,
hash: "0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed",
input: "0x",
nonce: 1,
r: "0x9b372d58eb5acce93a8aa742ce3af179576c17208835eaf356c270db24df448c",
s: "0x5990b41a4f0f8103f4786f785e47f797b83428c96c251ee89bc10cef1d143b35",
to: "0x6c8f6c9d9f8d63503fc10977db48cd92a15d34ff",
transactionIndex: 0,
v: "0x7d9",
value: 10000000000000000000
}
|
此时交易的 blockHash、blockNumber
都已经填充完毕,我们的交易被包含在了高度 #1450 的区块之中。我们查看一下接收方的余额。
1 2 | >web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
10
|
成功转账!至此这笔交易已经完整走完了从发出到最终入块的历程,那么,它被打包入的区块又是怎样的样子呢?我们来查看一下高度为#1450的区块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | >eth.getBlock(1450)
{
difficulty: 230123,
extraData: "0xd98301080e846765746888676f312e31302e338664617277696e",
gasLimit: 1041578754,
gasUsed: 21000,
hash: "0xa24f29bab9117a48ef6a83588dfecbb991ff2605dcfffffd3d486edb5a3bfe23",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x53dc408a8fa060fd3b72b30ca312f4b3f3232f4f",
mixHash: "0x9d9a14e1c4a87176f9c6625bf2a5d184c1a667204b0d0f21d638999b1a3da183",
nonce: "0x4fca8e74a63f357a",
number: 1450,
parentHash: "0x7560ceea1bd16c86f383fc31a9185a4014a1703d94838956717a5696dcb9bff4",
receiptsRoot: "0x61d0ae2203ef95f6e54d9fe14c6c3aa7bcdf9398be15b1f1d164502412863b92",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 656,
stateRoot: "0x905a8dfd9a0f2ee9f75b50397374fa63bf8119bda52ccb224e2f3bdd97ec4a86",
timestamp: 1537092501,
totalDifficulty: 255928573,
transactions: ["0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed"],
transactionsRoot: "0xef95d6a816bb8b56cf47d337553283593a1daafce4d3e0b3c4930080fd59dd55",
uncles: []
}
|
可以看到参数 transactions里面包含了一个列表,该列表有且仅有一个刚刚我们发出的交易;
因为没有产生日志事件(非合约调用),所以 logsBloom
日志索引参数被设置为全 0
;
由于私链网络中有且仅有我们一个节点在挖矿,没有竞争节点,所以 uncles
叔块列表依然为空列表。
参数中尤有意思的是 ExtraData
参数。它的值是一个16进制的值。
0xd98301080e846765746888676f312e31302e338664617277696e
如果我们将其转化为ASCII字符的话,它就是如下一句话,正巧是笔者运行的Geth节点软件的信息。
geth go1.10.3 darwin
至此,我们可以将 资料篇:区块 讲述区块结构的表格拿出来再补充完整:
域 | 描述 |
number | 区块高度编号 |
timestamp | Unix 时间戳 |
hash | 本区块的哈希值 |
parentHash | 前任区块的哈希值 |
nonce | PoW 算法的哈希值 |
extraData | 额外的信息 |
transRoot | 交易树的根哈希值 |
stateRoot | 状态树的根哈希值 |
receiptsRoot | 收据树的根哈希值 |
logsBloom | 该块的关键日志索引集合 |
Miner | 挖掘该块的矿工账户地址 |
gasLimit | 当前块允许包容的最大 gas 值 |
gasUsed | 总消耗掉的 gas |
difficulty | 当前块的挖矿难度值 |
totDifficulty | 区块链的总难度值 |
mixHash | 与 nonce 配合用于挖矿,由前任区块的一部分生成的哈希 |
size | 当前块体积(byte) |
sha3Uncles | 叔块列表的哈希值 |
uncles | 所引用的叔块列表 |
transactions | 所包含交易列表 |