Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions crates/blockchain/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ use tracing::warn;
///
/// These are the names consumers address events by (the SSE `event:` line and,
/// later, `?topics=` filtering), kept separate from [`ChainEvent`] so the
/// payload stays flat with the topic travelling out-of-band. The names match
/// the Ethereum beacon-API eventstream topics (`head`, `block`,
/// `finalized_checkpoint`); `justified_checkpoint` is an ethlambda extension
/// with no beacon analog.
/// payload stays flat with the topic travelling out-of-band. Names match the
/// Ethereum beacon-API eventstream topics where an analog exists (`head`,
/// `block`, `finalized_checkpoint`, `block_gossip`); `justified_checkpoint` is
/// an ethlambda extension with no direct beacon topic.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Topic {
Head,
Block,
JustifiedCheckpoint,
FinalizedCheckpoint,
/// A block seen on gossip, before import (analog of beacon `block_gossip`).
BlockGossip,
}

impl Topic {
Expand All @@ -41,6 +43,7 @@ impl Topic {
Topic::Block => "block",
Topic::JustifiedCheckpoint => "justified_checkpoint",
Topic::FinalizedCheckpoint => "finalized_checkpoint",
Topic::BlockGossip => "block_gossip",
}
}
}
Expand All @@ -60,6 +63,7 @@ impl FromStr for Topic {
"block" => Ok(Topic::Block),
"justified_checkpoint" => Ok(Topic::JustifiedCheckpoint),
"finalized_checkpoint" => Ok(Topic::FinalizedCheckpoint),
"block_gossip" => Ok(Topic::BlockGossip),
other => Err(UnknownTopic(other.to_string())),
}
}
Expand Down Expand Up @@ -90,6 +94,9 @@ pub enum ChainEvent {
JustifiedCheckpoint { slot: u64, block: H256, state: H256 },
/// The finalized checkpoint advanced.
FinalizedCheckpoint { slot: u64, block: H256, state: H256 },
/// A block seen on gossip, before import. Analog of beacon `block_gossip`;
/// `block` is imported later once its parent chain is available.
BlockGossip { slot: u64, block: H256 },
}

impl ChainEvent {
Expand All @@ -99,6 +106,7 @@ impl ChainEvent {
ChainEvent::Block { .. } => Topic::Block,
ChainEvent::JustifiedCheckpoint { .. } => Topic::JustifiedCheckpoint,
ChainEvent::FinalizedCheckpoint { .. } => Topic::FinalizedCheckpoint,
ChainEvent::BlockGossip { .. } => Topic::BlockGossip,
}
}
}
Expand Down Expand Up @@ -297,11 +305,12 @@ mod tests {
}
}

const ALL_TOPICS: [Topic; 4] = [
const ALL_TOPICS: [Topic; 5] = [
Topic::Head,
Topic::Block,
Topic::JustifiedCheckpoint,
Topic::FinalizedCheckpoint,
Topic::BlockGossip,
];

#[tokio::test]
Expand Down Expand Up @@ -356,6 +365,10 @@ mod tests {
},
Topic::FinalizedCheckpoint,
),
(
ChainEvent::BlockGossip { slot: 1, block },
Topic::BlockGossip,
),
];
for (event, topic) in cases {
assert_eq!(event.topic(), topic);
Expand Down
4 changes: 4 additions & 0 deletions crates/blockchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,10 @@ impl Handler<InitP2P> for BlockChainServer {

impl Handler<NewBlock> for BlockChainServer {
async fn handle(&mut self, msg: NewBlock, _ctx: &Context<Self>) {
self.events.emit(ChainEvent::BlockGossip {
slot: msg.block.message.slot,
block: msg.block.message.hash_tree_root(),
});
self.on_block(msg.block);
}
}
Expand Down
5 changes: 3 additions & 2 deletions docs/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ SSZ-encoded `SignedBlock` at the latest finalized checkpoint. The genesis/anchor

### `GET /lean/v0/events`

Server-Sent Events stream (`Content-Type: text/event-stream`) of live chain events published by the blockchain actor. Four event types:
Server-Sent Events stream (`Content-Type: text/event-stream`) of live chain events published by the blockchain actor. Five event types:

Payload fields mirror the Ethereum beacon-API eventstream: `block` is the block root, `state` the state root, and `slot` stands in for the beacon `epoch`.

Expand All @@ -96,6 +96,7 @@ Payload fields mirror the Ethereum beacon-API eventstream: `block` is the block
| `block` | `{ "slot": 128, "block": "0x…" }` | A block is imported into the store |
| `justified_checkpoint` | `{ "slot": 120, "block": "0x…", "state": "0x…" }` | The justified checkpoint advances |
| `finalized_checkpoint` | `{ "slot": 96, "block": "0x…", "state": "0x…" }` | The finalized checkpoint advances |
| `block_gossip` | `{ "slot": 128, "block": "0x…" }` | A block is seen on the network, before import |

The topic name travels only on the SSE `event:` line; the `data:` line carries the flat JSON payload. Example frame:

Expand All @@ -112,7 +113,7 @@ A **required** comma-separated list of event names selects which events to strea
curl -N 'http://127.0.0.1:5052/lean/v0/events?topics=head,finalized_checkpoint'
```

Valid values are exactly the event names above: `head`, `block`, `justified_checkpoint`, `finalized_checkpoint`. As in the Beacon API `eventstream` endpoint, `topics` is mandatory: there is no "subscribe to everything" default; list the topics you want.
Valid values are exactly the event names above: `head`, `block`, `justified_checkpoint`, `finalized_checkpoint`, `block_gossip`. As in the Beacon API `eventstream` endpoint, `topics` is mandatory: there is no "subscribe to everything" default; list the topics you want.

| Status | Condition |
|--------|-----------|
Expand Down
Loading