以太坊小知识(一) ——交易池和交易流

1、交易池容纳的交易量默认是有上限(不是无上限的)

以太坊的txpool中的pending集合(miner是从pending中拿交易组装block的)中容纳的交易数量默认设置为最大4096。但是在Geth v1.6.2中支持外部重置交易池默认配置。他们是–txpool标志。具体是–txpool.globalslots value。在Parity v1.6.8中也支持外部设置,具体是--tx-queue-size LIMIT。Parity默认是1024。一个账户默认只能放16条交易到pending中,满了以后,第17条乃至以后更多的交易会有一套规则来替换先前位于pending中的16条交易。其中一个规则就是GasPrice的值。下面是以太坊的go实现客户端geth的默认TxPoolConfig设置:

// DefaultTxPoolConfig contains the default configurations for the transaction// pool.
var DefaultTxPoolConfig = TxPoolConfig{
    Journal:   "transactions.rlp",
    Rejournal: time.Hour,

    PriceLimit: 1,
    PriceBump:  10,

    AccountSlots: 16,
    GlobalSlots:  4096,
    AccountQueue: 64,
    GlobalQueue:  1024,

    Lifetime: 3 * time.Hour,
}

这是Geth v1.6.2发行版的日志:

--txpool.pricelimit value    Minimum gas price limit to enforce for acceptance into the pool (default: 1)
--txpool.pricebump value     Price bump percentage to replace an already existing transaction (default: 10)
--txpool.accountslots value  Minimum number of executable transaction slots guaranteed per account (default: 16)
--txpool.globalslots value   Maximum number of executable transaction slots for all accounts (default: 4096)
--txpool.accountqueue value  Maximum number of non-executable transaction slots permitted per account (default: 64)
--txpool.globalqueue value   Maximum number of non-executable transaction slots for all accounts (default: 1024)
--txpool.lifetime value      Maximum amount of time non-executable transaction are queued (default: 3h0m0s)

以太坊的queue集合中容纳的交易数量默认设置为最大1024。相同账户默认只能放64条交易到queue中。

因此,以太坊中交易池最大容量是4096+1024,即5120条交易。

注意:Bitcoin中是mempool。以太坊的Geth实现中是transaction pool(txpool);以太坊的Parity实现中是tx queue。

有关交易池默认配置的代码在/core/tx_pool.go#line125-156。

我们从Etherscan中可以观察到pending中的交易数量。但是往往大于默认设置4096。这应该是Etherscan开发团队重置了默认参数。(话说与Etherscan交互的以太坊实例应该是Etherscan自己维护的集群啥的)

参考资料
When there are too many pending transactions
What is the max size of transactions can clients like geth keep in txpool?

2、矿工不能在一个区块中打包任意多的tx(只能尽可能多的打包),因为一个区块有gaslimit限制。

补充
ethstas.net是一个非常不错的以太坊浏览器。该站点统计了ethstas官方(不知道有没有官方?)维护或者其他机构加入的以太坊全节点。

站点展示的数据有GasLimit,GasPrice,挖矿难度Difficulty等常见参数外。最值得一提地是,它统计了39个全节点之间的区块传播(block propagation)时间的直方图。以及39个节点的peers数量,本地pending中的交易数量,本地最新区块高度(last block),以及区块中包含的交易数量,node延时多少等等关于p2p网络的参数。这是该浏览器的亮点。

3、每个miner在运行一个以太坊实例时是可以按需修改gasprice的值的。

例如,miner A可能设置的gp下限为10Gwei,miner B可能设置的gp下限为1Gwei。当miner A找到一个nonce值,将其组装的block广播到miner B时,B只会做新区块的头部验证工作(/consensus/ethash/conseneus.go#(ethash *Ethash) VerifyHeader),在区块头验证时会检查GasLimit,GasUsed等字段。

本地节点产生的交易不会进行gp检测。具体的代码体现在(pool *TxPool)validateTx中。具体是:

func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
    //...
    // Drop non-local transactions under our own minimal accepted gas price
    local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
    if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
        return ErrUnderpriced
    }
    //...
}

若local为true,则!local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 会出现短路,不会计算第二个条件,即不会比较gp。但是从网络中同步的交易就要进行gp检查了。以太坊的交易池设计是不是很人性化,给自己开后门(不比较),给别人关门(必须比较)。

func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
    // If the transaction is already known, discard it
    hash := tx.Hash()
    if pool.all.Get(hash) != nil {
        log.Trace("Discarding already known transaction", "hash", hash)
        return false, fmt.Errorf("known transaction: %x", hash)
    }
    // If the transaction fails basic validation, discard it
    if err := pool.validateTx(tx, local); err != nil {
        log.Trace("Discarding invalid transaction", "hash", hash, "err", err)
        invalidTxCounter.Inc(1)
        return false, err
    }
   //...

}

不管是从网络接收的交易还是本地节点产生的交易,最后的处理逻辑都是走的(pool *TxPool) add方法。

//本地节点产生单条交易
func (pool *TxPool) AddLocal(tx *types.Transaction) error {
    return pool.addTx(tx, !pool.config.NoLocals)
}

//网络中接收的单条交易
func (pool *TxPool) AddRemote(tx *types.Transaction) error {
    return pool.addTx(tx, false)
}

//本地节点产生一批交易
func (pool *TxPool) AddLocals(txs []*types.Transaction) []error {
    return pool.addTxs(txs, !pool.config.NoLocals)
}

//从网络中接收一批交易
func (pool *TxPool) AddRemotes(txs []*types.Transaction) []error {
    return pool.addTxs(txs, false)
}

因此,一条交易进入交易池的的核心逻辑之一还是(pool *TxPool) add方法。

4、一条交易的数据流
参考资料
Life Cycle of an Ethereum Transaction
这篇文章写得比较好,有对应的中译版本。
5、学习资料

1、https://blog.infura.io/
2、https://media.consensys.net

总结
本文记录了一些以太坊的小知识。

你可能感兴趣的:(Ethereum)