进入的路径
已签名交易
校验签名 · 格式 · 账户状态 · 发送方付不付得起
丢弃,并返回原因不是悄无声息——会告诉提交者为什么
交易存储
向对等节点广播
组批,然后交给共识
校验又便宜又本地,这样下游那些昂贵的资源就只花在真有可能执行的交易上。
拒绝
接受
交易如何暂存
接受下来的交易不是塞在一个扁平队列里。存储在同一份集合之上维护了好几个视图,每个视图回答一个不同的问题。同一份已接受交易集合在它之上的五个视图,不是五个队列
按账户排序对这个发送方来说,下一笔该是哪一笔?
优先级索引在所有发送方之间,应该先把什么交给共识?
过期索引什么已经过了期限?
时间线索引这个对等节点还没听说过什么?
停放区什么格式正确,但还不具备资格?
停放区正是集成缺陷现形的地方:一笔合法、但对它的发送方来说还轮不到的交易会被停放,而不是被拒——前面的缺口一补上就立刻可用。
停放区最值得留意,因为有一类集成缺陷正是在这里现形的。一笔交易可以完全合法,却仍然轮不到它——最常见的原因是同一账户更早的那笔还没到,或者还没被提交。这种交易会进停放区,不会被拒:它还留着,前面的缺口一补上就立刻可用。所以乱序提交的客户端只会慢,不会失败;但客户端要是始终不去补那个缺口,这些活儿就一直停在那里,直到过期。
时间线索引让对等传播变成增量的。每个对等节点都按“已经被服务到时间线的哪个位置”来跟踪,一次广播只发它缺的那部分,不把整个池子重发一遍。时间线是分桶的,不是一条单队列,这样流量很大的发送方也霸占不了每一个广播位。
传播与公平
节点不是各自独立地知道一笔交易的。接受了交易的节点会把它广播给对等节点,而对等节点并不一视同仁——上游的优先,好让交易朝着能对它采取行动的验证者走,不在网络里均匀扩散。 公平性靠近期历史来跟踪,不是逐条消息去强制。内存池维护一份滚动记录,记下最近是哪些发送方、哪些类型的流量吃掉了容量,再据此决定接下来先服务谁。靠的就是这个机制:某个账户的下单撤单风暴挤不掉网络的其余部分,同时又不用上按账户的硬性限流——那种限流会误伤正常做市。交接给共识
共识不从内存池里一笔一笔取交易。交易先汇成批次,批次在后台传给各验证者,一路收确认,直到发起方能证明网络里已经有足够多的节点拿着它。到这时候,区块提案才能引用它。 于是区块提案带的是批次摘要,不是交易本体:吞吐量上去了,共识消息还是那么大。已提交的区块也总是可重放的,因为它背后的数据在被引用之前就已经证明可用。这个证明怎么形成、怎么用,见 IntentionBFT。这也是为什么进了内存池不等于进了区块。一笔交易被接受、被存下、被广播,只是进了队列,还没有被排序。在共识提交包含它的那个区块之前,这笔交易的命运没有哪一部分是定下来的。
这对客户端意味着什么
- 提交时被拒是有用信息:它发生在交易存下来之前,给出的原因指向客户端自己能改的东西。
- 没动静不等于被拒:交易可能正停在客户端自己制造的缺口后面。要自己跟踪提交了什么、上链了什么,别把收不到确认当成被丢弃。
- 账户内部的顺序重要,账户之间的顺序不重要:同一个发送方的两笔交易有确定的先后;不同发送方的两笔交易由共识排序,不看谁先提交。
- 撤单在内存池里没有特权:撤单优先是内核执行的性质——同一个区块内,撤单先于主动吃单执行——不是排队的性质。