Skip to content

Commit

Permalink
Add whitelisted ERC20 tokens to the genesis file
Browse files Browse the repository at this point in the history
  • Loading branch information
sug0 committed Jul 15, 2023
1 parent c54097f commit 5cef5df
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 36 deletions.
7 changes: 7 additions & 0 deletions apps/src/lib/config/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,9 +896,11 @@ pub fn genesis(
}
#[cfg(any(test, feature = "dev"))]
pub fn genesis(num_validators: u64) -> Genesis {
use namada::ledger::eth_bridge::Erc20WhitelistEntry;
use namada::types::address::{
self, apfel, btc, dot, eth, kartoffel, nam, schnitzel,
};
use namada::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS;

use crate::wallet;

Expand Down Expand Up @@ -1096,6 +1098,11 @@ pub fn genesis(num_validators: u64) -> Genesis {
pos_params: PosParams::default(),
gov_params: GovParams::default(),
ethereum_bridge_params: Some(EthereumBridgeConfig {
erc20_whitelist: vec![Erc20WhitelistEntry {
token_address: DAI_ERC20_ETH_ADDRESS,
denomination: 18.into(),
token_cap: token::Amount::max(),
}],
eth_start_height: Default::default(),
min_confirmations: Default::default(),
contracts: Contracts {
Expand Down
23 changes: 6 additions & 17 deletions apps/src/lib/node/ledger/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use std::rc::Rc;

use borsh::{BorshDeserialize, BorshSerialize};
use namada::core::ledger::eth_bridge;
use namada::ledger::eth_bridge::{EthBridgeQueries, EthereumBridgeConfig};
use namada::ledger::eth_bridge::{EthBridgeQueries, EthereumOracleConfig};
use namada::ledger::events::log::EventLog;
use namada::ledger::events::Event;
use namada::ledger::gas::BlockGasMeter;
Expand Down Expand Up @@ -963,27 +963,16 @@ where
);
return;
}
let Some(config) = EthereumBridgeConfig::read(&self.wl_storage) else {
tracing::info!(
"Not starting oracle as the Ethereum bridge config couldn't be found in storage"
);
return;
};
let config = EthereumOracleConfig::read(&self.wl_storage).expect(
"The oracle config must be present in storage, since the \
bridge is enabled",
);
let start_block = self
.wl_storage
.storage
.ethereum_height
.clone()
.unwrap_or_else(|| {
self.wl_storage
.read(&eth_bridge::storage::eth_start_height_key())
.expect(
"Failed to read Ethereum start height from storage",
)
.expect(
"The Ethereum start height should be in storage",
)
});
.unwrap_or(config.eth_start_height);
tracing::info!(
?start_block,
"Found Ethereum height from which the Ethereum oracle should \
Expand Down
136 changes: 117 additions & 19 deletions ethereum_bridge/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,43 @@ use std::num::NonZeroU64;

use borsh::{BorshDeserialize, BorshSerialize};
use eyre::{eyre, Result};
use namada_core::ledger::eth_bridge::storage::whitelist;
use namada_core::ledger::storage;
use namada_core::ledger::storage::types::encode;
use namada_core::ledger::storage::WlStorage;
use namada_core::ledger::storage_api::{StorageRead, StorageWrite};
use namada_core::types::ethereum_events::EthAddress;
use namada_core::types::ethereum_structs;
use namada_core::types::storage::Key;
use namada_core::types::token::{Amount, Denomination};
use serde::{Deserialize, Serialize};

use crate::storage::eth_bridge_queries::{EthBridgeEnabled, EthBridgeStatus};
use crate::storage::eth_bridge_queries::{
EthBridgeEnabled, EthBridgeQueries, EthBridgeStatus,
};
use crate::{bridge_pool_vp, storage as bridge_storage, vp};

/// An ERC20 token whitelist entry.
#[derive(
Clone,
Copy,
Eq,
PartialEq,
Debug,
Deserialize,
Serialize,
BorshSerialize,
BorshDeserialize,
)]
pub struct Erc20WhitelistEntry {
/// The address of the whitelisted ERC20 token.
pub token_address: EthAddress,
/// The denomination of the whitelisted ERC20 token.
pub denomination: Denomination,
/// The token cap of the whitelisted ERC20 token.
pub token_cap: Amount,
}

/// Represents a configuration value for the minimum number of
/// confirmations an Ethereum event must reach before it can be acted on.
#[derive(
Expand Down Expand Up @@ -135,6 +160,8 @@ pub struct EthereumBridgeConfig {
/// Minimum number of confirmations needed to trust an Ethereum branch.
/// This must be at least one.
pub min_confirmations: MinimumConfirmations,
/// List of ERC20 token types whitelisted at genesis time.
pub erc20_whitelist: Vec<Erc20WhitelistEntry>,
/// The addresses of the Ethereum contracts that need to be directly known
/// by validators.
pub contracts: Contracts,
Expand All @@ -151,6 +178,7 @@ impl EthereumBridgeConfig {
H: 'static + storage::traits::StorageHasher,
{
let Self {
erc20_whitelist,
eth_start_height,
min_confirmations,
contracts:
Expand Down Expand Up @@ -187,13 +215,72 @@ impl EthereumBridgeConfig {
wl_storage
.write_bytes(&eth_start_height_key, encode(eth_start_height))
.unwrap();
for Erc20WhitelistEntry {
token_address: addr,
token_cap: cap,
denomination: denom,
} in erc20_whitelist
{
let key = whitelist::Key {
asset: *addr,
suffix: whitelist::KeyType::Whitelisted,
}
.into();
wl_storage.write_bytes(&key, encode(&true)).unwrap();

let key = whitelist::Key {
asset: *addr,
suffix: whitelist::KeyType::Cap,
}
.into();
wl_storage.write_bytes(&key, encode(cap)).unwrap();

let key = whitelist::Key {
asset: *addr,
suffix: whitelist::KeyType::Denomination,
}
.into();
wl_storage.write_bytes(&key, encode(denom)).unwrap();
}
// Initialize the storage for the Ethereum Bridge VP.
vp::init_storage(wl_storage);
// Initialize the storage for the Bridge Pool VP.
bridge_pool_vp::init_storage(wl_storage);
}
}

/// Subset of [`EthereumBridgeConfig`], containing only Ethereum
/// oracle specific parameters.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EthereumOracleConfig {
/// Initial Ethereum block height when events will first be extracted from.
pub eth_start_height: ethereum_structs::BlockHeight,
/// Minimum number of confirmations needed to trust an Ethereum branch.
/// This must be at least one.
pub min_confirmations: MinimumConfirmations,
/// The addresses of the Ethereum contracts that need to be directly known
/// by validators.
pub contracts: Contracts,
}

impl From<EthereumBridgeConfig> for EthereumOracleConfig {
fn from(config: EthereumBridgeConfig) -> Self {
let EthereumBridgeConfig {
eth_start_height,
min_confirmations,
contracts,
..
} = config;
Self {
eth_start_height,
min_confirmations,
contracts,
}
}
}

/// Reads the latest [`EthereumBridgeConfig`] from storage. If it is not
impl EthereumOracleConfig {
/// Reads the latest [`EthereumOracleConfig`] from storage. If it is not
/// present, `None` will be returned - this could be the case if the bridge
/// has not been bootstrapped yet. Panics if the storage appears to be
/// corrupt.
Expand All @@ -202,25 +289,27 @@ impl EthereumBridgeConfig {
DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>,
H: 'static + storage::traits::StorageHasher,
{
// TODO: remove present key check; `is_bridge_active` should not
// panic, when the active status key has not been written to; simply
// return bridge disabled instead
let has_active_key =
wl_storage.has_key(&bridge_storage::active_key()).unwrap();

if !has_active_key || !wl_storage.ethbridge_queries().is_bridge_active()
{
return None;
}

let min_confirmations_key = bridge_storage::min_confirmations_key();
let native_erc20_key = bridge_storage::native_erc20_key();
let bridge_contract_key = bridge_storage::bridge_contract_key();
let governance_contract_key = bridge_storage::governance_contract_key();
let eth_start_height_key = bridge_storage::eth_start_height_key();

let Some(min_confirmations) = StorageRead::read::<MinimumConfirmations>(
wl_storage,
&min_confirmations_key,
)
.unwrap_or_else(|err| {
panic!("Could not read {min_confirmations_key}: {err:?}")
}) else {
// The bridge has not been configured yet
return None;
};

// These reads must succeed otherwise the storage is corrupt or a
// read failed
let min_confirmations =
must_read_key(wl_storage, &min_confirmations_key);
let native_erc20 = must_read_key(wl_storage, &native_erc20_key);
let bridge_contract = must_read_key(wl_storage, &bridge_contract_key);
let governance_contract =
Expand Down Expand Up @@ -299,6 +388,7 @@ mod tests {
#[test]
fn test_round_trip_toml_serde() -> Result<()> {
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: MinimumConfirmations::default(),
contracts: Contracts {
Expand All @@ -324,6 +414,7 @@ mod tests {
fn test_ethereum_bridge_config_read_write_storage() {
let mut wl_storage = TestWlStorage::default();
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: MinimumConfirmations::default(),
contracts: Contracts {
Expand All @@ -340,15 +431,16 @@ mod tests {
};
config.init_storage(&mut wl_storage);

let read = EthereumBridgeConfig::read(&wl_storage).unwrap();
let read = EthereumOracleConfig::read(&wl_storage).unwrap();
let config = EthereumOracleConfig::from(config);

assert_eq!(config, read);
}

#[test]
fn test_ethereum_bridge_config_uninitialized() {
let wl_storage = TestWlStorage::default();
let read = EthereumBridgeConfig::read(&wl_storage);
let read = EthereumOracleConfig::read(&wl_storage);

assert!(read.is_none());
}
Expand All @@ -358,6 +450,7 @@ mod tests {
fn test_ethereum_bridge_config_storage_corrupt() {
let mut wl_storage = TestWlStorage::default();
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: MinimumConfirmations::default(),
contracts: Contracts {
Expand All @@ -379,7 +472,7 @@ mod tests {
.unwrap();

// This should panic because the min_confirmations value is not valid
EthereumBridgeConfig::read(&wl_storage);
EthereumOracleConfig::read(&wl_storage);
}

#[test]
Expand All @@ -388,16 +481,21 @@ mod tests {
)]
fn test_ethereum_bridge_config_storage_partially_configured() {
let mut wl_storage = TestWlStorage::default();
wl_storage
.write_bytes(
&bridge_storage::active_key(),
encode(&EthBridgeStatus::Enabled(EthBridgeEnabled::AtGenesis)),
)
.unwrap();
// Write a valid min_confirmations value
let min_confirmations_key = bridge_storage::min_confirmations_key();
wl_storage
.write_bytes(
&min_confirmations_key,
&bridge_storage::min_confirmations_key(),
MinimumConfirmations::default().try_to_vec().unwrap(),
)
.unwrap();

// This should panic as the other config values are not written
EthereumBridgeConfig::read(&wl_storage);
EthereumOracleConfig::read(&wl_storage);
}
}
3 changes: 3 additions & 0 deletions ethereum_bridge/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub fn bootstrap_ethereum_bridge(
wl_storage: &mut TestWlStorage,
) -> EthereumBridgeConfig {
let config = EthereumBridgeConfig {
// start with empty erc20 whitelist
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: MinimumConfirmations::from(unsafe {
// SAFETY: The only way the API contract of `NonZeroU64` can
Expand Down Expand Up @@ -212,6 +214,7 @@ pub fn init_storage_with_validators(
)
.expect("Test failed");
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: Default::default(),
contracts: Contracts {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ mod test_bridge_pool_vp {
fn setup_storage() -> WlStorage<MockDB, Sha256Hasher> {
// a dummy config for testing
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: Default::default(),
contracts: Contracts {
Expand Down
1 change: 1 addition & 0 deletions shared/src/ledger/native_vp/ethereum_bridge/vp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ mod tests {

// a dummy config for testing
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: Default::default(),
contracts: Contracts {
Expand Down
1 change: 1 addition & 0 deletions tests/src/native_vp/eth_bridge_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ mod test_bridge_pool_vp {
..Default::default()
};
let config = EthereumBridgeConfig {
erc20_whitelist: vec![],
eth_start_height: Default::default(),
min_confirmations: Default::default(),
contracts: Contracts {
Expand Down

0 comments on commit 5cef5df

Please sign in to comment.