diff --git a/.changelog/unreleased/docs/1058-ethbridge-spec-updates.md b/.changelog/unreleased/docs/1058-ethbridge-spec-updates.md new file mode 100644 index 0000000000..3187eb6ddb --- /dev/null +++ b/.changelog/unreleased/docs/1058-ethbridge-spec-updates.md @@ -0,0 +1,2 @@ +- Update specs for Ethereum bridge and block allocator + ([#1058](https://github.com/anoma/namada/pull/1058)) \ No newline at end of file diff --git a/documentation/specs/src/SUMMARY.md b/documentation/specs/src/SUMMARY.md index 94c6390400..267409fe92 100644 --- a/documentation/specs/src/SUMMARY.md +++ b/documentation/specs/src/SUMMARY.md @@ -9,6 +9,7 @@ - [Multisignature account](./base-ledger/multisignature.md) - [Fungible token](./base-ledger/fungible-token.md) - [Replay protection](./base-ledger/replay-protection.md) + - [Block space allocator](./base-ledger/block-space-allocator.md) - [Multi-asset shielded pool](./masp.md) - [Ledger integration](./masp/ledger-integration.md) - [Asset type](./masp/asset-type.md) @@ -17,6 +18,13 @@ - [Trusted setup](./masp/trusted-setup.md) - [Interoperability](./interoperability.md) - [Ethereum bridge](./interoperability/ethereum-bridge.md) + - [Security](./interoperability/ethereum-bridge/security.md) + - [Bootstrapping](./interoperability/ethereum-bridge/bootstrapping.md) + - [Ethereum events attestation](./interoperability/ethereum-bridge/ethereum_events_attestation.md) + - [Transfers to Namada](./interoperability/ethereum-bridge/transfers_to_namada.md) + - [Transfers to Ethereum](./interoperability/ethereum-bridge/transfers_to_ethereum.md) + - [Proofs](./interoperability/ethereum-bridge/proofs.md) + - [Ethereum smart contracts](./interoperability/ethereum-bridge/ethereum_smart_contracts.md) - [IBC](./interoperability/ibc.md) - [Economics](./economics.md) - [Fee system](./economics/fee-system.md) diff --git a/documentation/specs/src/base-ledger/block-space-allocator.md b/documentation/specs/src/base-ledger/block-space-allocator.md new file mode 100644 index 0000000000..aa05dcbaea --- /dev/null +++ b/documentation/specs/src/base-ledger/block-space-allocator.md @@ -0,0 +1,206 @@ +# Block space allocator + +Block space in Tendermint is a resource whose management is relinquished to the +running application. This section covers the design of an abstraction that +facilitates the process of transparently allocating space for transactions in a +block at some height $H$, whilst upholding the safety and liveness properties +of Namada. + +## On block sizes in Tendermint and Namada + +[Block sizes in Tendermint] +(configured through the $MaxBytes$ consensus +parameter) have a minimum value of $1\ \text{byte}$, and a hard cap of $100\ +MiB$, reflecting the header, evidence of misbehavior (used to slash +Byzantine validators) and transaction data, as well as any potential protobuf +serialization overhead. Some of these data are dynamic in nature (e.g. +evidence of misbehavior), so the total size reserved to transactions in a block +at some height $H_0$ might not be the same as another block's, say, at some +height $H_1 : H_1 \ne H_0$. During Tendermint's `PrepareProposal` ABCI phase, +applications receive a $MaxTxBytes$ parameter whose value already accounts for +the total space available for transactions at some height $H$. Namada does not +rely on the $MaxTxBytes$ parameter of `RequestPrepareProposal`; instead, +app-side validators configure a $MaxProposalSize$ parameter at genesis (or +through governance) and set Tendermint blocks' $MaxBytes$ parameter to its +upper bound. + +[Block sizes in Tendermint]: + +## Transaction batch construction + +During Tendermint's `PrepareProposal` ABCI phase, Namada (the ABCI server) is +fed a set of transactions $M = \{\ tx\ |\ tx\text{ in Tendermint's mempool}\ +\}$, whose total combined size (i.e. the sum of the bytes occupied by each $tx +: tx \in M$) may be greater than $MaxProposalBytes$. Therefore, consensus round +leaders are responsible for selecting a batch of transactions $P$ whose total +combined bytes $P_{Len} \le MaxProposalBytes$. + +To stay within these bounds, block space is **allotted** to different kinds of +transactions: decrypted, protocol and encrypted transactions. Each kind of +transaction gets about $\frac{1}{3} MaxProposalBytes$ worth of allotted space, +in an abstract container dubbed the `TxBin`. A transaction $tx : tx \in M$ may +be **dumped** to a `TxBin`, resulting in a successful operation, or an error, +if $tx$ is **rejected** due to lack of space in the `TxBin` or if $tx$'s size +**overflows** (i.e. does not fit in) the `TxBin`. Block proposers continue +dumping transactions from $M$ into a `TxBin` $B$ until a rejection error is +encountered, or until there are no more transactions of the same type as $B$'s +in $M$. The `BlockSpaceAllocator` contains three `TxBin` instances, responsible +for holding decrypted, protocol and encrypted transactions. + +block space allocator tx bins + +During occasional Namada protocol events, such as DKG parameter negotiation, +all available block space should be reserved to protocol transactions, +therefore the `BlockSpaceAllocator` was designed as a state machine, whose +state transitions depend on the state of Namada. The states of the +`BlockSpaceAllocator` are the following: + +1. `BuildingDecryptedTxBatch` - As the name implies, during this state the +decrypted transactions `TxBin` is filled with transactions of the same type. +Honest block proposers will only include decrypted transactions in a block at a +fixed height $H_0$ if encrypted transactions were available at $H_0 - 1$. The +decrypted transactions should be included in the same order of the encrypted +transactions of block $H_0 - 1$. Likewise, all decrypted transactions available +at $H_0$ must be included. +2. `BuildingProtocolTxBatch` - In a similar manner, during this +`BlockSpaceAllocator` state, the protocol transactions `TxBin` is populated +with transactions of the same type. Contrary to the first state, allocation +stops as soon as the respective `TxBin` runs out of space for some +$tx_{Protocol} : tx_{Protocol} \in M$. The `TxBin` for protocol transactions is +allotted half of the remaining block space, after decrypted transactions have +been **allocated**. +3. `BuildingEncryptedTxBatch` - This state behaves a lot like the previous +state, with one addition: it takes a parameter that guards the encrypted +transactions `TxBin`, which in effect splits the state into two sub-states. +When `WithEncryptedTxs` is active, we fill block space with encrypted +transactions (as the name implies); orthogonal to this mode of operation, there +is `WithoutEncryptedTxs`, which, as the name implies, does not allow encrypted +transactions to be included in a block. The `TxBin` for encrypted transactions +is allotted $\min(R,\frac{1}{3} MaxProposalBytes)$ bytes, where $R$ is the +block space remaining after allocating space for decrypted and protocol +transactions. +4. `FillingRemainingSpace` - The final state of the `BlockSpaceAllocator`. Due +to the short-circuit behavior of a `TxBin`, on allocation errors, some space +may be left unutilized at the end of the third state. At this state, the only +kinds of +transactions that are left to fill the available block space are +of type encrypted and protocol, but encrypted transactions are forbidden +to be included, to avoid breaking their invariant regarding +allotted block space (i.e. encrypted transactions can only occupy up to +$\frac{1}{3}$ of the total block space for a given height $H$). As such, +only protocol transactions are allowed at the fourth and final state of +the `BlockSpaceAllocator`. + +For a fixed block height $H_0$, if at $H_0 - 1$ and $H_0$ no encrypted +transactions are included in the respective proposals, the block decided for +height $H_0$ will only contain protocol transactions. Similarly, since at most +$\frac{1}{3}$ of the available block space at a fixed height $H_1$ is reserved +to encrypted transactions, and decrypted transactions at $H_1+1$ will take up +(at most) the same amount of space as encrypted transactions at height $H_1$, +each transaction kind's `TxBin` will generally get allotted about $\frac{1}{3}$ +of the available block space. + +### Example + +Consider the following diagram: + +block space allocator example + +We denote `D`, `P` and `E` as decrypted, protocol and encrypted transactions, +respectively. + +* At height $H$, block space is evenly divided in three parts, one for each +kind of transaction type. +* At height $H+1$, we do not include encrypted transactions in the proposal, +therefore protocol transactions are allowed to take up to $\frac{2}{3}$ of the +available block space. +* At height $H+2$, no encrypted transactions are included either. Notice that +no decrypted transactions were included in the proposal, since at height $H+1$ +we did not decide on any encrypted transactions. In sum, only protocol +transactions are included in the proposal for the block with height $H+2$. +* At height $H+3$, we propose encrypted transactions once more. Just like in +the previous scenario, no decrypted transactions are available. Encrypted +transactions are capped at $\frac{1}{3}$ of the available block space, so the +remaining $\frac{1}{2} - \frac{1}{3} = \frac{1}{6}$ of the available block +space is filled with protocol transactions. +* At height $H+4$, allocation returns to its normal operation, thus block space +is divided in three equal parts for each kind of transaction type. + +## Transaction batch validation + +Batches of transactions proposed during ABCI's `PrepareProposal` phase are +validated at the `ProcessProposal` phase. The validation conditions are +relaxed, compared to the rigid block structure imposed on blocks during +`PrepareProposal` (i.e. with decrypted, protocol and encrypted transactions +appearing in this order, as [examplified above](#example)). Let us fix $H$ as +the height of the block $B$ currently being decided through Tendermint's +consensus mechanism, $P$ as the batch of transactions proposed at $H$ as $B$'s +payload and $V$ as the current set of active validators. To vote on $P$, each +validator $v \in V$ checks: + +* If the length of $P$ in bytes, defined as $P_{Len} := \sum_{tx \in +P} \text{size\_of}(tx)$, is not greater than $MaxProposalBytes$. +* If $P$ does not contain more than $\frac{1}{3} MaxProposalBytes$ worth of +encrypted transactions. + - While not directly checked, our batch construction invariants guarantee +that we will constrain decrypted transactions to occupy up to $\frac{1}{3} +MaxProposalBytes$ bytes of the available block space at $H$ (or any block +height, in fact). +* If all decrypted transactions from $H-1$ have been included in the proposal +$P$, for height $H$. +* That no encrypted transactions were included in the proposal $P$, if no +encrypted transactions should be included at $H$. + - N.b. the conditions to reject encrypted transactions are still not clearly + specced out, therefore they will be left out of this section, for the + time being. + +Should any of these conditions not be met at some arbitrary round $R$ of $H$, +all honest validators $V_h : V_h \subseteq V$ will reject the proposal $P$. +Byzantine validators are permitted to re-order the layout of $P$ typically +derived from the [`BlockSpaceAllocator`](#transaction-batch-construction) $A$, +under normal operation, however this should not be a compromising factor of the +safety and liveness properties of Namada. The rigid layout of $B$ is simply a +consequence of $A$ allocating in different phases. + +### On validator set updates + +Validator set updates, one type of protocol transactions decided through BFT +consensus in Namada, are fundamental to the liveness properties of the Ethereum +bridge, thus, ideally we would also check if these would be included once per +epoch at the `ProcessProposal` stage. Unfortunately, achieving a quorum of +signatures for a validator set update between two adjacent block heights +through ABCI alone is not feasible. Hence, the Ethereum bridge is not a live +distributed system, since there is the possibility to cross an epoch boundary +without constructing a valid proof for some validator set update. In practice, +however, it is nearly impossible for the bridge to get "stuck", as validator +set updates are eagerly issued at the start of an epoch, whose length should be +long enough for consensus(*) to be reached on a single validator set update. + +(*) Note that we loosely used consensus here to refer to the process of +acquiring a quorum (e.g. more than $\frac{2}{3}$ of voting power, by stake) of +signatures on a single validator set update. "Chunks" of a proof (i.e. +individual votes) are decided and batched together, until a complete proof is +constructed. + +We cover validator set updates in detail in [the Ethereum bridge section]. + +[the Ethereum bridge section]: ../interoperability/ethereum-bridge.md + +## Governance + +Governance parameter update proposals for $MaxProposalBytes_H$ that take effect +at $H$, where $H$ is some arbitrary block height, should be such that +$MaxProposalBytes_H \ge \frac{1}{3} MaxProposalBytes_{H-1}$, to leave enough +room for all decrypted transactions from $H-1$ at $H$. Subsequent block heights +$H' : H' > H$ should eventually lead to allotted block space converging to about +$\frac{1}{3} MaxProposalBytes_H$ for each kind of transaction type. diff --git a/documentation/specs/src/interoperability/ethereum-bridge.md b/documentation/specs/src/interoperability/ethereum-bridge.md index 6d5370ea4e..ed2dc58c00 100644 --- a/documentation/specs/src/interoperability/ethereum-bridge.md +++ b/documentation/specs/src/interoperability/ethereum-bridge.md @@ -2,37 +2,35 @@ The Namada - Ethereum bridge exists to mint ERC20 tokens on Namada which naturally can be redeemed on Ethereum at a later time. Furthermore, it -allows the minting of wrapped tokens on Ethereum backed by escrowed assets on -Namada. +allows the minting of wrapped NAM (wNAM) tokens on Ethereum. The Namada Ethereum bridge system consists of: + * An Ethereum full node run by each Namada validator, for including relevant Ethereum events into Namada. * A set of validity predicates on Namada which roughly implements [ICS20](https://docs.cosmos.network/v0.42/modules/ibc/) fungible token transfers. * A set of Ethereum smart contracts. -* A relayer for submitting transactions to Ethereum +* An automated process to send validator set updates to the Ethereum smart + contracts. +* A relayer binary to aid in submitting transactions to Ethereum This basic bridge architecture should provide for almost-Namada consensus security for the bridge and free Ethereum state reads on Namada, plus bidirectional message passing with reasonably low gas costs on the Ethereum side. -## Security -On Namada, the validators are full nodes of Ethereum and their stake is also -accounting for security of the bridge. If they carry out a forking attack -on Namada to steal locked tokens of Ethereum their stake will be slashed on Namada. -On the Ethereum side, we will add a limit to the amount of assets that can be -locked to limit the damage a forking attack on Namada can do. To make an attack -more cumbersome we will also add a limit on how fast wrapped Ethereum assets can -be redeemed from Namada. This will not add more security, but rather make the -attack more inconvenient. - -## Ethereum Events Attestation -We want to store events from the smart contracts of our bridge onto Namada. We -will include events that have been seen by at least one validator, but will not -act on them until they have been seen by at least 2/3 of voting power. +## Topics + - [Bootstrapping](./ethereum-bridge/bootstrapping.md) + - [Security](./ethereum-bridge/security.md) + - [Ethereum Events Attestation](./ethereum-bridge/ethereum_events_attestation.md) + - [Transfers from Ethereum to Namada](./ethereum-bridge/transfers_to_namada.md) + - [Transfers from Namada to Ethereum](./ethereum-bridge/transfers_to_ethereum.md) + - [Proofs and validator set updates](./ethereum-bridge/proofs.md) + - [Smart Contracts](./ethereum-bridge/ethereum_smart_contracts.md) + +## Resources which may be helpful There will be multiple types of events emitted. Validators should ignore improperly formatted events. Raw events from Ethereum are converted to a @@ -392,4 +390,3 @@ Namada. - [ICS20](https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer) - [Rainbow Bridge contracts](https://github.com/aurora-is-near/rainbow-bridge/tree/master/contracts) - [IBC in Solidity](https://github.com/hyperledger-labs/yui-ibc-solidity) - diff --git a/documentation/specs/src/interoperability/ethereum-bridge/bootstrapping.md b/documentation/specs/src/interoperability/ethereum-bridge/bootstrapping.md new file mode 100644 index 0000000000..876f83ec09 --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/bootstrapping.md @@ -0,0 +1,117 @@ +# Bootstrapping the bridge + +## Overview + +The Ethereum bridge is not enabled at the launch of a Namada chain. Instead, +there are two governance parameters: + +- `eth_bridge_proxy_address` +- `eth_bridge_wnam_address` + +Both are initialized to the zero Ethereum address +(`"0x0000000000000000000000000000000000000000"`). An overview of the steps to +enable the Ethereum bridge for a given Namada chain are: + +- A governance proposal should be held to agree on a block height `h` at which + to launch the Ethereum bridge by means of a hard fork. +- If the proposal passes, the Namada chain must halt after finalizing block + `h-1`. This requires +- The [Ethereum bridge smart contracts](./ethereum_smart_contracts.md) are + deployed to the relevant EVM chain, with the active validator set at block + height `h` as the initial validator set that controls the bridge. +- Details are published so that the deployed contracts can be verified by anyone + who wishes to do so. +- If active validators for block height `h` regard the deployment as valid, the + chain should be restarted with a new genesis file that specifies + `eth_bridge_proxy_address` as the Ethereum address of the proxy contract. + +At this point, the bridge is launched and it may start being used. Validators' +ledger nodes will immediately and automatically coordinate in order to craft the +first validator set update protocol transaction. + +## Facets + +### Governance proposal + +The governance proposal can be freeform and simply indicate what the value of +`h` should be. Validators should then configure their nodes to halt at this +height. The `grace_epoch` is arbitrary as there is no code to be executed as +part of the proposal, instead validators must take action manually as soon as +the proposal passes. The block height `h` must be in an epoch that is strictly +greater than `voting_end_epoch`. + +### Value for launch height `h` + +The active validator set at the launch height chosen for starting the Ethereum +bridge will have the extra responsibility of restarting the chain if they +consider the deployed smart contracts as valid. For this reason, the validator +set at this height must be known in advance of the governance proposal +resolving, and a channel set up for offchain communication and co-ordination of +the chain restart. In practise, this means the governance proposal to launch the +chain should commit to doing so within an epoch of passing, so that the +validator set is definitely known in advance. + +### Deployer + +Once the smart contracts are fully deployed, only the active validator set for +block height `h` should have control of the contracts so in theory anyone could +do the Ethereum bridge smart contract deployment. + +### Backing out of Ethereum bridge launch + +If for some reason the validity of the smart contract deployment cannot be +agreed upon by the validators who will responsible for restarting Namada, it +must remain possible to restart the chain with the Ethereum bridge still not +enabled. + +## Example + +In this example, all epochs are assumed to be `100` blocks long, and the active +validator set does not change at any point. + +- A governance proposal is made to launch the Ethereum bridge at height `h = + 3400`, i.e. the first block of epoch `34`. + +```json +{ + "content": { + "title": "Launch the Ethereum bridge", + "authors": "hello@heliax.dev", + "discussions-to": "hello@heliax.dev", + "created": "2023-01-01T08:00:00Z", + "license": "Unlicense", + "abstract": "Halt the chain and launch the Ethereum bridge at Namada block height 3400", + "motivation": "", + }, + "author": "hello@heliax.dev", + "voting_start_epoch": 30, + "voting_end_epoch": 33, + "grace_epoch": 33, +} +``` + +- The governance proposal passes at block `3300` (the first block of epoch `33`) + +- Validators for epoch `33` manually configure their nodes to halt after having + finalized block `3399`, before that block is reached + +- The chain halts after having finalized block `3399` (the last block of epoch + `33`) + +- Putative Ethereum bridge smart contracts are deployed at this point, with the + proxy contract located at `0x00000000000000000000000000000000DeaDBeef` + +- Verification of the Ethereum bridge smart contracts take place + +- Validators coordinate to craft a new genesis file for the chain restart at + `3400`, with the governance parameter `eth_bridge_proxy_address` set to + `0x00000000000000000000000000000000DeaDBeef` and `eth_bridge_wnam_address` at + `0x000000000000000000000000000000000000Cafe` + +- The chain restarts at `3400` (the first block of epoch `34`) + +- The first ever validator set update (for epoch `35`) becomes possible within a + few blocks (e.g. by block `3410`) + +- A validator set update for epoch `35` is submitted to the Ethereum bridge + smart contracts diff --git a/documentation/specs/src/interoperability/ethereum-bridge/ethereum_events_attestation.md b/documentation/specs/src/interoperability/ethereum-bridge/ethereum_events_attestation.md new file mode 100644 index 0000000000..1c8bdd3095 --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/ethereum_events_attestation.md @@ -0,0 +1,147 @@ +# Ethereum Events Attestation + +We want to store events from the smart contracts of our bridge onto Namada. We +will include events that have been seen by at least one validator, but will not +act on them until they have been seen by at least 2/3 of voting power. + +There will be multiple types of events emitted. Validators should +ignore improperly formatted events. Raw events from Ethereum are converted to a +Rust enum type (`EthereumEvent`) by Namada validators before being included +in vote extensions or stored on chain. + +```rust +pub enum EthereumEvent { + // we will have different variants here corresponding to different types + // of raw events we receive from Ethereum + TransfersToNamada(Vec) + // ... +} +``` + +Each event will be stored with a list of the validators that have ever seen it +as well as the fraction of total voting power that has ever seen it. +Once an event has been seen by 2/3 of voting power, it is locked into a +`seen` state, and acted upon. + +There is no adjustment across epoch boundaries - e.g. if an event is seen by 1/3 +of voting power in epoch n, then seen by a different 1/3 of voting power in +epoch m>n, the event will be considered `seen` in total. Validators may never +vote more than once for a given event. + +## 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 but is initially set to __100__. + +Validators must not vote to include events that have not met the required +number of confirmations. Voting on unconfirmed events is considered a +slashable offence. + +## Storage +To make including new events easy, we take 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 +/eth_msgs/\$msg_hash/seen_by : BTreeSet
+/eth_msgs/\$msg_hash/voting_power: (u64, u64) # reduced fraction < 1 e.g. (2, 3) +/eth_msgs/\$msg_hash/seen: bool +``` + +`\$msg_hash` is the SHA256 digest of the Borsh serialization of the relevant +`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, block 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 the events of the Ethereum blocks they +have seen via their full node such that: +1. It's correctly formatted. +2. It's reached the required number of confirmations on the Ethereum chain + +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. +```rust +/// This struct will be created and signed over by each +/// active 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, +} +``` + +These votes will be given to the next block proposer who will +aggregate those that it can verify and will inject a signed protocol +transaction into their proposal. + +Validators will check this transaction and the validity of the new votes as +part of `ProcessProposal`, this includes checking: +- signatures +- that votes are really from active validators +- the calculation of backed voting power + +If vote extensions are supported, it is also checked that each vote extension +came from the previous round, requiring validators to sign over the Namada block +height with their vote extension. Signing over the block height also acts as +a replay protection mechanism. + +Furthermore, 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 vote extensions are not +enabled, unfortunately these checks cannot be made. + +In `FinalizeBlock`, we derive a second transaction (the "state update" +transaction) from the vote aggregation that: +- calculates the required changes to `/eth_msgs` storage and applies it +- acts on any `/eth_msgs/\$msg_hash` where `seen` is going from `false` to `true` + (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. \ No newline at end of file diff --git a/documentation/specs/src/interoperability/ethereum-bridge/ethereum_smart_contracts.md b/documentation/specs/src/interoperability/ethereum-bridge/ethereum_smart_contracts.md new file mode 100644 index 0000000000..669122b420 --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/ethereum_smart_contracts.md @@ -0,0 +1,182 @@ +# Ethereum Smart Contracts + +## Contracts + +There are five smart contracts that make up an Ethereum bridge deployment. + +- Proxy +- Bridge +- Governance +- Vault +- wNAM + +### Proxy + +The _Proxy_ contract serves as a dumb storage for holding the addresses of other +contracts, specifically the _Governance_ contract, the _Vault_ contract and the +current _Bridge_ contract. Once deployed, it is modifiable only by the +_Governance_ contract, to update the address for which contract is the current +_Bridge_ contract. + +The _Proxy_ contract is fixed forever once the bridge has been deployed. + +### Bridge + +The _Bridge_ contract is the only contract that unprivileged users of the bridge +may interact with. It provides methods for transferring ERC20s to Namada +(holding them in escrow in the _Vault_), as well as releasing escrowed ERC20s +from the _Vault_ for transfers made from Namada to Ethereum. It holds a +whitelist of ERC20s that may cross the bridge, and this whitelist may be updated +by the _Governance_ contract. + +### Governance + +The _Governance_ contract may "upgrade" the bridge by updating the _Proxy_ +contract to point to a new _Bridge_ contract and/or a new _Governance_ contract. +It may also withdraw all funds from the _Vault_ to any specified Ethereum +address, if a quorum of validators choose to do so. + +### wNAM + +The _wNAM_ contract is a simple ERC20 token with a fixed supply, which is all +minted when the bridge is first deployed. After initial deployment, the entire +supply of _wNAM_ belongs to the _Vault_ contract. As NAM is transferred from +Namada to Ethereum, wNAM may be released from the _Vault_ by the _Bridge_. + +The _wNAM_ contract is fixed forever once the bridge has been deployed. + +### Vault + +The _Vault_ contract holds in escrow any ERC20 tokens that have been sent over +the bridge to Namada, as well as a supply of _wNAM_ ERC20s to represent NAM that +has been sent from Namada to Ethereum. Funds held by the _Vault_ may only be +spendable by the current _Bridge_ contract. When ERC20 tokens are transferred +from Ethereum to Namada, they must be deposited to the _Vault_ via the _Bridge_ +contract. + +The _Vault_ contract is fixed forever once the bridge has been deployed. + +## Namada-side configuration + +When an account on Namada becomes a validator, they must provide two Ethereum +secp256k1 keys: + +- the bridge key - a hot key for normal operations +- the governance key - a cold key for exceptional operations, like emergency + withdrawal from the bridge + +These keys are used to control the bridge smart contracts, via signing of +messages. Validators should be challenged periodically to prove they still retain +knowledge of their governance key, which is not regularly used. + +## Deployment + +The contracts should be deployable by anyone to any EVM chain using an automated +script. The following configuration should be agreed up front by Namada +governance before deployment: + +- details of the initial active validator set that will control the bridge - + specifically, for each validator: + - their hot Ethereum address + - their cold Ethereum address + - their voting power on Namada for the epoch when the bridge will launch +- the total supply of the wNAM ERC20 token, which will represent Namada-native + NAM on the EVM chain +- an initial whitelist of ERC20 tokens that may cross the bridge from Ethereum + to Namada - specifically, for each whitelisted ERC20: + - the Ethereum address of the ERC20 contract + - a cap on the total amount that may cross the bridge, in units of ERC20 + +After a deployment has finished successfully, the deployer must not have any +privileged control of any of the contracts deployed. Any privileged actions must +only be possible via a message signed by a validator set that the smart +contracts are storing details of. + +## Communication + +### From Ethereum to Namada + +A Namada chain's validators are configured to listen to events emitted by the +smart contracts pointed to by the _Proxy_ contract. The address of the _Proxy_ +contract is set in a governance parameter in Namada storage. Namada validators +treat emitted events as authoritative and take action on them. Namada also knows +the address of the _wNAM_ ERC20 contract via a governance parameter, and treats +transfers of this ERC20 to Namada as an indication to release native NAM from +the `#EthBridgeEscrow` account on Namada, rather than to mint a wrapped ERC20 as +is the case with all other ERC20s. + +### From Namada to Ethereum + +At any time, the _Governance_ and _Bridge_ contracts must store: + +- a hash of the current Namada epoch's active validator set +- a hash of another epoch's active validator set. When the bridge is first + deployed, this will also be the current Namada epoch's active validator set, + but after the first validator set update is submitted to the _Governance_ + smart contract, this hash will always be an adjacent Namada epoch's active + validator set i.e. either the previous epoch's, or the next epoch's + +In the case of the _Governance_ contract, these are hashes of a map of +validator's _cold_ key addresses to their voting powers, while for the _Bridge_ +contract it is hashes of a map of validator's _hot_ key addresses to their +voting powers. Namada validators may post signatures as on chain of relevant +messages to be relayed to the Ethereum bridge smart contracts (e.g. validator +set updates, pending transfers, etc.). Methods of the Ethereum bridge smart +contracts should generally accept: + +- some message +- full details of some active validator set (i.e. relevant Ethereum addresses + + voting powers) +- signatures over the message by validators from the this active validator set + +Given this data, anyone should be able to make the relevant Ethereum smart +contract method call, if they are willing to pay the Ethereum gas. A call is +then authorized to happen if: + +- The active validator set specified in the call hashes to *either* of the + validator set hashes stored in the smart contract +- A quorum (i.e. more than 2/3 by voting power) of the signatures over the + message are valid + +### Validator set updates + +Initial deployment aside, at the beginning of each epoch, the smart contracts +will contain details of the current epoch's validator set and the previous +epoch's validator set. Namada validators must endeavor to sign details of the +next epoch's validator set and post them on Namada chain in a protocol +transaction. Details of the next epoch's validator set and a quorum of +signatures over it by validators from the current epoch's validator set must +then be relayed to the _Governance_ contract before the end of the epoch, which +will update both the _Governance_ and _Bridge_ smart contracts to have the hash +of the next epoch's validator set rather than the previous epoch's validator +set. This should happen before the current Namada epoch ends. If this does not +happen, then the Namada chain must either halt or not progress to the next +epoch, to avoid losing control of the bridge. + +When a validator set update is submitted, the hashes for the oldest validator +set are effectively "evicted" from the _Governance_ and _Bridge_ smart +contracts. At that point, messages signed by that evicted validator set will no +longer be accepted by the bridge. + +#### Example flow + +- Namada epoch `10` begins. Currently, the _Governance_ contract knows the + hashes of the validator sets for epochs `9` and `10`, as does the _Bridge_ + contract. +- Validators for epoch `10` post signatures over the hash of details of the + validator set for epoch `11` to Namada as protocol transactions +- A point is reached during epoch `10` at which a quorum of such signatures is + present on the Namada chain +- A relayer submits a validator set update for epoch `11` to _Governance_, using + a quorum of signatures from the Namada chain +- The _Governance_ and _Bridge_ contracts now know the hashes of the validator + sets for epochs `10` and `11`, and will accept messages signed by either of + them. It will no longer accept messages signed by the validator set for epoch + `9`. +- Namada progresses to epoch `11`, and the flow repeats + +NB: the flow for when the bridge has just launched is similar, except the +contracts know the details of only one epoch's validator set - the launch +epoch's. E.g. if the bridge launches at epoch `10`, then initially the contracts +know the hash only for epoch `10` and not epochs `10` and `11`, until the first +validator set update has been submitted \ No newline at end of file diff --git a/documentation/specs/src/interoperability/ethereum-bridge/proofs.md b/documentation/specs/src/interoperability/ethereum-bridge/proofs.md new file mode 100644 index 0000000000..5c67147d68 --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/proofs.md @@ -0,0 +1,101 @@ +# Proofs + +A proof for the bridge is a quorum of signatures by a valid validator set. A +bridge header is a proof attached to a message understandable to the +Ethereum smart contracts. For transferring value to Ethereum, a proof is a +signed Merkle tree root and inclusion proofs of asset transfer messages +understandable to the Ethereum smart contracts, as described in the section on +[batching](transfers_to_ethereum.md#batching) + +A message for transferring value to Ethereum is a `TransferToNamada` +instance as described +[here](./transfers_to_ethereum.md#bridge-pool-validity-predicate). + +Additionally, when the validator set changes, the smart contracts on +Ethereum must be updated so that it can continue to recognize valid proofs. +Since the Ethereum smart contract should accept any bridge +header signed by 2 / 3 of the staking validators, it needs up-to-date +knowledge of: +- The current validators' public keys +- The current stake of each validator + +This means that by the end of every Namada epoch, a special transaction must be +sent to the Ethereum smart contracts detailing the new public keys and stake +of the new validator set. This message must also be signed by at least 2 / 3 +of the current validators as a "transfer of power". + +If vote extensions are available, a fully crafted transfer of power message +will be made available on-chain. Otherwise, this message must be crafted +offline by aggregating the protocol txs from validators in which the sign +over the new validator set. + +If vote extensions are available, this signed data can be constructed +using them. Otherwise, validators must send protocol txs to be included on +the ledger. Once a quorum exist on chain, they can be aggregated into a +single message that can be relayed to Ethereum. Signing an +invalid validator transition set will be considered a slashable offense. + +Due to asynchronicity concerns, this message should be submitted well in +advance of the actual epoch change. It should happen at the beginning of each +new epoch. Bridge headers to ethereum should include the current Namada epoch +so that the smart contract knows how to verify the headers. In short, there +is a pipelining mechanism in the smart contract - the active validators for epoch `n` submit details of the active validator set for epoch `n+1`. + +Such a message is not prompted by any user transaction and thus will have +to be carried out by a _bridge relayer_. Once the necessary data to +construct the transfer of power message is on chain, any time afterwards a +Namada bridge process may take it to craft the appropriate header to the +Ethereum smart contracts. + +The details on bridge relayers are below in the corresponding section. + +Signing incorrect headers is considered a slashable offense. Anyone witnessing +an incorrect header that is signed may submit a complaint (a type of transaction) +to initiate slashing of the validator who made the signature. + +## Namada Bridge Relayers + +Validator changes must be turned into a message that can be communicated to +smart contracts on Ethereum. These smart contracts need this information +to verify proofs of actions taken on Namada. + +Since this is protocol level information, it is not user prompted and thus +should not be the responsibility of any user to submit such a transaction. +However, any user may choose to submit this transaction anyway. + +This necessitates a Namada node whose job it is to submit these transactions on +Ethereum by the conclusion of each Namada epoch. This node is called the +__bridge relayer__. In theory, since this message is publicly available +on the blockchain, anyone can submit this transaction, but only the +bridge relayer will be directly compensated by Namada. + +The bridge relayer will be chosen to be the proposer of the first block of the +new epoch. Anyone else may relay this message, but must pay for the fees out of +their own pocket. + +All Namada validators will have an option to serve as bridge relayer and +the Namada ledger will include a process that does the relaying. Since all +Namada validators are running Ethereum full nodes, they can monitor +that the message was relayed correctly by the bridge relayer. + +If the Ethereum event spawned by relaying their message gets accepted by the +Ethereum state inclusion onto Namada, new NAM tokens will be minted to +reward them. The reward amount shall be a protocol parameter that can be +changed via governance. It should be high enough to cover necessary gas fees. + +### Recovering from an update failure + +If vote extensions are not available, we cannot guarantee that a quorum of +validator signatures can be gathered for the message that updates the +validator set before the epoch ends. + +If a significant number of validators become inactive in the next epoch, we +need a means to complete validator set update. Until this is done, the +bridge will halt. + +In this case, the validators from that epoch will need to craft a +transaction with a quorum of signatures offline and submit it on-chain. This +transaction should include the validator set update. + +The only way this is impossible is if more than 1/3 of the validators by +stake from that epoch delete their ethereum keys, which is extremely unlikely. diff --git a/documentation/specs/src/interoperability/ethereum-bridge/security.md b/documentation/specs/src/interoperability/ethereum-bridge/security.md new file mode 100644 index 0000000000..0b022b2e0e --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/security.md @@ -0,0 +1,10 @@ +# Security + +On Namada, the validators are full nodes of Ethereum and their stake is also +accounting for security of the bridge. If they carry out a forking attack +on Namada to steal locked tokens of Ethereum their stake will be slashed on Namada. +On the Ethereum side, we will add a limit to the amount of assets that can be +locked to limit the damage a forking attack on Namada can do. To make an attack +more cumbersome we will also add a limit on how fast wrapped Ethereum assets can +be redeemed from Namada. This will not add more security, but rather make the +attack more inconvenient. diff --git a/documentation/specs/src/interoperability/ethereum-bridge/transfers_to_ethereum.md b/documentation/specs/src/interoperability/ethereum-bridge/transfers_to_ethereum.md new file mode 100644 index 0000000000..83611a656b --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/transfers_to_ethereum.md @@ -0,0 +1,133 @@ +# Transferring from Namada to Ethereum + +Moving assets from Namada to Ethereum will not be automatic, as opposed the +movement of value in the opposite direction. Instead, users must send an +appropriate transaction to Namada to initiate a transfer across the bridge +to Ethereum. Once this transaction is approved, a ["proof"](proofs.md), or +the parts necessary to create a proof, will be created and posted on Namada. + +It is incumbent on the end user to request an appropriate proof +of the transaction. This proof must be submitted to the appropriate Ethereum smart +contract by the user to redeem Ethereum assets / mint wrapped assets. This also +means all Ethereum gas costs are the responsibility of the end user. + +A relayer binary will be developed to aid users in accessing the proofs +generated by Namada validators as well as posting this proof to Ethereum. It +will also aid in batching transactions. + +## Moving value to Ethereum + +To redeem wrapped Ethereum assets, a user should make a transaction to burn +their wrapped tokens, which the `#EthBridge` validity predicate will accept. +For sending NAM over the bridge, a user should send their NAM to +`#EthBridgeEscrow`. In both cases, it's important that the user also adds a +`PendingTransfer` to the [Bridge Pool](#bridge-pool-validity-predicate). + +## Batching + +Ethereum gas fees make it prohibitively expensive to submit +the proof for a single transaction over the bridge. Instead, it is typically +more economical to submit proofs of many transactions in bulk. This batching +is described in this section. + +A pool of transfers from Namada to Ethereum will be kept by Namada. Every +transaction to Ethereum that Namada validators approve will be added to this +pool. We call this the _Bridge Pool_. + +The Bridge Pool should be thought of as a sort of mempool. When users who +wish to move assets to Ethereum submit their transactions, they will pay some +additional amount of NAM (of their choosing) as a way of covering the gas +costs on Ethereum. Namada validators will hold these fees in a Bridge Pool +Escrow. + +When a batch of transactions from the Bridge Pool is submitted by a user to +Ethereum, Namada validators will receive notifications via their full nodes. +They will then pay out the fees for each submitted transaction to the user who +relayed these transactions (still in NAM). These will be paid out from the +Bridge Pool Escrow. + +The idea is that users will only relay transactions from the Bridge Pool +that make economic sense. This prevents DoS attacks by underpaying fees as +well as obviating the need for Ethereum gas price oracles. It also means +that transfers to Ethereum are not ordered, preventing other attack vectors. + +The Bridge Pool will be organized as a Merkle tree. Every time it is updated, +the root of tree must be signed by a quorum of validators. When a user +wishes to construct a batch of transactions to relay to Ethereum, they +include the signed tree root and inclusion proofs for the subset of the pool +they are relaying. This can be easily verified by the Ethereum smart contracts. + +If vote extensions are available, these are used to collect the signatures +over the Merkle tree root. If they are not, these must be submitted as protocol +transactions, introducing latency to the pool. A user wishing to relay will +need to wait until a Merkle tree root is signed for a tree that +includes all the transactions they wish to relay. + +The Ethereum smart contracts won't keep track of this signed Merkle root. +Instead, part of the proof of correct batching is submitting a root to the +contracts that is signed by quorum of validators. Since the smart contracts +can trust such a signed root, it can then use the root to verify inclusion +proofs. + +### Bridge Pool validity predicate + +The Bridge Pool will have associated storage under the control of a native +validity predicate. The storage layout looks as follows. + +``` +# all values are Borsh-serialized +/pending_transfers: Vec +/signed_root: Signed +``` + +The pending transfers are instances of the following type: +```rust +pub struct TransferToEthereum { + /// The type of token + pub asset: EthAddress, + /// The recipient address + pub recipient: EthAddress, + /// The amount to be transferred + pub amount: Amount, + /// a nonce for replay protection + pub nonce: u64, +} + +pub struct PendingTransfer { + /// The message to send to Ethereum to + /// complete the transfer + pub transfer: TransferToEthereum, + /// The gas fees paid by the user sending + /// this transfer + pub gas_fee: GasFee, +} + +pub struct GasFee { + /// The amount of gas fees (in NAM) + /// paid by the user sending this transfer + pub amount: Amount, + /// The address of the account paying the fees + pub payer: Address, +} +``` +When a user submits initiates a transfer, their transaction should include wasm +to craft a `PendingTransfer` and append it to the pool in storage as well as +send the relevant gas fees into the Bridge Pool's escrow. This will be +validated by the Bridge Pool vp. + +The signed Merkle root is only modifiable by validators. The Merkle tree +only consists of the `TransferToEthereum` messages as Ethereum does not need +information about the gas fees paid on Namada. + +If vote extensions are not available, this signed root may lag behind the +list of pending transactions. However, it should be the eventually every +pending transaction is covered by the root or it times out. + +## Replay Protection and timeouts + +It is important that nonces are used to prevent copies of the same +transaction being submitted multiple times. Since we do not want to enforce +an order on the transactions, these nonces should land in a range. As a +consequence of this, it is possible that transactions in the Bridge Pool will +time out. Transactions that timed out should revert the state changes on +Namada including refunding the paid in fees. diff --git a/documentation/specs/src/interoperability/ethereum-bridge/transfers_to_namada.md b/documentation/specs/src/interoperability/ethereum-bridge/transfers_to_namada.md new file mode 100644 index 0000000000..d45abbc8cf --- /dev/null +++ b/documentation/specs/src/interoperability/ethereum-bridge/transfers_to_namada.md @@ -0,0 +1,52 @@ +# Transferring assets from Ethereum to Namada + +In order to facilitate transferring assets from Ethereum to Namada, There + will be two internal accounts with associated native validity predicates: + +- `#EthBridge` - Controls the `/eth_msgs/` [storage](ethereum_events_attestation.md#storage) +- and ledgers of balances + for wrapped Ethereum assets (ERC20 tokens) structured in a + ["multitoken"](https://github.com/anoma/anoma/issues/1102) hierarchy +- `#EthBridgeEscrow` which will hold in escrow wrapped Namada tokens which have + been sent to Ethereum. + +#### Wrapped ERC20 + +If an ERC20 token is transferred to Namada, once the associated +`TransferToNamada` Ethereum event is included into Namada, validators mint +the appropriate amount to the corresponding multitoken balance key for +the receiver, or release the escrowed native Namada token. + +```rust +pub struct EthAddress(pub [u8; 20]); + +/// An event transferring some kind of value from Ethereum to Namada +pub struct TransferToNamada { + /// Quantity of ether in the transfer + pub amount: Amount, + /// Address on Ethereum of the asset + pub asset: EthereumAsset, + /// The Namada address receiving wrapped assets on Namada + pub receiver: Address, +} +``` + +##### Example + +For 10 DAI i.e. ERC20([0x6b175474e89094c44da98b954eedeac495271d0f](https://etherscan.io/token/0x6b175474e89094c44da98b954eedeac495271d0f)) to `atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt` +``` +#EthBridge + /ERC20 + /0x6b175474e89094c44da98b954eedeac495271d0f + /balance + /atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt + += 10 +``` + +#### Namada tokens + +Any wrapped Namada tokens being redeemed from Ethereum must have an +equivalent amount of the native token held in escrow by `#EthBridgeEscrow`. +Once the associated`TransferToNamada` Ethereum event is included into +Namada, validators should simply make a transfer from `#EthBridgeEscrow` to +the `receiver` for the appropriate amount and asset.