以太坊 交易池(txpool)
本文讲解交易 tx 从发出到被打包都经历了哪些过程?txpool在这个过程中又起到什么作用?
1. txpool 启动参数
--txpool.nolocals 为本地提交交易禁用价格豁免 --txpool.journal value 本地交易的磁盘日志:用于节点重启 (默认: "transactions.rlp") --txpool.rejournal value 重新生成本地交易日志的时间间隔 (默认: 1小时) --txpool.pricelimit value 加入交易池的最小的gas价格限制(默认: 1) --txpool.pricebump value 价格波动百分比(相对之前已有交易) (默认: 10) --txpool.accountslots value 每个帐户保证可执行的最少交易槽数量 (默认: 16) --txpool.globalslots value 所有帐户可执行的最大交易槽数量 (默认: 4096) --txpool.accountqueue value 每个帐户允许的最多非可执行交易槽数量 (默认: 64) --txpool.globalqueue value 所有帐户非可执行交易最大槽数量 (默认: 1024) --txpool.lifetime value 非可执行交易最大入队时间(默认: 3小时)
2. txpool查看内容
> txpool.content { pending: {}, queued: {} }
txpool 由 pending 和 queued 两部分构成。那么他们两者有什么分别呢?一个为待打包状态,一个为队列排队中。这里我们发起了两笔不同的交易:
> eth.sendTransaction({from:"0xdae19174969a7404e222c24b6726e4d089c12768",to:"0x5929a871f57a1C5F7E4eA304CAe92DACD1C1556b",value:web3.toWei(0.01,"ether"),gasPrice:21000000000,nonce:2}); "0x7db7883bb23a31deb9f01b5e6fb28363b1aee1b9b6797ea8b5706be170a1187c" > eth.sendTransaction({from:"0xdae19174969a7404e222c24b6726e4d089c12768",to:"0x5929a871f57a1C5F7E4eA304CAe92DACD1C1556b",value:web3.toWei(0.01,"ether")}); "0x2784a79a8c454c72700e7be3b31c1c98ceaea232ca4992a6830b0fc999ebb653"
很显然,第一笔交易指定了 nonce 为2,第二笔交易未指定 nonce 值,因为此地址没有发起过交易那么 nonce 值默认为 0。这时我们再看一下 txpool 中的内容:
> txpool.content { pending: { 0xdAE19174969A7404e222c24B6726E4D089c12768: { 0: { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0xdae19174969a7404e222c24b6726e4d089c12768", gas: "0x15f90", gasPrice: "0x1", hash: "0x2784a79a8c454c72700e7be3b31c1c98ceaea232ca4992a6830b0fc999ebb653", input: "0x", nonce: "0x0", r: "0xdabcd46d8d0b61e468d9f10119d544437f89cd094c35a89e5cbed298faf52c4a", s: "0x3670f23ecfb0a12e982a60438640fe042eefc50646a077de0244a8d67a84af9e", to: "0x5929a871f57a1c5f7e4ea304cae92dacd1c1556b", transactionIndex: "0x0", v: "0xa95", value: "0x2386f26fc10000" } } }, queued: { 0xdAE19174969A7404e222c24B6726E4D089c12768: { 2: { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0xdae19174969a7404e222c24b6726e4d089c12768", gas: "0x15f90", gasPrice: "0x4e3b29200", hash: "0x7db7883bb23a31deb9f01b5e6fb28363b1aee1b9b6797ea8b5706be170a1187c", input: "0x", nonce: "0x2", r: "0xa8953a87c326c02da9d7a712d6c7ac0cd415cbc71ea0c24423f9e01b1fec65bd", s: "0x3faefc3a0db585a67f02996a7167890e41ff5fd8fd4be6efff3bea7a797fad29", to: "0x5929a871f57a1c5f7e4ea304cae92dacd1c1556b", transactionIndex: "0x0", v: "0xa96", value: "0x2386f26fc10000" } } } }
现在 txpool 中有两笔交易,其中 nonce 为 0 的在 pending 中,nonce 为 2 的在 queued 中。为什么只有 nonce 不同的两笔交易,在 txpool 中的位置却不同呢?
3. txpool 处理流程
首先,如果不传入nonce值,那么geth节点会默认计算当前地址已经发起了的交易中最大的nonce值为多少,然后将其+1,然后将此交易放置在pending中,等待节点打包。
其次,如果传入的nonce值过大,在进入txpool中检查到它之前的nonce并没有使用过,那么此笔交易不会发送到pending中,而且放置在queued中。只有当前面的nonce补齐之后,才会进入到pending中。那么,我们再发一笔交易把nonce补齐看看:
> eth.sendTransaction({from:"0xdae19174969a7404e222c24b6726e4d089c12768",to:"0x5929a871f57a1C5F7E4eA304CAe92DACD1C1556b",value:web3.toWei(0.01,"ether")}); "0x7ee17d38405c01bab4eec4d9dc62a6bba98283e243a2d9132187706485878ef5" > txpool.content { pending: { 0xdAE19174969A7404e222c24B6726E4D089c12768: { 0: { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0xdae19174969a7404e222c24b6726e4d089c12768", gas: "0x15f90", gasPrice: "0x1", hash: "0x2784a79a8c454c72700e7be3b31c1c98ceaea232ca4992a6830b0fc999ebb653", input: "0x", nonce: "0x0", r: "0xdabcd46d8d0b61e468d9f10119d544437f89cd094c35a89e5cbed298faf52c4a", s: "0x3670f23ecfb0a12e982a60438640fe042eefc50646a077de0244a8d67a84af9e", to: "0x5929a871f57a1c5f7e4ea304cae92dacd1c1556b", transactionIndex: "0x0", v: "0xa95", value: "0x2386f26fc10000" }, 1: { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0xdae19174969a7404e222c24b6726e4d089c12768", gas: "0x15f90", gasPrice: "0x1", hash: "0x7ee17d38405c01bab4eec4d9dc62a6bba98283e243a2d9132187706485878ef5", input: "0x", nonce: "0x1", r: "0xe03fb4d94b0ff04107c855bfd88a84ecdefb03f4c9b0cea5341591aa69d4751e", s: "0x4d2f60f4045e5492cd4818145cec73c78b00e0cff57026c4528d91a82dee76e1", to: "0x5929a871f57a1c5f7e4ea304cae92dacd1c1556b", transactionIndex: "0x0", v: "0xa96", value: "0x2386f26fc10000" }, 2: { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0xdae19174969a7404e222c24b6726e4d089c12768", gas: "0x15f90", gasPrice: "0x4e3b29200", hash: "0x7db7883bb23a31deb9f01b5e6fb28363b1aee1b9b6797ea8b5706be170a1187c", input: "0x", nonce: "0x2", r: "0xa8953a87c326c02da9d7a712d6c7ac0cd415cbc71ea0c24423f9e01b1fec65bd", s: "0x3faefc3a0db585a67f02996a7167890e41ff5fd8fd4be6efff3bea7a797fad29", to: "0x5929a871f57a1c5f7e4ea304cae92dacd1c1556b", transactionIndex: "0x0", v: "0xa96", value: "0x2386f26fc10000" } } }, queued: {} }
很明显,当中间的nonce被补齐之后,原来处于queued当中的交易被放置到了pending中。
4. 经验之谈
前文提到的如何处理过期交易中提到了补齐 nonce 和设置 –txpool.lifetime 也是基于今天这批文章讲述的基础逻辑。除此之外,我们还要了解一下 –txpool.accountqueue参数,它定义了每个账户在本节点 queued 中存放的最多的交易个数,默认是 64 个交易。
另外为了避免手续费过低导致交易一直存在于 txpool 当中占用内存,可以通过 console 设置手续费的最低值:
>miner.setGasPrice(51000000000) true
或者在启动参数上添加:
--gasprice "51000000000"
下一章:以太坊 交易、执行、存储
以太坊 交易结构、执行、存储:一、 交易的结构1. Transaction结构交易结构定义在 core/types/transaction.go 中:type Transaction struct { //交易数据 data ...