Skip to content

Commit 4a19d59

Browse files
committed
docs(synthetic-logs): trim verbose comments to short why-notes
1 parent a7bfd07 commit 4a19d59

3 files changed

Lines changed: 25 additions & 73 deletions

File tree

node/src/synthetic_logs_override.rs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,15 @@
33
// Copyright (C) 2020-2026 Intergalactic, Limited (GIB).
44
// SPDX-License-Identifier: Apache-2.0
55

6-
//! Node-side `StorageOverride` wrapper for the synthetic-logs node-indexing
7-
//! variant.
6+
//! `StorageOverride` that augments Frontier's reads with synthetic ethereum txs,
7+
//! produced client-side from a block's events via
8+
//! `event_logs::synthetic_txs_from_records` (no runtime API, any runtime version).
89
//!
9-
//! Wraps the stock Frontier `StorageOverride` and augments its reads with
10-
//! synthetic ethereum txs — substrate `Transfer`/`Swapped3`/`pallet_evm` log
11-
//! events translated to ERC-20 `Transfer` / uniswap-v2 `Swap` logs. The synth
12-
//! txs are NOT in consensus state; they're produced **client-side** from the
13-
//! block's events read out of state, using the pure
14-
//! `event_logs::synthetic_txs_from_records`. Because this never invokes a
15-
//! runtime API, it works against ANY runtime version — including blocks
16-
//! produced before this runtime shipped (bounded only by whether their events
17-
//! still decode).
18-
//!
19-
//! All three views stay index-aligned (real entries first, synth appended in a
20-
//! stable order, `transaction_index` continuing from the real count) so a synth
21-
//! tx's mapping-DB index resolves consistently across them:
22-
//! - `current_transaction_statuses` / `current_receipts`: real, then synth.
23-
//! - `current_block`: synth txs appended to `transactions` (so
24-
//! `eth_getTransactionByHash`/`*_receipt` can index them — fc-rpc does
25-
//! `block.transactions[index]`), and the synth-log blooms OR'd into
26-
//! `header.logs_bloom` so `filter_range_logs`' header-bloom prefilter doesn't
27-
//! skip synth-only blocks.
10+
//! Real entries come first, synth appended in stable order, so a synth tx's index
11+
//! resolves consistently across `current_transaction_statuses`/`current_receipts`
12+
//! and `current_block.transactions` (fc-rpc indexes `block.transactions[index]`).
13+
//! Synth blooms are OR'd into the header so the `eth_getLogs` bloom prefilter
14+
//! keeps synth-only blocks.
2815
2916
use std::{marker::PhantomData, sync::Arc};
3017

pallets/synthetic-logs/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,7 @@ pub fn encode_uint256_quad(a: U256, b: U256, c: U256, d: U256) -> Vec<u8> {
226226
}
227227

228228
// --- pure log-shape builders ------------------------------------------------
229-
// The evm-log *shape* lives here, decoupled from how it's delivered (substrate
230-
// mutation hook → on-chain synth tx, or node-side eth-rpc indexing). Callers
231-
// resolve addresses; these just encode. Unit-tested for evm-client parity.
229+
// Callers resolve addresses; these just encode the evm-log shape.
232230

233231
/// ERC-20 `Transfer(from, to, value)` log emitted from `token`'s address.
234232
pub fn build_erc20_transfer_log(token: H160, from: H160, to: H160, amount: U256) -> ethereum::Log {

runtime/hydradx/src/evm/event_logs.rs

Lines changed: 16 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,15 @@
33
// Copyright (C) 2020-2026 Intergalactic, Limited (GIB).
44
// SPDX-License-Identifier: Apache-2.0
55

6-
//! Event → synthetic-eth-tx translation for the node-indexing variant.
6+
//! Pure event → synthetic-eth-tx translation for the node-indexing variant.
77
//!
8-
//! Pure translation: `synthetic_txs_from_records` turns a block's
9-
//! `EventRecord`s into synthetic `(Transaction, TransactionStatus, Receipt)`
10-
//! triples using only pure helpers (`account_to_evm_address`,
11-
//! `asset_evm_address`, the `build_*`/`assemble_synth_txs` primitives) — no
12-
//! runtime-state reads. That's what lets the SAME code serve two callers:
13-
//!
14-
//! - `SyntheticEthLogsApi` (this runtime), gathering events from
15-
//! `frame_system::Events` — fast path for the current+ runtime; and
16-
//! - the node's client-side indexer, which reads `System::Events` from state
17-
//! and calls `synthetic_txs_from_records` directly — works against ANY
18-
//! runtime version (it never invokes a runtime API), so blocks produced
19-
//! before this runtime shipped are still indexed (bounded only by whether
20-
//! their events still decode).
21-
//!
22-
//! Scope: every balance movement (orml-tokens + native HDX via pallet-balances)
23-
//! — transfer, mint, burn/slash/dust, reserve/unreserve, repatriate, and
24-
//! lock/freeze — mapped to erc20 `Transfer` so a holder's aggregated transfers
25-
//! reconstruct its transferable balance (reserved and frozen amounts move to
26-
//! distinct per-owner sentinels). Plus `Swapped3` → uniswap-v2 `Swap`, and
27-
//! `pallet_evm::Event::Log` from internal `Executor::call` paths (deduped
28-
//! against real eth txs). This is the off-chain replica of what the removed
29-
//! on-chain synthetic-logs hooks emitted.
8+
//! `synthetic_txs_from_records` turns a block's `EventRecord`s into
9+
//! `(Transaction, TransactionStatus, Receipt)` triples with no state reads, so
10+
//! the node's client-side indexer can call it for any runtime version. Balance
11+
//! movements map to erc20 `Transfer` (reserved/frozen go to per-owner sentinels
12+
//! so aggregated transfers equal an account's transferable balance), plus
13+
//! `Swapped3` → uniswap-v2 `Swap` and internal `pallet_evm::Log` (deduped vs
14+
//! real eth txs).
3015
3116
use crate::{Runtime, RuntimeEvent};
3217
use frame_support::traits::Get;
@@ -88,11 +73,8 @@ fn is_asset_address(addr: H160) -> bool {
8873
addr.0[..15] == [0u8; 15] && addr.0[15] == 1
8974
}
9075

91-
/// One erc20 `Transfer(from, to, amount)` on `asset`'s evm address, or empty for
92-
/// a zero amount. The single primitive every balance movement below maps onto:
93-
/// mint = `from` zero, burn = `to` zero, reserve/lock = `to` the owner's
94-
/// reserved/frozen sentinel. Aggregating these per (asset, holder) reconstructs
95-
/// the holder's transferable balance.
76+
/// One erc20 `Transfer` on `asset`'s evm address (empty for zero). Mint = `from`
77+
/// zero, burn = `to` zero, reserve/lock = `to` the owner's sentinel.
9678
fn transfer(asset: u32, from: H160, to: H160, amount: u128) -> Vec<(H160, ethereum::Log)> {
9779
if amount == 0 {
9880
return Vec::new();
@@ -101,14 +83,7 @@ fn transfer(asset: u32, from: H160, to: H160, amount: u128) -> Vec<(H160, ethere
10183
sp_std::vec![(addr, build_erc20_transfer_log(addr, from, to, U256::from(amount)))]
10284
}
10385

104-
/// Pure: evm logs an indexer should see for one runtime event.
105-
///
106-
/// Covers every balance movement the on-chain synthetic-logs hooks did, now
107-
/// driven off-chain from events: transfer, deposit/mint (`0x0 → who`),
108-
/// withdraw/slash/burn/dust (`who → 0x0`), reserve/unreserve (`who ↔
109-
/// reserved_address_of(who)`), reserve repatriation, plus lock/freeze (`who ↔
110-
/// frozen_address_of(who)`, by frozen delta) so a holder's aggregated transfers
111-
/// equal its transferable balance.
86+
/// Pure: the evm logs an indexer should see for one runtime event.
11287
pub fn logs_from_event(event: &RuntimeEvent) -> Vec<(H160, ethereum::Log)> {
11388
use orml_tokens::Event as Tokens;
11489
use pallet_balances::Event as Balances;
@@ -206,12 +181,8 @@ pub fn logs_from_event(event: &RuntimeEvent) -> Vec<(H160, ethereum::Log)> {
206181
| RuntimeEvent::Balances(Balances::DustLost { account: who, amount }) => {
207182
transfer(CORE_ASSET_ID, evm_addr(who), ZERO, *amount)
208183
}
209-
// Native `Slashed` can't tell free from reserved at the event level, but in
210-
// this runtime native slashing only ever hits *reserved* balance (governance
211-
// bonds/deposits via democracy & elections `slash_reserved`); there are no
212-
// free native-slash call sites. So we burn from the reserved sentinel, which
213-
// keeps transferable reconstruction correct. Revisit if a free native-slash
214-
// path is ever introduced. (orml `Slashed` splits free/reserved explicitly.)
184+
// Native slashing here only ever hits reserved balance (governance bonds via
185+
// democracy/elections); the event can't distinguish, so burn the reserved sentinel.
215186
RuntimeEvent::Balances(Balances::Slashed { who, amount }) => {
216187
transfer(CORE_ASSET_ID, reserved_address_of(evm_addr(who)), ZERO, *amount)
217188
}
@@ -350,13 +321,9 @@ pub fn synthetic_txs_from_records(
350321
) -> Vec<(Transaction, TransactionStatus, Receipt)> {
351322
let base_tx_index = real_statuses.len() as u32;
352323

353-
// Per-log dedup: the stack runner emits an `EVM::Log` event for every log,
354-
// AND a real eth tx records the same logs in its receipt — so an `EVM::Log`
355-
// already present in the real tx of its extrinsic is a duplicate. Map each
356-
// eth-tx extrinsic to a multiset of its receipt logs and drop matches
357-
// one-for-one; any EXTRA logs in that extrinsic (a separate internal
358-
// `Executor::call`) survive and are synthesized. Real tx statuses are in the
359-
// same order as their `Executed` events.
324+
// An EVM log is also recorded in the receipt of the real eth tx in its extrinsic,
325+
// so drop EVM::Log events matching that receipt (one-for-one); extra logs from a
326+
// separate internal Executor::call survive. Real statuses follow Executed order.
360327
let mut real_logs_by_ext: BTreeMap<u32, BTreeMap<LogKey, u32>> = BTreeMap::new();
361328
let mut nth_eth_tx = 0usize;
362329
for rec in records.iter() {

0 commit comments

Comments
 (0)