From 091852937e298ff5304e6c11fbbf2c3b58c4df2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Sun, 21 Aug 2022 17:14:05 +0200 Subject: [PATCH 01/45] fixup! pos: add validator eth cold and hot keys --- proof_of_stake/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index a137eb8a91..89d7981445 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -571,6 +571,17 @@ pub trait PosBase { fn read_validator_set(&self) -> ValidatorSets; /// Read PoS total voting power of all validators (active and inactive). fn read_total_voting_power(&self) -> TotalVotingPowers; + /// Read PoS validator's Eth bridge governance key + fn read_validator_eth_cold_key( + &self, + key: &Self::Address, + ) -> Option; + + /// Read PoS validator's Eth validator set update signing key + fn read_validator_eth_hot_key( + &self, + key: &Self::Address, + ) -> Option; /// Write PoS parameters. fn write_pos_params(&mut self, params: &PosParams); @@ -624,6 +635,18 @@ pub trait PosBase { fn write_validator_set(&mut self, value: &ValidatorSets); /// Read PoS total voting power of all validators (active and inactive). fn write_total_voting_power(&mut self, value: &TotalVotingPowers); + /// Write PoS validator's Eth bridge governance key + fn write_validator_eth_cold_key( + &mut self, + address: &Self::Address, + value: &ValidatorEthKey, + ); + /// Write PoS validator's Eth validator set update signing key + fn write_validator_eth_hot_key( + &mut self, + address: &Self::Address, + value: &ValidatorEthKey, + ); /// Initialize staking reward account with the given public key. fn init_staking_reward_account( &mut self, From ebbc8fccbde82883ed73a740daccea85b2eaab3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Sun, 21 Aug 2022 17:14:33 +0200 Subject: [PATCH 02/45] shared: add eth keys for validators --- shared/src/ledger/pos/storage.rs | 87 +++++++++++++++++++++++++++++ shared/src/ledger/pos/vp.rs | 26 +++++++-- shared/src/types/transaction/mod.rs | 5 ++ vm_env/src/proof_of_stake.rs | 36 ++++++++++++ 4 files changed, 150 insertions(+), 4 deletions(-) diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index cfe1126b88..1443c27811 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -22,6 +22,8 @@ const VALIDATOR_ADDRESS_RAW_HASH: &str = "address_raw_hash"; const VALIDATOR_STAKING_REWARD_ADDRESS_STORAGE_KEY: &str = "staking_reward_address"; const VALIDATOR_CONSENSUS_KEY_STORAGE_KEY: &str = "consensus_key"; +const VALIDATOR_ETH_COLD_KEY_STORAGE_KEY: &str = "eth_cold_key"; +const VALIDATOR_ETH_HOT_KEY_STORAGE_KEY: &str = "eth_hot_key"; const VALIDATOR_STATE_STORAGE_KEY: &str = "state"; const VALIDATOR_TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_VOTING_POWER_STORAGE_KEY: &str = "voting_power"; @@ -142,6 +144,56 @@ pub fn is_validator_consensus_key_key(key: &Key) -> Option<&Address> { } } +/// Storage key for validator's eth cold key. +pub fn validator_eth_cold_key_key(validator: &Address) -> Key { + validator_prefix(validator) + .push(&VALIDATOR_ETH_COLD_KEY_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validator's eth cold key? +pub fn is_validator_eth_cold_key_key(key: &Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(validator), + DbKeySeg::StringSeg(key), + ] if addr == &ADDRESS + && prefix == VALIDATOR_STORAGE_PREFIX + && key == VALIDATOR_ETH_COLD_KEY_STORAGE_KEY => + { + Some(validator) + } + _ => None, + } +} + +/// Storage key for validator's eth hot key. +pub fn validator_eth_hot_key_key(validator: &Address) -> Key { + validator_prefix(validator) + .push(&VALIDATOR_ETH_HOT_KEY_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validator's eth hot key? +pub fn is_validator_eth_hot_key_key(key: &Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(validator), + DbKeySeg::StringSeg(key), + ] if addr == &ADDRESS + && prefix == VALIDATOR_STORAGE_PREFIX + && key == VALIDATOR_ETH_HOT_KEY_STORAGE_KEY => + { + Some(validator) + } + _ => None, + } +} + /// Storage key for validator's state. pub fn validator_state_key(validator: &Address) -> Key { validator_prefix(validator) @@ -450,6 +502,23 @@ where decode(value.unwrap()).unwrap() } + fn read_validator_eth_cold_key( + &self, + key: &Self::Address, + ) -> Option { + let (value, _gas) = + self.read(&validator_eth_cold_key_key(key)).unwrap(); + value.map(|value| decode(value).unwrap()) + } + + fn read_validator_eth_hot_key( + &self, + key: &Self::Address, + ) -> Option { + let (value, _gas) = self.read(&validator_eth_hot_key_key(key)).unwrap(); + value.map(|value| decode(value).unwrap()) + } + fn write_pos_params(&mut self, params: &PosParams) { self.write(¶ms_key(), encode(params)).unwrap(); } @@ -529,6 +598,24 @@ where .unwrap(); } + fn write_validator_eth_cold_key( + &mut self, + address: &Self::Address, + value: &types::ValidatorEthKey, + ) { + self.write(&validator_eth_cold_key_key(address), encode(value)) + .unwrap(); + } + + fn write_validator_eth_hot_key( + &mut self, + address: &Self::Address, + value: &types::ValidatorEthKey, + ) { + self.write(&validator_eth_hot_key_key(address), encode(value)) + .unwrap(); + } + fn init_staking_reward_account( &mut self, address: &Self::Address, diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 26be440536..7d2b7680f1 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -21,10 +21,11 @@ use super::{ is_validator_staking_reward_address_key, is_validator_total_deltas_key, is_validator_voting_power_key, params_key, staking_token_address, total_voting_power_key, unbond_key, validator_consensus_key_key, - validator_set_key, validator_slashes_key, - validator_staking_reward_address_key, validator_state_key, - validator_total_deltas_key, validator_voting_power_key, BondId, Bonds, - Unbonds, ValidatorConsensusKeys, ValidatorSets, ValidatorTotalDeltas, + validator_eth_cold_key_key, validator_eth_hot_key_key, validator_set_key, + validator_slashes_key, validator_staking_reward_address_key, + validator_state_key, validator_total_deltas_key, + validator_voting_power_key, BondId, Bonds, Unbonds, ValidatorConsensusKeys, + ValidatorSets, ValidatorTotalDeltas, }; use crate::ledger::governance::vp::is_proposal_accepted; use crate::ledger::native_vp::{self, Ctx, NativeVp}; @@ -415,6 +416,23 @@ where .unwrap(); decode(value).unwrap() } + + fn read_validator_eth_cold_key( + &self, + key: &Self::Address, + ) -> Option { + let value = + self.ctx.read_pre(&validator_eth_cold_key_key(key)).unwrap(); + value.map(|value| decode(value).unwrap()) + } + + fn read_validator_eth_hot_key( + &self, + key: &Self::Address, + ) -> Option { + let value = self.ctx.read_pre(&validator_eth_hot_key_key(key)).unwrap(); + value.map(|value| decode(value).unwrap()) + } } impl From for Error { diff --git a/shared/src/types/transaction/mod.rs b/shared/src/types/transaction/mod.rs index a7d5ee864b..b9fabcfd6b 100644 --- a/shared/src/types/transaction/mod.rs +++ b/shared/src/types/transaction/mod.rs @@ -187,6 +187,11 @@ pub struct InitValidator { pub account_key: common::PublicKey, /// A key to be used for signing blocks and votes on blocks. pub consensus_key: common::PublicKey, + /// An Eth bridge governance public key + pub eth_cold_key: secp256k1::PublicKey, + /// An Eth bridge hot signing public key used for validator set updates and + /// cross-chain transactions + pub eth_hot_key: secp256k1::PublicKey, /// Public key to be written into the staking reward account's storage. /// This can be used for signature verification of transactions for the /// newly created account. diff --git a/vm_env/src/proof_of_stake.rs b/vm_env/src/proof_of_stake.rs index afabd85ed7..53e2ab9470 100644 --- a/vm_env/src/proof_of_stake.rs +++ b/vm_env/src/proof_of_stake.rs @@ -62,6 +62,8 @@ pub fn init_validator( InitValidator { account_key, consensus_key, + eth_cold_key, + eth_hot_key, rewards_account_key, protocol_key, dkg_key, @@ -84,10 +86,14 @@ pub fn init_validator( let pk_key = key::pk_key(&rewards_address); tx::write(&pk_key.to_string(), &rewards_account_key); + let eth_cold_key = key::common::PublicKey::Secp256k1(eth_cold_key); + let eth_hot_key = key::common::PublicKey::Secp256k1(eth_hot_key); PoS.become_validator( &validator_address, &rewards_address, &consensus_key, + ð_cold_key, + ð_hot_key, current_epoch, )?; Ok((validator_address, rewards_address)) @@ -167,6 +173,20 @@ impl namada_proof_of_stake::PosReadOnly for PoS { fn read_total_voting_power(&self) -> TotalVotingPowers { tx::read(total_voting_power_key().to_string()).unwrap() } + + fn read_validator_eth_cold_key( + &self, + key: &Self::Address, + ) -> Option { + tx::read(validator_eth_cold_key_key(key).to_string()) + } + + fn read_validator_eth_hot_key( + &self, + key: &Self::Address, + ) -> Option { + tx::read(validator_eth_hot_key_key(key).to_string()) + } } impl namada_proof_of_stake::PosActions for PoS { @@ -258,4 +278,20 @@ impl namada_proof_of_stake::PosActions for PoS { ) { crate::token::tx::transfer(src, dest, token, None, amount) } + + fn write_validator_eth_cold_key( + &mut self, + address: &Self::Address, + value: types::ValidatorEthKey, + ) { + tx::write(validator_eth_cold_key_key(address).to_string(), &value) + } + + fn write_validator_eth_hot_key( + &self, + address: &Self::Address, + value: types::ValidatorEthKey, + ) { + tx::write(validator_eth_hot_key_key(address).to_string(), &value) + } } From 250ee2e7c4065241ee164c7959888074df71e957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Sun, 21 Aug 2022 17:14:59 +0200 Subject: [PATCH 03/45] client: add eth keys for validators --- apps/src/lib/cli.rs | 22 +++++++++++++++- apps/src/lib/client/tx.rs | 54 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 6eb6454bc4..f271433b9e 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1482,6 +1482,9 @@ pub mod args { arg_opt("account-key"); const VALIDATOR_CONSENSUS_KEY: ArgOpt = arg_opt("consensus-key"); + const VALIDATOR_ETH_COLD_KEY: ArgOpt = + arg_opt("eth-cold-key"); + const VALIDATOR_ETH_HOT_KEY: ArgOpt = arg_opt("eth-hot-key"); const VALIDATOR_CODE_PATH: ArgOpt = arg_opt("validator-code-path"); const VALUE: ArgOpt = arg_opt("value"); const WASM_CHECKSUMS_PATH: Arg = arg("wasm-checksums-path"); @@ -1700,6 +1703,8 @@ pub mod args { pub scheme: SchemeType, pub account_key: Option, pub consensus_key: Option, + pub eth_cold_key: Option, + pub eth_hot_key: Option, pub rewards_account_key: Option, pub protocol_key: Option, pub validator_vp_code_path: Option, @@ -1714,6 +1719,8 @@ pub mod args { let scheme = SCHEME.parse(matches); let account_key = VALIDATOR_ACCOUNT_KEY.parse(matches); let consensus_key = VALIDATOR_CONSENSUS_KEY.parse(matches); + let eth_cold_key = VALIDATOR_ETH_COLD_KEY.parse(matches); + let eth_hot_key = VALIDATOR_ETH_HOT_KEY.parse(matches); let rewards_account_key = REWARDS_KEY.parse(matches); let protocol_key = PROTOCOL_KEY.parse(matches); let validator_vp_code_path = VALIDATOR_CODE_PATH.parse(matches); @@ -1725,6 +1732,8 @@ pub mod args { scheme, account_key, consensus_key, + eth_cold_key, + eth_hot_key, rewards_account_key, protocol_key, validator_vp_code_path, @@ -1748,7 +1757,18 @@ pub mod args { )) .arg(VALIDATOR_CONSENSUS_KEY.def().about( "A consensus key for the validator account. A new one \ - will be generated if none given.", + will be generated if none given. Note that this must be \ + ed25519.", + )) + .arg(VALIDATOR_ETH_COLD_KEY.def().about( + "An Eth cold key for the validator account. A new one \ + will be generated if none given. Note that this must be \ + secp256k1.", + )) + .arg(VALIDATOR_ETH_COLD_KEY.def().about( + "An Eth cold key for the validator account. A new one \ + will be generated if none given. Note that this must be \ + secp256k1.", )) .arg(REWARDS_KEY.def().about( "A public key for the staking reward account. A new one \ diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 43eeb49c68..a1aabfe532 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -14,7 +14,7 @@ use namada::types::address::{xan as m1t, Address}; use namada::types::governance::{ OfflineProposal, OfflineVote, Proposal, ProposalVote, }; -use namada::types::key::*; +use namada::types::key::{self, *}; use namada::types::nft::{self, Nft, NftToken}; use namada::types::storage::{Epoch, Key}; use namada::types::token::Amount; @@ -159,6 +159,8 @@ pub async fn submit_init_validator( scheme, account_key, consensus_key, + eth_cold_key, + eth_hot_key, rewards_account_key, protocol_key, validator_vp_code_path, @@ -208,6 +210,48 @@ pub async fn submit_init_validator( .1 }); + let eth_cold_key = ctx + .get_opt_cached(ð_cold_key) + .map(|key| match *key { + common::SecretKey::Secp256k1(_) => key, + common::SecretKey::Ed25519(_) => { + eprintln!("Eth cold key can only be secp256k1"); + safe_exit(1) + } + }) + .unwrap_or_else(|| { + println!("Generating Eth cold key..."); + ctx.wallet + .gen_key( + // Note that ETH only allows secp256k1 + SchemeType::Secp256k1, + Some(consensus_key_alias.clone()), + unsafe_dont_encrypt, + ) + .1 + }); + + let eth_hot_key = ctx + .get_opt_cached(ð_hot_key) + .map(|key| match *key { + common::SecretKey::Secp256k1(_) => key, + common::SecretKey::Ed25519(_) => { + eprintln!("Eth hot key can only be secp256k1"); + safe_exit(1) + } + }) + .unwrap_or_else(|| { + println!("Generating Eth hot key..."); + ctx.wallet + .gen_key( + // Note that ETH only allows secp256k1 + SchemeType::Secp256k1, + Some(consensus_key_alias.clone()), + unsafe_dont_encrypt, + ) + .1 + }); + let rewards_account_key = ctx.get_opt_cached(&rewards_account_key).unwrap_or_else(|| { println!("Generating staking reward account key..."); @@ -269,6 +313,14 @@ pub async fn submit_init_validator( let data = InitValidator { account_key, consensus_key: consensus_key.ref_to(), + eth_cold_key: key::secp256k1::PublicKey::try_from_pk( + ð_cold_key.ref_to(), + ) + .unwrap(), + eth_hot_key: key::secp256k1::PublicKey::try_from_pk( + ð_hot_key.ref_to(), + ) + .unwrap(), rewards_account_key, protocol_key, dkg_key, From 6893c20adf934156e3e7a528f6166bfd06e4d9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Sun, 21 Aug 2022 17:15:17 +0200 Subject: [PATCH 04/45] TODO: update genesis --- apps/src/lib/config/genesis.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 5bd3dc803f..66e4b0caf4 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -160,6 +160,7 @@ pub mod genesis_config { pub validator: HashMap, } + // TODO add eth keys here #[derive(Clone, Default, Debug, Deserialize, Serialize)] pub struct ValidatorConfig { // Public key for consensus. (default: generate) @@ -298,6 +299,7 @@ pub mod genesis_config { let reward_vp_config = wasm.get(reward_vp_name).unwrap(); Validator { + // TODO add eth keys here pos_data: GenesisValidator { address: Address::decode(&config.address.as_ref().unwrap()) .unwrap(), From 92a0e224358599c74998f2d30843be90f831deb5 Mon Sep 17 00:00:00 2001 From: brentstone Date: Sun, 21 Aug 2022 17:46:45 +0200 Subject: [PATCH 05/45] config: add eth keys to genesis --- apps/src/lib/config/genesis.rs | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 66e4b0caf4..ca6b43fa95 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -165,6 +165,10 @@ pub mod genesis_config { pub struct ValidatorConfig { // Public key for consensus. (default: generate) pub consensus_public_key: Option, + // Public key (cold) for eth governance. (default: generate) + pub eth_cold_key: Option, + // Public key (hot) for eth bridge. (default: generate) + pub eth_hot_key: Option, // Public key for validator account. (default: generate) pub account_public_key: Option, // Public key for staking reward account. (default: generate) @@ -320,6 +324,18 @@ pub mod genesis_config { .unwrap() .to_public_key() .unwrap(), + eth_cold_key: config + .staking_reward_public_key + .as_ref() + .unwrap() + .to_public_key() + .unwrap(), + eth_hot_key: config + .staking_reward_public_key + .as_ref() + .unwrap() + .to_public_key() + .unwrap(), }, account_key: config .account_public_key @@ -752,8 +768,27 @@ pub fn genesis() -> Genesis { 24, 247, 69, 6, 9, 30, 44, 16, 88, 238, 77, 162, 243, 125, 240, 206, ]) .unwrap(); + + // TODO: should these bytes be different from the ed above? Should I + // generate them from somewhere? For now I'm just randomly + // adjusting some bytes, assuming that is fine. + let secp_eth_cold_keypair = secp256k1::SecretKey::try_from_slice(&[ + 64, 198, 87, 204, 44, 94, 234, 228, 217, 72, 245, 27, 40, 2, 151, 174, + 24, 247, 69, 6, 9, 30, 44, 16, 88, 238, 65, 162, 243, 125, 240, 206, + ]) + .unwrap(); + let secp_eth_hot_keypair = secp256k1::SecretKey::try_from_slice(&[ + 58, 198, 87, 204, 44, 94, 122, 228, 217, 72, 245, 27, 40, 2, 151, 174, + 24, 247, 69, 6, 9, 30, 44, 16, 88, 238, 77, 162, 243, 125, 240, 206, + ]) + .unwrap(); + let staking_reward_keypair = common::SecretKey::try_from_sk(&ed_staking_reward_keypair).unwrap(); + let eth_cold_keypair = + common::SecretKey::try_from_sk(&secp_eth_cold_keypair).unwrap(); + let eth_hot_keypair = + common::SecretKey::try_from_sk(&secp_eth_hot_keypair).unwrap(); let address = wallet::defaults::validator_address(); let staking_reward_address = Address::decode("atest1v4ehgw36xcersvee8qerxd35x9prsw2xg5erxv6pxfpygd2x89z5xsf5xvmnysejgv6rwd2rnj2avt").unwrap(); let (protocol_keypair, dkg_keypair) = wallet::defaults::validator_keys(); @@ -764,6 +799,8 @@ pub fn genesis() -> Genesis { tokens: token::Amount::whole(200_000), consensus_key: consensus_keypair.ref_to(), staking_reward_key: staking_reward_keypair.ref_to(), + eth_cold_key: eth_cold_keypair.ref_to(), + eth_hot_key: eth_hot_keypair.ref_to(), }, account_key: account_keypair.ref_to(), protocol_key: protocol_keypair.ref_to(), From fd925416a2a7eb60dd3ea9c934fa72cd7ee71c35 Mon Sep 17 00:00:00 2001 From: brentstone Date: Sun, 21 Aug 2022 17:59:39 +0200 Subject: [PATCH 06/45] client: add eth keys to pre_genesis --- apps/src/lib/client/utils.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 9043a14db7..ca0a3d7347 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -1089,6 +1089,12 @@ pub fn init_genesis_validator( consensus_public_key: Some(HexString( pre_genesis.consensus_key.ref_to().to_string(), )), + eth_cold_key: Some(HexString( + pre_genesis.eth_cold_key.ref_to().to_string(), + )), + eth_hot_key: Some(HexString( + pre_genesis.eth_hot_key.ref_to().to_string(), + )), account_public_key: Some(HexString( pre_genesis.account_key.ref_to().to_string(), )), From 93179b7427516a5b945900d0218b6a87be225dad Mon Sep 17 00:00:00 2001 From: brentstone Date: Sun, 21 Aug 2022 18:00:30 +0200 Subject: [PATCH 07/45] wallet: add eth keys to ValidatorWallet --- apps/src/lib/wallet/pre_genesis.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index 72f719d1e4..bc5bb84106 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -40,6 +40,10 @@ pub struct ValidatorWallet { pub account_key: Rc, /// Cryptographic keypair for consensus key pub consensus_key: Rc, + /// Cryptographic keypair for eth cold key + pub eth_cold_key: Rc, + /// Cryptographic keypair for eth hot key + pub eth_hot_key: Rc, /// Cryptographic keypair for rewards key pub rewards_key: Rc, /// Cryptographic keypair for Tendermint node key @@ -54,6 +58,10 @@ pub struct ValidatorStore { pub account_key: wallet::StoredKeypair, /// Cryptographic keypair for consensus key pub consensus_key: wallet::StoredKeypair, + /// Cryptographic keypair for eth cold key + pub eth_cold_key: wallet::StoredKeypair, + /// Cryptographic keypair for eth hot key + pub eth_hot_key: wallet::StoredKeypair, /// Cryptographic keypair for rewards key pub rewards_key: wallet::StoredKeypair, /// Cryptographic keypair for Tendermint node key @@ -119,6 +127,11 @@ impl ValidatorWallet { store.account_key.get(true, password.clone())?; let consensus_key = store.consensus_key.get(true, password.clone())?; + let eth_cold_key = + store.eth_cold_key.get(true, password.clone())?; + let eth_hot_key = + store.eth_hot_key.get(true, password.clone())?; + let rewards_key = store.rewards_key.get(true, password.clone())?; let tendermint_node_key = @@ -128,6 +141,8 @@ impl ValidatorWallet { store, account_key, consensus_key, + eth_cold_key, + eth_hot_key, rewards_key, tendermint_node_key, }) @@ -149,6 +164,11 @@ impl ValidatorWallet { SchemeType::Ed25519, &password, ); + let (eth_cold_key, eth_cold_sk) = + gen_key_to_store(SchemeType::Secp256k1, &password); + let (eth_hot_key, eth_hot_sk) = + gen_key_to_store(SchemeType::Secp256k1, &password); + let (rewards_key, rewards_sk) = gen_key_to_store(scheme, &password); let (tendermint_node_key, tendermint_node_sk) = gen_key_to_store( // Note that TM only allows ed25519 for node IDs @@ -159,6 +179,8 @@ impl ValidatorWallet { let store = ValidatorStore { account_key, consensus_key, + eth_cold_key, + eth_hot_key, rewards_key, tendermint_node_key, validator_keys, @@ -167,6 +189,8 @@ impl ValidatorWallet { store, account_key: account_sk, consensus_key: consensus_sk, + eth_cold_key: eth_cold_sk, + eth_hot_key: eth_hot_sk, rewards_key: rewards_sk, tendermint_node_key: tendermint_node_sk, } From 87f70e4299f29e8cd6ee0babbbf195d76f4b906a Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 24 Aug 2022 12:26:10 +0300 Subject: [PATCH 08/45] proper hard-coded secp keys, remove outdated comments --- apps/src/lib/config/genesis.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index ca6b43fa95..b0393cab5e 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -303,7 +303,6 @@ pub mod genesis_config { let reward_vp_config = wasm.get(reward_vp_name).unwrap(); Validator { - // TODO add eth keys here pos_data: GenesisValidator { address: Address::decode(&config.address.as_ref().unwrap()) .unwrap(), @@ -769,17 +768,14 @@ pub fn genesis() -> Genesis { ]) .unwrap(); - // TODO: should these bytes be different from the ed above? Should I - // generate them from somewhere? For now I'm just randomly - // adjusting some bytes, assuming that is fine. let secp_eth_cold_keypair = secp256k1::SecretKey::try_from_slice(&[ - 64, 198, 87, 204, 44, 94, 234, 228, 217, 72, 245, 27, 40, 2, 151, 174, - 24, 247, 69, 6, 9, 30, 44, 16, 88, 238, 65, 162, 243, 125, 240, 206, + 90, 83, 107, 155, 193, 251, 120, 27, 76, 1, 188, 8, 116, 121, 90, 99, + 65, 17, 187, 6, 238, 141, 63, 188, 76, 38, 102, 7, 47, 185, 28, 52, ]) .unwrap(); let secp_eth_hot_keypair = secp256k1::SecretKey::try_from_slice(&[ - 58, 198, 87, 204, 44, 94, 122, 228, 217, 72, 245, 27, 40, 2, 151, 174, - 24, 247, 69, 6, 9, 30, 44, 16, 88, 238, 77, 162, 243, 125, 240, 206, + 117, 93, 118, 129, 202, 67, 51, 62, 202, 196, 130, 244, 5, 44, 88, 200, + 121, 169, 11, 227, 79, 223, 74, 88, 49, 132, 213, 59, 64, 20, 13, 82, ]) .unwrap(); From be2a29546fd0bbcc50290802d8fa8b9110cbd084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 7 Sep 2022 15:55:12 +0200 Subject: [PATCH 09/45] shared: add conversion from secp256k1 pk to eth address --- shared/Cargo.toml | 1 + shared/src/types/key/secp256k1.rs | 47 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index ff22c0911b..b35b85bd36 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -92,6 +92,7 @@ tempfile = {version = "3.2.0", optional = true} # temporarily using fork work-around for https://github.com/informalsystems/tendermint-rs/issues/971 tendermint = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", features = ["secp256k1"]} tendermint-proto = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9"} +tiny-keccak = {version = "2.0.2", features = ["keccak"]} thiserror = "1.0.30" tiny-keccak = "2.0.2" tracing = "0.1.30" diff --git a/shared/src/types/key/secp256k1.rs b/shared/src/types/key/secp256k1.rs index 99bcbb3f67..198cb8d74b 100644 --- a/shared/src/types/key/secp256k1.rs +++ b/shared/src/types/key/secp256k1.rs @@ -22,6 +22,10 @@ use super::{ #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct PublicKey(pub libsecp256k1::PublicKey); +/// Eth address derived from secp256k1 key +#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct EthAddress([u8; 20]); + impl super::PublicKey for PublicKey { const TYPE: SchemeType = SigScheme::TYPE; @@ -133,6 +137,23 @@ impl From for PublicKey { } } +impl From<&PublicKey> for EthAddress { + fn from(pk: &PublicKey) -> Self { + use tiny_keccak::Hasher; + + let mut hasher = tiny_keccak::Keccak::v256(); + // We're removing the first byte with + // `libsecp256k1::util::TAG_PUBKEY_FULL` + let pk_bytes = &pk.0.serialize()[1..]; + hasher.update(pk_bytes); + let mut output = [0_u8; 32]; + hasher.finalize(&mut output); + let mut addr = [0; 20]; + addr.copy_from_slice(&output[12..]); + EthAddress(addr) + } +} + /// Secp256k1 secret key #[derive(Debug, Clone)] pub struct SecretKey(pub Box); @@ -501,3 +522,29 @@ impl super::SigScheme for SigScheme { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_eth_address_from_secp() { + // test vector from https://bitcoin.stackexchange.com/a/89848 + let sk_hex = + "c2c72dfbff11dfb4e9d5b0a20c620c58b15bb7552753601f043db91331b0db15"; + let expected_pk_hex = "a225bf565ff4ea039bccba3e26456e910cd74e4616d67ee0a166e26da6e5e55a08d0fa1659b4b547ba7139ca531f62907b9c2e72b80712f1c81ece43c33f4b8b"; + let expected_eth_addr_hex = "6ea27154616a29708dce7650b475dd6b82eba6a3"; + + let sk_bytes = hex::decode(sk_hex).unwrap(); + let sk = SecretKey::try_from_slice(&sk_bytes[..]).unwrap(); + let pk: PublicKey = sk.ref_to(); + // We're removing the first byte with + // `libsecp256k1::util::TAG_PUBKEY_FULL` + let pk_hex = hex::encode(&pk.0.serialize()[1..]); + assert_eq!(expected_pk_hex, pk_hex); + + let eth_addr: EthAddress = (&pk).into(); + let eth_addr_hex = hex::encode(eth_addr.0); + assert_eq!(expected_eth_addr_hex, eth_addr_hex); + } +} From de84663ac9343b884804f3d57e45f5bafe79264c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 7 Sep 2022 17:25:45 +0200 Subject: [PATCH 10/45] WIP: add maps for eth address reverse lookup to native address --- proof_of_stake/src/lib.rs | 87 +++++++++++++++++++++++++++++++- proof_of_stake/src/types.rs | 16 +++++- shared/src/ledger/pos/storage.rs | 34 +++++++++++++ 3 files changed, 133 insertions(+), 4 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 89d7981445..34cfb60378 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -33,8 +33,9 @@ use epoched::{ use parameters::PosParams; use thiserror::Error; use types::{ - ActiveValidator, Bonds, Epoch, GenesisValidator, Slash, SlashType, Slashes, - TotalVotingPowers, Unbond, Unbonds, ValidatorConsensusKeys, ValidatorSet, + ActiveValidator, Bonds, Epoch, EthAddress, EthKeyAddresses, + GenesisValidator, Slash, SlashType, Slashes, TotalVotingPowers, Unbond, + Unbonds, ValidatorConsensusKeys, ValidatorEthKey, ValidatorSet, ValidatorSetUpdate, ValidatorSets, ValidatorState, ValidatorStates, ValidatorTotalDeltas, ValidatorVotingPowers, VotingPower, VotingPowerDelta, }; @@ -146,6 +147,44 @@ pub trait PosReadOnly { fn read_validator_set(&self) -> ValidatorSets; /// Read PoS total voting power of all validators (active and inactive). fn read_total_voting_power(&self) -> TotalVotingPowers; + + /// Read PoS validator's Eth bridge governance key + fn read_validator_eth_cold_key( + &self, + key: &Self::Address, + ) -> Option; + + /// Read PoS validator's Eth validator set update signing key + fn read_validator_eth_hot_key( + &self, + key: &Self::Address, + ) -> Option; + + /// Read PoS map from eth address derived from cold keys to native addresses + fn read_eth_cold_key_addresses(&self) -> EthKeyAddresses; + + /// Read PoS map from eth address derived from hot keys to native addresses + fn read_eth_hot_key_addresses(&self) -> EthKeyAddresses; + + /// Try to find a native address associated with the given eth address + /// derived from a eth cold key + fn find_address_from_eth_cold_key_address( + &self, + eth_addr: &EthAddress, + ) -> Option { + let addresses = self.read_eth_cold_key_addresses(); + addresses.get(eth_addr).cloned() + } + + /// Try to find a native address associated with the given eth address + /// derived from a eth hot key + fn find_address_from_eth_hot_key_address( + &self, + eth_addr: &EthAddress, + ) -> Option { + let addresses = self.read_eth_hot_key_addresses(); + addresses.get(eth_addr).cloned() + } } /// PoS system trait to be implemented in integration that can read and write @@ -204,6 +243,19 @@ pub trait PosActions: PosReadOnly { fn write_validator_set(&mut self, value: ValidatorSets); /// Write PoS total voting power of all validators (active and inactive). fn write_total_voting_power(&mut self, value: TotalVotingPowers); + /// Write PoS validator's Eth bridge governance key + fn write_validator_eth_cold_key( + &mut self, + address: &Self::Address, + value: ValidatorEthKey, + ); + + /// Write PoS validator's Eth validator set update signing key + fn write_validator_eth_hot_key( + &self, + address: &Self::Address, + value: ValidatorEthKey, + ); /// Delete an emptied PoS bond (validator self-bond or a delegation). fn delete_bond(&mut self, key: &BondId); @@ -226,6 +278,8 @@ pub trait PosActions: PosReadOnly { address: &Self::Address, staking_reward_address: &Self::Address, consensus_key: &Self::PublicKey, + eth_cold_key: &Self::PublicKey, + eth_hot_key: &Self::PublicKey, current_epoch: impl Into, ) -> Result<(), BecomeValidatorError> { let current_epoch = current_epoch.into(); @@ -245,6 +299,8 @@ pub trait PosActions: PosReadOnly { } let BecomeValidatorData { consensus_key, + eth_cold_key, + eth_hot_key, state, total_deltas, voting_power, @@ -252,6 +308,8 @@ pub trait PosActions: PosReadOnly { ¶ms, address, consensus_key, + eth_cold_key, + eth_hot_key, &mut validator_set, current_epoch, ); @@ -260,6 +318,8 @@ pub trait PosActions: PosReadOnly { staking_reward_address.clone(), ); self.write_validator_consensus_key(address, consensus_key); + self.write_validator_eth_cold_key(address, eth_cold_key); + self.write_validator_eth_hot_key(address, eth_hot_key); self.write_validator_state(address, state); self.write_validator_set(validator_set); self.write_validator_address_raw_hash(address); @@ -707,6 +767,8 @@ pub trait PosBase { total_deltas, voting_power, bond: (bond_id, bond), + eth_cold_key, + eth_hot_key, } = res?; self.write_validator_address_raw_hash(address); self.write_validator_staking_reward_address( @@ -714,6 +776,8 @@ pub trait PosBase { &staking_reward_address, ); self.write_validator_consensus_key(address, &consensus_key); + self.write_validator_eth_cold_key(address, ð_cold_key); + self.write_validator_eth_hot_key(address, ð_hot_key); self.write_validator_state(address, &state); self.write_validator_total_deltas(address, &total_deltas); self.write_validator_voting_power(address, &voting_power); @@ -1079,6 +1143,8 @@ where total_deltas: ValidatorTotalDeltas, voting_power: ValidatorVotingPowers, bond: (BondId
, Bonds), + eth_cold_key: ValidatorEthKey, + eth_hot_key: ValidatorEthKey, } /// A function that returns genesis data created from the initial validator set. @@ -1178,9 +1244,15 @@ where tokens, consensus_key, staking_reward_key, + eth_cold_key, + eth_hot_key, }| { let consensus_key = Epoched::init_at_genesis(consensus_key.clone(), current_epoch); + let eth_cold_key = + Epoched::init_at_genesis(eth_cold_key.clone(), current_epoch); + let eth_hot_key = + Epoched::init_at_genesis(eth_hot_key.clone(), current_epoch); let state = Epoched::init_at_genesis( ValidatorState::Candidate, current_epoch, @@ -1210,6 +1282,8 @@ where total_deltas, voting_power, bond: (bond_id, bond), + eth_cold_key, + eth_hot_key, }) }, ); @@ -1320,6 +1394,8 @@ where + BorshSchema, { consensus_key: ValidatorConsensusKeys, + eth_cold_key: ValidatorEthKey, + eth_hot_key: ValidatorEthKey, state: ValidatorStates, total_deltas: ValidatorTotalDeltas, voting_power: ValidatorVotingPowers, @@ -1330,6 +1406,8 @@ fn become_validator( params: &PosParams, address: &Address, consensus_key: &PK, + eth_cold_key: &PK, + eth_hot_key: &PK, validator_set: &mut ValidatorSets
, current_epoch: Epoch, ) -> BecomeValidatorData @@ -1353,6 +1431,9 @@ where { let consensus_key = Epoched::init(consensus_key.clone(), current_epoch, params); + let eth_cold_key = + Epoched::init(eth_cold_key.clone(), current_epoch, params); + let eth_hot_key = Epoched::init(eth_hot_key.clone(), current_epoch, params); let mut state = Epoched::init_at_genesis(ValidatorState::Pending, current_epoch); @@ -1394,6 +1475,8 @@ where state, total_deltas, voting_power, + eth_cold_key, + eth_hot_key, } } diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index 45342ef277..c421903197 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -36,8 +36,15 @@ pub type Unbonds = pub type ValidatorSets
= Epoched, OffsetUnboundingLen>; /// Epoched total voting power. -pub type TotalVotingPowers = - EpochedDelta; +pub type TotalVotingPowers = EpochedDelta; +/// Epoched validator's eth key. +pub type ValidatorEthKey = Epoched; +/// Map from eth addresses back to native addresses. +pub type EthKeyAddresses
= HashMap; + +/// Eth address derived from secp256k1 key +#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct EthAddress([u8; 20]); /// Epoch identifier. Epochs are identified by consecutive natural numbers. /// @@ -118,6 +125,11 @@ pub struct GenesisValidator { pub consensus_key: PK, /// An public key associated with the staking reward address pub staking_reward_key: PK, + /// An Eth bridge governance public key + pub eth_cold_key: PK, + /// An Eth bridge hot signing public key used for validator set updates and + /// cross-chain transactions + pub eth_hot_key: PK, } /// An update of the active and inactive validator set. diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index 1443c27811..8e92ad32b9 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -24,6 +24,8 @@ const VALIDATOR_STAKING_REWARD_ADDRESS_STORAGE_KEY: &str = const VALIDATOR_CONSENSUS_KEY_STORAGE_KEY: &str = "consensus_key"; const VALIDATOR_ETH_COLD_KEY_STORAGE_KEY: &str = "eth_cold_key"; const VALIDATOR_ETH_HOT_KEY_STORAGE_KEY: &str = "eth_hot_key"; +const ETH_COLD_KEY_ADDRESSES_STORAGE_KEY: &str = "eth_cold_key_addresses"; +const ETH_HOT_KEY_ADDRESSES_STORAGE_KEY: &str = "eth_hot_key_addresses"; const VALIDATOR_STATE_STORAGE_KEY: &str = "state"; const VALIDATOR_TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_VOTING_POWER_STORAGE_KEY: &str = "voting_power"; @@ -418,6 +420,38 @@ pub fn get_validator_address_from_bond(key: &Key) -> Option
{ } } +/// Storage key for look-up from validator's eth cold key addresses to native +/// address. +pub fn eth_cold_key_addresses_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(Ð_COLD_KEY_ADDRESSES_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validators' eth cold key addresses? +pub fn is_eth_cold_key_addresses_key(key: &Key) -> bool { + matches!(&key.segments[..], + [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(suffix)] + if addr == &ADDRESS + && suffix == ETH_COLD_KEY_ADDRESSES_STORAGE_KEY) +} + +/// Storage key for look-up from validator's eth hot key addresses to native +/// address. +pub fn eth_hot_key_addresses_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(Ð_HOT_KEY_ADDRESSES_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Is storage key for validators' eth hot key addresses? +pub fn is_eth_hot_key_addresses_key(key: &Key) -> bool { + matches!(&key.segments[..], + [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(suffix)] + if addr == &ADDRESS + && suffix == ETH_HOT_KEY_ADDRESSES_STORAGE_KEY) +} + impl PosBase for Storage where D: storage::DB + for<'iter> storage::DBIter<'iter>, From f083429b20e626a37f3d43cefdcf880f22cebd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 7 Sep 2022 17:26:20 +0200 Subject: [PATCH 11/45] TODO: add eth hot key to validator keys in the wallet store --- apps/src/lib/wallet/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 76a2053419..356b7add53 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -30,6 +30,7 @@ pub struct ValidatorKeys { /// Special session keypair needed by validators for participating /// in the DKG protocol pub dkg_keypair: Option, + // TODO add eth hot key } impl ValidatorKeys { From 6b9de30b4a57d6788359349a99b5cbd7f8a02477 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 8 Sep 2022 12:12:54 +0100 Subject: [PATCH 12/45] WIP: Merge eth hot and cold keys into single storage sub-key space --- proof_of_stake/src/lib.rs | 75 ++++++++++++++++++++++++-------- shared/src/ledger/pos/storage.rs | 31 +++---------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 34cfb60378..581560513a 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -91,6 +91,7 @@ pub trait PosReadOnly { /// Cryptographic public key type type PublicKey: Debug + Clone + + TryInto + BorshDeserialize + BorshSerialize + BorshSchema; @@ -160,29 +161,17 @@ pub trait PosReadOnly { key: &Self::Address, ) -> Option; - /// Read PoS map from eth address derived from cold keys to native addresses - fn read_eth_cold_key_addresses(&self) -> EthKeyAddresses; + /// Read PoS map from eth address derived from cold or hot keys to native + /// addresses + fn read_eth_key_addresses(&self) -> EthKeyAddresses; - /// Read PoS map from eth address derived from hot keys to native addresses - fn read_eth_hot_key_addresses(&self) -> EthKeyAddresses; - - /// Try to find a native address associated with the given eth address - /// derived from a eth cold key - fn find_address_from_eth_cold_key_address( + /// Try to find a native address associated with the given Ethereum address + /// derived from an Ethereum cold or hot key + fn find_address_from_eth_key_address( &self, eth_addr: &EthAddress, ) -> Option { - let addresses = self.read_eth_cold_key_addresses(); - addresses.get(eth_addr).cloned() - } - - /// Try to find a native address associated with the given eth address - /// derived from a eth hot key - fn find_address_from_eth_hot_key_address( - &self, - eth_addr: &EthAddress, - ) -> Option { - let addresses = self.read_eth_hot_key_addresses(); + let addresses = self.read_eth_key_addresses(); addresses.get(eth_addr).cloned() } } @@ -257,6 +246,10 @@ pub trait PosActions: PosReadOnly { value: ValidatorEthKey, ); + /// Write PoS map from eth address derived from cold or hot keys to native + /// addresses + fn write_eth_key_addresses(&self, value: EthKeyAddresses); + /// Delete an emptied PoS bond (validator self-bond or a delegation). fn delete_bond(&mut self, key: &BondId); /// Delete an emptied PoS unbond (unbonded tokens from validator self-bond @@ -297,6 +290,18 @@ pub trait PosActions: PosReadOnly { ), ); } + let convert_key_to_addr = |k| { + k.try_into() + .map_err(|_| BecomeValidatorError::SecpKeyConversion) + }; + let eth_addresses_map = self.read_eth_key_addresses(); + let have_duped_key = eth_key_reverse_map + .contains_key(&convert_key_to_addr(eth_cold_key)) + || eth_key_reverse_map + .contains_key(&convert_key_to_addr(eth_hot_key)); + if have_duped_key { + return Err(BecomeValidatorError::DupedEthKeyFound); + } let BecomeValidatorData { consensus_key, eth_cold_key, @@ -584,6 +589,7 @@ pub trait PosBase { type PublicKey: 'static + Debug + Clone + + TryInto + BorshDeserialize + BorshSerialize + BorshSchema; @@ -707,6 +713,12 @@ pub trait PosBase { address: &Self::Address, value: &ValidatorEthKey, ); + /// Write PoS map from eth address derived from cold or hot keys to native + /// addresses + fn write_eth_key_addresses( + &mut self, + value: &EthKeyAddresses, + ); /// Initialize staking reward account with the given public key. fn init_staking_reward_account( &mut self, @@ -757,6 +769,8 @@ pub trait PosBase { total_bonded_balance, } = init_genesis(params, validators, current_epoch)?; + let mut eth_key_reverse_map = HashMap::default(); + for res in validators { let GenesisValidatorData { ref address, @@ -786,7 +800,22 @@ pub trait PosBase { &staking_reward_address, &staking_reward_key, ); + let convert_key_to_addr = + |k| k.try_into().map_err(|_| GenesisError::SecpKeyConversion); + if eth_key_reverse_map + .insert(convert_key_to_addr(ð_cold_key)?, address) + .is_some() + { + return Err(GenesisError::DupedEthKeyFound); + } + if eth_key_reverse_map + .insert(convert_key_to_addr(ð_hot_key)?, address) + .is_some() + { + return Err(GenesisError::DupedEthKeyFound); + } } + self.write_eth_key_addresses(eth_key_reverse_map); self.write_validator_set(&validator_set); self.write_total_voting_power(&total_voting_power); // Credit the bonded tokens to the PoS account @@ -980,6 +1009,10 @@ pub trait PosBase { pub enum GenesisError { #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), + #[error("Ethereum address can only be of secp kind")] + SecpKeyConversion, + #[error("Duplicate Ethereum key found")] + DupedEthKeyFound, } #[allow(missing_docs)] @@ -992,6 +1025,10 @@ pub enum BecomeValidatorError { address {0}" )] StakingRewardAddressEqValidatorAddress(Address), + #[error("Ethereum address can only be of secp kind")] + SecpKeyConversion, + #[error("Duplicate Ethereum key found")] + DupedEthKeyFound, } #[allow(missing_docs)] diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index 8e92ad32b9..92c84446c4 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -24,8 +24,7 @@ const VALIDATOR_STAKING_REWARD_ADDRESS_STORAGE_KEY: &str = const VALIDATOR_CONSENSUS_KEY_STORAGE_KEY: &str = "consensus_key"; const VALIDATOR_ETH_COLD_KEY_STORAGE_KEY: &str = "eth_cold_key"; const VALIDATOR_ETH_HOT_KEY_STORAGE_KEY: &str = "eth_hot_key"; -const ETH_COLD_KEY_ADDRESSES_STORAGE_KEY: &str = "eth_cold_key_addresses"; -const ETH_HOT_KEY_ADDRESSES_STORAGE_KEY: &str = "eth_hot_key_addresses"; +const ETH_KEY_ADDRESSES_STORAGE_KEY: &str = "eth_key_addresses"; const VALIDATOR_STATE_STORAGE_KEY: &str = "state"; const VALIDATOR_TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_VOTING_POWER_STORAGE_KEY: &str = "voting_power"; @@ -420,36 +419,20 @@ pub fn get_validator_address_from_bond(key: &Key) -> Option
{ } } -/// Storage key for look-up from validator's eth cold key addresses to native +/// Storage key for look-up from validator's eth key addresses to native /// address. -pub fn eth_cold_key_addresses_key() -> Key { +pub fn eth_key_addresses_key() -> Key { Key::from(ADDRESS.to_db_key()) - .push(Ð_COLD_KEY_ADDRESSES_STORAGE_KEY.to_owned()) + .push(Ð_KEY_ADDRESSES_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } -/// Is storage key for validators' eth cold key addresses? -pub fn is_eth_cold_key_addresses_key(key: &Key) -> bool { +/// Is storage key for validators' eth key addresses? +pub fn is_eth_key_addresses_key(key: &Key) -> bool { matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(suffix)] if addr == &ADDRESS - && suffix == ETH_COLD_KEY_ADDRESSES_STORAGE_KEY) -} - -/// Storage key for look-up from validator's eth hot key addresses to native -/// address. -pub fn eth_hot_key_addresses_key() -> Key { - Key::from(ADDRESS.to_db_key()) - .push(Ð_HOT_KEY_ADDRESSES_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Is storage key for validators' eth hot key addresses? -pub fn is_eth_hot_key_addresses_key(key: &Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(suffix)] - if addr == &ADDRESS - && suffix == ETH_HOT_KEY_ADDRESSES_STORAGE_KEY) + && suffix == ETH_KEY_ADDRESSES_STORAGE_KEY) } impl PosBase for Storage From e2838199fcda239a4fb8835565d77b00e9a5dda0 Mon Sep 17 00:00:00 2001 From: brentstone Date: Fri, 9 Sep 2022 02:04:00 +0200 Subject: [PATCH 13/45] add eth keys to gen_genesis_validator test --- apps/src/lib/config/genesis.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index b0393cab5e..57987a8619 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -160,7 +160,6 @@ pub mod genesis_config { pub validator: HashMap, } - // TODO add eth keys here #[derive(Clone, Default, Debug, Deserialize, Serialize)] pub struct ValidatorConfig { // Public key for consensus. (default: generate) @@ -923,11 +922,18 @@ pub mod tests { let srkp_arr = staking_reward_keypair.try_to_vec().unwrap(); let (protocol_keypair, dkg_keypair) = wallet::defaults::validator_keys(); + + // TODO: derive validator eth address from an eth keypair + let eth_cold_gov_keypair: common::SecretKey = secp256k1::SigScheme::generate(&mut rng).try_to_sk().unwrap(); + let eth_hot_bridge_keypair: common::SecretKey = secp256k1::SigScheme::generate(&mut rng).try_to_sk().unwrap(); + println!("address: {}", address); println!("staking_reward_address: {}", staking_reward_address); println!("keypair: {:?}", kp_arr); println!("staking_reward_keypair: {:?}", srkp_arr); println!("protocol_keypair: {:?}", protocol_keypair); println!("dkg_keypair: {:?}", dkg_keypair.try_to_vec().unwrap()); + println!("eth_cold_gov_keypair: {:?}", eth_cold_gov_keypair.try_to_vec().unwrap()); + println!("eth_hot_bridge_keypair: {:?}", eth_hot_bridge_keypair.try_to_vec().unwrap()); } } From 231a6645b8c513ea3af3777d041d8bac9c3f149c Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 9 Sep 2022 13:14:36 +0100 Subject: [PATCH 14/45] WIP: adding secp key as a ValidatorKeys field --- apps/src/lib/config/genesis.rs | 20 ++++++++++++++++---- apps/src/lib/wallet/store.rs | 23 +++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 57987a8619..fc916b9caa 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -924,8 +924,14 @@ pub mod tests { wallet::defaults::validator_keys(); // TODO: derive validator eth address from an eth keypair - let eth_cold_gov_keypair: common::SecretKey = secp256k1::SigScheme::generate(&mut rng).try_to_sk().unwrap(); - let eth_hot_bridge_keypair: common::SecretKey = secp256k1::SigScheme::generate(&mut rng).try_to_sk().unwrap(); + let eth_cold_gov_keypair: common::SecretKey = + secp256k1::SigScheme::generate(&mut rng) + .try_to_sk() + .unwrap(); + let eth_hot_bridge_keypair: common::SecretKey = + secp256k1::SigScheme::generate(&mut rng) + .try_to_sk() + .unwrap(); println!("address: {}", address); println!("staking_reward_address: {}", staking_reward_address); @@ -933,7 +939,13 @@ pub mod tests { println!("staking_reward_keypair: {:?}", srkp_arr); println!("protocol_keypair: {:?}", protocol_keypair); println!("dkg_keypair: {:?}", dkg_keypair.try_to_vec().unwrap()); - println!("eth_cold_gov_keypair: {:?}", eth_cold_gov_keypair.try_to_vec().unwrap()); - println!("eth_hot_bridge_keypair: {:?}", eth_hot_bridge_keypair.try_to_vec().unwrap()); + println!( + "eth_cold_gov_keypair: {:?}", + eth_cold_gov_keypair.try_to_vec().unwrap() + ); + println!( + "eth_hot_bridge_keypair: {:?}", + eth_hot_bridge_keypair.try_to_vec().unwrap() + ); } } diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 356b7add53..2f16aa7fee 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -8,6 +8,7 @@ use std::str::FromStr; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; +use either::*; use file_lock::{FileLock, FileOptions}; use namada::types::address::{Address, ImplicitAddress}; use namada::types::key::dkg_session_keys::DkgKeypair; @@ -288,16 +289,26 @@ impl Store { /// /// Note that this removes the validator data. pub fn gen_validator_keys( - protocol_keypair: Option, - scheme: SchemeType, + ethereum_bridge_key: Option, + protocol_keypair: Either, ) -> ValidatorKeys { - let protocol_keypair = - protocol_keypair.unwrap_or_else(|| gen_sk(scheme)); + let ethereum_bridge_key = ethereum_bridge_key + .map(|k| { + if !matches!(&k, common::SecretKey::Secp256k1(_)) { + panic!( + "Ethereum bridge keys can only be of kind Secp256k1" + ); + } + k + }) + .unwrap_or_else(|| gen_sk(SchemeType::Secp256k1)); + let protocol_keypair = protocol_keypair.map_left(gen_sk).into_inner(); let dkg_keypair = ferveo_common::Keypair::::new( &mut StdRng::from_entropy(), ); ValidatorKeys { protocol_keypair, + ethereum_bridge_key, dkg_keypair: Some(dkg_keypair.into()), } } @@ -528,7 +539,7 @@ mod test_wallet { fn test_toml_roundtrip_ed25519() { let mut store = Store::new(); let validator_keys = - Store::gen_validator_keys(None, SchemeType::Ed25519); + Store::gen_validator_keys(None, Left(SchemeType::Ed25519)); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys @@ -541,7 +552,7 @@ mod test_wallet { fn test_toml_roundtrip_secp256k1() { let mut store = Store::new(); let validator_keys = - Store::gen_validator_keys(None, SchemeType::Secp256k1); + Store::gen_validator_keys(None, Left(SchemeType::Secp256k1)); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys From 8e27bba534080197606d6c13284e15b44bd74adc Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 9 Sep 2022 13:26:34 +0100 Subject: [PATCH 15/45] WIP: add missing ValidatorKeys field --- apps/src/lib/wallet/store.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 2f16aa7fee..ac015c2ef5 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -28,6 +28,8 @@ use crate::config::genesis::genesis_config::GenesisConfig; pub struct ValidatorKeys { /// Special keypair for signing protocol txs pub protocol_keypair: common::SecretKey, + /// Special keypair for signing Ethereum bridge txs + pub eth_bridge_keypair: common::SecretKey, /// Special session keypair needed by validators for participating /// in the DKG protocol pub dkg_keypair: Option, @@ -289,10 +291,10 @@ impl Store { /// /// Note that this removes the validator data. pub fn gen_validator_keys( - ethereum_bridge_key: Option, + eth_bridge_keypair: Option, protocol_keypair: Either, ) -> ValidatorKeys { - let ethereum_bridge_key = ethereum_bridge_key + let eth_bridge_keypair = eth_bridge_keypair .map(|k| { if !matches!(&k, common::SecretKey::Secp256k1(_)) { panic!( @@ -308,7 +310,7 @@ impl Store { ); ValidatorKeys { protocol_keypair, - ethereum_bridge_key, + eth_bridge_keypair, dkg_keypair: Some(dkg_keypair.into()), } } From ade2415e96fda91a76c22dd521bd401e0b814ad5 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 9 Sep 2022 15:35:00 +0100 Subject: [PATCH 16/45] WIP: gen eth keys --- apps/src/lib/wallet/mod.rs | 58 ++++++++++++++++++++++++------------ apps/src/lib/wallet/store.rs | 6 ++-- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index b3048ef97d..1feb8108c1 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -118,27 +118,47 @@ impl Wallet { /// we should re-use a keypair already in the wallet pub fn gen_validator_keys( &mut self, + eth_bridge_pk: Option, protocol_pk: Option, - scheme: SchemeType, + protocol_key_scheme: SchemeType, ) -> Result { - let protocol_keypair = protocol_pk.map(|pk| { - self.find_key_by_pkh(&PublicKeyHash::from(&pk)) - .ok() - .or_else(|| { - self.store - .validator_data - .take() - .map(|data| Rc::new(data.keys.protocol_keypair)) - }) - .ok_or(FindKeyError::KeyNotFound) - }); - match protocol_keypair { - Some(Err(err)) => Err(err), - other => Ok(Store::gen_validator_keys( - other.map(|res| res.unwrap().as_ref().clone()), - scheme, - )), - } + let protocol_keypair = self.find_secret_key(protocol_pk, |data| { + Rc::new(data.keys.protocol_keypair) + })?; + let eth_bridge_keypair = self + .find_secret_key(eth_bridge_pk, |data| { + Rc::new(data.keys.eth_bridge_keypair) + })?; + Ok(Store::gen_validator_keys( + eth_bridge_keypair, + protocol_keypair, + protocol_key_scheme, + )) + } + + /// Find a corresponding [`common::SecretKey`] in [`Store`], for some + /// [`common::PublicKey`]. + /// + /// If a key was provided in `maybe_pk`, and it's found in [`Store`], we use + /// `extract_key` to retrieve it from [`ValidatorData`]. + fn find_secret_key( + &mut self, + maybe_pk: Option, + extract_key: F, + ) -> Result>, FindKeyError> + where + F: Fn(ValidatorData) -> Rc, + { + maybe_pk + .map(|pk| { + self.find_key_by_pkh(&PublicKeyHash::from(&pk)) + .ok() + .or_else(|| { + self.store.validator_data.take().map(extract_key) + }) + .ok_or(FindKeyError::KeyNotFound) + }) + .transpose() } /// Add validator data to the store diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index ac015c2ef5..36bb6b4b0a 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -292,7 +292,8 @@ impl Store { /// Note that this removes the validator data. pub fn gen_validator_keys( eth_bridge_keypair: Option, - protocol_keypair: Either, + protocol_keypair: Option, + protocol_keypair_scheme: SchemeType, ) -> ValidatorKeys { let eth_bridge_keypair = eth_bridge_keypair .map(|k| { @@ -304,7 +305,8 @@ impl Store { k }) .unwrap_or_else(|| gen_sk(SchemeType::Secp256k1)); - let protocol_keypair = protocol_keypair.map_left(gen_sk).into_inner(); + let protocol_keypair = + protocol_keypair.unwrap_or_else(|| gen_sk(protocol_keypair_scheme)); let dkg_keypair = ferveo_common::Keypair::::new( &mut StdRng::from_entropy(), ); From aa7e9970e38b915af62b3f4a9e7c2dc859c23601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 9 Sep 2022 17:31:17 +0200 Subject: [PATCH 17/45] fixup! WIP: gen eth keys --- apps/src/lib/wallet/store.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 36bb6b4b0a..d296137572 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -28,12 +28,11 @@ use crate::config::genesis::genesis_config::GenesisConfig; pub struct ValidatorKeys { /// Special keypair for signing protocol txs pub protocol_keypair: common::SecretKey, - /// Special keypair for signing Ethereum bridge txs + /// Special hot keypair for signing Ethereum bridge txs pub eth_bridge_keypair: common::SecretKey, /// Special session keypair needed by validators for participating /// in the DKG protocol pub dkg_keypair: Option, - // TODO add eth hot key } impl ValidatorKeys { From 5ba47813e1a75b294ec758afd3e2780d65438c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 9 Sep 2022 17:33:57 +0200 Subject: [PATCH 18/45] fixup! WIP: add maps for eth address reverse lookup to native address --- proof_of_stake/src/lib.rs | 97 ++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 581560513a..b5822e55be 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -20,7 +20,7 @@ pub mod validation; use core::fmt::Debug; use std::collections::{BTreeSet, HashMap}; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::fmt::Display; use std::hash::Hash; use std::num::TryFromIntError; @@ -91,7 +91,6 @@ pub trait PosReadOnly { /// Cryptographic public key type type PublicKey: Debug + Clone - + TryInto + BorshDeserialize + BorshSerialize + BorshSchema; @@ -274,7 +273,10 @@ pub trait PosActions: PosReadOnly { eth_cold_key: &Self::PublicKey, eth_hot_key: &Self::PublicKey, current_epoch: impl Into, - ) -> Result<(), BecomeValidatorError> { + ) -> Result<(), BecomeValidatorError> + where + for<'a> &'a Self::PublicKey: TryInto, + { let current_epoch = current_epoch.into(); let params = self.read_pos_params(); let mut validator_set = self.read_validator_set(); @@ -290,22 +292,13 @@ pub trait PosActions: PosReadOnly { ), ); } - let convert_key_to_addr = |k| { - k.try_into() - .map_err(|_| BecomeValidatorError::SecpKeyConversion) - }; - let eth_addresses_map = self.read_eth_key_addresses(); - let have_duped_key = eth_key_reverse_map - .contains_key(&convert_key_to_addr(eth_cold_key)) - || eth_key_reverse_map - .contains_key(&convert_key_to_addr(eth_hot_key)); - if have_duped_key { - return Err(BecomeValidatorError::DupedEthKeyFound); - } let BecomeValidatorData { consensus_key, eth_cold_key, eth_hot_key, + eth_cold_key_addr, + eth_hot_key_addr, + state, total_deltas, voting_power, @@ -317,7 +310,20 @@ pub trait PosActions: PosReadOnly { eth_hot_key, &mut validator_set, current_epoch, - ); + )?; + let mut eth_addresses_map = self.read_eth_key_addresses(); + if eth_addresses_map + .insert(eth_cold_key_addr, address.clone()) + .is_some() + { + return Err(BecomeValidatorError::DupedEthKeyFound); + } + if eth_addresses_map + .insert(eth_hot_key_addr, address.clone()) + .is_some() + { + return Err(BecomeValidatorError::DupedEthKeyFound); + } self.write_validator_staking_reward_address( address, staking_reward_address.clone(), @@ -325,6 +331,7 @@ pub trait PosActions: PosReadOnly { self.write_validator_consensus_key(address, consensus_key); self.write_validator_eth_cold_key(address, eth_cold_key); self.write_validator_eth_hot_key(address, eth_hot_key); + self.write_eth_key_addresses(eth_addresses_map); self.write_validator_state(address, state); self.write_validator_set(validator_set); self.write_validator_address_raw_hash(address); @@ -589,7 +596,6 @@ pub trait PosBase { type PublicKey: 'static + Debug + Clone - + TryInto + BorshDeserialize + BorshSerialize + BorshSchema; @@ -758,7 +764,10 @@ pub trait PosBase { > + Clone + 'a, current_epoch: impl Into, - ) -> Result<(), GenesisError> { + ) -> Result<(), GenesisError> + where + &'a Self::PublicKey: TryInto, + { let current_epoch = current_epoch.into(); self.write_pos_params(params); @@ -769,7 +778,7 @@ pub trait PosBase { total_bonded_balance, } = init_genesis(params, validators, current_epoch)?; - let mut eth_key_reverse_map = HashMap::default(); + let mut eth_addresses_map = HashMap::default(); for res in validators { let GenesisValidatorData { @@ -783,6 +792,8 @@ pub trait PosBase { bond: (bond_id, bond), eth_cold_key, eth_hot_key, + eth_cold_key_addr, + eth_hot_key_addr, } = res?; self.write_validator_address_raw_hash(address); self.write_validator_staking_reward_address( @@ -800,22 +811,20 @@ pub trait PosBase { &staking_reward_address, &staking_reward_key, ); - let convert_key_to_addr = - |k| k.try_into().map_err(|_| GenesisError::SecpKeyConversion); - if eth_key_reverse_map - .insert(convert_key_to_addr(ð_cold_key)?, address) + if eth_addresses_map + .insert(eth_cold_key_addr, address.clone()) .is_some() { return Err(GenesisError::DupedEthKeyFound); } - if eth_key_reverse_map - .insert(convert_key_to_addr(ð_hot_key)?, address) + if eth_addresses_map + .insert(eth_hot_key_addr, address.clone()) .is_some() { return Err(GenesisError::DupedEthKeyFound); } } - self.write_eth_key_addresses(eth_key_reverse_map); + self.write_eth_key_addresses(ð_addresses_map); self.write_validator_set(&validator_set); self.write_total_voting_power(&total_voting_power); // Credit the bonded tokens to the PoS account @@ -1182,6 +1191,8 @@ where bond: (BondId
, Bonds), eth_cold_key: ValidatorEthKey, eth_hot_key: ValidatorEthKey, + eth_cold_key_addr: EthAddress, + eth_hot_key_addr: EthAddress, } /// A function that returns genesis data created from the initial validator set. @@ -1236,6 +1247,7 @@ where + BorshSerialize + BorshSchema, PK: 'a + Debug + Clone + BorshDeserialize + BorshSerialize + BorshSchema, + &'a PK: TryInto, { // Accumulate the validator set and total voting power let mut active: BTreeSet> = BTreeSet::default(); @@ -1284,6 +1296,11 @@ where eth_cold_key, eth_hot_key, }| { + let convert_key_to_addr = |k: &'a PK| { + k.try_into().map_err(|_| GenesisError::SecpKeyConversion) + }; + let eth_cold_key_addr = convert_key_to_addr(ð_cold_key)?; + let eth_hot_key_addr = convert_key_to_addr(ð_hot_key)?; let consensus_key = Epoched::init_at_genesis(consensus_key.clone(), current_epoch); let eth_cold_key = @@ -1321,6 +1338,8 @@ where bond: (bond_id, bond), eth_cold_key, eth_hot_key, + eth_cold_key_addr, + eth_hot_key_addr, }) }, ); @@ -1433,23 +1452,26 @@ where consensus_key: ValidatorConsensusKeys, eth_cold_key: ValidatorEthKey, eth_hot_key: ValidatorEthKey, + eth_cold_key_addr: EthAddress, + eth_hot_key_addr: EthAddress, state: ValidatorStates, total_deltas: ValidatorTotalDeltas, voting_power: ValidatorVotingPowers, } /// A function that initialized data for a new validator. -fn become_validator( +fn become_validator<'a, Address, PK, TokenChange>( params: &PosParams, address: &Address, consensus_key: &PK, - eth_cold_key: &PK, - eth_hot_key: &PK, + eth_cold_key: &'a PK, + eth_hot_key: &'a PK, validator_set: &mut ValidatorSets
, current_epoch: Epoch, -) -> BecomeValidatorData +) -> Result, BecomeValidatorError
> where - Address: Debug + Address: Display + + Debug + Clone + Ord + Hash @@ -1457,6 +1479,7 @@ where + BorshSerialize + BorshSchema, PK: Debug + Clone + BorshDeserialize + BorshSerialize + BorshSchema, + &'a PK: TryInto, TokenChange: Default + Debug + Clone @@ -1466,6 +1489,12 @@ where + BorshSerialize + BorshSchema, { + let convert_key_to_addr = |k: &'a PK| { + k.try_into() + .map_err(|_| BecomeValidatorError::SecpKeyConversion) + }; + let eth_cold_key_addr = convert_key_to_addr(ð_cold_key)?; + let eth_hot_key_addr = convert_key_to_addr(ð_hot_key)?; let consensus_key = Epoched::init(consensus_key.clone(), current_epoch, params); let eth_cold_key = @@ -1507,14 +1536,16 @@ where params, ); - BecomeValidatorData { + Ok(BecomeValidatorData { consensus_key, state, total_deltas, voting_power, eth_cold_key, eth_hot_key, - } + eth_cold_key_addr, + eth_hot_key_addr, + }) } struct BondData From d3143f30bfd19a9c7f4e9b74030824c67b4e5342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 9 Sep 2022 18:42:52 +0200 Subject: [PATCH 19/45] write eth keys and addresses for genesis and on-chain created validators --- proof_of_stake/src/lib.rs | 39 ++++++++++++++++++----------- proof_of_stake/src/types.rs | 23 +++++++++++++++-- shared/src/ledger/pos/mod.rs | 41 +++++++++++++++++++++++++++---- shared/src/ledger/pos/storage.rs | 7 ++++++ shared/src/ledger/pos/vp.rs | 13 ++++++++-- shared/src/types/key/common.rs | 21 ++++++++++++++++ shared/src/types/key/secp256k1.rs | 2 +- vm_env/src/proof_of_stake.rs | 11 +++++++++ 8 files changed, 132 insertions(+), 25 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index b5822e55be..8c7a39c628 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -20,7 +20,7 @@ pub mod validation; use core::fmt::Debug; use std::collections::{BTreeSet, HashMap}; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::fmt::Display; use std::hash::Hash; use std::num::TryFromIntError; @@ -34,8 +34,8 @@ use parameters::PosParams; use thiserror::Error; use types::{ ActiveValidator, Bonds, Epoch, EthAddress, EthKeyAddresses, - GenesisValidator, Slash, SlashType, Slashes, TotalVotingPowers, Unbond, - Unbonds, ValidatorConsensusKeys, ValidatorEthKey, ValidatorSet, + GenesisValidator, Slash, SlashType, Slashes, TotalVotingPowers, TryRefTo, + Unbond, Unbonds, ValidatorConsensusKeys, ValidatorEthKey, ValidatorSet, ValidatorSetUpdate, ValidatorSets, ValidatorState, ValidatorStates, ValidatorTotalDeltas, ValidatorVotingPowers, VotingPower, VotingPowerDelta, }; @@ -275,7 +275,7 @@ pub trait PosActions: PosReadOnly { current_epoch: impl Into, ) -> Result<(), BecomeValidatorError> where - for<'a> &'a Self::PublicKey: TryInto, + Self::PublicKey: TryRefTo, { let current_epoch = current_epoch.into(); let params = self.read_pos_params(); @@ -766,7 +766,7 @@ pub trait PosBase { current_epoch: impl Into, ) -> Result<(), GenesisError> where - &'a Self::PublicKey: TryInto, + Self::PublicKey: TryRefTo, { let current_epoch = current_epoch.into(); self.write_pos_params(params); @@ -1246,8 +1246,13 @@ where + BorshDeserialize + BorshSerialize + BorshSchema, - PK: 'a + Debug + Clone + BorshDeserialize + BorshSerialize + BorshSchema, - &'a PK: TryInto, + PK: 'a + + Debug + + Clone + + BorshDeserialize + + BorshSerialize + + BorshSchema + + TryRefTo, { // Accumulate the validator set and total voting power let mut active: BTreeSet> = BTreeSet::default(); @@ -1297,10 +1302,10 @@ where eth_hot_key, }| { let convert_key_to_addr = |k: &'a PK| { - k.try_into().map_err(|_| GenesisError::SecpKeyConversion) + k.try_ref_to().map_err(|_| GenesisError::SecpKeyConversion) }; - let eth_cold_key_addr = convert_key_to_addr(ð_cold_key)?; - let eth_hot_key_addr = convert_key_to_addr(ð_hot_key)?; + let eth_cold_key_addr = convert_key_to_addr(eth_cold_key)?; + let eth_hot_key_addr = convert_key_to_addr(eth_hot_key)?; let consensus_key = Epoched::init_at_genesis(consensus_key.clone(), current_epoch); let eth_cold_key = @@ -1478,8 +1483,12 @@ where + BorshDeserialize + BorshSerialize + BorshSchema, - PK: Debug + Clone + BorshDeserialize + BorshSerialize + BorshSchema, - &'a PK: TryInto, + PK: Debug + + Clone + + BorshDeserialize + + BorshSerialize + + BorshSchema + + TryRefTo, TokenChange: Default + Debug + Clone @@ -1490,11 +1499,11 @@ where + BorshSchema, { let convert_key_to_addr = |k: &'a PK| { - k.try_into() + k.try_ref_to() .map_err(|_| BecomeValidatorError::SecpKeyConversion) }; - let eth_cold_key_addr = convert_key_to_addr(ð_cold_key)?; - let eth_hot_key_addr = convert_key_to_addr(ð_hot_key)?; + let eth_cold_key_addr = convert_key_to_addr(eth_cold_key)?; + let eth_hot_key_addr = convert_key_to_addr(eth_hot_key)?; let consensus_key = Epoched::init(consensus_key.clone(), current_epoch, params); let eth_cold_key = diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index c421903197..a559b67fa9 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -43,8 +43,27 @@ pub type ValidatorEthKey = Epoched; pub type EthKeyAddresses
= HashMap; /// Eth address derived from secp256k1 key -#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct EthAddress([u8; 20]); +#[derive( + Debug, + Eq, + PartialEq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, + BorshSchema, +)] +pub struct EthAddress(pub [u8; 20]); + +/// A ref-to-value conversion that may fail + +pub trait TryRefTo { + /// The error + type Error; + /// Try to perform the conversion. + fn try_ref_to(&self) -> Result; +} /// Epoch identifier. Epochs are identified by consecutive natural numbers. /// diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index c980da81da..7bfdd3cee6 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -3,6 +3,8 @@ mod storage; pub mod vp; +use std::convert::{TryFrom, TryInto}; + pub use namada_proof_of_stake; pub use namada_proof_of_stake::parameters::PosParams; pub use namada_proof_of_stake::types::{ @@ -15,8 +17,10 @@ pub use vp::PosVP; use crate::ledger::storage::{self as ledger_storage, Storage, StorageHasher}; use crate::types::address::{self, Address, InternalAddress}; +use crate::types::key::common; +use crate::types::key::secp256k1::EthAddress; use crate::types::storage::Epoch; -use crate::types::{key, token}; +use crate::types::token; /// Address of the PoS account implemented as a native VP pub const ADDRESS: Address = Address::Internal(InternalAddress::PoS); @@ -47,9 +51,7 @@ pub fn init_genesis_storage<'a, DB, H>( /// Alias for a PoS type with the same name with concrete type parameters pub type ValidatorConsensusKeys = - namada_proof_of_stake::types::ValidatorConsensusKeys< - key::common::PublicKey, - >; + namada_proof_of_stake::types::ValidatorConsensusKeys; /// Alias for a PoS type with the same name with concrete type parameters pub type ValidatorTotalDeltas = @@ -71,7 +73,7 @@ pub type BondId = namada_proof_of_stake::types::BondId
; pub type GenesisValidator = namada_proof_of_stake::types::GenesisValidator< Address, token::Amount, - key::common::PublicKey, + common::PublicKey, >; impl From for namada_proof_of_stake::types::Epoch { @@ -87,3 +89,32 @@ impl From for Epoch { Epoch(epoch) } } + +impl From for namada_proof_of_stake::types::EthAddress { + fn from(EthAddress(addr): EthAddress) -> Self { + namada_proof_of_stake::types::EthAddress(addr) + } +} + +impl TryFrom<&common::PublicKey> for namada_proof_of_stake::types::EthAddress { + type Error = common::EthAddressConvError; + + fn try_from(value: &common::PublicKey) -> Result { + let addr = EthAddress::try_from(value)?; + Ok(addr.into()) + } +} + +impl + namada_proof_of_stake::types::TryRefTo< + namada_proof_of_stake::types::EthAddress, + > for common::PublicKey +{ + type Error = common::EthAddressConvError; + + fn try_ref_to( + &self, + ) -> Result { + self.try_into() + } +} diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index 92c84446c4..3d613926ce 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -633,6 +633,13 @@ where .unwrap(); } + fn write_eth_key_addresses( + &mut self, + value: &types::EthKeyAddresses, + ) { + self.write(ð_key_addresses_key(), encode(value)).unwrap(); + } + fn init_staking_reward_account( &mut self, address: &Self::Address, diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 7d2b7680f1..66a348fbf9 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -16,8 +16,8 @@ use namada_proof_of_stake::{validation, PosReadOnly}; use thiserror::Error; use super::{ - bond_key, is_bond_key, is_params_key, is_total_voting_power_key, - is_unbond_key, is_validator_set_key, + bond_key, eth_key_addresses_key, is_bond_key, is_params_key, + is_total_voting_power_key, is_unbond_key, is_validator_set_key, is_validator_staking_reward_address_key, is_validator_total_deltas_key, is_validator_voting_power_key, params_key, staking_token_address, total_voting_power_key, unbond_key, validator_consensus_key_key, @@ -433,6 +433,15 @@ where let value = self.ctx.read_pre(&validator_eth_hot_key_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } + + fn read_eth_key_addresses(&self) -> types::EthKeyAddresses { + let value = self + .ctx + .read_pre(ð_key_addresses_key()) + .unwrap() + .unwrap(); + decode(value).unwrap() + } } impl From for Error { diff --git a/shared/src/types/key/common.rs b/shared/src/types/key/common.rs index 3cdec73bb9..861eac5b53 100644 --- a/shared/src/types/key/common.rs +++ b/shared/src/types/key/common.rs @@ -1,5 +1,6 @@ //! Cryptographic keys +use std::convert::TryFrom; use std::fmt::Display; use std::str::FromStr; @@ -7,7 +8,9 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; +use thiserror::Error; +use super::secp256k1::EthAddress; use super::{ ed25519, secp256k1, ParsePublicKeyError, ParseSecretKeyError, ParseSignatureError, RefTo, SchemeType, SigScheme as SigSchemeTrait, @@ -81,6 +84,24 @@ impl FromStr for PublicKey { } } +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum EthAddressConvError { + #[error("Eth key cannot be ed25519, only secp256k1")] + CannotBeEd25519, +} + +impl TryFrom<&PublicKey> for EthAddress { + type Error = EthAddressConvError; + + fn try_from(value: &PublicKey) -> Result { + match value { + PublicKey::Ed25519(_) => Err(EthAddressConvError::CannotBeEd25519), + PublicKey::Secp256k1(pk) => Ok(EthAddress::from(pk).into()), + } + } +} + /// Secret key #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, BorshSchema)] #[allow(clippy::large_enum_variant)] diff --git a/shared/src/types/key/secp256k1.rs b/shared/src/types/key/secp256k1.rs index 198cb8d74b..d3b037c0e3 100644 --- a/shared/src/types/key/secp256k1.rs +++ b/shared/src/types/key/secp256k1.rs @@ -24,7 +24,7 @@ pub struct PublicKey(pub libsecp256k1::PublicKey); /// Eth address derived from secp256k1 key #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct EthAddress([u8; 20]); +pub struct EthAddress(pub [u8; 20]); impl super::PublicKey for PublicKey { const TYPE: SchemeType = SigScheme::TYPE; diff --git a/vm_env/src/proof_of_stake.rs b/vm_env/src/proof_of_stake.rs index 53e2ab9470..835d7d6ab1 100644 --- a/vm_env/src/proof_of_stake.rs +++ b/vm_env/src/proof_of_stake.rs @@ -187,6 +187,10 @@ impl namada_proof_of_stake::PosReadOnly for PoS { ) -> Option { tx::read(validator_eth_hot_key_key(key).to_string()) } + + fn read_eth_key_addresses(&self) -> types::EthKeyAddresses { + tx::read(eth_key_addresses_key().to_string()).unwrap() + } } impl namada_proof_of_stake::PosActions for PoS { @@ -294,4 +298,11 @@ impl namada_proof_of_stake::PosActions for PoS { ) { tx::write(validator_eth_hot_key_key(address).to_string(), &value) } + + fn write_eth_key_addresses( + &self, + value: types::EthKeyAddresses, + ) { + tx::write(eth_key_addresses_key().to_string(), &value) + } } From dc0573392510a9e35a69b0cbbbfc21838ebbdc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 12 Sep 2022 16:16:38 +0200 Subject: [PATCH 20/45] fixup! WIP: gen eth keys --- apps/src/lib/client/tx.rs | 13 +++++++--- apps/src/lib/client/utils.rs | 35 +++++++++++++++++++++++++++ apps/src/lib/config/genesis.rs | 14 +++-------- apps/src/lib/node/ledger/shell/mod.rs | 8 +++--- apps/src/lib/wallet/defaults.rs | 16 ++++++++++-- apps/src/lib/wallet/mod.rs | 10 ++++++-- apps/src/lib/wallet/pre_genesis.rs | 15 +++++------- apps/src/lib/wallet/store.rs | 5 ++-- shared/src/types/key/common.rs | 2 +- 9 files changed, 84 insertions(+), 34 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index a1aabfe532..370780b0c1 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -177,6 +177,8 @@ pub async fn submit_init_validator( let validator_key_alias = format!("{}-key", alias); let consensus_key_alias = format!("{}-consensus-key", alias); let rewards_key_alias = format!("{}-rewards-key", alias); + let eth_hot_key_alias = format!("{}-eth-hot-key", alias); + let eth_cold_key_alias = format!("{}-eth-cold-key", alias); let account_key = ctx.get_opt_cached(&account_key).unwrap_or_else(|| { println!("Generating validator account key..."); ctx.wallet @@ -225,7 +227,7 @@ pub async fn submit_init_validator( .gen_key( // Note that ETH only allows secp256k1 SchemeType::Secp256k1, - Some(consensus_key_alias.clone()), + Some(eth_cold_key_alias.clone()), unsafe_dont_encrypt, ) .1 @@ -246,7 +248,7 @@ pub async fn submit_init_validator( .gen_key( // Note that ETH only allows secp256k1 SchemeType::Secp256k1, - Some(consensus_key_alias.clone()), + Some(eth_hot_key_alias.clone()), unsafe_dont_encrypt, ) .1 @@ -269,9 +271,12 @@ pub async fn submit_init_validator( if protocol_key.is_none() { println!("Generating protocol signing key..."); } + let eth_hot_pk = eth_hot_key.ref_to(); // Generate the validator keys - let validator_keys = - ctx.wallet.gen_validator_keys(protocol_key, scheme).unwrap(); + let validator_keys = ctx + .wallet + .gen_validator_keys(Some(eth_hot_pk), protocol_key, scheme) + .unwrap(); let protocol_key = validator_keys.get_protocol_keypair().ref_to(); let dkg_key = validator_keys .dkg_keypair diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index ca0a3d7347..0c06851684 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -590,6 +590,36 @@ pub fn init_network( keypair.ref_to() }); + let eth_hot_pk = try_parse_public_key( + format!("validator {name} eth hot key"), + &config.eth_hot_key, + ) + .unwrap_or_else(|| { + let alias = format!("{}-eth-hot-key", name); + println!("Generating validator {} eth hot key...", name); + let (_alias, keypair) = wallet.gen_key( + SchemeType::Secp256k1, + Some(alias), + unsafe_dont_encrypt, + ); + keypair.ref_to() + }); + + let eth_cold_pk = try_parse_public_key( + format!("validator {name} eth cold key"), + &config.eth_cold_key, + ) + .unwrap_or_else(|| { + let alias = format!("{}-eth-cold-key", name); + println!("Generating validator {} eth cold key...", name); + let (_alias, keypair) = wallet.gen_key( + SchemeType::Secp256k1, + Some(alias), + unsafe_dont_encrypt, + ); + keypair.ref_to() + }); + let dkg_pk = &config .dkg_public_key .as_ref() @@ -608,6 +638,7 @@ pub fn init_network( let validator_keys = wallet .gen_validator_keys( + Some(eth_hot_pk.clone()), Some(protocol_pk.clone()), SchemeType::Ed25519, ) @@ -624,6 +655,10 @@ pub fn init_network( Some(genesis_config::HexString(account_pk.to_string())); config.staking_reward_public_key = Some(genesis_config::HexString(staking_reward_pk.to_string())); + config.eth_cold_key = + Some(genesis_config::HexString(eth_cold_pk.to_string())); + config.eth_hot_key = + Some(genesis_config::HexString(eth_hot_pk.to_string())); config.protocol_public_key = Some(genesis_config::HexString(protocol_pk.to_string())); diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index fc916b9caa..80ef4ee7b0 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -772,21 +772,15 @@ pub fn genesis() -> Genesis { 65, 17, 187, 6, 238, 141, 63, 188, 76, 38, 102, 7, 47, 185, 28, 52, ]) .unwrap(); - let secp_eth_hot_keypair = secp256k1::SecretKey::try_from_slice(&[ - 117, 93, 118, 129, 202, 67, 51, 62, 202, 196, 130, 244, 5, 44, 88, 200, - 121, 169, 11, 227, 79, 223, 74, 88, 49, 132, 213, 59, 64, 20, 13, 82, - ]) - .unwrap(); let staking_reward_keypair = common::SecretKey::try_from_sk(&ed_staking_reward_keypair).unwrap(); let eth_cold_keypair = common::SecretKey::try_from_sk(&secp_eth_cold_keypair).unwrap(); - let eth_hot_keypair = - common::SecretKey::try_from_sk(&secp_eth_hot_keypair).unwrap(); let address = wallet::defaults::validator_address(); let staking_reward_address = Address::decode("atest1v4ehgw36xcersvee8qerxd35x9prsw2xg5erxv6pxfpygd2x89z5xsf5xvmnysejgv6rwd2rnj2avt").unwrap(); - let (protocol_keypair, dkg_keypair) = wallet::defaults::validator_keys(); + let (protocol_keypair, eth_bridge_keypair, dkg_keypair) = + wallet::defaults::validator_keys(); let validator = Validator { pos_data: GenesisValidator { address, @@ -795,7 +789,7 @@ pub fn genesis() -> Genesis { consensus_key: consensus_keypair.ref_to(), staking_reward_key: staking_reward_keypair.ref_to(), eth_cold_key: eth_cold_keypair.ref_to(), - eth_hot_key: eth_hot_keypair.ref_to(), + eth_hot_key: eth_bridge_keypair.ref_to(), }, account_key: account_keypair.ref_to(), protocol_key: protocol_keypair.ref_to(), @@ -920,7 +914,7 @@ pub mod tests { let staking_reward_keypair: common::SecretKey = ed25519::SigScheme::generate(&mut rng).try_to_sk().unwrap(); let srkp_arr = staking_reward_keypair.try_to_vec().unwrap(); - let (protocol_keypair, dkg_keypair) = + let (protocol_keypair, _eth_hot_bridge_keypair, dkg_keypair) = wallet::defaults::validator_keys(); // TODO: derive validator eth address from an eth keypair diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 68c9c44d00..fde27d0288 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -379,13 +379,15 @@ where } #[cfg(feature = "dev")] { - let validator_keys = wallet::defaults::validator_keys(); + let (protocol_keypair, eth_bridge_keypair, dkg_keypair) = + wallet::defaults::validator_keys(); ShellMode::Validator { data: wallet::ValidatorData { address: wallet::defaults::validator_address(), keys: wallet::ValidatorKeys { - protocol_keypair: validator_keys.0, - dkg_keypair: Some(validator_keys.1), + protocol_keypair, + eth_bridge_keypair, + dkg_keypair: Some(dkg_keypair), }, }, broadcast_sender, diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index eb5a6f3d7a..d4fb1b1b83 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -81,13 +81,23 @@ mod dev { use crate::wallet::alias::Alias; - /// Generate a new protocol signing keypair and DKG session keypair - pub fn validator_keys() -> (common::SecretKey, DkgKeypair) { + /// Generate a new protocol signing keypair, eth hot key and DKG session + /// keypair + pub fn validator_keys() -> (common::SecretKey, common::SecretKey, DkgKeypair) + { + // ed25519 bytes let bytes: [u8; 33] = [ 0, 200, 107, 23, 252, 78, 80, 8, 164, 142, 3, 194, 33, 12, 250, 169, 211, 127, 47, 13, 194, 54, 199, 81, 102, 246, 189, 119, 144, 25, 27, 113, 222, ]; + // secp256k1 bytes + let eth_bridge_key_bytes = [ + 1, 117, 93, 118, 129, 202, 67, 51, 62, 202, 196, 130, 244, 5, 44, + 88, 200, 121, 169, 11, 227, 79, 223, 74, 88, 49, 132, 213, 59, 64, + 20, 13, 82, + ]; + // DkgKeypair let dkg_bytes = [ 32, 0, 0, 0, 210, 193, 55, 24, 92, 233, 23, 2, 73, 204, 221, 107, 110, 222, 192, 136, 54, 24, 108, 236, 137, 27, 121, 142, 142, 7, @@ -96,6 +106,8 @@ mod dev { ( BorshDeserialize::deserialize(&mut bytes.as_ref()).unwrap(), + BorshDeserialize::deserialize(&mut eth_bridge_key_bytes.as_ref()) + .unwrap(), BorshDeserialize::deserialize(&mut dkg_bytes.as_ref()).unwrap(), ) } diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 1feb8108c1..492cee51bc 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -130,8 +130,14 @@ impl Wallet { Rc::new(data.keys.eth_bridge_keypair) })?; Ok(Store::gen_validator_keys( - eth_bridge_keypair, - protocol_keypair, + eth_bridge_keypair.map(|sk| { + Rc::try_unwrap(sk) + .expect("There should be only a single strong RC reference") + }), + protocol_keypair.map(|sk| { + Rc::try_unwrap(sk) + .expect("There should be only a single strong RC reference") + }), protocol_key_scheme, )) } diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index bc5bb84106..c2c7cf393b 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -60,13 +60,11 @@ pub struct ValidatorStore { pub consensus_key: wallet::StoredKeypair, /// Cryptographic keypair for eth cold key pub eth_cold_key: wallet::StoredKeypair, - /// Cryptographic keypair for eth hot key - pub eth_hot_key: wallet::StoredKeypair, /// Cryptographic keypair for rewards key pub rewards_key: wallet::StoredKeypair, /// Cryptographic keypair for Tendermint node key pub tendermint_node_key: wallet::StoredKeypair, - /// Special validator keys + /// Special validator keys. Contains the ETH hot key. pub validator_keys: wallet::ValidatorKeys, } @@ -130,7 +128,7 @@ impl ValidatorWallet { let eth_cold_key = store.eth_cold_key.get(true, password.clone())?; let eth_hot_key = - store.eth_hot_key.get(true, password.clone())?; + Rc::new(store.validator_keys.eth_bridge_keypair.clone()); let rewards_key = store.rewards_key.get(true, password.clone())?; @@ -166,8 +164,6 @@ impl ValidatorWallet { ); let (eth_cold_key, eth_cold_sk) = gen_key_to_store(SchemeType::Secp256k1, &password); - let (eth_hot_key, eth_hot_sk) = - gen_key_to_store(SchemeType::Secp256k1, &password); let (rewards_key, rewards_sk) = gen_key_to_store(scheme, &password); let (tendermint_node_key, tendermint_node_sk) = gen_key_to_store( @@ -175,12 +171,13 @@ impl ValidatorWallet { SchemeType::Ed25519, &password, ); - let validator_keys = store::Store::gen_validator_keys(None, scheme); + let validator_keys = + store::Store::gen_validator_keys(None, None, scheme); + let eth_hot_key = Rc::new(validator_keys.eth_bridge_keypair.clone()); let store = ValidatorStore { account_key, consensus_key, eth_cold_key, - eth_hot_key, rewards_key, tendermint_node_key, validator_keys, @@ -190,7 +187,7 @@ impl ValidatorWallet { account_key: account_sk, consensus_key: consensus_sk, eth_cold_key: eth_cold_sk, - eth_hot_key: eth_hot_sk, + eth_hot_key, rewards_key: rewards_sk, tendermint_node_key: tendermint_node_sk, } diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index d296137572..88af4c9111 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -8,7 +8,6 @@ use std::str::FromStr; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; -use either::*; use file_lock::{FileLock, FileOptions}; use namada::types::address::{Address, ImplicitAddress}; use namada::types::key::dkg_session_keys::DkgKeypair; @@ -542,7 +541,7 @@ mod test_wallet { fn test_toml_roundtrip_ed25519() { let mut store = Store::new(); let validator_keys = - Store::gen_validator_keys(None, Left(SchemeType::Ed25519)); + Store::gen_validator_keys(None, None, SchemeType::Ed25519); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys @@ -555,7 +554,7 @@ mod test_wallet { fn test_toml_roundtrip_secp256k1() { let mut store = Store::new(); let validator_keys = - Store::gen_validator_keys(None, Left(SchemeType::Secp256k1)); + Store::gen_validator_keys(None, None, SchemeType::Secp256k1); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys diff --git a/shared/src/types/key/common.rs b/shared/src/types/key/common.rs index 861eac5b53..7e9b468e50 100644 --- a/shared/src/types/key/common.rs +++ b/shared/src/types/key/common.rs @@ -97,7 +97,7 @@ impl TryFrom<&PublicKey> for EthAddress { fn try_from(value: &PublicKey) -> Result { match value { PublicKey::Ed25519(_) => Err(EthAddressConvError::CannotBeEd25519), - PublicKey::Secp256k1(pk) => Ok(EthAddress::from(pk).into()), + PublicKey::Secp256k1(pk) => Ok(EthAddress::from(pk)), } } } From dac56f26b4a6fd81dcdedb8bbceac10e27fdf437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 12 Sep 2022 17:35:31 +0200 Subject: [PATCH 21/45] fixup! WIP: gen eth keys --- apps/src/lib/wallet/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 492cee51bc..5646416a38 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -130,14 +130,8 @@ impl Wallet { Rc::new(data.keys.eth_bridge_keypair) })?; Ok(Store::gen_validator_keys( - eth_bridge_keypair.map(|sk| { - Rc::try_unwrap(sk) - .expect("There should be only a single strong RC reference") - }), - protocol_keypair.map(|sk| { - Rc::try_unwrap(sk) - .expect("There should be only a single strong RC reference") - }), + eth_bridge_keypair.map(|sk| sk.as_ref().clone()), + protocol_keypair.map(|sk| sk.as_ref().clone()), protocol_key_scheme, )) } From 5cbc47ca1e406c69e2a0e88527279d44978eecfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 12 Sep 2022 17:35:34 +0200 Subject: [PATCH 22/45] fix cli --- apps/src/lib/cli.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index f271433b9e..a714c2af22 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1765,9 +1765,9 @@ pub mod args { will be generated if none given. Note that this must be \ secp256k1.", )) - .arg(VALIDATOR_ETH_COLD_KEY.def().about( - "An Eth cold key for the validator account. A new one \ - will be generated if none given. Note that this must be \ + .arg(VALIDATOR_ETH_HOT_KEY.def().about( + "An Eth hot key for the validator account. A new one will \ + be generated if none given. Note that this must be \ secp256k1.", )) .arg(REWARDS_KEY.def().about( From 175da50fafc2f7bfacaf6ebac48a2b0d3e59e61a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 12 Sep 2022 16:45:44 +0100 Subject: [PATCH 23/45] Fix wasm tests --- shared/src/types/key/mod.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/shared/src/types/key/mod.rs b/shared/src/types/key/mod.rs index bb80372c4e..1c5ac85558 100644 --- a/shared/src/types/key/mod.rs +++ b/shared/src/types/key/mod.rs @@ -367,7 +367,7 @@ pub mod testing { use super::SigScheme; use crate::types::key::*; - /// A keypair for tests + /// An ed25519 keypair for tests pub fn keypair_1() -> ::SecretKey { // generated from `cargo test gen_keypair -- --nocapture` let bytes = [ @@ -381,7 +381,7 @@ pub mod testing { .unwrap() } - /// A keypair for tests + /// An ed25519 keypair for tests pub fn keypair_2() -> ::SecretKey { // generated from `cargo test gen_keypair -- --nocapture` let bytes = [ @@ -395,6 +395,32 @@ pub mod testing { .unwrap() } + /// An Ethereum keypair for tests + pub fn keypair_3() -> ::SecretKey { + let bytes = [ + 0xf3, 0x78, 0x78, 0x80, 0xba, 0x85, 0x0b, 0xa4, 0xc5, 0x74, 0x50, + 0x5a, 0x23, 0x54, 0x6d, 0x46, 0x74, 0xa1, 0x3f, 0x09, 0x75, 0x0c, + 0xf4, 0xb5, 0xb8, 0x17, 0x69, 0x64, 0xf4, 0x08, 0xd4, 0x80, + ]; + secp256k1::SecretKey::try_from_slice(bytes.as_ref()) + .unwrap() + .try_to_sk() + .unwrap() + } + + /// An Ethereum keypair for tests + pub fn keypair_4() -> ::SecretKey { + let bytes = [ + 0x68, 0xab, 0xce, 0x64, 0x54, 0x07, 0x7e, 0xf5, 0x1a, 0xb4, 0x31, + 0x7a, 0xb8, 0x8b, 0x98, 0x30, 0x27, 0x11, 0x4e, 0x58, 0x69, 0xd6, + 0x45, 0x94, 0xdc, 0x90, 0x8d, 0x94, 0xee, 0x58, 0x46, 0x91, + ]; + secp256k1::SecretKey::try_from_slice(bytes.as_ref()) + .unwrap() + .try_to_sk() + .unwrap() + } + /// Generate an arbitrary [`super::SecretKey`]. pub fn arb_keypair() -> impl Strategy { any::<[u8; 32]>().prop_map(move |seed| { From 3950da6c8c5855ebecc3021b548c0a9e45840167 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 13 Sep 2022 08:56:32 +0100 Subject: [PATCH 24/45] Remove duplicate tiny-keccak dep --- shared/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index b35b85bd36..9edd4079e8 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -92,9 +92,8 @@ tempfile = {version = "3.2.0", optional = true} # temporarily using fork work-around for https://github.com/informalsystems/tendermint-rs/issues/971 tendermint = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", features = ["secp256k1"]} tendermint-proto = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9"} -tiny-keccak = {version = "2.0.2", features = ["keccak"]} thiserror = "1.0.30" -tiny-keccak = "2.0.2" +tiny-keccak = {version = "2.0.2", features = ["keccak"]} tracing = "0.1.30" wasmer = {version = "=2.2.0", optional = true} wasmer-cache = {version = "=2.2.0", optional = true} From 7ca2f72901a9e0c15c6ae1284f4a09a81bf3318b Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 13 Sep 2022 09:05:44 +0100 Subject: [PATCH 25/45] Fix prepare and process proposal unit tests --- apps/src/lib/node/ledger/shell/prepare_proposal.rs | 10 +++++----- apps/src/lib/node/ledger/shell/process_proposal.rs | 6 +++--- proof_of_stake/src/types.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 8ee6550a94..8c97f91897 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -331,7 +331,7 @@ mod test_prepare_proposal { shell.storage.last_height = LAST_HEIGHT; let signed_vote_extension = { - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let validator_addr = wallet::defaults::validator_address(); // generate a valid signature @@ -363,7 +363,7 @@ mod test_prepare_proposal { // artificially change the block height shell.storage.last_height = LAST_HEIGHT; - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let validator_addr = wallet::defaults::validator_address(); let signed_vote_extension = { @@ -422,7 +422,7 @@ mod test_prepare_proposal { // artificially change the block height shell.storage.last_height = LAST_HEIGHT; - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let validator_addr = wallet::defaults::validator_address(); let ethereum_event = EthereumEvent::TransfersToNamada { @@ -499,7 +499,7 @@ mod test_prepare_proposal { // artificially change the block height shell.storage.last_height = LAST_HEIGHT; - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let validator_addr = wallet::defaults::validator_address(); let ethereum_event = EthereumEvent::TransfersToNamada { @@ -620,7 +620,7 @@ mod test_prepare_proposal { ); // test prepare proposal - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let validator_addr = wallet::defaults::validator_address(); let ethereum_event = EthereumEvent::TransfersToNamada { diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index c3a1fbd14b..59e4cc91ce 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -463,7 +463,7 @@ mod test_process_proposal { const LAST_HEIGHT: BlockHeight = BlockHeight(2); let (mut shell, _, _) = test_utils::setup(); shell.storage.last_height = LAST_HEIGHT; - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let vote_extension_digest = { let validator_addr = wallet::defaults::validator_address(); let signed_vote_extension = { @@ -531,7 +531,7 @@ mod test_process_proposal { const LAST_HEIGHT: BlockHeight = BlockHeight(2); let (mut shell, _, _) = test_utils::setup(); shell.storage.last_height = LAST_HEIGHT; - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let vote_extension_digest = { let addr = wallet::defaults::validator_address(); let event = EthereumEvent::TransfersToNamada { @@ -579,7 +579,7 @@ mod test_process_proposal { const PRED_LAST_HEIGHT: BlockHeight = BlockHeight(LAST_HEIGHT.0 - 1); let (mut shell, _, _) = test_utils::setup(); shell.storage.last_height = LAST_HEIGHT; - let (protocol_key, _) = wallet::defaults::validator_keys(); + let (protocol_key, _, _) = wallet::defaults::validator_keys(); let vote_extension_digest = { let addr = wallet::defaults::validator_address(); let event = EthereumEvent::TransfersToNamada { diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index a559b67fa9..5a115e96eb 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -36,7 +36,7 @@ pub type Unbonds = pub type ValidatorSets
= Epoched, OffsetUnboundingLen>; /// Epoched total voting power. -pub type TotalVotingPowers = EpochedDelta; +pub type TotalVotingPowers = EpochedDelta; /// Epoched validator's eth key. pub type ValidatorEthKey = Epoched; /// Map from eth addresses back to native addresses. From e22a4e28ec060c9afc311fd4495526884eee074e Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 13 Sep 2022 09:06:02 +0100 Subject: [PATCH 26/45] Run make fmt --- proof_of_stake/src/types.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index 5a115e96eb..5e16c69bcf 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -36,7 +36,8 @@ pub type Unbonds = pub type ValidatorSets
= Epoched, OffsetUnboundingLen>; /// Epoched total voting power. -pub type TotalVotingPowers = EpochedDelta; +pub type TotalVotingPowers = + EpochedDelta; /// Epoched validator's eth key. pub type ValidatorEthKey = Epoched; /// Map from eth addresses back to native addresses. From aaad19fee120da35928ae6597de3e9db51e664dd Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 09:54:40 +0100 Subject: [PATCH 27/45] Remove duped EthAddress type from namada shared --- shared/src/ledger/pos/mod.rs | 2 +- shared/src/types/ethereum_events.rs | 7 +++++++ shared/src/types/key/common.rs | 2 +- shared/src/types/key/secp256k1.rs | 5 +---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index 7bfdd3cee6..7253240a72 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -17,8 +17,8 @@ pub use vp::PosVP; use crate::ledger::storage::{self as ledger_storage, Storage, StorageHasher}; use crate::types::address::{self, Address, InternalAddress}; +use crate::types::ethereum_events::EthAddress; use crate::types::key::common; -use crate::types::key::secp256k1::EthAddress; use crate::types::storage::Epoch; use crate::types::token; diff --git a/shared/src/types/ethereum_events.rs b/shared/src/types/ethereum_events.rs index 869b4a4f14..5e4d286f88 100644 --- a/shared/src/types/ethereum_events.rs +++ b/shared/src/types/ethereum_events.rs @@ -80,6 +80,13 @@ impl FromStr for EthAddress { } } +impl From for EthAddress { + #[inline] + fn from(addr: namada_proof_of_stake::types::EthAddress) -> EthAddress { + EthAddress(addr.0) + } +} + /// A Keccak hash #[derive( Clone, diff --git a/shared/src/types/key/common.rs b/shared/src/types/key/common.rs index 7e9b468e50..59196e1ff0 100644 --- a/shared/src/types/key/common.rs +++ b/shared/src/types/key/common.rs @@ -10,12 +10,12 @@ use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::secp256k1::EthAddress; use super::{ ed25519, secp256k1, ParsePublicKeyError, ParseSecretKeyError, ParseSignatureError, RefTo, SchemeType, SigScheme as SigSchemeTrait, VerifySigError, }; +use crate::types::ethereum_events::EthAddress; /// Public key #[derive( diff --git a/shared/src/types/key/secp256k1.rs b/shared/src/types/key/secp256k1.rs index d3b037c0e3..9ff662aa36 100644 --- a/shared/src/types/key/secp256k1.rs +++ b/shared/src/types/key/secp256k1.rs @@ -17,15 +17,12 @@ use super::{ ParsePublicKeyError, ParseSecretKeyError, ParseSignatureError, RefTo, SchemeType, SigScheme as SigSchemeTrait, VerifySigError, }; +use crate::types::ethereum_events::EthAddress; /// secp256k1 public key #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct PublicKey(pub libsecp256k1::PublicKey); -/// Eth address derived from secp256k1 key -#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct EthAddress(pub [u8; 20]); - impl super::PublicKey for PublicKey { const TYPE: SchemeType = SigScheme::TYPE; From 166e48350ec5d5338ef6114d8d5e2beebcc471a9 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 10:27:58 +0100 Subject: [PATCH 28/45] Return an Ethereum bridge addr from a Namada validator addr --- apps/src/lib/node/ledger/shell/queries.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index f03a37a5b1..92248a1d6e 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -9,6 +9,7 @@ use namada::ledger::pos::namada_proof_of_stake::types::VotingPower; use namada::ledger::pos::types::WeightedValidator; use namada::ledger::pos::PosParams; use namada::types::address::Address; +use namada::types::ethereum_events::EthAddress; use namada::types::key; use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::storage::{Epoch, Key, PrefixValue}; @@ -332,6 +333,13 @@ pub(crate) trait QueriesExt { /// Retrieves the [`BlockHeight`] that is currently being decided. fn get_current_decision_height(&self) -> BlockHeight; + + /// For a given Namada validator, return its corresponding Ethereum bridge + /// address. + fn get_ethbridge_from_namada_addr( + &self, + validator: &Address, + ) -> Option; } impl QueriesExt for Storage @@ -541,6 +549,15 @@ where fn get_current_decision_height(&self) -> BlockHeight { self.last_height + 1 } + + fn get_ethbridge_from_namada_addr( + &self, + validator: &Address, + ) -> Option { + self.read_validator_eth_hot_key(validator) + .as_ref() + .and_then(|pk| pk.try_into().ok()) + } } /// This enum is used as a parameter to From 8fffeca14f1cf07bad355ad9dd4cb62c391534b4 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 10:54:17 +0100 Subject: [PATCH 29/45] Add voting powers map to valset upd vexts --- apps/src/lib/node/ledger/shell/queries.rs | 18 ++++++++ .../lib/node/ledger/shell/vote_extensions.rs | 42 +++++++++++++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index 92248a1d6e..80442a6588 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -340,6 +340,13 @@ pub(crate) trait QueriesExt { &self, validator: &Address, ) -> Option; + + /// For a given Namada validator, return its corresponding Ethereum + /// governance address. + fn get_ethgov_from_namada_addr( + &self, + validator: &Address, + ) -> Option; } impl QueriesExt for Storage @@ -550,6 +557,7 @@ where self.last_height + 1 } + #[inline] fn get_ethbridge_from_namada_addr( &self, validator: &Address, @@ -558,6 +566,16 @@ where .as_ref() .and_then(|pk| pk.try_into().ok()) } + + #[inline] + fn get_ethgov_from_namada_addr( + &self, + validator: &Address, + ) -> Option { + self.read_validator_eth_cold_key(validator) + .as_ref() + .and_then(|pk| pk.try_into().ok()) + } } /// This enum is used as a parameter to diff --git a/apps/src/lib/node/ledger/shell/vote_extensions.rs b/apps/src/lib/node/ledger/shell/vote_extensions.rs index 041b6a3f9c..73242f9e32 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions.rs @@ -11,6 +11,7 @@ mod extend_votes { use borsh::BorshDeserialize; use namada::proto::Signed; use namada::types::transaction::protocol::ProtocolTxType; + use namada::types::vote_extensions::validator_set_update::EthAddrBook; use namada::types::vote_extensions::{ ethereum_events, validator_set_update, VoteExtension, VoteExtensionDigest, @@ -109,28 +110,51 @@ mod extend_votes { .can_send_validator_set_update(SendValsetUpd::Now) .then(|| { let next_epoch = self.storage.get_current_epoch().0.next(); - let _validator_set = - self.storage.get_active_validators(Some(next_epoch)); + let voting_powers = self + .storage + .get_active_validators(Some(next_epoch)) + .into_iter() + .map(|validator| { + let hot_key_addr = self + .storage + .get_ethbridge_from_namada_addr( + &validator.address, + ) + .expect( + "All Namada validators should have an \ + Ethereum bridge key", + ); + let cold_key_addr = self + .storage + .get_ethgov_from_namada_addr(&validator.address) + .expect( + "All Namada validators should have an \ + Ethereum governance key", + ); + let eth_addr_book = EthAddrBook { + hot_key_addr, + cold_key_addr, + }; + (eth_addr_book, validator.voting_power) + }) + .collect(); let ext = validator_set_update::Vext { validator_addr, - // TODO: we need a way to map ethereum addresses to - // namada validator addresses - voting_powers: std::collections::HashMap::new(), + voting_powers, block_height: self .storage .get_current_decision_height(), }; - let protocol_key = match &self.mode { + let eth_key = match &self.mode { ShellMode::Validator { data, .. } => { - &data.keys.protocol_keypair + &data.keys.eth_bridge_keypair } _ => unreachable!("{VALIDATOR_EXPECT_MSG}"), }; - // TODO: sign validator set update with secp key instead - ext.sign(protocol_key) + ext.sign(eth_key) }) } From 48e1fe97a33eff02ea56cebb28c672221e0019ef Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 11:24:52 +0100 Subject: [PATCH 30/45] Add missing read_eth_key_addresses() to PosReadOnly --- proof_of_stake/src/lib.rs | 4 ++++ shared/src/ledger/pos/storage.rs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 8c7a39c628..51d032e479 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -655,6 +655,10 @@ pub trait PosBase { key: &Self::Address, ) -> Option; + /// Read PoS map from eth address derived from cold or hot keys to native + /// addresses + fn read_eth_key_addresses(&self) -> EthKeyAddresses; + /// Write PoS parameters. fn write_pos_params(&mut self, params: &PosParams); /// Write PoS validator's raw hash its address. diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index 3d613926ce..0c3599cd2a 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -536,6 +536,11 @@ where value.map(|value| decode(value).unwrap()) } + fn read_eth_key_addresses(&self) -> types::EthKeyAddresses { + let (value, _gas) = self.read(ð_key_addresses_key()).unwrap(); + decode(value.unwrap()).unwrap() + } + fn write_pos_params(&mut self, params: &PosParams) { self.write(¶ms_key(), encode(params)).unwrap(); } From ca8aef4da633ea894e13d7211c3b0f20cbcf7aa5 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 13:28:07 +0100 Subject: [PATCH 31/45] WIP: Verify voting powers in valset upd vext --- .../lib/node/ledger/shell/vote_extensions.rs | 10 ++++ .../vote_extensions/validator_set_update.rs | 51 +++++++++++++++++-- shared/src/types/ethereum_events.rs | 7 +++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/vote_extensions.rs b/apps/src/lib/node/ledger/shell/vote_extensions.rs index 73242f9e32..566d520da1 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions.rs @@ -47,6 +47,16 @@ mod extend_votes { PubKeyNotInStorage, #[error("The vote extension's signature is invalid.")] VerifySigFailed, + #[error( + "Validator is missing from an expected field in the vote \ + extension." + )] + ValidatorMissingFromExtension, + #[error( + "Found value for a field in the vote extension diverging from the \ + equivalent field in storage." + )] + DivergesFromStorage, } impl Shell diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index f56ef4a341..b22897703c 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; +use namada::ledger::pos::namada_proof_of_stake::PosBase; use namada::ledger::pos::types::VotingPower; use namada::ledger::storage::{DBIter, StorageHasher, DB}; use namada::types::storage::BlockHeight; @@ -69,11 +70,51 @@ where tracing::error!("Dropping vote extension issued at genesis"); return Err(VoteExtensionError::IssuedAtGenesis); } - // get the public key associated with this validator - let validator = &ext.data.validator_addr; + // verify if the voting powers in storage match the voting powers in the + // vote extensions + let eth_to_namada_map = self.storage.read_eth_key_addresses(); let last_height_epoch = self.storage.get_epoch(last_height).expect( "The epoch of the last block height should always be known", ); + let next_epoch = last_height_epoch.next(); + for (eth_addr, namada_addr) in eth_to_namada_map.iter() { + let &ext_power = match ext.data.voting_powers.get(eth_addr) { + Some(voting_power) => voting_power, + _ => { + tracing::error!( + eth_addr, + "Could not find expected Ethereum address in valset \ + upd vote extension", + ); + return Err( + VoteExtensionError::ValidatorMissingFromExtension, + ); + } + }; + let (namada_power, _) = self + .storage + .get_validator_from_address(namada_addr, Some(next_epoch)) + .map_err(|err| { + tracing::error!( + ?err, + validator = %namada_addr, + "Could not get voting power from Storage for some validator, \ + while validating valset upd vote extension" + ); + VoteExtensionError::PubKeyNotInStorage + })?; + if namada_power != ext_power { + tracing::error!( + validator = %namada_addr, + expected = ?namada_power, + got = ?ext_power, + "Found unexpected voting power value in valset upd vote extension", + ); + return Err(VoteExtensionError::DivergesFromStorage); + } + } + // get the public key associated with this validator + let validator = &ext.data.validator_addr; let (voting_power, pk) = self .storage .get_validator_from_address(validator, Some(last_height_epoch)) @@ -81,7 +122,8 @@ where tracing::error!( ?err, %validator, - "Could not get public key from Storage for some validator, while validating validator set update vote extension" + "Could not get public key from Storage for some validator, \ + while validating valset upd vote extension" ); VoteExtensionError::PubKeyNotInStorage })?; @@ -91,7 +133,8 @@ where tracing::error!( ?err, %validator, - "Failed to verify the signature of a validator set update vote extension issued by some validator" + "Failed to verify the signature of a valset upd vote \ + extension issued by some validator" ); VoteExtensionError::VerifySigFailed }) diff --git a/shared/src/types/ethereum_events.rs b/shared/src/types/ethereum_events.rs index 5e4d286f88..77e1713b42 100644 --- a/shared/src/types/ethereum_events.rs +++ b/shared/src/types/ethereum_events.rs @@ -1,5 +1,6 @@ //! Types representing data intended for Anoma via Ethereum events +use std::fmt::Display; use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; @@ -68,6 +69,12 @@ impl EthAddress { } } +impl Display for EthAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_canonical()) + } +} + impl FromStr for EthAddress { type Err = eyre::Error; From ab24237ca7bd4d0aff321b63cd655c5d5d095000 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 14:16:50 +0100 Subject: [PATCH 32/45] Verify voting powers in valset upd vext --- apps/src/lib/node/ledger/shell/queries.rs | 38 +++++++++++++++++++ .../lib/node/ledger/shell/vote_extensions.rs | 28 ++------------ .../vote_extensions/validator_set_update.rs | 24 +++--------- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index 80442a6588..75938e6ee1 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -14,6 +14,7 @@ use namada::types::key; use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::storage::{Epoch, Key, PrefixValue}; use namada::types::token::{self, Amount}; +use namada::types::vote_extensions::validator_set_update::EthAddrBook; use tendermint_proto::crypto::{ProofOp, ProofOps}; use tendermint_proto::google::protobuf; use tendermint_proto::types::EvidenceParams; @@ -347,6 +348,13 @@ pub(crate) trait QueriesExt { &self, validator: &Address, ) -> Option; + + /// Extension of [`Self::get_active_validators`], which additionally returns + /// all Ethereum addresses of some validator. + fn get_active_eth_addresses<'db>( + &'db self, + epoch: Option, + ) -> Box + 'db>; } impl QueriesExt for Storage @@ -576,6 +584,36 @@ where .as_ref() .and_then(|pk| pk.try_into().ok()) } + + #[inline] + fn get_active_eth_addresses<'db>( + &'db self, + epoch: Option, + ) -> Box + 'db> + { + let epoch = epoch.unwrap_or_else(|| self.get_current_epoch().0); + Box::new(self.get_active_validators(Some(epoch)).into_iter().map( + |validator| { + let hot_key_addr = self + .get_ethbridge_from_namada_addr(&validator.address) + .expect( + "All Namada validators should have an Ethereum bridge \ + key", + ); + let cold_key_addr = self + .get_ethgov_from_namada_addr(&validator.address) + .expect( + "All Namada validators should have an Ethereum \ + governance key", + ); + let eth_addr_book = EthAddrBook { + hot_key_addr, + cold_key_addr, + }; + (eth_addr_book, validator.address, validator.voting_power) + }, + )) + } } /// This enum is used as a parameter to diff --git a/apps/src/lib/node/ledger/shell/vote_extensions.rs b/apps/src/lib/node/ledger/shell/vote_extensions.rs index 566d520da1..6007f0db81 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions.rs @@ -11,7 +11,6 @@ mod extend_votes { use borsh::BorshDeserialize; use namada::proto::Signed; use namada::types::transaction::protocol::ProtocolTxType; - use namada::types::vote_extensions::validator_set_update::EthAddrBook; use namada::types::vote_extensions::{ ethereum_events, validator_set_update, VoteExtension, VoteExtensionDigest, @@ -122,30 +121,9 @@ mod extend_votes { let next_epoch = self.storage.get_current_epoch().0.next(); let voting_powers = self .storage - .get_active_validators(Some(next_epoch)) - .into_iter() - .map(|validator| { - let hot_key_addr = self - .storage - .get_ethbridge_from_namada_addr( - &validator.address, - ) - .expect( - "All Namada validators should have an \ - Ethereum bridge key", - ); - let cold_key_addr = self - .storage - .get_ethgov_from_namada_addr(&validator.address) - .expect( - "All Namada validators should have an \ - Ethereum governance key", - ); - let eth_addr_book = EthAddrBook { - hot_key_addr, - cold_key_addr, - }; - (eth_addr_book, validator.voting_power) + .get_active_eth_addresses(Some(next_epoch)) + .map(|(eth_addr_book, _, voting_power)| { + (eth_addr_book, voting_power) }) .collect(); diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index b22897703c..32344c5c75 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; -use namada::ledger::pos::namada_proof_of_stake::PosBase; use namada::ledger::pos::types::VotingPower; use namada::ledger::storage::{DBIter, StorageHasher, DB}; use namada::types::storage::BlockHeight; @@ -72,18 +71,19 @@ where } // verify if the voting powers in storage match the voting powers in the // vote extensions - let eth_to_namada_map = self.storage.read_eth_key_addresses(); let last_height_epoch = self.storage.get_epoch(last_height).expect( "The epoch of the last block height should always be known", ); let next_epoch = last_height_epoch.next(); - for (eth_addr, namada_addr) in eth_to_namada_map.iter() { - let &ext_power = match ext.data.voting_powers.get(eth_addr) { + for (eth_addr_book, namada_addr, namada_power) in + self.storage.get_active_eth_addresses(Some(next_epoch)) + { + let &ext_power = match ext.data.voting_powers.get(ð_addr_book) { Some(voting_power) => voting_power, _ => { tracing::error!( - eth_addr, - "Could not find expected Ethereum address in valset \ + ?eth_addr_book, + "Could not find expected Ethereum addresses in valset \ upd vote extension", ); return Err( @@ -91,18 +91,6 @@ where ); } }; - let (namada_power, _) = self - .storage - .get_validator_from_address(namada_addr, Some(next_epoch)) - .map_err(|err| { - tracing::error!( - ?err, - validator = %namada_addr, - "Could not get voting power from Storage for some validator, \ - while validating valset upd vote extension" - ); - VoteExtensionError::PubKeyNotInStorage - })?; if namada_power != ext_power { tracing::error!( validator = %namada_addr, From f22f19db1dd2ef00d6413591cf7e36561de807a2 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 14:54:40 +0100 Subject: [PATCH 33/45] Verify valset upd vext with secp key --- .../shell/vote_extensions/validator_set_update.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index 32344c5c75..f82992ec13 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; +use namada::ledger::pos::namada_proof_of_stake::PosBase; use namada::ledger::pos::types::VotingPower; use namada::ledger::storage::{DBIter, StorageHasher, DB}; use namada::types::storage::BlockHeight; @@ -42,12 +43,6 @@ where /// This method behaves exactly like [`Self::validate_valset_upd_vext`], /// with the added bonus of returning the vote extension back, if it /// is valid. - // TODO: - // - verify if the voting powers in the vote extension are the same - // as the ones in storage. we can't do this yet, because we need to map - // ethereum addresses to namada validator addresses - // - // - verify signatures with a secp key, instead of an ed25519 key pub fn validate_valset_upd_vext_and_get_it_back( &self, ext: validator_set_update::SignedVext, @@ -103,7 +98,7 @@ where } // get the public key associated with this validator let validator = &ext.data.validator_addr; - let (voting_power, pk) = self + let (voting_power, _) = self .storage .get_validator_from_address(validator, Some(last_height_epoch)) .map_err(|err| { @@ -115,6 +110,10 @@ where ); VoteExtensionError::PubKeyNotInStorage })?; + let pk = self + .storage + .read_validator_eth_hot_key(validator) + .expect("We should have this hot key in storage"); // verify the signature of the vote extension ext.verify(&pk) .map_err(|err| { From 5e5d88f1227f8ca14595c494bb09c09a4dbf2a80 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 15:34:50 +0100 Subject: [PATCH 34/45] Remove unused eth key addresses pos methods --- proof_of_stake/src/lib.rs | 95 ++------------------------------ proof_of_stake/src/types.rs | 2 - shared/src/ledger/pos/storage.rs | 29 ---------- shared/src/ledger/pos/vp.rs | 13 +---- vm_env/src/proof_of_stake.rs | 11 ---- 5 files changed, 7 insertions(+), 143 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 51d032e479..cb45560e93 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -33,11 +33,11 @@ use epoched::{ use parameters::PosParams; use thiserror::Error; use types::{ - ActiveValidator, Bonds, Epoch, EthAddress, EthKeyAddresses, - GenesisValidator, Slash, SlashType, Slashes, TotalVotingPowers, TryRefTo, - Unbond, Unbonds, ValidatorConsensusKeys, ValidatorEthKey, ValidatorSet, - ValidatorSetUpdate, ValidatorSets, ValidatorState, ValidatorStates, - ValidatorTotalDeltas, ValidatorVotingPowers, VotingPower, VotingPowerDelta, + ActiveValidator, Bonds, Epoch, EthAddress, GenesisValidator, Slash, + SlashType, Slashes, TotalVotingPowers, TryRefTo, Unbond, Unbonds, + ValidatorConsensusKeys, ValidatorEthKey, ValidatorSet, ValidatorSetUpdate, + ValidatorSets, ValidatorState, ValidatorStates, ValidatorTotalDeltas, + ValidatorVotingPowers, VotingPower, VotingPowerDelta, }; use crate::btree_set::BTreeSetShims; @@ -159,20 +159,6 @@ pub trait PosReadOnly { &self, key: &Self::Address, ) -> Option; - - /// Read PoS map from eth address derived from cold or hot keys to native - /// addresses - fn read_eth_key_addresses(&self) -> EthKeyAddresses; - - /// Try to find a native address associated with the given Ethereum address - /// derived from an Ethereum cold or hot key - fn find_address_from_eth_key_address( - &self, - eth_addr: &EthAddress, - ) -> Option { - let addresses = self.read_eth_key_addresses(); - addresses.get(eth_addr).cloned() - } } /// PoS system trait to be implemented in integration that can read and write @@ -245,10 +231,6 @@ pub trait PosActions: PosReadOnly { value: ValidatorEthKey, ); - /// Write PoS map from eth address derived from cold or hot keys to native - /// addresses - fn write_eth_key_addresses(&self, value: EthKeyAddresses); - /// Delete an emptied PoS bond (validator self-bond or a delegation). fn delete_bond(&mut self, key: &BondId); /// Delete an emptied PoS unbond (unbonded tokens from validator self-bond @@ -296,9 +278,6 @@ pub trait PosActions: PosReadOnly { consensus_key, eth_cold_key, eth_hot_key, - eth_cold_key_addr, - eth_hot_key_addr, - state, total_deltas, voting_power, @@ -311,19 +290,6 @@ pub trait PosActions: PosReadOnly { &mut validator_set, current_epoch, )?; - let mut eth_addresses_map = self.read_eth_key_addresses(); - if eth_addresses_map - .insert(eth_cold_key_addr, address.clone()) - .is_some() - { - return Err(BecomeValidatorError::DupedEthKeyFound); - } - if eth_addresses_map - .insert(eth_hot_key_addr, address.clone()) - .is_some() - { - return Err(BecomeValidatorError::DupedEthKeyFound); - } self.write_validator_staking_reward_address( address, staking_reward_address.clone(), @@ -331,7 +297,6 @@ pub trait PosActions: PosReadOnly { self.write_validator_consensus_key(address, consensus_key); self.write_validator_eth_cold_key(address, eth_cold_key); self.write_validator_eth_hot_key(address, eth_hot_key); - self.write_eth_key_addresses(eth_addresses_map); self.write_validator_state(address, state); self.write_validator_set(validator_set); self.write_validator_address_raw_hash(address); @@ -655,10 +620,6 @@ pub trait PosBase { key: &Self::Address, ) -> Option; - /// Read PoS map from eth address derived from cold or hot keys to native - /// addresses - fn read_eth_key_addresses(&self) -> EthKeyAddresses; - /// Write PoS parameters. fn write_pos_params(&mut self, params: &PosParams); /// Write PoS validator's raw hash its address. @@ -723,12 +684,6 @@ pub trait PosBase { address: &Self::Address, value: &ValidatorEthKey, ); - /// Write PoS map from eth address derived from cold or hot keys to native - /// addresses - fn write_eth_key_addresses( - &mut self, - value: &EthKeyAddresses, - ); /// Initialize staking reward account with the given public key. fn init_staking_reward_account( &mut self, @@ -782,8 +737,6 @@ pub trait PosBase { total_bonded_balance, } = init_genesis(params, validators, current_epoch)?; - let mut eth_addresses_map = HashMap::default(); - for res in validators { let GenesisValidatorData { ref address, @@ -796,8 +749,6 @@ pub trait PosBase { bond: (bond_id, bond), eth_cold_key, eth_hot_key, - eth_cold_key_addr, - eth_hot_key_addr, } = res?; self.write_validator_address_raw_hash(address); self.write_validator_staking_reward_address( @@ -815,20 +766,7 @@ pub trait PosBase { &staking_reward_address, &staking_reward_key, ); - if eth_addresses_map - .insert(eth_cold_key_addr, address.clone()) - .is_some() - { - return Err(GenesisError::DupedEthKeyFound); - } - if eth_addresses_map - .insert(eth_hot_key_addr, address.clone()) - .is_some() - { - return Err(GenesisError::DupedEthKeyFound); - } } - self.write_eth_key_addresses(ð_addresses_map); self.write_validator_set(&validator_set); self.write_total_voting_power(&total_voting_power); // Credit the bonded tokens to the PoS account @@ -1024,8 +962,6 @@ pub enum GenesisError { VotingPowerOverflow(TryFromIntError), #[error("Ethereum address can only be of secp kind")] SecpKeyConversion, - #[error("Duplicate Ethereum key found")] - DupedEthKeyFound, } #[allow(missing_docs)] @@ -1040,8 +976,6 @@ pub enum BecomeValidatorError { StakingRewardAddressEqValidatorAddress(Address), #[error("Ethereum address can only be of secp kind")] SecpKeyConversion, - #[error("Duplicate Ethereum key found")] - DupedEthKeyFound, } #[allow(missing_docs)] @@ -1195,8 +1129,6 @@ where bond: (BondId
, Bonds), eth_cold_key: ValidatorEthKey, eth_hot_key: ValidatorEthKey, - eth_cold_key_addr: EthAddress, - eth_hot_key_addr: EthAddress, } /// A function that returns genesis data created from the initial validator set. @@ -1305,11 +1237,6 @@ where eth_cold_key, eth_hot_key, }| { - let convert_key_to_addr = |k: &'a PK| { - k.try_ref_to().map_err(|_| GenesisError::SecpKeyConversion) - }; - let eth_cold_key_addr = convert_key_to_addr(eth_cold_key)?; - let eth_hot_key_addr = convert_key_to_addr(eth_hot_key)?; let consensus_key = Epoched::init_at_genesis(consensus_key.clone(), current_epoch); let eth_cold_key = @@ -1347,8 +1274,6 @@ where bond: (bond_id, bond), eth_cold_key, eth_hot_key, - eth_cold_key_addr, - eth_hot_key_addr, }) }, ); @@ -1461,8 +1386,6 @@ where consensus_key: ValidatorConsensusKeys, eth_cold_key: ValidatorEthKey, eth_hot_key: ValidatorEthKey, - eth_cold_key_addr: EthAddress, - eth_hot_key_addr: EthAddress, state: ValidatorStates, total_deltas: ValidatorTotalDeltas, voting_power: ValidatorVotingPowers, @@ -1502,12 +1425,6 @@ where + BorshSerialize + BorshSchema, { - let convert_key_to_addr = |k: &'a PK| { - k.try_ref_to() - .map_err(|_| BecomeValidatorError::SecpKeyConversion) - }; - let eth_cold_key_addr = convert_key_to_addr(eth_cold_key)?; - let eth_hot_key_addr = convert_key_to_addr(eth_hot_key)?; let consensus_key = Epoched::init(consensus_key.clone(), current_epoch, params); let eth_cold_key = @@ -1556,8 +1473,6 @@ where voting_power, eth_cold_key, eth_hot_key, - eth_cold_key_addr, - eth_hot_key_addr, }) } diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index 5e16c69bcf..1d9a08e05a 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -40,8 +40,6 @@ pub type TotalVotingPowers = EpochedDelta; /// Epoched validator's eth key. pub type ValidatorEthKey = Epoched; -/// Map from eth addresses back to native addresses. -pub type EthKeyAddresses
= HashMap; /// Eth address derived from secp256k1 key #[derive( diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index 0c3599cd2a..1443c27811 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -24,7 +24,6 @@ const VALIDATOR_STAKING_REWARD_ADDRESS_STORAGE_KEY: &str = const VALIDATOR_CONSENSUS_KEY_STORAGE_KEY: &str = "consensus_key"; const VALIDATOR_ETH_COLD_KEY_STORAGE_KEY: &str = "eth_cold_key"; const VALIDATOR_ETH_HOT_KEY_STORAGE_KEY: &str = "eth_hot_key"; -const ETH_KEY_ADDRESSES_STORAGE_KEY: &str = "eth_key_addresses"; const VALIDATOR_STATE_STORAGE_KEY: &str = "state"; const VALIDATOR_TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_VOTING_POWER_STORAGE_KEY: &str = "voting_power"; @@ -419,22 +418,6 @@ pub fn get_validator_address_from_bond(key: &Key) -> Option
{ } } -/// Storage key for look-up from validator's eth key addresses to native -/// address. -pub fn eth_key_addresses_key() -> Key { - Key::from(ADDRESS.to_db_key()) - .push(Ð_KEY_ADDRESSES_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Is storage key for validators' eth key addresses? -pub fn is_eth_key_addresses_key(key: &Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(suffix)] - if addr == &ADDRESS - && suffix == ETH_KEY_ADDRESSES_STORAGE_KEY) -} - impl PosBase for Storage where D: storage::DB + for<'iter> storage::DBIter<'iter>, @@ -536,11 +519,6 @@ where value.map(|value| decode(value).unwrap()) } - fn read_eth_key_addresses(&self) -> types::EthKeyAddresses { - let (value, _gas) = self.read(ð_key_addresses_key()).unwrap(); - decode(value.unwrap()).unwrap() - } - fn write_pos_params(&mut self, params: &PosParams) { self.write(¶ms_key(), encode(params)).unwrap(); } @@ -638,13 +616,6 @@ where .unwrap(); } - fn write_eth_key_addresses( - &mut self, - value: &types::EthKeyAddresses, - ) { - self.write(ð_key_addresses_key(), encode(value)).unwrap(); - } - fn init_staking_reward_account( &mut self, address: &Self::Address, diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 66a348fbf9..7d2b7680f1 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -16,8 +16,8 @@ use namada_proof_of_stake::{validation, PosReadOnly}; use thiserror::Error; use super::{ - bond_key, eth_key_addresses_key, is_bond_key, is_params_key, - is_total_voting_power_key, is_unbond_key, is_validator_set_key, + bond_key, is_bond_key, is_params_key, is_total_voting_power_key, + is_unbond_key, is_validator_set_key, is_validator_staking_reward_address_key, is_validator_total_deltas_key, is_validator_voting_power_key, params_key, staking_token_address, total_voting_power_key, unbond_key, validator_consensus_key_key, @@ -433,15 +433,6 @@ where let value = self.ctx.read_pre(&validator_eth_hot_key_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } - - fn read_eth_key_addresses(&self) -> types::EthKeyAddresses { - let value = self - .ctx - .read_pre(ð_key_addresses_key()) - .unwrap() - .unwrap(); - decode(value).unwrap() - } } impl From for Error { diff --git a/vm_env/src/proof_of_stake.rs b/vm_env/src/proof_of_stake.rs index 835d7d6ab1..53e2ab9470 100644 --- a/vm_env/src/proof_of_stake.rs +++ b/vm_env/src/proof_of_stake.rs @@ -187,10 +187,6 @@ impl namada_proof_of_stake::PosReadOnly for PoS { ) -> Option { tx::read(validator_eth_hot_key_key(key).to_string()) } - - fn read_eth_key_addresses(&self) -> types::EthKeyAddresses { - tx::read(eth_key_addresses_key().to_string()).unwrap() - } } impl namada_proof_of_stake::PosActions for PoS { @@ -298,11 +294,4 @@ impl namada_proof_of_stake::PosActions for PoS { ) { tx::write(validator_eth_hot_key_key(address).to_string(), &value) } - - fn write_eth_key_addresses( - &self, - value: types::EthKeyAddresses, - ) { - tx::write(eth_key_addresses_key().to_string(), &value) - } } From 930891721f713b7c0cf7a6319777a143b7e41bc9 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 15:44:52 +0100 Subject: [PATCH 35/45] Ignore test_secp_key_belongs_to_active_validator() unit test --- .../node/ledger/shell/vote_extensions/validator_set_update.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index f82992ec13..d45e5da841 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -464,7 +464,9 @@ mod test_vote_extensions { /// Test if a [`validator_set_update::Vext`] is signed with a secp key /// that belongs to an active validator of some previous epoch #[test] + #[ignore] fn test_secp_key_belongs_to_active_validator() { - // TODO + // TODO: we need to prove ownership of validator keys + // https://github.com/anoma/namada/issues/106 } } From e7c09940931a12923b9e21056513128c0388af05 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 15:54:10 +0100 Subject: [PATCH 36/45] Add get_eth_bridge_keypair() --- apps/src/lib/node/ledger/shell/mod.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index fde27d0288..9c9e65f279 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -244,9 +244,9 @@ impl ShellMode { } } - /// Get the protocol keypair for this validator + /// Get the protocol keypair for this validator. pub fn get_protocol_key(&self) -> Option<&common::SecretKey> { - match &self { + match self { ShellMode::Validator { data: ValidatorData { @@ -261,6 +261,24 @@ impl ShellMode { _ => None, } } + + /// Get the Ethereum bridge keypair for this validator. + pub fn get_eth_bridge_keypair(&self) -> Option<&common::SecretKey> { + match self { + ShellMode::Validator { + data: + ValidatorData { + keys: + ValidatorKeys { + eth_bridge_keypair, .. + }, + .. + }, + .. + } => Some(eth_bridge_keypair), + _ => None, + } + } } #[derive(Clone, Debug)] From cb0e980dd6b62f631a5e65904be1db2850848591 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 16:03:37 +0100 Subject: [PATCH 37/45] WIP: Fix unit tests --- apps/src/lib/node/ledger/shell/mod.rs | 18 +++-- .../vote_extensions/validator_set_update.rs | 69 ++++++++++++++----- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 9c9e65f279..a604ee9491 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -793,14 +793,20 @@ mod test_utils { pub(super) fn invalidate_signature( sig: common::Signature, ) -> common::Signature { - let mut sig_bytes = match sig { + match sig { common::Signature::Ed25519(ed25519::Signature(ref sig)) => { - sig.to_bytes() + let mut sig_bytes = sig.to_bytes(); + sig_bytes[0] = sig_bytes[0].wrapping_add(1); + common::Signature::Ed25519(ed25519::Signature(sig_bytes.into())) } - _ => unreachable!(), - }; - sig_bytes[0] = sig_bytes[0].wrapping_add(1); - common::Signature::Ed25519(ed25519::Signature(sig_bytes.into())) + common::Signature::Secp256k1(secp256k1::Signature(ref sig)) => { + let mut sig_bytes = sig.to_bytes(); + sig_bytes[0] = sig_bytes[0].wrapping_add(1); + common::Signature::Secp256k1(secp256k1::Signature( + sig_bytes.into(), + )) + } + } } /// A wrapper around the shell that implements diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index d45e5da841..ba69465939 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -261,9 +261,6 @@ mod test_vote_extensions { /// Test if a [`validator_set_update::Vext`] that incorrectly labels what /// block height it was included on in a vote extension is rejected - // TODO: - // - sign with secp key - // - add validator voting powers from storage #[test] fn test_reject_incorrect_block_height() { let (shell, _, _) = test_utils::setup(); @@ -271,6 +268,8 @@ mod test_vote_extensions { shell.mode.get_validator_address().unwrap().clone(); let protocol_key = shell.mode.get_protocol_key().expect("Test failed"); + let eth_bridge_key = + shell.mode.get_eth_bridge_keypair().expect("Test failed"); let ethereum_events = ethereum_events::Vext::empty( shell.storage.get_current_decision_height(), @@ -278,17 +277,24 @@ mod test_vote_extensions { ) .sign(protocol_key); + let voting_powers = { + let next_epoch = shell.storage.get_current_epoch().0.next(); + shell + .storage + .get_active_eth_addresses(Some(next_epoch)) + .map(|(eth_addr_book, _, voting_power)| { + (eth_addr_book, voting_power) + }) + .collect() + }; let validator_set_update = Some( validator_set_update::Vext { - // TODO: get voting powers from storage, associated with eth - // addrs - voting_powers: std::collections::HashMap::new(), + voting_powers, validator_addr, // invalid height block_height: shell.storage.get_current_decision_height() + 1, } - // TODO: sign with secp key - .sign(protocol_key), + .sign(eth_bridge_key), ); let req = request::VerifyVoteExtension { @@ -321,11 +327,19 @@ mod test_vote_extensions { validator_addr.clone(), ) .sign(&protocol_key); + let voting_powers = { + let next_epoch = shell.storage.get_current_epoch().0.next(); + shell + .storage + .get_active_eth_addresses(Some(next_epoch)) + .map(|(eth_addr_book, _, voting_power)| { + (eth_addr_book, voting_power) + }) + .collect() + }; let validator_set_update = Some( validator_set_update::Vext { - // TODO: get voting powers from storage, associated with eth - // addrs - voting_powers: std::collections::HashMap::new(), + voting_powers, block_height: shell.storage.get_current_decision_height(), validator_addr, } @@ -361,10 +375,18 @@ mod test_vote_extensions { .expect("Test failed") .clone(); let signed_height = shell.storage.get_current_decision_height(); + let voting_powers = { + let next_epoch = shell.storage.get_current_epoch().0.next(); + shell + .storage + .get_active_eth_addresses(Some(next_epoch)) + .map(|(eth_addr_book, _, voting_power)| { + (eth_addr_book, voting_power) + }) + .collect() + }; let vote_ext = validator_set_update::Vext { - // TODO: get voting powers from storage, associated with eth - // addrs - voting_powers: std::collections::HashMap::new(), + voting_powers, block_height: signed_height, validator_addr, } @@ -425,6 +447,8 @@ mod test_vote_extensions { shell.mode.get_validator_address().unwrap().clone(); let protocol_key = shell.mode.get_protocol_key().expect("Test failed"); + let eth_bridge_key = + shell.mode.get_eth_bridge_keypair().expect("Test failed"); let ethereum_events = ethereum_events::Vext::empty( shell.storage.get_current_decision_height(), @@ -433,15 +457,22 @@ mod test_vote_extensions { .sign(protocol_key); let validator_set_update = { + let voting_powers = { + let next_epoch = shell.storage.get_current_epoch().0.next(); + shell + .storage + .get_active_eth_addresses(Some(next_epoch)) + .map(|(eth_addr_book, _, voting_power)| { + (eth_addr_book, voting_power) + }) + .collect() + }; let mut ext = validator_set_update::Vext { - // TODO: get voting powers from storage, associated with eth - // addrs - voting_powers: std::collections::HashMap::new(), + voting_powers, block_height: shell.storage.get_current_decision_height(), validator_addr, } - // TODO: sign with secp key - .sign(protocol_key); + .sign(eth_bridge_key); ext.sig = test_utils::invalidate_signature(ext.sig); Some(ext) }; From 0d5c02907f833963d7d33bcfa8b19ca2a8e9596f Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 16:38:18 +0100 Subject: [PATCH 38/45] Add method to recover secp sig from a byte array --- shared/src/types/key/secp256k1.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/shared/src/types/key/secp256k1.rs b/shared/src/types/key/secp256k1.rs index 9ff662aa36..f9eebcd228 100644 --- a/shared/src/types/key/secp256k1.rs +++ b/shared/src/types/key/secp256k1.rs @@ -405,6 +405,21 @@ impl PartialOrd for Signature { } } +impl TryFrom<&[u8; 64]> for Signature { + type Error = ParseSignatureError; + + fn try_from(sig: &[u8; 64]) -> Result { + libsecp256k1::Signature::parse_standard(sig) + .map(Self) + .map_err(|err| { + ParseSignatureError::InvalidEncoding(std::io::Error::new( + ErrorKind::Other, + err, + )) + }) + } +} + /// An implementation of the Secp256k1 signature scheme #[derive( Debug, From 66c89db9f5a65e79de06c0abd74706c727c5050e Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 16:38:58 +0100 Subject: [PATCH 39/45] Add new test utils --- apps/src/lib/node/ledger/shell/mod.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index a604ee9491..dccd77ac55 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -781,7 +781,13 @@ mod test_utils { } /// Generate a random public/private keypair + #[inline] pub(super) fn gen_keypair() -> common::SecretKey { + gen_ed25519_keypair() + } + + /// Generate a random ed25519 public/private keypair + pub(super) fn gen_ed25519_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; use rand::thread_rng; @@ -789,6 +795,17 @@ mod test_utils { ed25519::SigScheme::generate(&mut rng).try_to_sk().unwrap() } + /// Generate a random secp256k1 public/private keypair + pub(super) fn gen_secp256k1_keypair() -> common::SecretKey { + use rand::prelude::ThreadRng; + use rand::thread_rng; + + let mut rng: ThreadRng = thread_rng(); + secp256k1::SigScheme::generate(&mut rng) + .try_to_sk() + .unwrap() + } + /// Invalidate a valid signature `sig`. pub(super) fn invalidate_signature( sig: common::Signature, @@ -800,11 +817,9 @@ mod test_utils { common::Signature::Ed25519(ed25519::Signature(sig_bytes.into())) } common::Signature::Secp256k1(secp256k1::Signature(ref sig)) => { - let mut sig_bytes = sig.to_bytes(); + let mut sig_bytes = sig.serialize(); sig_bytes[0] = sig_bytes[0].wrapping_add(1); - common::Signature::Secp256k1(secp256k1::Signature( - sig_bytes.into(), - )) + common::Signature::Secp256k1((&sig_bytes).try_into().unwrap()) } } } From e0f88e0d2eac84d98ea34080e779d0041ea81425 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 15 Sep 2022 16:39:13 +0100 Subject: [PATCH 40/45] Fix clippy checks --- .../shell/vote_extensions/validator_set_update.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index ba69465939..e479b52f3f 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -317,10 +317,10 @@ mod test_vote_extensions { #[test] fn test_valset_upd_must_be_signed_by_validator() { let (shell, _, _) = test_utils::setup(); - let (protocol_key, validator_addr) = { + let (eth_bridge_key, protocol_key, validator_addr) = { let bertha_key = wallet::defaults::bertha_keypair(); let bertha_addr = wallet::defaults::bertha_address(); - (bertha_key, bertha_addr) + (test_utils::gen_secp256k1_keypair(), bertha_key, bertha_addr) }; let ethereum_events = ethereum_events::Vext::empty( shell.storage.get_current_decision_height(), @@ -343,7 +343,7 @@ mod test_vote_extensions { block_height: shell.storage.get_current_decision_height(), validator_addr, } - .sign(&protocol_key), + .sign(ð_bridge_key), ); let req = request::VerifyVoteExtension { vote_extension: VoteExtension { @@ -369,6 +369,11 @@ mod test_vote_extensions { let (mut shell, _, _) = test_utils::setup(); let protocol_key = shell.mode.get_protocol_key().expect("Test failed").clone(); + let eth_bridge_key = shell + .mode + .get_eth_bridge_keypair() + .expect("Test failed") + .clone(); let validator_addr = shell .mode .get_validator_address() @@ -390,7 +395,7 @@ mod test_vote_extensions { block_height: signed_height, validator_addr, } - .sign(&protocol_key); + .sign(ð_bridge_key); // validators from the current epoch sign over validator // set of the next epoch From 86a4d5fb1728a09ca48ec0dce1960356e713c2c6 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 16 Sep 2022 09:18:34 +0100 Subject: [PATCH 41/45] Fixed signature of PoS eth key read methods --- proof_of_stake/src/lib.rs | 8 ++++---- shared/src/ledger/pos/storage.rs | 4 ++-- shared/src/ledger/pos/vp.rs | 4 ++-- vm_env/src/proof_of_stake.rs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index cb45560e93..a7bc5b9e9f 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -152,13 +152,13 @@ pub trait PosReadOnly { fn read_validator_eth_cold_key( &self, key: &Self::Address, - ) -> Option; + ) -> Option>; /// Read PoS validator's Eth validator set update signing key fn read_validator_eth_hot_key( &self, key: &Self::Address, - ) -> Option; + ) -> Option>; } /// PoS system trait to be implemented in integration that can read and write @@ -612,13 +612,13 @@ pub trait PosBase { fn read_validator_eth_cold_key( &self, key: &Self::Address, - ) -> Option; + ) -> Option>; /// Read PoS validator's Eth validator set update signing key fn read_validator_eth_hot_key( &self, key: &Self::Address, - ) -> Option; + ) -> Option>; /// Write PoS parameters. fn write_pos_params(&mut self, params: &PosParams); diff --git a/shared/src/ledger/pos/storage.rs b/shared/src/ledger/pos/storage.rs index 1443c27811..496ebdfbbd 100644 --- a/shared/src/ledger/pos/storage.rs +++ b/shared/src/ledger/pos/storage.rs @@ -505,7 +505,7 @@ where fn read_validator_eth_cold_key( &self, key: &Self::Address, - ) -> Option { + ) -> Option> { let (value, _gas) = self.read(&validator_eth_cold_key_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) @@ -514,7 +514,7 @@ where fn read_validator_eth_hot_key( &self, key: &Self::Address, - ) -> Option { + ) -> Option> { let (value, _gas) = self.read(&validator_eth_hot_key_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 7d2b7680f1..0b581ad260 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -420,7 +420,7 @@ where fn read_validator_eth_cold_key( &self, key: &Self::Address, - ) -> Option { + ) -> Option> { let value = self.ctx.read_pre(&validator_eth_cold_key_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) @@ -429,7 +429,7 @@ where fn read_validator_eth_hot_key( &self, key: &Self::Address, - ) -> Option { + ) -> Option> { let value = self.ctx.read_pre(&validator_eth_hot_key_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } diff --git a/vm_env/src/proof_of_stake.rs b/vm_env/src/proof_of_stake.rs index 53e2ab9470..295cf6e692 100644 --- a/vm_env/src/proof_of_stake.rs +++ b/vm_env/src/proof_of_stake.rs @@ -177,14 +177,14 @@ impl namada_proof_of_stake::PosReadOnly for PoS { fn read_validator_eth_cold_key( &self, key: &Self::Address, - ) -> Option { + ) -> Option> { tx::read(validator_eth_cold_key_key(key).to_string()) } fn read_validator_eth_hot_key( &self, key: &Self::Address, - ) -> Option { + ) -> Option> { tx::read(validator_eth_hot_key_key(key).to_string()) } } From 7e08fc09b6ea6f7e15d3e2615be4043d22a6d399 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 16 Sep 2022 09:53:38 +0100 Subject: [PATCH 42/45] Add epoched Ethereum keys --- apps/src/lib/node/ledger/shell/queries.rs | 22 ++++++++++++++----- .../vote_extensions/validator_set_update.rs | 7 ++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index 75938e6ee1..70753faa64 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -340,6 +340,7 @@ pub(crate) trait QueriesExt { fn get_ethbridge_from_namada_addr( &self, validator: &Address, + epoch: Option, ) -> Option; /// For a given Namada validator, return its corresponding Ethereum @@ -347,6 +348,7 @@ pub(crate) trait QueriesExt { fn get_ethgov_from_namada_addr( &self, validator: &Address, + epoch: Option, ) -> Option; /// Extension of [`Self::get_active_validators`], which additionally returns @@ -569,20 +571,24 @@ where fn get_ethbridge_from_namada_addr( &self, validator: &Address, + epoch: Option, ) -> Option { + let epoch = epoch.unwrap_or_else(|| self.get_current_epoch().0); self.read_validator_eth_hot_key(validator) .as_ref() - .and_then(|pk| pk.try_into().ok()) + .and_then(|epk| epk.get(epoch).and_then(|pk| pk.try_into().ok())) } #[inline] fn get_ethgov_from_namada_addr( &self, validator: &Address, + epoch: Option, ) -> Option { + let epoch = epoch.unwrap_or_else(|| self.get_current_epoch().0); self.read_validator_eth_cold_key(validator) .as_ref() - .and_then(|pk| pk.try_into().ok()) + .and_then(|epk| epk.get(epoch).and_then(|pk| pk.try_into().ok())) } #[inline] @@ -593,15 +599,21 @@ where { let epoch = epoch.unwrap_or_else(|| self.get_current_epoch().0); Box::new(self.get_active_validators(Some(epoch)).into_iter().map( - |validator| { + move |validator| { let hot_key_addr = self - .get_ethbridge_from_namada_addr(&validator.address) + .get_ethbridge_from_namada_addr( + &validator.address, + Some(epoch), + ) .expect( "All Namada validators should have an Ethereum bridge \ key", ); let cold_key_addr = self - .get_ethgov_from_namada_addr(&validator.address) + .get_ethgov_from_namada_addr( + &validator.address, + Some(epoch), + ) .expect( "All Namada validators should have an Ethereum \ governance key", diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs index e479b52f3f..255bf9c7b7 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/validator_set_update.rs @@ -110,12 +110,15 @@ where ); VoteExtensionError::PubKeyNotInStorage })?; - let pk = self + let epoched_pk = self .storage .read_validator_eth_hot_key(validator) .expect("We should have this hot key in storage"); + let pk = epoched_pk + .get(last_height_epoch) + .expect("We should have the hot key of the given epoch"); // verify the signature of the vote extension - ext.verify(&pk) + ext.verify(pk) .map_err(|err| { tracing::error!( ?err, From daab76d638221e2089b8f6174d51de056da2b5da Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 16 Sep 2022 10:04:14 +0100 Subject: [PATCH 43/45] Fix typo in secp key code --- shared/src/types/key/secp256k1.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared/src/types/key/secp256k1.rs b/shared/src/types/key/secp256k1.rs index f9eebcd228..96b892fdbf 100644 --- a/shared/src/types/key/secp256k1.rs +++ b/shared/src/types/key/secp256k1.rs @@ -454,14 +454,14 @@ impl super::SigScheme for SigScheme { /// Sign the data with a key fn sign(keypair: &SecretKey, data: impl AsRef<[u8]>) -> Self::Signature { - #[cfg(not(any(test, features = "secp256k1-sign-verify")))] + #[cfg(not(any(test, feature = "secp256k1-sign-verify")))] { // to avoid `unused-variables` warn let _ = (keypair, data); panic!("\"secp256k1-sign-verify\" feature must be enabled"); } - #[cfg(any(test, features = "secp256k1-sign-verify"))] + #[cfg(any(test, feature = "secp256k1-sign-verify"))] { use sha2::{Digest, Sha256}; let hash = Sha256::digest(data.as_ref()); @@ -476,14 +476,14 @@ impl super::SigScheme for SigScheme { data: &T, sig: &Self::Signature, ) -> Result<(), VerifySigError> { - #[cfg(not(any(test, features = "secp256k1-sign-verify")))] + #[cfg(not(any(test, feature = "secp256k1-sign-verify")))] { // to avoid `unused-variables` warn let _ = (pk, data, sig); panic!("\"secp256k1-sign-verify\" feature must be enabled"); } - #[cfg(any(test, features = "secp256k1-sign-verify"))] + #[cfg(any(test, feature = "secp256k1-sign-verify"))] { use sha2::{Digest, Sha256}; let bytes = &data @@ -509,14 +509,14 @@ impl super::SigScheme for SigScheme { data: &[u8], sig: &Self::Signature, ) -> Result<(), VerifySigError> { - #[cfg(not(any(test, features = "secp256k1-sign-verify")))] + #[cfg(not(any(test, feature = "secp256k1-sign-verify")))] { // to avoid `unused-variables` warn let _ = (pk, data, sig); panic!("\"secp256k1-sign-verify\" feature must be enabled"); } - #[cfg(any(test, features = "secp256k1-sign-verify"))] + #[cfg(any(test, feature = "secp256k1-sign-verify"))] { use sha2::{Digest, Sha256}; let hash = Sha256::digest(data); From b2fc5c1ab3586348539a2a0d2360dc6efb03a0c0 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 21 Sep 2022 10:30:07 +0100 Subject: [PATCH 44/45] Load the right keys from ValidatorConfig --- apps/src/lib/config/genesis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 80ef4ee7b0..5441ce524f 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -323,13 +323,13 @@ pub mod genesis_config { .to_public_key() .unwrap(), eth_cold_key: config - .staking_reward_public_key + .eth_cold_key .as_ref() .unwrap() .to_public_key() .unwrap(), eth_hot_key: config - .staking_reward_public_key + .eth_hot_key .as_ref() .unwrap() .to_public_key() From b608c78bdf6f67846baf4b5287ba3b436bf71a67 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 13 Oct 2022 08:04:23 +0000 Subject: [PATCH 45/45] [ci] wasm checksums update --- wasm/checksums.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index de0f089b1f..c929fc60c2 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,19 +1,19 @@ { "tx_bridge_pool.wasm": "tx_bridge_pool.e21563260c03cfdab1f195878f49bf93722027ad26fcd097cfebbc5c4d279082.wasm", - "tx_from_intent.wasm": "tx_from_intent.3b89f8ae7c2d0e78d813fd249bcfb80871205fa0eac306004184155df972bda5.wasm", - "tx_ibc.wasm": "tx_ibc.948b68260e10def4a1b80d0a13c8c02d25f18920cbbb6ef0febacd800cd2e4aa.wasm", - "tx_init_account.wasm": "tx_init_account.33ba50356486f46204bb311259446bad8217b3fad3338d3729097c4c27cf6123.wasm", - "tx_init_nft.wasm": "tx_init_nft.37dc3000a2365db54f0fbf550501e6d9ac6572820ce7a70dfa894e07e67bb764.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.f5db61cb13d2739fdb72e4f46b9f0413d41102d5b91b5b0c01fed87555c68723.wasm", - "tx_init_validator.wasm": "tx_init_validator.b564420e6a58e6a397df44c929e1e8e3d3297a76f17a008c0035cd95f46974d0.wasm", - "tx_mint_nft.wasm": "tx_mint_nft.26733715c5ef27cb27a6f88cbef2d468f092e357c39f2272195b94db3a2ca63e.wasm", - "tx_transfer.wasm": "tx_transfer.eb6d5fa86d9276f3628e3e093acaf11a77b1f44d8016ac971803e8f4613bdc2f.wasm", - "tx_unbond.wasm": "tx_unbond.553d6ca840850f1e76c3fda04efc8d3a929b4e0e69c0129685f3871a060b3ed0.wasm", - "tx_update_vp.wasm": "tx_update_vp.8e12c05146f0189242f53ba0aa077729fb25b1617b179180b290a92f4d0117b1.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.0f6c8bb64f0b5121efffe1f72a8dfcd584b523979b7fdd3e73e93e61a46bf31b.wasm", - "tx_withdraw.wasm": "tx_withdraw.fdbdf0db6de1d53c259f84b503d807a67560a3b45f477301e9a0b20ea8275034.wasm", - "vp_nft.wasm": "vp_nft.b1387806bd245f55eb7ef6ba70f4e645eab7dcc048ac8d314d8162fdd021cb9d.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.356e4873a8d44eba2f5472c2d37485a30b24fd6785fe1dc017d031ec116dbe6f.wasm", - "vp_token.wasm": "vp_token.3f492f380226899d1c0da5d5b69a6de5e67b8a7ebfc5179a506bf38c23819c96.wasm", - "vp_user.wasm": "vp_user.c11d62664abc50e9b613acba04b040f93c2a8fe013a4a916e452c6323cb1bd29.wasm" + "tx_from_intent.wasm": "tx_from_intent.3e5734a16b9ed575111765dd318c6f0045598991035b959a24f99bf77bd14634.wasm", + "tx_ibc.wasm": "tx_ibc.bd919697f8fc9bed1d50abf5b3a8e1b3b737fe2e65bfd8c055e965f0831910a5.wasm", + "tx_init_account.wasm": "tx_init_account.078a50524745f9293b3b32c0694eceb3da75621d99e1e44ef79f45928c53a4a2.wasm", + "tx_init_nft.wasm": "tx_init_nft.fe9013d06b1de44091da6adac2458a3688a2ce54177f758b2386c8307c9ed9c2.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.eb7551c29dab7fc2d317568b5d94a4b829ed8083eda7431f7c7507c0ee23a10f.wasm", + "tx_init_validator.wasm": "tx_init_validator.88506a9b248679d9f65f9adea6ab99f9fe4fa189b1b7928083f03eba1985c171.wasm", + "tx_mint_nft.wasm": "tx_mint_nft.cc2b4de9f8999ef6a56762d021a1f2ed81cd5d9082de3574afbc4195fbb90528.wasm", + "tx_transfer.wasm": "tx_transfer.b54edcb95323d2a7c2dacf695cae32ff1e28ad78b07bb16450903f27ac940c59.wasm", + "tx_unbond.wasm": "tx_unbond.ff2cff17d1ab94e4fbd69ab30af4ee732e576498e52dc33b15e1bb43d2dfc074.wasm", + "tx_update_vp.wasm": "tx_update_vp.28bfdcddf988bf0e9f5b51fd6f592931be8fdd51f960073afa47e0f9f15e81c2.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.66ee2ecebe5ebb2c1bc8749888890ef9d542affc23d27f0414c36a73a649f7e0.wasm", + "tx_withdraw.wasm": "tx_withdraw.4edb668ae6fcfe5dccdd3b58ffee3772f2e6d57e3794207b9353f87dff1940e3.wasm", + "vp_nft.wasm": "vp_nft.e34a534fe7621ec07036fab390098c90b21766f02f2a14800458cf3f3500480b.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.5475409ca94d33ffc4ffc93872f230a62ebf06545b601dc8427bb2bfa23834a2.wasm", + "vp_token.wasm": "vp_token.9d7841550cb1f20f25b8701b1b4075f43638d9dea77733035f106f98566bc24e.wasm", + "vp_user.wasm": "vp_user.dcc6032b8435ed6aeca16fb2eb54e8ffe8759b9efc11f67ffe98a334fb74dab1.wasm" } \ No newline at end of file