以太坊 转账与收款
以太坊区块链互动的唯一方式就是发送交易!无论是简单的转账,还是复杂的智能合约创建/调用,都是包含在交易内发送给以太坊网络的。
如果Geth节点已经停止的话,请用如下命令重启节点,这次我们不需要它有带console控制台互动功能。请读者替换正确的地址为挖矿奖励地址。
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节点:
cd ether-test geth --datadir ./db attach ipc:./db/geth.ipc
在控制台上我们让节点暂停挖矿。
> miner.stop() true
同样在控制台上我们解锁的转账方的账户,输入解锁密码,以及解锁时长 300秒 。
> personal.unlockAccount(eth.accounts[0], '123', 300) true
解锁账户的真正解锁的对象是存储于硬盘的keystore文件。 用户输入密码解锁后将私钥解出,暂存于内存。
在使用签名过后或者解锁时长到达规定后再从内存中移除私钥。这是设计上安全性的考量。不签名直接发送交易,将导致交易异常,无法成功发出。
好,接下来我们发送一笔 10 个以太币的转账,转账接收方是另一个我们持有的账户。
> eth.sendTransaction( ... { ...... from: eth.accounts[0], ...... to: eth.accounts[1], ...... value: web3.toWei(10, 'ether') ....} ..) "0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed"
交易发送成功!我们取回了一个长长的哈希值:
0x45b6be881cf86b79dc7ad8bf4d9cbfafc9aa191062411d6606a086bbc42042ed
这就是交易的哈希值(Transaction Hash, TxHash),这个值能够唯一索引到一笔交易。
查看转账状态
之前我们已经命令挖矿停止。此时我们可以暂停查看全网状态。让我们深入研究此时刚发出去的交易处在什么阶段。
> txpool.status { pending: 1, queued: 0 }
此时交易尚处在 待打包 (pending)的状态,它的具体内容又是什么呢?我们用交易的哈希值来查询一下它。
> 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 或者不确定状态。
让矿工运行!让这条交易被打包吧!
> miner.start(1); admin.sleepBlocks(1); miner.stop(); true
以上代码将会让挖矿节点重启挖矿,稍等片刻,并在出块1个以后暂停挖矿。停止后区块链再次停止出块,给我们一个机会再观察一下交易池。
> txpool.status { pending: 0, queued: 0 }
交易已经成功被矿工捕获并打包,我们再查看一下交易状态。
>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 的区块之中。我们查看一下接收方的余额。
>web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") 10
成功转账!至此这笔交易已经完整走完了从发出到最终入块的历程,那么,它被打包入的区块又是怎样的样子呢?我们来查看一下高度为#1450的区块
>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 | 所包含交易列表 |
对编程人员来说,以太坊的学习重点在于智能合约的开发。学习以太坊智能合约不仅仅是学习一门新的编程语言 Solidity,更是学习如何使用高效的开发工具、如何进行完整的测试。一个好的智能合约开发者除了通晓语言知识外,还能举 ...