Mempool Monitor

A Tezos node can notify applications when new operations are added to its mempool. Tezos assigns certain states to unconfirmed operations to reflect their eligibility for block inclusion.

  • applied means the operations is ready for inclusion in a future block

  • refused means the operation is permanently excluded from future considerations, possibly because its anchor branch is too old

  • branch refused means the anchor used by the operation is on a side-chain

  • branch delayed means the operation counter is in the future and another operation must be included first

  • unprocessed means the operation is new and must first be pre-validated

When opening a new monitor connection, the node first streams the list of currently applied operations and continues to stream new operations as they enter the applied state. Whenever a new block is added to the chain, the node re-evaluates its mempool, removing confirmed operations and possible changing the state of others. To reflect this change, the node closes all mempool monitoring connections which forces clients to reconnect and receive a fresh state copy.

Note that monitors in TzGo cannot be reused after close. The example below shows how to maintain a continued stream of mempool operations across connection closures. For a complete example see examples/mempool/main.go.

import "blockwatch.cc/tzgo/rpc"

// init SDK client
c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)

// define a mempool monitor pointer
var mon *rpc.MempoolMonitor

// reconnect/stream loop
for {
    // create a new monitor when none exists
    if mon == nil {
        fmt.Println("Connecting monitor")
        mon = rpc.NewMempoolMonitor()
        if err := c.MonitorMempool(ctx, mon); err != nil {
            mon.Close()
            return err
        }
    }
    // wait for the next operation
    ops, err := mon.Recv(ctx)
    if err != nil {
        if err != io.EOF {
            fmt.Println("Monitor failed")
            return err
        }
        // handle close event (a new block has been added)
        head, err := c.GetTipHeader(ctx)
        if err == nil {
            fmt.Println("Monitor closed on new block", head.Level, head.Hash)
        } else {
            fmt.Println("Monitor closed:", err)
        }
        mon = nil
        continue
    }
    // operations are recieved in chunks, here we just outpt hash and type
    for _, op := range ops {
        fmt.Println(op.Hash, op.Contents[0].OpKind())
    }
}

Last updated