Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

blockwatcher-types

blockwatcher-types is blockwatcher’s vocabulary crate: the canonical value model every decoder normalizes into, the shapes of every configuration resource (Network, Spec, Monitor, SinkDef, …), the schema types a predicate type-checks against, and the deterministic identifiers that let a downstream consumer deduplicate under at-least-once delivery. Nothing here runs anything: there is no pipeline, no async runtime, no retry loop. This crate only defines what everything else in the workspace passes around.

Its dependency list is the point, not an implementation detail: serde, serde_json, num-bigint, and indexmap, and nothing else. Every other crate in the core ring (blockwatcher-ports, blockwatcher-core, blockwatcher-api) and every module crate builds on this vocabulary without pulling in a chain SDK, an HTTP client, or a storage driver by way of it, because there is nothing in this crate’s own tree for them to pull in.

Key takeaways

  • blockwatcher-types is blockwatcher’s vocabulary crate: the canonical value model, every resource shape, the schema types a predicate checks against, and the deterministic identifiers a consumer deduplicates by.
  • Nothing here runs anything: no pipeline, no async runtime, no retry loop; this crate only defines what everything else passes around.
  • It depends on serde, serde_json, num-bigint, and indexmap, and nothing else, which is what lets every other crate build on this vocabulary without pulling in a chain SDK, an HTTP client, or a storage driver by way of it.

Responsibilities

  • Define Value, the canonical value model every decoded field is expressed in: see Chain-agnosticism for the full discussion; this page does not repeat it.
  • Define the configuration resource shapes: Network, Spec, Monitor, SinkDef, ModuleSel, RawSelector, DeliveryRetry, ResourceKind, VersionedRecord (the JSON-serializable structs a storage backend persists and the HTTP API accepts and returns). Monitor.gate is Option<ModuleSel>; config structs live with the modules (threshold / max_once in blockwatcher-gates), not a closed MonitorGate enum in types. GateHitRecord and GateMeta are the persisted journal shapes (gate.rs).
  • Define the schema vocabulary a predicate type-checks against: ValueType, EventSchema, FieldSchema, SchemaSet, and the FieldResolution/ FieldOrigin pair SchemaSet::resolve returns.
  • Define the pipeline’s own data shapes: RawEvent/RawPayload (what a source emits), DecodedEvent (what a decoder produces), Match (what a matcher accepts), SinkEvent (what a sink receives: Match or Retracted), and DeadLetter (what a sink gives up on).
  • Define Cursor and Checkpoint, the position and resume-point types every source’s progress is tracked in, without interpreting either one.
  • Define the deterministic identifier types: the string_id!-built newtypes (NetworkId, MonitorId, SinkId, SpecId, ChainKind) and MatchId, whose derive function is the one hashing algorithm in the crate.
  • Define SecretRef, the env:NAME reference type, and note that resolve is the one place this crate touches the outside world at all.

Not this crate’s job: deciding what a chain-specific decoder does with any of these shapes (blockwatcher-evm and future chain modules own that); defining the port traits that operate on them (blockwatcher-ports); running a pipeline, retrying a delivery, or persisting a resource (blockwatcher-core, blockwatcher-storage); the predicate language itself: this crate defines the schema vocabulary a predicate checks against, not the grammar or evaluator (blockwatcher-expr, see Predicates and the expression language).

Key types and traits

NameKindRole
ValueenumThe canonical value model; see Chain-agnosticism
Network, Spec, Monitor, SinkDef, ModuleSelstructConfiguration resource shapes stored via the Storage port and exposed over the HTTP API
RawSelector, DeliveryRetry, ResourceKind, VersionedRecordstruct/enumSupporting resource vocabulary: a monitor’s selector entry, the engine’s delivery-retry policy, the four resource kinds, and storage’s optimistic-concurrency envelope
ValueTypeenumThe type each schema field is declared at, shaped after Value’s own variants
EventKindstructAn open, string_id!-built vocabulary of occurrence kinds (event/function_call/transaction, or a chain family’s own); carried by both EventSchema and DecodedEvent, and hashed directly in MatchId::derive
EventSchema, FieldSchemastructOne declared occurrence and one declared field within it
SchemaSetstructThe fields and namespaces a compiled monitor makes available for a predicate to check against; resolve is the field-path resolution algorithm every predicate compile runs against
FieldResolution, FieldOriginenumThe three-way outcome of SchemaSet::resolve and whether a resolved field was declared or derived through an array
Cursor, CheckpointstructA source’s position in its own feed, and the resume point (cursor plus opaque source_state) a network’s pipeline persists
RawEvent, RawPayloadstruct/enumWhat a source emits into the pipeline, before decoding
DecodedEventstructThe normalized output of decoding: kind, name, canonical Value fields, cursor
Match, SinkEvent, DeadLetterstruct/enumOne accepted occurrence (with its deterministic MatchId), the closed event a sink delivers (Match or Retracted { match_id }), and the record of one that exhausted its delivery budget
MatchId, NetworkId, MonitorId, SinkId, SpecId, ChainKindstructDeterministic and open-vocabulary identifier newtypes; MatchId::derive is the crate’s one hashing algorithm
SecretRefstructAn env:NAME reference; resolve is the crate’s one I/O operation

How data flows through it

This crate has no pipeline of its own: its “flow” is the sequence of types that hand off from one pipeline stage to the next, all defined here even though the stages themselves live in other crates:

flowchart LR
    Config["Network / Spec / Monitor / SinkDef<br/>(write time)"] -.->|"read by"| Decoder
    Config -.->|"read by"| Matcher
    Source -->|"RawEvent / RawPayload"| Decoder
    Decoder -->|"DecodedEvent<br/>(Value tree, via SchemaSet)"| Matcher
    Matcher -->|"Match<br/>(MatchId::derive)"| SinkEvent
    SinkEvent -->|"Sink::deliver"| Sink
    Sink -.->|"on exhausted retries"| DeadLetter

Every arrow above is a type this crate defines; every box is a port implementation that lives elsewhere. SchemaSet::resolve runs once, at write time, when a monitor’s predicate is compiled against its selectors’ schemas, and never again while that pipeline is running: a decoded event at evaluation time is read through the already-resolved Ast, not re-resolved against the schema.

Neighbours

blockwatcher-types depends on the following in production (nothing else in the workspace):

  • serde: (de)serialization derive support
  • serde_json: JSON wire format
  • num-bigint: arbitrary-precision integers
  • indexmap: ordered maps

In [dev-dependencies] only:

  • proptest: property tests in id.rs over MatchId::derive

The following crates depend on it directly (per the dependency table):

  • blockwatcher-ports: trait boundary for all modules
  • blockwatcher-core: engine pipeline
  • blockwatcher-api: HTTP routes
  • blockwatcher-expr: predicate language and matcher module
  • blockwatcher-evm: EVM chain module
  • blockwatcher-evm-testkit: EVM module tests
  • blockwatcher-testkit: workspace test fixtures
  • blockwatcher-storage: storage module
  • blockwatcher-sinks: sink modules
  • blockwatcher (binary): process binary

blockwatcher-rpc reaches it only transitively, through blockwatcher-ports.

Reading the source

  1. Start at lib.rs: the module list, the re-exports, and the doc comment stating the crate’s one I/O exception (SecretRef::resolve).
  2. value.rs: the Value enum, its decimal/hex wire encoding, and from_json/get_path, the two general-purpose helpers built on it.
  3. schema.rs: ValueType, EventKind (an open vocabulary, not limited to the three constants it ships), EventSchema, SchemaSet, and resolve, the field-path resolution algorithm every predicate compile depends on.
  4. resource.rs: the configuration resource shapes: Network, Spec, Monitor, SinkDef, DeliveryRetry, RawSelector, ResourceKind, VersionedRecord. Monitor.gate is Option<ModuleSel>.
  5. gate.rs: GateHitRecord, GateMeta, GATE_HITS_CAP.
  6. id.rs: the string_id! macro, the identifier newtypes it builds, and MatchId::derive’s hashing algorithm.
  7. event.rs: the pipeline’s own data shapes: RawEvent, RawPayload, DecodedEvent, Match, SinkEvent, DeadLetter.
  8. cursor.rs: Cursor and Checkpoint.
  9. hex.rs: the internal 0x-hex serde helpers Value and RawPayload both serialize through.
  10. secret.rs: SecretRef, and the crate’s one I/O exception.