> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 内存池

> 在共识把交易提交进区块之前，交易如何被准入、暂存、排序和传播。

内存池夹在已签名交易和已提交区块之间，决定什么值得留、同一个发送方的交易按什么顺序变得可用、哪些对等节点会听说某笔交易，以及需求超过容量时丢掉什么。

对交易网络来说，这是承重结构，不是附属品。下单和撤单的流量是突发的，集中在少数发送方手里，对延迟的敏感还不对称：撤单迟到，比订单迟到更糟。下面这些结构之所以存在，就是因为单一的先进先出队列对上面每一点都应付不了。

<h2 id="the-path-in">
  进入的路径
</h2>

<div className="dg" data-dg="mempool-admission">
  <div className="dg-c" style={{aspectRatio:"720 / 252"}}>
    <svg className="dg-w" viewBox="0 0 720 252" aria-hidden="true">
      <path className="dg-wire dg--blue" d="M 124.00 145.00 L 145.60 145.00" />

      <path className="dg-head dg--blue" d="M 152.00 145.00 L 145.60 149.40 L 145.60 140.60 Z" />

      <path className="dg-wire dg--orange" d="M 340.00 145.00 L 356.00 145.00 L 356.00 46.00 L 365.60 46.00" />

      <path className="dg-head dg--orange" d="M 372.00 46.00 L 365.60 50.40 L 365.60 41.60 Z" />

      <path className="dg-wire dg--sky" d="M 340.00 145.00 L 365.60 145.00" />

      <path className="dg-head dg--sky" d="M 372.00 145.00 L 365.60 149.40 L 365.60 140.60 Z" />

      <path className="dg-wire dg--sky dg-soft" d="M 541.00 145.00 L 566.40 120.45" />

      <path className="dg-head dg--sky" d="M 571.00 116.00 L 569.46 123.61 L 563.34 117.28 Z" />

      <path className="dg-wire dg--sky dg-soft" d="M 541.00 145.00 L 566.55 171.40" />

      <path className="dg-head dg--sky" d="M 571.00 176.00 L 563.39 174.46 L 569.71 168.34 Z" />
    </svg>

    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"46.8254%",width:"16.6667%",height:"21.4286%"}}><span className="dg-t">已签名交易</span></div>
    <div className="dg-b dg--yellow" style={{left:"21.6667%",top:"42.8571%",width:"25.0000%",height:"29.3651%"}}><span className="dg-t">校验</span><span className="dg-s">签名 · 格式 · 账户状态 · 发送方付不付得起</span></div>
    <div className="dg-b dg--orange dg-left" style={{left:"52.2222%",top:"7.9365%",width:"47.7778%",height:"20.6349%"}}><span className="dg-t">丢弃，并返回原因</span><span className="dg-s">不是悄无声息——会告诉提交者为什么</span></div>
    <div className="dg-b dg--sky" style={{left:"52.2222%",top:"46.8254%",width:"22.2222%",height:"21.4286%"}}><span className="dg-t">交易存储</span></div>
    <div className="dg-b dg--sky" style={{left:"80.0000%",top:"36.5079%",width:"20.0000%",height:"19.0476%"}}><span className="dg-t">向对等节点广播</span></div>
    <div className="dg-b dg--sky" style={{left:"80.0000%",top:"60.3175%",width:"20.0000%",height:"19.0476%"}}><span className="dg-t">组批，然后交给共识</span></div>
    <div className="dg-free" style={{left:"0.0000%",top:"85.7143%",width:"100.0000%"}}><div className="dg-n">校验又便宜又本地，这样下游那些昂贵的资源就只花在真有可能执行的交易上。</div></div>
    <div className="dg-lbl" style={{left:"49.4444%",top:"36.5079%"}}>拒绝</div>
    <div className="dg-lbl" style={{left:"49.4444%",top:"51.9841%"}}>接受</div>
  </div>
</div>

交易存下来之前先过校验。校验又便宜又本地——签名、格式、账户状态，以及发送方付不付得起他要的东西——这样下游那些昂贵的资源就只花在真有可能执行的交易上。在这里被拒，会把原因返回给提交者，交易不会悄无声息地消失。

<h2 id="how-transactions-are-held">
  交易如何暂存
</h2>

接受下来的交易不是塞在一个扁平队列里。存储在同一份集合之上维护了好几个视图，每个视图回答一个不同的问题。

<div className="dg" data-dg="mempool-views">
  <div className="dg-c" style={{aspectRatio:"720 / 332"}}>
    <svg className="dg-w" viewBox="0 0 720 332" aria-hidden="true">
      <path className="dg-wire dg-soft" d="M 195.00 148.00 L 245.00 36.00" />

      <path className="dg-wire dg-soft" d="M 195.00 148.00 L 245.00 92.00" />

      <path className="dg-wire dg-soft" d="M 195.00 148.00 L 245.00 148.00" />

      <path className="dg-wire dg-soft" d="M 195.00 148.00 L 245.00 204.00" />

      <path className="dg-wire dg-soft" d="M 195.00 148.00 L 245.00 260.00" />
    </svg>

    <div className="dg-b dg--blue dg-round" style={{left:"0.0000%",top:"31.0241%",width:"26.3889%",height:"27.1084%"}}><span className="dg-t">同一份已接受交易集合</span><span className="dg-s">在它之上的五个视图，不是五个队列</span></div>
    <div className="dg-b dg--sky" style={{left:"34.7222%",top:"3.0120%",width:"65.2778%",height:"15.6627%"}}><span className="dg-t">按账户排序</span><span className="dg-s">对这个发送方来说，下一笔该是哪一笔？</span></div>
    <div className="dg-b dg--sky" style={{left:"34.7222%",top:"19.8795%",width:"65.2778%",height:"15.6627%"}}><span className="dg-t">优先级索引</span><span className="dg-s">在所有发送方之间，应该先把什么交给共识？</span></div>
    <div className="dg-b dg--sky" style={{left:"34.7222%",top:"36.7470%",width:"65.2778%",height:"15.6627%"}}><span className="dg-t">过期索引</span><span className="dg-s">什么已经过了期限？</span></div>
    <div className="dg-b dg--sky" style={{left:"34.7222%",top:"53.6145%",width:"65.2778%",height:"15.6627%"}}><span className="dg-t">时间线索引</span><span className="dg-s">这个对等节点还没听说过什么？</span></div>
    <div className="dg-b dg--yellow" style={{left:"34.7222%",top:"70.4819%",width:"65.2778%",height:"15.6627%"}}><span className="dg-t">停放区</span><span className="dg-s">什么格式正确，但还不具备资格？</span></div>
    <div className="dg-free" style={{left:"0.0000%",top:"89.1566%",width:"100.0000%"}}><div className="dg-n">停放区正是集成缺陷现形的地方：一笔合法、但对它的发送方来说还轮不到的交易会被停放，而不是被拒——前面的缺口一补上就立刻可用。</div></div>
  </div>
</div>

| 视图        | 回答的问题                |
| --------- | -------------------- |
| **按账户排序** | 对这个发送方来说，下一笔该是哪一笔？   |
| **优先级索引** | 在所有发送方之间，应该先把什么交给共识？ |
| **过期索引**  | 什么已经过了期限，应该被移除？      |
| **时间线索引** | 这个对等节点还没听说过什么？       |
| **停放区**   | 什么格式正确，但还不具备资格？      |

**停放区**最值得留意，因为有一类集成缺陷正是在这里现形的。一笔交易可以完全合法，却仍然轮不到它——最常见的原因是同一账户更早的那笔还没到，或者还没被提交。这种交易会进停放区，不会被拒：它还留着，前面的缺口一补上就立刻可用。所以乱序提交的客户端只会慢，不会失败；但客户端要是始终不去补那个缺口，这些活儿就一直停在那里，直到过期。

**时间线索引**让对等传播变成增量的。每个对等节点都按“已经被服务到时间线的哪个位置”来跟踪，一次广播只发它缺的那部分，不把整个池子重发一遍。时间线是分桶的，不是一条单队列，这样流量很大的发送方也霸占不了每一个广播位。

<h2 id="dissemination-and-fairness">
  传播与公平
</h2>

节点不是各自独立地知道一笔交易的。接受了交易的节点会把它广播给对等节点，而对等节点并不一视同仁——上游的优先，好让交易朝着能对它采取行动的验证者走，不在网络里均匀扩散。

公平性靠近期历史来跟踪，不是逐条消息去强制。内存池维护一份滚动记录，记下最近是哪些发送方、哪些类型的流量吃掉了容量，再据此决定接下来先服务谁。靠的就是这个机制：某个账户的下单撤单风暴挤不掉网络的其余部分，同时又不用上按账户的硬性限流——那种限流会误伤正常做市。

<h2 id="handoff-to-consensus">
  交接给共识
</h2>

共识不从内存池里一笔一笔取交易。交易先汇成**批次**，批次在后台传给各验证者，一路收确认，直到发起方能证明网络里已经有足够多的节点拿着它。到这时候，区块提案才能引用它。

于是区块提案带的是批次摘要，不是交易本体：吞吐量上去了，共识消息还是那么大。已提交的区块也总是可重放的，因为它背后的数据在被引用之前就已经证明可用。这个证明怎么形成、怎么用，见 [IntentionBFT](/zh/protocol/architecture/intention-bft)。

<Note>
  这也是为什么进了内存池不等于进了区块。一笔交易被接受、被存下、被广播，只是进了队列，还没有被排序。在共识提交包含它的那个区块之前，这笔交易的命运没有哪一部分是定下来的。
</Note>

<h2 id="what-this-means-for-a-client">
  这对客户端意味着什么
</h2>

* **提交时被拒是有用信息**：它发生在交易存下来之前，给出的原因指向客户端自己能改的东西。
* **没动静不等于被拒**：交易可能正停在客户端自己制造的缺口后面。要自己跟踪提交了什么、上链了什么，别把收不到确认当成被丢弃。
* **账户内部的顺序重要，账户之间的顺序不重要**：同一个发送方的两笔交易有确定的先后；不同发送方的两笔交易由共识排序，不看谁先提交。
* **撤单在内存池里没有特权**：撤单优先是[内核执行](/zh/protocol/architecture/kernel)的性质——同一个区块内，撤单先于主动吃单执行——不是排队的性质。
