Ethereum Events Attestation
Namada stores events from the smart contracts of its bridge. Namada includes events that have been voted by at least one
validator, but will not act on them until they have been seen
by a
weighted average of more than of the voting power
available across all epochs of the tally. Generically, Namada considers some
tally seen
if it satisfies the following inequality:
Where is the set of epochs when took place, is a function mapping an epoch to its total voting power and is the total fractional voting power aggregated behind a tally at some epoch (for instance, at , ). The only way to break the security of is if a set of Byzantine validators control an average of of the voting power available across .
There will be multiple types of events emitted. Validators should ignore
improperly formatted events. ABI encoded events from Ethereum are
decoded by ethbridge-rs
(opens in a new tab),
and converted to a Rust enum type (EthereumEvent
) by Namada validators
before being included in vote extensions or stored on chain.
pub enum EthereumEvent {
// Namada has different variants corresponding to different types
// of raw events Namada receives from Ethereum
TransfersToNamada(Vec<TransferToNamada>)
// ...
}
Validators may never vote more than once on a given event. To ensure
that this invariant is held, Namada keep track of who voted on some event
and events are timed out if they are not seen
within the span of
unbonding_length
epochs, which corresponds to the period of time
necessary for bonded tokens to be returned to an address (check the
relevant proof-of-stake
section for more
details). Timing out an event consists of removing all its associated
state from storage. Therefore, this mechanism serves another purpose:
purging forged events from storage, voted on by Byzantine validators.
Minimum confirmations
There will be a protocol-specified minimum number of confirmations that events must reach on the Ethereum chain, before validators can vote to include them on Namada. This minimum number of confirmations will be changeable via governance.
TransferToNamada
events may include a custom minimum number of
confirmations that must be at least the protocol-specified minimum
number of confirmations. However, this value is initially set to
100.
Validators must not vote to include events that have yet to meet the
required number of confirmations. Votes on unconfirmed events will
eventually time out in storage.
Assuming that an honest
majority of validators is operating Namada (i.e. by
voting power across all epochs in the tally), only confirmed events will
eventually become seen
.
Vote extension protocol transactions
A batch of Ethereum events newly confirmed at some block height is included by some validator in a protocol transaction Namada dubs the Ethereum events vote extension. The vote extension is signed by the protocol key of , uniquely identifying 's vote on some Ethereum event at .
Namada validators perform votes on other kinds of data, namely:
- Validator set update vote extensions. As the name implies, these are used to sign off on the set of validators of some epoch by the validators of epoch . The proof (quorum of signatures) is used to update the validator set reflected in the Ethereum smart contracts of the bridge.
- Bridge pool root vote extensions. These vote extensions are used to reach a quorum decision on the most recent root and nonce of the Ethereum bridge pool.
These protocol transactions are only ever included on-chain if the Tendermint version that is being used to run the ledger does not include a full ABCI++ (i.e. ABCI 2.0) implementation. Alternatively, nodes receive vote extensions from the previously decided block, never lagging behind more than one block height. Without ABCI++, vote extensions are included in arbitrary blocks, based on the contention of block proposers' mempools. This effectively means that a vote extension for some height may only be acted upon at some height , or even evicted from the mempool altogether, if it is never proposed.
Storage
To make including new events easy, Namada takes the approach of always overwriting the state with the new state rather than applying state diffs. The storage keys involved are:
# all values are Borsh-serialized
/eth_msgs/$msg_hash/body: EthereumEvent # the event to be voted on
/eth_msgs/$msg_hash/seen_by: BTreeMap<Address, BlockHeight> # mapping from a validator to the Namada height at which the event was observed to be confirmed by said validator
/eth_msgs/$msg_hash/voting_power: BTreeMap<Epoch, FractionalVotingPower> # mapping from epochs to aggregated voting powers, in the form of a reduced fraction < 1 e.g. (2, 3)
/eth_msgs/$msg_hash/seen: bool # >= 2/3 average voting power across all epochs it was voted on
/eth_msgs/$msg_hash/voting_started_epoch: Epoch # epoch when the tally started
Where $msg_hash
is the SHA256 digest of the Borsh serialization of
some EthereumEvent
.
Changes to this /eth_msgs
storage subspace are only ever made by nodes
as part of the ledger code based on the aggregate of votes by validators
for specific events. That is, changes to /eth_msgs
happen in block n
in a deterministic manner based on the votes included in the block
proposal for block n
. Depending on the underlying Tendermint version,
these votes will either be included as vote extensions or as protocol
transactions.
The /eth_msgs
storage subspace will belong to the EthBridge
validity
predicate. It should disallow any changes to this storage from wasm
transactions.
Including events into storage
For every Namada block proposal, the proposer should include the votes for events from other validators into their proposal. If the underlying Tendermint version supports vote extensions, consensus invariants guarantee that a quorum of votes from the previous block height can be included. Otherwise, validators can only submit votes by broadcasting protocol transactions, which comes with less guarantees (i.e. no consensus finality).
The vote of a validator should include each event of the Ethereum blocks their full node has seen that satisfies the following conditions:
- The event is correctly formatted.
- It has reached the required number of confirmations on the Ethereum chain.
- If a transfer to Ethereum event is detected, the underlying asset must be whitelisted on the Ethereum bridge smart contracts.
Each event that a validator is voting to include must be individually signed by them. If the validator is not voting to include any events, they must still provide a signed empty vector of events to indicate this.
The votes will include be a Borsh-serialization of something like the following:
/// This struct will be created and signed over by each
/// consensus validator, to be included as a vote extension at the end of a
/// Tendermint PreCommit phase or as Protocol Tx.
pub struct Vext {
/// The block height for which this [`Vext`] was made.
pub block_height: BlockHeight,
/// The address of the signing validator
pub validator_addr: Address,
/// The new ethereum events seen. These should be
/// deterministically ordered.
pub ethereum_events: Vec<EthereumEvent>,
}
These votes will be delivered to subsequent block proposers who will aggregate those that they can verify and will inject them into their proposal. With ABCI++ this involves creating a new protocol transaction, dubbed a digest, comprised of multiple individual votes on Ethereum events.
Validators will check the validity of Ethereum events vote extensions as
part of ProcessProposal
. This includes checking, among other things:
- The height within the vote extension is correct (e.g. not ahead of the last block height). If vote extensions are supported, it is also checked that each vote extension came from the previous height. Signing over the block height also acts as a replay protection mechanism.
- That signatures come from consensus validators, at the epoch the vote extensions originated from.
- The bridge was active when the extension was signed.
- Ethereum event nonces, to reject attempts to replay transactions through the bridge.
Furthermore, with ABCI++ enabled, the vote extensions included by the
block proposer should have a quorum of the total voting power of the
epoch of the block height behind it. Otherwise the block proposer would
not have passed the FinalizeBlock
phase of the last round of the last
block.
These checks are to prevent censorship of events from validators by the block proposer. If ABCI++ is not enabled, unfortunately these checks cannot be made.
In FinalizeBlock
, Namada derives a second transaction (the "state update"
transaction) from the vote aggregation that:
- Calculates the required changes to
/eth_msgs
storage and applies them. - Acts on any
/eth_msgs/$msg_hash
whereseen
is going fromfalse
totrue
(e.g. appropriately minting wrapped Ethereum assets).
This state update transaction will not be recorded on chain but will be deterministically derived from the protocol transaction including the aggregation of votes, which is recorded on chain. All ledger nodes will derive and apply the appropriate state changes to their own local blockchain storage.
The value of /eth_msgs/$msg_hash/seen
will also indicate if the event
has been acted upon on the Namada side. The appropriate transfers of
tokens to the given user will be included on chain free of charge and
requires no additional actions from the end user.