Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dc44135
docs(nitronode): reorg fix spec
nksazonov Jun 5, 2026
7b10976
feat(nitronode): add ConfirmationGate reorg protection
nksazonov Jun 8, 2026
be1d73f
feat(nitronode): expose confirmation_delay_secs via GetConfig API
nksazonov Jun 8, 2026
1d1b830
feat(nitronode): implement IsContractEventProcessed pre-check in reac…
nksazonov Jun 8, 2026
ccadb6c
feat(nitronode): implement startup reconciliation walk (§4.4)
nksazonov Jun 8, 2026
43d0850
docs(nitronode): reconcile reorg-fix spec with implementation
nksazonov Jun 8, 2026
2564636
feat(nitronode): bypass confirmation gate for historical event replay
nksazonov Jun 8, 2026
b1fb759
feat: address some docs comments
nksazonov Jun 10, 2026
ce01987
fix(nitronode/listener): route fresh historical events to gate
nksazonov Jun 10, 2026
5e234bb
fix(nitronode/reconciler): improve findCommonAncestor
nksazonov Jun 10, 2026
76fc4cb
refactor(nitronode/listener): own timestamp via ensureBlockTimestamp
nksazonov Jun 10, 2026
4ff2a73
refactor(nitronode/gate): rewrite ConfirmationGate per simplification…
nksazonov Jun 10, 2026
7e0996b
chore(nitronode/gate): rework tests and update spec for simplified gate
nksazonov Jun 10, 2026
c56163d
fix(nitronode/confirmation_gate): small fixes
nksazonov Jun 11, 2026
22159a9
docs(nitronode): move reorg-fix-spec.md to nitronode/docs/reorg-fix.md
nksazonov Jun 11, 2026
2ead611
docs(nitronode/reconciler): note empty-store vs full-depth-reorg conf…
nksazonov Jun 11, 2026
c398e28
feat(nitronode/gate): propagate handler errors via fatal channel
nksazonov Jun 11, 2026
4e7b954
refactor(nitronode/store): collapse IsContractEventPresent into IsCon…
nksazonov Jun 11, 2026
3512884
fix(nitronode/reactor): return pre-check error and unify onEventProce…
nksazonov Jun 11, 2026
fcc4f8e
refactor(nitronode/gate): replace fatal channel with closure-pattern …
nksazonov Jun 14, 2026
33c4b3a
fix(nitronode/listener): drop Removed:true live logs on no-gate path
nksazonov Jun 14, 2026
4709879
fix(nitronode/reconciler): resume from orphaned latest when all store…
nksazonov Jun 14, 2026
1bc0d56
docs: cross-link confirmation gate and normalize confirmation_delay_secs
nksazonov Jun 14, 2026
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
3 changes: 3 additions & 0 deletions docs/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ types:
- name: contract_address
type: string
description: Address of the main contract on this blockchain
- name: confirmation_delay_secs
type: integer
description: Per-chain reorg-protection window in seconds; events are buffered for this duration before being committed. 0 disables the gate. See nitronode/docs/reorg-fix.md.

- balance_entry:
description: Balance for a specific asset
Expand Down
1 change: 1 addition & 0 deletions docs/protocol/security-and-limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ In the current protocol version, participants MUST trust nodes for:
- **Off-chain transfer routing** — when a user sends funds off-chain to another party, the node must countersign both the sender's state (decreasing their allocation) and the receiver's credit state (increasing theirs); the on-chain contract cannot enforce atomicity between two independent channel updates. A malicious node could apply the sender's state while withholding the receiver's credit, capturing the transferred funds. Users must trust the node to faithfully execute both legs of every off-chain transfer.
- **Asset-symbol equivalence** — the node operator controls which chain-specific tokens are configured under a single unified asset symbol. The protocol treats all tokens sharing a symbol as fully fungible 1:1 representations of the same asset, so off-chain credit denominated in that asset can be redeemed from any of those token inventories regardless of which one originally backed it (the validator binds unchanneled credit to the chain/token chosen at first channel creation, enforcing only that the asset symbol matches). This is intended behaviour that enables cross-chain redemption. Operators MUST therefore configure only economically equivalent (1:1 redeemable) tokens under one symbol; grouping non-equivalent tokens (e.g. a test token and production USDC) under the same symbol would let credit sourced from the cheap inventory be redeemed against the valuable one. Token equivalence cannot be verified programmatically and is an operator configuration responsibility.
- **Signature validator registry** — the node operator controls which additional signature validators are registered on the ChannelHub contract. A malicious or compromised node could register a validator that approves forged user signatures, then use it to create channels or close them without the user's knowledge. A 1-day activation delay (`VALIDATOR_ACTIVATION_DELAY`) creates an observable window before any newly registered validator can be used. Users MUST monitor the `ValidatorRegistered` event on the ChannelHub contract and SHOULD revoke all ERC20 approvals granted to ChannelHub immediately upon detecting an unexpected registration. Once registered, a validator cannot be deactivated — the 1-day window is the entire response budget. Users SHOULD avoid granting large standing ERC20 approvals to ChannelHub to cap worst-case exposure.
- **Chain reorg depth** — the node credits off-chain balances after observing on-chain events. To bound reorg risk, each chain has a `confirmation_delay_secs` window before events are committed; events whose block is reorged out within that window are discarded. When the configured delay is set below the chain's hard finality time, a residual risk remains: a deeper reorg can leave the off-chain state with no on-chain backing. Operators MUST set `confirmation_delay_secs` to at least the chain's finality time when this residual exposure is unacceptable. See [Reorg-Protection Confirmation Gate](../../nitronode/docs/reorg-fix.md).

Participants do not need to trust nodes for:

Expand Down
4 changes: 4 additions & 0 deletions nitronode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Nitronode is built with a modular architecture:

- **RPC Server**: WebSocket-based JSON-RPC server handling client requests.
- **Blockchain Listeners**: Monitors on-chain events from Nitrolite `ChannelHub` contracts across multiple chains.
- **Confirmation Gate**: Per-chain reorg-protection buffer between the listener and event handlers. Delays event delivery by `confirmation_delay_secs` so that events whose blocks are reorged out before the window elapses are dropped instead of committed. See [docs/reorg-fix.md](docs/reorg-fix.md).
- **Event Handlers**: Processes blockchain events to update internal channel and user states.
- **Storage Layer**:
- **Database Store**: Persistent storage for channels, states, and transactions (supports SQLite and PostgreSQL).
Expand Down Expand Up @@ -53,6 +54,7 @@ blockchains:
id: 80002
contract_address: "0x9d1E88627884e066B81A02d69BCB2437a520534C"
block_step: 1000
confirmation_delay_secs: 10 # reorg-protection window; 0 disables. See docs/reorg-fix.md.

- name: base_sepolia
id: 84532
Expand Down Expand Up @@ -128,6 +130,7 @@ docker run -p 7824:7824 -e NITRONODE_SIGNER_KEY=... nitronode
nitronode/
├── api/ # JSON-RPC request handlers
├── config/ # Default configurations and migrations
├── docs/ # Component design notes (e.g. reorg-fix.md)
├── event_handlers/ # Logic for reacting to blockchain events
├── metrics/ # Prometheus telemetry implementation
├── store/ # Persistence layer (SQL and Memory)
Expand Down Expand Up @@ -157,6 +160,7 @@ The following protocol operations are fully specified in [protocol-description.m
- [Nitrolite Protocol Overview](../protocol-description.md)
- [Communication Flows](../docs/communication_flows/)
- [API Reference](../docs/api.yaml)
- [Reorg-Protection Confirmation Gate](docs/reorg-fix.md)

## License

Expand Down
7 changes: 4 additions & 3 deletions nitronode/api/node_v1/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (

func mapBlockchainV1(blockchain core.Blockchain) rpc.BlockchainInfoV1 {
return rpc.BlockchainInfoV1{
Name: blockchain.Name,
BlockchainID: strconv.FormatUint(blockchain.ID, 10),
ChannelHubAddress: blockchain.ChannelHubAddress,
Name: blockchain.Name,
BlockchainID: strconv.FormatUint(blockchain.ID, 10),
ChannelHubAddress: blockchain.ChannelHubAddress,
ConfirmationDelaySecs: blockchain.ConfirmationDelaySecs,
}
}

Expand Down
6 changes: 3 additions & 3 deletions nitronode/api/rpc_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ type RPCRouter struct {
}

type RPCRouterConfig struct {
NodeVersion string
MinChallenge uint32
MaxChallenge uint32
NodeVersion string
MinChallenge uint32
MaxChallenge uint32
MaxParticipants int
MaxSessionDataLen int
MaxSessionKeyIDs int
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- +goose Up

ALTER TABLE contract_events ADD COLUMN block_hash CHAR(66) NOT NULL DEFAULT '';

-- +goose Down

ALTER TABLE contract_events DROP COLUMN block_hash;
Loading
Loading