From 17d765d0e01df2141de8570e45d09fcacce02d2a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 9 Nov 2023 15:54:55 +0100 Subject: [PATCH 01/81] Remove aliases from genesis templates --- apps/src/lib/cli.rs | 115 ++- apps/src/lib/client/utils.rs | 40 +- apps/src/lib/config/genesis.rs | 313 +++++--- apps/src/lib/config/genesis/chain.rs | 210 +++-- apps/src/lib/config/genesis/templates.rs | 90 +-- apps/src/lib/config/genesis/transactions.rs | 757 ++++++------------- apps/src/lib/node/ledger/shell/init_chain.rs | 149 +--- core/src/types/address.rs | 15 + core/src/types/key/mod.rs | 1 + sdk/src/wallet/pre_genesis.rs | 2 +- tests/src/e2e/helpers.rs | 70 +- tests/src/e2e/ibc_tests.rs | 3 +- tests/src/e2e/ledger_tests.rs | 40 +- tests/src/e2e/setup.rs | 34 +- 14 files changed, 713 insertions(+), 1126 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 0bab1938cb..fbc24acf65 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -3034,7 +3034,6 @@ pub mod args { pub const RAW_PUBLIC_KEY: Arg = arg("public-key"); pub const RAW_PUBLIC_KEY_OPT: ArgOpt = arg_opt("public-key"); - pub const RAW_SOURCE: Arg = arg("source"); pub const RECEIVER: Arg = arg("receiver"); pub const RELAYER: Arg
= arg("relayer"); pub const SAFE_MODE: ArgFlag = flag("safe-mode"); @@ -3060,8 +3059,6 @@ pub mod args { pub const TM_ADDRESS: Arg = arg("tm-address"); pub const TOKEN_OPT: ArgOpt = TOKEN.opt(); pub const TOKEN: Arg = arg("token"); - pub const TRANSFER_FROM_SOURCE_AMOUNT: Arg = - arg("transfer-from-source-amount"); pub const TRANSFER_SOURCE: Arg = arg("source"); pub const TRANSFER_TARGET: Arg = arg("target"); pub const TX_HASH: Arg = arg("tx-hash"); @@ -6702,14 +6699,12 @@ pub mod args { #[derive(Clone, Debug)] pub struct InitGenesisValidator { - pub source: String, pub alias: String, pub commission_rate: Dec, pub max_commission_rate_change: Dec, pub net_address: SocketAddr, pub unsafe_dont_encrypt: bool, pub key_scheme: SchemeType, - pub transfer_from_source_amount: token::DenominatedAmount, pub self_bond_amount: token::DenominatedAmount, pub email: String, pub description: Option, @@ -6719,7 +6714,6 @@ pub mod args { impl Args for InitGenesisValidator { fn parse(matches: &ArgMatches) -> Self { - let source = RAW_SOURCE.parse(matches); let alias = ALIAS.parse(matches); let commission_rate = COMMISSION_RATE.parse(matches); let max_commission_rate_change = @@ -6727,12 +6721,6 @@ pub mod args { let net_address = NET_ADDRESS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let key_scheme = SCHEME.parse(matches); - // The denomination validation is handled by validating genesis - // files later. - // At this stage, we treat amounts as opaque and pass them on - // verbatim. - let transfer_from_source_amount = - TRANSFER_FROM_SOURCE_AMOUNT.parse(matches); // this must be an amount of native tokens let self_bond_amount = SELF_BOND_AMOUNT.parse(matches); let email = EMAIL.parse(matches); @@ -6740,14 +6728,12 @@ pub mod args { let website = WEBSITE_OPT.parse(matches); let discord_handle = DISCORD_OPT.parse(matches); Self { - source, alias, net_address, unsafe_dont_encrypt, key_scheme, commission_rate, max_commission_rate_change, - transfer_from_source_amount, self_bond_amount, email, description, @@ -6757,64 +6743,49 @@ pub mod args { } fn def(app: App) -> App { - app.arg(RAW_SOURCE.def().help( - "The source key for native token transfer from the \ - `balances.toml` genesis template.", - )) - .arg(ALIAS.def().help("The validator address alias.")) - .arg(NET_ADDRESS.def().help( - "Static {host:port} of your validator node's P2P address. \ - Namada uses port `26656` for P2P connections by default, but \ - you can configure a different value.", - )) - .arg(COMMISSION_RATE.def().help( - "The commission rate charged by the validator for delegation \ - rewards. This is a required parameter.", - )) - .arg(MAX_COMMISSION_RATE_CHANGE.def().help( - "The maximum change per epoch in the commission rate charged \ - by the validator for delegation rewards. This is a required \ - parameter.", - )) - .arg(UNSAFE_DONT_ENCRYPT.def().help( - "UNSAFE: Do not encrypt the generated keypairs. Do not use \ - this for keys used in a live network.", - )) - .arg(SCHEME.def().help( - "The key scheme/type used for the validator keys. Currently \ - supports ed25519 and secp256k1.", - )) - .arg(TRANSFER_FROM_SOURCE_AMOUNT.def().help( - "The amount of native token to transfer into the validator \ - account from the `--source`. To self-bond some tokens to the \ - validator at genesis, specify `--self-bond-amount`.", - )) - .arg( - SELF_BOND_AMOUNT - .def() - .help( - "The amount of native token to self-bond in PoS. \ - Because this amount will be bonded from the \ - validator's account, it must be lower or equal to \ - the amount specified in \ - `--transfer-from-source-amount`.", - ) - .requires(TRANSFER_FROM_SOURCE_AMOUNT.name), - ) - .arg(EMAIL.def().help( - "The email address of the validator. This is a required \ - parameter.", - )) - .arg(DESCRIPTION_OPT.def().help( - "The validator's description. This is an optional parameter.", - )) - .arg(WEBSITE_OPT.def().help( - "The validator's website. This is an optional parameter.", - )) - .arg(DISCORD_OPT.def().help( - "The validator's discord handle. This is an optional \ - parameter.", - )) + app.arg(ALIAS.def().help("The validator address alias.")) + .arg(NET_ADDRESS.def().help( + "Static {host:port} of your validator node's P2P address. \ + Namada uses port `26656` for P2P connections by default, \ + but you can configure a different value.", + )) + .arg(COMMISSION_RATE.def().help( + "The commission rate charged by the validator for \ + delegation rewards. This is a required parameter.", + )) + .arg(MAX_COMMISSION_RATE_CHANGE.def().help( + "The maximum change per epoch in the commission rate \ + charged by the validator for delegation rewards. This is \ + a required parameter.", + )) + .arg(UNSAFE_DONT_ENCRYPT.def().help( + "UNSAFE: Do not encrypt the generated keypairs. Do not \ + use this for keys used in a live network.", + )) + .arg(SCHEME.def().help( + "The key scheme/type used for the validator keys. \ + Currently supports ed25519 and secp256k1.", + )) + .arg( + SELF_BOND_AMOUNT.def().help( + "The amount of native token to self-bond in PoS.", + ), + ) + .arg(EMAIL.def().help( + "The email address of the validator. This is a required \ + parameter.", + )) + .arg(DESCRIPTION_OPT.def().help( + "The validator's description. This is an optional \ + parameter.", + )) + .arg(WEBSITE_OPT.def().help( + "The validator's website. This is an optional parameter.", + )) + .arg(DISCORD_OPT.def().help( + "The validator's discord handle. This is an optional \ + parameter.", + )) } } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 0ae2388fd2..a73898be0c 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -232,16 +232,12 @@ pub async fn join_network( .map(|(alias, _wallet)| alias.clone()); let validator_keys = validator_alias_and_pre_genesis_wallet.as_ref().map( |(_alias, wallet)| { - let tendermint_node_key: common::SecretKey = wallet - .tendermint_node_key - .try_to_sk() - .unwrap_or_else(|_err| { - eprintln!( - "Tendermint node key must be common (need to change?)" - ); - safe_exit(1) - }); - (tendermint_node_key, wallet.consensus_key.clone()) + let validator_account_pk: common::PublicKey = + wallet.account_key.ref_to(); + let tendermint_node_key: common::SecretKey = + wallet.tendermint_node_key.clone(); + let consensus_key: common::SecretKey = wallet.consensus_key.clone(); + (validator_account_pk, tendermint_node_key, consensus_key) }, ); let node_mode = if validator_alias.is_some() { @@ -254,7 +250,7 @@ pub async fn join_network( let config = genesis.derive_config( &chain_dir, node_mode, - validator_alias, + validator_keys.as_ref().map(|(pk, _, _)| pk), allow_duplicate_ip, ); @@ -273,7 +269,7 @@ pub async fn join_network( crate::wallet::save(&wallet).unwrap(); // Setup the node for a genesis validator, if used - if let Some((tendermint_node_key, consensus_key)) = validator_keys { + if let Some((_, tendermint_node_key, consensus_key)) = validator_keys { println!( "Setting up validator keys in CometBFT. Consensus key: {}.", consensus_key.to_public() @@ -596,14 +592,12 @@ pub fn default_base_dir( pub fn init_genesis_validator( global_args: args::Global, args::InitGenesisValidator { - source, alias, commission_rate, max_commission_rate_change, net_address, unsafe_dont_encrypt, key_scheme, - transfer_from_source_amount, self_bond_amount, email, description, @@ -611,20 +605,6 @@ pub fn init_genesis_validator( discord_handle, }: args::InitGenesisValidator, ) { - let (mut source_wallet, wallet_file) = - load_pre_genesis_wallet_or_exit(&global_args.base_dir); - - let source_key = source_wallet - .find_secret_key(&source, None) - .unwrap_or_else(|err| { - eprintln!( - "Couldn't find key for source \"{source}\" in the pre-genesis \ - wallet {}. Failed with {err}.", - wallet_file.to_string_lossy() - ); - safe_exit(1) - }); - // Validate the commission rate data if commission_rate > Dec::one() { eprintln!("The validator commission rate must not exceed 1.0 or 100%"); @@ -664,19 +644,15 @@ pub fn init_genesis_validator( let transactions = genesis::transactions::init_validator( genesis::transactions::GenesisValidatorData { - source_key, - alias: alias::Alias::from(alias), commission_rate, max_commission_rate_change, net_address, - transfer_from_source_amount, self_bond_amount, email, description, website, discord_handle, }, - &mut source_wallet, &validator_wallet, ); diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 32d2fcf3ae..7e527a50ed 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -5,19 +5,18 @@ pub mod templates; pub mod toml_utils; pub mod transactions; -use std::array::TryFromSliceError; use std::collections::{BTreeMap, HashMap}; +use std::fmt::{Display, Formatter}; use borsh::{BorshDeserialize, BorshSerialize}; -use data_encoding::HEXLOWER; use derivative::Derivative; use namada::core::ledger::governance::parameters::GovernanceParameters; use namada::core::ledger::pgf::parameters::PgfParameters; -use namada::core::types::string_encoding; +use namada::core::types::string_encoding::StringEncoded; use namada::ledger::eth_bridge::EthereumBridgeParams; use namada::ledger::parameters::EpochDuration; use namada::ledger::pos::{Dec, GenesisValidator, OwnedPosParams}; -use namada::types::address::Address; +use namada::types::address::{Address, EstablishedAddress}; use namada::types::chain::ProposalBytes; use namada::types::key::*; use namada::types::time::{DateTimeUtc, DurationSecs}; @@ -28,6 +27,115 @@ use serde::{Deserialize, Serialize}; #[cfg(all(any(test, feature = "benches"), not(feature = "integration")))] use crate::config::genesis::chain::Finalized; +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Ord, + PartialOrd, + Hash, +)] +pub enum GenesisAddress { + /// Encoded as `public_key = "value"` in toml. + PublicKey(StringEncoded), + /// Encoded as `established_address = "value"` in toml. + EstablishedAddress(EstablishedAddress), +} + +impl GenesisAddress { + /// Return an [`Address`] from this [`GenesisAddress`]. + #[inline] + pub fn address(&self) -> Address { + match self { + Self::EstablishedAddress(addr) => { + Address::Established(addr.clone()) + } + Self::PublicKey(pk) => (&pk.raw).into(), + } + } +} + +impl Serialize for GenesisAddress { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + GenesisAddress::EstablishedAddress(address) => { + Serialize::serialize( + &Address::Established(address.clone()), + serializer, + ) + } + GenesisAddress::PublicKey(pk) => { + Serialize::serialize(pk, serializer) + } + } + } +} + +impl<'de> Deserialize<'de> for GenesisAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use std::str::FromStr; + + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = GenesisAddress; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str( + "a bech32m encoded public key or an established address", + ) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + // Try to deserialize a PK first + let maybe_pk = + StringEncoded::::from_str(value); + match maybe_pk { + Ok(pk) => Ok(GenesisAddress::PublicKey(pk)), + Err(_) => { + // If that doesn't work, attempt to retrieve + // an established address + let address = Address::from_str(value) + .map_err(serde::de::Error::custom)?; + if let Address::Established(established) = address { + Ok(GenesisAddress::EstablishedAddress(established)) + } else { + Err(serde::de::Error::custom( + "expected an established address or public key", + )) + } + } + } + } + } + + deserializer.deserialize_str(FieldVisitor) + } +} + +impl Display for GenesisAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GenesisAddress::EstablishedAddress(address) => { + write!(f, "{}", Address::Established(address.clone()).encode()) + } + GenesisAddress::PublicKey(pk) => write!(f, "{}", pk), + } + } +} + #[derive(Debug, BorshSerialize, BorshDeserialize)] #[borsh(init=init)] pub struct Genesis { @@ -189,55 +297,6 @@ pub struct Parameters { pub minimum_gas_price: BTreeMap, } -#[derive( - Clone, - Debug, - Deserialize, - Serialize, - BorshSerialize, - BorshDeserialize, - PartialOrd, - Ord, - PartialEq, - Eq, -)] -pub struct HexString(pub String); - -impl HexString { - pub fn parse(&self) -> Result, HexKeyError> { - let bytes = HEXLOWER.decode(self.0.as_ref())?; - Ok(bytes) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum HexKeyError { - #[error("Invalid hex string: {0:?}")] - InvalidHexString(data_encoding::DecodeError), - #[error("Invalid sha256 checksum: {0}")] - InvalidSha256(TryFromSliceError), - #[error("Invalid public key: {0}")] - InvalidPublicKey(string_encoding::DecodeError), -} - -impl From for HexKeyError { - fn from(err: data_encoding::DecodeError) -> Self { - Self::InvalidHexString(err) - } -} - -impl From for HexKeyError { - fn from(err: string_encoding::DecodeError) -> Self { - Self::InvalidPublicKey(err) - } -} - -impl From for HexKeyError { - fn from(err: TryFromSliceError) -> Self { - Self::InvalidSha256(err) - } -} - /// Modify the default genesis file (namada/genesis/localnet/) to /// accommodate testing. /// @@ -252,16 +311,16 @@ pub fn make_dev_genesis( use std::str::FromStr; use std::time::Duration; - use namada::core::types::string_encoding::StringEncoded; use namada::ledger::eth_bridge::{Contracts, UpgradeableContract}; use namada::proto::{standalone_signature, SerializeWithBorsh}; use namada::types::address::wnam; use namada::types::chain::ChainIdPrefix; use namada::types::ethereum_events::EthAddress; + use namada::types::key::*; use namada::types::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::wallet::alias::Alias; - use crate::config::genesis::chain::finalize; + use crate::config::genesis::chain::{finalize, DeriveEstablishedAddress}; use crate::config::genesis::transactions::UnsignedValidatorAccountTx; use crate::wallet::defaults; @@ -314,17 +373,6 @@ pub fn make_dev_genesis( }; } - // Use the default address for matching established accounts - let default_addresses: HashMap = - defaults::addresses().into_iter().collect(); - if let Some(accs) = genesis.transactions.established_account.as_mut() { - for acc in accs { - if let Some(addr) = default_addresses.get(&acc.tx.alias) { - acc.address = addr.clone(); - } - } - } - // Use the default token address for matching tokens let default_tokens: HashMap = defaults::tokens() .into_iter() @@ -336,14 +384,25 @@ pub fn make_dev_genesis( } } + let assert_established_addr = |addr: &Address| { + if let Address::Established(addr) = addr { + addr.clone() + } else { + panic!("should have gotten an established address") + } + }; + // remove Albert's bond since it messes up existing unit test math if let Some(bonds) = genesis.transactions.bond.as_mut() { - bonds.retain(|bond| { - bond.source - != transactions::AliasOrPk::Alias( - Alias::from_str("albert").unwrap(), - ) - }) + let default_addresses: HashMap = + defaults::addresses().into_iter().collect(); + let fat_alberts_address = + GenesisAddress::EstablishedAddress(assert_established_addr( + default_addresses + .get(&Alias::from_str("albert").unwrap()) + .unwrap(), + )); + bonds.retain(|bond| bond.source != fat_alberts_address); }; let secp_eth_cold_keypair = secp256k1::SecretKey::try_from_slice(&[ 90, 83, 107, 155, 193, 251, 120, 27, 76, 1, 188, 8, 116, 121, 90, 99, @@ -360,38 +419,65 @@ pub fn make_dev_genesis( }, }; // Add other validators with randomly generated keys if needed - for val in 0..(num_validators - 1) { + for _val in 0..(num_validators - 1) { let consensus_keypair: common::SecretKey = testing::gen_keypair::() .try_to_sk() .unwrap(); let account_keypair = consensus_keypair.clone(); - let address = namada::types::address::gen_established_address( - "validator account", - ); let eth_cold_keypair = common::SecretKey::try_from_sk(&secp_eth_cold_keypair).unwrap(); let (protocol_keypair, eth_bridge_keypair) = defaults::validator_keys(); - let alias = Alias::from_str(&format!("validator-{}", val + 1)) - .expect("infallible"); // add the validator - if let Some(vals) = genesis.transactions.validator_account.as_mut() { + let validator_address = if let Some(vals) = + genesis.transactions.validator_account.as_mut() + { + let validator_account_tx = transactions::ValidatorAccountTx { + vp: "vp_validator".to_string(), + commission_rate: Dec::new(5, 2).expect("This can't fail"), + max_commission_rate_change: Dec::new(1, 2) + .expect("This can't fail"), + email: "null@null.net".to_string(), + description: None, + website: None, + discord_handle: None, + net_address: SocketAddr::new( + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 8080, + ), + account_key: StringEncoded { + raw: account_keypair.to_public(), + }, + consensus_key: StringEncoded { + raw: consensus_keypair.to_public(), + }, + protocol_key: StringEncoded { + raw: protocol_keypair.to_public(), + }, + tendermint_node_key: StringEncoded { + raw: consensus_keypair.to_public(), + }, + eth_hot_key: StringEncoded { + raw: eth_bridge_keypair.to_public(), + }, + eth_cold_key: StringEncoded { + raw: eth_cold_keypair.to_public(), + }, + }; + let validator_address = + validator_account_tx.derive_established_address(); vals.push(chain::FinalizedValidatorAccountTx { - address, + address: Address::Established(validator_address.clone()), tx: transactions::ValidatorAccountTx { - alias: alias.clone(), - vp: "vp_user".to_string(), - commission_rate: Dec::new(5, 2).expect("This can't fail"), - max_commission_rate_change: Dec::new(1, 2) - .expect("This can't fail"), - email: "null@null.net".to_string(), - description: None, - website: None, - discord_handle: None, - net_address: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), - 8080, - ), + vp: validator_account_tx.vp, + commission_rate: validator_account_tx.commission_rate, + max_commission_rate_change: validator_account_tx + .max_commission_rate_change, + email: validator_account_tx.email, + description: validator_account_tx.description, + website: validator_account_tx.website, + discord_handle: validator_account_tx.discord_handle, + net_address: validator_account_tx.net_address, account_key: sign_pk(&account_keypair), consensus_key: sign_pk(&consensus_keypair), protocol_key: sign_pk(&protocol_keypair), @@ -399,43 +485,18 @@ pub fn make_dev_genesis( eth_hot_key: sign_pk(ð_bridge_keypair), eth_cold_key: sign_pk(ð_cold_keypair), }, - }) + }); + validator_address + } else { + unreachable!() }; - // add the balance to validators implicit key - if let Some(bals) = genesis - .balances - .token - .get_mut(&Alias::from_str("nam").unwrap()) - { - bals.0.insert( - StringEncoded { - raw: account_keypair.ref_to(), - }, - token::DenominatedAmount { - amount: token::Amount::native_whole(200_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, - ); - } - // transfer funds from implicit key to validator - if let Some(trans) = genesis.transactions.transfer.as_mut() { - trans.push(transactions::TransferTx { - token: Alias::from_str("nam").expect("infallible"), - source: StringEncoded { - raw: account_keypair.ref_to(), - }, - target: alias.clone(), - amount: token::DenominatedAmount { - amount: token::Amount::native_whole(200_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, - }) - } // self bond if let Some(bonds) = genesis.transactions.bond.as_mut() { bonds.push(transactions::BondTx { - source: transactions::AliasOrPk::Alias(alias.clone()), - validator: alias, + source: GenesisAddress::EstablishedAddress( + validator_address.clone(), + ), + validator: Address::Established(validator_address), amount: token::DenominatedAmount { amount: token::Amount::native_whole(100_000), denom: NATIVE_MAX_DECIMAL_PLACES.into(), diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index 502e8c27f5..a0d82b52ed 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -5,10 +5,13 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use namada::ledger::parameters::EpochDuration; -use namada::types::address::{Address, EstablishedAddressGen, MASP}; +use namada::types::address::{ + Address, EstablishedAddress, EstablishedAddressGen, +}; use namada::types::chain::{ChainId, ChainIdPrefix}; use namada::types::dec::Dec; use namada::types::hash::Hash; +use namada::types::key::{common, RefTo}; use namada::types::time::{DateTimeUtc, DurationNanos, Rfc3339String}; use namada::types::token::Amount; use namada_sdk::wallet::store::AddressVpType; @@ -29,9 +32,33 @@ use crate::wasm_loader; pub const METADATA_FILE_NAME: &str = "chain.toml"; -// Rng source used for generating genesis addresses. Because the process has to -// be deterministic, change of this value is a breaking change for genesis. -const ADDRESS_RNG_SOURCE: &[u8] = &[]; +/// Derive established addresses from seed data. +pub trait DeriveEstablishedAddress { + /// Arbitrary data to hash the seed data with. + const SALT: &'static str; + + /// Derive an established address. + fn derive_established_address(&self) -> EstablishedAddress + where + Self: BorshSerialize, + { + let mut hasher = Sha256::new(); + hasher.update(Self::SALT.as_bytes()); + hasher.update(self.serialize_to_vec()); + let digest = hasher.finalize(); + let digest_ref: &[u8; 32] = digest.as_ref(); + EstablishedAddress::from(*digest_ref) + } + + /// Derive an address. + #[inline] + fn derive_address(&self) -> Address + where + Self: BorshSerialize, + { + Address::Established(self.derive_established_address()) + } +} impl Finalized { /// Write all genesis and the chain metadata TOML files to the given @@ -111,31 +138,14 @@ impl Finalized { config.address.clone(), ); } - if let Some(txs) = &self.transactions.validator_account { - for tx in txs { - wallet.insert_address( - tx.tx.alias.normalize(), - tx.address.clone(), - false, - ); - } - } - if let Some(txs) = &self.transactions.established_account { - for tx in txs { - wallet.insert_address( - tx.tx.alias.normalize(), - tx.address.clone(), - false, - ); - } - } if let Some(pre_genesis_wallet) = pre_genesis_wallet { wallet.extend(pre_genesis_wallet); } if let Some((alias, validator_wallet)) = validator { + let account_pk = validator_wallet.account_key.ref_to(); let address = self .transactions - .find_validator(&alias) + .find_validator(&account_pk) .map(|tx| tx.address.clone()) .expect("Validator alias not found in genesis transactions."); wallet.extend_from_pre_genesis_validator( @@ -152,10 +162,12 @@ impl Finalized { &self, base_dir: &Path, node_mode: TendermintMode, - validator_alias: Option, + validator_account_pk: Option<&common::PublicKey>, allow_duplicate_ip: bool, ) -> Config { - if node_mode != TendermintMode::Validator && validator_alias.is_some() { + if node_mode != TendermintMode::Validator + && validator_account_pk.is_some() + { println!( "Warning: Validator alias used to derive config, but node \ mode is not validator, it is {node_mode:?}!" @@ -166,15 +178,17 @@ impl Finalized { // Derive persistent peers from genesis let persistent_peers = self.derive_persistent_peers(); - // If `validator_wallet` is given, find its net_address + // If `validator_account_pk` is given, find its net_address let validator_net_and_tm_address = - if let Some(alias) = validator_alias.as_ref() { - self.transactions.find_validator(alias).map(|validator_tx| { - ( - validator_tx.tx.net_address, - validator_tx.derive_tendermint_address(), - ) - }) + if let Some(account_pk) = validator_account_pk { + self.transactions.find_validator(account_pk).map( + |validator_tx| { + ( + validator_tx.tx.net_address, + validator_tx.derive_tendermint_address(), + ) + }, + ) } else { None }; @@ -413,31 +427,6 @@ impl Finalized { pub fn get_token_address(&self, alias: &Alias) -> Option<&Address> { self.tokens.token.get(alias).map(|token| &token.address) } - - pub fn get_user_address(&self, alias: &Alias) -> Option
{ - if alias.to_string() == *"masp" { - return Some(MASP); - } - let established = self.transactions.established_account.as_ref()?; - let validators = self.transactions.validator_account.as_ref()?; - established - .iter() - .find_map(|tx| { - (&tx.tx.alias == alias).then_some(tx.address.clone()) - }) - .or_else(|| { - validators.iter().find_map(|tx| { - (&tx.tx.alias == alias).then_some(tx.address.clone()) - }) - }) - } - - pub fn get_validator_address(&self, alias: &Alias) -> Option<&Address> { - let validators = self.transactions.validator_account.as_ref()?; - validators - .iter() - .find_map(|tx| (&tx.tx.alias == alias).then_some(&tx.address)) - } } /// Create the [`Finalized`] chain configuration. Derives the chain ID from the @@ -467,7 +456,7 @@ pub fn finalize( }, }; let genesis_bytes = genesis_to_gen_address.serialize_to_vec(); - let mut addr_gen = established_address_gen(&genesis_bytes); + let addr_gen = established_address_gen(&genesis_bytes); // Generate addresses let templates::All { @@ -477,11 +466,9 @@ pub fn finalize( parameters, transactions, } = genesis_to_gen_address.templates; - let tokens = FinalizedTokens::finalize_from(tokens, &mut addr_gen); - let transactions = - FinalizedTransactions::finalize_from(transactions, &mut addr_gen); - let parameters = - FinalizedParameters::finalize_from(&transactions, parameters); + let tokens = FinalizedTokens::finalize_from(tokens); + let transactions = FinalizedTransactions::finalize_from(transactions); + let parameters = FinalizedParameters::finalize_from(parameters); // Store the last state of the address generator in the metadata let mut metadata = genesis_to_gen_address.metadata; @@ -530,20 +517,6 @@ pub fn finalize( } } -/// Use bytes as a deterministic seed for address generator. -fn established_address_gen(bytes: &[u8]) -> EstablishedAddressGen { - let mut hasher = Sha256::new(); - hasher.update(bytes); - // hex of the first 40 chars of the hash - let hash = format!("{:.width$X}", hasher.finalize(), width = 40); - EstablishedAddressGen::new(hash) -} - -/// Deterministically generate an [`Address`]. -fn gen_address(gen: &mut EstablishedAddressGen) -> Address { - gen.generate_address(ADDRESS_RNG_SOURCE) -} - /// Chain genesis config to be finalized. This struct is used to derive the /// chain ID to construct a [`Finalized`] chain genesis config. #[derive( @@ -563,6 +536,15 @@ pub type ToFinalize = Chain; /// Chain genesis config. pub type Finalized = Chain; +/// Use bytes as a deterministic seed for address generator. +fn established_address_gen(bytes: &[u8]) -> EstablishedAddressGen { + let mut hasher = Sha256::new(); + hasher.update(bytes); + // hex of the first 40 chars of the hash + let hash = format!("{:.width$X}", hasher.finalize(), width = 40); + EstablishedAddressGen::new(hash) +} + /// Chain genesis config with generic chain ID. #[derive( Clone, @@ -599,15 +581,14 @@ pub struct FinalizedTokens { } impl FinalizedTokens { - fn finalize_from( - tokens: templates::Tokens, - addr_gen: &mut EstablishedAddressGen, - ) -> FinalizedTokens { + fn finalize_from(tokens: templates::Tokens) -> FinalizedTokens { let templates::Tokens { token } = tokens; let token = token .into_iter() .map(|(key, config)| { - let address = gen_address(addr_gen); + let address = Address::Established( + (&key, &config).derive_established_address(), + ); (key, FinalizedTokenConfig { address, config }) }) .collect(); @@ -631,6 +612,10 @@ pub struct FinalizedTokenConfig { pub config: templates::TokenConfig, } +impl DeriveEstablishedAddress for (&Alias, &templates::TokenConfig) { + const SALT: &'static str = "token-config"; +} + #[derive( Clone, Debug, @@ -645,52 +630,55 @@ pub struct FinalizedTokenConfig { pub struct FinalizedTransactions { pub established_account: Option>, pub validator_account: Option>, - pub transfer: Option>>, pub bond: Option>>, } impl FinalizedTransactions { fn finalize_from( transactions: transactions::Transactions, - addr_gen: &mut EstablishedAddressGen, ) -> FinalizedTransactions { let transactions::Transactions { established_account, validator_account, - transfer, bond, } = transactions; let established_account = established_account.map(|txs| { txs.into_iter() - .map(|tx| { - let address = gen_address(addr_gen); - FinalizedEstablishedAccountTx { address, tx } + .map(|tx| FinalizedEstablishedAccountTx { + address: transactions::UnsignedEstablishedAccountTx::from( + &tx, + ) + .derive_address(), + tx, }) .collect() }); let validator_account = validator_account.map(|txs| { txs.into_iter() - .map(|tx| { - let address = gen_address(addr_gen); - FinalizedValidatorAccountTx { address, tx } + .map(|tx| FinalizedValidatorAccountTx { + address: transactions::UnsignedValidatorAccountTx::from( + &tx, + ) + .derive_address(), + tx, }) .collect() }); FinalizedTransactions { established_account, validator_account, - transfer, bond, } } fn find_validator( &self, - alias: &Alias, + validator_account_pk: &common::PublicKey, ) -> Option<&FinalizedValidatorAccountTx> { - self.validator_account - .as_ref() - .and_then(|txs| txs.iter().find(|tx| &tx.tx.alias == alias)) + let validator_accounts = self.validator_account.as_ref()?; + validator_accounts + .iter() + .find(|tx| &tx.tx.account_key.pk.raw == validator_account_pk) } } @@ -705,7 +693,7 @@ impl FinalizedTransactions { Eq, )] pub struct FinalizedParameters { - pub parameters: templates::ChainParams, + pub parameters: templates::ChainParams, pub pos_params: templates::PosParams, pub gov_params: templates::GovernanceParams, pub pgf_params: namada::core::ledger::pgf::parameters::PgfParameters, @@ -714,7 +702,6 @@ pub struct FinalizedParameters { impl FinalizedParameters { fn finalize_from( - txs: &FinalizedTransactions, templates::Parameters { parameters, pos_params, @@ -724,32 +711,11 @@ impl FinalizedParameters { }: templates::Parameters, ) -> Self { use namada::core::ledger::pgf::parameters::PgfParameters; - let mut finalized_pgf_params = PgfParameters { - stewards: Default::default(), + let finalized_pgf_params = PgfParameters { + stewards: pgf_params.stewards, pgf_inflation_rate: pgf_params.pgf_inflation_rate, stewards_inflation_rate: pgf_params.stewards_inflation_rate, }; - finalized_pgf_params.stewards = pgf_params - .stewards - .into_iter() - .map(|alias| { - let maybe_estbd = txs - .established_account - .as_ref() - .unwrap() - .iter() - .find(|tx| tx.tx.alias == alias) - .map(|tx| tx.address.clone()); - let maybe_validator = txs - .validator_account - .as_ref() - .unwrap() - .iter() - .find(|tx| tx.tx.alias == alias) - .map(|tx| tx.address.clone()); - maybe_estbd.or(maybe_validator).unwrap() - }) - .collect(); Self { parameters, pos_params, diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index 50afeb9ac8..fab82401f0 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -5,12 +5,11 @@ use std::marker::PhantomData; use std::path::Path; use borsh::{BorshDeserialize, BorshSerialize}; -use namada::core::types::key::common; -use namada::core::types::string_encoding::StringEncoded; use namada::core::types::{ethereum_structs, token}; use namada::eth_bridge::parameters::{ Contracts, Erc20WhitelistEntry, MinimumConfirmations, }; +use namada::types::address::Address; use namada::types::chain::ProposalBytes; use namada::types::dec::Dec; use namada::types::token::{ @@ -19,10 +18,13 @@ use namada::types::token::{ use serde::{Deserialize, Serialize}; use super::toml_utils::{read_toml, write_toml}; -use super::transactions::{self, Transactions}; -use crate::config::genesis::transactions::{ - BondTx, SignedBondTx, SignedTransferTx, TransferTx, +use super::transactions::{ + self, Transactions, UnsignedEstablishedAccountTx, + UnsignedValidatorAccountTx, }; +use crate::config::genesis::chain::DeriveEstablishedAddress; +use crate::config::genesis::transactions::{BondTx, SignedBondTx}; +use crate::config::genesis::GenesisAddress; use crate::wallet::Alias; pub const BALANCES_FILE_NAME: &str = "balances.toml"; @@ -97,9 +99,9 @@ impl UndenominatedBalances { })? .denom; let mut denominated_bals = BTreeMap::new(); - for (pk, bal) in bals.0.into_iter() { + for (addr, bal) in bals.0.into_iter() { let denominated = bal.increase_precision(denom)?; - denominated_bals.insert(pk, denominated); + denominated_bals.insert(addr, denominated); } balances .token @@ -136,7 +138,7 @@ pub struct DenominatedBalances { Eq, )] pub struct RawTokenBalances( - pub BTreeMap, token::DenominatedAmount>, + pub BTreeMap, ); /// Genesis balances for a given token @@ -151,7 +153,7 @@ pub struct RawTokenBalances( Eq, )] pub struct TokenBalances( - pub BTreeMap, token::DenominatedAmount>, + pub BTreeMap, ); /// Genesis validity predicates @@ -450,7 +452,7 @@ pub struct GovernanceParams { )] pub struct PgfParams { /// The set of stewards - pub stewards: BTreeSet, + pub stewards: BTreeSet
, /// The pgf funding inflation rate pub pgf_inflation_rate: Dec, /// The pgf stewards inflation rate @@ -489,11 +491,11 @@ pub struct EthBridgeParams { } impl TokenBalances { - pub fn get(&self, pk: common::PublicKey) -> Option { - let pk = StringEncoded { raw: pk }; - self.0.get(&pk).map(|amt| amt.amount) + pub fn get(&self, addr: &GenesisAddress) -> Option { + self.0.get(addr).map(|amt| amt.amount) } } + #[derive( Clone, Debug, @@ -535,14 +537,6 @@ pub trait TemplateValidation: Serialize { + BorshDeserialize + PartialEq + Eq; - type TransferTx: for<'a> Deserialize<'a> - + Serialize - + Clone - + std::fmt::Debug - + BorshSerialize - + BorshDeserialize - + PartialEq - + Eq; type BondTx: for<'a> Deserialize<'a> + Serialize + Clone @@ -564,9 +558,8 @@ pub trait TemplateValidation: Serialize { impl TemplateValidation for Unvalidated { type Amount = token::DenominatedAmount; type Balances = UndenominatedBalances; - type BondTx = SignedBondTx; + type BondTx = SignedBondTx; type GasMinimums = BTreeMap; - type TransferTx = SignedTransferTx; } impl TemplateValidation for Validated { @@ -574,7 +567,6 @@ impl TemplateValidation for Validated { type Balances = DenominatedBalances; type BondTx = BondTx; type GasMinimums = BTreeMap; - type TransferTx = TransferTx; } #[derive( @@ -763,22 +755,16 @@ pub fn load_and_validate(templates_dir: &Path) -> Option> { is_valid = false; } - let txs = if let Some(tokens) = tokens.as_ref() { - if let Some(txs) = transactions.and_then(|txs| { - transactions::validate( - txs, - vps.as_ref(), - balances.as_ref(), - tokens, - parameters.as_ref(), - ) - }) { - println!("Transactions file is valid."); - Some(txs) - } else { - is_valid = false; - None - } + let txs = if let Some(txs) = transactions.and_then(|txs| { + transactions::validate( + txs, + vps.as_ref(), + balances.as_ref(), + parameters.as_ref(), + ) + }) { + println!("Transactions file is valid."); + Some(txs) } else { is_valid = false; None @@ -834,13 +820,21 @@ pub fn validate_parameters( for steward in ¶meters.pgf_params.stewards { let mut found_steward = false; if let Some(accs) = &txs.established_account { - if accs.iter().any(|acct| acct.alias == *steward) { + if accs.iter().any(|acct| { + let addr = + UnsignedEstablishedAccountTx::from(acct).derive_address(); + &addr == steward + }) { found_steward = true; } } if let Some(accs) = &txs.validator_account { - if accs.iter().any(|acct| acct.alias == *steward) { + if accs.iter().any(|acct| { + let addr = + UnsignedValidatorAccountTx::from(acct).derive_address(); + &addr == steward + }) { found_steward = true; } } @@ -941,6 +935,7 @@ mod tests { use std::path::PathBuf; use namada::core::types::key; + use namada::core::types::string_encoding::StringEncoded; use namada::types::key::RefTo; use tempfile::tempdir; @@ -978,6 +973,8 @@ mod tests { let path = test_dir.path().join(BALANCES_FILE_NAME); let sk = key::testing::keypair_1(); let pk = sk.ref_to(); + let address = + GenesisAddress::PublicKey(StringEncoded { raw: pk.clone() }); let balance = token::Amount::from(101_000_001); let token_alias = Alias::from("Some_token".to_string()); let contents = format!( @@ -991,13 +988,6 @@ mod tests { let balances = read_balances(&path).unwrap(); let example_balance = balances.token.get(&token_alias).unwrap(); - assert_eq!( - balance, - example_balance - .0 - .get(&StringEncoded { raw: pk }) - .unwrap() - .amount - ); + assert_eq!(balance, example_balance.0.get(&address).unwrap().amount); } } diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index b617b5a3bf..388f707b23 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -1,13 +1,12 @@ //! Genesis transactions -use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::fmt::{Debug, Display, Formatter}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt::Debug; use std::net::SocketAddr; -use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; -use namada::core::types::storage; +use namada::core::types::address::Address; use namada::core::types::string_encoding::StringEncoded; use namada::proto::{ standalone_signature, verify_standalone_sig, SerializeWithBorsh, @@ -17,28 +16,25 @@ use namada::types::key::{common, RefTo, VerifySigError}; use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; -use namada_sdk::wallet::{FindKeyError, Wallet}; +use namada_sdk::wallet::Wallet; use serde::{Deserialize, Serialize}; -use super::templates::{ - DenominatedBalances, Parameters, TokenBalances, ValidityPredicates, -}; +use super::templates::{DenominatedBalances, Parameters, ValidityPredicates}; +use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::templates::{ - TemplateValidation, Tokens, Unvalidated, Validated, + TemplateValidation, Unvalidated, Validated, }; -use crate::config::genesis::HexString; -use crate::wallet::{Alias, CliWalletUtils}; +use crate::config::genesis::GenesisAddress; +use crate::wallet::CliWalletUtils; pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; pub struct GenesisValidatorData { - pub source_key: common::SecretKey, - pub alias: Alias, pub commission_rate: Dec, pub max_commission_rate_change: Dec, pub net_address: SocketAddr, - pub transfer_from_source_amount: token::DenominatedAmount, pub self_bond_amount: token::DenominatedAmount, pub email: String, pub description: Option, @@ -56,7 +52,6 @@ pub fn sign_txs( let UnsignedTransactions { established_account, validator_account, - transfer, bond, } = txs; @@ -69,13 +64,11 @@ pub fn sign_txs( if let Some(bonds) = bond.as_ref() { for bond in bonds { - if let AliasOrPk::Alias(source) = &bond.source { - if source == &bond.validator { - panic!( - "Validator self-bonds must be signed with a validator \ - wallet." - ) - } + if bond.source.address() == bond.validator { + panic!( + "Validator self-bonds must be signed with a validator \ + wallet." + ) } } } @@ -87,11 +80,6 @@ pub fn sign_txs( .collect() }); let validator_account = None; - let transfer = transfer.map(|tx| { - tx.into_iter() - .map(|tx| sign_transfer_tx(tx, wallet)) - .collect() - }); let bond = bond.map(|tx| { tx.into_iter() .map(|tx| sign_delegation_bond_tx(tx, wallet, &established_account)) @@ -101,7 +89,6 @@ pub fn sign_txs( Transactions { established_account, validator_account, - transfer, bond, } } @@ -116,23 +103,18 @@ pub fn parse_unsigned( /// Create signed [`Transactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { - source_key, - alias, commission_rate, max_commission_rate_change, net_address, - transfer_from_source_amount, self_bond_amount, email, description, website, discord_handle, }: GenesisValidatorData, - source_wallet: &mut Wallet, validator_wallet: &ValidatorWallet, ) -> Transactions { let unsigned_validator_account_tx = UnsignedValidatorAccountTx { - alias: alias.clone(), account_key: StringEncoded::new(validator_wallet.account_key.ref_to()), consensus_key: StringEncoded::new( validator_wallet.consensus_key.ref_to(), @@ -162,31 +144,23 @@ pub fn init_validator( discord_handle, net_address, }; + let unsigned_validator_addr = + unsigned_validator_account_tx.derive_address(); + let unsigned_validator_established_addr = + unsigned_validator_account_tx.derive_established_address(); let validator_account = Some(vec![sign_validator_account_tx( unsigned_validator_account_tx, validator_wallet, )]); - let transfer = if transfer_from_source_amount.amount.is_zero() { - None - } else { - let unsigned_transfer_tx = TransferTx { - // Only native token can be staked - token: Alias::from("NAM"), - source: StringEncoded::new(source_key.ref_to()), - target: alias.clone(), - amount: transfer_from_source_amount, - }; - let transfer_tx = sign_transfer_tx(unsigned_transfer_tx, source_wallet); - Some(vec![transfer_tx]) - }; - let bond = if self_bond_amount.amount.is_zero() { None } else { let unsigned_bond_tx = BondTx { - source: AliasOrPk::Alias(alias.clone()), - validator: alias, + source: GenesisAddress::EstablishedAddress( + unsigned_validator_established_addr, + ), + validator: unsigned_validator_addr, amount: self_bond_amount, }; let bond_tx = sign_self_bond_tx(unsigned_bond_tx, validator_wallet); @@ -195,7 +169,6 @@ pub fn init_validator( Transactions { validator_account, - transfer, bond, ..Default::default() } @@ -215,18 +188,11 @@ pub fn sign_established_account_tx( authorization: sig, } }); - let UnsignedEstablishedAccountTx { - alias, - vp, - public_key: _, - storage, - } = unsigned_tx; + let UnsignedEstablishedAccountTx { vp, public_key: _ } = unsigned_tx; SignedEstablishedAccountTx { - alias, vp, public_key: key, - storage, } } @@ -249,7 +215,6 @@ pub fn sign_validator_account_tx( sign_tx(&unsigned_tx, &validator_wallet.tendermint_node_key); let ValidatorAccountTx { - alias, account_key, consensus_key, protocol_key, @@ -294,7 +259,6 @@ pub fn sign_validator_account_tx( }; SignedValidatorAccountTx { - alias, account_key, consensus_key, protocol_key, @@ -312,20 +276,10 @@ pub fn sign_validator_account_tx( } } -pub fn sign_transfer_tx( - unsigned_tx: TransferTx, - source_wallet: &mut Wallet, -) -> SignedTransferTx { - let source_key = source_wallet - .find_key_by_pk(&unsigned_tx.source, None) - .expect("Key for source must be present to sign with it."); - unsigned_tx.sign(&source_key) -} - pub fn sign_self_bond_tx( unsigned_tx: BondTx, validator_wallet: &ValidatorWallet, -) -> SignedBondTx { +) -> SignedBondTx { unsigned_tx.sign(&validator_wallet.account_key) } @@ -333,76 +287,9 @@ pub fn sign_delegation_bond_tx( unsigned_tx: BondTx, wallet: &mut Wallet, established_accounts: &Option>>, -) -> SignedBondTx { - let alias = &unsigned_tx.source; - // Try to look-up the source from wallet first - if it's an alias of an - // implicit account that should give us the right key - let found_key = match alias { - AliasOrPk::Alias(alias) => { - wallet.find_secret_key(&alias.normalize(), None) - } - AliasOrPk::PublicKey(pk) => wallet.find_key_by_pk(pk, None), - }; - let source_key = match found_key { - Ok(key) => key, - Err(FindKeyError::KeyNotFound) => { - // If it's not in the wallet, it must be an established account - // so we need to look-up its public key first - let pk = established_accounts - .as_ref() - .unwrap_or_else(|| { - panic!( - "Signing a bond failed. Cannot find \"{alias}\" in \ - the wallet and there are no established accounts." - ); - }) - .iter() - .find_map(|account| match alias { - AliasOrPk::Alias(alias) => { - // delegation from established account - if &account.alias == alias { - Some( - &account - .public_key - .as_ref() - .unwrap_or_else(|| { - panic!( - "Signing a bond failed. The \ - established account \"{alias}\" \ - has no public key. Add a public \ - to be able to sign bonds." - ); - }) - .pk - .raw, - ) - } else { - None - } - } - AliasOrPk::PublicKey(pk) => { - // delegation from an implicit account - Some(&pk.raw) - } - }) - .unwrap_or_else(|| { - panic!( - "Signing a bond failed. Cannot find \"{alias}\" in \ - the wallet or in the established accounts." - ); - }); - wallet.find_key_by_pk(pk, None).unwrap_or_else(|err| { - panic!( - "Signing a bond failed. Cannot find key for established \ - account \"{alias}\" in the wallet. Failed with {err}." - ); - }) - } - Err(err) => panic!( - "Signing a bond failed. Failed to read the key for \"{alias}\" \ - from wallet with {err}." - ), - }; +) -> SignedBondTx { + let source_key = + look_up_sk_from(&unsigned_tx.source, wallet, established_accounts); unsigned_tx.sign(&source_key) } @@ -429,7 +316,6 @@ pub fn sign_tx( pub struct Transactions { pub established_account: Option>, pub validator_account: Option>, - pub transfer: Option>, pub bond: Option>, } @@ -456,16 +342,6 @@ impl Transactions { txs }) .or(other.validator_account); - self.transfer = self - .transfer - .take() - .map(|mut txs| { - if let Some(new_txs) = other.transfer.as_mut() { - txs.append(new_txs); - } - txs - }) - .or(other.transfer); self.bond = self .bond .take() @@ -484,7 +360,6 @@ impl Default for Transactions { Self { established_account: None, validator_account: None, - transfer: None, bond: None, } } @@ -509,14 +384,14 @@ impl Transactions { self.bond .as_ref() .map(|txs| { - let mut stakes: BTreeMap<&Alias, token::Amount> = + let mut stakes: BTreeMap<&Address, token::Amount> = BTreeMap::new(); for tx in txs { let entry = stakes.entry(&tx.validator).or_default(); *entry += tx.amount.amount; } - stakes.into_iter().any(|(_validator, stake)| { + stakes.into_values().any(|stake| { let tendermint_voting_power = namada::ledger::pos::into_tm_voting_power( votes_per_token, @@ -536,7 +411,6 @@ impl Transactions { pub struct UnsignedTransactions { pub established_account: Option>, pub validator_account: Option>, - pub transfer: Option>>, pub bond: Option>>, } @@ -556,7 +430,6 @@ pub type SignedValidatorAccountTx = ValidatorAccountTx; Eq, )] pub struct ValidatorAccountTx { - pub alias: Alias, pub vp: String, /// Commission rate charged on rewards for delegators (bounded inside /// 0-1) @@ -579,6 +452,10 @@ pub struct ValidatorAccountTx { pub eth_cold_key: PK, } +impl DeriveEstablishedAddress for UnsignedValidatorAccountTx { + const SALT: &'static str = "validator-account-tx"; +} + pub type UnsignedEstablishedAccountTx = EstablishedAccountTx>; @@ -595,129 +472,21 @@ pub type SignedEstablishedAccountTx = EstablishedAccountTx; Eq, )] pub struct EstablishedAccountTx { - pub alias: Alias, pub vp: String, /// PKs have to come last in TOML to avoid `ValueAfterTable` error pub public_key: Option, - #[serde(default)] - /// Initial storage key values - pub storage: HashMap, -} - -pub type SignedTransferTx = Signed>; - -impl SignedTransferTx { - /// Verify the signature of `TransferTx`. This should not depend - /// on whether the contained amount is denominated or not. - /// - /// Since we denominate amounts as part of validation, we can - /// only verify signatures on [`SignedTransferTx`] - /// types. - pub fn verify_sig(&self) -> Result<(), VerifySigError> { - let Self { data, signature } = self; - verify_standalone_sig::<_, SerializeWithBorsh>( - &data.data_to_sign(), - &data.source.raw, - signature, - ) - } -} - -#[derive( - Clone, - Debug, - Deserialize, - Serialize, - BorshSerialize, - BorshDeserialize, - PartialEq, - Eq, -)] -pub struct TransferTx { - pub token: Alias, - pub source: StringEncoded, - pub target: Alias, - pub amount: T::Amount, } -impl TransferTx { - /// Add the correct denomination to the contained amount - pub fn denominate( - self, - tokens: &Tokens, - ) -> eyre::Result> { - let TransferTx { - token, - source, - target, - amount, - } = self; - let denom = - if let Some(super::templates::TokenConfig { denom, .. }) = - tokens.token.get(&token) - { - *denom - } else { - eprintln!( - "Genesis files contained transfer of token {}, which is \ - not in the `tokens.toml` file", - token - ); - return Err(eyre::eyre!( - "Genesis files contained transfer of token {}, which is \ - not in the `tokens.toml` file", - token - )); - }; - let amount = amount.increase_precision(denom).map_err(|e| { - eprintln!( - "A bond amount in the transactions.toml file was incorrectly \ - formatted:\n{}", - e - ); - e - })?; - - Ok(TransferTx { - token, - source, - target, - amount, - }) - } - - /// The signable data. This does not include the phantom data. - fn data_to_sign(&self) -> Vec { - [ - self.token.serialize_to_vec(), - self.source.serialize_to_vec(), - self.target.serialize_to_vec(), - self.amount.serialize_to_vec(), - ] - .concat() - } - - /// Sign the transfer. - /// - /// Since we denominate amounts as part of validation, we can - /// only verify signatures on [`SignedTransferTx`] - /// types. Thus we only allow signing of [`TransferTx`] - /// types. - pub fn sign(self, key: &common::SecretKey) -> SignedTransferTx { - let sig = standalone_signature::<_, SerializeWithBorsh>( - key, - &self.data_to_sign(), - ); - SignedTransferTx { - data: self, - signature: StringEncoded { raw: sig }, - } - } +impl DeriveEstablishedAddress for UnsignedEstablishedAccountTx { + const SALT: &'static str = "established-account-tx"; } -pub type SignedBondTx = Signed>; +pub type SignedBondTx = Signed>; -impl SignedBondTx { +impl SignedBondTx +where + T: BorshSerialize + TemplateValidation, +{ /// Verify the signature of `BondTx`. This should not depend /// on whether the contained amount is denominated or not. /// @@ -748,11 +517,26 @@ impl SignedBondTx { Eq, )] pub struct BondTx { - pub source: AliasOrPk, - pub validator: Alias, + pub source: GenesisAddress, + pub validator: Address, pub amount: T::Amount, } +impl BondTx +where + T: TemplateValidation + BorshSerialize, +{ + /// The signable data. This does not include the phantom data. + fn data_to_sign(&self) -> Vec { + [ + self.source.serialize_to_vec(), + self.validator.serialize_to_vec(), + self.amount.serialize_to_vec(), + ] + .concat() + } +} + impl BondTx { /// Add the correct denomination to the contained amount pub fn denominate(self) -> eyre::Result> { @@ -778,23 +562,13 @@ impl BondTx { }) } - /// The signable data. This does not include the phantom data. - fn data_to_sign(&self) -> Vec { - [ - self.source.serialize_to_vec(), - self.validator.serialize_to_vec(), - self.amount.serialize_to_vec(), - ] - .concat() - } - /// Sign the transfer. /// /// Since we denominate amounts as part of validation, we can /// only verify signatures on [`SignedBondTx`] /// types. Thus we only allow signing of [`BondTx`] /// types. - pub fn sign(self, key: &common::SecretKey) -> SignedBondTx { + pub fn sign(self, key: &common::SecretKey) -> SignedBondTx { let sig = standalone_signature::<_, SerializeWithBorsh>( key, &self.data_to_sign(), @@ -806,74 +580,6 @@ impl BondTx { } } -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, PartialEq, Eq)] -pub enum AliasOrPk { - /// `alias = "value"` in toml (encoded via `AliasSerHelper`) - Alias(Alias), - /// `public_key = "value"` in toml (encoded via `PkSerHelper`) - PublicKey(StringEncoded), -} - -impl Serialize for AliasOrPk { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - AliasOrPk::Alias(alias) => Serialize::serialize(alias, serializer), - AliasOrPk::PublicKey(pk) => Serialize::serialize(pk, serializer), - } - } -} - -impl<'de> Deserialize<'de> for AliasOrPk { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct FieldVisitor; - - impl<'de> serde::de::Visitor<'de> for FieldVisitor { - type Value = AliasOrPk; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - formatter.write_str( - "a bech32m encoded `common::PublicKey` or an alias", - ) - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - // Try to deserialize a PK first - let maybe_pk = - StringEncoded::::from_str(value); - match maybe_pk { - Ok(pk) => Ok(AliasOrPk::PublicKey(pk)), - Err(_) => { - // If that doesn't work, use it as an alias - let alias = Alias::from_str(value) - .map_err(serde::de::Error::custom)?; - Ok(AliasOrPk::Alias(alias)) - } - } - } - } - - deserializer.deserialize_str(FieldVisitor) - } -} - -impl Display for AliasOrPk { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AliasOrPk::Alias(alias) => write!(f, "{}", alias), - AliasOrPk::PublicKey(pk) => write!(f, "{}", pk), - } - } -} - #[derive( Clone, Debug, @@ -909,21 +615,19 @@ pub fn validate( transactions: Transactions, vps: Option<&ValidityPredicates>, balances: Option<&DenominatedBalances>, - tokens: &Tokens, parameters: Option<&Parameters>, ) -> Option> { let mut is_valid = true; - let mut all_used_aliases: BTreeSet = BTreeSet::default(); - let mut established_accounts: BTreeMap> = + let mut all_used_addresses: BTreeSet
= BTreeSet::default(); + let mut established_accounts: BTreeMap> = BTreeMap::default(); - let mut validator_accounts: BTreeMap = + let mut validator_accounts: BTreeMap = BTreeMap::default(); let Transactions { ref established_account, ref validator_account, - ref transfer, bond, } = transactions; @@ -932,7 +636,7 @@ pub fn validate( if !validate_established_account( tx, vps, - &mut all_used_aliases, + &mut all_used_addresses, &mut established_accounts, ) { is_valid = false; @@ -945,7 +649,7 @@ pub fn validate( if !validate_validator_account( tx, vps, - &mut all_used_aliases, + &mut all_used_addresses, &mut validator_accounts, ) { is_valid = false; @@ -964,10 +668,7 @@ pub fn validate( ( token.clone(), TokenBalancesForValidation { - // Add an accumulator for tokens transferred to - // aliases - aliases: BTreeMap::new(), - pks: token_balances.clone(), + amounts: token_balances.0.clone(), }, ) }) @@ -975,28 +676,6 @@ pub fn validate( }) .unwrap_or_default(); - let validated_txs = if let Some(txs) = transfer { - let validated_txs: Vec<_> = txs - .iter() - .filter_map(|tx| { - validate_transfer( - tx, - &mut token_balances, - &all_used_aliases, - tokens, - ) - }) - .collect(); - if validated_txs.len() != txs.len() { - is_valid = false; - None - } else { - Some(validated_txs) - } - } else { - None - }; - let validated_bonds = if let Some(txs) = bond { if !txs.is_empty() { match parameters { @@ -1038,34 +717,69 @@ pub fn validate( }; is_valid.then_some(Transactions { - established_account: transactions.established_account, - validator_account: transactions.validator_account, - transfer: validated_txs, + established_account: transactions.established_account.map( + |established_accounts| { + established_accounts + .into_iter() + .map(|acct| EstablishedAccountTx { + vp: acct.vp, + public_key: acct.public_key, + }) + .collect() + }, + ), + validator_account: transactions.validator_account.map( + |validator_accounts| { + validator_accounts + .into_iter() + .map(|acct| ValidatorAccountTx { + vp: acct.vp, + commission_rate: acct.commission_rate, + max_commission_rate_change: acct + .max_commission_rate_change, + email: acct.email, + description: acct.description, + website: acct.website, + discord_handle: acct.discord_handle, + net_address: acct.net_address, + account_key: acct.account_key, + consensus_key: acct.consensus_key, + protocol_key: acct.protocol_key, + tendermint_node_key: acct.tendermint_node_key, + eth_hot_key: acct.eth_hot_key, + eth_cold_key: acct.eth_cold_key, + }) + .collect() + }, + ), bond: validated_bonds, }) } fn validate_bond( - tx: SignedBondTx, + tx: SignedBondTx, balances: &mut BTreeMap, - established_accounts: &BTreeMap>, - validator_accounts: &BTreeMap, + established_accounts: &BTreeMap>, + validator_accounts: &BTreeMap, parameters: &Parameters, ) -> Option> { // Check signature let mut is_valid = { let source = &tx.data.source; if let Some(source_pk) = match source { - AliasOrPk::Alias(alias) => { + GenesisAddress::EstablishedAddress(address) => { // Try to find the source's PK in either established_accounts or // validator_accounts + let established_addr = Address::Established(address.clone()); established_accounts - .get(alias) + .get(&established_addr) .cloned() .flatten() - .or_else(|| validator_accounts.get(alias).cloned()) + .or_else(|| { + validator_accounts.get(&established_addr).cloned() + }) } - AliasOrPk::PublicKey(pk) => Some(pk.raw.clone()), + GenesisAddress::PublicKey(pk) => Some(pk.raw.clone()), } { if tx.verify_sig(&source_pk).is_err() { eprintln!("Invalid bond tx signature.",); @@ -1104,10 +818,7 @@ fn validate_bond( let native_token = ¶meters.parameters.native_token; match balances.get_mut(native_token) { Some(balances) => { - let balance = match source { - AliasOrPk::Alias(source) => balances.aliases.get_mut(source), - AliasOrPk::PublicKey(source) => balances.pks.0.get_mut(source), - }; + let balance = balances.amounts.get_mut(source); match balance { Some(balance) => { if *balance < *amount { @@ -1121,14 +832,7 @@ fn validate_bond( } else { // Deduct the amount from source if amount == balance { - match source { - AliasOrPk::Alias(source) => { - balances.aliases.remove(source); - } - AliasOrPk::PublicKey(source) => { - balances.pks.0.remove(source); - } - } + balances.amounts.remove(source); } else { balance.amount -= amount.amount; } @@ -1157,34 +861,33 @@ fn validate_bond( #[derive(Clone, Debug)] pub struct TokenBalancesForValidation { - /// Accumulator for tokens transferred to aliases - pub aliases: BTreeMap, - /// Token balances from the balances file, associated with PKs - pub pks: TokenBalances, + /// Accumulator for tokens transferred to accounts + pub amounts: BTreeMap, } pub fn validate_established_account( tx: &SignedEstablishedAccountTx, vps: Option<&ValidityPredicates>, - all_used_aliases: &mut BTreeSet, - established_accounts: &mut BTreeMap>, + all_used_addresses: &mut BTreeSet
, + established_accounts: &mut BTreeMap>, ) -> bool { let mut is_valid = true; + let established_address = EstablishedAccountTx::from(tx).derive_address(); established_accounts.insert( - tx.alias.clone(), + established_address.clone(), tx.public_key.as_ref().map(|signed| signed.pk.raw.clone()), ); - // Check that alias is unique - if all_used_aliases.contains(&tx.alias) { + // Check that the established address is unique + if all_used_addresses.contains(&established_address) { eprintln!( - "A duplicate alias \"{}\" found in a `established_account` tx.", - tx.alias + "A duplicate address \"{}\" found in a `established_account` tx.", + established_address ); is_valid = false; } else { - all_used_aliases.insert(tx.alias.clone()); + all_used_addresses.insert(established_address); } // Check the VP exists @@ -1221,22 +924,24 @@ fn validate_established_account_sig( pub fn validate_validator_account( tx: &ValidatorAccountTx, vps: Option<&ValidityPredicates>, - all_used_aliases: &mut BTreeSet, - validator_accounts: &mut BTreeMap, + all_used_addresses: &mut BTreeSet
, + validator_accounts: &mut BTreeMap, ) -> bool { let mut is_valid = true; - validator_accounts.insert(tx.alias.clone(), tx.account_key.pk.raw.clone()); + let established_address = ValidatorAccountTx::from(tx).derive_address(); + validator_accounts + .insert(established_address.clone(), tx.account_key.pk.raw.clone()); - // Check that alias is unique - if all_used_aliases.contains(&tx.alias) { + // Check that address is unique + if all_used_addresses.contains(&established_address) { eprintln!( - "A duplicate alias \"{}\" found in a `validator_account` tx.", - tx.alias + "A duplicate address \"{}\" found in a `validator_account` tx.", + established_address ); is_valid = false; } else { - all_used_aliases.insert(tx.alias.clone()); + all_used_addresses.insert(established_address.clone()); } // Check the VP exists @@ -1261,8 +966,8 @@ pub fn validate_validator_account( ) { eprintln!( "Invalid `account_key` authorization for `validator_account` tx \ - with alias \"{}\".", - tx.alias + with address \"{}\".", + established_address ); is_valid = false; } @@ -1273,8 +978,8 @@ pub fn validate_validator_account( ) { eprintln!( "Invalid `consensus_key` authorization for `validator_account` tx \ - with alias \"{}\".", - tx.alias + with address \"{}\".", + established_address ); is_valid = false; } @@ -1285,8 +990,8 @@ pub fn validate_validator_account( ) { eprintln!( "Invalid `protocol_key` authorization for `validator_account` tx \ - with alias \"{}\".", - tx.alias + with address \"{}\".", + established_address ); is_valid = false; } @@ -1297,8 +1002,8 @@ pub fn validate_validator_account( ) { eprintln!( "Invalid `tendermint_node_key` authorization for \ - `validator_account` tx with alias \"{}\".", - tx.alias + `validator_account` tx with address \"{}\".", + established_address ); is_valid = false; } @@ -1310,8 +1015,8 @@ pub fn validate_validator_account( ) { eprintln!( "Invalid `eth_hot_key` authorization for `validator_account` tx \ - with alias \"{}\".", - tx.alias + with address \"{}\".", + established_address ); is_valid = false; } @@ -1323,8 +1028,8 @@ pub fn validate_validator_account( ) { eprintln!( "Invalid `eth_cold_key` authorization for `validator_account` tx \ - with alias \"{}\".", - tx.alias + with address \"{}\".", + established_address ); is_valid = false; } @@ -1332,90 +1037,6 @@ pub fn validate_validator_account( is_valid } -/// Updates the token balances with all the valid transfers applied -pub fn validate_transfer( - tx: &SignedTransferTx, - balances: &mut BTreeMap, - all_used_aliases: &BTreeSet, - tokens: &Tokens, -) -> Option> { - let mut is_valid = true; - // Check signature - if tx.verify_sig().is_err() { - eprintln!("Invalid transfer tx signature.",); - is_valid = false; - } - - let unsigned: TransferTx = tx.into(); - let validated = unsigned.denominate(tokens).ok()?; - let TransferTx { - token, - source, - target, - amount, - .. - } = &validated; - - // Check that the target exists - if !all_used_aliases.contains(target) { - eprintln!( - "Invalid transfer tx. The target alias \"{target}\" no matching \ - account found." - ); - is_valid = false; - } - - // Check token balance of the source and update token balances of the source - // and target - match balances.get_mut(token) { - Some(balances) => match balances.pks.0.get_mut(source) { - Some(balance) => { - if balance.amount < amount.amount { - eprintln!( - "Invalid transfer tx. Source {source} doesn't have \ - enough balance of token \"{token}\" to transfer {}. \ - Got {}.", - amount, balance, - ); - is_valid = false; - } else { - // Deduct the amount from source - if amount.amount == balance.amount { - balances.pks.0.remove(source); - } else { - balance.amount -= amount.amount; - } - - // Add the amount to target - let target_balance = balances - .aliases - .entry(target.clone()) - .or_insert_with(|| DenominatedAmount { - amount: token::Amount::zero(), - denom: amount.denom, - }); - target_balance.amount += amount.amount; - } - } - None => { - eprintln!( - "Invalid transfer tx. Source {source} has no balance of \ - token \"{token}\"." - ); - is_valid = false; - } - }, - None => { - eprintln!( - "Invalid transfer tx. Token \"{token}\" not found in balances." - ); - is_valid = false; - } - } - - is_valid.then_some(validated) -} - fn validate_signature( tx_data: &T, pk: &common::PublicKey, @@ -1434,17 +1055,10 @@ fn validate_signature( impl From<&SignedEstablishedAccountTx> for UnsignedEstablishedAccountTx { fn from(tx: &SignedEstablishedAccountTx) -> Self { - let SignedEstablishedAccountTx { - alias, - vp, - public_key, - storage, - } = tx; + let SignedEstablishedAccountTx { vp, public_key } = tx; Self { - alias: alias.clone(), vp: vp.clone(), public_key: public_key.as_ref().map(|signed| signed.pk.clone()), - storage: storage.clone(), } } } @@ -1452,7 +1066,6 @@ impl From<&SignedEstablishedAccountTx> for UnsignedEstablishedAccountTx { impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { fn from(tx: &SignedValidatorAccountTx) -> Self { let SignedValidatorAccountTx { - alias, vp, commission_rate, max_commission_rate_change, @@ -1467,10 +1080,10 @@ impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { tendermint_node_key, eth_hot_key, eth_cold_key, + .. } = tx; Self { - alias: alias.clone(), vp: vp.clone(), commission_rate: *commission_rate, max_commission_rate_change: *max_commission_rate_change, @@ -1489,16 +1102,78 @@ impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { } } -impl From<&SignedTransferTx> for TransferTx { - fn from(tx: &SignedTransferTx) -> Self { - let SignedTransferTx { data, signature: _ } = tx; +impl From<&SignedBondTx> for BondTx { + fn from(tx: &SignedBondTx) -> Self { + let SignedBondTx { data, .. } = tx; data.clone() } } -impl From<&SignedBondTx> for BondTx { - fn from(tx: &SignedBondTx) -> Self { - let SignedBondTx { data, signature: _ } = tx; - data.clone() +/// Attempt to look-up a secret key. +fn look_up_sk_from( + source: &GenesisAddress, + wallet: &mut Wallet, + established_accounts: &Option>>, +) -> common::SecretKey { + // Try to look-up the source from wallet first + match source { + GenesisAddress::EstablishedAddress(_) => None, + GenesisAddress::PublicKey(pk) => wallet.find_key_by_pk(pk, None).ok(), } + .unwrap_or_else(|| { + // If it's not in the wallet, it must be an established account + // so we need to look-up its public key first + let pk = established_accounts + .as_ref() + .unwrap_or_else(|| { + panic!( + "Signing failed. Cannot find \"{source}\" in the wallet \ + and there are no established accounts." + ); + }) + .iter() + .find_map(|account| match source { + GenesisAddress::EstablishedAddress(address) => { + // delegation from established account + if &EstablishedAccountTx::from(account) + .derive_established_address() + == address + { + Some( + &account + .public_key + .as_ref() + .unwrap_or_else(|| { + panic!( + "Signing failed. The established \ + account \"{source}\" has no public \ + key. Add a public to be able to sign \ + bonds." + ); + }) + .pk + .raw, + ) + } else { + None + } + } + GenesisAddress::PublicKey(pk) => { + // delegation from an implicit account + Some(&pk.raw) + } + }) + .unwrap_or_else(|| { + panic!( + "Signing failed. Cannot find \"{source}\" in the wallet \ + or in the established accounts." + ); + }); + wallet.find_key_by_pk(pk, None).unwrap_or_else(|err| { + panic!( + "Signing failed. Cannot find key for established account \ + \"{source}\" in the wallet. Failed with {err}." + ); + }) + }) } diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 1f612b2f26..3714ea6aef 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -25,7 +25,7 @@ use crate::config::genesis::chain::{ }; use crate::config::genesis::templates::{TokenBalances, TokenConfig}; use crate::config::genesis::transactions::{ - BondTx, EstablishedAccountTx, TransferTx, ValidatorAccountTx, + BondTx, EstablishedAccountTx, ValidatorAccountTx, }; use crate::facade::tendermint::v0_37::abci::{request, response}; use crate::facade::tendermint_proto::google::protobuf; @@ -154,7 +154,6 @@ where &pos_params, current_epoch, ); - self.apply_genesis_txs_transfer(&genesis); self.apply_genesis_txs_bonds(&genesis); pos::namada_proof_of_stake::compute_and_store_total_consensus_stake( @@ -307,25 +306,26 @@ where .expect("Token with configured balance not found in genesis.") .address; let mut total_token_balance = token::Amount::zero(); - for (owner_pk, balance) in balances { - let owner = Address::from(&owner_pk.raw); - storage_api::account::set_public_key_at( - &mut self.wl_storage, - &owner, - &owner_pk.raw, - 0, - ) - .unwrap(); + for (owner, balance) in balances { + if let genesis::GenesisAddress::PublicKey(pk) = owner { + storage_api::account::set_public_key_at( + &mut self.wl_storage, + &owner.address(), + &pk.raw, + 0, + ) + .unwrap(); + } tracing::info!( "Crediting {} {} tokens to {}", balance, token_alias, - owner_pk.raw + owner, ); credit_tokens( &mut self.wl_storage, token_address, - &owner, + &owner.address(), balance.amount, ) .expect("Couldn't credit initial balance"); @@ -350,18 +350,12 @@ where if let Some(txs) = genesis.transactions.established_account.as_ref() { for FinalizedEstablishedAccountTx { address, - tx: - EstablishedAccountTx { - alias, - vp, - public_key, - storage, - }, + tx: EstablishedAccountTx { vp, public_key }, } in txs { tracing::debug!( "Applying genesis tx to init an established account \ - {alias}" + {address}" ); let vp_code = self.lookup_vp(vp, genesis, vp_cache); let code_hash = CodeHash::sha256(&vp_code); @@ -378,16 +372,6 @@ where ) .unwrap(); } - - // Place the keys under the owners sub-storage - let sub_key = namada::core::types::storage::Key::from( - address.to_db_key(), - ); - for (key, value) in storage { - self.wl_storage - .write_bytes(&sub_key.join(key), value.parse().unwrap()) - .unwrap(); - } } } } @@ -405,7 +389,6 @@ where address, tx: ValidatorAccountTx { - alias, vp, commission_rate, max_commission_rate_change, @@ -420,11 +403,12 @@ where tendermint_node_key: _, eth_hot_key, eth_cold_key, + .. }, } in txs { tracing::debug!( - "Applying genesis tx to init a validator account {alias}" + "Applying genesis tx to init a validator account {address}" ); let vp_code = self.lookup_vp(vp, genesis, vp_cache); @@ -469,8 +453,8 @@ where }, ) { tracing::warn!( - "Genesis init genesis validator tx for {alias} failed \ - with {err}. Skipping." + "Genesis init genesis validator tx for {address} \ + failed with {err}. Skipping." ); continue; } @@ -478,70 +462,6 @@ where } } - /// Apply genesis txs to transfer tokens - fn apply_genesis_txs_transfer( - &mut self, - genesis: &genesis::chain::Finalized, - ) { - if let Some(txs) = &genesis.transactions.transfer { - for TransferTx { - token, - source, - target, - amount, - .. - } in txs - { - let token = match genesis.get_token_address(token) { - Some(token) => { - tracing::debug!( - "Applying genesis tx to transfer {} of token \ - {token} from {source} to {target}", - amount - ); - token - } - None => { - tracing::warn!( - "Genesis token transfer tx uses an unknown token \ - alias {token}. Skipping." - ); - continue; - } - }; - let target = match genesis.get_user_address(target) { - Some(target) => target, - None => { - tracing::warn!( - "Genesis token transfer tx uses an unknown target \ - alias {target}. Skipping." - ); - continue; - } - }; - let source: Address = (&source.raw).into(); - tracing::debug!( - "Transfer addresses: token {token} from {source} to \ - {target}" - ); - - if let Err(err) = storage_api::token::transfer( - &mut self.wl_storage, - token, - &source, - &target, - amount.amount, - ) { - tracing::warn!( - "Genesis token transfer tx failed with: {err}. \ - Skipping." - ); - continue; - }; - } - } - } - /// Apply genesis txs to transfer tokens fn apply_genesis_txs_bonds(&mut self, genesis: &genesis::chain::Finalized) { let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); @@ -559,38 +479,9 @@ where amount, ); - let source = match source { - genesis::transactions::AliasOrPk::Alias(alias) => { - match genesis.get_user_address(alias) { - Some(addr) => addr, - None => { - tracing::warn!( - "Cannot find bond source address with \ - alias \"{alias}\". Skipping." - ); - continue; - } - } - } - genesis::transactions::AliasOrPk::PublicKey(pk) => { - Address::from(&pk.raw) - } - }; - - let validator = match genesis.get_validator_address(validator) { - Some(addr) => addr, - None => { - tracing::warn!( - "Cannot find bond validator address with alias \ - \"{validator}\". Skipping." - ); - continue; - } - }; - if let Err(err) = pos::namada_proof_of_stake::bond_tokens( &mut self.wl_storage, - Some(&source), + Some(&source.address()), validator, amount.amount, current_epoch, diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 44373695a3..91028f0750 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -392,6 +392,20 @@ pub struct EstablishedAddress { hash: [u8; HASH_LEN], } +impl From<[u8; HASH_LEN]> for EstablishedAddress { + fn from(hash: [u8; HASH_LEN]) -> Self { + Self { hash } + } +} + +impl From<[u8; SHA_HASH_LEN]> for EstablishedAddress { + fn from(input_hash: [u8; SHA_HASH_LEN]) -> Self { + let mut hash = [0; HASH_LEN]; + hash.copy_from_slice(&input_hash[..HASH_LEN]); + Self { hash } + } +} + /// A generator of established addresses #[derive( Debug, @@ -438,6 +452,7 @@ impl EstablishedAddressGen { #[derive( Debug, Clone, + Default, BorshSerialize, BorshDeserialize, BorshSchema, diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index b7a426607a..9a13c13503 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -371,6 +371,7 @@ pub trait SigScheme: Eq + Ord + Debug + Serialize + Default { PartialOrd, Ord, Hash, + Default, )] pub struct PublicKeyHash(pub(crate) [u8; address::HASH_LEN]); diff --git a/sdk/src/wallet/pre_genesis.rs b/sdk/src/wallet/pre_genesis.rs index 689015c234..a0c9d4d58e 100644 --- a/sdk/src/wallet/pre_genesis.rs +++ b/sdk/src/wallet/pre_genesis.rs @@ -42,7 +42,7 @@ pub struct ValidatorWallet { pub tendermint_node_key: common::SecretKey, } -// Validator pre-genesis wallet store includes all the required keys for +/// Validator pre-genesis wallet store includes all the required keys for /// genesis setup. #[derive(Serialize, Deserialize, Debug)] pub struct ValidatorStore { diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 09ec4ab808..13fe2b7287 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -19,10 +19,13 @@ use namada::types::address::Address; use namada::types::key::*; use namada::types::storage::Epoch; use namada::types::token; -use namada_apps::config::genesis::chain; +use namada_apps::config::genesis::chain::DeriveEstablishedAddress; +use namada_apps::config::genesis::{templates, transactions}; use namada_apps::config::utils::convert_tm_addr_to_socket_addr; use namada_apps::config::{Config, TendermintMode}; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; +use namada_sdk::wallet::fs::FsWalletUtils; +use namada_sdk::wallet::Wallet; use super::setup::{ self, sleep, NamadaBgCmd, NamadaCmd, Test, ENV_VAR_DEBUG, @@ -172,25 +175,66 @@ pub fn get_actor_rpc(test: &Test, who: &Who) -> String { format!("{}:{}", socket_addr.ip(), socket_addr.port()) } +/// Get some nodes's wallet. +pub fn get_node_wallet(test: &Test, who: &Who) -> Wallet { + let wallet_store_dir = + test.get_base_dir(who).join(test.net.chain_id.as_str()); + let mut wallet = FsWalletUtils::new(wallet_store_dir); + wallet.load().expect("Failed to load wallet"); + wallet +} + /// Get the public key of the validator pub fn get_validator_pk(test: &Test, who: &Who) -> Option { let index = match who { Who::NonValidator => return None, Who::Validator(i) => i, }; - let path = test.test_dir.path().join("genesis"); - let genesis = chain::Finalized::read_toml_files( - &path.join(test.net.chain_id.as_str()), + let mut wallet = get_node_wallet(test, who); + let sk = wallet + .find_secret_key(format!("validator-{index}-validator-key"), None) + .ok()?; + Some(sk.ref_to()) +} + +/// Get a pregenesis wallet. +pub fn get_pregenesis_wallet>( + base_dir_path: P, +) -> Wallet { + let mut wallet_store_dir = base_dir_path.as_ref().to_path_buf(); + wallet_store_dir.push("pre-genesis"); + + let mut wallet = FsWalletUtils::new(wallet_store_dir); + wallet.load().expect("Failed to load wallet"); + + wallet +} + +/// Get a pregenesis public key. +pub fn get_pregenesis_pk>( + alias: &str, + base_dir_path: P, +) -> Option { + let mut wallet = get_pregenesis_wallet(base_dir_path); + let sk = wallet.find_secret_key(alias, None).ok()?; + Some(sk.ref_to()) +} + +/// Get a pregenesis public key. +pub fn get_established_addr_from_pregenesis>( + alias: &str, + base_dir_path: P, + genesis: &templates::All, +) -> Option
{ + let pk = get_pregenesis_pk(alias, base_dir_path)?; + let established_accounts = + genesis.transactions.established_account.as_ref()?; + let acct = established_accounts.iter().find(|&acct| { + acct.public_key.as_ref().expect("the unexpected").pk.raw == pk + })?; + Some( + transactions::UnsignedEstablishedAccountTx::from(acct).derive_address(), ) - .unwrap(); - genesis - .transactions - .validator_account? - .iter() - .find(|val_tx| { - val_tx.tx.alias.to_string() == format!("validator-{}", index) - }) - .map(|val_tx| val_tx.tx.account_key.pk.raw.clone()) } /// Find the address of an account by its alias from the wallet diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index e1b3479d6e..08a864f3d1 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -262,13 +262,14 @@ fn setup_two_single_node_nets() -> Result<(Test, Test)> { .map_err(|_| eyre!("Could not read genesis files from test b"))?; // chain b's validator needs to listen on a different port than chain a's // validator + let validator_pk = get_validator_pk(&test_b, &Who::Validator(0)).unwrap(); let validator_tx = genesis_b .transactions .validator_account .as_mut() .unwrap() .iter_mut() - .find(|val| val.tx.alias.to_string() == *"validator-0") + .find(|val| val.tx.account_key.pk.raw == validator_pk) .unwrap(); let new_port = validator_tx.tx.net_address.port() + setup::ANOTHER_CHAIN_PORT_OFFSET; diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 9f584e2b24..a8ccfb3f83 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -30,7 +30,6 @@ use namada_core::ledger::governance::cli::onchain::{ }; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::masp::fs::FsShieldedUtils; -use namada_sdk::wallet::alias::Alias; use namada_test_utils::TestWasms; use namada_vp_prelude::BTreeSet; use serde_json::json; @@ -38,8 +37,8 @@ use setup::constants::*; use setup::Test; use super::helpers::{ - epochs_per_year_from_min_duration, get_height, wait_for_block_height, - wait_for_wasm_pre_compile, + epochs_per_year_from_min_duration, get_established_addr_from_pregenesis, + get_height, wait_for_block_height, wait_for_wasm_pre_compile, }; use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode, NamadaCmd}; use crate::e2e::helpers::{ @@ -921,15 +920,13 @@ fn pos_bonds() -> Result<()> { base_dir, default_port_offset, ); - let bonds = genesis.transactions.bond.unwrap(); - genesis.transactions.bond = Some( + genesis.transactions.bond = Some({ + let mut bonds = genesis.transactions.bond.unwrap(); + // NB: the last bond should be from `validator-1`. + // we will filter it out from the list of bonds + bonds.pop(); bonds - .into_iter() - .filter(|bond| { - (&bond.data.validator).as_ref() != "validator-1" - }) - .collect(), - ); + }); genesis }, None, @@ -2116,7 +2113,12 @@ fn pgf_governance_proposal() -> Result<()> { genesis.parameters.parameters.min_num_of_blocks = 4; genesis.parameters.parameters.max_expected_time_per_block = 1; genesis.parameters.pgf_params.stewards = - BTreeSet::from_iter(Alias::from_str("albert")); + BTreeSet::from_iter([get_established_addr_from_pregenesis( + "albert-key", + base_dir, + &genesis, + ) + .unwrap()]); setup::set_validators(1, genesis, base_dir, |_| 0) }, None, @@ -3170,15 +3172,13 @@ fn deactivate_and_reactivate_validator() -> Result<()> { base_dir, default_port_offset, ); - let bonds = genesis.transactions.bond.unwrap(); - genesis.transactions.bond = Some( + genesis.transactions.bond = Some({ + let mut bonds = genesis.transactions.bond.unwrap(); + // NB: the last bond should be from `validator-1`. + // we will filter it out from the list of bonds + bonds.pop(); bonds - .into_iter() - .filter(|bond| { - (&bond.data.validator).as_ref() != "validator-1" - }) - .collect(), - ); + }); genesis }, None, diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 35f5216905..6d4379720a 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -23,15 +23,15 @@ use namada_apps::client::utils::{ self, validator_pre_genesis_dir, validator_pre_genesis_txs_file, }; use namada_apps::config::genesis::toml_utils::read_toml; -use namada_apps::config::genesis::{chain, templates}; +use namada_apps::config::genesis::{templates, GenesisAddress}; use namada_apps::config::{ethereum_bridge, genesis, Config}; use namada_apps::{config, wallet}; +use namada_core::types::address::Address; use namada_core::types::key::{RefTo, SchemeType}; use namada_core::types::string_encoding::StringEncoded; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::wallet::alias::Alias; use namada_tx_prelude::token; -use namada_vp_prelude::HashSet; use once_cell::sync::Lazy; use rand::rngs::OsRng; use rand::Rng; @@ -169,7 +169,7 @@ where .expect("NAM balances should exist in pre-genesis wallet already") .0 .insert( - StringEncoded::new(sk.ref_to()), + GenesisAddress::PublicKey(StringEncoded::new(sk.ref_to())), token::DenominatedAmount { amount: token::Amount::from_uint( 3000000, @@ -251,10 +251,10 @@ fn remove_self_bonds(genesis: &mut templates::All) { bonds .into_iter() .filter(|bond| { - if let genesis::transactions::AliasOrPk::Alias(alias) = + if let genesis::GenesisAddress::EstablishedAddress(address) = &bond.data.source { - *alias != bond.data.validator + Address::Established(address.clone()) != bond.data.validator } else { true } @@ -400,27 +400,23 @@ pub fn network( archive_dir, ); - let genesis_new = chain::Finalized::read_toml_files( - &genesis_dir.join(net.chain_id.as_str()), - ) - .unwrap(); - let validator_aliases = genesis_new + let validator_aliases = templates .transactions .validator_account .as_ref() .map(|txs| { - txs.iter().fold(HashSet::new(), |mut acc, finalized| { - acc.insert(finalized.tx.alias.to_string()); - acc - }) + Either::Right( + // hacky way to get all the validator indexes :-) + (0..txs.len()).map(|index| format!("validator-{index}")), + ) }) - .unwrap_or_default(); + .unwrap_or(Either::Left([].into_iter())); // Setup a dir for every validator and non-validator using their // pre-genesis wallets - for alias in &validator_aliases { + for alias in validator_aliases { let validator_base_dir = - test_dir.path().join(utils::NET_ACCOUNTS_DIR).join(alias); + test_dir.path().join(utils::NET_ACCOUNTS_DIR).join(&alias); // Copy the main wallet from templates dir into validator's base dir. { @@ -440,7 +436,7 @@ pub fn network( } println!("{} {}.", "Joining network with ".yellow(), alias); let validator_base_dir = - test_dir.path().join(utils::NET_ACCOUNTS_DIR).join(alias); + test_dir.path().join(utils::NET_ACCOUNTS_DIR).join(&alias); let mut join_network = run_cmd( Bin::Client, [ @@ -449,7 +445,7 @@ pub fn network( "--chain-id", net.chain_id.as_str(), "--genesis-validator", - alias, + &alias, "--dont-prefetch-wasm", ], Some(5), From 9980166034bde65b2676f9d61bf79fa1edd68aeb Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 16 Nov 2023 13:53:56 +0000 Subject: [PATCH 02/81] Load test established addrs from pre-genesis wallet Additionally, lazy-load the pre-genesis user and validator wallets, such that loading a key during a test does not result in a new I/O operation. --- apps/src/lib/wallet/defaults.rs | 112 +++++++++++++++++--------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index 71be346c70..19c8eda366 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -12,6 +12,7 @@ pub use dev::{ mod dev { use std::collections::HashMap; + use lazy_static::lazy_static; use namada::ledger::{governance, pgf, pos}; use namada::types::address::{ apfel, btc, dot, eth, kartoffel, nam, schnitzel, Address, @@ -19,15 +20,19 @@ mod dev { use namada::types::key::*; use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; + use namada_sdk::wallet::Wallet; + + use crate::wallet::CliWalletUtils; /// Get protocol, eth_bridge, and dkg keys from the validator pre-genesis /// wallet pub fn validator_keys() -> (common::SecretKey, common::SecretKey) { - let protocol_key = get_validator_pre_genesis_wallet() + let protocol_key = VALIDATOR_WALLET .store .validator_keys - .protocol_keypair; - let eth_bridge_key = get_validator_pre_genesis_wallet().eth_hot_key; + .protocol_keypair + .clone(); + let eth_bridge_key = VALIDATOR_WALLET.eth_hot_key.clone(); (protocol_key, eth_bridge_key) } @@ -86,25 +91,30 @@ mod dev { /// An established user address for testing & development pub fn albert_address() -> Address { - Address::decode("tnam1qxgzrwqn9qny9fzd7xnlrdkf7hhj9ecyx5mv3sgw") - .expect("The token address decoding shouldn't fail") + PREGENESIS_WALLET + .find_address("albert") + .expect("Albert's address should be in the pre-genesis wallet") + .into_owned() } /// An established user address for testing & development pub fn bertha_address() -> Address { - Address::decode("tnam1qyctxtpnkhwaygye0sftkq28zedf774xc5a2m7st") - .expect("The token address decoding shouldn't fail") + PREGENESIS_WALLET + .find_address("bertha") + .expect("Bertha's address should be in the pre-genesis wallet") + .into_owned() } /// An established user address for testing & development pub fn christel_address() -> Address { - Address::decode("tnam1q99ylwumqqs5r7uwgmyu7e94n07vjeqr4g970na0") - .expect("The token address decoding shouldn't fail") + PREGENESIS_WALLET + .find_address("christel") + .expect("Christel's address should be in the pre-genesis wallet") + .into_owned() } /// An implicit user address for testing & development pub fn daewon_address() -> Address { - // "tnam1qq83g60hemh00tza9naxmrhg7stz7neqhytnj6l0" (&daewon_keypair().ref_to()).into() } @@ -115,25 +125,21 @@ mod dev { /// An established validator address for testing & development pub fn validator_address() -> Address { - Address::decode("tnam1qxcc0xpgs72z6s5kx9ayvejs3mftf05jkutgz2cc") - .expect("The token address decoding shouldn't fail") + PREGENESIS_WALLET + .find_address("validator-0") + .expect( + "The zeroth validator's address should be in the pre-genesis \ + wallet", + ) + .into_owned() } /// Get an unecrypted keypair from the pre-genesis wallet. pub fn get_unencrypted_keypair(name: &str) -> common::SecretKey { - let mut root_dir = std::env::current_dir() - .expect("Current directory should exist") - .canonicalize() - .expect("Current directory should exist"); - // Find the project root dir - while !root_dir.join("rust-toolchain.toml").exists() { - root_dir.pop(); - } - let path = root_dir.join("genesis/localnet/src/pre-genesis"); - let wallet = crate::wallet::load(&path).unwrap(); - let sk = match wallet.get_secret_keys().get(name).unwrap().0 { + let sk = match PREGENESIS_WALLET.get_secret_keys().get(name).unwrap().0 + { namada_sdk::wallet::StoredKeypair::Encrypted(_) => { - panic!("{}'s keypair should not be encrypted", name) + panic!("{name}'s keypair should not be encrypted") } namada_sdk::wallet::StoredKeypair::Raw(sk) => sk, }; @@ -165,35 +171,37 @@ mod dev { get_unencrypted_keypair("ester") } - /// Get validator pre-genesis wallet - pub fn get_validator_pre_genesis_wallet() -> ValidatorWallet { - let mut root_dir = std::env::current_dir() - .expect("Current directory should exist") - .canonicalize() - .expect("Current directory should exist"); - // Find the project root dir - while !root_dir.join("rust-toolchain.toml").exists() { - root_dir.pop(); - } - let path = - root_dir.join("genesis/localnet/src/pre-genesis/validator-0"); - crate::wallet::pre_genesis::load(&path).unwrap() - } - /// Get the validator consensus keypair from the wallet. pub fn validator_keypair() -> common::SecretKey { - let mut root_dir = std::env::current_dir() - .expect("Current directory should exist") - .canonicalize() - .expect("Current directory should exist"); - // Find the project root dir - while !root_dir.join("rust-toolchain.toml").exists() { - root_dir.pop(); - } - let path = - root_dir.join("genesis/localnet/src/pre-genesis/validator-0"); - - let wallet = crate::wallet::pre_genesis::load(&path).unwrap(); - wallet.consensus_key + VALIDATOR_WALLET.consensus_key.clone() + } + + lazy_static! { + static ref PREGENESIS_WALLET: Wallet = { + let mut root_dir = std::env::current_dir() + .expect("Current directory should exist") + .canonicalize() + .expect("Current directory should exist"); + // Find the project root dir + while !root_dir.join("rust-toolchain.toml").exists() { + root_dir.pop(); + } + let path = root_dir.join("genesis/localnet/src/pre-genesis"); + crate::wallet::load(&path).unwrap() + }; + + static ref VALIDATOR_WALLET: ValidatorWallet = { + let mut root_dir = std::env::current_dir() + .expect("Current directory should exist") + .canonicalize() + .expect("Current directory should exist"); + // Find the project root dir + while !root_dir.join("rust-toolchain.toml").exists() { + root_dir.pop(); + } + let path = + root_dir.join("genesis/localnet/src/pre-genesis/validator-0"); + crate::wallet::pre_genesis::load(&path).unwrap() + }; } } From 8d6fbf8014a52a0d82ad646dbbd68816b103ec80 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 15 Nov 2023 10:39:02 +0000 Subject: [PATCH 03/81] Fix localnet genesis templates --- genesis/localnet/balances.toml | 133 +++++++++--------- genesis/localnet/parameters.toml | 7 +- .../established-account-tx-albert.toml | 6 + .../established-account-tx-bertha.toml | 6 + .../established-account-tx-christel.toml | 6 + .../src/pre-genesis/signed-transactions.toml | 118 ++-------------- .../pre-genesis/unsigned-transactions.toml | 95 ++----------- genesis/localnet/src/pre-genesis/wallet.toml | 32 ++--- 8 files changed, 127 insertions(+), 276 deletions(-) create mode 100644 genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml create mode 100644 genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml create mode 100644 genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml diff --git a/genesis/localnet/balances.toml b/genesis/localnet/balances.toml index 308cea9776..78ddf06758 100644 --- a/genesis/localnet/balances.toml +++ b/genesis/localnet/balances.toml @@ -1,7 +1,8 @@ # Genesis balances. # # This files contains the genesis balances of any tokens present at genesis -# associated with public keys. +# associated with implicit addresses (in the form of public keys) or established +# addresses. # # For example: # ``` @@ -9,102 +10,108 @@ # some_pk_bech32m_encoding = 10 # genesis tokens, the amount can have up to 6 decimal places # ``` # -# The public keys present in here are taken from `pre-genesis/wallet.toml` +# The public keys present in here are taken from `pre-genesis/wallet.toml`. +# As for the established addresses, these are derived from their respective +# genesis tx toml files. [token.NAM] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "2000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "2000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "2000000" +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "2000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "2000000" +tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq = "2000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "2000000" +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "2000000" +tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79 = "2000000" # daewon tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "100000000" # ester tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "200000" +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "200000" +tpknam1qpzrttnzfyt6xfu2vy092eruasll3z52rjfexwapdw0rdww5uktlk3j73dw = "200000" [token.BTC] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "1000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "1000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "1000000" -# daewon +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +# daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" -# ester +# ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "1000000" +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" [token.ETH] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "1000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "1000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "1000000" -# daewon +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +# daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" -# ester +# ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "1000000" +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" [token.DOT] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "1000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "1000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "1000000" -# daewon +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +# daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" -# ester +# ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "1000000" +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" [token.Schnitzel] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "1000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "1000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "1000000" -# daewon +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +# daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" -# ester +# ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "1000000" +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" [token.Apfel] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "1000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "1000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "1000000" -# daewon +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +# daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" -# ester +# ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "1000000" +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" [token.Kartoffel] -# albert-key -tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r = "1000000" -# bertha-key -tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t = "1000000" +# albert +tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +# bertha +tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" # christel -tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu = "1000000" -# daewon +tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +# daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" -# ester +# ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" -# validator-0-key -tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch = "1000000" \ No newline at end of file +# validator-0 +tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" diff --git a/genesis/localnet/parameters.toml b/genesis/localnet/parameters.toml index 238af98bdd..00a721290f 100644 --- a/genesis/localnet/parameters.toml +++ b/genesis/localnet/parameters.toml @@ -91,7 +91,12 @@ min_proposal_grace_epochs = 6 # Public goods funding parameters [pgf_params] # Initial set of stewards -stewards = ["bertha", "validator-0"] +stewards = [ + # bertha + "tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7", + # validator-0 + "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" +] # The pgf funding inflation rate pgf_inflation_rate = "0.1" # The pgf stewards inflation rate diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml new file mode 100644 index 0000000000..8bdc7b04b1 --- /dev/null +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml @@ -0,0 +1,6 @@ +[[established_account]] +vp = "vp_user" + +[established_account.public_key] +pk = "tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" +authorization = "signam1qz39jpapmmxyc99ra8ty74kdx8c0kax3n42z7n5y44e3tcara4ezm3l4llgwgqhp0kgg0kz48d02v2xfeqn5at4h069dddvmlhral7qgm59d37" diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml new file mode 100644 index 0000000000..b6b1611bb5 --- /dev/null +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml @@ -0,0 +1,6 @@ +[[established_account]] +vp = "vp_user" + +[established_account.public_key] +pk = "tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" +authorization = "signam1qqqvnh3e3j8rv6au622yw74jj76wpty7pttlnfussnyffhg8sk07q7g5ehw5hyz67amnvr4afmcr6tcfs6ktylcqrtc0h350j4zd0qgpqwn9z7" diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml new file mode 100644 index 0000000000..2ecbd53966 --- /dev/null +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml @@ -0,0 +1,6 @@ +[[established_account]] +vp = "vp_user" + +[established_account.public_key] +pk = "tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" +authorization = "signam1qqfsz9dvtsan6kqp8zn256gvnuw5hsr27935w63cu0hl30535ewx7tpsf3zypq0hxmjh23z4q4sk7l046n6gjqmvg9ueyxl69lxgzvcfvujj9x" diff --git a/genesis/localnet/src/pre-genesis/signed-transactions.toml b/genesis/localnet/src/pre-genesis/signed-transactions.toml index 29157987cc..9d6baed550 100644 --- a/genesis/localnet/src/pre-genesis/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/signed-transactions.toml @@ -1,126 +1,26 @@ [[established_account]] -alias = "albert" vp = "vp_user" [established_account.public_key] -pk = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -authorization = "signam1qzq6dwf2a9dq4hp3nmxfgckh55mulzryxsfkqhr9uvfvmtr9wt38lyyvzvfqryxnat2a4ry6hygv957z683dyngu03gse2uvl5ldfccyqvdpfr" - -[established_account.storage] +pk = "tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" +authorization = "signam1qz39jpapmmxyc99ra8ty74kdx8c0kax3n42z7n5y44e3tcara4ezm3l4llgwgqhp0kgg0kz48d02v2xfeqn5at4h069dddvmlhral7qgm59d37" [[established_account]] -alias = "bertha" vp = "vp_user" [established_account.public_key] -pk = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -authorization = "signam1qzt58nd7k2mj647x8x4ydhsjkut7dmsl7yjlnrnwu9kzjdch3cljv6dq05mx2kvwn80kjezh7lz26adc5ksvyn3knufymtkhlmnhg3c8rvmxvv" - -[established_account.storage] +pk = "tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" +authorization = "signam1qqqvnh3e3j8rv6au622yw74jj76wpty7pttlnfussnyffhg8sk07q7g5ehw5hyz67amnvr4afmcr6tcfs6ktylcqrtc0h350j4zd0qgpqwn9z7" [[established_account]] -alias = "christel" vp = "vp_user" [established_account.public_key] -pk = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -authorization = "signam1qrf2l6a5u4ywskryhdueudnm50j3kqhujas3mmktenqm89fmlskjnyeskr2tr7js5swmtqqtenkj6ap9xpelx2w40fjjczc4w9xtdggqn3k4vv" - -[established_account.storage] - -[[transfer]] -token = "nam" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qrxfthuz5yzapdt82d8nc656ffrzkxnkfg6fw8w7e6045vcpn8kpr4c536ug4fs2ddz5823dfd3w4qnhcl40qmtasfccaa99a0hvjcqv97vg8n" - -[[transfer]] -token = "btc" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qq826qel8zn5p4dfx25ma45uxeskx0yjlsaxp38uyvehvds6t93c8wkhaydunm4nzz9750dwhkjst00mfrwmu066y3rj6gtk5wjskxq9rksf35" - -[[transfer]] -token = "eth" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qzghddk0u6kpqaxhskrj8ah69d3hce95wndgvlpuyvuw0h0fnyrnp026ltvtfmfhpzywkf88yyhv80hzkus57s9nn6vn3ljvmkkf7zgqtfyyak" - -[[transfer]] -token = "dot" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qqlu8kajh0up5zhewd2h44fhq7zdaapqt9xaj8xp7jmdqpv4dl9d5tesw3wyr9su962rvpkf02x27579vqjk4f4advx7t9ejz74cjrc24m6y4r" - -[[transfer]] -token = "schnitzel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qpukdd6f7pvtgau9v58xxwrnmv5pjra6527aggce9j8cu6fnekzag2cmmf4w0x0cn76gpth2vw6zcsnw37xjuz96hflqfnk5sznxe6cfvwa9m4" - -[[transfer]] -token = "apfel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qprnsyhlruvw4a6jjs98xdd04gxdsgycywzt79gyyak68gn4f50ppdvdm0q6ns44cheu7s43kal0qsldml8s0zexqk3s9zmglu4l6fsz0zx8xa" - -[[transfer]] -token = "kartoffel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qpg4y6ltax3qfclguym2kpa0ezlkf4hz4u3h8fc2ndmuzm4zfe26ygq9u6nhjjvj93n5v296x4arkvjk22ygee2fx054ce7ap6gvynq2nsscys" - -[[transfer]] -token = "nam" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" -signature = "signam1qpe3ms76cxvqsa58624ked5ndjr3jr3u7lf9ypalxdh29gajwmsxtzjxzswk0xmza90u47tgksr0d7enpd730ps7fq5u76l7ekxeyvgfj9fhwt" - -[[transfer]] -token = "btc" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" -signature = "signam1qqq4yc6s3h4ysezcmgsdgyfsqu8mmrwwc8a358sm864zehx74ums8fmgmkyyr3wz9qqrc5fagwrw0a4mgqkkleumc0u6ynshj5ns0tcdz7wtf3" - -[[transfer]] -token = "eth" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" -signature = "signam1qzyqkdxt3cxf0huv0hzskqmuthw38s3kj2sx50znzc68gzx64hrd3hkyx0g27pruwa9fv42suw3k8gkfaelcp4ymnvfw7ltx0mz2z9qrt9gesf" - -[[transfer]] -token = "nam" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" -signature = "signam1qran2jh0dwuwchh45snhk3p9uyxqym57eg527pcjeekwhvpr5j0fy6dte7x3v2dwwc6vqmv49drpvmk6uhzlt2p8rqx0th3h7smf8rqrem03s8" - -[[transfer]] -token = "btc" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" -signature = "signam1qrxp6p56qtvsyp7jfs4jru3mkfv6l4t7hpguur72579pqjaexdry9turgmzwzj6qmua3s2y2a4krtuxwaag66j4dpz44aaqksy30tfcx6f3ez0" - -[[transfer]] -token = "eth" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" -signature = "signam1qrtrec6mps9p7zhmm7sywxwc7zdm32umt0zdwpuflrkh6952rx74amnsf9hn0yughs4e6czgpdmwsmujvkr4ptk2x24vtc8phz0s2asv27zjfs" +pk = "tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" +authorization = "signam1qqfsz9dvtsan6kqp8zn256gvnuw5hsr27935w63cu0hl30535ewx7tpsf3zypq0hxmjh23z4q4sk7l046n6gjqmvg9ueyxl69lxgzvcfvujj9x" [[bond]] -source = "albert" -validator = "validator-0" +source = "tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt" +validator = "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" amount = "20000" -signature = "signam1qpx0kc8rl966vnnswfwhcwxh5vv69p7lev2apc2yephag978gqg5903kuw6dugg9c6pg5jdw048umk72ntympphz0dka74aspldslaqrs6cwg6" +signature = "signam1qz00emwjxmvgjlknc55qffxepur6xlay4zhmnejchw32nuwz2kajmn06hdkmdctu9nrnp3kk0hg6495rsvm5paglyuvyekzcvcxzp2qxvf8hlh" diff --git a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml index 66029a039d..4450ea7289 100644 --- a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml +++ b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml @@ -13,102 +13,23 @@ # Albert [[established_account]] -alias = "albert" vp = "vp_user" -public_key = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" +public_key = "tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" # Bertha [[established_account]] -alias = "bertha" vp = "vp_user" -public_key = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" +public_key = "tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" # Christel [[established_account]] -alias = "christel" vp = "vp_user" -public_key = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" +public_key = "tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" -# Transfer all the Albert's tokens into it's established account: -[[transfer]] -token = "NAM" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "BTC" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "ETH" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "DOT" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "Schnitzel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "Apfel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "Kartoffel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" - -[[transfer]] -token = "NAM" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" - -[[transfer]] -token = "BTC" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" - -[[transfer]] -token = "ETH" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" - -[[transfer]] -token = "NAM" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" - -[[transfer]] -token = "BTC" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" - -[[transfer]] -token = "ETH" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" +########################################################################################## +# Albert bonds to `validator-0` [[bond]] -source = "albert" -validator = "validator-0" -amount = "20000" \ No newline at end of file +source = "tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt" +validator = "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" +amount = "20000" diff --git a/genesis/localnet/src/pre-genesis/wallet.toml b/genesis/localnet/src/pre-genesis/wallet.toml index 14efec25d9..60a328c0c0 100644 --- a/genesis/localnet/src/pre-genesis/wallet.toml +++ b/genesis/localnet/src/pre-genesis/wallet.toml @@ -5,41 +5,41 @@ [payment_addrs] [secret_keys] -albert-key = "unencrypted:0083318ccceac6c08a0177667840b4b93f0e455e45d4c38c28b73b8f8462fbf548" -bertha-key = "unencrypted:0073ed61817720d27784e7a9bca4a60668d763a6f7ecac2d45ed1f241aa5c59e99" -christel-key = "unencrypted:003625b939a58ef60402d7cf8fc04250026cc1ba90cc3028af1ce6b22be857ffc7" +albert-key = "unencrypted:000d5e9d7d66f0e4307edacde6e6578e31d331bcf234352647d00d20955102d3ce" +bertha-key = "unencrypted:0009eedb2e68fd69d6772bf02d92a10f85dfa7eac6fdad0ee589255756b25ab760" +christel-key = "unencrypted:00a08de8d33b9798328d2e4476fade49f515dc82754451fc6ef7060ea0bd1e8f1a" daewon = "unencrypted:00d19e226c0e7d123d79f5908b5948d4c461b66a5f8aa95600c28b55ab6f5dc772" ester = "unencrypted:01369093e2035d84f72a7e5a17c89b7a938b5d08cc87b2289805e3afcc66ef9a42" faucet-key = "unencrypted:00548aa8393422b88dce5f4be8ee0320638061c3e0649ada1b0dacbec4c0c75bb2" -validator-0-balance-key = "unencrypted:000b9c8cb8f3ad6b8a387b064a11c0e98189814e9733aa7bb1e802425f6356f98a" [public_keys] -albert-key = "ED25519_PK_PREFIXtpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -bertha-key = "ED25519_PK_PREFIXtpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -christel-key = "ED25519_PK_PREFIXtpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" +albert-key = "ED25519_PK_PREFIXtpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" +bertha-key = "ED25519_PK_PREFIXtpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" +christel-key = "ED25519_PK_PREFIXtpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" daewon = "ED25519_PK_PREFIXtpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm" ester = "SECP256K1_PK_PREFIXtpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4" faucet-key = "ED25519_PK_PREFIXtpknam1qzh2d8vk9wvj2j63fa3cvjru9uldpdjctjjxpafl5r8vwwf56pdgyq0vra4" -validator-0-balance-key = "ED25519_PK_PREFIXtpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch" [derivation_paths] [addresses] -albert-key = "tnam1qp2yqaffsk2wekd8fheu5c0cv3heg8v37vmdd45u" -bertha-key = "tnam1qqm6sgr63e2zgngh5hlff8r8rennq4vldgdyqdaa" -christel-key = "tnam1qrcgykypppxwx96zkejvhjnda7mn9m3prsz78pkf" +albert = "tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt" +bertha = "tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7" +christel = "tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns" +albert-key = "tnam1qrr8r00nrf6490ff9zgfz52xrnfhdja7rqvldl46" +bertha-key = "tnam1qqc2ge6tg7s03nlshjr5d79dqyjr33aa8g2mxyzm" +christel-key = "tnam1qz0nvec686e9pks8ynhm5ddq8ke7j2eey50uagtr" daewon = "tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn" ester = "tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q" faucet-key = "tnam1qzgcl2znamndmku7ujw6e79dmvd4v7rfd5c89dfz" -validator-0-balance-key = "tnam1qrarncdv96reutgdetkwewdpdyncykaflyzz36vh" +validator-0 = "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" [pkhs] -37A8207A8E54244D17A5FE949C671E6730559F6A = "bertha-key" -544075298594ECD9A74DF3CA61F8646F941D91F3 = "albert-key" +30A4674B47A0F8CFF0BC8746F8AD012438C7BD3A = "bertha-key" +C671BDF31A7552BD2928909151461CD376CBBE18 = "albert-key" 71DA9D35A05AB0E04FD0F99F5AB64B7121B07C35 = "daewon" 918FA853EEE6DDDB9EE49DACF8ADDB1B5678696D = "faucet-key" -F0825881084CE31742B664CBCA6DEFB732EE211C = "christel-key" +9F36671A3EB250DA0724EFBA35A03DB3E92B3925 = "christel-key" 7716860CF696BE44EA6250858FDB3CDEF9F63049 = "ester" -FA39E1AC2E879E2D0DCAECECB9A16927825BA9F9 = "validator-0-balance-key" [address_vp_types] From 89d8e07ee7abd1657513a38e820a31120d2bdd0b Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 17 Nov 2023 15:01:55 +0000 Subject: [PATCH 04/81] Early check of available balance to bond --- proof_of_stake/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 4ab419905b..0026adb25f 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -803,9 +803,7 @@ where return Ok(()); } - let params = read_pos_params(storage)?; - let offset = offset_opt.unwrap_or(params.pipeline_len); - let offset_epoch = current_epoch + offset; + // Transfer the bonded tokens from the source to PoS if let Some(source) = source { if source != validator && is_validator(storage, source)? { return Err( @@ -813,6 +811,15 @@ where ); } } + let source = source.unwrap_or(validator); + tracing::debug!("Source {source} --> Validator {validator}"); + + let staking_token = staking_token_address(storage); + token::transfer(storage, &staking_token, source, &ADDRESS, amount)?; + + let params = read_pos_params(storage)?; + let offset = offset_opt.unwrap_or(params.pipeline_len); + let offset_epoch = current_epoch + offset; // Check that the validator is actually a validator let validator_state_handle = validator_state_handle(validator); @@ -821,9 +828,6 @@ where return Err(BondError::NotAValidator(validator.clone()).into()); } - let source = source.unwrap_or(validator); - tracing::debug!("Source {} --> Validator {}", source, validator); - let bond_handle = bond_handle(source, validator); let total_bonded_handle = total_bonded_handle(validator); @@ -877,10 +881,6 @@ where offset_opt, )?; - // Transfer the bonded tokens from the source to PoS - let staking_token = staking_token_address(storage); - token::transfer(storage, &staking_token, source, &ADDRESS, amount)?; - Ok(()) } From ddb700e07017527a86bc665716605e1b1275a68e Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 17 Nov 2023 15:05:08 +0000 Subject: [PATCH 05/81] Adjust dev genesis --- apps/src/lib/config/genesis.rs | 72 ++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 7e527a50ed..52320210f6 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -317,7 +317,6 @@ pub fn make_dev_genesis( use namada::types::chain::ChainIdPrefix; use namada::types::ethereum_events::EthAddress; use namada::types::key::*; - use namada::types::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::wallet::alias::Alias; use crate::config::genesis::chain::{finalize, DeriveEstablishedAddress}; @@ -356,23 +355,6 @@ pub fn make_dev_genesis( erc20_whitelist: vec![], }); - if let Some(vals) = genesis.transactions.validator_account.as_mut() { - let tx = vals.get_mut(0).unwrap(); - // Use the default validator address - tx.address = defaults::validator_address(); - - // Use the default validator key - have to add it with a sig - tx.address = defaults::validator_address(); - let sk = defaults::validator_keypair(); - let pk = StringEncoded::new(sk.to_public()); - let unsigned_tx = UnsignedValidatorAccountTx::from(&tx.tx); - let sig = transactions::sign_tx(&unsigned_tx, &sk); - tx.tx.account_key = transactions::SignedPk { - pk, - authorization: sig, - }; - } - // Use the default token address for matching tokens let default_tokens: HashMap = defaults::tokens() .into_iter() @@ -404,6 +386,35 @@ pub fn make_dev_genesis( )); bonds.retain(|bond| bond.source != fat_alberts_address); }; + // fetch validator's balances + let (first_val_balance, first_val_bonded) = { + let nam_balances = genesis + .balances + .token + .get_mut(&Alias::from_str("nam").unwrap()) + .unwrap(); + + let tx = genesis + .transactions + .validator_account + .as_ref() + .unwrap() + .get(0) + .unwrap(); + let genesis_addr = GenesisAddress::EstablishedAddress( + UnsignedValidatorAccountTx::from(&tx.tx) + .derive_established_address(), + ); + + let balance = *nam_balances.0.get(&genesis_addr).unwrap(); + let bonded = { + let bond = + genesis.transactions.bond.as_mut().unwrap().get(0).unwrap(); + bond.amount + }; + + (balance, bonded) + }; let secp_eth_cold_keypair = secp256k1::SecretKey::try_from_slice(&[ 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, @@ -429,7 +440,7 @@ pub fn make_dev_genesis( common::SecretKey::try_from_sk(&secp_eth_cold_keypair).unwrap(); let (protocol_keypair, eth_bridge_keypair) = defaults::validator_keys(); // add the validator - let validator_address = if let Some(vals) = + let (validator_address, account_pk) = if let Some(vals) = genesis.transactions.validator_account.as_mut() { let validator_account_tx = transactions::ValidatorAccountTx { @@ -486,10 +497,26 @@ pub fn make_dev_genesis( eth_cold_key: sign_pk(ð_cold_keypair), }, }); - validator_address + (validator_address, account_keypair.ref_to()) } else { unreachable!() }; + // credit nam tokens to validators such that they can bond + { + let nam_balances = genesis + .balances + .token + .get_mut(&Alias::from_str("nam").unwrap()) + .unwrap(); + + let validator_addr = + GenesisAddress::EstablishedAddress(validator_address.clone()); + let account_pk = + GenesisAddress::PublicKey(StringEncoded::new(account_pk)); + + nam_balances.0.insert(validator_addr, first_val_balance); + nam_balances.0.insert(account_pk, first_val_balance); + } // self bond if let Some(bonds) = genesis.transactions.bond.as_mut() { bonds.push(transactions::BondTx { @@ -497,10 +524,7 @@ pub fn make_dev_genesis( validator_address.clone(), ), validator: Address::Established(validator_address), - amount: token::DenominatedAmount { - amount: token::Amount::native_whole(100_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, + amount: first_val_bonded, }) } } From cae69500bdb1a0c1e139bded606630cf5193d3ed Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 17 Nov 2023 15:36:25 +0000 Subject: [PATCH 06/81] Fix PoS rewards rounding errors in unit tests --- apps/src/lib/node/ledger/shell/finalize_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 257b766a03..d7c35e8bd9 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -2545,8 +2545,8 @@ mod test_finalize_block { (Dec::one() - stake_ratio) * inflation_3 + commission; let exp_del_reward = del_rewards_no_commission - commission; - assert_eq!(exp_val_reward, val_reward_2); - assert_eq!(exp_del_reward, del_reward_1); + assert!(is_reward_equal_enough(exp_val_reward, val_reward_2, 1)); + assert!(is_reward_equal_enough(exp_del_reward, del_reward_1, 1)); } /// A unit test for changing consensus keys and communicating to CometBFT From 77fd80f8584c803db0ced86866e5ae6e1078c721 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 15 Nov 2023 14:22:21 +0000 Subject: [PATCH 07/81] CLI utils subcmd to generate established account genesis tx --- apps/src/lib/cli.rs | 54 +++++++++++++++++++ apps/src/lib/cli/client.rs | 3 ++ apps/src/lib/client/utils.rs | 60 +++++++++++++++++++++ apps/src/lib/config/genesis/transactions.rs | 19 +++++++ 4 files changed, 136 insertions(+) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index fbc24acf65..a5132d78f3 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2179,6 +2179,7 @@ pub mod cmds { FetchWasms(FetchWasms), ValidateWasm(ValidateWasm), InitNetwork(InitNetwork), + InitGenesisEstablishedAccount(InitGenesisEstablishedAccount), InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), @@ -2199,6 +2200,8 @@ pub mod cmds { SubCmd::parse(matches).map(Self::ValidateWasm); let init_network = SubCmd::parse(matches).map(Self::InitNetwork); + let init_established = SubCmd::parse(matches) + .map(Self::InitGenesisEstablishedAccount); let init_genesis = SubCmd::parse(matches).map(Self::InitGenesisValidator); let pk_to_tm_address = @@ -2214,6 +2217,7 @@ pub mod cmds { .or(fetch_wasms) .or(validate_wasm) .or(init_network) + .or(init_established) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) @@ -2230,6 +2234,7 @@ pub mod cmds { .subcommand(FetchWasms::def()) .subcommand(ValidateWasm::def()) .subcommand(InitNetwork::def()) + .subcommand(InitGenesisEstablishedAccount::def()) .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) @@ -2320,6 +2325,29 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct InitGenesisEstablishedAccount( + pub args::InitGenesisEstablishedAccount, + ); + + impl SubCmd for InitGenesisEstablishedAccount { + const CMD: &'static str = "init-genesis-established-account"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + Self(args::InitGenesisEstablishedAccount::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Initialize an established account available at genesis.", + ) + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator(pub args::InitGenesisValidator); @@ -3084,6 +3112,7 @@ pub mod args { pub const VERIFICATION_KEY: ArgOpt = arg_opt("verification-key"); pub const VIEWING_KEY: Arg = arg("key"); + pub const VP: ArgOpt = arg_opt("vp"); pub const WALLET_ALIAS_FORCE: ArgFlag = flag("wallet-alias-force"); pub const WASM_CHECKSUMS_PATH: Arg = arg("wasm-checksums-path"); pub const WASM_DIR: ArgOpt = arg_opt("wasm-dir"); @@ -6697,6 +6726,31 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct InitGenesisEstablishedAccount { + pub vp: String, + pub wallet_alias: String, + } + + impl Args for InitGenesisEstablishedAccount { + fn parse(matches: &ArgMatches) -> Self { + let wallet_alias = ALIAS.parse(matches); + let vp = VP.parse(matches).unwrap_or_else(|| "vp_user".to_string()); + Self { wallet_alias, vp } + } + + fn def(app: App) -> App { + app.arg( + ALIAS + .def() + .help("The alias of the key to use from the wallet."), + ) + .arg(VP.def().help( + "The validity predicate of the account. Defaults to `vp_user`.", + )) + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator { pub alias: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 4d56c1e9c1..449fb2f839 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -634,6 +634,9 @@ impl CliApi { Utils::InitNetwork(InitNetwork(args)) => { utils::init_network(global_args, args) } + Utils::InitGenesisEstablishedAccount( + InitGenesisEstablishedAccount(args), + ) => utils::init_genesis_established_account(global_args, args), Utils::InitGenesisValidator(InitGenesisValidator(args)) => { utils::init_genesis_validator(global_args, args) } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index a73898be0c..bb0b9c587b 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use borsh_ext::BorshSerializeExt; +use color_eyre::owo_colors::OwoColorize; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; @@ -587,6 +588,56 @@ pub fn default_base_dir( ); } +/// Initialize a genesis established account. +/// key into a special "pre-genesis" wallet. +pub fn init_genesis_established_account( + global_args: args::Global, + args: args::InitGenesisEstablishedAccount, +) { + let (mut pre_genesis_wallet, _) = + load_pre_genesis_wallet_or_exit(&global_args.base_dir); + + let alias = args.wallet_alias.as_str(); + let public_key = { + let sk = pre_genesis_wallet + .find_secret_key(alias, None) + .unwrap_or_else(|err| { + eprintln!( + "Failed to look-up `{alias}` in the pre-genesis wallet: \ + {err}", + ); + safe_exit(1) + }); + sk.ref_to() + }; + + let (address, txs) = genesis::transactions::init_established_account( + args.vp, + public_key, + &mut pre_genesis_wallet, + ); + let toml_path = { + let pre_genesis_dir = global_args.base_dir.join(PRE_GENESIS_DIR); + established_acc_pre_genesis_txs_file(alias, &pre_genesis_dir) + }; + let toml_path_str = toml_path.to_string_lossy(); + + let genesis_part = toml::to_string(&txs).unwrap(); + fs::write(&toml_path, genesis_part).unwrap_or_else(|err| { + eprintln!( + "Couldn't write pre-genesis transactions file to {toml_path_str}. \ + Failed with: {err}", + ); + safe_exit(1) + }); + + println!( + "{}: {address}", + "Derived established account address".bold() + ); + println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); +} + /// Initialize genesis validator's address, consensus key and validator account /// key into a special "pre-genesis" wallet. pub fn init_genesis_validator( @@ -760,6 +811,15 @@ pub fn validator_pre_genesis_txs_file(pre_genesis_path: &Path) -> PathBuf { pre_genesis_path.join("transactions.toml") } +/// The default path to an established account txs file. +pub fn established_acc_pre_genesis_txs_file( + wallet_key_alias: &str, + pre_genesis_path: &Path, +) -> PathBuf { + pre_genesis_path + .join(format!("established-account-tx-{wallet_key_alias}.toml")) +} + /// The default validator pre-genesis directory pub fn validator_pre_genesis_dir(base_dir: &Path, alias: &str) -> PathBuf { base_dir.join(PRE_GENESIS_DIR).join(alias) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 388f707b23..6495da6a6f 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -100,6 +100,25 @@ pub fn parse_unsigned( toml::from_slice(bytes) } +/// Create signed [`Transactions`] for an established account. +pub fn init_established_account( + vp: String, + public_key: common::PublicKey, + wallet: &mut Wallet, +) -> (Address, Transactions) { + let unsigned_tx = EstablishedAccountTx { + vp, + public_key: Some(StringEncoded::new(public_key)), + }; + let address = unsigned_tx.derive_address(); + let signed_tx = sign_established_account_tx(unsigned_tx, wallet); + let txs = Transactions { + established_account: Some(vec![signed_tx]), + ..Default::default() + }; + (address, txs) +} + /// Create signed [`Transactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { From 78fbee71238b42ef75e89787f2c09776d86d5c4f Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 15 Nov 2023 14:38:53 +0000 Subject: [PATCH 08/81] Rework CLI utils subcmd to generate validator account genesis tx --- apps/src/lib/client/utils.rs | 25 ++++++++------------- apps/src/lib/config/genesis/transactions.rs | 15 +++++++------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index bb0b9c587b..0b3c1c8a16 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -693,7 +693,7 @@ pub fn init_genesis_validator( pre_genesis::validator_file_name(&pre_genesis_dir).to_string_lossy() ); - let transactions = genesis::transactions::init_validator( + let (address, transactions) = genesis::transactions::init_validator( genesis::transactions::GenesisValidatorData { commission_rate, max_commission_rate_change, @@ -706,27 +706,20 @@ pub fn init_genesis_validator( }, &validator_wallet, ); + let toml_path = validator_pre_genesis_txs_file(&pre_genesis_dir); + let toml_path_str = toml_path.to_string_lossy(); let genesis_part = toml::to_string(&transactions).unwrap(); - println!("Your public signed pre-genesis transactions TOML:"); - println!(); - println!("{genesis_part}"); - - let file_name = validator_pre_genesis_txs_file(&pre_genesis_dir); - fs::write(&file_name, genesis_part).unwrap_or_else(|err| { + fs::write(&toml_path, genesis_part).unwrap_or_else(|err| { eprintln!( - "Couldn't write pre-genesis transactions file to {}. Failed with: \ - {}", - file_name.to_string_lossy(), - err + "Couldn't write pre-genesis transactions file to {toml_path_str}. \ + Failed with: {err}", ); safe_exit(1) }); - println!(); - println!( - "Pre-genesis transactions TOML written to {}", - file_name.to_string_lossy() - ); + + println!("{}: {address}", "Derived validator account address".bold()); + println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } /// Try to load a pre-genesis wallet or terminate if it cannot be found. diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 6495da6a6f..56c75687dc 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -132,7 +132,7 @@ pub fn init_validator( discord_handle, }: GenesisValidatorData, validator_wallet: &ValidatorWallet, -) -> Transactions { +) -> (Address, Transactions) { let unsigned_validator_account_tx = UnsignedValidatorAccountTx { account_key: StringEncoded::new(validator_wallet.account_key.ref_to()), consensus_key: StringEncoded::new( @@ -164,8 +164,6 @@ pub fn init_validator( net_address, }; let unsigned_validator_addr = - unsigned_validator_account_tx.derive_address(); - let unsigned_validator_established_addr = unsigned_validator_account_tx.derive_established_address(); let validator_account = Some(vec![sign_validator_account_tx( unsigned_validator_account_tx, @@ -177,20 +175,23 @@ pub fn init_validator( } else { let unsigned_bond_tx = BondTx { source: GenesisAddress::EstablishedAddress( - unsigned_validator_established_addr, + unsigned_validator_addr.clone(), ), - validator: unsigned_validator_addr, + validator: Address::Established(unsigned_validator_addr.clone()), amount: self_bond_amount, }; let bond_tx = sign_self_bond_tx(unsigned_bond_tx, validator_wallet); Some(vec![bond_tx]) }; - Transactions { + let address = Address::Established(unsigned_validator_addr); + let txs = Transactions { validator_account, bond, ..Default::default() - } + }; + + (address, txs) } pub fn sign_established_account_tx( From 3dbb4e897193f4aa5cf5af30ca13f66d8d886ee5 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 15 Nov 2023 15:55:33 +0000 Subject: [PATCH 09/81] CLI utils subcmd to derive genesis account addrs --- apps/src/lib/cli.rs | 40 ++++++++++++ apps/src/lib/cli/client.rs | 3 + apps/src/lib/client/utils.rs | 119 +++++++++++++++++++++++++++++++---- 3 files changed, 150 insertions(+), 12 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index a5132d78f3..dbddc20565 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2179,6 +2179,7 @@ pub mod cmds { FetchWasms(FetchWasms), ValidateWasm(ValidateWasm), InitNetwork(InitNetwork), + DeriveGenesisAddresses(DeriveGenesisAddresses), InitGenesisEstablishedAccount(InitGenesisEstablishedAccount), InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), @@ -2200,6 +2201,8 @@ pub mod cmds { SubCmd::parse(matches).map(Self::ValidateWasm); let init_network = SubCmd::parse(matches).map(Self::InitNetwork); + let derive_addresses = + SubCmd::parse(matches).map(Self::DeriveGenesisAddresses); let init_established = SubCmd::parse(matches) .map(Self::InitGenesisEstablishedAccount); let init_genesis = @@ -2217,6 +2220,7 @@ pub mod cmds { .or(fetch_wasms) .or(validate_wasm) .or(init_network) + .or(derive_addresses) .or(init_established) .or(init_genesis) .or(pk_to_tm_address) @@ -2234,6 +2238,7 @@ pub mod cmds { .subcommand(FetchWasms::def()) .subcommand(ValidateWasm::def()) .subcommand(InitNetwork::def()) + .subcommand(DeriveGenesisAddresses::def()) .subcommand(InitGenesisEstablishedAccount::def()) .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) @@ -2325,6 +2330,25 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct DeriveGenesisAddresses(pub args::DeriveGenesisAddresses); + + impl SubCmd for DeriveGenesisAddresses { + const CMD: &'static str = "derive-genesis-addresses"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + Self(args::DeriveGenesisAddresses::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Derive account addresses from a genesis txs toml file.") + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct InitGenesisEstablishedAccount( pub args::InitGenesisEstablishedAccount, @@ -6726,6 +6750,22 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct DeriveGenesisAddresses { + pub genesis_txs_path: PathBuf, + } + + impl Args for DeriveGenesisAddresses { + fn parse(matches: &ArgMatches) -> Self { + let genesis_txs_path = PATH.parse(matches); + Self { genesis_txs_path } + } + + fn def(app: App) -> App { + app.arg(PATH.def().help("Path to the genesis txs toml file.")) + } + } + #[derive(Clone, Debug)] pub struct InitGenesisEstablishedAccount { pub vp: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 449fb2f839..2f06a497d4 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -634,6 +634,9 @@ impl CliApi { Utils::InitNetwork(InitNetwork(args)) => { utils::init_network(global_args, args) } + Utils::DeriveGenesisAddresses(DeriveGenesisAddresses(args)) => { + utils::derive_genesis_addresses(global_args, args) + } Utils::InitGenesisEstablishedAccount( InitGenesisEstablishedAccount(args), ) => utils::init_genesis_established_account(global_args, args), diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 0b3c1c8a16..4f4b88a7e5 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -22,6 +22,7 @@ use sha2::{Digest, Sha256}; use crate::cli::args; use crate::cli::context::ENV_VAR_WASM_DIR; +use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::global::GlobalConfig; use crate::config::{ self, genesis, get_default_namada_folder, Config, TendermintMode, @@ -588,6 +589,95 @@ pub fn default_base_dir( ); } +/// Derive and print all established addresses from the provided +/// genesis txs toml file. +pub fn derive_genesis_addresses( + global_args: args::Global, + args: args::DeriveGenesisAddresses, +) { + let maybe_pre_genesis_wallet = + try_load_pre_genesis_wallet(&global_args.base_dir) + .map(|(wallet, _)| wallet); + + let genesis_txs = + genesis::templates::read_transactions(&args.genesis_txs_path).unwrap(); + + println!("{}", "Established account txs:".underline().bold()); + for tx in genesis_txs + .established_account + .as_ref() + .into_iter() + .flatten() + { + let address = { + let unsigned = + genesis::transactions::UnsignedEstablishedAccountTx::from(tx); + unsigned.derive_address() + }; + let pk = if let Some(public_key) = tx.public_key.as_ref() { + &public_key.pk.raw + } else { + continue; + }; + + println!(); + println!("{} {address}", "Address:".bold().bright_green()); + println!("{} {pk}", "Public key:".bold().bright_green()); + + let maybe_alias = + maybe_pre_genesis_wallet.as_ref().and_then(|wallet| { + let implicit_address = pk.into(); + wallet.find_alias(&implicit_address) + }); + + if let Some(alias) = maybe_alias { + println!("{} {alias}", "Wallet alias:".bold().bright_green()); + } + } + if genesis_txs + .established_account + .as_ref() + .map(|txs| txs.is_empty()) + .unwrap_or(true) + { + println!(); + println!("{}", "".dimmed()); + } + + println!(); + + println!("{}", "Validator account txs:".underline().bold()); + for tx in genesis_txs.validator_account.as_ref().into_iter().flatten() { + let address = { + let unsigned = + genesis::transactions::UnsignedValidatorAccountTx::from(tx); + unsigned.derive_address() + }; + println!(); + println!("{} {address}", "Address:".bold().bright_green()); + let keys = [ + ("Account key:", &tx.account_key.pk.raw), + ("Consensus key:", &tx.consensus_key.pk.raw), + ("Protocol key:", &tx.protocol_key.pk.raw), + ("Tendermint node key:", &tx.tendermint_node_key.pk.raw), + ("Ethereum hot key:", &tx.eth_hot_key.pk.raw), + ("Ethereum cold key:", &tx.eth_cold_key.pk.raw), + ]; + for (description, key) in keys { + println!("{} {key}", description.bold().bright_green()); + } + } + if genesis_txs + .validator_account + .as_ref() + .map(|txs| txs.is_empty()) + .unwrap_or(true) + { + println!(); + println!("{}", "".dimmed()); + } +} + /// Initialize a genesis established account. /// key into a special "pre-genesis" wallet. pub fn init_genesis_established_account( @@ -722,22 +812,27 @@ pub fn init_genesis_validator( println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } +/// Try to load a pre-genesis wallet or return nothing, +/// if it cannot be found. +pub fn try_load_pre_genesis_wallet( + base_dir: &Path, +) -> Option<(Wallet, PathBuf)> { + let pre_genesis_dir = base_dir.join(PRE_GENESIS_DIR); + + crate::wallet::load(&pre_genesis_dir).map(|wallet| { + let wallet_file = crate::wallet::wallet_file(&pre_genesis_dir); + (wallet, wallet_file) + }) +} + /// Try to load a pre-genesis wallet or terminate if it cannot be found. pub fn load_pre_genesis_wallet_or_exit( base_dir: &Path, ) -> (Wallet, PathBuf) { - let pre_genesis_dir = base_dir.join(PRE_GENESIS_DIR); - let wallet_file = crate::wallet::wallet_file(&pre_genesis_dir); - ( - crate::wallet::load(&pre_genesis_dir).unwrap_or_else(|| { - eprintln!( - "No pre-genesis wallet found at {}.", - wallet_file.to_string_lossy() - ); - safe_exit(1) - }), - wallet_file, - ) + try_load_pre_genesis_wallet(base_dir).unwrap_or_else(|| { + eprintln!("No pre-genesis wallet found.",); + safe_exit(1) + }) } async fn download_file(url: impl AsRef) -> reqwest::Result { From 682bc05a282239f988d0ae6dc957419b40f07b30 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 20 Nov 2023 12:55:24 +0000 Subject: [PATCH 10/81] Update masp proofs --- ...14612B09451DF258B1B593CFED41B0E9256A2E.bin | Bin 0 -> 24494 bytes ...EAD637C92544211E7B3765CA7AA186FB1E409.bin} | Bin 7448 -> 7448 bytes ...4E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin | Bin 0 -> 7448 bytes ...37D60B440C71729F148923838380D5CFE71A9.bin} | Bin 9941 -> 9941 bytes ...9402D995E6F74FFBF5CE3BA13049D272C5E752.bin | Bin 0 -> 9941 bytes ...FE78D0FC876E11ADD563D149168D4BDE86FBB8.bin | Bin 0 -> 7448 bytes ...6BC11A30C23464FD05B13516E0B576456D6BE9.bin | Bin 0 -> 9649 bytes ...6D2AEEF55A6F202D8EB4564017DB5BEF872107.bin | Bin 0 -> 15597 bytes ...98CB65B7F71F737870C86AEA55454B8B32DC2D.bin | Bin 0 -> 7448 bytes ...8B21F6D7D9EE1DA5A6E60E9A37CD30DA4A505F.bin | Bin 0 -> 7448 bytes ...99481AB5F7C8F57502C5EAB2BF7594EA6CED8F.bin | Bin 0 -> 15257 bytes ...699E65D4B40A4E1BC9E7A32D63CF28466A2F20.bin | Bin 0 -> 17018 bytes ...79048F119C09D85335704A083700CA11415DF3.bin | Bin 0 -> 10382 bytes 13 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test_fixtures/masp_proofs/0AB9DC2FED12A93FDA42447E6A14612B09451DF258B1B593CFED41B0E9256A2E.bin rename test_fixtures/masp_proofs/{90502CEAE917AE038FDA2337AFB0464F2130123149D5691B85C35AABC49FA2BD.bin => 25FA248C27E805F28102B6E3DCFEAD637C92544211E7B3765CA7AA186FB1E409.bin} (52%) create mode 100644 test_fixtures/masp_proofs/2E78CC40C345D7EA50407110784E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin rename test_fixtures/masp_proofs/{B947FEFC6EC94A041657FF3A9420D5BB3FDEB1199EF712C059C80C4DBF6F3ED8.bin => 76BADC1367FEF1062029B180AD637D60B440C71729F148923838380D5CFE71A9.bin} (58%) create mode 100644 test_fixtures/masp_proofs/8DAE054DE58BDFFE4F551092B09402D995E6F74FFBF5CE3BA13049D272C5E752.bin create mode 100644 test_fixtures/masp_proofs/96CBC406931DDBC76053B417CCFE78D0FC876E11ADD563D149168D4BDE86FBB8.bin create mode 100644 test_fixtures/masp_proofs/ABB08197C40226F64D449C7F116BC11A30C23464FD05B13516E0B576456D6BE9.bin create mode 100644 test_fixtures/masp_proofs/AE4CEC9192B52E8CE5AB0C25936D2AEEF55A6F202D8EB4564017DB5BEF872107.bin create mode 100644 test_fixtures/masp_proofs/B74C1EC89D48DC01F71DA402F898CB65B7F71F737870C86AEA55454B8B32DC2D.bin create mode 100644 test_fixtures/masp_proofs/CA6AF0286DB05F3AB2513109958B21F6D7D9EE1DA5A6E60E9A37CD30DA4A505F.bin create mode 100644 test_fixtures/masp_proofs/D007D1734AD42D34174F04566899481AB5F7C8F57502C5EAB2BF7594EA6CED8F.bin create mode 100644 test_fixtures/masp_proofs/DEDF664AD06184041515677A72699E65D4B40A4E1BC9E7A32D63CF28466A2F20.bin create mode 100644 test_fixtures/masp_proofs/FD874BACE965B637E528BF66B079048F119C09D85335704A083700CA11415DF3.bin diff --git a/test_fixtures/masp_proofs/0AB9DC2FED12A93FDA42447E6A14612B09451DF258B1B593CFED41B0E9256A2E.bin b/test_fixtures/masp_proofs/0AB9DC2FED12A93FDA42447E6A14612B09451DF258B1B593CFED41B0E9256A2E.bin new file mode 100644 index 0000000000000000000000000000000000000000..7524753fec47fdf4c0e73b22036c7a19f82254b9 GIT binary patch literal 24494 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

JDQtI*;I|w*r8`pBru!%!` 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 M|MT}xU#DpNH?ielt^fc4 literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/90502CEAE917AE038FDA2337AFB0464F2130123149D5691B85C35AABC49FA2BD.bin b/test_fixtures/masp_proofs/25FA248C27E805F28102B6E3DCFEAD637C92544211E7B3765CA7AA186FB1E409.bin similarity index 52% rename from test_fixtures/masp_proofs/90502CEAE917AE038FDA2337AFB0464F2130123149D5691B85C35AABC49FA2BD.bin rename to test_fixtures/masp_proofs/25FA248C27E805F28102B6E3DCFEAD637C92544211E7B3765CA7AA186FB1E409.bin index 44754a7a468bd33137f1d504b5f7e3d54f8ed301..60aa5777a44a62800525dddf8d444f2de76bccf5 100644 GIT binary patch delta 2133 zcmbPXHN&c&k%56h6o^@X*x}BE6;l4&&IMcTSnhLJk6U8O#oARFy;m%Y?8J}$`%<*_x?28M^PJjUpRDk<=2xD$byc-xG`sw+#cuIw+xz!%GO$*6oiJ8f zW$9}5oM8*d3NS#^TMyRUVG?+;M%{lI$6e0_C!fvN-hQcR)tguA@|<2XY0O-|{wxm7 zV7)5q0!sV5d{?eH$K9jTn^>wclVc*cXsqD6l7N#Z#V^mnrD50uUzT{mGh) zo+^YCfx>7KyX)rLj6O^XgaT#qMo!Po6Ig&M355Vyr7Js7B_aQA-pE-p;GQB#*iXi=-sS44xCBXbnWquD=}Mp&qL7r zXx;CF2gEWbmkXFEoaJ;17I*FL(JVIHenXI{Qnlb?q+o8`2Sy9NU>DPXj?Je9fcC4n zNgUdHC&eYBQGd_#*XgX2e+vaT96X(MA&D`3vfF(Xw~Fp>Dm)=detT82r_7O=aI@eF z$D-MMQpwh0T|Cns6(86oYvT6#biDj&^@EdUWPS6Tc>KNp{{r+gzgJ4_q;P zxI=tvL8a1NSG74384S7(S@(K6{hK%67Y5pYb5gr~#p7Vn#?a2;IMYn zLftqO{WzsBhdmC6dR;$Y-eF%gJzs9a+gy$ynnE!nCz@2%0z$tj8Nx~I1t zHK>y?IHz^j^!uY3*^}psnJBb#Eo$hzG$;C+>V-W!#S2&tEI!(9V)42A?8+C06I_y3 zZ2l|;w7=eP_Txjp+h^C_;`_L}y6nPaWywIosXpga$A2U+GokI{r~b-K&Z#zBjb3F} z-7S}IX7m=1KWz7{^b%8ezF*58jonONCOG6xcp!f<^xJZ=M;+_a|FXXO-LJZF+f&Z8 z6{j@T9%xkw4p`L6wfcO0Myj?;@7j>368Bs8FYz$XuV(#Lbh28Lbr=7w8Ru`@?<|vJ zC^D_sarMy`Po7e+W|#R3J%Z zMpX8nYxa_vccQfBbFAyKCK_=Dx1`Ca?QKG-kJxRo&^PU@5S;a-RoG#+wkOztAc#y?#ogZub<^{ zd8cHWZ$ISv^S_!`jQT!#Ro>q*r(aEHsz1DW=hWsb-L0qdU!L9>m2qYTlkV5}TpM-Y z$6OWyHv>jiw?Cim}kUo z)y7 zYex0)yo=o#40I>h#VO>b)qGwcrY*UiDNm?>L*=fo8GPCP{SJB0@~mIOVWPQBYzG1RK)R7dMFS|XKO?J3kZa3YTwB>uZ;WhIZ?JI4o-RO!kr44Sw!S z_OIvT$eO>Un&|Gws=%l}k(KljM0hh@B6`DvrjYeVt&qpFe9Pj@VOeR|jKdDyJRcUhYn*HVTlq}mM uB`&{?ZV+s~agSZ*K(emVnf?c=Un5uV+Bj#@yL%hiLG=g-uuOg{qYVI3M!&iM delta 2133 zcmc(gc{JPU8pnf>)EbmFMVqR%v_q|-y(L5iwIsyac2HAP8>TaM;<$9EE%npVpw_B2 zw5g<#RxEANWok>bQd@)SHbE>YS50*O>6v@ZbTRgRgo1@ z3oeS#NvuUlg=AwXe2;3+`U^iBQb8bZ+7VIa0!NZhk;1 z1^R5>>o^Yt%b6k?(6hptma6dDY)GsH*}<7aZFR)C(|Wsohq0b}U?-?_IfOXuD}=87 z3x6=gyBsH>ZY|^i?-bl^&`#Iv9qX(HIxbyfFSH6$f~DvDkyElOi3tB*wLg3mD&5|+ z{q_;X^h;Ol>+~nRq~*v95s46CePU;eIpabg5Cbzs#2xYlcZ`IFQ1Wk2*4%{Q#-unr zIOyHy+#9%0*vf%S>NA*Velj7`d~vgSxyhd+z?(l$1=f|^ZG?F~75Aat8bfhfKh&G8 zBp{1yJx|4FCzOiGU4WfYVmHt;IYcq(s52QVT7b>hXjJijoY!zK-R4j{``BxI; znIxuhq|Wlmr?AOGKJe?y_pE_3Ra1m+PFSnlw7y(b-my`8DH^`}`k2gM5VnhBI~Cnp zh$c`4s-pR7q3u4>%w=8{f8tJ;>dgV*0Ni8yS0C{-WAnSZJ4>NAmuCf5QEeI(w3VMM zFg4u-S8y_^GYR@n54cvI_k!*^xNoHL?6mzz)RNnLxp&|Cvv$FoN+iteHv>wOf3QUT zg{v?3&PlaB8`mHamkbBWMdTB%Pj=61_rt4AN`Y0DLxf@BW`7 z7ruDAJ%UR#HyD{8WOm8of>^0Vt;_y@%%rWCXOM#j>AQslMqZ!qIOdCD$h=u&;(-y*y_f4(&7j!B@9>EXSKY--8IBThumYYd0p33W8uWEYMp8P zN_pDFk-5-O$;_PuJijO7Y4x=6%ZU$^SfNHT*rDG~`&hqGz&^v&_qXQd3@U@embg(W zH?;l{Xng;HyG&CGbWx#1JF|U(UN@aGic0OMh&D{87&?n@B*+&$tn0E<%{KD11=MTZ zdZ0$HT|e4BJ^0U6`HlQYz2s#xz$TCxS^E2EbR%Zm zQ*f-VjG~Zx#4~7*d_iMGl(kgdt~R3Ep2ZV>%MBlh-4EsSTs)q~2f*1KJxidSQ#!^_ zNjX^usk~7?X*$bl;mxlh-N%PcLQ{82Q=YS;%e><$tcKZO{lb3mF9R2m{({+(>Xyy( zKC}|J!i+JNz^g!b{}gbgJA3`4$;-#?2aDT6I(ZD9xYDB9&;`4ct43b0YWvw26@y&Z zLo{n*lJFcrCy$S5fgg=?Ei|QY^fLbZEd~}BQNF{%;>*zGUc~rX9c*ynw5gg*Pue-4 zGTuEyIdaYFu9#cBU6zYmZ<&{Unyr)bgvao&F=p((s2Uw@**URKx;qR4kZ+Gq(iu>?!Ch zDRFdt6pvw<9ezi$gh0r<@Z5}aO&%1L`e@cgMR6vv+x4WccVw2vda(f#t4ESzhZPQ^ zJHweOQy&L(Y(H7zJwIqYPO~0`?j5*+{u(qxN-|lZTEw{QTADh%^^JThFz{L3;7`hB B6g>a{ diff --git a/test_fixtures/masp_proofs/2E78CC40C345D7EA50407110784E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin b/test_fixtures/masp_proofs/2E78CC40C345D7EA50407110784E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin new file mode 100644 index 0000000000000000000000000000000000000000..84a26fb9c53d6d0fb35ffc72b4793cddd2bf5ba0 GIT binary patch literal 7448 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 P2TJWtK3~S>io*W```{Q& delta 3321 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 literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/96CBC406931DDBC76053B417CCFE78D0FC876E11ADD563D149168D4BDE86FBB8.bin b/test_fixtures/masp_proofs/96CBC406931DDBC76053B417CCFE78D0FC876E11ADD563D149168D4BDE86FBB8.bin new file mode 100644 index 0000000000000000000000000000000000000000..8c4d3c8e9b19a4a18ab50b12050c676db892002d GIT binary patch literal 7448 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%0Aya*| zvj}fgz)VP+@j6F0yh0#fzg=3!QglDn=w(c3Vr0uAbmdz4+Zaf=)9Y@lZ;MC;yd zOl^QO1`IU5gpQN-2Km=sHD!j_(vRa1vhLM?5<{Eq9+-)4gufH=9JaD8(xZ_{MV~|j za;g=%OTV10QIDfERRU{A}tbVIc?EWyNJ1KQtdh;o%p3mn_IQLxbHx!lf=#Zuu&CJ;*n&3 z762Y3nAHLa#X9$egH)lLRgsN7jCz)f4khy2oz<0HaczJ77SaLD3)`EVAspHHiDjj6B9-0{$e*nS<#*RZNnB;Q3}|oSREu$8{I;d>%Nge{M}7a-5dMAh%WlUn_FMbE04L1?S^xk5 literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/ABB08197C40226F64D449C7F116BC11A30C23464FD05B13516E0B576456D6BE9.bin b/test_fixtures/masp_proofs/ABB08197C40226F64D449C7F116BC11A30C23464FD05B13516E0B576456D6BE9.bin new file mode 100644 index 0000000000000000000000000000000000000000..2282101aaa8a712f861f1835cf6d76c5577c8012 GIT binary patch literal 9649 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 H$KZbeL!IpY literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/AE4CEC9192B52E8CE5AB0C25936D2AEEF55A6F202D8EB4564017DB5BEF872107.bin b/test_fixtures/masp_proofs/AE4CEC9192B52E8CE5AB0C25936D2AEEF55A6F202D8EB4564017DB5BEF872107.bin new file mode 100644 index 0000000000000000000000000000000000000000..89cbcb8263c2e14317c383f6882ee46b68cae626 GIT binary patch literal 15597 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 Fe*h&YL~;NC literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/B74C1EC89D48DC01F71DA402F898CB65B7F71F737870C86AEA55454B8B32DC2D.bin b/test_fixtures/masp_proofs/B74C1EC89D48DC01F71DA402F898CB65B7F71F737870C86AEA55454B8B32DC2D.bin new file mode 100644 index 0000000000000000000000000000000000000000..53999911ff012f317dacadec5895581c3e897c0b GIT binary patch literal 7448 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 literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/CA6AF0286DB05F3AB2513109958B21F6D7D9EE1DA5A6E60E9A37CD30DA4A505F.bin b/test_fixtures/masp_proofs/CA6AF0286DB05F3AB2513109958B21F6D7D9EE1DA5A6E60E9A37CD30DA4A505F.bin new file mode 100644 index 0000000000000000000000000000000000000000..7efd591c4ee22679b0f4884b1e4b2ef443203576 GIT binary patch literal 7448 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 literal 0 HcmV?d00001 diff --git a/test_fixtures/masp_proofs/FD874BACE965B637E528BF66B079048F119C09D85335704A083700CA11415DF3.bin b/test_fixtures/masp_proofs/FD874BACE965B637E528BF66B079048F119C09D85335704A083700CA11415DF3.bin new file mode 100644 index 0000000000000000000000000000000000000000..cf7fa35cdd506f74232707adb1621d4f43bcd063 GIT binary patch literal 10382 zcmeI2Wl&wqmVj|PxCID=gTq0B1P>4#0vtTJ`@vlT2X_lW4j$Yk!QCaegam@SYjB^J zcW-Lu$E&(`YG!^+y{xLv4{+7f+V(vE>Pn zEn?CjCPWH3S(*;57*BTOH&wv|aDIJx&}c+B7H7bG8}7O!*sjaMcH2l-4eUA#Kg*pr z*j}@oQNLs%hHII{G;=m#o<;2|NoqsQRT4a+_tU_C$H>o*`YG^MmwcW^_f;ohN-s9v zM0**5xVES~cgau9t5mStZGmVAe~kO%DI|8P8G~@(GN{~+cbsss?`1AzO3_U(ND%(~ zk|($QLMrS-#j6)5CBZ#-lxh4_Ok~P0(sQIyf#Pa0@NKb}pgr|mgGrXg@1yGv3vYq! zj4hSXuTijBpyKWOuX`rGkdQPE*IG@!eVB4hzhO-XZeRpk0@c0v#pVFab=h1|2nor< zBDh_40(D!UNlg>pTIOM-dI`TRfpE`P$j9DB4OaPb^zySUg36!yd7j%_gM8K=;H0md z9sn`37Je<2epCD9<3k4uNvq8Szm}!0)p?+Y^pS$9QYHKRsV!}{vh0!V-P~H57XZm} z%pi#8{eCKUi)gYHY{SaKc3^LXv2h`zfN5f9+EV*?-ofs5kT zLr(iT@B;#mr2Yj%?q=1?eb^&Y#|xb&3ppx}*uZk4>w09U1jiZR$(La_QiVYNPYd5? z06=aaf{B!gDH|~S-c+#t@Vq8ueKnIjWtxR4K{~Gcc^4hOwH8N+e;pSv<^s&pjQF)u zi&LUXwX}Ojp}LU>IcdciF#!u)_0@w6m@h>Ej{KtHz=_mW#{gBz>4Lg@UX~``OOAeQ z&hKo52MVg(;&IC20VK`Cd_0S-UJ7m|I^{P4tWXh9>|jSkxX^o#jvlDgjutLCc~WC1L8XDB(&tl&-z0Xrq|gQD^2rjW6xPyexKBj^DoKeBO>MTo zTIv*gcv>63O@0u! zn7k_S;Wq_`4s`AhA_H07(P~m7>Lt6*DNRCT`|{L-K=xoHj0%P1U^Nq7S}kC>3ymeS zZS0%7EuHJ0!b=?Pr9wVCb+GpvGUFod?p$-AY}d@u=}t#}4!qBXSIufC+b+woF(bY& zy(W%gapfBtpxv}ZTe|tIk3FeNNMZU_oH`3oV^eBed(P%^TOLmC4{ePfv1vH(c(B5o3tl z69g(?BcJC$gS4$Rze@bez$~x6iaq#?7T8mZSF>(}c|OIOKmkZEL{xTrpw*vrt<=8I z12AjfsbECfL!3?aw6miQ3n&u@ElHC{SFqYwgCV(oS(1VzgLf*XQDK-?Es3#qM!?L2 zu=VR^iz)T%iq^VP@Kvq(M-dt{r2g{&LL|Ydbw?n}!4eFKshvtj;i8JpP8zIEx?5PXSz$Ey+kN^4>AX?u)QM{ztcf%PF>NofnzmEbl|i= z1q$SmS(fzHuFx6Tkb`eu!!1YUU2W={XG>#wOZS5aae(||(F7@@Sm$zMb1LsjY0=4fL2^^BpMNs;j^+%??A>GDH0 z!oWz_`41I7bEIFW`lbILQP|SR@?3OQb&!Ywip+PS-Z^4#01rCZ)VWG=vh zqKHP`Hz0+kinrVIIGP^rH-&#w_&0_BjVau}6=Iv@fG8uT8dm~HoSE}w*R63ZmRI>t+-2?C!Urx&)d^5(=7}*vD6D&+Jyo_@!fhZ@N3?KP>0)?ex@P|+AUITaU?a?oB&;8#WtMvN45eyDR-xO2od%4TY zc_5<9Cm{iBapDlPZw~cFVzLR}|Hy6`#2q6Qj$}Z(Q!E{=FyN^IH0vsMr``7GxY&3X zI@FR_wZ=|QIc+j;utNtwud-0i1(YGt_DIJw`L+HI6VM9RjOI_0XE}3S1R@sA3usN~ z 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 Date: Mon, 20 Nov 2023 12:55:46 +0000 Subject: [PATCH 11/81] Fix masp integration tests --- tests/src/integration/masp.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index c5b51a6eac..bb7ca903b6 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -127,7 +127,7 @@ fn masp_incentives() -> Result<()> { }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.022")); + assert!(captured.contains("nam: 0.023")); // Assert NAM balance at MASP pool is exclusively the // rewards from the shielded BTC @@ -147,7 +147,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.022")); + assert!(captured.contains("nam: 0.023")); // Wait till epoch boundary node.next_epoch(); @@ -189,7 +189,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.08729")); + assert!(captured.contains("nam: 0.09189")); // Assert NAM balance at MASP pool is exclusively the // rewards from the shielded BTC @@ -209,7 +209,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.08729")); + assert!(captured.contains("nam: 0.09189")); // Wait till epoch boundary node.next_epoch(); @@ -312,7 +312,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.0207")); + assert!(captured.contains("nam: 0.02085")); // Assert NAM balance at MASP pool is an accumulation of // rewards from both the shielded BTC and shielded ETH @@ -332,7 +332,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.3726")); + assert!(captured.contains("nam: 0.39198")); // Wait till epoch boundary node.next_epoch(); @@ -398,7 +398,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.085204")); + assert!(captured.contains("nam: 0.085834")); node.next_epoch(); @@ -420,7 +420,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.134567")); + assert!(captured.contains("nam: 1.198717")); // Wait till epoch boundary node.next_epoch(); @@ -483,7 +483,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.355211")); + assert!(captured.contains("nam: 1.428567")); // Assert NAM balance at MASP pool is // the accumulation of rewards from the shielded assets (BTC and ETH) @@ -503,7 +503,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.459458")); + assert!(captured.contains("nam: 1.541561")); // Wait till epoch boundary node.next_epoch(); @@ -527,7 +527,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.520493")); + assert!(captured.contains("nam: 1.602735")); // Assert NAM balance at VK(B) is the rewards dispensed earlier // (since VK(A) has no shielded assets, no further rewards should @@ -548,7 +548,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0.125958")); + assert!(captured.contains("nam: 0.12677")); // Assert NAM balance at MASP pool is // the accumulation of rewards from the shielded assets (BTC and ETH) @@ -568,7 +568,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 1.637454")); + assert!(captured.contains("nam: 1.729505")); // Wait till epoch boundary to prevent conversion expiry during transaction // construction @@ -677,7 +677,7 @@ fn masp_incentives() -> Result<()> { ) }); assert!(captured.result.is_ok()); - assert!(captured.contains("nam: 0")); + assert!(captured.contains("nam: 0.115575")); Ok(()) } From b9d38d752904ca926012c621299ee7c17361ef2b Mon Sep 17 00:00:00 2001 From: satan Date: Tue, 21 Nov 2023 13:01:27 +0100 Subject: [PATCH 12/81] rebased on v0.27 and some initial multisig fixes --- apps/src/lib/client/utils.rs | 31 +-- apps/src/lib/config/genesis/chain.rs | 7 +- apps/src/lib/config/genesis/templates.rs | 5 +- apps/src/lib/config/genesis/transactions.rs | 263 +++++++++---------- apps/src/lib/node/ledger/shell/init_chain.rs | 7 +- core/src/types/key/mod.rs | 7 + tests/src/e2e/helpers.rs | 2 +- tests/src/e2e/ledger_tests.rs | 14 +- 8 files changed, 166 insertions(+), 170 deletions(-) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 4f4b88a7e5..523fe31fcc 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -609,29 +609,24 @@ pub fn derive_genesis_addresses( .into_iter() .flatten() { - let address = { - let unsigned = - genesis::transactions::UnsignedEstablishedAccountTx::from(tx); - unsigned.derive_address() - }; - let pk = if let Some(public_key) = tx.public_key.as_ref() { - &public_key.pk.raw - } else { - continue; - }; + let address = tx.derive_address(); println!(); println!("{} {address}", "Address:".bold().bright_green()); - println!("{} {pk}", "Public key:".bold().bright_green()); - let maybe_alias = - maybe_pre_genesis_wallet.as_ref().and_then(|wallet| { - let implicit_address = pk.into(); - wallet.find_alias(&implicit_address) - }); + println!("{}", "Public key(s):".bold().bright_green()); + for (ix, pk) in tx.public_keys.iter().enumerate() { + println!(" {}. {}", ix, pk); - if let Some(alias) = maybe_alias { - println!("{} {alias}", "Wallet alias:".bold().bright_green()); + let maybe_alias = + maybe_pre_genesis_wallet.as_ref().and_then(|wallet| { + let implicit_address = (&pk.raw).into(); + wallet.find_alias(&implicit_address) + }); + + if let Some(alias) = maybe_alias { + println!("{} {alias}", "Wallet alias:".bold().bright_green()); + } } } if genesis_txs diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index a0d82b52ed..a5dd2a6352 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -645,10 +645,7 @@ impl FinalizedTransactions { let established_account = established_account.map(|txs| { txs.into_iter() .map(|tx| FinalizedEstablishedAccountTx { - address: transactions::UnsignedEstablishedAccountTx::from( - &tx, - ) - .derive_address(), + address: tx.derive_address(), tx, }) .collect() @@ -739,7 +736,7 @@ impl FinalizedParameters { pub struct FinalizedEstablishedAccountTx { pub address: Address, #[serde(flatten)] - pub tx: transactions::SignedEstablishedAccountTx, + pub tx: transactions::EstablishedAccountTx, } #[derive( diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index fab82401f0..423905a847 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -18,10 +18,7 @@ use namada::types::token::{ use serde::{Deserialize, Serialize}; use super::toml_utils::{read_toml, write_toml}; -use super::transactions::{ - self, Transactions, UnsignedEstablishedAccountTx, - UnsignedValidatorAccountTx, -}; +use super::transactions::{self, Transactions, UnsignedValidatorAccountTx}; use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::transactions::{BondTx, SignedBondTx}; use crate::config::genesis::GenesisAddress; diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 56c75687dc..b0eb57fb9a 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -6,6 +6,7 @@ use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; +use itertools::Itertools; use namada::core::types::address::Address; use namada::core::types::string_encoding::StringEncoded; use namada::proto::{ @@ -108,7 +109,7 @@ pub fn init_established_account( ) -> (Address, Transactions) { let unsigned_tx = EstablishedAccountTx { vp, - public_key: Some(StringEncoded::new(public_key)), + public_keys: Some(StringEncoded::new(public_key)), }; let address = unsigned_tx.derive_address(); let signed_tx = sign_established_account_tx(unsigned_tx, wallet); @@ -194,28 +195,6 @@ pub fn init_validator( (address, txs) } -pub fn sign_established_account_tx( - unsigned_tx: UnsignedEstablishedAccountTx, - wallet: &mut Wallet, -) -> SignedEstablishedAccountTx { - let key = unsigned_tx.public_key.as_ref().map(|pk| { - let secret = wallet - .find_key_by_pk(pk, None) - .expect("Key for source must be present to sign with it."); - let sig = sign_tx(&unsigned_tx, &secret); - SignedPk { - pk: pk.clone(), - authorization: sig, - } - }); - let UnsignedEstablishedAccountTx { vp, public_key: _ } = unsigned_tx; - - SignedEstablishedAccountTx { - vp, - public_key: key, - } -} - pub fn sign_validator_account_tx( unsigned_tx: UnsignedValidatorAccountTx, validator_wallet: &ValidatorWallet, @@ -306,7 +285,7 @@ pub fn sign_self_bond_tx( pub fn sign_delegation_bond_tx( unsigned_tx: BondTx, wallet: &mut Wallet, - established_accounts: &Option>>, + established_accounts: &Option>, ) -> SignedBondTx { let source_key = look_up_sk_from(&unsigned_tx.source, wallet, established_accounts); @@ -334,7 +313,7 @@ pub fn sign_tx( Eq, )] pub struct Transactions { - pub established_account: Option>, + pub established_account: Option>, pub validator_account: Option>, pub bond: Option>, } @@ -429,7 +408,7 @@ impl Transactions { #[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] pub struct UnsignedTransactions { - pub established_account: Option>, + pub established_account: Option>, pub validator_account: Option>, pub bond: Option>>, } @@ -476,11 +455,6 @@ impl DeriveEstablishedAddress for UnsignedValidatorAccountTx { const SALT: &'static str = "validator-account-tx"; } -pub type UnsignedEstablishedAccountTx = - EstablishedAccountTx>; - -pub type SignedEstablishedAccountTx = EstablishedAccountTx; - #[derive( Clone, Debug, @@ -491,13 +465,32 @@ pub type SignedEstablishedAccountTx = EstablishedAccountTx; PartialEq, Eq, )] -pub struct EstablishedAccountTx { +pub struct EstablishedAccountTx { pub vp: String, + #[serde(default = "default_threshold")] + pub threshold: u8, + #[serde(deserialize_with = "deserialize_single_or_vec")] /// PKs have to come last in TOML to avoid `ValueAfterTable` error - pub public_key: Option, + pub public_keys: Vec>, +} + +const fn default_threshold() -> u8 { + 1 } -impl DeriveEstablishedAddress for UnsignedEstablishedAccountTx { +/// Deserialize a vec of type `T`, or if a single `T` is provided, +/// deserialize that and place in a vector. +fn deserialize_single_or_vec<'de, D, T>(d: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, + T: Deserialize<'de>, +{ + T::deserialize(d.clone()) + .map(|t| vec![t]) + .or_else(|_| Vec::::deserialize(d)) +} + +impl DeriveEstablishedAddress for EstablishedAccountTx { const SALT: &'static str = "established-account-tx"; } @@ -507,7 +500,7 @@ impl SignedBondTx where T: BorshSerialize + TemplateValidation, { - /// Verify the signature of `BondTx`. This should not depend + /// Verify the signatures of `BondTx`. This should not depend /// on whether the contained amount is denominated or not. /// /// Since we denominate amounts as part of validation, we can @@ -515,13 +508,56 @@ where /// types. pub fn verify_sig( &self, - pk: &common::PublicKey, + pks: &[common::PublicKey], + threshold: u8, ) -> Result<(), VerifySigError> { - let Self { data, signature } = self; - verify_standalone_sig::<_, SerializeWithBorsh>( - &data.data_to_sign(), - pk, - signature, + let Self { + data, + signatures: signature, + } = self; + if pks.len() > u8::MAX as usize { + eprintln!("You're multisig is too facking big"); + return Err(VerifySigError::TooGoddamnBig); + } + let mut valid_sigs = 0; + for pk in &pks { + valid_sigs += self.signatures.iter().any(|sig| { + verify_standalone_sig::<_, SerializeWithBorsh>( + &data.data_to_sign(), + pk, + &sig.raw, + ) + .is_ok() + }) as u8; + if valid_sigs >= threshold { + break; + } + } + if valid_sigs >= threshold { + Ok(()) + } else { + Err(VerifySigError::ThresholdNotMet(threshold, valid_sigs)) + } + } +} + +impl SignedBondTx { + /// Sign the transfer and add to the list of signatures. + /// + /// Since we denominate amounts as part of validation, we can + /// only verify signatures on [`SignedBondTx`] + /// types. Thus we only allow signing of [`BondTx`] + /// types. + pub fn sign(&mut self, key: &[common::SecretKey]) { + self.signatures.extend( + key.iter() + .map(|sk| { + standalone_signature::<_, SerializeWithBorsh>( + sk, + &self.data.data_to_sign(), + ) + }) + .collect(), ) } } @@ -581,21 +617,13 @@ impl BondTx { amount, }) } +} - /// Sign the transfer. - /// - /// Since we denominate amounts as part of validation, we can - /// only verify signatures on [`SignedBondTx`] - /// types. Thus we only allow signing of [`BondTx`] - /// types. - pub fn sign(self, key: &common::SecretKey) -> SignedBondTx { - let sig = standalone_signature::<_, SerializeWithBorsh>( - key, - &self.data_to_sign(), - ); +impl From> for SignedBondTx { + fn from(bond: BondTx) -> Self { SignedBondTx { - data: self, - signature: StringEncoded { raw: sig }, + data: bond, + signatures: vec![], } } } @@ -613,7 +641,7 @@ impl BondTx { pub struct Signed { #[serde(flatten)] pub data: T, - pub signature: StringEncoded, + pub signatures: Vec>, } #[derive( @@ -640,8 +668,10 @@ pub fn validate( let mut is_valid = true; let mut all_used_addresses: BTreeSet

= BTreeSet::default(); - let mut established_accounts: BTreeMap> = - BTreeMap::default(); + let mut established_accounts: BTreeMap< + Address, + (Vec, u8), + > = BTreeMap::default(); let mut validator_accounts: BTreeMap = BTreeMap::default(); @@ -737,17 +767,7 @@ pub fn validate( }; is_valid.then_some(Transactions { - established_account: transactions.established_account.map( - |established_accounts| { - established_accounts - .into_iter() - .map(|acct| EstablishedAccountTx { - vp: acct.vp, - public_key: acct.public_key, - }) - .collect() - }, - ), + established_account: transactions.established_account, validator_account: transactions.validator_account.map( |validator_accounts| { validator_accounts @@ -779,29 +799,33 @@ pub fn validate( fn validate_bond( tx: SignedBondTx, balances: &mut BTreeMap, - established_accounts: &BTreeMap>, + established_accounts: &BTreeMap, u8)>, validator_accounts: &BTreeMap, parameters: &Parameters, ) -> Option> { // Check signature let mut is_valid = { let source = &tx.data.source; - if let Some(source_pk) = match source { + let maybe_source = match source { GenesisAddress::EstablishedAddress(address) => { // Try to find the source's PK in either established_accounts or // validator_accounts let established_addr = Address::Established(address.clone()); established_accounts .get(&established_addr) - .cloned() - .flatten() + .map(|(pks, t)| (pks.as_slice(), *t)) .or_else(|| { - validator_accounts.get(&established_addr).cloned() + validator_accounts + .get(&established_addr) + .map(|addr| (std::slice::from_ref(addr), 1)) }) } - GenesisAddress::PublicKey(pk) => Some(pk.raw.clone()), - } { - if tx.verify_sig(&source_pk).is_err() { + GenesisAddress::PublicKey(pk) => { + Some((std::slice::from_ref(&pk.raw), 1)) + } + }; + if let Some((source_pks, threshold)) = maybe_source { + if tx.verify_sig(&source_pks, threshold).is_err() { eprintln!("Invalid bond tx signature.",); false } else { @@ -886,17 +910,24 @@ pub struct TokenBalancesForValidation { } pub fn validate_established_account( - tx: &SignedEstablishedAccountTx, + tx: &EstablishedAccountTx, vps: Option<&ValidityPredicates>, all_used_addresses: &mut BTreeSet
, - established_accounts: &mut BTreeMap>, + established_accounts: &mut BTreeMap, u8)>, ) -> bool { let mut is_valid = true; - let established_address = EstablishedAccountTx::from(tx).derive_address(); + let established_address = tx.derive_address(); + if tx.threshold == 0 { + eprintln!("An established account may not have zero thresold"); + is_valid = false; + } established_accounts.insert( established_address.clone(), - tx.public_key.as_ref().map(|signed| signed.pk.raw.clone()), + ( + tx.public_keys.iter().map(|k| k.raw.clone()).collect(), + tx.threshold, + ), ); // Check that the established address is unique @@ -924,23 +955,13 @@ pub fn validate_established_account( } // If PK is used, check the authorization - if let Some(pk) = tx.public_key.as_ref() { - if !validate_established_account_sig(pk, tx) { - is_valid = false; - } + if tx.public_keys.is_empty() { + eprintln!("An `established_account` tx was found with no public keys."); + is_valid = false; } - is_valid } -fn validate_established_account_sig( - SignedPk { pk, authorization }: &SignedPk, - tx: &SignedEstablishedAccountTx, -) -> bool { - let unsigned = UnsignedEstablishedAccountTx::from(tx); - validate_signature(&unsigned, &pk.raw, &authorization.raw) -} - pub fn validate_validator_account( tx: &ValidatorAccountTx, vps: Option<&ValidityPredicates>, @@ -1073,16 +1094,6 @@ fn validate_signature( } } -impl From<&SignedEstablishedAccountTx> for UnsignedEstablishedAccountTx { - fn from(tx: &SignedEstablishedAccountTx) -> Self { - let SignedEstablishedAccountTx { vp, public_key } = tx; - Self { - vp: vp.clone(), - public_key: public_key.as_ref().map(|signed| signed.pk.clone()), - } - } -} - impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { fn from(tx: &SignedValidatorAccountTx) -> Self { let SignedValidatorAccountTx { @@ -1133,17 +1144,19 @@ impl From<&SignedBondTx> for BondTx { fn look_up_sk_from( source: &GenesisAddress, wallet: &mut Wallet, - established_accounts: &Option>>, -) -> common::SecretKey { + established_accounts: &Option>, +) -> Vec { // Try to look-up the source from wallet first match source { GenesisAddress::EstablishedAddress(_) => None, - GenesisAddress::PublicKey(pk) => wallet.find_key_by_pk(pk, None).ok(), + GenesisAddress::PublicKey(pk) => { + wallet.find_key_by_pk(pk, None).map(|sk| vec![sk]).ok() + } } .unwrap_or_else(|| { // If it's not in the wallet, it must be an established account // so we need to look-up its public key first - let pk = established_accounts + established_accounts .as_ref() .unwrap_or_else(|| { panic!( @@ -1155,24 +1168,13 @@ fn look_up_sk_from( .find_map(|account| match source { GenesisAddress::EstablishedAddress(address) => { // delegation from established account - if &EstablishedAccountTx::from(account) - .derive_established_address() - == address - { + if account.derive_established_address() == address { Some( - &account - .public_key - .as_ref() - .unwrap_or_else(|| { - panic!( - "Signing failed. The established \ - account \"{source}\" has no public \ - key. Add a public to be able to sign \ - bonds." - ); - }) - .pk - .raw, + account + .public_keys + .iter() + .map(|pk| &pk.raw) + .collect::>(), ) } else { None @@ -1180,7 +1182,7 @@ fn look_up_sk_from( } GenesisAddress::PublicKey(pk) => { // delegation from an implicit account - Some(&pk.raw) + Some(vec![&pk.raw]) } }) .unwrap_or_else(|| { @@ -1188,12 +1190,9 @@ fn look_up_sk_from( "Signing failed. Cannot find \"{source}\" in the wallet \ or in the established accounts." ); - }); - wallet.find_key_by_pk(pk, None).unwrap_or_else(|err| { - panic!( - "Signing failed. Cannot find key for established account \ - \"{source}\" in the wallet. Failed with {err}." - ); - }) + }) + .iter() + .filter_map(|pk| wallet.find_key_by_pk(pk, None).ok()) + .collect() }) } diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 3714ea6aef..79ba18f267 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -11,7 +11,6 @@ use namada::ledger::{ibc, pos}; use namada::proof_of_stake::BecomeValidator; use namada::types::hash::Hash as CodeHash; use namada::types::key::*; -use namada::types::storage::KeySeg; use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::vm::validate_untrusted_wasm; use namada_sdk::eth_bridge::EthBridgeStatus; @@ -350,7 +349,11 @@ where if let Some(txs) = genesis.transactions.established_account.as_ref() { for FinalizedEstablishedAccountTx { address, - tx: EstablishedAccountTx { vp, public_key }, + tx: + EstablishedAccountTx { + vp, + public_keys: public_key, + }, } in txs { tracing::debug!( diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 9a13c13503..fb9964cccc 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -124,6 +124,13 @@ pub enum VerifySigError { MismatchedScheme, #[error("Signature verification went out of gas: {0}")] OutOfGas(#[from] crate::ledger::gas::Error), + #[error( + "The number of valid signatures did not meet the required threshold, \ + required {0} got {1}" + )] + ThresholdNotMet(u8, u8), + #[error("Too many fucking sigs")] + TooGoddamnBig, } #[allow(missing_docs)] diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 13fe2b7287..704365e652 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -230,7 +230,7 @@ pub fn get_established_addr_from_pregenesis>( let established_accounts = genesis.transactions.established_account.as_ref()?; let acct = established_accounts.iter().find(|&acct| { - acct.public_key.as_ref().expect("the unexpected").pk.raw == pk + acct.public_keys.as_ref().expect("the unexpected").pk.raw == pk })?; Some( transactions::UnsignedEstablishedAccountTx::from(acct).derive_address(), diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index a8ccfb3f83..19784b2ddc 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -3475,15 +3475,13 @@ fn test_invalid_validator_txs() -> Result<()> { base_dir, default_port_offset, ); - let bonds = genesis.transactions.bond.unwrap(); - genesis.transactions.bond = Some( + genesis.transactions.bond = Some({ + let mut bonds = genesis.transactions.bond.unwrap(); + // NB: the last bond should be from `validator-1`. + // we will filter it out from the list of bonds + bonds.pop(); bonds - .into_iter() - .filter(|bond| { - (&bond.data.validator).as_ref() != "validator-1" - }) - .collect(), - ); + }); genesis }, None, From 6823331dead1896114d8dc8ffbb993e400d44c2d Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 21 Nov 2023 14:12:43 +0000 Subject: [PATCH 13/81] Remove established account init CLI subcmd --- apps/src/lib/cli.rs | 53 ------------------ apps/src/lib/cli/client.rs | 3 -- apps/src/lib/client/utils.rs | 59 --------------------- apps/src/lib/config/genesis/transactions.rs | 19 ------- 4 files changed, 134 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index dbddc20565..bda9e4b6aa 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2180,7 +2180,6 @@ pub mod cmds { ValidateWasm(ValidateWasm), InitNetwork(InitNetwork), DeriveGenesisAddresses(DeriveGenesisAddresses), - InitGenesisEstablishedAccount(InitGenesisEstablishedAccount), InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), @@ -2203,8 +2202,6 @@ pub mod cmds { SubCmd::parse(matches).map(Self::InitNetwork); let derive_addresses = SubCmd::parse(matches).map(Self::DeriveGenesisAddresses); - let init_established = SubCmd::parse(matches) - .map(Self::InitGenesisEstablishedAccount); let init_genesis = SubCmd::parse(matches).map(Self::InitGenesisValidator); let pk_to_tm_address = @@ -2221,7 +2218,6 @@ pub mod cmds { .or(validate_wasm) .or(init_network) .or(derive_addresses) - .or(init_established) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) @@ -2239,7 +2235,6 @@ pub mod cmds { .subcommand(ValidateWasm::def()) .subcommand(InitNetwork::def()) .subcommand(DeriveGenesisAddresses::def()) - .subcommand(InitGenesisEstablishedAccount::def()) .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) @@ -2349,29 +2344,6 @@ pub mod cmds { } } - #[derive(Clone, Debug)] - pub struct InitGenesisEstablishedAccount( - pub args::InitGenesisEstablishedAccount, - ); - - impl SubCmd for InitGenesisEstablishedAccount { - const CMD: &'static str = "init-genesis-established-account"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - Self(args::InitGenesisEstablishedAccount::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Initialize an established account available at genesis.", - ) - .add_args::() - } - } - #[derive(Clone, Debug)] pub struct InitGenesisValidator(pub args::InitGenesisValidator); @@ -6766,31 +6738,6 @@ pub mod args { } } - #[derive(Clone, Debug)] - pub struct InitGenesisEstablishedAccount { - pub vp: String, - pub wallet_alias: String, - } - - impl Args for InitGenesisEstablishedAccount { - fn parse(matches: &ArgMatches) -> Self { - let wallet_alias = ALIAS.parse(matches); - let vp = VP.parse(matches).unwrap_or_else(|| "vp_user".to_string()); - Self { wallet_alias, vp } - } - - fn def(app: App) -> App { - app.arg( - ALIAS - .def() - .help("The alias of the key to use from the wallet."), - ) - .arg(VP.def().help( - "The validity predicate of the account. Defaults to `vp_user`.", - )) - } - } - #[derive(Clone, Debug)] pub struct InitGenesisValidator { pub alias: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 2f06a497d4..472b7ae099 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -637,9 +637,6 @@ impl CliApi { Utils::DeriveGenesisAddresses(DeriveGenesisAddresses(args)) => { utils::derive_genesis_addresses(global_args, args) } - Utils::InitGenesisEstablishedAccount( - InitGenesisEstablishedAccount(args), - ) => utils::init_genesis_established_account(global_args, args), Utils::InitGenesisValidator(InitGenesisValidator(args)) => { utils::init_genesis_validator(global_args, args) } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 523fe31fcc..7420029726 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -673,56 +673,6 @@ pub fn derive_genesis_addresses( } } -/// Initialize a genesis established account. -/// key into a special "pre-genesis" wallet. -pub fn init_genesis_established_account( - global_args: args::Global, - args: args::InitGenesisEstablishedAccount, -) { - let (mut pre_genesis_wallet, _) = - load_pre_genesis_wallet_or_exit(&global_args.base_dir); - - let alias = args.wallet_alias.as_str(); - let public_key = { - let sk = pre_genesis_wallet - .find_secret_key(alias, None) - .unwrap_or_else(|err| { - eprintln!( - "Failed to look-up `{alias}` in the pre-genesis wallet: \ - {err}", - ); - safe_exit(1) - }); - sk.ref_to() - }; - - let (address, txs) = genesis::transactions::init_established_account( - args.vp, - public_key, - &mut pre_genesis_wallet, - ); - let toml_path = { - let pre_genesis_dir = global_args.base_dir.join(PRE_GENESIS_DIR); - established_acc_pre_genesis_txs_file(alias, &pre_genesis_dir) - }; - let toml_path_str = toml_path.to_string_lossy(); - - let genesis_part = toml::to_string(&txs).unwrap(); - fs::write(&toml_path, genesis_part).unwrap_or_else(|err| { - eprintln!( - "Couldn't write pre-genesis transactions file to {toml_path_str}. \ - Failed with: {err}", - ); - safe_exit(1) - }); - - println!( - "{}: {address}", - "Derived established account address".bold() - ); - println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); -} - /// Initialize genesis validator's address, consensus key and validator account /// key into a special "pre-genesis" wallet. pub fn init_genesis_validator( @@ -894,15 +844,6 @@ pub fn validator_pre_genesis_txs_file(pre_genesis_path: &Path) -> PathBuf { pre_genesis_path.join("transactions.toml") } -/// The default path to an established account txs file. -pub fn established_acc_pre_genesis_txs_file( - wallet_key_alias: &str, - pre_genesis_path: &Path, -) -> PathBuf { - pre_genesis_path - .join(format!("established-account-tx-{wallet_key_alias}.toml")) -} - /// The default validator pre-genesis directory pub fn validator_pre_genesis_dir(base_dir: &Path, alias: &str) -> PathBuf { base_dir.join(PRE_GENESIS_DIR).join(alias) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index b0eb57fb9a..ddb8f7e90a 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -101,25 +101,6 @@ pub fn parse_unsigned( toml::from_slice(bytes) } -/// Create signed [`Transactions`] for an established account. -pub fn init_established_account( - vp: String, - public_key: common::PublicKey, - wallet: &mut Wallet, -) -> (Address, Transactions) { - let unsigned_tx = EstablishedAccountTx { - vp, - public_keys: Some(StringEncoded::new(public_key)), - }; - let address = unsigned_tx.derive_address(); - let signed_tx = sign_established_account_tx(unsigned_tx, wallet); - let txs = Transactions { - established_account: Some(vec![signed_tx]), - ..Default::default() - }; - (address, txs) -} - /// Create signed [`Transactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { From ca0b3fa2c0044b01c9d7332141b1458a18f98386 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 21 Nov 2023 14:48:13 +0000 Subject: [PATCH 14/81] Fix rebase + multisig related compilation issues --- apps/src/lib/config/genesis/templates.rs | 3 +- apps/src/lib/config/genesis/transactions.rs | 58 ++++++-------------- apps/src/lib/node/ledger/shell/init_chain.rs | 27 ++++----- tests/src/e2e/helpers.rs | 8 +-- 4 files changed, 36 insertions(+), 60 deletions(-) diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index 423905a847..a9f513214e 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -818,8 +818,7 @@ pub fn validate_parameters( let mut found_steward = false; if let Some(accs) = &txs.established_account { if accs.iter().any(|acct| { - let addr = - UnsignedEstablishedAccountTx::from(acct).derive_address(); + let addr = acct.derive_address(); &addr == steward }) { found_steward = true; diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index ddb8f7e90a..da38a63721 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -6,7 +6,6 @@ use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; -use itertools::Itertools; use namada::core::types::address::Address; use namada::core::types::string_encoding::StringEncoded; use namada::proto::{ @@ -75,11 +74,6 @@ pub fn sign_txs( } // Sign all the transactions - let established_account = established_account.map(|tx| { - tx.into_iter() - .map(|tx| sign_established_account_tx(tx, wallet)) - .collect() - }); let validator_account = None; let bond = bond.map(|tx| { tx.into_iter() @@ -260,7 +254,9 @@ pub fn sign_self_bond_tx( unsigned_tx: BondTx, validator_wallet: &ValidatorWallet, ) -> SignedBondTx { - unsigned_tx.sign(&validator_wallet.account_key) + let mut signed = SignedBondTx::from(unsigned_tx); + signed.sign(std::slice::from_ref(&validator_wallet.account_key)); + signed } pub fn sign_delegation_bond_tx( @@ -268,9 +264,11 @@ pub fn sign_delegation_bond_tx( wallet: &mut Wallet, established_accounts: &Option>, ) -> SignedBondTx { - let source_key = + let source_keys = look_up_sk_from(&unsigned_tx.source, wallet, established_accounts); - unsigned_tx.sign(&source_key) + let mut signed = SignedBondTx::from(unsigned_tx); + signed.sign(&source_keys); + signed } pub fn sign_tx( @@ -450,7 +448,6 @@ pub struct EstablishedAccountTx { pub vp: String, #[serde(default = "default_threshold")] pub threshold: u8, - #[serde(deserialize_with = "deserialize_single_or_vec")] /// PKs have to come last in TOML to avoid `ValueAfterTable` error pub public_keys: Vec>, } @@ -459,18 +456,6 @@ const fn default_threshold() -> u8 { 1 } -/// Deserialize a vec of type `T`, or if a single `T` is provided, -/// deserialize that and place in a vector. -fn deserialize_single_or_vec<'de, D, T>(d: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, - T: Deserialize<'de>, -{ - T::deserialize(d.clone()) - .map(|t| vec![t]) - .or_else(|_| Vec::::deserialize(d)) -} - impl DeriveEstablishedAddress for EstablishedAccountTx { const SALT: &'static str = "established-account-tx"; } @@ -492,17 +477,14 @@ where pks: &[common::PublicKey], threshold: u8, ) -> Result<(), VerifySigError> { - let Self { - data, - signatures: signature, - } = self; + let Self { data, signatures } = self; if pks.len() > u8::MAX as usize { eprintln!("You're multisig is too facking big"); return Err(VerifySigError::TooGoddamnBig); } let mut valid_sigs = 0; - for pk in &pks { - valid_sigs += self.signatures.iter().any(|sig| { + for pk in pks { + valid_sigs += signatures.iter().any(|sig| { verify_standalone_sig::<_, SerializeWithBorsh>( &data.data_to_sign(), pk, @@ -530,16 +512,12 @@ impl SignedBondTx { /// types. Thus we only allow signing of [`BondTx`] /// types. pub fn sign(&mut self, key: &[common::SecretKey]) { - self.signatures.extend( - key.iter() - .map(|sk| { - standalone_signature::<_, SerializeWithBorsh>( - sk, - &self.data.data_to_sign(), - ) - }) - .collect(), - ) + self.signatures.extend(key.iter().map(|sk| { + StringEncoded::new(standalone_signature::<_, SerializeWithBorsh>( + sk, + &self.data.data_to_sign(), + )) + })) } } @@ -806,7 +784,7 @@ fn validate_bond( } }; if let Some((source_pks, threshold)) = maybe_source { - if tx.verify_sig(&source_pks, threshold).is_err() { + if tx.verify_sig(source_pks, threshold).is_err() { eprintln!("Invalid bond tx signature.",); false } else { @@ -1149,7 +1127,7 @@ fn look_up_sk_from( .find_map(|account| match source { GenesisAddress::EstablishedAddress(address) => { // delegation from established account - if account.derive_established_address() == address { + if &account.derive_established_address() == address { Some( account .public_keys diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 79ba18f267..c21463b28d 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -307,11 +307,11 @@ where let mut total_token_balance = token::Amount::zero(); for (owner, balance) in balances { if let genesis::GenesisAddress::PublicKey(pk) = owner { - storage_api::account::set_public_key_at( + storage_api::account::init_account_storage( &mut self.wl_storage, &owner.address(), - &pk.raw, - 0, + std::slice::from_ref(&pk.raw), + 1, ) .unwrap(); } @@ -352,7 +352,8 @@ where tx: EstablishedAccountTx { vp, - public_keys: public_key, + threshold, + public_keys, }, } in txs { @@ -366,15 +367,15 @@ where .write_bytes(&Key::validity_predicate(address), code_hash) .unwrap(); - if let Some(pk) = public_key { - storage_api::account::set_public_key_at( - &mut self.wl_storage, - address, - &pk.pk.raw, - 0, - ) - .unwrap(); - } + let public_keys: Vec<_> = + public_keys.iter().map(|pk| pk.raw.clone()).collect(); + storage_api::account::init_account_storage( + &mut self.wl_storage, + address, + &public_keys, + *threshold, + ) + .unwrap(); } } } diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 704365e652..d7cea1d79c 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -20,7 +20,7 @@ use namada::types::key::*; use namada::types::storage::Epoch; use namada::types::token; use namada_apps::config::genesis::chain::DeriveEstablishedAddress; -use namada_apps::config::genesis::{templates, transactions}; +use namada_apps::config::genesis::templates; use namada_apps::config::utils::convert_tm_addr_to_socket_addr; use namada_apps::config::{Config, TendermintMode}; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; @@ -230,11 +230,9 @@ pub fn get_established_addr_from_pregenesis>( let established_accounts = genesis.transactions.established_account.as_ref()?; let acct = established_accounts.iter().find(|&acct| { - acct.public_keys.as_ref().expect("the unexpected").pk.raw == pk + acct.public_keys.len() == 1 && acct.public_keys[0].raw == pk })?; - Some( - transactions::UnsignedEstablishedAccountTx::from(acct).derive_address(), - ) + Some(acct.derive_address()) } /// Find the address of an account by its alias from the wallet From f7833530f220a0aea2198c8fdf5379e66a87326c Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 21 Nov 2023 15:36:21 +0000 Subject: [PATCH 15/81] Implement Display and FromStr for established addrs --- core/src/types/address.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 91028f0750..4275ebae0d 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -406,6 +406,29 @@ impl From<[u8; SHA_HASH_LEN]> for EstablishedAddress { } } +impl string_encoding::Format for EstablishedAddress { + type EncodedBytes<'a> = [u8; raw::ADDR_ENCODING_LEN]; + + const HRP: &'static str = string_encoding::ADDRESS_HRP; + + #[inline] + fn to_bytes(&self) -> [u8; raw::ADDR_ENCODING_LEN] { + Address::Established(self.hash.into()).to_bytes() + } + + #[inline] + fn decode_bytes(bytes: &[u8]) -> Result { + match Address::decode_bytes(bytes)? { + Address::Established(established) => Ok(established), + address => Err(DecodeError::InvalidInnerEncoding(format!( + "Expected established address, got {address:?}" + ))), + } + } +} + +impl_display_and_from_str_via_format!(EstablishedAddress); + /// A generator of established addresses #[derive( Debug, From 41e1f5257ecaf463fce04f219a2eeb2b43ae2df0 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 21 Nov 2023 15:38:41 +0000 Subject: [PATCH 16/81] Implement serde serialization for validator meta --- Cargo.lock | 1 + proof_of_stake/Cargo.toml | 1 + proof_of_stake/src/types.rs | 3 +++ wasm/Cargo.lock | 1 + wasm_for_tests/wasm_source/Cargo.lock | 1 + 5 files changed, 7 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a760f39701..4e7035351b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4020,6 +4020,7 @@ dependencies = [ "pretty_assertions", "proptest", "proptest-state-machine", + "serde 1.0.190", "test-log", "thiserror", "tracing", diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index 19eb0e2493..700dccb7fc 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -24,6 +24,7 @@ data-encoding.workspace = true derivative.workspace = true once_cell.workspace = true proptest = {version = "1.2.0", optional = true} +serde.workspace = true thiserror.workspace = true tracing.workspace = true diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index 21919896b0..20c9b2a0ca 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -21,6 +21,7 @@ use namada_core::types::storage::{Epoch, KeySeg}; use namada_core::types::token; use namada_core::types::token::Amount; pub use rev_order::ReverseOrdTokenAmount; +use serde::{Deserialize, Serialize}; use crate::parameters::PosParams; @@ -345,6 +346,8 @@ pub struct GenesisValidator { BorshSerialize, BorshSchema, BorshDeserialize, + Deserialize, + Serialize, Eq, Ord, PartialOrd, diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 66a3bb5817..f8851f7973 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3087,6 +3087,7 @@ dependencies = [ "namada_core", "once_cell", "proptest", + "serde", "thiserror", "tracing", ] diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 133935e7f0..71b92988cd 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -3087,6 +3087,7 @@ dependencies = [ "namada_core", "once_cell", "proptest", + "serde", "thiserror", "tracing", ] From a4a523fdfe36b3cfba988b461f71726c47e2ea0f Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 21 Nov 2023 16:07:56 +0000 Subject: [PATCH 17/81] wip mess --- apps/src/lib/client/utils.rs | 7 +- apps/src/lib/config/genesis/chain.rs | 5 +- apps/src/lib/config/genesis/transactions.rs | 74 ++++++++++++--------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 7420029726..431ea44a67 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -643,13 +643,8 @@ pub fn derive_genesis_addresses( println!("{}", "Validator account txs:".underline().bold()); for tx in genesis_txs.validator_account.as_ref().into_iter().flatten() { - let address = { - let unsigned = - genesis::transactions::UnsignedValidatorAccountTx::from(tx); - unsigned.derive_address() - }; println!(); - println!("{} {address}", "Address:".bold().bright_green()); + println!("{} {}", "Address:".bold().bright_green(), tx.address); let keys = [ ("Account key:", &tx.account_key.pk.raw), ("Consensus key:", &tx.consensus_key.pk.raw), diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index a5dd2a6352..fc3b42eac7 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -653,10 +653,7 @@ impl FinalizedTransactions { let validator_account = validator_account.map(|txs| { txs.into_iter() .map(|tx| FinalizedValidatorAccountTx { - address: transactions::UnsignedValidatorAccountTx::from( - &tx, - ) - .derive_address(), + address: Address::Established(tx.address.clone()), tx, }) .collect() diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index da38a63721..6d93507117 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -6,8 +6,9 @@ use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; -use namada::core::types::address::Address; +use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; +use namada::ledger::pos::types::ValidatorMetaData; use namada::proto::{ standalone_signature, verify_standalone_sig, SerializeWithBorsh, }; @@ -98,6 +99,7 @@ pub fn parse_unsigned( /// Create signed [`Transactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { + address, commission_rate, max_commission_rate_change, net_address, @@ -110,6 +112,7 @@ pub fn init_validator( validator_wallet: &ValidatorWallet, ) -> (Address, Transactions) { let unsigned_validator_account_tx = UnsignedValidatorAccountTx { + address, account_key: StringEncoded::new(validator_wallet.account_key.ref_to()), consensus_key: StringEncoded::new( validator_wallet.consensus_key.ref_to(), @@ -139,8 +142,7 @@ pub fn init_validator( discord_handle, net_address, }; - let unsigned_validator_addr = - unsigned_validator_account_tx.derive_established_address(); + let unsigned_validator_addr = unsigned_validator_account_tx.address.clone(); let validator_account = Some(vec![sign_validator_account_tx( unsigned_validator_account_tx, validator_wallet, @@ -189,6 +191,7 @@ pub fn sign_validator_account_tx( sign_tx(&unsigned_tx, &validator_wallet.tendermint_node_key); let ValidatorAccountTx { + address, account_key, consensus_key, protocol_key, @@ -233,6 +236,7 @@ pub fn sign_validator_account_tx( }; SignedValidatorAccountTx { + address, account_key, consensus_key, protocol_key, @@ -408,17 +412,15 @@ pub type SignedValidatorAccountTx = ValidatorAccountTx; Eq, )] pub struct ValidatorAccountTx { + /// The address of the validator. + pub address: StringEncoded, + // TODO: remove the vp field pub vp: String, /// Commission rate charged on rewards for delegators (bounded inside /// 0-1) pub commission_rate: Dec, /// Maximum change in commission rate permitted per epoch pub max_commission_rate_change: Dec, - /// Validator metadata - pub email: String, - pub description: Option, - pub website: Option, - pub discord_handle: Option, /// P2P IP:port pub net_address: SocketAddr, /// PKs have to come last in TOML to avoid `ValueAfterTable` error @@ -428,10 +430,8 @@ pub struct ValidatorAccountTx { pub tendermint_node_key: PK, pub eth_hot_key: PK, pub eth_cold_key: PK, -} - -impl DeriveEstablishedAddress for UnsignedValidatorAccountTx { - const SALT: &'static str = "validator-account-tx"; + /// Validator metadata + pub metadata: ValidatorMetaData, } #[derive( @@ -658,7 +658,7 @@ pub fn validate( if !validate_validator_account( tx, vps, - &mut all_used_addresses, + &all_used_addresses, &mut validator_accounts, ) { is_valid = false; @@ -732,14 +732,11 @@ pub fn validate( validator_accounts .into_iter() .map(|acct| ValidatorAccountTx { + address: acct.address, vp: acct.vp, commission_rate: acct.commission_rate, max_commission_rate_change: acct .max_commission_rate_change, - email: acct.email, - description: acct.description, - website: acct.website, - discord_handle: acct.discord_handle, net_address: acct.net_address, account_key: acct.account_key, consensus_key: acct.consensus_key, @@ -747,6 +744,7 @@ pub fn validate( tendermint_node_key: acct.tendermint_node_key, eth_hot_key: acct.eth_hot_key, eth_cold_key: acct.eth_cold_key, + metadata: acct.metadata, }) .collect() }, @@ -924,25 +922,33 @@ pub fn validate_established_account( pub fn validate_validator_account( tx: &ValidatorAccountTx, vps: Option<&ValidityPredicates>, - all_used_addresses: &mut BTreeSet
, - validator_accounts: &mut BTreeMap, + all_used_addresses: &BTreeSet
, + validator_accounts: &mut BTreeSet
, ) -> bool { let mut is_valid = true; - let established_address = ValidatorAccountTx::from(tx).derive_address(); - validator_accounts - .insert(established_address.clone(), tx.account_key.pk.raw.clone()); - - // Check that address is unique - if all_used_addresses.contains(&established_address) { - eprintln!( - "A duplicate address \"{}\" found in a `validator_account` tx.", - established_address - ); - is_valid = false; - } else { - all_used_addresses.insert(established_address.clone()); - } + let established_address = { + let established_address = Address::Established(tx.address.clone()); + if !all_used_addresses.contains(&established_address) { + eprintln!( + "Unable to find established account with address \"{}\" in a \ + `validator_account` tx, to initialize a new validator with.", + established_address + ); + is_valid = false; + } + if validator_accounts.contains(&established_address) { + eprintln!( + "A duplicate validator \"{}\" found in a `validator_account` \ + tx.", + established_address + ); + is_valid = false; + } else { + validator_accounts.insert(established_address.clone()); + } + established_address + }; // Check the VP exists if !vps @@ -1056,6 +1062,7 @@ fn validate_signature( impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { fn from(tx: &SignedValidatorAccountTx) -> Self { let SignedValidatorAccountTx { + address, vp, commission_rate, max_commission_rate_change, @@ -1074,6 +1081,7 @@ impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { } = tx; Self { + address: address.clone(), vp: vp.clone(), commission_rate: *commission_rate, max_commission_rate_change: *max_commission_rate_change, From c15e750de8025b3746f65b52a4a51aad343cac69 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 22 Nov 2023 11:30:30 +0000 Subject: [PATCH 18/81] clippy is pleased --- apps/src/lib/cli.rs | 23 ++- apps/src/lib/client/utils.rs | 39 ++--- apps/src/lib/config/genesis.rs | 67 ++++---- apps/src/lib/config/genesis/chain.rs | 22 ++- apps/src/lib/config/genesis/templates.rs | 11 +- apps/src/lib/config/genesis/transactions.rs | 164 ++++++------------- apps/src/lib/node/ledger/shell/init_chain.rs | 22 +-- apps/src/lib/wallet/pre_genesis.rs | 13 +- sdk/src/wallet/pre_genesis.rs | 4 - sdk/src/wallet/store.rs | 5 - tests/src/e2e/ibc_tests.rs | 16 +- 11 files changed, 158 insertions(+), 228 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index bda9e4b6aa..d6957bdd62 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2867,7 +2867,7 @@ pub mod args { use std::str::FromStr; use namada::ibc::core::ics24_host::identifier::{ChannelId, PortId}; - use namada::types::address::Address; + use namada::types::address::{Address, EstablishedAddress}; use namada::types::chain::{ChainId, ChainIdPrefix}; use namada::types::dec::Dec; use namada::types::ethereum_events::EthAddress; @@ -3054,6 +3054,7 @@ pub mod args { pub const PROPOSAL_VOTE_ETH_OPT: ArgOpt = arg_opt("eth"); pub const PROPOSAL_VOTE: Arg = arg("vote"); pub const RAW_ADDRESS: Arg
= arg("address"); + pub const RAW_ADDRESS_ESTABLISHED: Arg = arg("address"); pub const RAW_ADDRESS_OPT: ArgOpt
= RAW_ADDRESS.opt(); pub const RAW_PUBLIC_KEY: Arg = arg("public-key"); pub const RAW_PUBLIC_KEY_OPT: ArgOpt = @@ -6751,6 +6752,7 @@ pub mod args { pub description: Option, pub website: Option, pub discord_handle: Option, + pub address: EstablishedAddress, } impl Args for InitGenesisValidator { @@ -6768,6 +6770,7 @@ pub mod args { let description = DESCRIPTION_OPT.parse(matches); let website = WEBSITE_OPT.parse(matches); let discord_handle = DISCORD_OPT.parse(matches); + let address = RAW_ADDRESS_ESTABLISHED.parse(matches); Self { alias, net_address, @@ -6780,11 +6783,16 @@ pub mod args { description, website, discord_handle, + address, } } fn def(app: App) -> App { app.arg(ALIAS.def().help("The validator address alias.")) + .arg(RAW_ADDRESS_ESTABLISHED.def().help( + "The address of an established account to be promoted to \ + a validator.", + )) .arg(NET_ADDRESS.def().help( "Static {host:port} of your validator node's P2P address. \ Namada uses port `26656` for P2P connections by default, \ @@ -6854,13 +6862,19 @@ pub mod args { pub struct SignGenesisTx { pub path: PathBuf, pub output: Option, + pub validator_alias: Option, } impl Args for SignGenesisTx { fn parse(matches: &ArgMatches) -> Self { let path = PATH.parse(matches); let output = OUTPUT.parse(matches); - Self { path, output } + let validator_alias = ALIAS_OPT.parse(matches); + Self { + path, + output, + validator_alias, + } } fn def(app: App) -> App { @@ -6872,6 +6886,11 @@ pub mod args { "Save the output to a TOML file. When not supplied, the \ signed transactions will be printed to stdout instead.", )) + .arg( + ALIAS_OPT + .def() + .help("Optional alias to a validator wallet."), + ) } } } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 431ea44a67..489c1af2eb 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -234,12 +234,10 @@ pub async fn join_network( .map(|(alias, _wallet)| alias.clone()); let validator_keys = validator_alias_and_pre_genesis_wallet.as_ref().map( |(_alias, wallet)| { - let validator_account_pk: common::PublicKey = - wallet.account_key.ref_to(); let tendermint_node_key: common::SecretKey = wallet.tendermint_node_key.clone(); let consensus_key: common::SecretKey = wallet.consensus_key.clone(); - (validator_account_pk, tendermint_node_key, consensus_key) + (tendermint_node_key, consensus_key) }, ); let node_mode = if validator_alias.is_some() { @@ -252,7 +250,7 @@ pub async fn join_network( let config = genesis.derive_config( &chain_dir, node_mode, - validator_keys.as_ref().map(|(pk, _, _)| pk), + validator_keys.as_ref().map(|(sk, _)| sk.ref_to()).as_ref(), allow_duplicate_ip, ); @@ -271,7 +269,7 @@ pub async fn join_network( crate::wallet::save(&wallet).unwrap(); // Setup the node for a genesis validator, if used - if let Some((_, tendermint_node_key, consensus_key)) = validator_keys { + if let Some((tendermint_node_key, consensus_key)) = validator_keys { println!( "Setting up validator keys in CometBFT. Consensus key: {}.", consensus_key.to_public() @@ -646,7 +644,6 @@ pub fn derive_genesis_addresses( println!(); println!("{} {}", "Address:".bold().bright_green(), tx.address); let keys = [ - ("Account key:", &tx.account_key.pk.raw), ("Consensus key:", &tx.consensus_key.pk.raw), ("Protocol key:", &tx.protocol_key.pk.raw), ("Tendermint node key:", &tx.tendermint_node_key.pk.raw), @@ -684,6 +681,7 @@ pub fn init_genesis_validator( description, website, discord_handle, + address, }: args::InitGenesisValidator, ) { // Validate the commission rate data @@ -725,6 +723,7 @@ pub fn init_genesis_validator( let (address, transactions) = genesis::transactions::init_validator( genesis::transactions::GenesisValidatorData { + address, commission_rate, max_commission_rate_change, net_address, @@ -857,7 +856,11 @@ pub fn validate_genesis_templates( /// Sign genesis transactions. pub fn sign_genesis_tx( global_args: args::Global, - args::SignGenesisTx { path, output }: args::SignGenesisTx, + args::SignGenesisTx { + path, + output, + validator_alias, + }: args::SignGenesisTx, ) { let (mut wallet, _wallet_file) = load_pre_genesis_wallet_or_exit(&global_args.base_dir); @@ -877,18 +880,16 @@ pub fn sign_genesis_tx( ); safe_exit(1); }); - if unsigned.validator_account.is_some() - && !unsigned.validator_account.as_ref().unwrap().is_empty() - { - eprintln!( - "Validator transactions must be signed with a validator wallet. \ - You can use `namada client utils init-genesis-validator` \ - supplied with the required arguments to generate a validator \ - wallet and sign the validator genesis transactions." - ); - safe_exit(1); - } - let signed = genesis::transactions::sign_txs(unsigned, &mut wallet); + let maybe_pre_genesis_wallet = validator_alias.and_then(|alias| { + let pre_genesis_dir = + validator_pre_genesis_dir(&global_args.base_dir, &alias); + crate::wallet::pre_genesis::load(&pre_genesis_dir).ok() + }); + let signed = genesis::transactions::sign_txs( + unsigned, + &mut wallet, + maybe_pre_genesis_wallet.as_ref(), + ); match output { Some(output_path) => { diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 52320210f6..ae8062ca00 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -25,7 +25,7 @@ use namada::types::{storage, token}; use serde::{Deserialize, Serialize}; #[cfg(all(any(test, feature = "benches"), not(feature = "integration")))] -use crate::config::genesis::chain::Finalized; +use crate::config::genesis::chain::{Finalized, FinalizedEstablishedAccountTx}; #[derive( Clone, @@ -312,6 +312,7 @@ pub fn make_dev_genesis( use std::time::Duration; use namada::ledger::eth_bridge::{Contracts, UpgradeableContract}; + use namada::ledger::pos::types::ValidatorMetaData; use namada::proto::{standalone_signature, SerializeWithBorsh}; use namada::types::address::wnam; use namada::types::chain::ChainIdPrefix; @@ -320,7 +321,6 @@ pub fn make_dev_genesis( use namada_sdk::wallet::alias::Alias; use crate::config::genesis::chain::{finalize, DeriveEstablishedAddress}; - use crate::config::genesis::transactions::UnsignedValidatorAccountTx; use crate::wallet::defaults; let mut current_path = std::env::current_dir() @@ -401,10 +401,8 @@ pub fn make_dev_genesis( .unwrap() .get(0) .unwrap(); - let genesis_addr = GenesisAddress::EstablishedAddress( - UnsignedValidatorAccountTx::from(&tx.tx) - .derive_established_address(), - ); + let genesis_addr = + GenesisAddress::EstablishedAddress(tx.tx.address.raw.clone()); let balance = *nam_balances.0.get(&genesis_addr).unwrap(); let bonded = { @@ -435,30 +433,45 @@ pub fn make_dev_genesis( testing::gen_keypair::() .try_to_sk() .unwrap(); - let account_keypair = consensus_keypair.clone(); let eth_cold_keypair = common::SecretKey::try_from_sk(&secp_eth_cold_keypair).unwrap(); let (protocol_keypair, eth_bridge_keypair) = defaults::validator_keys(); // add the validator - let (validator_address, account_pk) = if let Some(vals) = - genesis.transactions.validator_account.as_mut() - { + let validator_address = { + let vals = genesis.transactions.validator_account.as_mut().unwrap(); + let established_accounts = + genesis.transactions.established_account.as_mut().unwrap(); + + let tx = transactions::EstablishedAccountTx { + vp: "vp_user".to_string(), + public_keys: vec![StringEncoded::new( + consensus_keypair.ref_to(), + )], + threshold: 1, + }; + let address = tx.derive_established_address(); + let established_account_tx = FinalizedEstablishedAccountTx { + address: Address::Established(address.clone()), + tx, + }; + established_accounts.push(established_account_tx); + let validator_account_tx = transactions::ValidatorAccountTx { + address: StringEncoded::new(address.clone()), vp: "vp_validator".to_string(), commission_rate: Dec::new(5, 2).expect("This can't fail"), max_commission_rate_change: Dec::new(1, 2) .expect("This can't fail"), - email: "null@null.net".to_string(), - description: None, - website: None, - discord_handle: None, + metadata: ValidatorMetaData { + email: "null@null.net".to_string(), + description: None, + website: None, + discord_handle: None, + }, net_address: SocketAddr::new( IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8080, ), - account_key: StringEncoded { - raw: account_keypair.to_public(), - }, consensus_key: StringEncoded { raw: consensus_keypair.to_public(), }, @@ -475,21 +488,16 @@ pub fn make_dev_genesis( raw: eth_cold_keypair.to_public(), }, }; - let validator_address = - validator_account_tx.derive_established_address(); vals.push(chain::FinalizedValidatorAccountTx { - address: Address::Established(validator_address.clone()), + address: Address::Established(address.clone()), tx: transactions::ValidatorAccountTx { + address: StringEncoded::new(address.clone()), vp: validator_account_tx.vp, commission_rate: validator_account_tx.commission_rate, max_commission_rate_change: validator_account_tx .max_commission_rate_change, - email: validator_account_tx.email, - description: validator_account_tx.description, - website: validator_account_tx.website, - discord_handle: validator_account_tx.discord_handle, + metadata: validator_account_tx.metadata, net_address: validator_account_tx.net_address, - account_key: sign_pk(&account_keypair), consensus_key: sign_pk(&consensus_keypair), protocol_key: sign_pk(&protocol_keypair), tendermint_node_key: sign_pk(&consensus_keypair), @@ -497,9 +505,7 @@ pub fn make_dev_genesis( eth_cold_key: sign_pk(ð_cold_keypair), }, }); - (validator_address, account_keypair.ref_to()) - } else { - unreachable!() + address }; // credit nam tokens to validators such that they can bond { @@ -511,8 +517,9 @@ pub fn make_dev_genesis( let validator_addr = GenesisAddress::EstablishedAddress(validator_address.clone()); - let account_pk = - GenesisAddress::PublicKey(StringEncoded::new(account_pk)); + let account_pk = GenesisAddress::PublicKey(StringEncoded::new( + consensus_keypair.ref_to(), + )); nam_balances.0.insert(validator_addr, first_val_balance); nam_balances.0.insert(account_pk, first_val_balance); diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index fc3b42eac7..55f675b6d2 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -142,10 +142,10 @@ impl Finalized { wallet.extend(pre_genesis_wallet); } if let Some((alias, validator_wallet)) = validator { - let account_pk = validator_wallet.account_key.ref_to(); + let tendermint_pk = validator_wallet.tendermint_node_key.ref_to(); let address = self .transactions - .find_validator(&account_pk) + .find_validator(&tendermint_pk) .map(|tx| tx.address.clone()) .expect("Validator alias not found in genesis transactions."); wallet.extend_from_pre_genesis_validator( @@ -162,12 +162,10 @@ impl Finalized { &self, base_dir: &Path, node_mode: TendermintMode, - validator_account_pk: Option<&common::PublicKey>, + tendermint_pk: Option<&common::PublicKey>, allow_duplicate_ip: bool, ) -> Config { - if node_mode != TendermintMode::Validator - && validator_account_pk.is_some() - { + if node_mode != TendermintMode::Validator && tendermint_pk.is_some() { println!( "Warning: Validator alias used to derive config, but node \ mode is not validator, it is {node_mode:?}!" @@ -178,10 +176,10 @@ impl Finalized { // Derive persistent peers from genesis let persistent_peers = self.derive_persistent_peers(); - // If `validator_account_pk` is given, find its net_address + // If `tendermint_pk` is given, find its net_address let validator_net_and_tm_address = - if let Some(account_pk) = validator_account_pk { - self.transactions.find_validator(account_pk).map( + if let Some(tendermint_pk) = tendermint_pk { + self.transactions.find_validator(tendermint_pk).map( |validator_tx| { ( validator_tx.tx.net_address, @@ -653,7 +651,7 @@ impl FinalizedTransactions { let validator_account = validator_account.map(|txs| { txs.into_iter() .map(|tx| FinalizedValidatorAccountTx { - address: Address::Established(tx.address.clone()), + address: Address::Established(tx.address.raw.clone()), tx, }) .collect() @@ -667,12 +665,12 @@ impl FinalizedTransactions { fn find_validator( &self, - validator_account_pk: &common::PublicKey, + tendermint_pk: &common::PublicKey, ) -> Option<&FinalizedValidatorAccountTx> { let validator_accounts = self.validator_account.as_ref()?; validator_accounts .iter() - .find(|tx| &tx.tx.account_key.pk.raw == validator_account_pk) + .find(|tx| &tx.tx.tendermint_node_key.pk.raw == tendermint_pk) } } diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index a9f513214e..620c00a77e 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -18,7 +18,7 @@ use namada::types::token::{ use serde::{Deserialize, Serialize}; use super::toml_utils::{read_toml, write_toml}; -use super::transactions::{self, Transactions, UnsignedValidatorAccountTx}; +use super::transactions::{self, Transactions}; use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::transactions::{BondTx, SignedBondTx}; use crate::config::genesis::GenesisAddress; @@ -825,15 +825,6 @@ pub fn validate_parameters( } } - if let Some(accs) = &txs.validator_account { - if accs.iter().any(|acct| { - let addr = - UnsignedValidatorAccountTx::from(acct).derive_address(); - &addr == steward - }) { - found_steward = true; - } - } is_valid = found_steward && is_valid; if !is_valid { eprintln!( diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 6d93507117..7ad8979e06 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -33,6 +33,7 @@ use crate::wallet::CliWalletUtils; pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; pub struct GenesisValidatorData { + pub address: EstablishedAddress, pub commission_rate: Dec, pub max_commission_rate_change: Dec, pub net_address: SocketAddr, @@ -49,6 +50,7 @@ pub struct GenesisValidatorData { pub fn sign_txs( txs: UnsignedTransactions, wallet: &mut Wallet, + validator_wallet: Option<&ValidatorWallet>, ) -> Transactions { let UnsignedTransactions { established_account, @@ -56,32 +58,22 @@ pub fn sign_txs( bond, } = txs; - // Validate input first - if validator_account.is_some() && !validator_account.unwrap().is_empty() { - panic!( - "Validator transactions must be signed with a validator wallet." - ); - } - - if let Some(bonds) = bond.as_ref() { - for bond in bonds { - if bond.source.address() == bond.validator { - panic!( - "Validator self-bonds must be signed with a validator \ - wallet." - ) - } - } - } - - // Sign all the transactions - let validator_account = None; - let bond = bond.map(|tx| { - tx.into_iter() + // Sign bond txs + let bond = bond.map(|txs| { + txs.into_iter() .map(|tx| sign_delegation_bond_tx(tx, wallet, &established_account)) .collect() }); + // Sign validator account txs + let validator_account = validator_account.map(|txs| { + let validator_wallet = validator_wallet + .expect("Validator wallet required to sign validator account txs"); + txs.into_iter() + .map(|tx| sign_validator_account_tx(tx, validator_wallet)) + .collect() + }); + Transactions { established_account, validator_account, @@ -96,7 +88,7 @@ pub fn parse_unsigned( toml::from_slice(bytes) } -/// Create signed [`Transactions`] for a genesis validator. +/// Create [`UnsignedTransactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { address, @@ -110,10 +102,9 @@ pub fn init_validator( discord_handle, }: GenesisValidatorData, validator_wallet: &ValidatorWallet, -) -> (Address, Transactions) { +) -> (Address, UnsignedTransactions) { let unsigned_validator_account_tx = UnsignedValidatorAccountTx { - address, - account_key: StringEncoded::new(validator_wallet.account_key.ref_to()), + address: StringEncoded::new(address), consensus_key: StringEncoded::new( validator_wallet.consensus_key.ref_to(), ), @@ -136,17 +127,17 @@ pub fn init_validator( vp: "vp_user".to_string(), commission_rate, max_commission_rate_change, - email, - description, - website, - discord_handle, net_address, + metadata: ValidatorMetaData { + email, + description, + website, + discord_handle, + }, }; - let unsigned_validator_addr = unsigned_validator_account_tx.address.clone(); - let validator_account = Some(vec![sign_validator_account_tx( - unsigned_validator_account_tx, - validator_wallet, - )]); + let unsigned_validator_addr = + unsigned_validator_account_tx.address.raw.clone(); + let validator_account = Some(vec![unsigned_validator_account_tx]); let bond = if self_bond_amount.amount.is_zero() { None @@ -158,12 +149,11 @@ pub fn init_validator( validator: Address::Established(unsigned_validator_addr.clone()), amount: self_bond_amount, }; - let bond_tx = sign_self_bond_tx(unsigned_bond_tx, validator_wallet); - Some(vec![bond_tx]) + Some(vec![unsigned_bond_tx]) }; let address = Address::Established(unsigned_validator_addr); - let txs = Transactions { + let txs = UnsignedTransactions { validator_account, bond, ..Default::default() @@ -177,7 +167,6 @@ pub fn sign_validator_account_tx( validator_wallet: &ValidatorWallet, ) -> SignedValidatorAccountTx { // Sign the tx with every validator key to authorize their usage - let account_key_sig = sign_tx(&unsigned_tx, &validator_wallet.account_key); let consensus_key_sig = sign_tx(&unsigned_tx, &validator_wallet.consensus_key); let protocol_key_sig = sign_tx( @@ -192,26 +181,18 @@ pub fn sign_validator_account_tx( let ValidatorAccountTx { address, - account_key, consensus_key, protocol_key, tendermint_node_key, vp, commission_rate, max_commission_rate_change, - email, - description, - website, - discord_handle, net_address, eth_hot_key, eth_cold_key, + metadata, } = unsigned_tx; - let account_key = SignedPk { - pk: account_key, - authorization: account_key_sig, - }; let consensus_key = SignedPk { pk: consensus_key, authorization: consensus_key_sig, @@ -237,32 +218,19 @@ pub fn sign_validator_account_tx( SignedValidatorAccountTx { address, - account_key, consensus_key, protocol_key, tendermint_node_key, vp, commission_rate, max_commission_rate_change, - email, - description, - website, - discord_handle, net_address, eth_hot_key, eth_cold_key, + metadata, } } -pub fn sign_self_bond_tx( - unsigned_tx: BondTx, - validator_wallet: &ValidatorWallet, -) -> SignedBondTx { - let mut signed = SignedBondTx::from(unsigned_tx); - signed.sign(std::slice::from_ref(&validator_wallet.account_key)); - signed -} - pub fn sign_delegation_bond_tx( unsigned_tx: BondTx, wallet: &mut Wallet, @@ -424,7 +392,6 @@ pub struct ValidatorAccountTx { /// P2P IP:port pub net_address: SocketAddr, /// PKs have to come last in TOML to avoid `ValueAfterTable` error - pub account_key: PK, pub consensus_key: PK, pub protocol_key: PK, pub tendermint_node_key: PK, @@ -460,7 +427,21 @@ impl DeriveEstablishedAddress for EstablishedAccountTx { const SALT: &'static str = "established-account-tx"; } -pub type SignedBondTx = Signed>; +#[derive( + Clone, + Debug, + Deserialize, + Serialize, + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, +)] +pub struct SignedBondTx { + #[serde(flatten)] + pub data: BondTx, + pub signatures: Vec>, +} impl SignedBondTx where @@ -587,22 +568,6 @@ impl From> for SignedBondTx { } } -#[derive( - Clone, - Debug, - Deserialize, - Serialize, - BorshSerialize, - BorshDeserialize, - PartialEq, - Eq, -)] -pub struct Signed { - #[serde(flatten)] - pub data: T, - pub signatures: Vec>, -} - #[derive( Clone, Debug, @@ -631,8 +596,7 @@ pub fn validate( Address, (Vec, u8), > = BTreeMap::default(); - let mut validator_accounts: BTreeMap = - BTreeMap::default(); + let mut validator_accounts = BTreeSet::new(); let Transactions { ref established_account, @@ -738,7 +702,6 @@ pub fn validate( max_commission_rate_change: acct .max_commission_rate_change, net_address: acct.net_address, - account_key: acct.account_key, consensus_key: acct.consensus_key, protocol_key: acct.protocol_key, tendermint_node_key: acct.tendermint_node_key, @@ -757,7 +720,7 @@ fn validate_bond( tx: SignedBondTx, balances: &mut BTreeMap, established_accounts: &BTreeMap, u8)>, - validator_accounts: &BTreeMap, + validator_accounts: &BTreeSet
, parameters: &Parameters, ) -> Option> { // Check signature @@ -771,11 +734,6 @@ fn validate_bond( established_accounts .get(&established_addr) .map(|(pks, t)| (pks.as_slice(), *t)) - .or_else(|| { - validator_accounts - .get(&established_addr) - .map(|addr| (std::slice::from_ref(addr), 1)) - }) } GenesisAddress::PublicKey(pk) => { Some((std::slice::from_ref(&pk.raw), 1)) @@ -807,7 +765,7 @@ fn validate_bond( } = &validated_bond; // Check that the validator exists - if !validator_accounts.contains_key(validator) { + if !validator_accounts.contains(validator) { eprintln!( "Invalid bond tx. The target validator \"{validator}\" account \ not found." @@ -928,7 +886,7 @@ pub fn validate_validator_account( let mut is_valid = true; let established_address = { - let established_address = Address::Established(tx.address.clone()); + let established_address = Address::Established(tx.address.raw.clone()); if !all_used_addresses.contains(&established_address) { eprintln!( "Unable to find established account with address \"{}\" in a \ @@ -965,18 +923,6 @@ pub fn validate_validator_account( // Check keys authorizations let unsigned = UnsignedValidatorAccountTx::from(tx); - if !validate_signature( - &unsigned, - &tx.account_key.pk.raw, - &tx.account_key.authorization.raw, - ) { - eprintln!( - "Invalid `account_key` authorization for `validator_account` tx \ - with address \"{}\".", - established_address - ); - is_valid = false; - } if !validate_signature( &unsigned, &tx.consensus_key.pk.raw, @@ -1066,12 +1012,8 @@ impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { vp, commission_rate, max_commission_rate_change, - email, - description, - website, - discord_handle, + metadata, net_address, - account_key, consensus_key, protocol_key, tendermint_node_key, @@ -1085,12 +1027,8 @@ impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { vp: vp.clone(), commission_rate: *commission_rate, max_commission_rate_change: *max_commission_rate_change, - email: email.clone(), - description: description.clone(), - website: website.clone(), - discord_handle: discord_handle.clone(), + metadata: metadata.clone(), net_address: *net_address, - account_key: account_key.pk.clone(), consensus_key: consensus_key.pk.clone(), protocol_key: protocol_key.pk.clone(), tendermint_node_key: tendermint_node_key.pk.clone(), diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index c21463b28d..ee9b7af289 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -14,7 +14,6 @@ use namada::types::key::*; use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::vm::validate_untrusted_wasm; use namada_sdk::eth_bridge::EthBridgeStatus; -use namada_sdk::proof_of_stake::types::ValidatorMetaData; use namada_sdk::proof_of_stake::PosParams; use super::*; @@ -396,12 +395,8 @@ where vp, commission_rate, max_commission_rate_change, - email, - description, - website, - discord_handle, + metadata, net_address: _, - account_key, consensus_key, protocol_key, tendermint_node_key: _, @@ -420,14 +415,6 @@ where self.wl_storage .write_bytes(&Key::validity_predicate(address), code_hash) .expect("Unable to write user VP"); - // Validator account key - storage_api::account::set_public_key_at( - &mut self.wl_storage, - address, - &account_key.pk.raw, - 0, - ) - .unwrap(); self.wl_storage .write(&protocol_pk_key(address), &protocol_key.pk.raw) @@ -447,12 +434,7 @@ where current_epoch, commission_rate: *commission_rate, max_commission_rate_change: *max_commission_rate_change, - metadata: ValidatorMetaData { - email: email.clone(), - description: description.clone(), - website: website.clone(), - discord_handle: discord_handle.clone(), - }, + metadata: metadata.clone(), offset_opt: Some(0), }, ) { diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index 7ff77407fb..12f88ed99a 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -72,18 +72,12 @@ pub fn load(store_dir: &Path) -> Result { })?; let store = ValidatorStore::decode(store).map_err(ReadError::Decode)?; - let password = if store.account_key.is_encrypted() - || store.consensus_key.is_encrypted() - || store.account_key.is_encrypted() - { + let password = if store.consensus_key.is_encrypted() { Some(CliWalletUtils::read_password(false)) } else { None }; - let account_key = store - .account_key - .get::(true, password.clone())?; let consensus_key = store .consensus_key .get::(true, password.clone())?; @@ -97,7 +91,6 @@ pub fn load(store_dir: &Path) -> Result { Ok(ValidatorWallet { store, - account_key, consensus_key, eth_cold_key, eth_hot_key, @@ -111,8 +104,6 @@ fn gen( scheme: SchemeType, password: Option>, ) -> ValidatorWallet { - let (account_key, account_sk) = - gen_key_to_store(scheme, password.clone(), &mut OsRng); let (consensus_key, consensus_sk) = gen_key_to_store( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, @@ -130,7 +121,6 @@ fn gen( let validator_keys = gen_validator_keys(None, None, scheme); let eth_hot_key = validator_keys.eth_bridge_keypair.clone(); let store = ValidatorStore { - account_key, consensus_key, eth_cold_key, tendermint_node_key, @@ -138,7 +128,6 @@ fn gen( }; ValidatorWallet { store, - account_key: account_sk, consensus_key: consensus_sk, eth_cold_key: eth_cold_sk, eth_hot_key, diff --git a/sdk/src/wallet/pre_genesis.rs b/sdk/src/wallet/pre_genesis.rs index a0c9d4d58e..abbb918470 100644 --- a/sdk/src/wallet/pre_genesis.rs +++ b/sdk/src/wallet/pre_genesis.rs @@ -30,8 +30,6 @@ pub enum ReadError { pub struct ValidatorWallet { /// The wallet store that can be written/read to/from TOML pub store: ValidatorStore, - /// Cryptographic keypair for validator account key - pub account_key: common::SecretKey, /// Cryptographic keypair for consensus key pub consensus_key: common::SecretKey, /// Cryptographic keypair for eth cold key @@ -46,8 +44,6 @@ pub struct ValidatorWallet { /// genesis setup. #[derive(Serialize, Deserialize, Debug)] pub struct ValidatorStore { - /// Cryptographic keypair for validator account key - pub account_key: wallet::StoredKeypair, /// Cryptographic keypair for consensus key pub consensus_key: wallet::StoredKeypair, /// Cryptographic keypair for eth cold key diff --git a/sdk/src/wallet/store.rs b/sdk/src/wallet/store.rs index 980aa6ae70..6c7b539c2d 100644 --- a/sdk/src/wallet/store.rs +++ b/sdk/src/wallet/store.rs @@ -612,14 +612,12 @@ impl Store { validator_alias: Alias, other: pre_genesis::ValidatorWallet, ) { - let account_key_alias = alias::validator_key(&validator_alias); let consensus_key_alias = alias::validator_consensus_key(&validator_alias); let tendermint_node_key_alias = alias::validator_tendermint_node_key(&validator_alias); let keys = [ - (account_key_alias.clone(), other.store.account_key), (consensus_key_alias.clone(), other.store.consensus_key), ( tendermint_node_key_alias.clone(), @@ -628,11 +626,9 @@ impl Store { ]; self.secret_keys.extend(keys.into_iter()); - let account_pk = other.account_key.ref_to(); let consensus_pk = other.consensus_key.ref_to(); let tendermint_node_pk = other.tendermint_node_key.ref_to(); let addresses = [ - (account_key_alias.clone(), (&account_pk).into()), (consensus_key_alias.clone(), (&consensus_pk).into()), ( tendermint_node_key_alias.clone(), @@ -642,7 +638,6 @@ impl Store { self.addresses.extend(addresses.into_iter()); let pkhs = [ - ((&account_pk).into(), account_key_alias), ((&consensus_pk).into(), consensus_key_alias), ((&tendermint_node_pk).into(), tendermint_node_key_alias), ]; diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index 08a864f3d1..b0d0cf47d1 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -73,6 +73,7 @@ use namada_apps::facade::tendermint::block::Header as TmHeader; use namada_apps::facade::tendermint::merkle::proof::ProofOps as TmProof; use namada_apps::facade::tendermint_config::net::Address as TendermintAddress; use namada_apps::facade::tendermint_rpc::{Client, HttpClient, Url}; +use namada_core::types::string_encoding::StringEncoded; use namada_sdk::masp::fs::FsShieldedUtils; use prost::Message; use setup::constants::*; @@ -263,13 +264,26 @@ fn setup_two_single_node_nets() -> Result<(Test, Test)> { // chain b's validator needs to listen on a different port than chain a's // validator let validator_pk = get_validator_pk(&test_b, &Who::Validator(0)).unwrap(); + let validator_addr = genesis_b + .transactions + .established_account + .as_ref() + .unwrap() + .iter() + .find_map(|acct| { + acct.tx + .public_keys + .contains(&StringEncoded::new(validator_pk.clone())) + .then(|| acct.address.clone()) + }) + .unwrap(); let validator_tx = genesis_b .transactions .validator_account .as_mut() .unwrap() .iter_mut() - .find(|val| val.tx.account_key.pk.raw == validator_pk) + .find(|val| val.address == validator_addr) .unwrap(); let new_port = validator_tx.tx.net_address.port() + setup::ANOTHER_CHAIN_PORT_OFFSET; From c4260b6bf8f564c0fc4ea942b613c22008188d0b Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 22 Nov 2023 15:05:35 +0000 Subject: [PATCH 19/81] Revert "Remove established account init CLI subcmd" This reverts commit c50b0e33c653a525cd6870e5e6c69d70acacecd6. --- apps/src/lib/cli.rs | 65 ++++++++++++++++++++ apps/src/lib/cli/client.rs | 3 + apps/src/lib/client/utils.rs | 67 +++++++++++++++++++++ apps/src/lib/config/genesis/transactions.rs | 34 +++++++++++ 4 files changed, 169 insertions(+) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index d6957bdd62..e675e75f15 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2180,6 +2180,7 @@ pub mod cmds { ValidateWasm(ValidateWasm), InitNetwork(InitNetwork), DeriveGenesisAddresses(DeriveGenesisAddresses), + InitGenesisEstablishedAccount(InitGenesisEstablishedAccount), InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), @@ -2202,6 +2203,8 @@ pub mod cmds { SubCmd::parse(matches).map(Self::InitNetwork); let derive_addresses = SubCmd::parse(matches).map(Self::DeriveGenesisAddresses); + let init_established = SubCmd::parse(matches) + .map(Self::InitGenesisEstablishedAccount); let init_genesis = SubCmd::parse(matches).map(Self::InitGenesisValidator); let pk_to_tm_address = @@ -2218,6 +2221,7 @@ pub mod cmds { .or(validate_wasm) .or(init_network) .or(derive_addresses) + .or(init_established) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) @@ -2235,6 +2239,7 @@ pub mod cmds { .subcommand(ValidateWasm::def()) .subcommand(InitNetwork::def()) .subcommand(DeriveGenesisAddresses::def()) + .subcommand(InitGenesisEstablishedAccount::def()) .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) @@ -2344,6 +2349,29 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct InitGenesisEstablishedAccount( + pub args::InitGenesisEstablishedAccount, + ); + + impl SubCmd for InitGenesisEstablishedAccount { + const CMD: &'static str = "init-genesis-established-account"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + Self(args::InitGenesisEstablishedAccount::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Initialize an established account available at genesis.", + ) + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator(pub args::InitGenesisValidator); @@ -2904,6 +2932,7 @@ pub mod args { pub const ALIAS_OPT: ArgOpt = ALIAS.opt(); pub const ALIAS: Arg = arg("alias"); pub const ALIAS_FORCE: ArgFlag = flag("alias-force"); + pub const ALIAS_MANY: ArgMulti = arg_multi("alias"); pub const ALLOW_DUPLICATE_IP: ArgFlag = flag("allow-duplicate-ip"); pub const AMOUNT: Arg = arg("amount"); pub const ARCHIVE_DIR: ArgOpt = arg_opt("archive-dir"); @@ -6739,6 +6768,42 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct InitGenesisEstablishedAccount { + pub vp: String, + pub wallet_aliases: Vec, + pub threshold: u8, + } + + impl Args for InitGenesisEstablishedAccount { + fn parse(matches: &ArgMatches) -> Self { + let wallet_aliases = ALIAS_MANY.parse(matches); + let vp = VP.parse(matches).unwrap_or_else(|| "vp_user".to_string()); + let threshold = THRESOLD.parse(matches).unwrap_or(1); + Self { + wallet_aliases, + vp, + threshold, + } + } + + fn def(app: App) -> App { + app.arg( + ALIAS_MANY + .def() + .help("The aliases of the keys to use from the wallet."), + ) + .arg(THRESOLD.def().help( + "The minimum number of signatures to be provided for \ + authorization. Must be less than or equal to the maximum \ + number of key aliases provided.", + )) + .arg(VP.def().help( + "The validity predicate of the account. Defaults to `vp_user`.", + )) + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator { pub alias: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 472b7ae099..2f06a497d4 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -637,6 +637,9 @@ impl CliApi { Utils::DeriveGenesisAddresses(DeriveGenesisAddresses(args)) => { utils::derive_genesis_addresses(global_args, args) } + Utils::InitGenesisEstablishedAccount( + InitGenesisEstablishedAccount(args), + ) => utils::init_genesis_established_account(global_args, args), Utils::InitGenesisValidator(InitGenesisValidator(args)) => { utils::init_genesis_validator(global_args, args) } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 489c1af2eb..f7a18ed867 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -9,6 +9,8 @@ use color_eyre::owo_colors::OwoColorize; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; +use itertools::Itertools; +use namada::core::types::string_encoding::StringEncoded; use namada::types::chain::ChainId; use namada::types::dec::Dec; use namada::types::key::*; @@ -665,6 +667,62 @@ pub fn derive_genesis_addresses( } } +/// Initialize a genesis established account. +/// key into a special "pre-genesis" wallet. +pub fn init_genesis_established_account( + global_args: args::Global, + args: args::InitGenesisEstablishedAccount, +) { + let (mut pre_genesis_wallet, _) = + load_pre_genesis_wallet_or_exit(&global_args.base_dir); + + let public_keys: Vec<_> = args + .wallet_aliases + .iter() + .map(|alias| { + let sk = pre_genesis_wallet + .find_secret_key(alias, None) + .unwrap_or_else(|err| { + eprintln!( + "Failed to look-up `{alias}` in the pre-genesis \ + wallet: {err}", + ); + safe_exit(1) + }); + StringEncoded::new(sk.ref_to()) + }) + .collect(); + + let (address, txs) = genesis::transactions::init_established_account( + args.vp, + public_keys, + args.threshold, + ); + let toml_path = { + let pre_genesis_dir = global_args.base_dir.join(PRE_GENESIS_DIR); + established_acc_pre_genesis_txs_file( + &args.wallet_aliases.iter().join("_"), + &pre_genesis_dir, + ) + }; + let toml_path_str = toml_path.to_string_lossy(); + + let genesis_part = toml::to_string(&txs).unwrap(); + fs::write(&toml_path, genesis_part).unwrap_or_else(|err| { + eprintln!( + "Couldn't write pre-genesis transactions file to {toml_path_str}. \ + Failed with: {err}", + ); + safe_exit(1) + }); + + println!( + "{}: {address}", + "Derived established account address".bold() + ); + println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); +} + /// Initialize genesis validator's address, consensus key and validator account /// key into a special "pre-genesis" wallet. pub fn init_genesis_validator( @@ -838,6 +896,15 @@ pub fn validator_pre_genesis_txs_file(pre_genesis_path: &Path) -> PathBuf { pre_genesis_path.join("transactions.toml") } +/// The default path to an established account txs file. +pub fn established_acc_pre_genesis_txs_file( + wallet_key_alias: &str, + pre_genesis_path: &Path, +) -> PathBuf { + pre_genesis_path + .join(format!("established-account-tx-{wallet_key_alias}.toml")) +} + /// The default validator pre-genesis directory pub fn validator_pre_genesis_dir(base_dir: &Path, alias: &str) -> PathBuf { base_dir.join(PRE_GENESIS_DIR).join(alias) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 7ad8979e06..6bf25be086 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -88,6 +88,25 @@ pub fn parse_unsigned( toml::from_slice(bytes) } +/// Create signed [`Transactions`] for an established account. +pub fn init_established_account( + vp: String, + public_keys: Vec>, + threshold: u8, +) -> (Address, Transactions) { + let unsigned_tx = EstablishedAccountTx { + vp, + threshold, + public_keys, + }; + let address = unsigned_tx.derive_address(); + let txs = Transactions { + established_account: Some(vec![unsigned_tx]), + ..Default::default() + }; + (address, txs) +} + /// Create [`UnsignedTransactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { @@ -837,6 +856,21 @@ pub fn validate_established_account( eprintln!("An established account may not have zero thresold"); is_valid = false; } + if tx.threshold as usize > tx.public_keys.len() { + eprintln!( + "An established account may not have a threshold ({}) greater \ + than the number of public keys associated with it ({})", + tx.threshold, + tx.public_keys.len() + ); + is_valid = false; + } + if tx.public_keys.len() > u8::MAX as usize { + eprintln!( + "The number of configured public keys is way too fucking big" + ); + is_valid = false; + } established_accounts.insert( established_address.clone(), ( From 974ecea8ee6223965211bfa7faccc6d1d401557e Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 22 Nov 2023 15:51:54 +0000 Subject: [PATCH 20/81] Unify genesis templates --- apps/src/lib/cli.rs | 55 ++++++++++ apps/src/lib/cli/client.rs | 3 + apps/src/lib/client/utils.rs | 114 ++++++++++++++++++++ apps/src/lib/config/genesis/transactions.rs | 7 ++ 4 files changed, 179 insertions(+) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index e675e75f15..ac6de317db 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2187,6 +2187,7 @@ pub mod cmds { EpochSleep(EpochSleep), ValidateGenesisTemplates(ValidateGenesisTemplates), SignGenesisTx(SignGenesisTx), + UnifyGenesisTemplates(UnifyGenesisTemplates), } impl SubCmd for Utils { @@ -2216,6 +2217,8 @@ pub mod cmds { SubCmd::parse(matches).map(Self::ValidateGenesisTemplates); let genesis_tx = SubCmd::parse(matches).map(Self::SignGenesisTx); + let unify_genesis_templates = + SubCmd::parse(matches).map(Self::UnifyGenesisTemplates); join_network .or(fetch_wasms) .or(validate_wasm) @@ -2228,6 +2231,7 @@ pub mod cmds { .or(epoch_sleep) .or(validate_genesis_templates) .or(genesis_tx) + .or(unify_genesis_templates) }) } @@ -2246,6 +2250,7 @@ pub mod cmds { .subcommand(EpochSleep::def()) .subcommand(ValidateGenesisTemplates::def()) .subcommand(SignGenesisTx::def()) + .subcommand(UnifyGenesisTemplates::def()) .subcommand_required(true) .arg_required_else_help(true) } @@ -2349,6 +2354,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct UnifyGenesisTemplates(pub args::UnifyGenesisTemplates); + + impl SubCmd for UnifyGenesisTemplates { + const CMD: &'static str = "unify-genesis-templates"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + Self(args::UnifyGenesisTemplates::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Concatenate all the given genesis templates into a \ + single (unified) genesis transactions file.", + ) + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct InitGenesisEstablishedAccount( pub args::InitGenesisEstablishedAccount, @@ -3063,6 +3090,7 @@ pub mod args { pub const OWNER: Arg = arg("owner"); pub const OWNER_OPT: ArgOpt = OWNER.opt(); pub const PATH: Arg = arg("path"); + pub const PATH_MANY: ArgMulti = arg_multi("path"); pub const PIN: ArgFlag = flag("pin"); pub const PORT_ID: ArgDefault = arg_default( "port-id", @@ -3101,6 +3129,7 @@ pub mod args { pub const SIGNING_KEY: Arg = arg("signing-key"); pub const SIGNING_KEYS: ArgMulti = arg_multi("signing-keys"); pub const SIGNATURES: ArgMulti = arg_multi("signatures"); + pub const SIGNED: ArgFlag = flag("signed"); pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); pub const STEWARD: Arg = arg("steward"); @@ -6804,6 +6833,32 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct UnifyGenesisTemplates { + pub paths: Vec, + pub signed: bool, + } + + impl Args for UnifyGenesisTemplates { + fn parse(matches: &ArgMatches) -> Self { + let paths = PATH_MANY.parse(matches); + let signed = SIGNED.parse(matches); + Self { paths, signed } + } + + fn def(app: App) -> App { + app.arg(PATH_MANY.def().help( + "Paths to genesis transaction toml files. Repeat as many \ + times as needed.", + )) + .arg( + SIGNED.def().help( + "Whether the genesis tx toml files are signed or not.", + ), + ) + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator { pub alias: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 2f06a497d4..1dc76054af 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -666,6 +666,9 @@ impl CliApi { Utils::SignGenesisTx(SignGenesisTx(args)) => { utils::sign_genesis_tx(global_args, args) } + Utils::UnifyGenesisTemplates(UnifyGenesisTemplates(args)) => { + utils::unify_genesis_templates(args) + } }, } Ok(()) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index f7a18ed867..c2d2a0050d 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -809,6 +809,120 @@ pub fn init_genesis_validator( println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } +/// Concatenate all the given genesis templates into a single (unified) +/// genesis transactions file. +pub fn unify_genesis_templates( + args::UnifyGenesisTemplates { signed, paths }: args::UnifyGenesisTemplates, +) { + if signed { + unify_genesis_templates_signed(&paths); + } else { + unify_genesis_templates_unsigned(&paths); + } +} + +fn unify_genesis_templates_signed(paths: &[PathBuf]) { + let mut transactions = genesis::transactions::Transactions::< + genesis::templates::Unvalidated, + > { + established_account: Some(vec![]), + validator_account: Some(vec![]), + bond: Some(vec![]), + }; + + for path in paths { + let contents = fs::read(path).unwrap_or_else(|err| { + eprintln!( + "Unable to read from file {}. Failed with {err}.", + path.to_string_lossy() + ); + safe_exit(1); + }); + let mut parsed = genesis::transactions::parse_signed(&contents) + .unwrap_or_else(|err| { + eprintln!( + "Unable to parse the TOML from {}. Failed with {err}.", + path.to_string_lossy() + ); + safe_exit(1); + }); + + if let Some((accum, txs)) = transactions + .established_account + .as_mut() + .zip(parsed.established_account.as_mut()) + { + accum.append(txs); + } + + if let Some((accum, txs)) = transactions + .validator_account + .as_mut() + .zip(parsed.validator_account.as_mut()) + { + accum.append(txs); + } + + if let Some((accum, txs)) = + transactions.bond.as_mut().zip(parsed.bond.as_mut()) + { + accum.append(txs); + } + } + + println!("{}", toml::to_string(&transactions).unwrap()); +} + +fn unify_genesis_templates_unsigned(paths: &[PathBuf]) { + let mut transactions = genesis::transactions::UnsignedTransactions { + established_account: Some(vec![]), + validator_account: Some(vec![]), + bond: Some(vec![]), + }; + + for path in paths { + let contents = fs::read(path).unwrap_or_else(|err| { + eprintln!( + "Unable to read from file {}. Failed with {err}.", + path.to_string_lossy() + ); + safe_exit(1); + }); + let mut parsed = genesis::transactions::parse_unsigned(&contents) + .unwrap_or_else(|err| { + eprintln!( + "Unable to parse the TOML from {}. Failed with {err}.", + path.to_string_lossy() + ); + safe_exit(1); + }); + + if let Some((accum, txs)) = transactions + .established_account + .as_mut() + .zip(parsed.established_account.as_mut()) + { + accum.append(txs); + } + + if let Some((accum, txs)) = transactions + .validator_account + .as_mut() + .zip(parsed.validator_account.as_mut()) + { + accum.append(txs); + } + + if let Some((accum, txs)) = + transactions.bond.as_mut().zip(parsed.bond.as_mut()) + { + accum.append(txs); + } + } + + println!("{}", toml::to_string(&transactions).unwrap()); +} + /// Try to load a pre-genesis wallet or return nothing, /// if it cannot be found. pub fn try_load_pre_genesis_wallet( diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 6bf25be086..7ef398c7a5 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -88,6 +88,13 @@ pub fn parse_unsigned( toml::from_slice(bytes) } +/// Parse [`Transactions`] from bytes. +pub fn parse_signed( + bytes: &[u8], +) -> Result, toml::de::Error> { + toml::from_slice(bytes) +} + /// Create signed [`Transactions`] for an established account. pub fn init_established_account( vp: String, From 0232679ecbaa8b9404e251cac16fb12114b4fafb Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 23 Nov 2023 10:14:01 +0100 Subject: [PATCH 21/81] Revert "Unify genesis templates" This reverts commit b95c14bc0b9f5312061ed7db328918d5add53ba3. --- apps/src/lib/cli.rs | 55 ---------- apps/src/lib/cli/client.rs | 3 - apps/src/lib/client/utils.rs | 114 -------------------- apps/src/lib/config/genesis/transactions.rs | 7 -- 4 files changed, 179 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index ac6de317db..e675e75f15 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2187,7 +2187,6 @@ pub mod cmds { EpochSleep(EpochSleep), ValidateGenesisTemplates(ValidateGenesisTemplates), SignGenesisTx(SignGenesisTx), - UnifyGenesisTemplates(UnifyGenesisTemplates), } impl SubCmd for Utils { @@ -2217,8 +2216,6 @@ pub mod cmds { SubCmd::parse(matches).map(Self::ValidateGenesisTemplates); let genesis_tx = SubCmd::parse(matches).map(Self::SignGenesisTx); - let unify_genesis_templates = - SubCmd::parse(matches).map(Self::UnifyGenesisTemplates); join_network .or(fetch_wasms) .or(validate_wasm) @@ -2231,7 +2228,6 @@ pub mod cmds { .or(epoch_sleep) .or(validate_genesis_templates) .or(genesis_tx) - .or(unify_genesis_templates) }) } @@ -2250,7 +2246,6 @@ pub mod cmds { .subcommand(EpochSleep::def()) .subcommand(ValidateGenesisTemplates::def()) .subcommand(SignGenesisTx::def()) - .subcommand(UnifyGenesisTemplates::def()) .subcommand_required(true) .arg_required_else_help(true) } @@ -2354,28 +2349,6 @@ pub mod cmds { } } - #[derive(Clone, Debug)] - pub struct UnifyGenesisTemplates(pub args::UnifyGenesisTemplates); - - impl SubCmd for UnifyGenesisTemplates { - const CMD: &'static str = "unify-genesis-templates"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - Self(args::UnifyGenesisTemplates::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Concatenate all the given genesis templates into a \ - single (unified) genesis transactions file.", - ) - .add_args::() - } - } - #[derive(Clone, Debug)] pub struct InitGenesisEstablishedAccount( pub args::InitGenesisEstablishedAccount, @@ -3090,7 +3063,6 @@ pub mod args { pub const OWNER: Arg = arg("owner"); pub const OWNER_OPT: ArgOpt = OWNER.opt(); pub const PATH: Arg = arg("path"); - pub const PATH_MANY: ArgMulti = arg_multi("path"); pub const PIN: ArgFlag = flag("pin"); pub const PORT_ID: ArgDefault = arg_default( "port-id", @@ -3129,7 +3101,6 @@ pub mod args { pub const SIGNING_KEY: Arg = arg("signing-key"); pub const SIGNING_KEYS: ArgMulti = arg_multi("signing-keys"); pub const SIGNATURES: ArgMulti = arg_multi("signatures"); - pub const SIGNED: ArgFlag = flag("signed"); pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); pub const STEWARD: Arg = arg("steward"); @@ -6833,32 +6804,6 @@ pub mod args { } } - #[derive(Clone, Debug)] - pub struct UnifyGenesisTemplates { - pub paths: Vec, - pub signed: bool, - } - - impl Args for UnifyGenesisTemplates { - fn parse(matches: &ArgMatches) -> Self { - let paths = PATH_MANY.parse(matches); - let signed = SIGNED.parse(matches); - Self { paths, signed } - } - - fn def(app: App) -> App { - app.arg(PATH_MANY.def().help( - "Paths to genesis transaction toml files. Repeat as many \ - times as needed.", - )) - .arg( - SIGNED.def().help( - "Whether the genesis tx toml files are signed or not.", - ), - ) - } - } - #[derive(Clone, Debug)] pub struct InitGenesisValidator { pub alias: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 1dc76054af..2f06a497d4 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -666,9 +666,6 @@ impl CliApi { Utils::SignGenesisTx(SignGenesisTx(args)) => { utils::sign_genesis_tx(global_args, args) } - Utils::UnifyGenesisTemplates(UnifyGenesisTemplates(args)) => { - utils::unify_genesis_templates(args) - } }, } Ok(()) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index c2d2a0050d..f7a18ed867 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -809,120 +809,6 @@ pub fn init_genesis_validator( println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } -/// Concatenate all the given genesis templates into a single (unified) -/// genesis transactions file. -pub fn unify_genesis_templates( - args::UnifyGenesisTemplates { signed, paths }: args::UnifyGenesisTemplates, -) { - if signed { - unify_genesis_templates_signed(&paths); - } else { - unify_genesis_templates_unsigned(&paths); - } -} - -fn unify_genesis_templates_signed(paths: &[PathBuf]) { - let mut transactions = genesis::transactions::Transactions::< - genesis::templates::Unvalidated, - > { - established_account: Some(vec![]), - validator_account: Some(vec![]), - bond: Some(vec![]), - }; - - for path in paths { - let contents = fs::read(path).unwrap_or_else(|err| { - eprintln!( - "Unable to read from file {}. Failed with {err}.", - path.to_string_lossy() - ); - safe_exit(1); - }); - let mut parsed = genesis::transactions::parse_signed(&contents) - .unwrap_or_else(|err| { - eprintln!( - "Unable to parse the TOML from {}. Failed with {err}.", - path.to_string_lossy() - ); - safe_exit(1); - }); - - if let Some((accum, txs)) = transactions - .established_account - .as_mut() - .zip(parsed.established_account.as_mut()) - { - accum.append(txs); - } - - if let Some((accum, txs)) = transactions - .validator_account - .as_mut() - .zip(parsed.validator_account.as_mut()) - { - accum.append(txs); - } - - if let Some((accum, txs)) = - transactions.bond.as_mut().zip(parsed.bond.as_mut()) - { - accum.append(txs); - } - } - - println!("{}", toml::to_string(&transactions).unwrap()); -} - -fn unify_genesis_templates_unsigned(paths: &[PathBuf]) { - let mut transactions = genesis::transactions::UnsignedTransactions { - established_account: Some(vec![]), - validator_account: Some(vec![]), - bond: Some(vec![]), - }; - - for path in paths { - let contents = fs::read(path).unwrap_or_else(|err| { - eprintln!( - "Unable to read from file {}. Failed with {err}.", - path.to_string_lossy() - ); - safe_exit(1); - }); - let mut parsed = genesis::transactions::parse_unsigned(&contents) - .unwrap_or_else(|err| { - eprintln!( - "Unable to parse the TOML from {}. Failed with {err}.", - path.to_string_lossy() - ); - safe_exit(1); - }); - - if let Some((accum, txs)) = transactions - .established_account - .as_mut() - .zip(parsed.established_account.as_mut()) - { - accum.append(txs); - } - - if let Some((accum, txs)) = transactions - .validator_account - .as_mut() - .zip(parsed.validator_account.as_mut()) - { - accum.append(txs); - } - - if let Some((accum, txs)) = - transactions.bond.as_mut().zip(parsed.bond.as_mut()) - { - accum.append(txs); - } - } - - println!("{}", toml::to_string(&transactions).unwrap()); -} - /// Try to load a pre-genesis wallet or return nothing, /// if it cannot be found. pub fn try_load_pre_genesis_wallet( diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 7ef398c7a5..6bf25be086 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -88,13 +88,6 @@ pub fn parse_unsigned( toml::from_slice(bytes) } -/// Parse [`Transactions`] from bytes. -pub fn parse_signed( - bytes: &[u8], -) -> Result, toml::de::Error> { - toml::from_slice(bytes) -} - /// Create signed [`Transactions`] for an established account. pub fn init_established_account( vp: String, From 5138fb1d8f9190c9fd135fe1b904febd169df59d Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 23 Nov 2023 15:05:28 +0100 Subject: [PATCH 22/81] [feat]: Fixed cli tooling for multi-signing txs --- apps/src/lib/cli.rs | 17 +- apps/src/lib/client/utils.rs | 238 +++++++++++++------- apps/src/lib/config/genesis/templates.rs | 12 +- apps/src/lib/config/genesis/transactions.rs | 59 +++-- core/src/types/token.rs | 6 + 5 files changed, 228 insertions(+), 104 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index e675e75f15..e3cf07fffc 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2389,7 +2389,8 @@ pub mod cmds { .about( "Initialize genesis validator's address, consensus key \ and validator account key and use it in the ledger's \ - node.", + node. Appends validator creation and self-bond txs to a \ + .toml file containing an established account tx.", ) .add_args::() } @@ -6773,6 +6774,7 @@ pub mod args { pub vp: String, pub wallet_aliases: Vec, pub threshold: u8, + pub output_path: PathBuf, } impl Args for InitGenesisEstablishedAccount { @@ -6780,10 +6782,12 @@ pub mod args { let wallet_aliases = ALIAS_MANY.parse(matches); let vp = VP.parse(matches).unwrap_or_else(|| "vp_user".to_string()); let threshold = THRESOLD.parse(matches).unwrap_or(1); + let output_path = PATH.parse(matches); Self { wallet_aliases, vp, threshold, + output_path, } } @@ -6801,6 +6805,10 @@ pub mod args { .arg(VP.def().help( "The validity predicate of the account. Defaults to `vp_user`.", )) + .arg(PATH.def().help( + "The path of the .toml file to write the established account \ + transaction to. Overwrites the file if it exists.", + )) } } @@ -6818,6 +6826,7 @@ pub mod args { pub website: Option, pub discord_handle: Option, pub address: EstablishedAddress, + pub tx_path: PathBuf, } impl Args for InitGenesisValidator { @@ -6836,6 +6845,7 @@ pub mod args { let website = WEBSITE_OPT.parse(matches); let discord_handle = DISCORD_OPT.parse(matches); let address = RAW_ADDRESS_ESTABLISHED.parse(matches); + let tx_path = PATH.parse(matches); Self { alias, net_address, @@ -6848,6 +6858,7 @@ pub mod args { description, website, discord_handle, + tx_path, address, } } @@ -6858,6 +6869,10 @@ pub mod args { "The address of an established account to be promoted to \ a validator.", )) + .arg(PATH.def().help( + "The .toml file containing an established account tx from \ + which to create a validator.", + )) .arg(NET_ADDRESS.def().help( "Static {host:port} of your validator node's P2P address. \ Namada uses port `26656` for P2P connections by default, \ diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index f7a18ed867..a190617bf7 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -9,7 +9,6 @@ use color_eyre::owo_colors::OwoColorize; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; -use itertools::Itertools; use namada::core::types::string_encoding::StringEncoded; use namada::types::chain::ChainId; use namada::types::dec::Dec; @@ -25,6 +24,9 @@ use sha2::{Digest, Sha256}; use crate::cli::args; use crate::cli::context::ENV_VAR_WASM_DIR; use crate::config::genesis::chain::DeriveEstablishedAddress; +use crate::config::genesis::transactions::{ + sign_delegation_bond_tx, UnsignedTransactions, +}; use crate::config::global::GlobalConfig; use crate::config::{ self, genesis, get_default_namada_folder, Config, TendermintMode, @@ -598,21 +600,51 @@ pub fn derive_genesis_addresses( let maybe_pre_genesis_wallet = try_load_pre_genesis_wallet(&global_args.base_dir) .map(|(wallet, _)| wallet); - - let genesis_txs = - genesis::templates::read_transactions(&args.genesis_txs_path).unwrap(); + let contents = + fs::read_to_string(&args.genesis_txs_path).unwrap_or_else(|err| { + eprintln!( + "Unable to read from file {}. Failed with error {err}.", + args.genesis_txs_path.to_string_lossy() + ); + safe_exit(1) + }); + let (estbd_txs, validator_addrs) = + toml::from_str::<'_, UnsignedTransactions>(&contents) + .ok() + .map(|txs| { + ( + txs.established_account.unwrap_or_default(), + txs.validator_account + .unwrap_or_default() + .into_iter() + .map(|acct| acct.address) + .collect::>(), + ) + }) + .unwrap_or_else(|| { + let genesis_txs = genesis::templates::read_transactions( + &args.genesis_txs_path, + ) + .unwrap(); + ( + genesis_txs.established_account.unwrap_or_default(), + genesis_txs + .validator_account + .unwrap_or_default() + .into_iter() + .map(|acct| acct.address) + .collect(), + ) + }); println!("{}", "Established account txs:".underline().bold()); - for tx in genesis_txs - .established_account - .as_ref() - .into_iter() - .flatten() - { - let address = tx.derive_address(); - + for tx in &estbd_txs { println!(); - println!("{} {address}", "Address:".bold().bright_green()); + println!( + "{} {}", + "Address:".bold().bright_green(), + tx.derive_address() + ); println!("{}", "Public key(s):".bold().bright_green()); for (ix, pk) in tx.public_keys.iter().enumerate() { @@ -629,39 +661,18 @@ pub fn derive_genesis_addresses( } } } - if genesis_txs - .established_account - .as_ref() - .map(|txs| txs.is_empty()) - .unwrap_or(true) - { + if estbd_txs.is_empty() { println!(); println!("{}", "".dimmed()); } - println!(); println!("{}", "Validator account txs:".underline().bold()); - for tx in genesis_txs.validator_account.as_ref().into_iter().flatten() { + for addr in &validator_addrs { println!(); - println!("{} {}", "Address:".bold().bright_green(), tx.address); - let keys = [ - ("Consensus key:", &tx.consensus_key.pk.raw), - ("Protocol key:", &tx.protocol_key.pk.raw), - ("Tendermint node key:", &tx.tendermint_node_key.pk.raw), - ("Ethereum hot key:", &tx.eth_hot_key.pk.raw), - ("Ethereum cold key:", &tx.eth_cold_key.pk.raw), - ]; - for (description, key) in keys { - println!("{} {key}", description.bold().bright_green()); - } + println!("{} {}", "Address:".bold().bright_green(), addr.raw); } - if genesis_txs - .validator_account - .as_ref() - .map(|txs| txs.is_empty()) - .unwrap_or(true) - { + if validator_addrs.is_empty() { println!(); println!("{}", "".dimmed()); } @@ -673,23 +684,23 @@ pub fn init_genesis_established_account( global_args: args::Global, args: args::InitGenesisEstablishedAccount, ) { - let (mut pre_genesis_wallet, _) = + let (pre_genesis_wallet, _) = load_pre_genesis_wallet_or_exit(&global_args.base_dir); let public_keys: Vec<_> = args .wallet_aliases .iter() .map(|alias| { - let sk = pre_genesis_wallet - .find_secret_key(alias, None) - .unwrap_or_else(|err| { + let pk = pre_genesis_wallet.find_public_key(alias).unwrap_or_else( + |err| { eprintln!( "Failed to look-up `{alias}` in the pre-genesis \ wallet: {err}", ); safe_exit(1) - }); - StringEncoded::new(sk.ref_to()) + }, + ); + StringEncoded::new(pk) }) .collect(); @@ -698,13 +709,7 @@ pub fn init_genesis_established_account( public_keys, args.threshold, ); - let toml_path = { - let pre_genesis_dir = global_args.base_dir.join(PRE_GENESIS_DIR); - established_acc_pre_genesis_txs_file( - &args.wallet_aliases.iter().join("_"), - &pre_genesis_dir, - ) - }; + let toml_path = args.output_path; let toml_path_str = toml_path.to_string_lossy(); let genesis_part = toml::to_string(&txs).unwrap(); @@ -717,8 +722,9 @@ pub fn init_genesis_established_account( }); println!( - "{}: {address}", - "Derived established account address".bold() + "{}: {}", + "Derived established account address".bold(), + address.green(), ); println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } @@ -739,9 +745,36 @@ pub fn init_genesis_validator( description, website, discord_handle, + tx_path, address, }: args::InitGenesisValidator, ) { + let contents = fs::read_to_string(&tx_path).unwrap_or_else(|err| { + eprintln!( + "Unable to read from file {}. Failed with error {err}.", + tx_path.to_string_lossy() + ); + safe_exit(1) + }); + let prev_txs: UnsignedTransactions = toml::from_str(&contents).unwrap(); + if prev_txs + .established_account + .as_ref() + .and_then(|accts| { + accts + .iter() + .find(|acct| acct.derive_established_address() == address) + }) + .is_none() + { + eprintln!( + "The provided file did not contain an established account tx with \ + the provided address {}", + address + ); + safe_exit(1); + } + // Validate the commission rate data if commission_rate > Dec::one() { eprintln!("The validator commission rate must not exceed 1.0 or 100%"); @@ -779,7 +812,7 @@ pub fn init_genesis_validator( pre_genesis::validator_file_name(&pre_genesis_dir).to_string_lossy() ); - let (address, transactions) = genesis::transactions::init_validator( + let (address, mut transactions) = genesis::transactions::init_validator( genesis::transactions::GenesisValidatorData { address, commission_rate, @@ -793,8 +826,20 @@ pub fn init_genesis_validator( }, &validator_wallet, ); - let toml_path = validator_pre_genesis_txs_file(&pre_genesis_dir); + let toml_path = tx_path; let toml_path_str = toml_path.to_string_lossy(); + // append new transactions to the previous txs from the provided file. + transactions.established_account = prev_txs.established_account; + transactions + .validator_account + .as_mut() + .unwrap() + .append(&mut prev_txs.validator_account.unwrap_or_default()); + transactions + .bond + .as_mut() + .unwrap() + .append(&mut prev_txs.bond.unwrap_or_default()); let genesis_part = toml::to_string(&transactions).unwrap(); fs::write(&toml_path, genesis_part).unwrap_or_else(|err| { @@ -805,7 +850,11 @@ pub fn init_genesis_validator( safe_exit(1) }); - println!("{}: {address}", "Derived validator account address".bold()); + println!( + "{}: {}", + "Validator account address".bold(), + address.green() + ); println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } @@ -896,15 +945,6 @@ pub fn validator_pre_genesis_txs_file(pre_genesis_path: &Path) -> PathBuf { pre_genesis_path.join("transactions.toml") } -/// The default path to an established account txs file. -pub fn established_acc_pre_genesis_txs_file( - wallet_key_alias: &str, - pre_genesis_path: &Path, -) -> PathBuf { - pre_genesis_path - .join(format!("established-account-tx-{wallet_key_alias}.toml")) -} - /// The default validator pre-genesis directory pub fn validator_pre_genesis_dir(base_dir: &Path, alias: &str) -> PathBuf { base_dir.join(PRE_GENESIS_DIR).join(alias) @@ -931,35 +971,67 @@ pub fn sign_genesis_tx( ) { let (mut wallet, _wallet_file) = load_pre_genesis_wallet_or_exit(&global_args.base_dir); - + let maybe_pre_genesis_wallet = validator_alias.and_then(|alias| { + let pre_genesis_dir = + validator_pre_genesis_dir(&global_args.base_dir, &alias); + pre_genesis::load(&pre_genesis_dir).ok() + }); let contents = fs::read(&path).unwrap_or_else(|err| { eprintln!( "Unable to read from file {}. Failed with {err}.", path.to_string_lossy() ); - safe_exit(1); + safe_exit(1) }); - let unsigned = genesis::transactions::parse_unsigned(&contents) + let (signed, append) = genesis::transactions::parse_unsigned(&contents) + .map(|unsigned| { + ( + genesis::transactions::sign_txs( + unsigned, + &mut wallet, + maybe_pre_genesis_wallet.as_ref(), + ), + true, + ) + }) .unwrap_or_else(|err| { - eprintln!( - "Unable to parse the TOML from {}. Failed with {err}.", - path.to_string_lossy() - ); - safe_exit(1); + let mut genesis_txs = genesis::templates::read_transactions(&path) + .unwrap_or_else(|e| { + eprintln!( + "Unable to parse the TOML from {}. Could not parse as \ + unsigned with {err}. Could not parse as signed with \ + {e}.", + path.to_string_lossy() + ); + safe_exit(1) + }); + // Sign bond txs + let bond = genesis_txs.bond.map(|txs| { + txs.into_iter() + .map(|tx| { + sign_delegation_bond_tx( + tx, + &mut wallet, + &genesis_txs.established_account, + ) + }) + .collect() + }); + genesis_txs.bond = bond; + (genesis_txs, false) }); - let maybe_pre_genesis_wallet = validator_alias.and_then(|alias| { - let pre_genesis_dir = - validator_pre_genesis_dir(&global_args.base_dir, &alias); - crate::wallet::pre_genesis::load(&pre_genesis_dir).ok() - }); - let signed = genesis::transactions::sign_txs( - unsigned, - &mut wallet, - maybe_pre_genesis_wallet.as_ref(), - ); match output { Some(output_path) => { + let signed = if append { + let mut prev_txs = + genesis::templates::read_transactions(&output_path) + .unwrap_or_default(); + prev_txs.merge(signed); + prev_txs + } else { + signed + }; let transactions = toml::to_vec(&signed).unwrap(); fs::write(&output_path, transactions).unwrap_or_else(|err| { eprintln!( diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index 620c00a77e..ba7a4bc682 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -502,6 +502,8 @@ impl TokenBalances { BorshSerialize, PartialEq, Eq, + PartialOrd, + Ord, )] pub struct Unvalidated {} @@ -514,6 +516,8 @@ pub struct Unvalidated {} BorshSerialize, PartialEq, Eq, + PartialOrd, + Ord, )] pub struct Validated {} @@ -525,7 +529,9 @@ pub trait TemplateValidation: Serialize { + BorshSerialize + BorshDeserialize + PartialEq - + Eq; + + Eq + + PartialOrd + + Ord; type Balances: for<'a> Deserialize<'a> + Serialize + Clone @@ -541,7 +547,9 @@ pub trait TemplateValidation: Serialize { + BorshSerialize + BorshDeserialize + PartialEq - + Eq; + + Eq + + PartialOrd + + Ord; type GasMinimums: for<'a> Deserialize<'a> + Serialize + Clone diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 6bf25be086..5eab9b4073 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -6,6 +6,7 @@ use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; +use itertools::Itertools; use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::types::ValidatorMetaData; @@ -61,7 +62,9 @@ pub fn sign_txs( // Sign bond txs let bond = bond.map(|txs| { txs.into_iter() - .map(|tx| sign_delegation_bond_tx(tx, wallet, &established_account)) + .map(|tx| { + sign_delegation_bond_tx(tx.into(), wallet, &established_account) + }) .collect() }); @@ -69,8 +72,15 @@ pub fn sign_txs( let validator_account = validator_account.map(|txs| { let validator_wallet = validator_wallet .expect("Validator wallet required to sign validator account txs"); + let tnk = validator_wallet.tendermint_node_key.ref_to(); txs.into_iter() - .map(|tx| sign_validator_account_tx(tx, validator_wallet)) + .filter_map(|tx| { + if tx.tendermint_node_key.raw == tnk { + Some(sign_validator_account_tx(tx, validator_wallet)) + } else { + None + } + }) .collect() }); @@ -251,15 +261,14 @@ pub fn sign_validator_account_tx( } pub fn sign_delegation_bond_tx( - unsigned_tx: BondTx, + mut to_sign: SignedBondTx, wallet: &mut Wallet, established_accounts: &Option>, ) -> SignedBondTx { let source_keys = - look_up_sk_from(&unsigned_tx.source, wallet, established_accounts); - let mut signed = SignedBondTx::from(unsigned_tx); - signed.sign(&source_keys); - signed + look_up_sk_from(&to_sign.data.source, wallet, established_accounts); + to_sign.sign(&source_keys); + to_sign } pub fn sign_tx( @@ -300,7 +309,8 @@ impl Transactions { } txs }) - .or(other.established_account); + .or(other.established_account) + .map(|txs| txs.into_iter().sorted().dedup().collect()); self.validator_account = self .validator_account .take() @@ -310,7 +320,8 @@ impl Transactions { } txs }) - .or(other.validator_account); + .or(other.validator_account) + .map(|txs| txs.into_iter().sorted().dedup().collect()); self.bond = self .bond .take() @@ -320,7 +331,8 @@ impl Transactions { } txs }) - .or(other.bond); + .or(other.bond) + .map(|txs| txs.into_iter().sorted().dedup().collect()); } } @@ -397,8 +409,10 @@ pub type SignedValidatorAccountTx = ValidatorAccountTx; BorshDeserialize, PartialEq, Eq, + PartialOrd, + Ord, )] -pub struct ValidatorAccountTx { +pub struct ValidatorAccountTx { /// The address of the validator. pub address: StringEncoded, // TODO: remove the vp field @@ -429,6 +443,8 @@ pub struct ValidatorAccountTx { BorshDeserialize, PartialEq, Eq, + PartialOrd, + Ord, )] pub struct EstablishedAccountTx { pub vp: String, @@ -455,6 +471,8 @@ impl DeriveEstablishedAddress for EstablishedAccountTx { BorshDeserialize, PartialEq, Eq, + PartialOrd, + Ord, )] pub struct SignedBondTx { #[serde(flatten)] @@ -517,7 +535,10 @@ impl SignedBondTx { sk, &self.data.data_to_sign(), )) - })) + })); + let mut sigs = vec![]; + std::mem::swap(&mut self.signatures, &mut sigs); + self.signatures = sigs.into_iter().sorted().dedup().collect(); } } @@ -530,6 +551,8 @@ impl SignedBondTx { BorshDeserialize, PartialEq, Eq, + PartialOrd, + Ord, )] pub struct BondTx { pub source: GenesisAddress, @@ -596,6 +619,8 @@ impl From> for SignedBondTx { BorshDeserialize, PartialEq, Eq, + PartialOrd, + Ord, )] pub struct SignedPk { pub pk: StringEncoded, @@ -1095,14 +1120,12 @@ fn look_up_sk_from( .unwrap_or_else(|| { // If it's not in the wallet, it must be an established account // so we need to look-up its public key first + if established_accounts.is_none() { + return vec![]; + } established_accounts .as_ref() - .unwrap_or_else(|| { - panic!( - "Signing failed. Cannot find \"{source}\" in the wallet \ - and there are no established accounts." - ); - }) + .unwrap() .iter() .find_map(|account| match source { GenesisAddress::EstablishedAddress(address) => { diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 5c319b7da6..eec4b5a7d4 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -504,6 +504,12 @@ impl PartialOrd for DenominatedAmount { } } +impl Ord for DenominatedAmount { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + impl serde::Serialize for Amount { fn serialize( &self, From 34e3502105a83b150cbbd7f001a328b17ae32ea4 Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 23 Nov 2023 16:28:18 +0100 Subject: [PATCH 23/81] some dicking around --- apps/src/lib/cli/client.rs | 2 +- apps/src/lib/client/utils.rs | 71 +++++++++++---------- apps/src/lib/config/genesis/transactions.rs | 60 +++++++++++------ 3 files changed, 80 insertions(+), 53 deletions(-) diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 2f06a497d4..10679bfa3e 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -664,7 +664,7 @@ impl CliApi { args, )) => utils::validate_genesis_templates(global_args, args), Utils::SignGenesisTx(SignGenesisTx(args)) => { - utils::sign_genesis_tx(global_args, args) + utils::sign_genesis_tx(global_args, args).await } }, } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index a190617bf7..3cf835419a 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -961,7 +961,7 @@ pub fn validate_genesis_templates( } /// Sign genesis transactions. -pub fn sign_genesis_tx( +pub async fn sign_genesis_tx( global_args: args::Global, args::SignGenesisTx { path, @@ -983,44 +983,47 @@ pub fn sign_genesis_tx( ); safe_exit(1) }); - let (signed, append) = genesis::transactions::parse_unsigned(&contents) - .map(|unsigned| { - ( + let (signed, append) = + match genesis::transactions::parse_unsigned(&contents) { + Ok(unsigned) => ( genesis::transactions::sign_txs( unsigned, &mut wallet, maybe_pre_genesis_wallet.as_ref(), - ), + ) + .await, true, - ) - }) - .unwrap_or_else(|err| { - let mut genesis_txs = genesis::templates::read_transactions(&path) - .unwrap_or_else(|e| { - eprintln!( - "Unable to parse the TOML from {}. Could not parse as \ - unsigned with {err}. Could not parse as signed with \ - {e}.", - path.to_string_lossy() - ); - safe_exit(1) - }); - // Sign bond txs - let bond = genesis_txs.bond.map(|txs| { - txs.into_iter() - .map(|tx| { - sign_delegation_bond_tx( - tx, - &mut wallet, - &genesis_txs.established_account, - ) - }) - .collect() - }); - genesis_txs.bond = bond; - (genesis_txs, false) - }); - + ), + Err(err) => { + let mut genesis_txs = + genesis::templates::read_transactions(&path) + .unwrap_or_else(|e| { + eprintln!( + "Unable to parse the TOML from {}. Could not \ + parse as unsigned with {err}. Could not \ + parse as signed with {e}.", + path.to_string_lossy() + ); + safe_exit(1) + }); + // Sign bond txs + if let Some(txs) = genesis_txs.bond { + let mut bonds = vec![]; + for tx in txs { + bonds.push( + sign_delegation_bond_tx( + tx, + &mut wallet, + &genesis_txs.established_account, + ) + .await, + ); + } + genesis_txs.bond = Some(bonds); + } + (genesis_txs, false) + } + }; match output { Some(output_path) => { let signed = if append { diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 5eab9b4073..79bbf6d445 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -3,6 +3,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; use std::net::SocketAddr; +use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; @@ -11,13 +12,14 @@ use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::types::ValidatorMetaData; use namada::proto::{ - standalone_signature, verify_standalone_sig, SerializeWithBorsh, + standalone_signature, verify_standalone_sig, SerializeWithBorsh, Tx, }; use namada::types::dec::Dec; use namada::types::key::{common, RefTo, VerifySigError}; use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use namada::types::transaction::{pos, Fee, TxType}; use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; use namada_sdk::wallet::Wallet; @@ -48,7 +50,7 @@ pub struct GenesisValidatorData { /// Panics if given `txs.validator_accounts` is not empty, because validator /// transactions must be signed with a validator wallet (see /// `init-genesis-validator` command). -pub fn sign_txs( +pub async fn sign_txs( txs: UnsignedTransactions, wallet: &mut Wallet, validator_wallet: Option<&ValidatorWallet>, @@ -60,13 +62,22 @@ pub fn sign_txs( } = txs; // Sign bond txs - let bond = bond.map(|txs| { - txs.into_iter() - .map(|tx| { - sign_delegation_bond_tx(tx.into(), wallet, &established_account) - }) - .collect() - }); + let bond = if let Some(txs) = bond { + let mut bonds = vec![]; + for tx in txs { + bonds.push( + sign_delegation_bond_tx( + tx.into(), + wallet, + &established_account, + ) + .await, + ); + } + Some(bonds) + } else { + None + }; // Sign validator account txs let validator_account = validator_account.map(|txs| { @@ -260,14 +271,14 @@ pub fn sign_validator_account_tx( } } -pub fn sign_delegation_bond_tx( +pub async fn sign_delegation_bond_tx( mut to_sign: SignedBondTx, wallet: &mut Wallet, established_accounts: &Option>, ) -> SignedBondTx { let source_keys = look_up_sk_from(&to_sign.data.source, wallet, established_accounts); - to_sign.sign(&source_keys); + to_sign.sign(&source_keys).await; to_sign } @@ -529,7 +540,7 @@ impl SignedBondTx { /// only verify signatures on [`SignedBondTx`] /// types. Thus we only allow signing of [`BondTx`] /// types. - pub fn sign(&mut self, key: &[common::SecretKey]) { + pub async fn sign(&mut self, key: &[common::SecretKey]) { self.signatures.extend(key.iter().map(|sk| { StringEncoded::new(standalone_signature::<_, SerializeWithBorsh>( sk, @@ -566,12 +577,25 @@ where { /// The signable data. This does not include the phantom data. fn data_to_sign(&self) -> Vec { - [ - self.source.serialize_to_vec(), - self.validator.serialize_to_vec(), - self.amount.serialize_to_vec(), - ] - .concat() + let mut tx = Tx::from_type(TxType::Raw); + tx.add_code_from_hash(Default::default(), None); + tx.add_data(pos::Bond { + validator: self.validator.clone(), + amount: self.amount.clone(), + source: Some(self.source.address()), + }); + let pk = common::PublicKey::from_str("tpknam1qp4jyqv4d2uh4zw2g64nl77knllqat4esw2mw94ypp8eq624am5hkf5uvjd").unwrap(); + tx.add_wrapper( + Fee { + amount_per_gas_unit: Default::default(), + token: Address::from(&pk), + }, + pk, + Default::default(), + Default::default(), + None, + ); + tx.serialize_to_vec() } } From dc66383f42f1b593c3a737fa0059408051d63596 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 24 Nov 2023 09:24:38 +0000 Subject: [PATCH 24/81] Temporarily revert hw wallet signing changes --- apps/src/lib/config/genesis/transactions.rs | 29 +++++---------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 79bbf6d445..223004c925 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -3,7 +3,6 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; use std::net::SocketAddr; -use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; @@ -12,14 +11,13 @@ use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::types::ValidatorMetaData; use namada::proto::{ - standalone_signature, verify_standalone_sig, SerializeWithBorsh, Tx, + standalone_signature, verify_standalone_sig, SerializeWithBorsh, }; use namada::types::dec::Dec; use namada::types::key::{common, RefTo, VerifySigError}; use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; -use namada::types::transaction::{pos, Fee, TxType}; use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; use namada_sdk::wallet::Wallet; @@ -577,25 +575,12 @@ where { /// The signable data. This does not include the phantom data. fn data_to_sign(&self) -> Vec { - let mut tx = Tx::from_type(TxType::Raw); - tx.add_code_from_hash(Default::default(), None); - tx.add_data(pos::Bond { - validator: self.validator.clone(), - amount: self.amount.clone(), - source: Some(self.source.address()), - }); - let pk = common::PublicKey::from_str("tpknam1qp4jyqv4d2uh4zw2g64nl77knllqat4esw2mw94ypp8eq624am5hkf5uvjd").unwrap(); - tx.add_wrapper( - Fee { - amount_per_gas_unit: Default::default(), - token: Address::from(&pk), - }, - pk, - Default::default(), - Default::default(), - None, - ); - tx.serialize_to_vec() + [ + self.source.serialize_to_vec(), + self.validator.serialize_to_vec(), + self.amount.serialize_to_vec(), + ] + .concat() } } From 4c6af4d070c5ef2d3876761c950df4523f916b2e Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 24 Nov 2023 13:20:03 +0000 Subject: [PATCH 25/81] sumbish --- apps/src/lib/client/utils.rs | 26 +- apps/src/lib/config/genesis.rs | 32 +- apps/src/lib/config/genesis/chain.rs | 10 +- apps/src/lib/config/genesis/transactions.rs | 402 ++++++++++++------- apps/src/lib/node/ledger/shell/init_chain.rs | 28 +- tests/src/e2e/ibc_tests.rs | 6 +- 6 files changed, 312 insertions(+), 192 deletions(-) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 3cf835419a..6c42648ab5 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -9,6 +9,7 @@ use color_eyre::owo_colors::OwoColorize; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; +use itertools::Either; use namada::core::types::string_encoding::StringEncoded; use namada::types::chain::ChainId; use namada::types::dec::Dec; @@ -25,7 +26,7 @@ use crate::cli::args; use crate::cli::context::ENV_VAR_WASM_DIR; use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::transactions::{ - sign_delegation_bond_tx, UnsignedTransactions, + sign_delegation_bond_tx, sign_validator_account_tx, UnsignedTransactions, }; use crate::config::global::GlobalConfig; use crate::config::{ @@ -632,7 +633,7 @@ pub fn derive_genesis_addresses( .validator_account .unwrap_or_default() .into_iter() - .map(|acct| acct.address) + .map(|acct| acct.data.address) .collect(), ) }); @@ -1021,6 +1022,27 @@ pub async fn sign_genesis_tx( } genesis_txs.bond = Some(bonds); } + // Sign validator txs + if let Some(txs) = genesis_txs.validator_account { + let mut validator_accounts = vec![]; + for tx in txs { + validator_accounts.push( + sign_validator_account_tx( + Either::Right(tx), + &mut wallet, + genesis_txs + .established_account + .as_ref() + .expect( + "Established account txs required \ + when signing validator account txs", + ), + ) + .await, + ); + } + genesis_txs.validator_account = Some(validator_accounts); + } (genesis_txs, false) } }; diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index ae8062ca00..33d8fe90ba 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -402,7 +402,7 @@ pub fn make_dev_genesis( .get(0) .unwrap(); let genesis_addr = - GenesisAddress::EstablishedAddress(tx.tx.address.raw.clone()); + GenesisAddress::EstablishedAddress(tx.tx.data.address.raw.clone()); let balance = *nam_balances.0.get(&genesis_addr).unwrap(); let bonded = { @@ -490,20 +490,22 @@ pub fn make_dev_genesis( }; vals.push(chain::FinalizedValidatorAccountTx { address: Address::Established(address.clone()), - tx: transactions::ValidatorAccountTx { - address: StringEncoded::new(address.clone()), - vp: validator_account_tx.vp, - commission_rate: validator_account_tx.commission_rate, - max_commission_rate_change: validator_account_tx - .max_commission_rate_change, - metadata: validator_account_tx.metadata, - net_address: validator_account_tx.net_address, - consensus_key: sign_pk(&consensus_keypair), - protocol_key: sign_pk(&protocol_keypair), - tendermint_node_key: sign_pk(&consensus_keypair), - eth_hot_key: sign_pk(ð_bridge_keypair), - eth_cold_key: sign_pk(ð_cold_keypair), - }, + tx: transactions::Signed::new( + transactions::ValidatorAccountTx { + address: StringEncoded::new(address.clone()), + vp: validator_account_tx.vp, + commission_rate: validator_account_tx.commission_rate, + max_commission_rate_change: validator_account_tx + .max_commission_rate_change, + metadata: validator_account_tx.metadata, + net_address: validator_account_tx.net_address, + consensus_key: sign_pk(&consensus_keypair), + protocol_key: sign_pk(&protocol_keypair), + tendermint_node_key: sign_pk(&consensus_keypair), + eth_hot_key: sign_pk(ð_bridge_keypair), + eth_cold_key: sign_pk(ð_cold_keypair), + }, + ), }); address }; diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index 55f675b6d2..b5ff28d086 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -182,7 +182,7 @@ impl Finalized { self.transactions.find_validator(tendermint_pk).map( |validator_tx| { ( - validator_tx.tx.net_address, + validator_tx.tx.data.net_address, validator_tx.derive_tendermint_address(), ) }, @@ -651,7 +651,7 @@ impl FinalizedTransactions { let validator_account = validator_account.map(|txs| { txs.into_iter() .map(|tx| FinalizedValidatorAccountTx { - address: Address::Established(tx.address.raw.clone()), + address: Address::Established(tx.data.address.raw.clone()), tx, }) .collect() @@ -670,7 +670,7 @@ impl FinalizedTransactions { let validator_accounts = self.validator_account.as_ref()?; validator_accounts .iter() - .find(|tx| &tx.tx.tendermint_node_key.pk.raw == tendermint_pk) + .find(|tx| &tx.tx.data.tendermint_node_key.pk.raw == tendermint_pk) } } @@ -754,12 +754,12 @@ impl FinalizedValidatorAccountTx { pub fn derive_tendermint_address(&self) -> TendermintAddress { // Derive the node ID from the node key let node_id: TendermintNodeId = - id_from_pk(&self.tx.tendermint_node_key.pk.raw); + id_from_pk(&self.tx.data.tendermint_node_key.pk.raw); // Build the list of persistent peers from the validators' node IDs TendermintAddress::from_str(&format!( "{}@{}", - node_id, self.tx.net_address, + node_id, self.tx.data.net_address, )) .expect("Validator address must be valid") } diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 223004c925..cc0a7712c3 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -6,7 +6,7 @@ use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; -use itertools::Itertools; +use itertools::{Either, Itertools}; use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::types::ValidatorMetaData; @@ -31,6 +31,12 @@ use crate::config::genesis::templates::{ use crate::config::genesis::GenesisAddress; use crate::wallet::CliWalletUtils; +/// Helper trait to fetch tx data to sign. +pub trait TxToSign { + /// Return tx data to sign. + fn tx_to_sign(&self) -> Vec; +} + pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; pub struct GenesisValidatorData { @@ -78,20 +84,30 @@ pub async fn sign_txs( }; // Sign validator account txs - let validator_account = validator_account.map(|txs| { + let validator_account = if let Some(txs) = validator_account { let validator_wallet = validator_wallet .expect("Validator wallet required to sign validator account txs"); let tnk = validator_wallet.tendermint_node_key.ref_to(); - txs.into_iter() - .filter_map(|tx| { - if tx.tendermint_node_key.raw == tnk { - Some(sign_validator_account_tx(tx, validator_wallet)) - } else { - None - } - }) - .collect() - }); + let mut filtered_txs = vec![]; + for tx in txs { + if tx.tendermint_node_key.raw == tnk { + filtered_txs.push( + sign_validator_account_tx( + Either::Left((tx, validator_wallet)), + wallet, + established_account.as_ref().expect( + "Established account txs required when signing \ + validator account txs", + ), + ) + .await, + ); + } + } + Some(filtered_txs) + } else { + None + }; Transactions { established_account, @@ -200,73 +216,92 @@ pub fn init_validator( (address, txs) } -pub fn sign_validator_account_tx( - unsigned_tx: UnsignedValidatorAccountTx, - validator_wallet: &ValidatorWallet, +pub async fn sign_validator_account_tx( + to_sign: Either< + (UnsignedValidatorAccountTx, &ValidatorWallet), + SignedValidatorAccountTx, + >, + wallet: &mut Wallet, + established_accounts: &[EstablishedAccountTx], ) -> SignedValidatorAccountTx { - // Sign the tx with every validator key to authorize their usage - let consensus_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.consensus_key); - let protocol_key_sig = sign_tx( - &unsigned_tx, - &validator_wallet.store.validator_keys.protocol_keypair, - ); - let eth_hot_key_sig = sign_tx(&unsigned_tx, &validator_wallet.eth_hot_key); - let eth_cold_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.eth_cold_key); - let tendermint_node_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.tendermint_node_key); - - let ValidatorAccountTx { - address, - consensus_key, - protocol_key, - tendermint_node_key, - vp, - commission_rate, - max_commission_rate_change, - net_address, - eth_hot_key, - eth_cold_key, - metadata, - } = unsigned_tx; - - let consensus_key = SignedPk { - pk: consensus_key, - authorization: consensus_key_sig, - }; - let protocol_key = SignedPk { - pk: protocol_key, - authorization: protocol_key_sig, - }; - let tendermint_node_key = SignedPk { - pk: tendermint_node_key, - authorization: tendermint_node_key_sig, - }; - - let eth_hot_key = SignedPk { - pk: eth_hot_key, - authorization: eth_hot_key_sig, + let mut to_sign = match to_sign { + Either::Right(signed_tx) => signed_tx, + Either::Left((unsigned_tx, validator_wallet)) => { + // Sign the tx with every validator key to authorize their usage + let consensus_key_sig = + sign_tx(&unsigned_tx, &validator_wallet.consensus_key); + let protocol_key_sig = sign_tx( + &unsigned_tx, + &validator_wallet.store.validator_keys.protocol_keypair, + ); + let eth_hot_key_sig = + sign_tx(&unsigned_tx, &validator_wallet.eth_hot_key); + let eth_cold_key_sig = + sign_tx(&unsigned_tx, &validator_wallet.eth_cold_key); + let tendermint_node_key_sig = + sign_tx(&unsigned_tx, &validator_wallet.tendermint_node_key); + + let ValidatorAccountTx { + address, + consensus_key, + protocol_key, + tendermint_node_key, + vp, + commission_rate, + max_commission_rate_change, + net_address, + eth_hot_key, + eth_cold_key, + metadata, + } = unsigned_tx; + + let consensus_key = SignedPk { + pk: consensus_key, + authorization: consensus_key_sig, + }; + let protocol_key = SignedPk { + pk: protocol_key, + authorization: protocol_key_sig, + }; + let tendermint_node_key = SignedPk { + pk: tendermint_node_key, + authorization: tendermint_node_key_sig, + }; + + let eth_hot_key = SignedPk { + pk: eth_hot_key, + authorization: eth_hot_key_sig, + }; + + let eth_cold_key = SignedPk { + pk: eth_cold_key, + authorization: eth_cold_key_sig, + }; + + Signed::new(ValidatorAccountTx { + address, + consensus_key, + protocol_key, + tendermint_node_key, + vp, + commission_rate, + max_commission_rate_change, + net_address, + eth_hot_key, + eth_cold_key, + metadata, + }) + } }; - let eth_cold_key = SignedPk { - pk: eth_cold_key, - authorization: eth_cold_key_sig, - }; + let source_keys = look_up_sks_from( + &GenesisAddress::EstablishedAddress(to_sign.data.address.raw.clone()), + wallet, + Some(established_accounts), + ); - SignedValidatorAccountTx { - address, - consensus_key, - protocol_key, - tendermint_node_key, - vp, - commission_rate, - max_commission_rate_change, - net_address, - eth_hot_key, - eth_cold_key, - metadata, - } + to_sign.sign(&source_keys).await; + to_sign } pub async fn sign_delegation_bond_tx( @@ -274,8 +309,11 @@ pub async fn sign_delegation_bond_tx( wallet: &mut Wallet, established_accounts: &Option>, ) -> SignedBondTx { - let source_keys = - look_up_sk_from(&to_sign.data.source, wallet, established_accounts); + let source_keys = look_up_sks_from( + &to_sign.data.source, + wallet, + established_accounts.as_ref().map(|txs| txs.as_slice()), + ); to_sign.sign(&source_keys).await; to_sign } @@ -407,7 +445,9 @@ pub struct UnsignedTransactions { pub type UnsignedValidatorAccountTx = ValidatorAccountTx>; -pub type SignedValidatorAccountTx = ValidatorAccountTx; +pub type SignedValidatorAccountTx = Signed>; + +pub type SignedBondTx = Signed>; #[derive( Clone, @@ -443,6 +483,25 @@ pub struct ValidatorAccountTx { pub metadata: ValidatorMetaData, } +impl TxToSign for ValidatorAccountTx { + fn tx_to_sign(&self) -> Vec { + [ + self.address.serialize_to_vec(), + self.vp.serialize_to_vec(), + self.commission_rate.serialize_to_vec(), + self.max_commission_rate_change.serialize_to_vec(), + self.net_address.serialize_to_vec(), + self.consensus_key.pk.raw.serialize_to_vec(), + self.protocol_key.pk.raw.serialize_to_vec(), + self.tendermint_node_key.pk.raw.serialize_to_vec(), + self.eth_hot_key.pk.raw.serialize_to_vec(), + self.eth_cold_key.pk.raw.serialize_to_vec(), + self.metadata.serialize_to_vec(), + ] + .concat() + } +} + #[derive( Clone, Debug, @@ -483,44 +542,75 @@ impl DeriveEstablishedAddress for EstablishedAccountTx { PartialOrd, Ord, )] -pub struct SignedBondTx { +pub struct Signed { #[serde(flatten)] - pub data: BondTx, - pub signatures: Vec>, + pub data: T, + pub signatures: BTreeMap< + StringEncoded, + StringEncoded, + >, } -impl SignedBondTx -where - T: BorshSerialize + TemplateValidation, -{ - /// Verify the signatures of `BondTx`. This should not depend - /// on whether the contained amount is denominated or not. - /// - /// Since we denominate amounts as part of validation, we can - /// only verify signatures on [`SignedBondTx`] - /// types. +impl Signed { + /// Instantiate data to be signed. + pub const fn new(data: T) -> Self { + Self { + data, + signatures: BTreeMap::new(), + } + } + + /// Return the inner wrapped `T`. + pub fn into_inner(self) -> T { + let Signed { data, .. } = self; + data + } + + /// Sign the underlying data and add to the list of signatures. + pub async fn sign(&mut self, keys: &[common::SecretKey]) + where + T: BorshSerialize + TxToSign, + { + for sk in keys { + self.signatures.insert( + StringEncoded::new(sk.ref_to()), + StringEncoded::new( + standalone_signature::<_, SerializeWithBorsh>( + sk, + &self.data.tx_to_sign(), + ), + ), + ); + } + } + + /// Verify the signatures of the inner data. pub fn verify_sig( &self, pks: &[common::PublicKey], threshold: u8, - ) -> Result<(), VerifySigError> { + ) -> Result<(), VerifySigError> + where + T: BorshSerialize + TxToSign, + { let Self { data, signatures } = self; if pks.len() > u8::MAX as usize { eprintln!("You're multisig is too facking big"); return Err(VerifySigError::TooGoddamnBig); } let mut valid_sigs = 0; + let tx_to_sign = data.tx_to_sign(); for pk in pks { - valid_sigs += signatures.iter().any(|sig| { - verify_standalone_sig::<_, SerializeWithBorsh>( - &data.data_to_sign(), + if let Some(sig) = signatures.get(&StringEncoded::new(pk.clone())) { + valid_sigs += verify_standalone_sig::<_, SerializeWithBorsh>( + &tx_to_sign, pk, &sig.raw, ) - .is_ok() - }) as u8; - if valid_sigs >= threshold { - break; + .is_ok() as u8; + if valid_sigs >= threshold { + break; + } } } if valid_sigs >= threshold { @@ -531,26 +621,6 @@ where } } -impl SignedBondTx { - /// Sign the transfer and add to the list of signatures. - /// - /// Since we denominate amounts as part of validation, we can - /// only verify signatures on [`SignedBondTx`] - /// types. Thus we only allow signing of [`BondTx`] - /// types. - pub async fn sign(&mut self, key: &[common::SecretKey]) { - self.signatures.extend(key.iter().map(|sk| { - StringEncoded::new(standalone_signature::<_, SerializeWithBorsh>( - sk, - &self.data.data_to_sign(), - )) - })); - let mut sigs = vec![]; - std::mem::swap(&mut self.signatures, &mut sigs); - self.signatures = sigs.into_iter().sorted().dedup().collect(); - } -} - #[derive( Clone, Debug, @@ -569,12 +639,11 @@ pub struct BondTx { pub amount: T::Amount, } -impl BondTx +impl TxToSign for BondTx where T: TemplateValidation + BorshSerialize, { - /// The signable data. This does not include the phantom data. - fn data_to_sign(&self) -> Vec { + fn tx_to_sign(&self) -> Vec { [ self.source.serialize_to_vec(), self.validator.serialize_to_vec(), @@ -611,11 +680,9 @@ impl BondTx { } impl From> for SignedBondTx { + #[inline] fn from(bond: BondTx) -> Self { - SignedBondTx { - data: bond, - signatures: vec![], - } + Signed::new(bond) } } @@ -676,6 +743,7 @@ pub fn validate( tx, vps, &all_used_addresses, + &established_accounts, &mut validator_accounts, ) { is_valid = false; @@ -748,19 +816,23 @@ pub fn validate( |validator_accounts| { validator_accounts .into_iter() - .map(|acct| ValidatorAccountTx { - address: acct.address, - vp: acct.vp, - commission_rate: acct.commission_rate, - max_commission_rate_change: acct - .max_commission_rate_change, - net_address: acct.net_address, - consensus_key: acct.consensus_key, - protocol_key: acct.protocol_key, - tendermint_node_key: acct.tendermint_node_key, - eth_hot_key: acct.eth_hot_key, - eth_cold_key: acct.eth_cold_key, - metadata: acct.metadata, + .map(|acct| SignedValidatorAccountTx { + signatures: acct.signatures, + data: ValidatorAccountTx { + address: acct.data.address, + vp: acct.data.vp, + commission_rate: acct.data.commission_rate, + max_commission_rate_change: acct + .data + .max_commission_rate_change, + net_address: acct.data.net_address, + consensus_key: acct.data.consensus_key, + protocol_key: acct.data.protocol_key, + tendermint_node_key: acct.data.tendermint_node_key, + eth_hot_key: acct.data.eth_hot_key, + eth_cold_key: acct.data.eth_cold_key, + metadata: acct.data.metadata, + }, }) .collect() }, @@ -946,12 +1018,39 @@ pub fn validate_established_account( } pub fn validate_validator_account( - tx: &ValidatorAccountTx, + signed_tx: &SignedValidatorAccountTx, vps: Option<&ValidityPredicates>, all_used_addresses: &BTreeSet
, + established_accounts: &BTreeMap, u8)>, validator_accounts: &mut BTreeSet
, ) -> bool { - let mut is_valid = true; + let tx = &signed_tx.data; + + // Check signature + let mut is_valid = { + let maybe_source = { + let established_addr = Address::Established(tx.address.raw.clone()); + established_accounts + .get(&established_addr) + .map(|(pks, t)| (pks.as_slice(), *t)) + }; + if let Some((source_pks, threshold)) = maybe_source { + if signed_tx.verify_sig(source_pks, threshold).is_err() { + eprintln!("Invalid validator account signature."); + false + } else { + true + } + } else { + let source = &tx.address.raw; + eprintln!( + "Invalid validator account tx. Couldn't verify the underlying \ + established account signatures, because the source account's \ + \"{source}\" public keys cannot be found." + ); + false + } + }; let established_address = { let established_address = Address::Established(tx.address.raw.clone()); @@ -1073,9 +1172,9 @@ fn validate_signature( } } -impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { - fn from(tx: &SignedValidatorAccountTx) -> Self { - let SignedValidatorAccountTx { +impl From<&ValidatorAccountTx> for UnsignedValidatorAccountTx { + fn from(tx: &ValidatorAccountTx) -> Self { + let ValidatorAccountTx { address, vp, commission_rate, @@ -1106,18 +1205,12 @@ impl From<&SignedValidatorAccountTx> for UnsignedValidatorAccountTx { } } -impl From<&SignedBondTx> for BondTx { - fn from(tx: &SignedBondTx) -> Self { - let SignedBondTx { data, .. } = tx; - data.clone() - } -} - -/// Attempt to look-up a secret key. -fn look_up_sk_from( +/// Attempt look-up a subset of secret keys from the wallet matching +/// the queried established account's public keys. +fn look_up_sks_from( source: &GenesisAddress, wallet: &mut Wallet, - established_accounts: &Option>, + established_accounts: Option<&[EstablishedAccountTx]>, ) -> Vec { // Try to look-up the source from wallet first match source { @@ -1133,7 +1226,6 @@ fn look_up_sk_from( return vec![]; } established_accounts - .as_ref() .unwrap() .iter() .find_map(|account| match source { diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index ee9b7af289..88af71c96a 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -23,7 +23,7 @@ use crate::config::genesis::chain::{ }; use crate::config::genesis::templates::{TokenBalances, TokenConfig}; use crate::config::genesis::transactions::{ - BondTx, EstablishedAccountTx, ValidatorAccountTx, + BondTx, EstablishedAccountTx, Signed as SignedTx, ValidatorAccountTx, }; use crate::facade::tendermint::v0_37::abci::{request, response}; use crate::facade::tendermint_proto::google::protobuf; @@ -391,17 +391,21 @@ where for FinalizedValidatorAccountTx { address, tx: - ValidatorAccountTx { - vp, - commission_rate, - max_commission_rate_change, - metadata, - net_address: _, - consensus_key, - protocol_key, - tendermint_node_key: _, - eth_hot_key, - eth_cold_key, + SignedTx { + data: + ValidatorAccountTx { + vp, + commission_rate, + max_commission_rate_change, + metadata, + net_address: _, + consensus_key, + protocol_key, + tendermint_node_key: _, + eth_hot_key, + eth_cold_key, + .. + }, .. }, } in txs diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index b0d0cf47d1..0b20e3fa7c 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -285,9 +285,9 @@ fn setup_two_single_node_nets() -> Result<(Test, Test)> { .iter_mut() .find(|val| val.address == validator_addr) .unwrap(); - let new_port = - validator_tx.tx.net_address.port() + setup::ANOTHER_CHAIN_PORT_OFFSET; - validator_tx.tx.net_address.set_port(new_port); + let new_port = validator_tx.tx.data.net_address.port() + + setup::ANOTHER_CHAIN_PORT_OFFSET; + validator_tx.tx.data.net_address.set_port(new_port); genesis_b .write_toml_files(&genesis_b_dir.join(test_a.net.chain_id.as_str())) .map_err(|_| eyre!("Could not write genesis toml files for test_b"))?; From f77550885806169de7a3c0ad02f467dcbc72fa64 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 24 Nov 2023 14:18:57 +0000 Subject: [PATCH 26/81] Sign over an actual bond tx --- apps/src/lib/config/genesis/templates.rs | 1 + apps/src/lib/config/genesis/transactions.rs | 36 ++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index ba7a4bc682..166efcad9a 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -524,6 +524,7 @@ pub struct Validated {} pub trait TemplateValidation: Serialize { type Amount: for<'a> Deserialize<'a> + Serialize + + Into + Clone + std::fmt::Debug + BorshSerialize diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index cc0a7712c3..cb0e9cdd4d 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -11,13 +11,15 @@ use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::types::ValidatorMetaData; use namada::proto::{ - standalone_signature, verify_standalone_sig, SerializeWithBorsh, + standalone_signature, verify_standalone_sig, SerializeWithBorsh, Tx, }; use namada::types::dec::Dec; -use namada::types::key::{common, RefTo, VerifySigError}; +use namada::types::key::{common, ed25519, RefTo, SigScheme, VerifySigError}; use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use namada::types::transaction::{pos, Fee, TxType}; +use namada_sdk::tx::TX_BOND_WASM; use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; use namada_sdk::wallet::Wallet; @@ -644,12 +646,30 @@ where T: TemplateValidation + BorshSerialize, { fn tx_to_sign(&self) -> Vec { - [ - self.source.serialize_to_vec(), - self.validator.serialize_to_vec(), - self.amount.serialize_to_vec(), - ] - .concat() + let mut tx = Tx::from_type(TxType::Raw); + tx.add_code_from_hash( + Default::default(), + Some(TX_BOND_WASM.to_string()), + ); + tx.add_data(pos::Bond { + validator: self.validator.clone(), + amount: self.amount.clone().into(), + source: Some(self.source.address()), + }); + let pk = + common::SecretKey::Ed25519(ed25519::SigScheme::from_bytes([0; 32])) + .ref_to(); + tx.add_wrapper( + Fee { + amount_per_gas_unit: Default::default(), + token: Address::from(&pk), + }, + pk, + Default::default(), + Default::default(), + None, + ); + tx.to_bytes() } } From 694b3688b86fdbc6956b60777ff6122f658f3f34 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 24 Nov 2023 15:28:09 +0000 Subject: [PATCH 27/81] Implement signable tx helper --- apps/src/lib/config/genesis/transactions.rs | 52 ++++++++++++--------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index cb0e9cdd4d..40028d470d 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -39,6 +39,27 @@ pub trait TxToSign { fn tx_to_sign(&self) -> Vec; } +/// Return a ready to sign genesis [`Tx`]. +fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { + let mut tx = Tx::from_type(TxType::Raw); + tx.add_code_from_hash(Default::default(), Some(tag.as_ref().to_string())); + tx.add_data(data); + let pk = + common::SecretKey::Ed25519(ed25519::SigScheme::from_bytes([0; 32])) + .ref_to(); + tx.add_wrapper( + Fee { + amount_per_gas_unit: Default::default(), + token: Address::from(&pk), + }, + pk, + Default::default(), + Default::default(), + None, + ); + tx +} + pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; pub struct GenesisValidatorData { @@ -646,30 +667,15 @@ where T: TemplateValidation + BorshSerialize, { fn tx_to_sign(&self) -> Vec { - let mut tx = Tx::from_type(TxType::Raw); - tx.add_code_from_hash( - Default::default(), - Some(TX_BOND_WASM.to_string()), - ); - tx.add_data(pos::Bond { - validator: self.validator.clone(), - amount: self.amount.clone().into(), - source: Some(self.source.address()), - }); - let pk = - common::SecretKey::Ed25519(ed25519::SigScheme::from_bytes([0; 32])) - .ref_to(); - tx.add_wrapper( - Fee { - amount_per_gas_unit: Default::default(), - token: Address::from(&pk), + get_tx_to_sign( + TX_BOND_WASM, + pos::Bond { + validator: self.validator.clone(), + amount: self.amount.clone().into(), + source: Some(self.source.address()), }, - pk, - Default::default(), - Default::default(), - None, - ); - tx.to_bytes() + ) + .to_bytes() } } From fd0132ef916a193efdfd6147cfa5cf224d1db18f Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 24 Nov 2023 15:42:20 +0000 Subject: [PATCH 28/81] Sign over an actual become validator tx --- apps/src/lib/config/genesis/transactions.rs | 40 ++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 40028d470d..cb0faaaabd 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -5,7 +5,6 @@ use std::fmt::Debug; use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; use itertools::{Either, Itertools}; use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; @@ -19,7 +18,7 @@ use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada::types::transaction::{pos, Fee, TxType}; -use namada_sdk::tx::TX_BOND_WASM; +use namada_sdk::tx::{TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM}; use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; use namada_sdk::wallet::Wallet; @@ -508,20 +507,29 @@ pub struct ValidatorAccountTx { impl TxToSign for ValidatorAccountTx { fn tx_to_sign(&self) -> Vec { - [ - self.address.serialize_to_vec(), - self.vp.serialize_to_vec(), - self.commission_rate.serialize_to_vec(), - self.max_commission_rate_change.serialize_to_vec(), - self.net_address.serialize_to_vec(), - self.consensus_key.pk.raw.serialize_to_vec(), - self.protocol_key.pk.raw.serialize_to_vec(), - self.tendermint_node_key.pk.raw.serialize_to_vec(), - self.eth_hot_key.pk.raw.serialize_to_vec(), - self.eth_cold_key.pk.raw.serialize_to_vec(), - self.metadata.serialize_to_vec(), - ] - .concat() + get_tx_to_sign( + TX_BECOME_VALIDATOR_WASM, + pos::BecomeValidator { + address: Address::Established(self.address.raw.clone()), + consensus_key: self.consensus_key.pk.raw.clone(), + eth_hot_key: match &self.eth_hot_key.pk.raw { + common::PublicKey::Secp256k1(key) => key.clone(), + _ => unreachable!(), + }, + eth_cold_key: match &self.eth_cold_key.pk.raw { + common::PublicKey::Secp256k1(key) => key.clone(), + _ => unreachable!(), + }, + protocol_key: self.protocol_key.pk.raw.clone(), + commission_rate: self.commission_rate, + max_commission_rate_change: self.max_commission_rate_change, + email: self.metadata.email.clone(), + description: self.metadata.description.clone(), + website: self.metadata.website.clone(), + discord_handle: self.metadata.discord_handle.clone(), + }, + ) + .to_bytes() } } From 16a489e91b87c80f1eb5531582f66ca27f34cb95 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 24 Nov 2023 15:42:50 +0000 Subject: [PATCH 29/81] Validator key validation --- apps/src/lib/config/genesis/transactions.rs | 37 +++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index cb0faaaabd..2a0b4f4d05 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -1060,13 +1060,44 @@ pub fn validate_validator_account( ) -> bool { let tx = &signed_tx.data; + // Check eth keys are secp256k1 keys + if !matches!( + &signed_tx.data.eth_cold_key.pk.raw, + common::PublicKey::Secp256k1(_) + ) { + panic!( + "The validator with address {} has a non Secp256k1 Ethereum cold \ + key", + signed_tx.data.address + ); + } + if !matches!( + &signed_tx.data.eth_hot_key.pk.raw, + common::PublicKey::Secp256k1(_) + ) { + panic!( + "The validator with address {} has a non Secp256k1 Ethereum hot \ + key", + signed_tx.data.address + ); + } + // Check signature let mut is_valid = { let maybe_source = { let established_addr = Address::Established(tx.address.raw.clone()); - established_accounts - .get(&established_addr) - .map(|(pks, t)| (pks.as_slice(), *t)) + established_accounts.get(&established_addr).map(|(pks, t)| { + let all_ed25519_keys = pks + .iter() + .all(|key| matches!(key, common::PublicKey::Ed25519(_))); + if !all_ed25519_keys { + panic!( + "Not all account keys of the validator with address \ + {established_addr} are Ed25519 keys" + ); + } + (pks.as_slice(), *t) + }) }; if let Some((source_pks, threshold)) = maybe_source { if signed_tx.verify_sig(source_pks, threshold).is_err() { From d2a075f62282494eb89b1798ac6f378a34edce26 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 27 Nov 2023 17:12:19 +0100 Subject: [PATCH 30/81] lifetime hell --- apps/src/lib/cli.rs | 7 + apps/src/lib/client/tx.rs | 206 ++++++++-------- apps/src/lib/client/utils.rs | 32 +++ apps/src/lib/config/genesis.rs | 2 +- apps/src/lib/config/genesis/chain.rs | 2 +- apps/src/lib/config/genesis/templates.rs | 2 +- apps/src/lib/config/genesis/toml_utils.rs | 38 --- apps/src/lib/config/genesis/transactions.rs | 259 ++++++++++++-------- apps/src/lib/config/genesis/utils.rs | 76 ++++++ sdk/src/io.rs | 1 + sdk/src/lib.rs | 9 +- sdk/src/signing.rs | 13 +- tests/src/e2e/setup.rs | 2 +- 13 files changed, 398 insertions(+), 251 deletions(-) delete mode 100644 apps/src/lib/config/genesis/toml_utils.rs create mode 100644 apps/src/lib/config/genesis/utils.rs diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index e3cf07fffc..b74bb5c496 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6943,6 +6943,7 @@ pub mod args { pub path: PathBuf, pub output: Option, pub validator_alias: Option, + pub use_device: bool, } impl Args for SignGenesisTx { @@ -6950,10 +6951,12 @@ pub mod args { let path = PATH.parse(matches); let output = OUTPUT.parse(matches); let validator_alias = ALIAS_OPT.parse(matches); + let use_device = USE_DEVICE.parse(matches); Self { path, output, validator_alias, + use_device, } } @@ -6971,6 +6974,10 @@ pub mod args { .def() .help("Optional alias to a validator wallet."), ) + .arg(USE_DEVICE.def().help( + "Derive an address and public key from the seed stored on the \ + connected hardware wallet.", + )) } } } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 7f2f2f930b..3e1c7cc10c 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1,6 +1,8 @@ use std::collections::HashSet; use std::fs::File; +use std::future::Future; use std::io::Write; +use std::pin::Pin; use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; @@ -22,8 +24,10 @@ use namada::types::key::{self, *}; use namada::types::transaction::pos::{BecomeValidator, ConsensusKeyChange}; use namada_sdk::rpc::{TxBroadcastData, TxResponse}; use namada_sdk::wallet::alias::validator_consensus_key; +use namada_sdk::wallet::{Wallet, WalletIo}; use namada_sdk::{display_line, edisplay_line, error, signing, tx, Namada}; use rand::rngs::OsRng; +use tokio::sync::RwLock; use super::rpc; use crate::cli::{args, safe_exit}; @@ -68,9 +72,105 @@ pub async fn aux_signing_data<'a>( Ok(signing_data) } +pub fn with_hardware_wallet<'a, 'b, U: WalletIo + Clone>( + wallet: &RwLock<&'b mut Wallet>, + app: &NamadaApp, +) -> impl Fn( + Tx, + common::PublicKey, + HashSet, +) -> Pin>>> { + let wallet = wallet.read(); + move |mut tx: Tx, + pubkey: common::PublicKey, + parts: HashSet| { + Box::pin(async move { + // Obtain derivation path + let path = wallet + .await + .find_path_by_pkh(&(&pubkey).into()) + .map_err(|_| { + error::Error::Other( + "Unable to find derivation path for key".to_string(), + ) + })?; + let path = BIP44Path { + path: path.to_string(), + }; + // Now check that the public key at this path in the Ledger + // matches + let response_pubkey = app + .get_address_and_pubkey(&path, false) + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + let response_pubkey = + common::PublicKey::try_from_slice(&response_pubkey.public_key) + .map_err(|err| { + error::Error::Other(format!( + "unable to decode public key from hardware \ + wallet: {}", + err + )) + })?; + if response_pubkey != pubkey { + return Err(error::Error::Other(format!( + "Unrecognized public key fetched fom Ledger: {}. Expected \ + {}.", + response_pubkey, pubkey, + ))); + } + // Get the Ledger to sign using our obtained derivation path + let response = app + .sign(&path, &tx.serialize_to_vec()) + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + // Sign the raw header if that is requested + if parts.contains(&signing::Signable::RawHeader) { + let pubkey = + common::PublicKey::try_from_slice(&response.pubkey) + .expect("unable to parse public key from Ledger"); + let signature = + common::Signature::try_from_slice(&response.raw_signature) + .expect("unable to parse signature from Ledger"); + // Signatures from the Ledger come back in compressed + // form + let compressed = CompressedSignature { + targets: response.raw_indices, + signer: Signer::PubKeys(vec![pubkey]), + signatures: [(0, signature)].into(), + }; + // Expand out the signature before adding it to the + // transaction + tx.add_section(Section::Signature(compressed.expand(&tx))); + } + // Sign the fee header if that is requested + if parts.contains(&signing::Signable::FeeHeader) { + let pubkey = + common::PublicKey::try_from_slice(&response.pubkey) + .expect("unable to parse public key from Ledger"); + let signature = common::Signature::try_from_slice( + &response.wrapper_signature, + ) + .expect("unable to parse signature from Ledger"); + // Signatures from the Ledger come back in compressed + // form + let compressed = CompressedSignature { + targets: response.wrapper_indices, + signer: Signer::PubKeys(vec![pubkey]), + signatures: [(0, signature)].into(), + }; + // Expand out the signature before adding it to the + // transaction + tx.add_section(Section::Signature(compressed.expand(&tx))); + } + Ok(tx) + }) + } +} + // Sign the given transaction using a hardware wallet as a backup -pub async fn sign<'a>( - context: &impl Namada<'a>, +pub async fn sign<'a, N: Namada<'a>>( + context: &N, tx: &mut Tx, args: &args::Tx, signing_data: SigningTxData, @@ -84,112 +184,14 @@ pub async fn sign<'a>( let app = NamadaApp::new(TransportNativeHID::new(&hidapi).map_err( |err| { error::Error::Other(format!( - "Unable to connect to Ledger: {}", + "Unble to connect to Ledger: {}", err )) }, )?); // A closure to facilitate signing transactions also using the Ledger let with_hw = - |mut tx: Tx, - pubkey: common::PublicKey, - parts: HashSet| { - let app = &app; - async move { - // Obtain derivation path corresponding to the signing - // public key - let path = context - .wallet() - .await - .find_path_by_pkh(&(&pubkey).into()) - .map_err(|_| { - error::Error::Other( - "Unable to find derivation path for key" - .to_string(), - ) - })?; - let path = BIP44Path { - path: path.to_string(), - }; - // Now check that the public key at this path in the Ledger - // matches - let response_pubkey = app - .get_address_and_pubkey(&path, false) - .await - .map_err(|err| error::Error::Other(err.to_string()))?; - let response_pubkey = common::PublicKey::try_from_slice( - &response_pubkey.public_key, - ) - .map_err(|err| { - error::Error::Other(format!( - "unable to decode public key from hardware \ - wallet: {}", - err - )) - })?; - if response_pubkey != pubkey { - return Err(error::Error::Other(format!( - "Unrecognized public key fetched fom Ledger: {}. \ - Expected {}.", - response_pubkey, pubkey, - ))); - } - // Get the Ledger to sign using our obtained derivation path - let response = app - .sign(&path, &tx.serialize_to_vec()) - .await - .map_err(|err| error::Error::Other(err.to_string()))?; - // Sign the raw header if that is requested - if parts.contains(&signing::Signable::RawHeader) { - let pubkey = - common::PublicKey::try_from_slice(&response.pubkey) - .expect( - "unable to parse public key from Ledger", - ); - let signature = common::Signature::try_from_slice( - &response.raw_signature, - ) - .expect("unable to parse signature from Ledger"); - // Signatures from the Ledger come back in compressed - // form - let compressed = CompressedSignature { - targets: response.raw_indices, - signer: Signer::PubKeys(vec![pubkey]), - signatures: [(0, signature)].into(), - }; - // Expand out the signature before adding it to the - // transaction - tx.add_section(Section::Signature( - compressed.expand(&tx), - )); - } - // Sign the fee header if that is requested - if parts.contains(&signing::Signable::FeeHeader) { - let pubkey = - common::PublicKey::try_from_slice(&response.pubkey) - .expect( - "unable to parse public key from Ledger", - ); - let signature = common::Signature::try_from_slice( - &response.wrapper_signature, - ) - .expect("unable to parse signature from Ledger"); - // Signatures from the Ledger come back in compressed - // form - let compressed = CompressedSignature { - targets: response.wrapper_indices, - signer: Signer::PubKeys(vec![pubkey]), - signatures: [(0, signature)].into(), - }; - // Expand out the signature before adding it to the - // transaction - tx.add_section(Section::Signature( - compressed.expand(&tx), - )); - } - Ok(tx) - } - }; + with_hardware_wallet::(context.wallet_lock(), &app); // Finally, begin the signing with the Ledger as backup context.sign(tx, args, signing_data, with_hw).await?; } else { diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 6c42648ab5..dea469bb86 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -11,6 +11,7 @@ use flate2::write::GzEncoder; use flate2::Compression; use itertools::Either; use namada::core::types::string_encoding::StringEncoded; +use namada::types::address::nam; use namada::types::chain::ChainId; use namada::types::dec::Dec; use namada::types::key::*; @@ -968,6 +969,7 @@ pub async fn sign_genesis_tx( path, output, validator_alias, + use_device, }: args::SignGenesisTx, ) { let (mut wallet, _wallet_file) = @@ -1016,6 +1018,7 @@ pub async fn sign_genesis_tx( tx, &mut wallet, &genesis_txs.established_account, + get_tx_args(use_device), ) .await, ); @@ -1037,6 +1040,7 @@ pub async fn sign_genesis_tx( "Established account txs required \ when signing validator account txs", ), + get_tx_args(use_device), ) .await, ); @@ -1079,6 +1083,34 @@ pub async fn sign_genesis_tx( } } +fn get_tx_args(use_device: bool) -> args::Tx { + args::Tx { + dry_run: false, + dry_run_wrapper: false, + dump_tx: false, + output_folder: None, + force: false, + broadcast_only: false, + ledger_address: (), + initialized_account_alias: None, + wallet_alias_force: false, + fee_amount: None, + wrapper_fee_payer: None, + fee_token: nam(), + fee_unshield: None, + gas_limit: Default::default(), + expiration: None, + disposable_signing_key: false, + chain_id: None, + signing_keys: vec![], + signatures: vec![], + tx_reveal_code_path: Default::default(), + verification_key: None, + password: None, + use_device, + } +} + /// Add a spinning wheel to a message for long running commands. /// Can be turned off for E2E tests by setting the `REDUCED_CLI_PRINTING` /// environment variable. diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 33d8fe90ba..ca340cadfd 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -2,8 +2,8 @@ pub mod chain; pub mod templates; -pub mod toml_utils; pub mod transactions; +pub mod utils; use std::collections::{BTreeMap, HashMap}; use std::fmt::{Display, Formatter}; diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index b5ff28d086..88e6893acc 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -19,7 +19,7 @@ use namada_sdk::wallet::{pre_genesis, Wallet}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -use super::toml_utils::{read_toml, write_toml}; +use super::utils::{read_toml, write_toml}; use super::{templates, transactions}; use crate::config::genesis::templates::Validated; use crate::config::utils::{set_ip, set_port}; diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index 166efcad9a..fb374c4866 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -17,8 +17,8 @@ use namada::types::token::{ }; use serde::{Deserialize, Serialize}; -use super::toml_utils::{read_toml, write_toml}; use super::transactions::{self, Transactions}; +use super::utils::{read_toml, write_toml}; use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::transactions::{BondTx, SignedBondTx}; use crate::config::genesis::GenesisAddress; diff --git a/apps/src/lib/config/genesis/toml_utils.rs b/apps/src/lib/config/genesis/toml_utils.rs deleted file mode 100644 index e699341951..0000000000 --- a/apps/src/lib/config/genesis/toml_utils.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::path::Path; - -use eyre::Context; -use serde::de::DeserializeOwned; -use serde::Serialize; - -pub fn read_toml( - path: &Path, - which_file: &str, -) -> eyre::Result { - let file_contents = std::fs::read_to_string(path).wrap_err_with(|| { - format!( - "Couldn't read {which_file} config file from {}", - path.to_string_lossy() - ) - })?; - toml::from_str(&file_contents).wrap_err_with(|| { - format!( - "Couldn't parse {which_file} TOML from {}", - path.to_string_lossy() - ) - }) -} - -pub fn write_toml( - data: &T, - path: &Path, - which_file: &str, -) -> eyre::Result<()> { - let file_contents = toml::to_vec(data) - .wrap_err_with(|| format!("Couldn't format {which_file} to TOML."))?; - std::fs::write(path, file_contents).wrap_err_with(|| { - format!( - "Couldn't write {which_file} TOML to {}", - path.to_string_lossy() - ) - }) -} diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 2a0b4f4d05..cc3ff7c592 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -8,34 +8,44 @@ use borsh::{BorshDeserialize, BorshSerialize}; use itertools::{Either, Itertools}; use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::string_encoding::StringEncoded; +use namada::ledger::pos::common::PublicKey; use namada::ledger::pos::types::ValidatorMetaData; -use namada::proto::{ - standalone_signature, verify_standalone_sig, SerializeWithBorsh, Tx, -}; +use namada::proto::{verify_standalone_sig, Section, SerializeWithBorsh, Tx}; use namada::types::dec::Dec; use namada::types::key::{common, ed25519, RefTo, SigScheme, VerifySigError}; use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada::types::transaction::{pos, Fee, TxType}; +use namada_sdk::signing::{sign_tx, SigningTxData}; use namada_sdk::tx::{TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM}; use namada_sdk::wallet::alias::Alias; use namada_sdk::wallet::pre_genesis::ValidatorWallet; use namada_sdk::wallet::Wallet; use serde::{Deserialize, Serialize}; +use tokio::sync::RwLock; use super::templates::{DenominatedBalances, Parameters, ValidityPredicates}; +use crate::cli::args; use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::templates::{ TemplateValidation, Unvalidated, Validated, }; -use crate::config::genesis::GenesisAddress; +use crate::config::genesis::{utils, GenesisAddress}; use crate::wallet::CliWalletUtils; /// Helper trait to fetch tx data to sign. pub trait TxToSign { /// Return tx data to sign. - fn tx_to_sign(&self) -> Vec; + fn tx_to_sign(&self) -> Tx; + + /// Get public keys that may sign this transaction from a genesis address + fn get_pks( + &self, + accounts: &[EstablishedAccountTx], + ) -> (Vec, u8); + + fn get_owner(&self) -> GenesisAddress; } /// Return a ready to sign genesis [`Tx`]. @@ -245,23 +255,33 @@ pub async fn sign_validator_account_tx( >, wallet: &mut Wallet, established_accounts: &[EstablishedAccountTx], + args: args::Tx, ) -> SignedValidatorAccountTx { let mut to_sign = match to_sign { Either::Right(signed_tx) => signed_tx, Either::Left((unsigned_tx, validator_wallet)) => { + fn sign_key( + tx_data: &T, + keypair: &common::SecretKey, + ) -> StringEncoded { + StringEncoded::new(namada::proto::standalone_signature::< + T, + SerializeWithBorsh, + >(keypair, tx_data)) + } // Sign the tx with every validator key to authorize their usage let consensus_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.consensus_key); - let protocol_key_sig = sign_tx( + sign_key(&unsigned_tx, &validator_wallet.consensus_key); + let protocol_key_sig = sign_key( &unsigned_tx, &validator_wallet.store.validator_keys.protocol_keypair, ); let eth_hot_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.eth_hot_key); + sign_key(&unsigned_tx, &validator_wallet.eth_hot_key); let eth_cold_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.eth_cold_key); + sign_key(&unsigned_tx, &validator_wallet.eth_cold_key); let tendermint_node_key_sig = - sign_tx(&unsigned_tx, &validator_wallet.tendermint_node_key); + sign_key(&unsigned_tx, &validator_wallet.tendermint_node_key); let ValidatorAccountTx { address, @@ -316,13 +336,7 @@ pub async fn sign_validator_account_tx( } }; - let source_keys = look_up_sks_from( - &GenesisAddress::EstablishedAddress(to_sign.data.address.raw.clone()), - wallet, - Some(established_accounts), - ); - - to_sign.sign(&source_keys).await; + to_sign.sign(established_accounts, wallet, args).await; to_sign } @@ -330,26 +344,15 @@ pub async fn sign_delegation_bond_tx( mut to_sign: SignedBondTx, wallet: &mut Wallet, established_accounts: &Option>, + args: args::Tx, ) -> SignedBondTx { - let source_keys = look_up_sks_from( - &to_sign.data.source, - wallet, - established_accounts.as_ref().map(|txs| txs.as_slice()), - ); - to_sign.sign(&source_keys).await; + let default = vec![]; + let established_accounts = + established_accounts.as_ref().unwrap_or(&default); + to_sign.sign(established_accounts, wallet, &mut args).await; to_sign } -pub fn sign_tx( - tx_data: &T, - keypair: &common::SecretKey, -) -> StringEncoded { - StringEncoded::new(namada::proto::standalone_signature::< - T, - SerializeWithBorsh, - >(keypair, tx_data)) -} - #[derive( Clone, Debug, @@ -506,7 +509,7 @@ pub struct ValidatorAccountTx { } impl TxToSign for ValidatorAccountTx { - fn tx_to_sign(&self) -> Vec { + fn tx_to_sign(&self) -> Tx { get_tx_to_sign( TX_BECOME_VALIDATOR_WASM, pos::BecomeValidator { @@ -529,7 +532,39 @@ impl TxToSign for ValidatorAccountTx { discord_handle: self.metadata.discord_handle.clone(), }, ) - .to_bytes() + } + + fn get_pks( + &self, + established_accounts: &[EstablishedAccountTx], + ) -> (Vec, u8) { + established_accounts + .iter() + .find_map(|account| { + if &account.derive_established_address() == self.address.raw { + Some(( + account + .public_keys + .iter() + .map(|pk| pk.raw.clone()) + .collect::>(), + account.threshold, + )) + } else { + None + } + }) + .unwrap_or_else(|| { + panic!( + "Cannot sign a pre-genesis tx because the established \ + address {} could not be found", + self.address + ) + }) + } + + fn get_owner(&self) -> GenesisAddress { + GenesisAddress::EstablishedAddress(self.address.raw.clone()) } } @@ -598,19 +633,59 @@ impl Signed { } /// Sign the underlying data and add to the list of signatures. - pub async fn sign(&mut self, keys: &[common::SecretKey]) - where + pub async fn sign( + &mut self, + established_accounts: &[EstablishedAccountTx], + wallet: &mut Wallet, + args: args::Tx, + ) where T: BorshSerialize + TxToSign, { - for sk in keys { + let wallet_lock = RwLock::new(wallet); + let (pks, threshold) = self.data.get_pks(established_accounts); + let owner = self.data.get_owner().address(); + let signing_data = SigningTxData { + owner: Some(owner), + account_public_keys_map: Some(pks.iter().cloned().collect()), + public_keys: pks.clone(), + threshold, + fee_payer: common::SecretKey::Ed25519( + ed25519::SigScheme::from_bytes([0; 32]), + ) + .ref_to(), + }; + + let mut tx = self.data.tx_to_sign(); + sign_tx( + wallet, + &args, + &mut tx, + signing_data, + utils::with_hardware_wallet(&wallet_lock), + ) + .await + .expect("Failed to sign pre-genesis transaction."); + + let raw_header_hash = tx.raw_header_hash(); + let sigs = tx + .sections + .iter() + .find_map(|sec| { + if let Section::Signature(signatures) = sec { + if &[raw_header_hash] == signatures.targets.as_slice() { + Some(signatures) + } else { + None + } + } else { + None + } + }) + .unwrap(); + for (ix, sig) in sigs.signatures.into_iter() { self.signatures.insert( - StringEncoded::new(sk.ref_to()), - StringEncoded::new( - standalone_signature::<_, SerializeWithBorsh>( - sk, - &self.data.tx_to_sign(), - ), - ), + StringEncoded::new(pks[ix as usize].clone()), + StringEncoded::new(sig), ); } } @@ -674,7 +749,7 @@ impl TxToSign for BondTx where T: TemplateValidation + BorshSerialize, { - fn tx_to_sign(&self) -> Vec { + fn tx_to_sign(&self) -> Tx { get_tx_to_sign( TX_BOND_WASM, pos::Bond { @@ -683,7 +758,42 @@ where source: Some(self.source.address()), }, ) - .to_bytes() + } + + fn get_pks( + &self, + established_accounts: &[EstablishedAccountTx], + ) -> (Vec, u8) { + match &self.source { + GenesisAddress::PublicKey(pk) => (vec![pk.raw.clone()], 1), + GenesisAddress::EstablishedAddress(owner) => established_accounts + .iter() + .find_map(|account| { + if &account.derive_established_address() == owner { + Some(( + account + .public_keys + .iter() + .map(|pk| pk.raw.clone()) + .collect::>(), + account.threshold, + )) + } else { + None + } + }) + .unwrap_or_else(|| { + panic!( + "Cannot sign a pre-genesis tx because the established \ + address {} could not be found", + owner + ) + }), + } + } + + fn get_owner(&self) -> GenesisAddress { + self.source.clone() } } @@ -1269,58 +1379,3 @@ impl From<&ValidatorAccountTx> for UnsignedValidatorAccountTx { } } } - -/// Attempt look-up a subset of secret keys from the wallet matching -/// the queried established account's public keys. -fn look_up_sks_from( - source: &GenesisAddress, - wallet: &mut Wallet, - established_accounts: Option<&[EstablishedAccountTx]>, -) -> Vec { - // Try to look-up the source from wallet first - match source { - GenesisAddress::EstablishedAddress(_) => None, - GenesisAddress::PublicKey(pk) => { - wallet.find_key_by_pk(pk, None).map(|sk| vec![sk]).ok() - } - } - .unwrap_or_else(|| { - // If it's not in the wallet, it must be an established account - // so we need to look-up its public key first - if established_accounts.is_none() { - return vec![]; - } - established_accounts - .unwrap() - .iter() - .find_map(|account| match source { - GenesisAddress::EstablishedAddress(address) => { - // delegation from established account - if &account.derive_established_address() == address { - Some( - account - .public_keys - .iter() - .map(|pk| &pk.raw) - .collect::>(), - ) - } else { - None - } - } - GenesisAddress::PublicKey(pk) => { - // delegation from an implicit account - Some(vec![&pk.raw]) - } - }) - .unwrap_or_else(|| { - panic!( - "Signing failed. Cannot find \"{source}\" in the wallet \ - or in the established accounts." - ); - }) - .iter() - .filter_map(|pk| wallet.find_key_by_pk(pk, None).ok()) - .collect() - }) -} diff --git a/apps/src/lib/config/genesis/utils.rs b/apps/src/lib/config/genesis/utils.rs new file mode 100644 index 0000000000..dcc7e71cca --- /dev/null +++ b/apps/src/lib/config/genesis/utils.rs @@ -0,0 +1,76 @@ +use std::collections::HashSet; +use std::future::Future; +use std::path::Path; + +use eyre::Context; +use ledger_namada_rs::NamadaApp; +use ledger_transport_hid::hidapi::HidApi; +use ledger_transport_hid::TransportNativeHID; +use namada::proto::Tx; +use namada::types::key::common; +use namada_sdk::wallet::Wallet; +use namada_sdk::{error, signing}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use tokio::sync::RwLock; + +use crate::wallet::CliWalletUtils; + +pub fn read_toml( + path: &Path, + which_file: &str, +) -> eyre::Result { + let file_contents = std::fs::read_to_string(path).wrap_err_with(|| { + format!( + "Couldn't read {which_file} config file from {}", + path.to_string_lossy() + ) + })?; + toml::from_str(&file_contents).wrap_err_with(|| { + format!( + "Couldn't parse {which_file} TOML from {}", + path.to_string_lossy() + ) + }) +} + +pub fn write_toml( + data: &T, + path: &Path, + which_file: &str, +) -> eyre::Result<()> { + let file_contents = toml::to_vec(data) + .wrap_err_with(|| format!("Couldn't format {which_file} to TOML."))?; + std::fs::write(path, file_contents).wrap_err_with(|| { + format!( + "Couldn't write {which_file} TOML to {}", + path.to_string_lossy() + ) + }) +} + +pub(super) fn with_hardware_wallet( + wallet: &RwLock<&mut Wallet>, +) -> F +where + F: Fn(Tx, common::PublicKey, HashSet) -> T, + T: Future> + Sized, +{ + // Setup a reusable context for signing transactions using the Ledger + let hidapi = + HidApi::new().map_err(|err| panic!("Failed to create Hidapi: {}", err)); + let app = NamadaApp::new( + TransportNativeHID::new(&hidapi) + .map_err(|err| panic!("Unable to connect to Ledger: {}", err))?, + ); + |tx: Tx, pubkey: common::PublicKey, parts: HashSet| async move { + if parts.contains(&signing::Signable::FeeHeader) { + return Ok(tx); + } + let app = app; + let with_hw = crate::client::tx::with_hardware_wallet::( + wallet, &app, + ); + with_hw(tx, pubkey, parts).await + } +} diff --git a/sdk/src/io.rs b/sdk/src/io.rs index 248f6f91d9..76f97d50b7 100644 --- a/sdk/src/io.rs +++ b/sdk/src/io.rs @@ -79,6 +79,7 @@ pub trait Io { } /// Rust native I/O handling. +#[derive(Default)] pub struct StdIo; #[async_trait::async_trait(?Send)] diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 04a2c63943..df3b881396 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -92,6 +92,9 @@ pub trait Namada<'a>: Sized { &self, ) -> RwLockWriteGuard<&'a mut Wallet>; + /// Obtain the wallet lock + fn wallet_lock(&self) -> &RwLock<&'a mut Wallet>; + /// Obtain read guard on the shielded context async fn shielded( &self, @@ -532,7 +535,7 @@ pub trait Namada<'a>: Sized { signing_data: SigningTxData, with: impl Fn(Tx, common::PublicKey, HashSet) -> F, ) -> crate::error::Result<()> { - signing::sign_tx(self, args, tx, signing_data, with).await + signing::sign_tx(self.wallet_lock(), args, tx, signing_data, with).await } /// Process the given transaction using the given flags @@ -707,6 +710,10 @@ where ) -> RwLockWriteGuard<&'a mut ShieldedContext> { self.shielded.write().await } + + fn wallet_lock(&self) -> &RwLock<&'a mut Wallet> { + &self.wallet + } } /// Allow the prototypical Tx builder to be modified diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index dd75e942f0..6240e0fd81 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -33,6 +33,7 @@ use prost::Message; use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; use sha2::Digest; +use tokio::sync::RwLock; use super::masp::{ShieldedContext, ShieldedTransfer}; use crate::args::SdkTypes; @@ -225,8 +226,12 @@ pub async fn default_sign( /// hashes needed for monitoring the tx on chain. /// /// If it is a dry run, it is not put in a wrapper, but returned as is. -pub async fn sign_tx<'a, F: std::future::Future>>( - context: &impl Namada<'a>, +pub async fn sign_tx< + 'a, + U: WalletIo, + F: std::future::Future>, +>( + wallet: &RwLock<&'a mut Wallet>, args: &args::Tx, tx: &mut Tx, signing_data: SigningTxData, @@ -251,7 +256,7 @@ pub async fn sign_tx<'a, F: std::future::Future>>( // Then try to sign the raw header with private keys in the software wallet if let Some(account_public_keys_map) = signing_data.account_public_keys_map { - let mut wallet = context.wallet_mut().await; + let mut wallet = wallet.write().await; let signing_tx_keypairs = signing_data .public_keys .iter() @@ -299,7 +304,7 @@ pub async fn sign_tx<'a, F: std::future::Future>>( let key = { // Lock the wallet just long enough to extract a key from it without // interfering with the sign closure call - let mut wallet = context.wallet_mut().await; + let mut wallet = wallet.write().await; find_key_by_pk(*wallet, args, &signing_data.fee_payer) }; match key { diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 6d4379720a..7852218ba8 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -22,7 +22,7 @@ use namada::types::chain::ChainId; use namada_apps::client::utils::{ self, validator_pre_genesis_dir, validator_pre_genesis_txs_file, }; -use namada_apps::config::genesis::toml_utils::read_toml; +use namada_apps::config::genesis::utils::read_toml; use namada_apps::config::genesis::{templates, GenesisAddress}; use namada_apps::config::{ethereum_bridge, genesis, Config}; use namada_apps::{config, wallet}; From 7fe53eb1ee4fdf66906cf04c56e05a147afa6910 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 28 Nov 2023 09:50:27 +0000 Subject: [PATCH 31/81] lifetime heaven --- apps/src/lib/client/tx.rs | 191 +++++++-------- apps/src/lib/client/utils.rs | 34 +-- apps/src/lib/config/genesis.rs | 1 - apps/src/lib/config/genesis/chain.rs | 8 +- apps/src/lib/config/genesis/transactions.rs | 239 +++++++++++++------ apps/src/lib/config/genesis/utils.rs | 44 ++-- apps/src/lib/node/ledger/shell/init_chain.rs | 5 +- core/src/proto/types.rs | 8 +- core/src/types/account.rs | 6 + core/src/types/key/mod.rs | 2 - sdk/src/lib.rs | 21 +- sdk/src/signing.rs | 19 +- tests/src/e2e/ibc_tests.rs | 5 +- 13 files changed, 328 insertions(+), 255 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 3e1c7cc10c..fef1d52035 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1,8 +1,6 @@ use std::collections::HashSet; use std::fs::File; -use std::future::Future; use std::io::Write; -use std::pin::Pin; use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; @@ -72,100 +70,87 @@ pub async fn aux_signing_data<'a>( Ok(signing_data) } -pub fn with_hardware_wallet<'a, 'b, U: WalletIo + Clone>( - wallet: &RwLock<&'b mut Wallet>, - app: &NamadaApp, -) -> impl Fn( - Tx, - common::PublicKey, - HashSet, -) -> Pin>>> { - let wallet = wallet.read(); - move |mut tx: Tx, - pubkey: common::PublicKey, - parts: HashSet| { - Box::pin(async move { - // Obtain derivation path - let path = wallet - .await - .find_path_by_pkh(&(&pubkey).into()) - .map_err(|_| { - error::Error::Other( - "Unable to find derivation path for key".to_string(), - ) - })?; - let path = BIP44Path { - path: path.to_string(), - }; - // Now check that the public key at this path in the Ledger - // matches - let response_pubkey = app - .get_address_and_pubkey(&path, false) - .await - .map_err(|err| error::Error::Other(err.to_string()))?; - let response_pubkey = - common::PublicKey::try_from_slice(&response_pubkey.public_key) - .map_err(|err| { - error::Error::Other(format!( - "unable to decode public key from hardware \ - wallet: {}", - err - )) - })?; - if response_pubkey != pubkey { - return Err(error::Error::Other(format!( - "Unrecognized public key fetched fom Ledger: {}. Expected \ - {}.", - response_pubkey, pubkey, - ))); - } - // Get the Ledger to sign using our obtained derivation path - let response = app - .sign(&path, &tx.serialize_to_vec()) - .await - .map_err(|err| error::Error::Other(err.to_string()))?; - // Sign the raw header if that is requested - if parts.contains(&signing::Signable::RawHeader) { - let pubkey = - common::PublicKey::try_from_slice(&response.pubkey) - .expect("unable to parse public key from Ledger"); - let signature = - common::Signature::try_from_slice(&response.raw_signature) - .expect("unable to parse signature from Ledger"); - // Signatures from the Ledger come back in compressed - // form - let compressed = CompressedSignature { - targets: response.raw_indices, - signer: Signer::PubKeys(vec![pubkey]), - signatures: [(0, signature)].into(), - }; - // Expand out the signature before adding it to the - // transaction - tx.add_section(Section::Signature(compressed.expand(&tx))); - } - // Sign the fee header if that is requested - if parts.contains(&signing::Signable::FeeHeader) { - let pubkey = - common::PublicKey::try_from_slice(&response.pubkey) - .expect("unable to parse public key from Ledger"); - let signature = common::Signature::try_from_slice( - &response.wrapper_signature, - ) +pub async fn with_hardware_wallet<'a, U: WalletIo + Clone>( + mut tx: Tx, + pubkey: common::PublicKey, + parts: HashSet, + (wallet, app): (&RwLock<&'a mut Wallet>, &NamadaApp), +) -> Result { + // Obtain derivation path + let path = wallet + .read() + .await + .find_path_by_pkh(&(&pubkey).into()) + .map_err(|_| { + error::Error::Other( + "Unable to find derivation path for key".to_string(), + ) + })?; + let path = BIP44Path { + path: path.to_string(), + }; + // Now check that the public key at this path in the Ledger + // matches + let response_pubkey = app + .get_address_and_pubkey(&path, false) + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + let response_pubkey = + common::PublicKey::try_from_slice(&response_pubkey.public_key) + .map_err(|err| { + error::Error::Other(format!( + "unable to decode public key from hardware wallet: {}", + err + )) + })?; + if response_pubkey != pubkey { + return Err(error::Error::Other(format!( + "Unrecognized public key fetched fom Ledger: {}. Expected {}.", + response_pubkey, pubkey, + ))); + } + // Get the Ledger to sign using our obtained derivation path + let response = app + .sign(&path, &tx.serialize_to_vec()) + .await + .map_err(|err| error::Error::Other(err.to_string()))?; + // Sign the raw header if that is requested + if parts.contains(&signing::Signable::RawHeader) { + let pubkey = common::PublicKey::try_from_slice(&response.pubkey) + .expect("unable to parse public key from Ledger"); + let signature = + common::Signature::try_from_slice(&response.raw_signature) .expect("unable to parse signature from Ledger"); - // Signatures from the Ledger come back in compressed - // form - let compressed = CompressedSignature { - targets: response.wrapper_indices, - signer: Signer::PubKeys(vec![pubkey]), - signatures: [(0, signature)].into(), - }; - // Expand out the signature before adding it to the - // transaction - tx.add_section(Section::Signature(compressed.expand(&tx))); - } - Ok(tx) - }) + // Signatures from the Ledger come back in compressed + // form + let compressed = CompressedSignature { + targets: response.raw_indices, + signer: Signer::PubKeys(vec![pubkey]), + signatures: [(0, signature)].into(), + }; + // Expand out the signature before adding it to the + // transaction + tx.add_section(Section::Signature(compressed.expand(&tx))); + } + // Sign the fee header if that is requested + if parts.contains(&signing::Signable::FeeHeader) { + let pubkey = common::PublicKey::try_from_slice(&response.pubkey) + .expect("unable to parse public key from Ledger"); + let signature = + common::Signature::try_from_slice(&response.wrapper_signature) + .expect("unable to parse signature from Ledger"); + // Signatures from the Ledger come back in compressed + // form + let compressed = CompressedSignature { + targets: response.wrapper_indices, + signer: Signer::PubKeys(vec![pubkey]), + signatures: [(0, signature)].into(), + }; + // Expand out the signature before adding it to the + // transaction + tx.add_section(Section::Signature(compressed.expand(&tx))); } + Ok(tx) } // Sign the given transaction using a hardware wallet as a backup @@ -189,14 +174,22 @@ pub async fn sign<'a, N: Namada<'a>>( )) }, )?); - // A closure to facilitate signing transactions also using the Ledger - let with_hw = - with_hardware_wallet::(context.wallet_lock(), &app); + let with_hw_data = (context.wallet_lock(), &app); // Finally, begin the signing with the Ledger as backup - context.sign(tx, args, signing_data, with_hw).await?; + context + .sign( + tx, + args, + signing_data, + with_hardware_wallet::, + with_hw_data, + ) + .await?; } else { // Otherwise sign without a backup procedure - context.sign(tx, args, signing_data, default_sign).await?; + context + .sign(tx, args, signing_data, default_sign, ()) + .await?; } Ok(()) } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index dea469bb86..afb6dfef20 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -11,7 +11,6 @@ use flate2::write::GzEncoder; use flate2::Compression; use itertools::Either; use namada::core::types::string_encoding::StringEncoded; -use namada::types::address::nam; use namada::types::chain::ChainId; use namada::types::dec::Dec; use namada::types::key::*; @@ -993,6 +992,7 @@ pub async fn sign_genesis_tx( unsigned, &mut wallet, maybe_pre_genesis_wallet.as_ref(), + use_device, ) .await, true, @@ -1018,7 +1018,7 @@ pub async fn sign_genesis_tx( tx, &mut wallet, &genesis_txs.established_account, - get_tx_args(use_device), + use_device, ) .await, ); @@ -1040,7 +1040,7 @@ pub async fn sign_genesis_tx( "Established account txs required \ when signing validator account txs", ), - get_tx_args(use_device), + use_device, ) .await, ); @@ -1083,34 +1083,6 @@ pub async fn sign_genesis_tx( } } -fn get_tx_args(use_device: bool) -> args::Tx { - args::Tx { - dry_run: false, - dry_run_wrapper: false, - dump_tx: false, - output_folder: None, - force: false, - broadcast_only: false, - ledger_address: (), - initialized_account_alias: None, - wallet_alias_force: false, - fee_amount: None, - wrapper_fee_payer: None, - fee_token: nam(), - fee_unshield: None, - gas_limit: Default::default(), - expiration: None, - disposable_signing_key: false, - chain_id: None, - signing_keys: vec![], - signatures: vec![], - tx_reveal_code_path: Default::default(), - verification_key: None, - password: None, - use_device, - } -} - /// Add a spinning wheel to a message for long running commands. /// Can be turned off for E2E tests by setting the `REDUCED_CLI_PRINTING` /// environment variable. diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index ca340cadfd..c24497d549 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -489,7 +489,6 @@ pub fn make_dev_genesis( }, }; vals.push(chain::FinalizedValidatorAccountTx { - address: Address::Established(address.clone()), tx: transactions::Signed::new( transactions::ValidatorAccountTx { address: StringEncoded::new(address.clone()), diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index 88e6893acc..d8e57f11f6 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -146,7 +146,7 @@ impl Finalized { let address = self .transactions .find_validator(&tendermint_pk) - .map(|tx| tx.address.clone()) + .map(|tx| Address::Established(tx.tx.data.address.raw.clone())) .expect("Validator alias not found in genesis transactions."); wallet.extend_from_pre_genesis_validator( address, @@ -650,10 +650,7 @@ impl FinalizedTransactions { }); let validator_account = validator_account.map(|txs| { txs.into_iter() - .map(|tx| FinalizedValidatorAccountTx { - address: Address::Established(tx.data.address.raw.clone()), - tx, - }) + .map(|tx| FinalizedValidatorAccountTx { tx }) .collect() }); FinalizedTransactions { @@ -745,7 +742,6 @@ pub struct FinalizedEstablishedAccountTx { Eq, )] pub struct FinalizedValidatorAccountTx { - pub address: Address, #[serde(flatten)] pub tx: transactions::SignedValidatorAccountTx, } diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index cc3ff7c592..25f8ac3455 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -1,22 +1,33 @@ //! Genesis transactions -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::fmt::Debug; use std::net::SocketAddr; use borsh::{BorshDeserialize, BorshSerialize}; +use borsh_ext::BorshSerializeExt; use itertools::{Either, Itertools}; +use ledger_namada_rs::NamadaApp; +use ledger_transport_hid::hidapi::HidApi; +use ledger_transport_hid::TransportNativeHID; +use namada::core::types::account::AccountPublicKeysMap; use namada::core::types::address::{Address, EstablishedAddress}; +use namada::core::types::chain::ChainId; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::common::PublicKey; use namada::ledger::pos::types::ValidatorMetaData; -use namada::proto::{verify_standalone_sig, Section, SerializeWithBorsh, Tx}; +use namada::proto::{ + verify_standalone_sig, Code, Commitment, Data, Section, SerializeWithBorsh, + SignatureIndex, Tx, +}; +use namada::types::address::nam; use namada::types::dec::Dec; -use namada::types::key::{common, ed25519, RefTo, SigScheme, VerifySigError}; +use namada::types::key::{common, ed25519, RefTo, SigScheme}; use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada::types::transaction::{pos, Fee, TxType}; +use namada_sdk::args::Tx as TxArgs; use namada_sdk::signing::{sign_tx, SigningTxData}; use namada_sdk::tx::{TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM}; use namada_sdk::wallet::alias::Alias; @@ -26,7 +37,6 @@ use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; use super::templates::{DenominatedBalances, Parameters, ValidityPredicates}; -use crate::cli::args; use crate::config::genesis::chain::DeriveEstablishedAddress; use crate::config::genesis::templates::{ TemplateValidation, Unvalidated, Validated, @@ -34,6 +44,12 @@ use crate::config::genesis::templates::{ use crate::config::genesis::{utils, GenesisAddress}; use crate::wallet::CliWalletUtils; +/// Dummy chain id used to sign [`Tx`] objects at pre-genesis. +const NAMADA_GENESIS_TX_CHAIN_ID: &str = "namada-genesis"; + +/// Timestamp used to sign pre-genesis [`Tx`] objects. +pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; + /// Helper trait to fetch tx data to sign. pub trait TxToSign { /// Return tx data to sign. @@ -48,14 +64,51 @@ pub trait TxToSign { fn get_owner(&self) -> GenesisAddress; } +/// Return a dummy set of tx arguments to sign with the +/// hardware wallet. +fn get_tx_args(use_device: bool) -> TxArgs { + TxArgs { + dry_run: false, + dry_run_wrapper: false, + dump_tx: false, + output_folder: None, + force: false, + broadcast_only: false, + ledger_address: (), + initialized_account_alias: None, + wallet_alias_force: false, + fee_amount: None, + wrapper_fee_payer: None, + fee_token: nam(), + fee_unshield: None, + gas_limit: Default::default(), + expiration: None, + disposable_signing_key: false, + chain_id: None, + signing_keys: vec![], + signatures: vec![], + tx_reveal_code_path: Default::default(), + verification_key: None, + password: None, + use_device, + } +} + /// Return a ready to sign genesis [`Tx`]. fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { let mut tx = Tx::from_type(TxType::Raw); - tx.add_code_from_hash(Default::default(), Some(tag.as_ref().to_string())); - tx.add_data(data); - let pk = - common::SecretKey::Ed25519(ed25519::SigScheme::from_bytes([0; 32])) - .ref_to(); + tx.header.chain_id = ChainId(NAMADA_GENESIS_TX_CHAIN_ID.to_string()); + tx.header.timestamp = PRE_GENESIS_TX_TIMESTAMP; + tx.set_code(Code { + salt: [0; 8], + code: Commitment::Hash(Default::default()), + tag: Some(tag.as_ref().to_string()), + }); + tx.set_data(Data { + salt: [0; 8], + data: data.serialize_to_vec(), + }); + let pk = get_sentinnel_pubkey(); tx.add_wrapper( Fee { amount_per_gas_unit: Default::default(), @@ -69,7 +122,11 @@ fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { tx } -pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; +/// Get a dummy public key. +#[inline] +fn get_sentinnel_pubkey() -> common::PublicKey { + common::SecretKey::Ed25519(ed25519::SigScheme::from_bytes([0; 32])).ref_to() +} pub struct GenesisValidatorData { pub address: EstablishedAddress, @@ -90,6 +147,7 @@ pub async fn sign_txs( txs: UnsignedTransactions, wallet: &mut Wallet, validator_wallet: Option<&ValidatorWallet>, + use_device: bool, ) -> Transactions { let UnsignedTransactions { established_account, @@ -106,6 +164,7 @@ pub async fn sign_txs( tx.into(), wallet, &established_account, + use_device, ) .await, ); @@ -131,6 +190,7 @@ pub async fn sign_txs( "Established account txs required when signing \ validator account txs", ), + use_device, ) .await, ); @@ -255,7 +315,7 @@ pub async fn sign_validator_account_tx( >, wallet: &mut Wallet, established_accounts: &[EstablishedAccountTx], - args: args::Tx, + use_device: bool, ) -> SignedValidatorAccountTx { let mut to_sign = match to_sign { Either::Right(signed_tx) => signed_tx, @@ -336,7 +396,7 @@ pub async fn sign_validator_account_tx( } }; - to_sign.sign(established_accounts, wallet, args).await; + to_sign.sign(established_accounts, wallet, use_device).await; to_sign } @@ -344,12 +404,12 @@ pub async fn sign_delegation_bond_tx( mut to_sign: SignedBondTx, wallet: &mut Wallet, established_accounts: &Option>, - args: args::Tx, + use_device: bool, ) -> SignedBondTx { let default = vec![]; let established_accounts = established_accounts.as_ref().unwrap_or(&default); - to_sign.sign(established_accounts, wallet, &mut args).await; + to_sign.sign(established_accounts, wallet, use_device).await; to_sign } @@ -541,7 +601,7 @@ impl TxToSign for ValidatorAccountTx { established_accounts .iter() .find_map(|account| { - if &account.derive_established_address() == self.address.raw { + if account.derive_established_address() == self.address.raw { Some(( account .public_keys @@ -637,11 +697,10 @@ impl Signed { &mut self, established_accounts: &[EstablishedAccountTx], wallet: &mut Wallet, - args: args::Tx, + use_device: bool, ) where T: BorshSerialize + TxToSign, { - let wallet_lock = RwLock::new(wallet); let (pks, threshold) = self.data.get_pks(established_accounts); let owner = self.data.get_owner().address(); let signing_data = SigningTxData { @@ -649,30 +708,63 @@ impl Signed { account_public_keys_map: Some(pks.iter().cloned().collect()), public_keys: pks.clone(), threshold, - fee_payer: common::SecretKey::Ed25519( - ed25519::SigScheme::from_bytes([0; 32]), - ) - .ref_to(), + fee_payer: get_sentinnel_pubkey(), }; let mut tx = self.data.tx_to_sign(); - sign_tx( - wallet, - &args, - &mut tx, - signing_data, - utils::with_hardware_wallet(&wallet_lock), - ) - .await - .expect("Failed to sign pre-genesis transaction."); + let wallet_lock = RwLock::new(wallet); + + if use_device { + let hidapi = HidApi::new().expect("Failed to create Hidapi"); + let transport = TransportNativeHID::new(&hidapi) + .expect("Failed to create hardware wallet connection"); + let app = NamadaApp::new(transport); + + sign_tx( + &wallet_lock, + &get_tx_args(use_device), + &mut tx, + signing_data, + utils::with_hardware_wallet, + (&wallet_lock, &app), + ) + .await + .expect("Failed to sign pre-genesis transaction."); + } else { + async fn software_wallet_sign( + tx: Tx, + pubkey: common::PublicKey, + _parts: HashSet, + _user: (), + ) -> Result { + if pubkey == get_sentinnel_pubkey() { + Ok(tx) + } else { + Err(namada_sdk::error::Error::Other(format!( + "unable to sign transaction with {pubkey}", + ))) + } + } + + sign_tx( + &wallet_lock, + &get_tx_args(use_device), + &mut tx, + signing_data, + software_wallet_sign, + (), + ) + .await + .expect("Failed to sign pre-genesis transaction."); + } let raw_header_hash = tx.raw_header_hash(); let sigs = tx .sections - .iter() + .into_iter() .find_map(|sec| { if let Section::Signature(signatures) = sec { - if &[raw_header_hash] == signatures.targets.as_slice() { + if [raw_header_hash] == signatures.targets.as_slice() { Some(signatures) } else { None @@ -691,39 +783,40 @@ impl Signed { } /// Verify the signatures of the inner data. - pub fn verify_sig( - &self, - pks: &[common::PublicKey], - threshold: u8, - ) -> Result<(), VerifySigError> + pub fn verify_sig(&self, threshold: u8) -> Result<(), String> where T: BorshSerialize + TxToSign, { let Self { data, signatures } = self; - if pks.len() > u8::MAX as usize { - eprintln!("You're multisig is too facking big"); - return Err(VerifySigError::TooGoddamnBig); - } - let mut valid_sigs = 0; - let tx_to_sign = data.tx_to_sign(); - for pk in pks { - if let Some(sig) = signatures.get(&StringEncoded::new(pk.clone())) { - valid_sigs += verify_standalone_sig::<_, SerializeWithBorsh>( - &tx_to_sign, - pk, - &sig.raw, + let (public_keys, signatures): (Vec<_>, Vec<_>) = signatures + .iter() + .map(|(pk, sig)| { + ( + pk.raw.clone(), + SignatureIndex { + index: None, + signature: sig.raw.clone(), + pubkey: pk.raw.clone(), + }, ) - .is_ok() as u8; - if valid_sigs >= threshold { - break; - } - } - } - if valid_sigs >= threshold { - Ok(()) - } else { - Err(VerifySigError::ThresholdNotMet(threshold, valid_sigs)) - } + }) + .unzip(); + let signed_tx = { + let mut tx = data.tx_to_sign(); + tx.add_signatures(signatures); + tx + }; + signed_tx + .verify_signatures( + &[signed_tx.raw_header_hash()], + AccountPublicKeysMap::from_iter(public_keys.into_iter()), + &None, + threshold, + None, + || Ok(()), + ) + .map_err(|err| err.to_string())?; + Ok(()) } } @@ -995,22 +1088,18 @@ fn validate_bond( // Check signature let mut is_valid = { let source = &tx.data.source; - let maybe_source = match source { + let maybe_threshold = match source { GenesisAddress::EstablishedAddress(address) => { // Try to find the source's PK in either established_accounts or // validator_accounts let established_addr = Address::Established(address.clone()); - established_accounts - .get(&established_addr) - .map(|(pks, t)| (pks.as_slice(), *t)) - } - GenesisAddress::PublicKey(pk) => { - Some((std::slice::from_ref(&pk.raw), 1)) + established_accounts.get(&established_addr).map(|(_, t)| *t) } + GenesisAddress::PublicKey(_) => Some(1), }; - if let Some((source_pks, threshold)) = maybe_source { - if tx.verify_sig(source_pks, threshold).is_err() { - eprintln!("Invalid bond tx signature.",); + if let Some(threshold) = maybe_threshold { + if let Err(err) = tx.verify_sig(threshold) { + eprintln!("Invalid bond tx signature: {err}"); false } else { true @@ -1194,7 +1283,7 @@ pub fn validate_validator_account( // Check signature let mut is_valid = { - let maybe_source = { + let maybe_threshold = { let established_addr = Address::Established(tx.address.raw.clone()); established_accounts.get(&established_addr).map(|(pks, t)| { let all_ed25519_keys = pks @@ -1206,12 +1295,12 @@ pub fn validate_validator_account( {established_addr} are Ed25519 keys" ); } - (pks.as_slice(), *t) + *t }) }; - if let Some((source_pks, threshold)) = maybe_source { - if signed_tx.verify_sig(source_pks, threshold).is_err() { - eprintln!("Invalid validator account signature."); + if let Some(threshold) = maybe_threshold { + if let Err(err) = signed_tx.verify_sig(threshold) { + eprintln!("Invalid validator account signature: {err}"); false } else { true diff --git a/apps/src/lib/config/genesis/utils.rs b/apps/src/lib/config/genesis/utils.rs index dcc7e71cca..7918f8bc5b 100644 --- a/apps/src/lib/config/genesis/utils.rs +++ b/apps/src/lib/config/genesis/utils.rs @@ -1,10 +1,8 @@ use std::collections::HashSet; -use std::future::Future; use std::path::Path; use eyre::Context; use ledger_namada_rs::NamadaApp; -use ledger_transport_hid::hidapi::HidApi; use ledger_transport_hid::TransportNativeHID; use namada::proto::Tx; use namada::types::key::common; @@ -49,28 +47,24 @@ pub fn write_toml( }) } -pub(super) fn with_hardware_wallet( - wallet: &RwLock<&mut Wallet>, -) -> F -where - F: Fn(Tx, common::PublicKey, HashSet) -> T, - T: Future> + Sized, -{ - // Setup a reusable context for signing transactions using the Ledger - let hidapi = - HidApi::new().map_err(|err| panic!("Failed to create Hidapi: {}", err)); - let app = NamadaApp::new( - TransportNativeHID::new(&hidapi) - .map_err(|err| panic!("Unable to connect to Ledger: {}", err))?, - ); - |tx: Tx, pubkey: common::PublicKey, parts: HashSet| async move { - if parts.contains(&signing::Signable::FeeHeader) { - return Ok(tx); - } - let app = app; - let with_hw = crate::client::tx::with_hardware_wallet::( - wallet, &app, - ); - with_hw(tx, pubkey, parts).await +pub(super) async fn with_hardware_wallet<'a>( + tx: Tx, + pubkey: common::PublicKey, + parts: HashSet, + (wallet, app): ( + &RwLock<&'a mut Wallet>, + &NamadaApp, + ), +) -> Result { + if parts.contains(&signing::Signable::FeeHeader) { + Ok(tx) + } else { + crate::client::tx::with_hardware_wallet( + tx, + pubkey, + parts, + (wallet, app), + ) + .await } } diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 88af71c96a..c07f627f8a 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -9,6 +9,7 @@ use namada::ledger::storage_api::token::{credit_tokens, write_denom}; use namada::ledger::storage_api::StorageWrite; use namada::ledger::{ibc, pos}; use namada::proof_of_stake::BecomeValidator; +use namada::types::address::Address; use namada::types::hash::Hash as CodeHash; use namada::types::key::*; use namada::types::time::{DateTimeUtc, TimeZone, Utc}; @@ -389,11 +390,11 @@ where ) { if let Some(txs) = genesis.transactions.validator_account.as_ref() { for FinalizedValidatorAccountTx { - address, tx: SignedTx { data: ValidatorAccountTx { + address, vp, commission_rate, max_commission_rate_change, @@ -410,6 +411,8 @@ where }, } in txs { + let address = &Address::Established(address.raw.clone()); + tracing::debug!( "Applying genesis tx to init a validator account {address}" ); diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 97c16f1b1d..96652c22cb 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -1312,9 +1312,11 @@ impl Tx { } } } - Err(Error::InvalidSectionSignature( - "signature threshold not met.".to_string(), - )) + Err(Error::InvalidSectionSignature(format!( + "signature threshold not met: ({} < {})", + verified_pks.len(), + threshold + ))) } /// Verify that the sections with the given hashes have been signed together diff --git a/core/src/types/account.rs b/core/src/types/account.rs index 83f0b0aafe..b38c8b6792 100644 --- a/core/src/types/account.rs +++ b/core/src/types/account.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use super::address::Address; use super::key::{common, RefTo}; +use crate::hints; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, @@ -62,6 +63,11 @@ impl FromIterator for AccountPublicKeysMap { let mut idx_to_pk = HashMap::new(); for (index, public_key) in iter.into_iter().enumerate() { + if hints::unlikely(index > u8::MAX as usize) { + panic!( + "Only up to 255 signers are allowed in a multisig account" + ); + } pk_to_idx.insert(public_key.to_owned(), index as u8); idx_to_pk.insert(index as u8, public_key.to_owned()); } diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index fb9964cccc..9d22ee3af1 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -129,8 +129,6 @@ pub enum VerifySigError { required {0} got {1}" )] ThresholdNotMet(u8, u8), - #[error("Too many fucking sigs")] - TooGoddamnBig, } #[allow(missing_docs)] diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index df3b881396..1b467947b0 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -528,14 +528,27 @@ pub trait Namada<'a>: Sized { } /// Sign the given transaction using the given signing data - async fn sign>>( + async fn sign( &self, tx: &mut Tx, args: &args::Tx, signing_data: SigningTxData, - with: impl Fn(Tx, common::PublicKey, HashSet) -> F, - ) -> crate::error::Result<()> { - signing::sign_tx(self.wallet_lock(), args, tx, signing_data, with).await + with: impl Fn(Tx, common::PublicKey, HashSet, D) -> F, + user_data: D, + ) -> crate::error::Result<()> + where + D: Clone, + F: std::future::Future>, + { + signing::sign_tx( + self.wallet_lock(), + args, + tx, + signing_data, + with, + user_data, + ) + .await } /// Process the given transaction using the given flags diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index 6240e0fd81..f2e0d52473 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -208,6 +208,7 @@ pub async fn default_sign( _tx: Tx, pubkey: common::PublicKey, _parts: HashSet, + _user: (), ) -> Result { Err(Error::Other(format!( "unable to sign transaction with {}", @@ -226,17 +227,19 @@ pub async fn default_sign( /// hashes needed for monitoring the tx on chain. /// /// If it is a dry run, it is not put in a wrapper, but returned as is. -pub async fn sign_tx< - 'a, - U: WalletIo, - F: std::future::Future>, ->( +pub async fn sign_tx<'a, D, F, U>( wallet: &RwLock<&'a mut Wallet>, args: &args::Tx, tx: &mut Tx, signing_data: SigningTxData, - sign: impl Fn(Tx, common::PublicKey, HashSet) -> F, -) -> Result<(), Error> { + sign: impl Fn(Tx, common::PublicKey, HashSet, D) -> F, + user_data: D, +) -> Result<(), Error> +where + D: Clone, + U: WalletIo, + F: std::future::Future>, +{ let mut used_pubkeys = HashSet::new(); // First try to sign the raw header with the supplied signatures @@ -290,6 +293,7 @@ pub async fn sign_tx< tx.clone(), pubkey.clone(), HashSet::from([Signable::RawHeader]), + user_data.clone(), ) .await { @@ -316,6 +320,7 @@ pub async fn sign_tx< tx.clone(), signing_data.fee_payer.clone(), HashSet::from([Signable::FeeHeader, Signable::RawHeader]), + user_data, ) .await?; } diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index 0b20e3fa7c..7aa6355087 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -283,7 +283,10 @@ fn setup_two_single_node_nets() -> Result<(Test, Test)> { .as_mut() .unwrap() .iter_mut() - .find(|val| val.address == validator_addr) + .find(|val| { + Address::Established(val.tx.data.address.raw.clone()) + == validator_addr + }) .unwrap(); let new_port = validator_tx.tx.data.net_address.port() + setup::ANOTHER_CHAIN_PORT_OFFSET; From 7af5d934dd51fd0516719480ea4f2ad2e1540e77 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 28 Nov 2023 16:20:04 +0000 Subject: [PATCH 32/81] wip: fixing localnet genesis templates --- genesis/localnet/balances.toml | 56 +++--- genesis/localnet/parameters.toml | 4 +- .../established-account-tx-albert.toml | 6 +- .../established-account-tx-bertha.toml | 6 +- .../established-account-tx-christel.toml | 6 +- .../established-account-tx-validator-0.toml | 4 + .../src/pre-genesis/signed-transactions.toml | 26 ++- .../pre-genesis/unsigned-transactions.toml | 13 +- .../validator-0/signed-transactions.toml | 45 +++++ .../pre-genesis/validator-0/transactions.toml | 44 ----- .../validator-0/unsigned-transactions.toml | 24 +++ .../validator-0/validator-wallet.toml | 1 - genesis/localnet/src/pre-genesis/wallet.toml | 11 +- genesis/localnet/transactions.toml | 168 ++++-------------- 14 files changed, 167 insertions(+), 247 deletions(-) create mode 100644 genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml create mode 100644 genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml delete mode 100644 genesis/localnet/src/pre-genesis/validator-0/transactions.toml create mode 100644 genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml diff --git a/genesis/localnet/balances.toml b/genesis/localnet/balances.toml index 78ddf06758..a14c274456 100644 --- a/genesis/localnet/balances.toml +++ b/genesis/localnet/balances.toml @@ -16,102 +16,102 @@ [token.NAM] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "2000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "2000000" tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "2000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "2000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "2000000" tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq = "2000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "2000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "2000000" tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79 = "2000000" # daewon tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "100000000" # ester tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "200000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "200000" tpknam1qpzrttnzfyt6xfu2vy092eruasll3z52rjfexwapdw0rdww5uktlk3j73dw = "200000" [token.BTC] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "1000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "1000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "1000000" # daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" # ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" [token.ETH] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "1000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "1000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "1000000" # daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" # ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" [token.DOT] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "1000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "1000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "1000000" # daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" # ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" [token.Schnitzel] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "1000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "1000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "1000000" # daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" # ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" [token.Apfel] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "1000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "1000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "1000000" # daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" # ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" [token.Kartoffel] # albert -tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt = "1000000" +tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu = "1000000" # bertha -tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7 = "1000000" +tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn = "1000000" # christel -tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns = "1000000" +tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf = "1000000" # daewon-key tpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm = "1000000" # ester-key tpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4 = "1000000" # validator-0 -tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w = "1000000" +tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx = "1000000" diff --git a/genesis/localnet/parameters.toml b/genesis/localnet/parameters.toml index 00a721290f..266fe803fa 100644 --- a/genesis/localnet/parameters.toml +++ b/genesis/localnet/parameters.toml @@ -93,9 +93,9 @@ min_proposal_grace_epochs = 6 # Initial set of stewards stewards = [ # bertha - "tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7", + "tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn", # validator-0 - "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" + "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" ] # The pgf funding inflation rate pgf_inflation_rate = "0.1" diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml index 8bdc7b04b1..532198f7e2 100644 --- a/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml @@ -1,6 +1,4 @@ [[established_account]] vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" -authorization = "signam1qz39jpapmmxyc99ra8ty74kdx8c0kax3n42z7n5y44e3tcara4ezm3l4llgwgqhp0kgg0kz48d02v2xfeqn5at4h069dddvmlhral7qgm59d37" +threshold = 1 +public_keys = ["tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha"] diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml index b6b1611bb5..177974ff4a 100644 --- a/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-bertha.toml @@ -1,6 +1,4 @@ [[established_account]] vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" -authorization = "signam1qqqvnh3e3j8rv6au622yw74jj76wpty7pttlnfussnyffhg8sk07q7g5ehw5hyz67amnvr4afmcr6tcfs6ktylcqrtc0h350j4zd0qgpqwn9z7" +threshold = 1 +public_keys = ["tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq"] diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml index 2ecbd53966..cdb56e0666 100644 --- a/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-christel.toml @@ -1,6 +1,4 @@ [[established_account]] vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" -authorization = "signam1qqfsz9dvtsan6kqp8zn256gvnuw5hsr27935w63cu0hl30535ewx7tpsf3zypq0hxmjh23z4q4sk7l046n6gjqmvg9ueyxl69lxgzvcfvujj9x" +threshold = 1 +public_keys = ["tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79"] diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml new file mode 100644 index 0000000000..562ffd2ba0 --- /dev/null +++ b/genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml @@ -0,0 +1,4 @@ +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj"] diff --git a/genesis/localnet/src/pre-genesis/signed-transactions.toml b/genesis/localnet/src/pre-genesis/signed-transactions.toml index 9d6baed550..1fd02da9ff 100644 --- a/genesis/localnet/src/pre-genesis/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/signed-transactions.toml @@ -1,26 +1,22 @@ [[established_account]] vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" -authorization = "signam1qz39jpapmmxyc99ra8ty74kdx8c0kax3n42z7n5y44e3tcara4ezm3l4llgwgqhp0kgg0kz48d02v2xfeqn5at4h069dddvmlhral7qgm59d37" +threshold = 1 +public_keys = ["tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79"] [[established_account]] vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" -authorization = "signam1qqqvnh3e3j8rv6au622yw74jj76wpty7pttlnfussnyffhg8sk07q7g5ehw5hyz67amnvr4afmcr6tcfs6ktylcqrtc0h350j4zd0qgpqwn9z7" +threshold = 1 +public_keys = ["tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq"] [[established_account]] vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" -authorization = "signam1qqfsz9dvtsan6kqp8zn256gvnuw5hsr27935w63cu0hl30535ewx7tpsf3zypq0hxmjh23z4q4sk7l046n6gjqmvg9ueyxl69lxgzvcfvujj9x" +threshold = 1 +public_keys = ["tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha"] [[bond]] -source = "tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt" -validator = "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" +source = "tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" -signature = "signam1qz00emwjxmvgjlknc55qffxepur6xlay4zhmnejchw32nuwz2kajmn06hdkmdctu9nrnp3kk0hg6495rsvm5paglyuvyekzcvcxzp2qxvf8hlh" + +[bond.signatures] +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qqrhz8puehekzc4960f322f2nm9vn9r47wavvqs33f6vv6uerycj8e2x89ljzmuyekn7rfg6enchhn2s0wm2r6czlhep5kj0an5ll4qqqkkgqz" diff --git a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml index 4450ea7289..c1f290bec5 100644 --- a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml +++ b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml @@ -14,22 +14,25 @@ # Albert [[established_account]] vp = "vp_user" -public_key = "tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" +threshold = 1 +public_keys = ["tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha"] # Bertha [[established_account]] vp = "vp_user" -public_key = "tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq" +threshold = 1 +public_keys = ["tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq"] # Christel [[established_account]] vp = "vp_user" -public_key = "tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79" +threshold = 1 +public_keys = ["tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79"] ########################################################################################## # Albert bonds to `validator-0` [[bond]] -source = "tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt" -validator = "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" +source = "tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" diff --git a/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml b/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml new file mode 100644 index 0000000000..c3a9a6ec8c --- /dev/null +++ b/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml @@ -0,0 +1,45 @@ +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj"] + +[[validator_account]] +address = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +vp = "vp_user" +commission_rate = "0.05" +max_commission_rate_change = "0.01" +net_address = "127.0.0.1:27656" + +[validator_account.consensus_key] +pk = "tpknam1qr9u5py97pdmcvnrxhzuuv79ydv5rw7r9z402sucwt6h0lvmmmwqy2wrweg" +authorization = "signam1qqxx2kpu0yla6jy958t58c649m5gf8v5rjqk5rqemkvxa3xynfxgznjwx2yzre20l7n7mvul2p4vdpsunmjt33fu9c94tu26caevrvgdllk4mx" + +[validator_account.protocol_key] +pk = "tpknam1qrenhfdphzpszlr7fzand6qgmppge430g3a2lquqzhz64fkve5mq2hdfjaa" +authorization = "signam1qqfz3hym66j8my6exzfv88c8e829gulwr4eycyzn5j4a5tns8wj6md6j2kwly4kdr2ts8jwln7mta6weacgf2xjh08scykl3yzqeers899d9fn" + +[validator_account.tendermint_node_key] +pk = "tpknam1qpgcgptcjl22hl2te2uqnp33aqjmvfaud3a3f3sgtxezg7uu5rsv6d2flr3" +authorization = "signam1qpev0rmgakenvsec34xlyz8e2cpyc64u0fex6dv2us2x0yg9plyfrmfwcpxg2png7xl8404fmm6vp4h0afz25jclvc0yavjrc2vncps8wqqgds" + +[validator_account.eth_hot_key] +pk = "tpknam1qypnh98mexms8edj8rcwu0cayx0459p39dwzsffxrr394mf4cse707qcctyrx" +authorization = "signam1qy04m2dqtvwrkk7rjxzpeua89hag2yc38rm3z9ljeg9c7ymp35tuqfd7yk0w4epnsydfvdyd682lvcrtzf28rdkd8snzmmyj846r36zyqq77j5s0" + +[validator_account.eth_cold_key] +pk = "tpknam1qypz8zr0w8lsz3s98vh4p974xuxeedpecj9s2l3326r3kdz4tc0snrcpnc8yv" +authorization = "signam1q8pmqdqcqh9v8djeqlrzy4mv845v9harnm9u5z54nwlrhwkyvc9y79sv0ucaa08c7y3xypcn7wqj6qvjqzfzs9h89hesuxttlujswz5wqyux24m9" + +[validator_account.metadata] +email = "null@null.net" + +[validator_account.signatures] +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qzxx53nx6tptrfp2qhr2du8lmlvhyrxuvcp3razw0yqha45redp7nqsx09d45jads34ecrny5mj2fplru9vyq8ghttksczj86vqx2tg0kwk2f9" + +[[bond]] +source = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +amount = "100000" + +[bond.signatures] +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qrzf2xg5m2hudxc7p0zjnnzlnfrlsjkt5j0wpesw9le2nn7u4sgr3y8s9sr7xs5yzs5qt5fhmtmp2j5cmfr8ulzz2rhwmqzfslry56cymvurrg" diff --git a/genesis/localnet/src/pre-genesis/validator-0/transactions.toml b/genesis/localnet/src/pre-genesis/validator-0/transactions.toml deleted file mode 100644 index 85c174edc2..0000000000 --- a/genesis/localnet/src/pre-genesis/validator-0/transactions.toml +++ /dev/null @@ -1,44 +0,0 @@ -[[validator_account]] -alias = "validator-0" -vp = "vp_user" -commission_rate = "0.05" -max_commission_rate_change = "0.01" -email = "null@null.net" -net_address = "127.0.0.1:27656" - -[validator_account.account_key] -pk = "tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj" -authorization = "signam1qzyc89wynvm4n7eq7jglv4d8upaxu38mkkktykjjtgqygmk4sq97ffxermz5wpur4rqr8gk6fze5uvc4u7q4qzqsr285gungdyhv2acgna2ltq" - -[validator_account.consensus_key] -pk = "tpknam1qr9u5py97pdmcvnrxhzuuv79ydv5rw7r9z402sucwt6h0lvmmmwqy2wrweg" -authorization = "signam1qzt7ww0q00punp93jrvtkq62n4rk8y5d90tllmlkzr0z7urwfdkn7z8ttne92utwskl6ves4qp66r60tg2xqr568ranwfhcm0665wncqq4t5gx" - -[validator_account.protocol_key] -pk = "tpknam1qrenhfdphzpszlr7fzand6qgmppge430g3a2lquqzhz64fkve5mq2hdfjaa" -authorization = "signam1qqeaqzj9fu0xsu7pvrkwla5asxde2wak7fslst0p79vxxydt40tzdj54js7dmjvqs2quygqqwup8z2cfffvm86ff8cud6gzel9g40ycw25wp5d" - -[validator_account.tendermint_node_key] -pk = "tpknam1qpgcgptcjl22hl2te2uqnp33aqjmvfaud3a3f3sgtxezg7uu5rsv6d2flr3" -authorization = "signam1qrq7x5e7svuddy8dyr3xpcdt6a07u6sky0n5qnv5z757g33t5zm3jnnh9uhh073g05n8jx8rmdu5r2djhwh6few6u329u9cld4c7ynsw2592h8" - -[validator_account.eth_hot_key] -pk = "tpknam1qypnh98mexms8edj8rcwu0cayx0459p39dwzsffxrr394mf4cse707qcctyrx" -authorization = "signam1qyrvwveg76nrz2ppnsl0em82gcl4p90cqw8eadyzcvk59e2dx0jqja8ln9l9j79sxzhhh5gdggduz78vtthzj5nzs0xvpwhjuh3hymapqq07qxzm" - -[validator_account.eth_cold_key] -pk = "tpknam1qypz8zr0w8lsz3s98vh4p974xuxeedpecj9s2l3326r3kdz4tc0snrcpnc8yv" -authorization = "signam1q992kq4ymfl6v8arc2s2tn0w7c4f48wx6fxry9qxv2qk8zvjan8q7j9rxg73cx0f5l56fy53x2jgwv7vy8jtxzucckdzgnxc5tpzajs4qq8r02r2" - -[[transfer]] -token = "nam" -source = "tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch" -target = "validator-0" -amount = "200000" -signature = "signam1qz8ch7n6jp7g7hvnzkhmprhqc5umd0gatarlttm7gpcdx6g8hmgqffdq67fn6vzcflmym5xej3m5jmfw7nap00s4nlx2zxrlwz0wlfgt3lswty" - -[[bond]] -source = "validator-0" -validator = "validator-0" -amount = "100000" -signature = "signam1qqukrz3js2pgzrne90u5f7y4mstrzvmzcq7eelml9m3lzrh9zwpf0vhgjq0rsw4ufj4ffdx8ew74uxkk4df649pg6qq5fgwyqa9mpfcyvskk67" diff --git a/genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml b/genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml new file mode 100644 index 0000000000..aa84f561ac --- /dev/null +++ b/genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml @@ -0,0 +1,24 @@ +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj"] + +[[validator_account]] +address = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +vp = "vp_user" +commission_rate = "0.05" +max_commission_rate_change = "0.01" +net_address = "127.0.0.1:27656" +consensus_key = "tpknam1qr9u5py97pdmcvnrxhzuuv79ydv5rw7r9z402sucwt6h0lvmmmwqy2wrweg" +protocol_key = "tpknam1qrenhfdphzpszlr7fzand6qgmppge430g3a2lquqzhz64fkve5mq2hdfjaa" +tendermint_node_key = "tpknam1qpgcgptcjl22hl2te2uqnp33aqjmvfaud3a3f3sgtxezg7uu5rsv6d2flr3" +eth_hot_key = "tpknam1qypnh98mexms8edj8rcwu0cayx0459p39dwzsffxrr394mf4cse707qcctyrx" +eth_cold_key = "tpknam1qypz8zr0w8lsz3s98vh4p974xuxeedpecj9s2l3326r3kdz4tc0snrcpnc8yv" + +[validator_account.metadata] +email = "null@null.net" + +[[bond]] +source = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +amount = "100000" diff --git a/genesis/localnet/src/pre-genesis/validator-0/validator-wallet.toml b/genesis/localnet/src/pre-genesis/validator-0/validator-wallet.toml index 2e0107b173..d7ceb76fb9 100644 --- a/genesis/localnet/src/pre-genesis/validator-0/validator-wallet.toml +++ b/genesis/localnet/src/pre-genesis/validator-0/validator-wallet.toml @@ -1,4 +1,3 @@ -account_key = "unencrypted:0024204e13c51b26ed9b42c05647bc46b3821bb453e53d194962ede57ce5ec66ac" consensus_key = "unencrypted:0037a44681b64d42497e2229516479f83e5344a7a30e8316bde45881a1ec745359" eth_cold_key = "unencrypted:010d34fcb42383f0babe7c86203f07bf4134f0756a630eb07923f21c90cc068602" tendermint_node_key = "unencrypted:002f2e94bb5834e18433343345baf47b2b5d4e1ef4d46ba0f876d3d2c89d831419" diff --git a/genesis/localnet/src/pre-genesis/wallet.toml b/genesis/localnet/src/pre-genesis/wallet.toml index 60a328c0c0..fbf083e87b 100644 --- a/genesis/localnet/src/pre-genesis/wallet.toml +++ b/genesis/localnet/src/pre-genesis/wallet.toml @@ -11,6 +11,7 @@ christel-key = "unencrypted:00a08de8d33b9798328d2e4476fade49f515dc82754451fc6ef7 daewon = "unencrypted:00d19e226c0e7d123d79f5908b5948d4c461b66a5f8aa95600c28b55ab6f5dc772" ester = "unencrypted:01369093e2035d84f72a7e5a17c89b7a938b5d08cc87b2289805e3afcc66ef9a42" faucet-key = "unencrypted:00548aa8393422b88dce5f4be8ee0320638061c3e0649ada1b0dacbec4c0c75bb2" +validator-0-account-key = "unencrypted:0024204e13c51b26ed9b42c05647bc46b3821bb453e53d194962ede57ce5ec66ac" [public_keys] albert-key = "ED25519_PK_PREFIXtpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha" @@ -19,20 +20,21 @@ christel-key = "ED25519_PK_PREFIXtpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkax daewon = "ED25519_PK_PREFIXtpknam1qzz4x4fammhdcfa0g8xw4udkq8s4n6kjhzlxh00ul3da05wuu9wkykqqvjm" ester = "SECP256K1_PK_PREFIXtpknam1qypvqpzu74nafjahlwyq272dj76qq9rz30dulyc94883tmj893mquqs74gxv4" faucet-key = "ED25519_PK_PREFIXtpknam1qzh2d8vk9wvj2j63fa3cvjru9uldpdjctjjxpafl5r8vwwf56pdgyq0vra4" +validator-0-account-key = "ED25519_PK_PREFIXtpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj" [derivation_paths] [addresses] -albert = "tnam1q8uthkc42sw609e8ce6tcjwu6mgz6zgtccff8wdt" -bertha = "tnam1qx6qc3cpupxgntk7awtqrnqh02cyr3e8vghpspr7" -christel = "tnam1qy38nhqlcceqjn5sflrs6mvmp8fq2h79kvsdrwns" +albert = "tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu" +bertha = "tnam1q9rhgyv3ydq0zu3whnftvllqnvhvhm270qxay5tn" +christel = "tnam1q9sx4ekzqaq3xdxtruxkm764nhl00cvcsc7df5jf" albert-key = "tnam1qrr8r00nrf6490ff9zgfz52xrnfhdja7rqvldl46" bertha-key = "tnam1qqc2ge6tg7s03nlshjr5d79dqyjr33aa8g2mxyzm" christel-key = "tnam1qz0nvec686e9pks8ynhm5ddq8ke7j2eey50uagtr" daewon = "tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn" ester = "tnam1qpm3dpsv76ttu382vfggtr7m8n00na3sfyzm2g2q" faucet-key = "tnam1qzgcl2znamndmku7ujw6e79dmvd4v7rfd5c89dfz" -validator-0 = "tnam1qyfcx4un953sf06lhcja377hk2z6faw67ykx549w" +validator-0 = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" [pkhs] 30A4674B47A0F8CFF0BC8746F8AD012438C7BD3A = "bertha-key" @@ -41,5 +43,6 @@ C671BDF31A7552BD2928909151461CD376CBBE18 = "albert-key" 918FA853EEE6DDDB9EE49DACF8ADDB1B5678696D = "faucet-key" 9F36671A3EB250DA0724EFBA35A03DB3E92B3925 = "christel-key" 7716860CF696BE44EA6250858FDB3CDEF9F63049 = "ester" +9D371C6D220F8A4C6D70F0D3F828698881AB2447 = "validator-0-account-key" [address_vp_types] diff --git a/genesis/localnet/transactions.toml b/genesis/localnet/transactions.toml index 0662c5147b..76526a2ca6 100644 --- a/genesis/localnet/transactions.toml +++ b/genesis/localnet/transactions.toml @@ -1,181 +1,77 @@ # Transactions pasted from: # -# 1. `src/pre-genesis/validator-0/transactions.toml` +# 1. `src/pre-genesis/validator-0/signed-transactions.toml` # 2. `src/pre-genesis/signed-transactions.toml` # 1. +[[established_account]] +vp = "vp_user" +threshold = 1 +public_keys = ["tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj"] + [[validator_account]] -alias = "validator-0" +address = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" vp = "vp_user" commission_rate = "0.05" max_commission_rate_change = "0.01" -email = "null@null.net" net_address = "127.0.0.1:27656" -[validator_account.account_key] -pk = "tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj" -authorization = "signam1qzyc89wynvm4n7eq7jglv4d8upaxu38mkkktykjjtgqygmk4sq97ffxermz5wpur4rqr8gk6fze5uvc4u7q4qzqsr285gungdyhv2acgna2ltq" - [validator_account.consensus_key] pk = "tpknam1qr9u5py97pdmcvnrxhzuuv79ydv5rw7r9z402sucwt6h0lvmmmwqy2wrweg" -authorization = "signam1qzt7ww0q00punp93jrvtkq62n4rk8y5d90tllmlkzr0z7urwfdkn7z8ttne92utwskl6ves4qp66r60tg2xqr568ranwfhcm0665wncqq4t5gx" +authorization = "signam1qqxx2kpu0yla6jy958t58c649m5gf8v5rjqk5rqemkvxa3xynfxgznjwx2yzre20l7n7mvul2p4vdpsunmjt33fu9c94tu26caevrvgdllk4mx" [validator_account.protocol_key] pk = "tpknam1qrenhfdphzpszlr7fzand6qgmppge430g3a2lquqzhz64fkve5mq2hdfjaa" -authorization = "signam1qqeaqzj9fu0xsu7pvrkwla5asxde2wak7fslst0p79vxxydt40tzdj54js7dmjvqs2quygqqwup8z2cfffvm86ff8cud6gzel9g40ycw25wp5d" +authorization = "signam1qqfz3hym66j8my6exzfv88c8e829gulwr4eycyzn5j4a5tns8wj6md6j2kwly4kdr2ts8jwln7mta6weacgf2xjh08scykl3yzqeers899d9fn" [validator_account.tendermint_node_key] pk = "tpknam1qpgcgptcjl22hl2te2uqnp33aqjmvfaud3a3f3sgtxezg7uu5rsv6d2flr3" -authorization = "signam1qrq7x5e7svuddy8dyr3xpcdt6a07u6sky0n5qnv5z757g33t5zm3jnnh9uhh073g05n8jx8rmdu5r2djhwh6few6u329u9cld4c7ynsw2592h8" +authorization = "signam1qpev0rmgakenvsec34xlyz8e2cpyc64u0fex6dv2us2x0yg9plyfrmfwcpxg2png7xl8404fmm6vp4h0afz25jclvc0yavjrc2vncps8wqqgds" [validator_account.eth_hot_key] pk = "tpknam1qypnh98mexms8edj8rcwu0cayx0459p39dwzsffxrr394mf4cse707qcctyrx" -authorization = "signam1qyrvwveg76nrz2ppnsl0em82gcl4p90cqw8eadyzcvk59e2dx0jqja8ln9l9j79sxzhhh5gdggduz78vtthzj5nzs0xvpwhjuh3hymapqq07qxzm" +authorization = "signam1qy04m2dqtvwrkk7rjxzpeua89hag2yc38rm3z9ljeg9c7ymp35tuqfd7yk0w4epnsydfvdyd682lvcrtzf28rdkd8snzmmyj846r36zyqq77j5s0" [validator_account.eth_cold_key] pk = "tpknam1qypz8zr0w8lsz3s98vh4p974xuxeedpecj9s2l3326r3kdz4tc0snrcpnc8yv" -authorization = "signam1q992kq4ymfl6v8arc2s2tn0w7c4f48wx6fxry9qxv2qk8zvjan8q7j9rxg73cx0f5l56fy53x2jgwv7vy8jtxzucckdzgnxc5tpzajs4qq8r02r2" +authorization = "signam1q8pmqdqcqh9v8djeqlrzy4mv845v9harnm9u5z54nwlrhwkyvc9y79sv0ucaa08c7y3xypcn7wqj6qvjqzfzs9h89hesuxttlujswz5wqyux24m9" + +[validator_account.metadata] +email = "null@null.net" -[[transfer]] -token = "nam" -source = "tpknam1qzp22w6trhmxmp2dx5h883c2l684z0e20a9egusmxaat62wvaa4agjf64ch" -target = "validator-0" -amount = "200000" -signature = "signam1qz8ch7n6jp7g7hvnzkhmprhqc5umd0gatarlttm7gpcdx6g8hmgqffdq67fn6vzcflmym5xej3m5jmfw7nap00s4nlx2zxrlwz0wlfgt3lswty" +[validator_account.signatures] +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qzxx53nx6tptrfp2qhr2du8lmlvhyrxuvcp3razw0yqha45redp7nqsx09d45jads34ecrny5mj2fplru9vyq8ghttksczj86vqx2tg0kwk2f9" [[bond]] -source = "validator-0" -validator = "validator-0" +source = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "100000" -signature = "signam1qqukrz3js2pgzrne90u5f7y4mstrzvmzcq7eelml9m3lzrh9zwpf0vhgjq0rsw4ufj4ffdx8ew74uxkk4df649pg6qq5fgwyqa9mpfcyvskk67" +[bond.signatures] +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qrzf2xg5m2hudxc7p0zjnnzlnfrlsjkt5j0wpesw9le2nn7u4sgr3y8s9sr7xs5yzs5qt5fhmtmp2j5cmfr8ulzz2rhwmqzfslry56cymvurrg" # 2. [[established_account]] -alias = "albert" vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -authorization = "signam1qzq6dwf2a9dq4hp3nmxfgckh55mulzryxsfkqhr9uvfvmtr9wt38lyyvzvfqryxnat2a4ry6hygv957z683dyngu03gse2uvl5ldfccyqvdpfr" - -[established_account.storage] +threshold = 1 +public_keys = ["tpknam1qqwfpuvn8x7yqtquejhppef4vddv9ghusec2rkaxav38amamxaxpgj9cs79"] [[established_account]] -alias = "bertha" vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -authorization = "signam1qzt58nd7k2mj647x8x4ydhsjkut7dmsl7yjlnrnwu9kzjdch3cljv6dq05mx2kvwn80kjezh7lz26adc5ksvyn3knufymtkhlmnhg3c8rvmxvv" - -[established_account.storage] +threshold = 1 +public_keys = ["tpknam1qq52dx5e290wyh7ngdt6wudtyd502lg6ln49yvg3vz97e8j2ruux5e3yewq"] [[established_account]] -alias = "christel" vp = "vp_user" - -[established_account.public_key] -pk = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -authorization = "signam1qrf2l6a5u4ywskryhdueudnm50j3kqhujas3mmktenqm89fmlskjnyeskr2tr7js5swmtqqtenkj6ap9xpelx2w40fjjczc4w9xtdggqn3k4vv" - -[established_account.storage] - -[[transfer]] -token = "nam" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qrxfthuz5yzapdt82d8nc656ffrzkxnkfg6fw8w7e6045vcpn8kpr4c536ug4fs2ddz5823dfd3w4qnhcl40qmtasfccaa99a0hvjcqv97vg8n" - -[[transfer]] -token = "btc" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qq826qel8zn5p4dfx25ma45uxeskx0yjlsaxp38uyvehvds6t93c8wkhaydunm4nzz9750dwhkjst00mfrwmu066y3rj6gtk5wjskxq9rksf35" - -[[transfer]] -token = "eth" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qzghddk0u6kpqaxhskrj8ah69d3hce95wndgvlpuyvuw0h0fnyrnp026ltvtfmfhpzywkf88yyhv80hzkus57s9nn6vn3ljvmkkf7zgqtfyyak" - -[[transfer]] -token = "dot" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qqlu8kajh0up5zhewd2h44fhq7zdaapqt9xaj8xp7jmdqpv4dl9d5tesw3wyr9su962rvpkf02x27579vqjk4f4advx7t9ejz74cjrc24m6y4r" - -[[transfer]] -token = "schnitzel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qpukdd6f7pvtgau9v58xxwrnmv5pjra6527aggce9j8cu6fnekzag2cmmf4w0x0cn76gpth2vw6zcsnw37xjuz96hflqfnk5sznxe6cfvwa9m4" - -[[transfer]] -token = "apfel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qprnsyhlruvw4a6jjs98xdd04gxdsgycywzt79gyyak68gn4f50ppdvdm0q6ns44cheu7s43kal0qsldml8s0zexqk3s9zmglu4l6fsz0zx8xa" - -[[transfer]] -token = "kartoffel" -source = "tpknam1qz0aphcsrw37j8fy742cjwhphu9jwx7esd3ad4xxtxrkwv07ff63we33t3r" -target = "albert" -amount = "1000000" -signature = "signam1qpg4y6ltax3qfclguym2kpa0ezlkf4hz4u3h8fc2ndmuzm4zfe26ygq9u6nhjjvj93n5v296x4arkvjk22ygee2fx054ce7ap6gvynq2nsscys" - -[[transfer]] -token = "nam" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" -signature = "signam1qpe3ms76cxvqsa58624ked5ndjr3jr3u7lf9ypalxdh29gajwmsxtzjxzswk0xmza90u47tgksr0d7enpd730ps7fq5u76l7ekxeyvgfj9fhwt" - -[[transfer]] -token = "btc" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" -signature = "signam1qqq4yc6s3h4ysezcmgsdgyfsqu8mmrwwc8a358sm864zehx74ums8fmgmkyyr3wz9qqrc5fagwrw0a4mgqkkleumc0u6ynshj5ns0tcdz7wtf3" - -[[transfer]] -token = "eth" -source = "tpknam1qpyfnrl6qdqvguah9kknvp9t6ajcrec7fge56pcgaa655zkua3nds48x83t" -target = "bertha" -amount = "1000000" -signature = "signam1qzyqkdxt3cxf0huv0hzskqmuthw38s3kj2sx50znzc68gzx64hrd3hkyx0g27pruwa9fv42suw3k8gkfaelcp4ymnvfw7ltx0mz2z9qrt9gesf" - -[[transfer]] -token = "nam" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" -signature = "signam1qran2jh0dwuwchh45snhk3p9uyxqym57eg527pcjeekwhvpr5j0fy6dte7x3v2dwwc6vqmv49drpvmk6uhzlt2p8rqx0th3h7smf8rqrem03s8" - -[[transfer]] -token = "btc" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" -signature = "signam1qrxp6p56qtvsyp7jfs4jru3mkfv6l4t7hpguur72579pqjaexdry9turgmzwzj6qmua3s2y2a4krtuxwaag66j4dpz44aaqksy30tfcx6f3ez0" - -[[transfer]] -token = "eth" -source = "tpknam1qp6uy52q0fldjxupznuskm69fkuswx3fq3vw9kekzp4enkh5h7pmzwgc7uu" -target = "christel" -amount = "1000000" -signature = "signam1qrtrec6mps9p7zhmm7sywxwc7zdm32umt0zdwpuflrkh6952rx74amnsf9hn0yughs4e6czgpdmwsmujvkr4ptk2x24vtc8phz0s2asv27zjfs" +threshold = 1 +public_keys = ["tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha"] [[bond]] -source = "albert" -validator = "validator-0" +source = "tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" -signature = "signam1qpx0kc8rl966vnnswfwhcwxh5vv69p7lev2apc2yephag978gqg5903kuw6dugg9c6pg5jdw048umk72ntympphz0dka74aspldslaqrs6cwg6" + +[bond.signatures] +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qqrhz8puehekzc4960f322f2nm9vn9r47wavvqs33f6vv6uerycj8e2x89ljzmuyekn7rfg6enchhn2s0wm2r6czlhep5kj0an5ll4qqqkkgqz" From 1a4e160acd9e2634329f007f034e4e97bde4b919 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 29 Nov 2023 10:12:11 +0000 Subject: [PATCH 33/81] Fix dev genesis --- apps/src/lib/config/genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index c24497d549..b16a337972 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -458,7 +458,7 @@ pub fn make_dev_genesis( let validator_account_tx = transactions::ValidatorAccountTx { address: StringEncoded::new(address.clone()), - vp: "vp_validator".to_string(), + vp: "vp_user".to_string(), commission_rate: Dec::new(5, 2).expect("This can't fail"), max_commission_rate_change: Dec::new(1, 2) .expect("This can't fail"), From 26161f1f51f32d3d8d8b7861d6a690e1b4ee1511 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 29 Nov 2023 10:54:28 +0000 Subject: [PATCH 34/81] Add base dir arg to gen localnet script --- scripts/gen_localnet.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/gen_localnet.py b/scripts/gen_localnet.py index b5e99f1479..9e03c7d6cf 100644 --- a/scripts/gen_localnet.py +++ b/scripts/gen_localnet.py @@ -44,6 +44,7 @@ def edit_parameters(params_toml, **kwargs): parser = argparse.ArgumentParser(description='Builds a localnet for testing purposes') # Add the arguments +parser.add_argument('--base-dir', type=str, help='The path to the base directory of the chain.') parser.add_argument('--localnet-dir', type=str, help='The localnet directory containing the genesis templates.') parser.add_argument('-m', '--mode', type=str, help='The mode to run the localnet in. Can be release or debug, defaults to debug.') parser.add_argument('--epoch-length', type=int, help='The epoch length in seconds, defaults to parameters.toml value.') @@ -116,7 +117,10 @@ def edit_parameters(params_toml, **kwargs): TEMPLATES_PATH=localnet_dir WASM_CHECKSUMS_PATH=namada_dir + '/wasm/checksums.json' WASM_PATH=namada_dir + '/wasm/' -BASE_DIR = subprocess.check_output([namadac_bin, "utils", "default-base-dir"]).decode().strip() +BASE_DIR = args.base_dir + +if not BASE_DIR: + BASE_DIR = subprocess.check_output([namadac_bin, "utils", "default-base-dir"]).decode().strip() # Delete the base dir if os.path.isdir(BASE_DIR): @@ -133,7 +137,7 @@ def edit_parameters(params_toml, **kwargs): print(f"Cannot find wasm directory that is not empty at {WASM_PATH}") sys.exit(1) -os.system(f"{namadac_bin} utils init-network --chain-prefix {CHAIN_PREFIX} --genesis-time {GENESIS_TIME} --templates-path {TEMPLATES_PATH} --wasm-checksums-path {WASM_CHECKSUMS_PATH}") +os.system(f"{namadac_bin} --base-dir={BASE_DIR} utils init-network --chain-prefix {CHAIN_PREFIX} --genesis-time {GENESIS_TIME} --templates-path {TEMPLATES_PATH} --wasm-checksums-path {WASM_CHECKSUMS_PATH}") base_dir_files = os.listdir(BASE_DIR) CHAIN_ID="" @@ -154,7 +158,7 @@ def edit_parameters(params_toml, **kwargs): print(f"Cannot find pre-genesis directory that is not empty at {PRE_GENESIS_PATH}") sys.exit(1) -os.system(f"NAMADA_NETWORK_CONFIGS_DIR='{temp_dir}' {namadac_bin} utils join-network --chain-id {CHAIN_ID} --genesis-validator {GENESIS_VALIDATOR} --pre-genesis-path {PRE_GENESIS_PATH} --dont-prefetch-wasm") +os.system(f"NAMADA_NETWORK_CONFIGS_DIR='{temp_dir}' {namadac_bin} --base-dir={BASE_DIR} utils join-network --chain-id {CHAIN_ID} --genesis-validator {GENESIS_VALIDATOR} --pre-genesis-path {PRE_GENESIS_PATH} --dont-prefetch-wasm") shutil.rmtree(BASE_DIR + '/' + CHAIN_ID + '/wasm/') shutil.move(temp_dir + CHAIN_ID + '/wasm/', BASE_DIR + '/' + CHAIN_ID + '/wasm/') @@ -167,5 +171,5 @@ def edit_parameters(params_toml, **kwargs): shutil.rmtree(temp_dir) print("Run the ledger using the following command:") -print(f"{namada_bin} ledger run") +print(f"{namada_bin} --base-dir={BASE_DIR} ledger run") From 8a7ee7ea36cd7c71dab1a178f67e1759df08223c Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 29 Nov 2023 11:20:20 +0000 Subject: [PATCH 35/81] Update masp proofs --- ...27024E9AC79985302DD15C96CB57743A74CC3.bin} | Bin 9941 -> 9941 bytes ...ECD8CFD237A237C63AA565FC44893414EE7FC.bin} | Bin 9649 -> 9649 bytes ...7E2CFFC5F8055CE2E2FAFB4DE8E7C2216D5F8.bin} | Bin 7448 -> 7448 bytes ...62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin} | Bin 7448 -> 7448 bytes ...3F779253B53A9A0BD62FD3DF8F6FCF4AF1E145.bin | Bin 17018 -> 0 bytes ...BF9BC175A70082068C8785BDDBF49DCCA4BE66.bin | Bin 7448 -> 0 bytes ...B5464810F6974083CC2FEE2756ED1E97B8143.bin} | Bin 7448 -> 7448 bytes ...C89879834677F926130D56EB5E4067728EB5CF.bin | Bin 10382 -> 0 bytes ...BE50202AF004DFD895A721EDA284D96B253ACC.bin | Bin 7448 -> 0 bytes ...3CEA99A9BC66B2BAB03D4ABA1AE57B12596061.bin | Bin 7448 -> 0 bytes ...F136D4522BAFC99E0E81FD54A28A38E8419CFC.bin | Bin 20518 -> 0 bytes ...CBE1A9EAF3496A30D69069DE2FBD293EEC978.bin} | Bin 7448 -> 7448 bytes ...8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin} | Bin 24494 -> 24494 bytes ...6D2CF14C7F9E90A8588BA75DA0D2860F36E2CB.bin | Bin 9649 -> 0 bytes ...6D2AEEF55A6F202D8EB4564017DB5BEF872107.bin | Bin 15597 -> 15597 bytes ...396732D2EBF77728D2772EB251123DF2CEF6A1.bin | Bin 9941 -> 0 bytes ...A5C2232840271F032F756953233DB3E53E757.bin} | Bin 9941 -> 9941 bytes ...CC6F78791F8954A0219C07B15344AEE0D2E0F.bin} | Bin 10382 -> 10382 bytes ...5CF776D5DE7C9153D67B7FDF22ED6F35558738.bin | Bin 24494 -> 0 bytes ...DBA1FDB2515DF33D43611F2860B8D3C178B474.bin | Bin 7448 -> 0 bytes ...99481AB5F7C8F57502C5EAB2BF7594EA6CED8F.bin | Bin 15257 -> 15257 bytes ...699E65D4B40A4E1BC9E7A32D63CF28466A2F20.bin | Bin 17018 -> 17018 bytes ...6C8B75587507CA52981F48FD1FC9C5BE0BEE43.bin | Bin 15257 -> 0 bytes ...1D5DEF92B9A9BB3A159947560514682CC3877.bin} | Bin 7448 -> 7448 bytes 24 files changed, 0 insertions(+), 0 deletions(-) rename test_fixtures/masp_proofs/{76BADC1367FEF1062029B180AD637D60B440C71729F148923838380D5CFE71A9.bin => 30E148B3F9E8D21A41ABB09756027024E9AC79985302DD15C96CB57743A74CC3.bin} (76%) rename test_fixtures/masp_proofs/{ABB08197C40226F64D449C7F116BC11A30C23464FD05B13516E0B576456D6BE9.bin => 473EDF0B2908F047110AC52C7F7ECD8CFD237A237C63AA565FC44893414EE7FC.bin} (76%) rename test_fixtures/masp_proofs/{96CBC406931DDBC76053B417CCFE78D0FC876E11ADD563D149168D4BDE86FBB8.bin => 4B412E2EA5AC98758E696AB36327E2CFFC5F8055CE2E2FAFB4DE8E7C2216D5F8.bin} (75%) rename test_fixtures/masp_proofs/{B74C1EC89D48DC01F71DA402F898CB65B7F71F737870C86AEA55454B8B32DC2D.bin => 4CCB9ADD3188CD893508CBB4FCB62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin} (76%) delete mode 100644 test_fixtures/masp_proofs/51EF94B13138B91DB3081F95193F779253B53A9A0BD62FD3DF8F6FCF4AF1E145.bin delete mode 100644 test_fixtures/masp_proofs/544F7432B95E26E312105FB804BF9BC175A70082068C8785BDDBF49DCCA4BE66.bin rename test_fixtures/masp_proofs/{25FA248C27E805F28102B6E3DCFEAD637C92544211E7B3765CA7AA186FB1E409.bin => 553C507BF748CC2353DCE98EB91B5464810F6974083CC2FEE2756ED1E97B8143.bin} (75%) delete mode 100644 test_fixtures/masp_proofs/6DD5A788D36258E9D5FEF454DBC89879834677F926130D56EB5E4067728EB5CF.bin delete mode 100644 test_fixtures/masp_proofs/6E92D0B97A65FB5ADFA6371A6CBE50202AF004DFD895A721EDA284D96B253ACC.bin delete mode 100644 test_fixtures/masp_proofs/8032CA7B951C625E43F48AEBD53CEA99A9BC66B2BAB03D4ABA1AE57B12596061.bin delete mode 100644 test_fixtures/masp_proofs/8A79BF5E0292339E70287DB626F136D4522BAFC99E0E81FD54A28A38E8419CFC.bin rename test_fixtures/masp_proofs/{CA6AF0286DB05F3AB2513109958B21F6D7D9EE1DA5A6E60E9A37CD30DA4A505F.bin => 8BA2DA741BF1FE1CDEC5295AE3ECBE1A9EAF3496A30D69069DE2FBD293EEC978.bin} (74%) rename test_fixtures/masp_proofs/{0AB9DC2FED12A93FDA42447E6A14612B09451DF258B1B593CFED41B0E9256A2E.bin => 978C35E058808D61F0E265D72DE8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin} (79%) delete mode 100644 test_fixtures/masp_proofs/A8A9963AC2983B576BAE4DE9BA6D2CF14C7F9E90A8588BA75DA0D2860F36E2CB.bin delete mode 100644 test_fixtures/masp_proofs/AEA19C9B07742FF5F6D759B171396732D2EBF77728D2772EB251123DF2CEF6A1.bin rename test_fixtures/masp_proofs/{8DAE054DE58BDFFE4F551092B09402D995E6F74FFBF5CE3BA13049D272C5E752.bin => BB9AA71A4227C9E62948CBA8DB4A5C2232840271F032F756953233DB3E53E757.bin} (76%) rename test_fixtures/masp_proofs/{FD874BACE965B637E528BF66B079048F119C09D85335704A083700CA11415DF3.bin => C506FA6C2EFB37B06CD5A473AF5CC6F78791F8954A0219C07B15344AEE0D2E0F.bin} (76%) delete mode 100644 test_fixtures/masp_proofs/C788F9057C615CCE7B260BB8BF5CF776D5DE7C9153D67B7FDF22ED6F35558738.bin delete mode 100644 test_fixtures/masp_proofs/CBF4087F2AB578C83A736E4598DBA1FDB2515DF33D43611F2860B8D3C178B474.bin delete mode 100644 test_fixtures/masp_proofs/EDE8DC791D02098C5C199CAF2F6C8B75587507CA52981F48FD1FC9C5BE0BEE43.bin rename test_fixtures/masp_proofs/{2E78CC40C345D7EA50407110784E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin => F0471ECBD3AF04B4A373D2966781D5DEF92B9A9BB3A159947560514682CC3877.bin} (71%) diff --git a/test_fixtures/masp_proofs/76BADC1367FEF1062029B180AD637D60B440C71729F148923838380D5CFE71A9.bin b/test_fixtures/masp_proofs/30E148B3F9E8D21A41ABB09756027024E9AC79985302DD15C96CB57743A74CC3.bin similarity index 76% rename from test_fixtures/masp_proofs/76BADC1367FEF1062029B180AD637D60B440C71729F148923838380D5CFE71A9.bin rename to test_fixtures/masp_proofs/30E148B3F9E8D21A41ABB09756027024E9AC79985302DD15C96CB57743A74CC3.bin index 0e0759b3c3552261d66ce23876c2e75855f93a5f..5abe95051451598fa2d1f6a3bd271e04bcf7ed78 100644 GIT binary patch delta 1787 zcmV6$a7qi5GPB%elF*CIFLeMZu+Mxu9)b}>nmTYlU^A>AcN1@9TEq& zs7pm>Y+@>!qQujzr`KV%6Y~47Kx=Vn1e3lQKp@`BYJ<_cN>dGFfXBj+O&hki;+#L5 z^KQxs%#TP2;31P98bLvwu9vmg|Gq1?#X+TcT%^xJXqe{LxL^=L_OI}8XwnD*hsy~r z5Rv^2*w(yrg|bjolq147-R`wk@cY8mg;L?>v!ELE0wDK^o#4R!Gmki->*G?tWMAh3 zROL-s9BoF^UKm?!m~FGP9i#$KE&H>68Qa|A;j2C*V~$nXn(g8Gz5h8#Q&y?9aaoLv zsszZ@3KN2Ty9A6W(6ApcG%t_xleZ-VIv7~Y$4A3ChnQW+LzzXxOFSKKc({GJk{KzH zod}cJAV7bJ`hKbbxhNkyloDktzrjuq-wbG4cK?s?S(o_Ur~c7d+dQjlHb+QBgm1AE{P!@gk*8QdRb4xCXkPDCy-+b*h-ZCzayS*70% zfVW{+FOT&bNtt&64m0n7NPRX%XCvw_u=y736Ab_u^|q zg?;rHy^^igfUO-0vnjZi1S}cEil!@o?#YT%7Qff4cG4}s+ulQCk)}2Vv*Lj>QOHS2WbA8 zEVvL#xX3u}?GJ7`N7hTuNH#v$3fMtZT9hBcr9aZ&3W~|f|1P|no{XbfpS~yLaI8-F zaxkrGDv%d(H4p9F4ZWh?8{LwA9J}f0tqo=Jc|KTi@D5El_Ox zVsKa|1nF_>!^t>_zMF|Xk2$Wxe;nGJUU?a3t~=24EXfAQan^q_St7tJV~u~Fc|jCS z42Vdp4ex7_x2^cX=c&53)$LvKQ91U);rg65bu$XxTd^NG1-N809fz&7{)DRb{IjOb z&8>&ll&rJ;DqBN?s<5ln?H6-`QTchpyG7o?xbE};J`K=O=@4pKTy#KDOa{i0X=fIE(1SoTLVOgD7>H z6fBKwS~e5P+Osr_A1A=9{Z89DDh(McD^>yf%Z(fO{uH9?dL)a~hL(S0R!51u$}a^S zE^mVfAus&OPHvTU9dlK?7)e|EAeum-$n0n;;Vr(lo42#k>4nZbA9)g#i8t=Ys?-}uwqN^$enBpXpR08h8GnIddMX0DAjrElNgOU1EfP@S|jXDUXApI0d?2j8BaY>*qQikNv zxnwydT`fZ{JF%6#O-bTSz1Ln5gu`|pHRNoScBMeb+dVito9O~=qN>F`_x_*Vqm9() zI3kKsq{1ZFTG*N&d1b(Y6#j`B_-VM%_^{Y>Rh-$V-nqTuwIhEI;}u)OKE7zVSN3>A z8})FfP6dWM?6(4D*_cQw=Yko*ZXWZXWJOkBstd>)&z2YP1ZX*rO6(!B_lB1~z%#kv zBvc-OH@oG#Y!#C2=DP1ILIs}XcdYpTqt{~P)IMTF#SUiCBZm>PTy=G2dyoa7lD}yc dQgHf+93Nst1#qCOp7Xa@qg@ delta 1787 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 P1}d3Wegf$WlXWFL%#Bgn diff --git a/test_fixtures/masp_proofs/96CBC406931DDBC76053B417CCFE78D0FC876E11ADD563D149168D4BDE86FBB8.bin b/test_fixtures/masp_proofs/4B412E2EA5AC98758E696AB36327E2CFFC5F8055CE2E2FAFB4DE8E7C2216D5F8.bin similarity index 75% rename from test_fixtures/masp_proofs/96CBC406931DDBC76053B417CCFE78D0FC876E11ADD563D149168D4BDE86FBB8.bin rename to test_fixtures/masp_proofs/4B412E2EA5AC98758E696AB36327E2CFFC5F8055CE2E2FAFB4DE8E7C2216D5F8.bin index 8c4d3c8e9b19a4a18ab50b12050c676db892002d..8b15531e3ec213b150ecbd96ab3450d790576908 100644 GIT binary patch delta 1200 zcmbPXHN$FxiAe0#t)glQ&rD{soR)I4x8B^x?%^{zn@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_29~|Ebfx!xiL*LrrgZ9?rl8h2##N6Kbe}!&Da)F)JKSUPW@p|nObUK? z*2@e24KFL(aInI?B~kiY(ZxgY_pSu|+gr1XQSImc&EWz-RgX^0HN0~2q}%E1p;NbN zbg|7@>3(`k;_S)KpB+=<=bSTZbG9&0Rblk*&Xu-h9J{(ji}ycxpWe>Z8(fwUdCUCZ z#+XKj(v6$z#Z;N=XKuKA*sQQ%=d?0~Q@M35TNsS~`0<6Q_H1H*w@o9B!^qU@`Gfqw zFHXrt+$~dG;WTf})B25JGj_H+q!g8|{I_n)(ZsE*&fPiJe1@?aQ7G~^2RYk`=j_10>oYZ!$FLf$RE5Gtx_E3>``{I3b z{~Tm#yQt-}z4r0)ol9=&xBpzluK9ZEg=^RMp3N5*>1fXIRMk75QTE;9|Bm=DAL$z& zN6YT5joh~Wz>0-^j~rOpEv#O>HLRCZc(?j)O}N2KoyA69!)y-QH-Aa0+x0a(MauD) zu`;!Q!g+hy3^P~#mA_FV_3Pg8 zsggdqQqyUiIOV&nToT02{)OqZ@v3@ zYVXd1&LxK(j=o;!`a(7S)$0x2W=-cm2wo2}`lXktes#|BN0&akbscJPd4DQ);T_8^ z`7-~E=`m^bH-6Rk#+?3gH#K*$?;~;Nmx_A&>JqzFyHEKLIisp=+nqY6rT({nJzXO1 z!6Uorn468oyUVN-OZF~d(fPiv`E_lTZk3~r&EdsaS+j)M{`_ZAxfJtIcK6MDb0%(_ zu){7Xq44#a>aAsF$A4brR^(-9PW5ePJg1!V_wYnrq57#C-5vVB^|}RAJ6)Tw{psE< zceXOG{BUN^{N3eOa>Z}0TeHc{BKh*1=W)GTx_ajp&d^opoErId*^bJPt?N#e|IW7+ WwRyKe=I*i=6*fjoIVL}mF#!M?9z&-9 delta 1200 zcmbPXHN$FxiAbFH)`>d;b+!Dj<~g;yK3U;y&96K;n@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_29|t|maJ;^oL$OcAET7}K5O}NEB(B?!EK|{-AyNcUXifh?9BUxNkQnn z=H3@hE)UWUDziPGb6&=5^GvC4!714v!VIdeFL-fmbGQId)lRjIYYWXz+Z%W|iXNWY z6H+C;^2*l=GX{I>nc0!+SMhJo76z*NckFs-+A){!3+G!bdAq;q?p@V0A{BnyLnn5N z-ju!Ole4*AOqIF5;ik!lS%-di*R8(e>^{X#C*tbajg7aQ*+mQ#l#CkIKi*#?!@jL~ zgUzx8?R?_}>sdB9TAqDTGJi#j`doe%0q)kEBR+V>3jU!2jCU*oRIA+YJ|{oju#`96NSHn;wvnx?dFO7Cud z)y%6oO;$`v(`T&>3NgJ<^@OD{^q-CE((nI|rBvQuT=>BKmEEnA&z5yX?Y1^s!}RRO zC1szEEh3j@tUYj5Qr$93^9t8yJrB;Ehnl+V*s}r_hdoxxXr^O@u9MoX(S zE-P-^eqt?u^@TDCj@8k^n^$qM*H5-HwEeNMx0D)L*0O3UJg_zyZhZ)6Q6rrojYZ(KIlsPQgQv@tZ^h}#|4p5@Mat1bIsHKW z9h=ha@x>o*3x3RRPx$=5HZ?YMx#spwrS@k1&DYxZr93h`%;eW%e!O7zyt@;Q%bx!+ zv13}_p0^X^)m;~!HwfPO)P%wM#F4x0k^d?#gq$#%s3w>4!{egl(bmQ-KX=V<&sV+3 zFQW5u`#DF>>xWNVo3NAVuWj|2octBBik`E$F0<6j8HKEzEF@y}F^9s?{QG0|O|ZiSS8-zSei)pgpLoC`N4 zT=$N0SX0n@v;LPv)YhcvDR(dD{Rw$F`Lmqt$7xTNcAXD$5IZ}gQJX=~rA;S`l|6TH zVv*bOikuaBg+2<`jg{H^KK;%4S+H`;5nH{O+P~c{-+tAEDwy*$8uNU4y}-5QP;yV6 zO*O;EG=KNwD=QMb9G)+ImatJ{?&74vqX!xcPpbFa$*Y)8H*jK5Rd#$-KnN%Arkm_CEeCQoK97kN-3}-E?tMhuimc SCr(5?;oszF%su&uj0php*e#9# diff --git a/test_fixtures/masp_proofs/B74C1EC89D48DC01F71DA402F898CB65B7F71F737870C86AEA55454B8B32DC2D.bin b/test_fixtures/masp_proofs/4CCB9ADD3188CD893508CBB4FCB62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin similarity index 76% rename from test_fixtures/masp_proofs/B74C1EC89D48DC01F71DA402F898CB65B7F71F737870C86AEA55454B8B32DC2D.bin rename to test_fixtures/masp_proofs/4CCB9ADD3188CD893508CBB4FCB62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin index 53999911ff012f317dacadec5895581c3e897c0b..e95e73953ed6015ec87fbee783238a40bf2604e4 100644 GIT binary patch delta 1200 zcmbPXHN$FxiAe0#t)glQ&rD{soR)I4x8B^x?%^{zn@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_2A04w?>UYtOIK!wUli&N)-B|nb=s?c#p?f+OZ8iBNJg}6cIN%Uq;RdY znYYSi)&GYI3bs#=?NsN`D=W-eUz_J~BDhG<>{jvSZ~>sId8sP9Kji#B71v$D@KG!y z@qM}X+L?J4#XEGTnR)I_$=RGO3{=&@mXI;^)#-iJnPzX+AMC7^zQgN3_vD@*75$mB zHXZq)v$o7f~Zzc%kZeCON?BZJtGpWh;c{kRfDE}W@m-L&xJ zw?a10<3WFyw5<_%*pv9RR_JEnYWp{nHx(FS397r8(N+ zX{#!p+lFnM{^V&6OUk18y7RPlL~kgV{@hYCIBthzQT3Md*Bd$QR+v3rslQlo`KDv> zui1{d*4&>Vf~Pg>A^9}4_(SFt^FwAbmV4U;qCA9_l0g0 zdcVKnyW%Wy+kJmTUu2sed42ca9OpQvDR1UAh}wKY>&wIup5x3t1qL1~S~ znanPCzQk14ZVo&0f3BOrq*VU%cMjQS3p|{#IVrw==3cdkCm!DxWolWyWW3_=)yFtx z%DT_Tzhti2oVc~3gio=@MCi{Vfyt?J@`IATDgLNdFVLHJWdGs?e>|9OnHKA}IE2pW zpC)wHiIa1t8?$V^ zYd;b+!Dj<~g;yK3U;y&96K;n@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_29`OCkE_XF(eM#Z*DbqUyydxa$nK?n9bflm-FA-jHC*1Y*_rnXlfv>P zuNfb=&ywptwsJywet%+Q#G+$+nWa?gcJvvG9w`sq94-JP7OgxSu|p7}j2>}p-$ z?5p2*uUNFXUQCs_e%roDb9i_~jyMOszgs1|{I7L@oRM9D*`#YJH8m$QnU=`B(_y~* z>2J^#AB#udFYGK`C@#V9fjQ*o>xRA}+pFbU+fs^>PfXt4^X;+c(s(-~rrH19(^WSA z|MEO^S8?(q#s$o0<7Cg*v#AK~)7|xa`*f|`uFc*{=ghZ?ZF@A~QuD%kJ-Y;khC?@Y z-uB#krCMIu#C-Xplh*l;z4H&)I#q~lbSt^NP4oZqTv6rIWkuFC&psUFT+N*-_5X;S zzughb??JncOs<`tXdtmm(cn$v`G?x!@sA}^7P_t4E`9BLRn+Il(+f9WJ9sluX-CMy z+1hE39~r%9XVHr&y7whwP1>VxSDot@&s0{kT${Un&V^;0V&{K2A#!3%&a3?#W&50_ zG9FpJQSyt2;HSFlM+&&rZL3?Yi|4EmabA8ZcxT_@C*POzIyLO#s6M@F>C;ZX#}RR6 za=m43qG#8?j5S%iCnzz|n`w!?ti^}ocIo5dTZ2C^_3Fn~>sT|Ny}_{fQbE(K<F@C9EIM{`v2Lo68iL)LlKY|2Hn3ZQuJ~SJJ;L5w#k-R)6`y z^KokEI-Py-@?OiGKi=I|&9r3(Q-GfSnSb{sj~st3Keb@j4}QC6s~PJAl8xOhjixvV zw*PzaxwU5gv|qVv>Zcm_A6XOsPvX>!LrGH}SYOmpe9&IwQP`}Ja`}YHcjLTWvs7-+ z7MG|@kVq&MT|C>Q{ouFil`Qt`6P{iOw>f)iPTZbfGKHii z=KXG^lC`sf>+k)05g;w?x!G25?Lqz~p3Vvbb zr5IS0COvTR7V@lOHxD`%c+R&>@@s#HlH%Ew&lu+z?_dh`XXl)`gPpnm^kVxt0o&iL VZ1PGH(7GBZztx40b@CG#69BgNIRpRz diff --git a/test_fixtures/masp_proofs/51EF94B13138B91DB3081F95193F779253B53A9A0BD62FD3DF8F6FCF4AF1E145.bin b/test_fixtures/masp_proofs/51EF94B13138B91DB3081F95193F779253B53A9A0BD62FD3DF8F6FCF4AF1E145.bin deleted file mode 100644 index 8b21aac70d3fd50b05a84cd005d0e440b1cbefc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17018 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 diff --git a/test_fixtures/masp_proofs/544F7432B95E26E312105FB804BF9BC175A70082068C8785BDDBF49DCCA4BE66.bin b/test_fixtures/masp_proofs/544F7432B95E26E312105FB804BF9BC175A70082068C8785BDDBF49DCCA4BE66.bin deleted file mode 100644 index 1636c1394f086ad2f8bb900c9ad58e1fc5e4caef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7448 zcmeHMWl$Vjw;h7JBxqo;;1HaF03rCGfdPUOLI}>_7ChMCF2Nyaa0@cHySoz@BtXy* zoR{49S-raTZf<=)@7-0qYj>YseQNDq=kz{(kN^Mx@efD&;h@u z_e>#^G-8J_Q*`_9R8_@GH34hiP=DM8*k08+Yu%V3D|p#)vKvYnyChgXQ9;1rvb13S zoD8hF%a?-t39S9D? zgltJUfQy*E4-xJEB7Yz2;vf`$@|YQeV^xsO0OWiFf1}t(R0I*!t1@%+ij52W|FFNc zA^kZ1^gkp|jdgkVANJ1&|CTwx&T2F%#ZHP_y!sG;4?}i(6_-c@aR!Zw-Q-=NBj~lV zuK`q~pW_OoxfK0jNB_uj+l`A^){RAjT|mBkePlP$unq=eNsi*kk>aJo;gF>6AN6z3 z+lGCpQj8VuZq5`D=YiBsg2g>@uPqI2^bAo%hS)rMN8&Fyndo`IHK^y+T_43T*TCPR{Yy&q-=zP9Xn*y)@?VR#*c&&! zG-t9!U9c`_T?yyh%R6b#Gav5`K&AqAXD_AyTeSave}Bp7Pl)zc3!490wD8(VZ1=hi zN)5Dxn*-Oov%~vvJ|p*Boa|{~sQ<{N)!(B1OG@=$r0@NRu$b~0^PKK>eqQ`-mGG;@ z>+hBMzgk259`;wK^54V$>WKP1><`{3j9O=k=8NJ&6V=1_sK*BIyL)O{i^j+FPH?54 zT6Dai&UDwtv2O5;xM*p^>2V?yv+g2k-V(^auJ3I$j??It#?XBNjn1D~8#Id|+K53A zq>t+~iYV?(w0|kBQrh!6Ko-MssG~*F=@}wT;x3DisCvBUZ)fg^1%KC+F*acm`fSDC;leDejC+4goP{LA_70vI zIN@dv7(B#0rZd#pwOM%D$AJRg)TE0a-y?9g+)6K>H=dcjP0u%HN`iI7WbGmM%MGWAm;u`H6Q6 z@%=j}^sY~dW_(7=>UP~wSsW@$n8-!1M)9ujctNZD0<|M0(Fl5wrfz!sTq|Ge3E<0C zKt%)J!88KUp5XS10eTOh!w)`{4m5#<9I#QpGFlS}sGS@9gk5lw+gw#Nw1+sPc6dOY zsB6|GEAuVjGvzu^j^!f54s#9SGgkYYBmbekkp?7IY#fDz8`)06QX?edwrI6J0_!L+ zGOc`p(%rF5iUjMmbw#k4I>KJdfevUt(min9LomX56rOZXPk0igiP=rbvFP4ct!1Ae zRW7A@)fmtnE(0J>LK;}g$nLVS_@3FCnG>GSE&C)w;vNA)%JLQ)irfcR{N6zT6;3T! zsXLpeDfXXsWHPdAJ8jXbVP<(Z1IZQ=W|Fusdo@nO`2mb=k~=A9>**!_RAKMCt04() zWoPn?h;#B*nh52CDt7R&T@=tpawT$eYY3yHbgo(dB&~O5)4XC^(5@wUY;&4y?BpDT zWs4E_i5HH+#LvTxMD^62AxRdP*EO#z1?zvKgmVQf%b>JUDRyR=nz<$RyukGIyRl@`3DHl^z3!4vyxFNb-kj6h zXoi8g(Ln5UHrFxRg3?Gr{r1PVMv7ehNyssRbO%x|Q>4ZuQobZV?ra<=Fus1RJ4aIb za7rIcYR;u*Qyi6ACK1mA<(bnDlS&$sk%h*6XN|t)o!Dp}r}kL4SRl)J^cJxlJH zWLHK6WFZeVyRS9sW`Pa$m+#-uK|(=MhlGmE*0|TDygc~En_IrD9`wwPs8gbf`soMs ztuEHL)Ane^wzQwi&+-!GWK8JZ!_RV`zc3hEM9g^9Xe@BOR$)iVI6jM1tpQYMGF*H^@8LUd z7i98F+!AeYgG&-22l;Jjky}8uvO{92j61lYtSDqMc|%9b4)M_u1ZGo@I+&93)R$!T z^DqLCm3zuO#sm4iAaJC9VEOHlx4kk7i?L!V8c&Q}IzPBuc03Bne679CK3}Hs!!V%s zk^fX&R85B3?Il!XYS}y^`h4I2_@pb=a3eZd(W}+iwiB#~lhH%UQq31)z5>dq$45ui zqOTQgD)&~WvB~GTvs`mHOuf>KRWW|466$VTZ*{Kt4bM6F4yAMjb6e%DoBqO+PuKMI z&_kZxG6@Ng1$3`BZbN08t?-?kSCd%j9fZ{VB1&b~_*`L#vrnHcb$Qv3&r%&ocTw84X%|e_Ks3 zL51ERP;7lbq05=QhXppK4!sBd^_!Xp#o`YVDj?wF2>L_}ni^ZA_07Ax$mPbqSRnvsLo)0;njXKN3Cs$f+aj^D%e-IPd@r8F_C~7ToO4azded+ey7=5f-N9UNLuO?Z zV=>4-Gh79Z$Wrk(U)LTtfLJx%@z}_rP|-rV;7(Xe+l2TYYSq@L<$Ei)2YGTXiZ7b= zGkZ%#@F-ioZFR*{KIWAGNX&tP{p?L@t^8)!HZz|iFJi`vRk=Foyd=amcU=h)aVRm* z_NOUp%o}`S9WfIz;tJ+R?|7dK-%Z$>h~8M{vIf~-5!=puj}dw>5=z$aRbnIEoCS6W zAkMrZ7E!~G#U$F^+Yb-8@xDSWVYx&Xd&Z-MMam-CdBu?=6;dchFVmBncIyRed~EFG zcoc?T*JOHNaK2Q2)O~S|d3z~)zxsu>y=9=WQ1K;dk?+^(g164F#7dTL<2bLqz(Nx0 zGas~N*gq2r^wB3~=P*Eg_)W10!spmh>au<3;kViTeYe-vai_Y9M}_QWyCJ8$eL&15 zye;|oN$b(oHY*lyTx?#Cd1o)Z9KSt-f<_vuR&o#OQ`y)Zn`mi_Rz7`%vlLd@p0;&! zDi2YSQc}5V)T4C@vMV5CZ3np-c#HQrO=)I$4jCaJe3UiOAca6PmrISX2sOVtTS~JL z{y?|rL+~e$oLI(|1^-ZSQ;1%HRJD5hnT27ap-J-c!!qgqp&QJCb1G`+ zoTy|0>b`i%b(tuYx$sw&&k{Xs=dwEl2cB!!eQ>3q*9rY19BAfU{!=*btfV43p+N1x zH34}nIgIhb6&PVzhC+U8_7gd9)0w2BpRULm+0pJz5Rk;HFacUu__`Z?y?Jwq)w0ja z@$O=sx2)4S+&2dILW1#3*S7>k>>y7^)FlCg%HN*<1K*% zH)q4}{)D$TP}?}4rcE$cY&DND4q^2=c8ulYi%bfawLU)nCJVInn-WF?58rm`>MU_i zixSpdoQ__x8cs1)i-6Br-(Fii5vq!`kroer7#j!6EO(u1nV%V1cZk+wQ`)kL2l6!9 z;0XBpnhf4tGF67Q>{r%N=r}@Zgix(o)NbiCsXODUuk~bSVqG$UQvKjIJx!+oN3Yp6 zK?Nule_!1fYBv22?4=s^p)TH|WkCMXyqFgcUQhVBL3A;_(OwqDYm)=Y6r*ZOf+SY zVdV&)YeDjgX1u@A>PZv^&MO3l1GRt5!_ZkC7)2SiGWr$8bj zm%Da_L5y-H+SO{b%o#L<*O!m#*7I&HmabiMwiTJHl1v!7Nz7htcF{IZA8jbiy>+1$ zHLj+)UaxNvNq5e}GB-(5&&h>8pM#RXh`)a0WCzZZ^})((xJAmjB@yHy84%=03{-n3 zsGCWFZKLr*=!kX$nL2l^TUOfP(0-Ca6|InDbyve5)t(qKUQ-qI>Aa%ES@hRW|-;9`RiFrL@t3~f^@oe8Iq}` zy_B>`yMt%OS7_x*&p9t6-zT{O8;(BI8)09Dlow&QJbX;GrreJvZm$#aUABU{`01?_ zWonKa_Pa%Kq(59l{hzG{zljxPb*-z{n#aAvw|)bA@|BNLbPO028@!1&I+L5lD*_~6 z-yhU^*!MD}yEPMHnN+pRBXm)NNmZVsnRg4p!ehwXA$^lUhAvK}ald}0haNuOThyh~ zw;wLJ0u$4xf z)lX&btQ&$TJl3uTjg$(r`Z-Z9ZD6z2C+SPQfFsmny~8cfcc0->d`|N!Oala}T)`9y zZg}`u?)?_oM#p1QgZnLX@An2Dvy7jdSqv8xMrcp1JgyR)Qrm}wXdXGd+{KHe)$kJf z8X)H$duM7Q%p2oxtf6dy5O4NpGpfE`ulmSm(gM}3HzUc_GH%Hy@=)9vMqISQ<@eOrYZ9|l= zcCy(|tXWJb^#zT+2`Z1qJ;lj%G*O3y?eyk1WPUibVa{>RDBza2mApY8txD~C-J diff --git a/test_fixtures/masp_proofs/25FA248C27E805F28102B6E3DCFEAD637C92544211E7B3765CA7AA186FB1E409.bin b/test_fixtures/masp_proofs/553C507BF748CC2353DCE98EB91B5464810F6974083CC2FEE2756ED1E97B8143.bin similarity index 75% rename from test_fixtures/masp_proofs/25FA248C27E805F28102B6E3DCFEAD637C92544211E7B3765CA7AA186FB1E409.bin rename to test_fixtures/masp_proofs/553C507BF748CC2353DCE98EB91B5464810F6974083CC2FEE2756ED1E97B8143.bin index 60aa5777a44a62800525dddf8d444f2de76bccf5..166af873e703183593d4e58d398140d4cf2a7982 100644 GIT binary patch delta 1199 zcmbPXHN$FxiAe0#t)glQ&rD{soR)I4x8B^x?%^{zn@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_29~GM!Xi9VW0!T-6rKxC@(ox1Xu2Y@?qNvN>&82^I_~V7oq4}7DYW>! z`FuHKU+{arQr>3{HcSO>iucTK-0HsnzVBe)+j$o^hYJ8zIYs(8?myj^h||0-Ni_)R^mQ*8dBC@#w_Hg7$r@E+3f7^v6eS<5b;@J@(2ICKRrhxUF7wXeGBc zV}w)MD(@_jg!M~~U(>pB=b3rk>RhE~zdY}seG%-s?&ZY@evf9h3!UZ8j#4+Dr=6Z< zx2o}O`p4?eOsf(yWj-WnFnwpqm0Dc;@NJi;CigEn(}rxzwnge2`zG(pkvS04{C2llZK{dzS%0@wfN>-%{m9`~9dxs^56`rkkC!xjls7Wr-2 zGO_D)?&ke%%(4NdCD-q5x+ZwJan0Ha-%A(O3J)#p5WcxmVTw`7_A~jb@5cZ75xd^z z^~~<&KhzrT-QHCH;xLm$PKlHc%bBf>+0`WzoIE!cWnTQ}ZNm{05y6-?@p7lfd7j)8 zf5T?(D|d;b+!Dj<~g;yK3U;y&96K;n@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_29_rG%iql%U%uYMv`zbzcWaukQ|8&{HpgwN_J8>J`pKgUo1J;TFewP# zJox4Jg-c88D&Bu%Ypy%B$?v*Ix$l*EsnQvDrYGDE*&HqaRCVxl)`cX-@X2oXRop7N zzp3zqDEaMG$(}MtX2Q*aD;$e9XA1*Wt=+UxH%>)APU*{Gj{~A!*AJL?*jG)@m)r0* zmm|n#$?VPbVrtCw32h%g^;d3kPPO4`^eVgRZn=CjqqlhcVY_domzcuy{aW^D>}L8h z!69$L1Nn=g-R6xtm-XH6e$|cJo^qzGIHj@nK&wh{z@k>J)#o!(wOxAGhCG$H z-@1Q^hk1TA>$jql)taok_;1ZPf8%~Fl$za?om5}QD;e{6Z7y&A zcgs0e(UYvVK8VndwB6KP{dVqMcfS*&0!b<}qO$*7vzN@g6a7{EjJYMF$mBJ3ye6&t zuJ<*axjap)^Uu}~KiJH)(|oHWC)yU)S*_}{occ1>+ewdq(}E}4GUuLfSt+%}qUrkM zZ&Th~>;5$Nb=^04-4~)UyPd4^>L0}`Jg5l`5;=MP|J2lD=i}Zj`*YXsR>Y;R?XzZD zZ`SvHFP1myUdKA#h9_TK734E_UzW0X{Vb2mJ0;V6`ytn#|JA%=)c47&^8SuF{c1YX z;mtdzHfQN>J)QsZ^vncl!nP zUH?1t3PS9?B964WEarMFq9h+)bf}fVJR@!^hmX~3#jq|f`}^VSymMc*d|Mc4xPQ~J zRSU1UA6Cseyrtwq+;OYrDGYAGG5LEx%1P#3Gm7V3?9O1IJHakaAvdk&^8zt#$@NTm zLj4;mcYV#^%l7Yg$a|J&{h}vHqe6Sa1Gkqk^@|Rjl79C6LRDS5tF?QFXVv*1j;p5e zI^QxXTJrNkfYE`sdgnslMs941s#UYs%${0N6Z%3U>*K}t4U-L`j-=Rp+3m4x;)~VW z`I%T(xO{tByEHVkTmQphQL|^VkIZiHb7!)DZ9nPp@X_BDSKnBz|MO&Sj{o9`jsNSa z>ow#S&f*iSU%vQ&=25QcNt_Pk*>F>?OGTTpI&{(9vj75`YxT7iFI9kg|)tpt;p);q_e46r&DTj`{eoeH78yEr^5TW zM^-&7eE;nd&c$8l8cA@T0EWZ5Puart$0gJAQGd+ag?l695N^gmGj8o7Gc Q#yOMT-P_1M`H74P0JcUnPXGV_ diff --git a/test_fixtures/masp_proofs/6DD5A788D36258E9D5FEF454DBC89879834677F926130D56EB5E4067728EB5CF.bin b/test_fixtures/masp_proofs/6DD5A788D36258E9D5FEF454DBC89879834677F926130D56EB5E4067728EB5CF.bin deleted file mode 100644 index 94f997688ba6215afe7dba75785ff44eb9cd9d65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10382 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 yQm)ULrP}O8+E~!gC zLNi!nTYZd;h61lW~xqRP!b@sE^ifbTDE6iTm3< zlpn{R_Q$~s8wb6(zWpcu+3;_f1K8!OFsm*!i-P)u1(1DSragt`MfTRlc-R&X7z@tQ z5tfB>M--eK;3(AmKkn#%vaD;^abSvGMNL>9F^PUCa55a;QPhr1%rV4PDYa+RTg1OY_}lqQX5deV_E(4Ze=S-f z$l52JOln#BZZ}$@qwF4_4;ryX=(S+4#clun5uU-nMf;bO>c4~j6Qcdq|H^+Y+Oh!r ztctv;HXWh*@C`M*iX)!{4I)_xt-xdVfN+zgp1z*P?Bzo5F?E zZ&K-ECLxc!ZxLUCO^+-g`FOcAqRt_sSB`&+_Ae>be+7;D!^2@IWG(>T?)|*@+brQ% zi`U;P^MAF5_&x5ge#(E3`>P}B_qadeMjx)i2gtoEO_y2oJ{u=`Tt2}OoL1qa!WD!c zIS9*=d%!pQ+2%MNoB7y7eQ6{}LIlodi6MivD7x;GM8#0=&+i?#Ko9jezdwV2^y1jl zFy}j9buwsZ2!vjwPbDxZ1_bIJ>AF{xd&Mz2seB~p-ta1Nt_HzBqPJtAe8$=`!x_)svOS0K z0_usbJJSqGg-Uj=@X~+1>4ifr+b9&V&1O|~Wuq*gXElCVE!!GQv>AkvU9y2Vyed#a zNLLV6m&Giwl^(UU~a|ZENBg33w1Uf}=hKAi|EPZ-V_65R;*zR0%X=7ik zKnfrC@Jj#S>A}wrcn8HS8iq8C-Z&qG^Z@XJjpNq0A30p4p)$pwpU4S<)}(^dz07-f z`$qJTdr{yvk&O-4*{5ettx-f+);zu!SviZmNE$rUysExsq*jKJ><#Ld5sU5;8tFJ50H zCsCK^dW~lvhJ}dLHuWFJn9{BEx%EIh+ZRwe7!xWXBcZH$5SpsihkFvl5&jq*_jcW&*%$eU?cw{tqp-Q5KiQNZH;h0`r^gISZnQ|??FhD@~s;9BqLr{2Ibl|Lu<)(@WIe^gi@ zrG6CqL;(d-Ag@my#coNh;Jr)vmFn5X3`LiJX+)6BD*-2Y8%XG^`O~fZaOLl#l3PzJ z)_{HmxQZNeN}cu|2Il0~sY5L8smn+0dHrIxw4DwFP>gWS>yYi2Fbz!ggbQx>)*1bG zV~l9Ah6Yq6mL;4P-#BwOXRjQcvfAZ=1dR@vN>=0ZjB(dlvO1mbRvNy_tYY&v@X97S zDt|^5SQUioW_@E4$)ln%q#Ty_R9uYY`p_}pX#ywI_J&(t)pUu_J2b@0<)lKgAKW-d zgTZ%S#<+01+Fa+0;@&Z6=8e$7ap`dGVjd&yqZhw7!ryi~xUz4nK=bv5yz3X8_mFzj zo#le;b5k{Ktzx8lKCf7OyaQ?Ic^o5zQUYyta}F@k8l*r=f09j>Abv5pCSUoGb6oWZ z_TB7(@&%0UMR6bB7)rR&kw+=wY!TC`hconm8s@?3GD~Z~>oUV6Qy5I#BqP4_MG_U^ zNQ#rYPGfG-TNOeQ4Bka{FTLoK%Z6i;otA1E^oGafb4~5Nu`X&@dZ?_SL&V(em6qI! ziE0odih9ytGhO9*Oex;hshnsSt;TRQg3C=nuBES6K(8E}<1P>Yb*3_)3*#b9FH72+ zDZ2tOSL!-Fj5jT&Ej#k7>*{5GR`~7ZMPbN7=?HzuD>}PVBUOV1T5lh5SygJV(qwt< z=Ru!V5l`$1s zavv(dVErix{pq4Dn7FjR92P!{r)$l}IgTvA6u&K?!rA zMjOvdHa9vC^zq3fjOlBibzjp*Q5|oCG*sg|VGl_oUJq*&y>+;8gQxGj#unPs5-^@w5a=VPct>T;T>w%ujrMbK~dbw#)-x zM-k{+tc6AtEH3N1%scj)Mjc`TPi2IdRa+#%+RFSW^#v>9)MNLEzsj9jshn5q()+q> zrQ9pEYsfhWrYS#F4RtE4xQ~u4drOnpM4y>N($u&U)p%~Rh`lVkcQ0Nks%B$@aWMVp z$RD<2BD8_dja$b;sM(V|x_1h;EsO1~3U7daJDe5?W399?$V=}^JKwMim2A?>8{gON zRjz*6Rye&7+)W8TJEdQdW2F7?Fz(V2+=hqrjZ)#+v73vwgRR6!9;Ko@qy6I7Rbk5< zS_RYttjlAe<&}((CKr7lS{ah|#ZGbtl-M&JhJKSE3Eqi5;4T%k1n3fWzGQi(!Aq|) z&LmuSxYZ9guaBImbJ(3YVVOY+$_wjYeNR;$o{!MqbeP58Mhi_fXfRv!aK{X?P=Px3 z2$|&{G#>1HN;Er;mT0BP$dF65H>p(2E=wL( zi?Sy%*bK%FW)T`IoM`a0eQ#>z)=PsbYQ z29`qamhrpqe`?}c5J_*WwJ%Y(;hbcD@}9&_O>!v{%6oaj2$W@usg5{bYsQ{&;F1cR z?+OxTi8_!Pj>NdgOvK@}tnJ*UFc^3NbyK2RP=sC&OcK~~IE$*i)xvn~U0mVBwIBwq z(0jy>QjJkgV+G;$UhiT{0(saI(ZRwcaU~ZnJWfg8oYgI`&``GZr9Yn*P`6T&&UoEb z$B~k0>@UnR2z>OU?$J|X)BY>150DEfp*_MQ|F!G>Cbe*@qyaHbOuL?tX}n;^Co$d5 zG=^bo0?If_SQEu75k!^mR0`X3MU)g<5V9UnV=)B9w|!(d4cW`$B zhs&qKU@XUeN3pHkLouQ(<2u0k`vAAE94nFxl5Gk7b0w3VshPcopgo;zc9+Dg`-j#V&-5n?0&?+b#pj*vS}?DtIKT>yMpr*Hu%LiaKT_ z*k!l38QTp!o$GxchGLZ9MHZ91WuG{=U-$e6ZrSy(lO zr>X08@D_TS7Ja4A)4=;GOvPT!WOO0FRB-aGI>d%#%kjSXYBU6)~l zI#CQ@)FeCF{=myo;BB2#FQ-Xmgh zVM;;8oGjWI2CJboy}KSim`2yh|KB~FPYR^_uzsrt}M=$a4i4ZJET) z6RM0nAKc(2X_P;nME(D*hK3`^SJ%RwJZUG12xLH8p|)|2$%Eom8XonRhTTu z9xYtMRkR$aN7PdR@YCw?O*kuE^G6qdmCPX&Q5|!5VJ}+nB|8ovbs#w0rEPDJ4=K32 z?%JHSov%qQ&#-1#tAmBl6NosL2*9wuv2<6W)M3)=cePkYiFdxx)e5_Nml>xS^#b(# zUAExeNXR;F*mXy|yc1|IZ)mc*YvP@G+5THv?oAT5{pq!2_L%rm9$VT8kA zGPJb5mKTe;;Z3B6v!{vvUJ$1j=21DF=&-3J7?UsEyb#ZQ=WUTMy5k)Z1Q@*(TevwF zPEq!xUw^>gu-!@(!sMVvOhZEUjL*`Db<_b>lUiVNJrQ5;277!kDi79Hvged;z zOX)dO*qT_wQyYXUci}c3LBYG7*{>k)Ky5Y%F&vM2M_*^wOJN5<42y+;3Rs~NMuT^=lTqz zyRSRL+SLM+n{}L!K+@SD+vogMWb~5_a0G(j*~-hqwvn_3#$Fbw54~PddWY+nxnB%# zo!JzQ#m@_>aKdBY?=R6l)_;|n3bc$DnY&QFjozY5vUWY(%NYV4n?y?0)-7>i;d6f9 zfJO}t$k-4ER8O;{T9|c@Mr5`1Jd>uevX81NM8$zifpE zKMX?TR539D`@R$su|AeW#5?5Ukx^U?#RisV#&m}|ItpyGEG#Zp16)u3WP*c*LvQ0) zo|c6O0$f)y5lUjA5aY??`5b~oPkFPag3U7kbNT@YHe`SdhkZcaOI6?@r)nobKRX>U zHajW0iA7^g7F4E*4>ziggO9*80DTf@;)Q?OGTx%pv2FNvp`!cFjmiNk`-)!%OQyt|m^ zzJL2^A6P8a8a;?IsRcz=Ss*iYE%PFMH6g4G!~!M0umz8zy8mDk6Mw+GNrW_P<)x`5s|u0ye);3`;R>~l#n8PFE^mR`CSdBt3Qmu}tsxCoZrAHf zMBAMCGM>%tELAY(kzd%4(}oTf3;@^=ejal;(*_>l`o!`r8gntJ-x@@in7dle1X^5w5sRa-BL83!p zE35@12!u`YCAtnk8uF*an3O9I@aCye9G13m{O#nqQsZRtrh_XHLXwv_Kv4XCE^$HR zxK|9Cd$kZdVNP~8y@B;s5LPKE(e7ubHK37bn{GR|OeB5B3Ayd=spA$=E-R+7d+B)HVO`)b%AO~3u_kby(cgV zk{m2h4WP1>1B0oOO$k&nVkglAggOzMQ(H%?y&Y;l_ZL4RvThc0*r>{Rs9_tHu=eGf z0wjBvE^m*!3-f?I_uU$HdgxAQt_&$qyvZ~WW}O+PE1tGqKNT_Umsbo# z^~suVk+yZ2H7f+q;`9OQ2$10nl^8!DV#=!+akfz5sM0{;;Z26Gsm_>;&jYyup)8u( z!s)tA?=gto5T+v7X zKHqBdyF*3gFw}J}Ch@s0FQ^OxNOdvuSooViS}eV+0di5AzhwADERN>`{NDmOSA!yM znB|4kQLq=D;G0J5XaO>&lckM*!k3+NhSMutpal`HljfI%)I2T~wc9jWt%d+?bu<+o zkg_-`Q@}P|43_YlHfUSgwuTiDtx=*KlNls{s{q@)Y@l(Q)W8Z??o|qCE27}(K-V-^ z9Nt5Ghzbo6@MTQqmk(QjH&G2A^mZQ#)l!GKO3BWV#x+mtRa8l;HsBK7aN&O6#u)Y1 zl$==JbV`Zb(m;e?lJd4UqZM@7g%CcuM z!Y2@>f`mIw6|q;($9WK2+iH^@l{@snds#FD1$^@D$J^bD75je{*x7xXu(e%t@oDZJ%9b530L#RT=I zIR8};Z!Rnpu@rxA)&M&=L#u_ytWMvod%@7WV2RwtRYB3eoGM#AG9H~zPpb5tQf8L4 zh7t9TeQQ=flc;toabriP+0fRh)yi+0hjHS&eFOrO>)I71Td}jG5yn!|k>8HS>Vr6p z!^&Kwte+I-7#bn7eyvQfKrle6S^`ZziJRu~YeCj1$6@Lj(IPq{40do~x8R~(`XYi; zl0u~<;LP<7Bn0}0!hb0Ihr<73DSW5vZj%v&_ui|&*77TP#LB&lw1Z%NC#&@P8c(9P zjTN9U^XHFS+|O1|is(0GFP6)wu9F1fJ^lEEn^@38czYAT?6VkF*Cbi=?YOjpcv27; z-Pm;rwJ5s8x5kPwV8|w6h~M=6ZT|;R_%dw497wpNUd~qKc@VLnW3$s0H{@Ml$~Sk%Y85M;)7UY8rHfsdFE(JP}=xRgyNglyK> zWX~==T-EOo6#dYo4^5>(ys12fvTIsC5cpG(_*0SiQ<3<8wj!aK?G%L%jDIjvbl4Hq zvdpmCuXNeeG&HwJ0*M_P!3qR0bNHO84J;d+hVLd-T;9mCOK4Py%0ew+UWx2?$prxb zn6yqLjlobgE1IVDibF7=&P#%@HT>u@+=#6k_;9??%e)#kNF#;D6pubhryoV|p zGE13>X%Gj3CG*)IH)IUlnjn zNqg{s9?HQM$UrAQOkvef53vmbkX7J@eqpM*qOn%P%4WY)Shb7SV+# zJcfl?BQ#!s_|*NScGU&I4If<==}GN1sy%6Vf_V~Tzk+yXYs zNQsKYUutDu21a@tQlsV-O;&sWO*2%J;>&$^&_i$?Dv!Qg&5q`JK%8^LmXL|%z2YVs z02qOCSg!>lU}b1e)AnZ^Jl3H+Wxm|_8Tw5@VmJ4I4qzN->OJae4RSRN=b(3tHg!Tn z%Go4@f{-E>uhSv7MYQQ}V*T~j3;a*Laebx-q{Z20ul4o3n6})XV+x?;=ER6`3(`)< zvW>n5{C*$?x#n8bqd)`5{{%oF@>a^fmQFk6=cx=XL|Ol&{kj5mzM9S9=)o1Y0(C{< zV*x0~#`vdo{;?kT@VA41%ztJC{yRmTl^y}28Zn5MIim>NulymD8bR5!sYSWm4rUJT zymGejJHr3AsQ;+0>= zq#P>vJHr3AsG~wP)>li?(Z!c9e<9U-q1&`u-eJp*1}u13u9bv!cmEOf?@<3|+W$^b z^Qt1FjJzFt&k7qotX*95LFCmj^nmnXd>3WZyUP?G`yJtbThx6*z=HEmWE-Nawp0P4 z))?vinml2|yP7R2tNS=|5A=UT{X5kEnf4z={lE3l|Ec%=Pi4>)^?wx={_XA#(rM}r z=0IoQ*PFm>GEc}C5GgDR?xav*whAFLBtjU0KcfE66!UMd|2swfk1vX1eZ+sQ`)`YS zva-g#IhE{fS!?n0e%8Cpb7m7$G6Ak)2C6u_KBsQGTA z1xZGa*WuBTkW(g3^Q{W8KnceuuIQa@2*1sOP3_ZPajZZD_xz+f%Bk4SP9(Zcm`A55 zO`+)`X{xGuNm{$*Su-QS(q>%G3rek~xCVQ!*Cs{}68|uB>{g)8&O=^r;Xw;1x@KrS zSQv5}QiSjVb~Jp85Xp$Cn?8}iY$yq=_&RE{%3n|d0yu5cPXnfVb<1C~76Lx1FgKX* zlxs#A7rXW9H%w}e7I-KLEBvzmUAa`emJXj!x|9n#i%Ycg=wTL25a$S5uQUX92|JVY zk)O%YUXnUadsb&3BVB|G@x-Hfyc0DzYe^`#lF!e5<*^`_T7f)x0dme^tyl)W0k!ik zx~FQuGZvfgfYEfH1xQ4WsX2!NT11w!tik}KuFvdlU5-|W6zkn*9U1m;;jyyQxw#PAGFzxIj`8YV!%Px*VOsC>wtnkM76eqC^qFe@&G{)0Ye5tF* zsid6Lmbx#9DliwyAil5Y+{!;lOWkfI`HQA!ZotBZ8K`0Z-Mj#*wPlT0wE}jXfGm<&I)_12#H(gUoR})NFOMOK6ZzNh~TsjW`;*VnkgrR=#ce2wYpU6!KveY zG6y4M@s}?afx=^`veecoYbEQr8D^iJ7rt&kcu!Xcfrl=@j5G&2bFgnjy|!=iZ**c- zZha49L8PP?nNDL@PpElFkimtOyg!Pm@tTnsNEGy`!q-ZpkOqUXD~2K7JvrzKB=7 z0W14RX^XTU3bgX=se4aukYj0Z^%1WJqOHMI*hXSk`=-RG|5{V;dmr$Iy(!YJ8ju$JThAm*{O3kl!=6U+O6Vk)VMX>&SMtM%;2jZ_{y{P=?6z) zyy~x{6hKbEkF zGk;Sek@=*^C&3>cpP*4bQP7Acy2=1FN#Pc=F(h;p=~sA%W#FZ)vx|gqF#?-$9g@t_PG}Z9qR8ErLv?jkPoNaBOi8o{D#chGF=WT~MtMX7J{P>pc`n6hsyQ zQE(PyKL=xwr|J;eN5t0Sg>|9mUi<*9sy~}VV_O?V{WjzfeQT7o+ zy7-?~V)b!ImYh3$=cf0G^i9ah5H^1MEuF7PMMIskgoDi!NMJs zIH~kgQ)<#}#E5)uXtlEx4fN|TG8!jSxp=B|vz~5p>)u^Vj{N8O={b+kKFO)$6NmIp zc4pZ~)$64RIgLwif$0e}TsjVKvC#O2Nk(sK_xj$tK@EYbDR-u-Z{GuG)8~k}+pV^A zSw9`z&>Bv`gj`bTjLAJ0UAu67ECyTmO#M5V7#gdf=4bk#x>5H*J?O0-T7LQvB*>)A zpAH5edKO~|Uj~IikD3Lp31h9uAwTlHqt?}`|LX75dLh-;<`f=;;?Ln6yd$&_v%E<8 zUIcnvJ0TYeub+J7ZRDmHvRY5X{K4PJ>JbR(=$mg}z7SD7u&Mny**dlx%$f(~F(qXF z4RjjKExYVsZ!=GL+93zps=`}g5wHP*xMGh(fWUpb0pku*rF~7-S=iv8874P zqOQ8@sm_>yec7K}4xj~_@)mYcyTK4#uGp>B!-$^6AccHmQDBQ z6HcZStS##M)txij%9?oudbY6`VlyMkwkAZKhGntq`7vjYa6wQ@Z#z4rzM^Kkyt{!d ztb%ydRT4N7H55*uNc<7sK;jtdwRD`a0EHhs*4$;Q5`&i==i|{Na{3fY|0vm|>Z{xv zhAFuyp7sn`%*v1pDWwB<$u`eBBvV$^8K)ysFJ$S`D)9=Z=m|Q-jE5NB2R0kUw)-B& znFXsBqTJBWLw8y^kHz*<&q2AIA%vB$c}9;E;1sXC{(kDb(^;j>uUn-s+FF}^xY>0^ z>S8zPACwP<$qHv0SC zp*Jewf>uH7Uh1NVs?@_~v;%Kxd4LUV>P)0Udd0dRd4Q7L2rapZvamSxfM)k!H1cO& zqak-FtMebTVSa&G(q0E36=MNWo8{~vG8=3JNRQ3IOW>Fj(=sHFvb~JkRBu{7=QsMU z`a8ZZsRoW?G(|_#gS(#70Zbq#95yh7k@&HnhV-3tx zwboNrGGZeEXjYB_ zq4)-J?FSjv2zUN^K|lILR`KBT+0J%9gy5D#8^nZ@cT~0iKgN8#q05xhnjNm2_?M6lnk(MtHZaDO?Zghnw*K@jhC*S zJa#+)L7L0i<^~OqoY_Xdxt%rm!m>kCuT*EyGb*iW_@QY?by*}RlOc(a4xS7NuO;P z?Qw~!MCEGks=FhZ5wNHgF2KsaCqY5Uo8G&zCDhp^usa!&ZWn`&_>w=^r6li|yCb=k z$F0cR{gT2_jzpRfwJxVebdY~$YdZ~yl*!k+ZP($+KvM2q&l*X`Mc%|t=_V2UFQG!} zu?)?Sb|djFpr)Ii%hy;H@ptkU>L5>eLP&c%eIBaZCRq7U+)75yJekGHMx%|YgImsV z$i`>NRvzN@5&T1v$GRbP?hGynYCU4y6Z=46>1XNr-q82zTZM&BbrJUKkTY~`7k?ZI zK(S7EWTg0t?Xe+x-5mX-t@HsY1fJf7QW_ULs5-!WFxhxtfldbOWfO$ZLfWga06W-T z(*1+(iGp}Mb`#z19xrFc=xo3_LaT=|Du#BNwgEu9|x6&I+5s5@Q44eADo=w zvv*e)6N{mD65@{*2DO{82fV?BSYFSs<)qtj>Q!~tu&c1wC>5*jdBwY8WP#TC%X)Qh zP`=+~EkO)T%q{7v;?3p6{8(KEF<#EOjfdyD=9A@)ojHza(Op zYRxKSo7Pw|O>RKXPOWscZKrhAxi}OGtQbcw;7#}}uPE(7K1|c>M;B5iI|wQ=ou<@$ zF2+Y6D>p+jhx6zMX(`C8?qPihU{{J)8U$Q74I-wO0Gn8_BMM`Tw5zs~N2p;tX!fwf(eNTY2qX;co zLF|IIDUU4r)JPs@${c=9c9n;XAN*X;>87K`j-FhEA$D})#12mfpM&z*} zn3|o)6&q|sd+|v)4z6;6O&a2)=n@lhs0p4re!r6;aN4FTI0Y7881WSkY}}z@?uz^J zx!fZ`yG2vD1oL|-u_>s>(<08C?Uk$|wMy$YkZJp)?x3sq_&t_1OJRT7D5nqQmiRsE z+AaT&Fmm2Tpam&sR!F%os21Sxw!E80#Vi>|a!Df6H{ksl8txc|{Yxu0+HfZh5;|B~ zz#ps(@{1x?e1W`o8f^IO1AOO-;}K~-%R5yqUcNt#&0)H*_S9dMiwq$mmDrAQV-IBQ zEd2V+9!7Z{;QVbVE?zblRX*D|MQfBe#v`a?u;{!dx>8vaqko3+0zRYk54PEDpW)EU zKK)goNY+^ha4J$iwtN#sHKBh!BB@#$T~$UbGNH5>K8a+fu`kEnE zBs*tyu)af#b2JiYHH(S;I6aIgjPxVTUDz}Ti`xg8u9WYPUkOA_90i-hmH-qdGFh~- z_du`temX$Iy^lxrn3#z(5o8w1rGlaKQ0MCP4Rvx?ZFWW4IJ*1SyfWtzU*|>rVcLtX z*~?lle*6_oxR{$k(WcB?>~}SWS1wd+^f71Kb^DXs;fefLdshqX$xWVSOnU7XIWn5@ zh?6yZZhH_GI?e=VnxE;6|M14@V0wy@G%Y^FJQ!-&ckB(5A&$2g^1 zCne5Rm2SBPepP4a68VO@Ah8RI@cBkRejDAa@3Hq0Qew~))>P}R$&%Ay zSGIwMzqx2OmI*#8?J;9#7LEAU^6ly4NP*w``OdZTd>K4&AhnMJp+zNl4^?Qpl-mM| z*UXylkYfp)i#{gGV^UZnJS|3kDYenQQ&i? z^QERX{L<*^c+n1WZGWdwKQ7qMDDu@?+0zbBow{=%A>Y9cJDA!7#HAt$N}x$Y85ZQs zgU?E6C4MN&t+CU%q65^z)eQp*?PGMcJNJ_wD4(|xJ%IcNd1eKqjuDidl!VR-OK4lt z6ut@2UdIZ>PAE)V%O?H`f`C>T6XF(e|HA>N!7!!TtZ~2|MmNa!0a zd5UVu`{iiP0)AfLsiOu^RznA3X5Sk4qG623bpXPQHRFO6Rsb0L92;x#T_pSFcS(H{ zdzc_YcgSW9w}pm}E(%&kMOtZUe96C;c@V{j2c94`^UbdnidY; z?>j3%#UmSdssa|)?%uJkHSmRz){WcIY+Vr$>cacj zm^Mrxv_=<9Z_&{X&pQ!|^(!T?C(SV4X*6~WZIzz@dA#V#bO-DTPR(~P=xiz5erdvw z@abIy*NHVZ3ef9dT_lK(^cYgwkWiyk1Qvtk>@Cb;M>{L2qQInBG=UWXT)bdejZZPq zg6bOfX<-xW`s*6=t~5yX$jZgX>u*tdHL^^hB&uVy0s-RHD`aJQWKPKu&{4S|F#nHGvhxaMjZt%0^gjS*oaQL7pyNGTk zr9;ZXPkFp0FH$c&y6lP>?%eaYc^mom1*?ZEIto*!m(ojaMom1 zzYP|AAbQ`Z2Vq^&DAxb70qloMHK@GRMeZvnm`VPWdXea8EuzfeKcObRfuu`)5DL#m zpVA(#Yoi=T#4}EbD@&1Qh2dRdlCNXDp=Ofx-%5eP^(PxWS8^kqzAl+hDk1w0x@kAd{6zq1FxJ&KivS5`{Ll!o+ANUK%`UaD;j zsR+;FA)27{0VM4;egJ?&aqp+ve8jZBt^_%f~i8_8ECrE+gm|jtM1(&%5rtuxJyL&q}(MxnsNHNL?sGLoM(YK90{#@ z1jm+MZordk=RnUpqYM3tVEVk@ayWJH;F+DCI|;Ou#qpDO$6f21Vp7+q7wuE1bcwT( zCVSvOa_E_hLcQqFPwA2rY>lT2ZAXUD)%|G_IEVN)*B*)GHI@-~b}UbP zcNno|Oo{Z^$R&#SPYN1dXD*B8kYM{yo1FKs%wN#9mwNoBi4e8fD2#SfwD9OmC#Sj5 zuKBuU#rU&%xg-503My(nky|v6bVYcJ_@YZsy0Nu4Vh{C9aLA>W?DWe4TRh*O^K-%R}1K8sfGQN`MhQ z+6JAaW+u(Z6%j+5RMqHZYSboX!R1$HHfBf);?MIpYgh?V(F^Ja2e_8@3inil_0GZ< zL9A^+3pAfBPH+%es^jpn!^eCg3-xSGK|juBVo`=6NO-a+Y?qqy0vf&At=vqm%b6IU zt90b_bg+8tX>fjIj_#dv{wmo+#xWrPiwL(qtdV>Lc@9u`fkOI#K_E!}MliVQunpF% z+O-iiP%MQf<`Z%fHHI*QA3WQjTPnP6@`ShpqvqtS(Y%le;WK0a29N>%hJchxHo)?P zGsSaT+;M`wa)#cN1?7d03!5S>Ej@e_@soZ@B4L1|D~x)z({f*_`y$%*MC z0H1EYvjSuG+6m3=oxg0Q2cBW^L^bgjRx&0l(&Vpz5E@vva8K5UbonZ@s#LX}uGr8W zm+YozdI>}5_`uFWQnE^|NE4dZrSDNKXGxn!IjX5{r1RvFKW;#}ZtBh4lkc`l`|N!Z zbsE46^=-SZkJ0$1*Vf^~DFsHPp#4i>O-3iNzkVRx=)<>Jd`iMweUx+OMk3v z9yuAQOGYmOYwYTy>!@bupYe|Y7gl9IJL(;T#7r3JWRZB*-x-;Ex4ff4hM@X;tO%I# zD8x-zIc0#}bS^6(D%N&R5039;+H&8lgUegL(@jI5;aK`btGOpj2WFiWn+p2?4ULse zww)r@Iv+|gdfDfyqO|ifeL=@B7|duu9>eBytju_1tN&r*9irL!+?5qGF*NK!D5LYGYM{D zA*OSDZ<$CDO@nTf7=TOM1Am2efP|kWjspV9P#mFxcCX2E)B;U6&6R+V>g!`Gh5FWH zAca~ZSkqDuL*zD9pQ{->kkT~NrlOl#HO#A${Zp*~58h%=9d2BxsZH(JWQ?giqy*8- zCrl{*_rnXlR|Y~ zzIJE7!)~$ce^J>t4o{pb{G?3wCF6wf{zuVuvH}M-hYJ8z{pl={pSJhG@%vvNd*%yd znDMsS#;|-$^oVV;$+4K38@M@J7^rH~&!t-ryCn+#5-U>O+;GEKbjlp2Isc}wm?)$> zQ)=$yqRsVUs?7CE_vl`1ohin1gOA0!SDq!M_Oy>u?h1`}fi0JETy4K@Q!{s1+1Hy~ zAyLiu_uq57j}4K3`P>&Di`(X6+WK(G9sg_QUAJ7bm?szLTz`0S=aXYkeR}Qg$k_6j zsDAl8@shL=Ta@~l(|0FaW4Tl;_54_q39A!F@KcW1+VB>csh9r**RM)edw!U4%4v_n z|L>JH#D?`7wb&(%j?_5LwivTxDf&d_Sz zzlU$dth5MdJQ_de?6hMCuQD~(i&f80i!|xDX?m@BUUXrjvs~L5PHqi@$`w6R7hJq_ z=#lXoUd9C)hmopd2!TjslMulDUSKPB7Xn4SMR+IU*#^KbRW z1&!111SkL5sW#{M^|XKQvg?%3eOUfb!g;y*e%=H7d3mB{9NoljWF0Y0@{7IGl|W(V zQ)@2G_pC9Iby+RAL0d90Yu2tvZu2TTLCI*#y^0CHgq2P`zWpi5c~Z~Yvpk#r?PSpB za!IOMX*#p{+}hKgcNlKOD7G$I_x$g2BL#NrxcZ-y4*y==pgWb%#r@ye{;zAKV{(p9 zH{?BX+T&%HY<1?Qw;R7)zq36uL+<&ub#a@QhgC+JeX3t=(VtcO`n?U;<3s;#C52a{ zn#ukC5b5T*Y?4LZT;HB|@f$QAw=5OOm%Y03*t>Q6irzl+aO(CdyV3Pu;?cr4HIq2@ z+8;^yAL#IjuU;=h-X~K1=cPT3Q+>95d|J?Yr>soj~9SCE_DcB_KNn?UR~T^q`0-icIBU~XTmK_PDg03;E}1#G&?@;@cpIS!q#`=Uo7*q SG7FOu(@w9E;h6kH#smP8^DV#t delta 1200 zcmbPXHN$FxiAbFH)`>d;b+!Dj<~g;yK3U;y&96K;n@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_1{Q_r{zYGP%=A=*{k)R$7XH$ayW0Llsm=Z2V<^#%(!aJ@26OL%O$ox{EB zSc{($>*jhfRp$CtFW+$s%5=QtJCgZL_HfO;gJ1dV)y15@on8~16J6P-_d;aG-qZam zP7#_(PtAQ|FqT0@x(5_ z)(J1#*Dct8Vn);(!yO|CCj-1N$^`qj30{@%QJ ze?`9;4Hu{9tqc40I$rLwVafdZ9wFW4x|+$;tE+;2A{_&A*u?nQ*5*o`&1ZO@7qoF_ z>fH;X_1fmO)AYCWZz>Z_2{O3Jl3n>59A$RoO>g#3@%`;Iv*M+4a(#s2 zgf0uyHBk?IzP)7$?8%J~Nv}BSu6!@>$OG@)b1qGfnj^`Py0Y+52ixUJ&QG&8bw+yb zc$T9nVD(RlO=NPq>RUsnk4`7vHl16)a>tJpiEr0hFT6>Z&5`t7|AhAH+*!#PVUdDe z<|Q9rNR@`LCwr|4ws1DN_}O!|)%_bS0&N@WA2l=eGB!qvo;3W}Cz7nVK+D}qa>JxE z(r@pb&$}YGqnGd6wnvwBmrQ+jv+Lonqc_W(@7>DO;>q05`Aub`-)GMKAD!9Ddo*YK z*)&I9bws=j@k{{ z#m;Y<^w-uuy5pXsbR*`3s(bwkd(nvVuN3&-|EgaX^-1p5)SpaF&$eWIvC|W+zRCDf zf7{Qc-LtK(_cF#LzI(e_$78q3i#-RJT$z8CNuSzd?Aaf$Hl6+3jQj6*9$0niv@YV`O9x(^4J!;FRS~O?{b9ElFO~HE~%o!B4$u|{(m$HTiiPk8@t@0r_Ly6ItvEu;I96>-Tgx%@7&O=!9Ic@tkC`wF+v R&y^GE&!6z(n*2n@1OV|0FDC#1 diff --git a/test_fixtures/masp_proofs/0AB9DC2FED12A93FDA42447E6A14612B09451DF258B1B593CFED41B0E9256A2E.bin b/test_fixtures/masp_proofs/978C35E058808D61F0E265D72DE8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin similarity index 79% rename from test_fixtures/masp_proofs/0AB9DC2FED12A93FDA42447E6A14612B09451DF258B1B593CFED41B0E9256A2E.bin rename to test_fixtures/masp_proofs/978C35E058808D61F0E265D72DE8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin index 7524753fec47fdf4c0e73b22036c7a19f82254b9..06926fb5befcee3e11c47b388e13801d856b5362 100644 GIT binary patch delta 4215 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 N_Y;JkJPDIBUwryXE%yKb delta 4215 zcmV--5Qy)tzX7hl0gyNp#SNqam2PD&_iLuZ{aX!p$g7dou}l_5HN_320+nuMF86Du z!~I(gcgU-e)(XeJD~zhgiAMZbB`xtABihnOCq;RHH`Y}7OssTGh2N8xMnE7%5}k6^ z*wKGeE%WD&Tp=Y6x}(D#m9r2id~oQ}tzW&9_eMY>mTg5lUGMTAe4SA7mEhPg!U>g zKp<0zZQkj_GK`0h3JMG#{R-~9&7$5b%OqI|)*LPmQLd90NI)P?(_2ML`KA;n zuX9ATlh;T7_h>!7;ZQfiPymzsd&*%4kYs8<;VS*({Oyvw=-2| zC`W-#3_9cg5;jm9XB~QtO7rX(hC|73lDhgY;eQz__tEM3*|>1?wK?(Zq~EWj$g(JN zptDfqtOlU9QS)F>6-YR0s&cJ>5O~8cQqi2hR8ihv+xey zE;o-iERgkYJd8NmE<-OQ(MbL_i1CXza_cu(zMo#^hb%k=Uy`&vmDjQgg%hE#$pk>@ zW}%338c8A*VDNQa;by3uG(k-?Ta#a9(#DjPmYmr`Uck|wtoNc%s8BI`EpZyZ?te5# zj^=-X>L!J2`)M?%E=0U1@kht&>h@_O3S6@*q`v)EE**Ck1C8n1$&3DdMVBY`v{9a% zzwvq4Q@R?A+oE{|-i(UMJ_*a#21HsWCX;8VTPTooVBhH=> z)_G$6p`;GC_xWyN)U}Ha>s!|vXn*<=S{OCzw#lhw8$;-G>dLg)ICUt7emB2fD;7g4 zi!`3}HE%yP5v#(jn)yg z8khu@)OnncDXPr>try&KEO0Hw3ivH~oK1k`p&Qyp7bmKV$y9*S+n!P%Q-4qD^oLOP zG9v>agF|5s@bctEAR=>HXpuo_$<&W4So5@RZaj_)B9E-!_w`KsZox@+qy zvsx~Lhf*Fdxeq<>{mCRe(aY(CJ?2qS~UP>zclA($7FRyc&Fq7=6d0gu@HNUNrpr$E06|3@Dj8OWN51iHbwt;z09&-(@Oi z0@H|b?WD*V7fLOW1}8cxk2?++9QE1_#{j&I-$BPPmd4azaz>R~>VMf|=;DB1Li2rz zmy$kAObj(i1cd$e_t5+xb+&5Se|*|M`C;cy3kh0Ceby;y^AiyHUDH1qNe%p`Wlvkr zHujxJozKB}j!SAPwdZ&8A%?=J_T@n)o|T$EIf1w`| z0ZKH{4Q1V{1h8=ddw)i@q!tjDRspabph-W<97Ax>2P(Fu z@|<9}d^+(o%Hb9i;7sM-#l&hTAmYy-&U{Bv%6}8iLzY$&fC&(C)~YBLznTH!^Qmm3 zTtBsH2n<2m1UBNq`i5$0-KY5jEcfLIX+xe@=uo?!w9f`tVt;uwGOW0qDhiUD4dHGg)|OK1gMD`R0nBlBA+B^d ztwW*{J}4wNnj+NbLOqJ^9-7rx_B`g)bhXwYoENu~7eR`hBFFc^*a!(|5abqF#b&Y+ zBx=!A%*EaE9DicGxA6)L>~#n$(|n@7>+NswX!*CpZl&0UEHpp9gfZIo`p29XHc9;! z9=K;RnV?56B1)X+m{6_Q!N8|mBURaC>OQC?cuw7EWj#bGiu(`Y4HIFdv`}1t5$X4| z%%WXvU>MO~gP}?bJxc+JNl%KpoMDE*DH#eZi&t(+f`77x4Zq9*00DY{zaD?dUTn!m zy`>%;iUQkhvw>5AQgDNYMy@Y>7SAEs+DU8tshZ~i1&C82`hy}jduqc;*bs6BmDct@ zK6*^(GN0kI+`CSIRtuylMWg(d{VPz(4?VpuA z0MomwFe&|D!j@VXV1nt6pT)s_e>h@AhDM)=2k44NVc`oEFJtt_ZOfA9)CMt?mg>BW za4`eN-$hkQ`VFlrGfCVUMsEM8X&o$Ga|?|;K&8H=#&miKvHtOfDES z(pq6xM$R;ljp{5OR@F+OxHXXX{8+1rzGgwF#sM>;F1Nf9kXMmGxZ!j^x4Z&;PTsxTput!sVzK$YBY(3$KN$^`k(H{c0uJl}LMs6c-`zILZ!CY_ zl;|bdrg@becT9|X%Di5TP;f##O&P)a?^MmLiEG@uGUiO|CWTV!Cl|;OK?mIjS5LyQs4~lR zOm`xZy!H}T?w0~|pe!qB-Hio@m46exyI$!){8#8qSiD7ER1gr#0mXl;bqq5#o_HkU zM`^2y71A6z0E<1>|GhG&A928voO<-xUVuAc0lE|A#Y0!oN=)7PrULbkYacycR>MZqcPuNk&2R{Z zYWhm0w%`g)Lrc&wl!2RGW`DS4*w(%JBmBxy;;LVl>40$iM^{-JwuQaQ$9uKX{~jUX zK)eK!wFjJ3_j&7CwHiGgJd>=rV%(P|+j;3vd{kl|xqL(=ylD7y2ZS zHCp?OTYS#n^627%5jfvfF|)4g{FTsY7ivLc=aAxe(vB_K?)AVDX@9U`K>?6$koZaa zg4^RJmpa`S%7J%*O*tsh8*ge?K1`L%Bq|s_(qp6hJ$L04VO9IUoBAfL1C~gUtpsZe zxgN7;GL%W*YAJ(=jj88zh72`B)HmzHjninH$T?SlOnhDj$#HCkr8DOF#?j;T+IpT1 zeo+wCH6Cs918@R>_FyIa+OFDWL9rz~UG(al=uaSRbrYw~i0&KvwW@Ag_)6&3>Fn~2me=et%v4Biry#>vuP~zY;JRoM#4A^Ur zDFc1=v~V-adiLz!8HyQ@N?XOm5h(jPJ15C%mj@CZ(gjY%M1RntVZV4xjHzB()0r_N z9fi)y=XgC8r0R1+ol_4w2Xf<*Bzgn!u|ljfpdsH7$6^x`Ij3J5Kc?$v@gDFydo-3>S(QGcuvc2@D}S!>N6m?h-Ynu zq);Hb&b@T^AAcld)$s5GykN$yhnNIv&6cd^fUzEScaqPA@A<&ua1g-28*_(uUCWOW z?MP0c6z^8Nr|)+G;4>2gfK;(s6Vv<%8<*$~HgEo%ErwFJ?M?E&Z-KYrn~JNib7;Ue zFn|JM3t+<-slK192t^Y44+Z=9XOI9}EOp|{yR@%dbbr+b$P5_UuMN$E%xp8N(?Y53 zi~j4UkX`Y~u|&in=O%pNh{kuqC*7m{NUZu8l1`(X2h9-RTm<=7PA6{~)HJu3*dOt@ z33aB*qQ)VL433xYS7*8v_}6Z@DNoqMvnw9-{b;CnYrD=eMF>_XGaA8v>0tO90zV3UL?*NNImSBytJxZMfZ9u4Du9&l0J8@$Sk*1V4 zXX;La@)Cszx^-$#x?63BbT z_1+K~0|CU_QZ4QSUxoN+Z##k(zy}6I?9rX*101_`eA^d%j@?NrffTo8snw~KuS-nw N-J%QLj|7u4UwouuA!Yyo diff --git a/test_fixtures/masp_proofs/A8A9963AC2983B576BAE4DE9BA6D2CF14C7F9E90A8588BA75DA0D2860F36E2CB.bin b/test_fixtures/masp_proofs/A8A9963AC2983B576BAE4DE9BA6D2CF14C7F9E90A8588BA75DA0D2860F36E2CB.bin deleted file mode 100644 index e475b08f8b72978c2a6cf90280bec31fc71997bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9649 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 S`T6eG_!p=Db^hxv(0>8E_ROCE diff --git a/test_fixtures/masp_proofs/AE4CEC9192B52E8CE5AB0C25936D2AEEF55A6F202D8EB4564017DB5BEF872107.bin b/test_fixtures/masp_proofs/AE4CEC9192B52E8CE5AB0C25936D2AEEF55A6F202D8EB4564017DB5BEF872107.bin index 89cbcb8263c2e14317c383f6882ee46b68cae626..cee551d22af939387e165299cdaa13f5271f37e3 100644 GIT binary patch delta 2810 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 M5E3kJ1(T6HTq1^EzW@LL delta 2810 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 MR{=Z{4wI2QT>Q>!H2?qr diff --git a/test_fixtures/masp_proofs/AEA19C9B07742FF5F6D759B171396732D2EBF77728D2772EB251123DF2CEF6A1.bin b/test_fixtures/masp_proofs/AEA19C9B07742FF5F6D759B171396732D2EBF77728D2772EB251123DF2CEF6A1.bin deleted file mode 100644 index 4b7967cf8517fcab5932ceb352630a2cdb92311b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9941 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 diff --git a/test_fixtures/masp_proofs/8DAE054DE58BDFFE4F551092B09402D995E6F74FFBF5CE3BA13049D272C5E752.bin b/test_fixtures/masp_proofs/BB9AA71A4227C9E62948CBA8DB4A5C2232840271F032F756953233DB3E53E757.bin similarity index 76% rename from test_fixtures/masp_proofs/8DAE054DE58BDFFE4F551092B09402D995E6F74FFBF5CE3BA13049D272C5E752.bin rename to test_fixtures/masp_proofs/BB9AA71A4227C9E62948CBA8DB4A5C2232840271F032F756953233DB3E53E757.bin index 8b2d8fd22e477c812e7551fb70c4dbf7b87d2048..ecdd51d5db955a7751dc46818651c02457d964b6 100644 GIT binary patch delta 1787 zcmVAXj&az46Rjj|0J5Fp>IkCk?W$!?A+oKt#JSMLRV!Ye` zdzw`1N<|GjF5)+3LK%HsOO6qrxQR(o&aYFy9~$_;<1Uo~-SFufqYX_GcZ|0K7TPS{qrH2vH5m9MDWEK z{Nx*NdhT#wsu@WPyN24>IDFGhxoJB^K2TGreW8CU6SuphYpTc=uXU&4jfDmxsw}vG zTpQi(b&2TW_L?a7!h}|&AT*5-!OAh=2|x1e+#&mu^_v`I4G)UH# z$Ep-7jvSdAy!&ple?!}PFsb;5;AaaB4vkER>soG7JBBL?XeN+NJrSNP6fkoq@gSHAk9IT=TrcqHU)99a{el!_p{^TFSedeO-4E9PzP zGMmrK_S7_OQlsf8^9ijYsOeV%l#PF)`?|#jU5~LgXg)C6Q8AmMk|~4S=hjxtJKjGG zb4a|802IKG2!7+cW;+6E%^Bc4zZ2pXxeR=5yzIpv8NxT!CS_3o?Aeb7`s*I3`Q}mD zfVpe3BZMb54`w^=?Rh?afhQjHzdA1b}-cjvU3z)km`Rylxk2g z1hF=ZTQH}l(ivMp){YE>O+AT3M;%nnoQ+1h=UTN z;Rv(lM4yTCOyBQfFiacM8A(#YM87Z?!N$`(1JM=3!qyUI3XNm&F^W3CoCF9wwZ}A( zc||TYC1uy6Yv-8t(5)C;P5OVzHYjP;^g~J`i^QeL&g?Bw53gNcyThaZI2)WJIjda( z*jx{Bab6E=gJ9fqGHi?nBZQ8OmP$i%K!uY(XOX+QI>!%Q38l5V)q?bU+|vdwspebK z`UK3x_SK7qNg8JeZ^9o@uzAYQTzzo!)Pl8*gGMEet1LzEU&Nt^4?$f-4{NiPZd-tSgmHk(wT)H zOQrPYx9#I0?Ky2EEDr^63uZazyRk1ghrH^y|MNL4k9DP$Sa)pqZx3Gi@XMub6t&vo zV~-pD#{)qpcsYVR;KhHMXTaSVf|H<$tPhB?>#>>2e_yb;AL4e7-KYx>h<7v<$h+m{ zmw69W%c+V9sHSPRiTMs#bVZ;yhcsd@BWX)l8{scnwov3|L3 d5ODu8L*cz>is%crs0#yt?`|#Q`~j1hCOlRZcC`Qi delta 1787 zcmVAZ&YTEYNxA z=*#^~OF8&pS_b#~26&jEGkZ#1To>#_tdqVOKp-Vx=7FY`AmEl#8~6y*oSfi5*CE-n zr0a#{Ln#WID)W;b8bLumXa7KJIT3*!T!~LMg5Uv+MCozx_aXSS&$kOjJY}o`svWyP zZCalXphvyypJM0{g=34;JE&a16dO&RxOJ80v!ELE0w4`dA~So{siY#C$ygX91~-QG zV*-5({L`=DTS1quKr^$n9i#$K{>vX(<~K4ba`CwTd<)e0XwEHyriAI{N57cB`aXdn z5##?7Z_kj}=J%;1Lkmpi>H>W%2jGFpkFAeXO90Tz?(>Ilzht|BwrOc|O7dk~k{KzH zod}cJAV7bpQOQ{1pS1|2FWps`)UhJYLbipA3E97~CMX(8UJ!F{F7}~lG5~TH?7_?> zgZgEY$5EAEgrxE)`DKC`jkP;Qg(I#@N6rN@2#^Jg6?6~8c_pzSvIEJiFESY!dzp9= zD&q?nb_eHCSpy=)Rf-?+KMA|E_AN1Pbe_ppMp1ui#kPR0+=Pk?ndie^7k+pCwxLWM zz26Zy$(HMhT6&sW)OFhf!F(i&o1v)#DFe_GV70%D=Bwz8svaJLtR&`umBAfdRHgFm zwcv-vt-jb9pC0mDZ{o3Vn5%N<=&=r^x9$Rb9g}VAVM+QT-fQVmx99pc6e&|(WNMNK zIzlK}Kexd!0_}rE3yxulAx9^*jumYm(`8RZu{z@c zaBVFLg^&~U(Ne+DwHf%mqroQ+a3-laXN`X!9ig=#HncSuT;o=NbfJ?k;%_NRY;Hey zP>?eC-ERR|vmxbV^31*!_@L8uEVwQggb<7`*X8Le-jbbTuli0ib4^__hR|088U*H?M5wf`{{NTfEf6OyI%#;EjN+^^@>RuNI|2F zh5O?grR6JJ*bDiof?%sdO_ra?p|z8xFh|8rb6^5fKKEO$Eg2DW)+3@}SW8h_@n*2H z83wAa+H^^gQ|g@iE~IHZ6oD*IgI0gRJ>~-f?f@isv<|2bTa{wZ|JbgS9t#VWvzDXs zif}O-=UE7m(?ODfEvNUD53HQhd7MWbTnZA4%jUBnQUWM<4v>}>iLkb5Z{W*V4WBQa! z4!4o9fA}uDll|as=I^Wvbf*pgE}@Sgt(}kN{Q@Nco~uW#y>M-!KXOZ^=08$|x3?8% zpamHA*%?YmQ5^ebxQ4$JOfa7Sje+qitPT#dn(Z(CtM6X0AKP$GnLd~PiVc*$_e%ZJ z&*izz&j)j%>K-bsbm&7lb_aiGsoAWh8b{RPzj zsJNVL2M}J;BVJ^Lp{F*j82Cek2Z*7t4QXXSc8A6$A4H%s+j;& zAo;REmVTD5Nn1jh*rUfQ+c2~+4!Jl|R)?ID@N9zv=lBC`SUG3D+%lO@n3)$r@F$E> z=1Ai}75R<>#`8LkC7PomD45?gta(7&{x4*>t~g&b7-feXWMW9AYEZf7_Gg zH)B7E!^L@({W(9z@9KZQa94{{iHD?MuJ07Gw5B*`X5m!eqf>Ij6jwxl%}3G17GX6a zUET|siwC)?e%zrVVt2>{zb0t+sQ{{)i&Au8Wr0}Sv}{VjmbrJ|Bw0A8#y=RLIb-5Y zz+1!XVWqRj8Y7$QE84~~2UymWf;Z4-u0^u}?(Hl?x&c3guVjDaWm0Xz2Kj&rZU^H` z5%x=ldf9GITn9$kQK1k4Ux^}uYa1>M)e3}iF3om-g*I=GDUUuLu;(L0><5(J#or_K zL#I7RLjtoxaG%y3xzOAM-qy*O3+T5#TX?i0W7}lM ddqk@4K^aMhWEu;eNQ$KnO~|%3QRk6B3yjw%VYOjQw5_gdRb&laCufAiNBct0i&T z^P)P;T7i*nCU<^2N6D>`>sBJ-VIlC%1C#F?L17>7ZMT;;^ziP( z?t7?2Yl5bKHp{G3K9$cv5z|DFhBLaeksPD~f3H4S`KrSe0@_duGWY9ad>1rc&Z6-D zXV<;Sl}b;suWmlsqsvRK6>(80v(VY`nkx}Mtcat7AWTW(bb~1>EQnVF90=_4y)T}V zJhC9UJ!YQbkFO1E;52n9wwkADW~4%wr-9MwHP#-~3es=fl{%>z*z{QUPoDVYV&m+f zv$P+T0#HErW;lk9QqNt@+m2>*CO&n0dAl3kKovgKNLY6y1SknU4=^WqDoK`%&v}z* zFSm&c76ib+z zO7F*};S>zCA1L;3;3cra_hRGg49pt_!>}POS7G-z_O;O@(&aBPxzD&h06+t00G|LC z3_^H5x{M`618ds3jtbgt%1p;JOjxp{Np7 zK_802Dc5e--@~Ct6t;TU-+!%U6VS_Sp=E?IFG8H>oJ2vg^@%7lLJ9t@qJD}hPxl$= zqf1}Tl#%^=Upta->R$WtYPV5iFfe?#RejW zB!^Yb4Wng7g=$QeK3l7G0;j1+$WHz2eF z%wKz=7$~+yy}Yc&bLUl#0Uw&`v$L=y6g@0eZTI@Kx>`=2UxvQu_78LGG(MMA*cpRE zbDN8g34ZH;5a)lM%0*H76UKm+htxHnu8$t0m%Tqxzn%{%sAX*DU0XOhbAK!QWc^2C zZyw#I97rAPDfO3_;D6EJ?E;$7N(&1MM0oNX!lC&qoJT|SeML$Z%_XEpnvPg;mbMo) z#hhsne2rFYtgEuI@RUM4SF%Uy|BZ=Uj5#e*2klNR!-c_)(|gRmUDLxdd2JXvjq;T^QzqL(>=;_h7r{1sh8*70#Tq#`J>^Bnooi|e!mOocI<(?o?B(muP zeNL4pAXcEN;D1#N^~W^3e(L?pFW-W6!264r0vSVx1CdcYCKXS4t3pT*86}}|p>v6S z5q1jUEbVGpGIVmb)Ks9mRooR}QM0Q}>>et((iOU$Ix%>{KOL}E0qM0bFw zhvrwYDrhOZ4m&5wXT~kM@jIyH#*{!OEuU8C|-a%`KUIK-2_K4=;Rs zQ#QM8FuZk_{;PAEpnI(trImD^DO74zoat>{)KJnaVv=QZwc?HHIN7;08l)<-TtKiD z5US;TWPkW8wf+4{6roC0E}MRr;ZF4sBjdEIZ9_AbScl1@U2&9c+RsRc%QGp1y1a-?}u1>tEW(Wu;0W$H$KWJ z*F1uZyzoQtaP-9QPixu1yK!&UUP5V0wgIN6CCM8b@br+KxC985r0YyC!4Mg@t8|2> zSS~HV{Tj8Qq(@%@jK_i_w`uwtS%BK)<}tVSP+Jzu3r|sP6bugt_>=i`vb17~gnlBL=am2(A*6)Dch9F`A!Dh*&ORSrrQVssy=Ux%}v1<6emq@Qy&FC=vG*=xAtDB=c`ZeQ+bi~z1v&W zSoEX{7)q_|6*4&7iIe%}YaKrf8mG{ISxOIW@7qCvWRppXQI1iNI20^%3*5dC+$dP)>v1AZI3Zf*+UmpuyhW@q9Ed_mbj@mHD}KwolaCufAXN1*LDW^!^@}EQb*Q zg+s$dpleBcDgeIDo6ZdVWa7dw&lx0XrP}|ZjTOxfF^-cl8!3^U2$RYpK!1-5eFJ&k z+913hO%*Ocr&pK0VnS*#XPp3%QFHn3RI4m)?gtI9#*b1-Gf1w09+zYCJC3%Jh6IfVVAz7!i|FBTv_Qh2Q zh=3SzqpU(jF6erh4G;ZCSKPPprDzxsaX+Q3nZC%Y+=grEjyvsYlYdcB3O(vG({(ZQ z(xLPvHe;6=?yfs8Z#g zGx<`8CBuFjSObmbLx-n|gkqP2j72?E^Th}L64-7!vxJFdSfG}fNVzu0S60IH`iF^Jx6~Q9OG9|bobt=U3?22 z%b<&1^@1|}b1GeS4X5izD=0;;PM?{|SS|oB3bND{n?06iGtaKcQq@54f%gJ`}0#Mt^#pI0%uQjfG?g$&Vpq z!Qy|4{BGmGM;W!t5GWd_D}Z3>Kf6E_m#sBbD&|>sVn3L$4oA2+|6RQ{D;pY5<-{JH zTwNb$bo}H4tzDW9Tr~~q>p?Ig@6MO?z@Ve6tpUm~A6FB1Sc*KewC(y6J~3G^Q6o%L z`hN(R48l48?SDw5V&;qSmK4XmdW0?MbsLOgSKH2z?eY^^Q?G=T`u^|^Y(K=h5OeaT zFc6>=3tRA>hfU9+^JFR_2?t892$TyeiJp8g^uq_9iP^%!XGPx11LVJa6#S|WxxkumFhQRhR;9AFZ?Az$4?inq8F2fHd_plhnN^&)e9~?ys@twfY<2r&V zqM1*N6--()iZ~L{n<_4QW~S^@2P+uj4K=TCONT#lw~x=dgsh&XFm(%ax}GKJnJNZ~yW6Lj+PRl6A4!fadz@J|vOmys`?Ywh6;1w~u1*@G{j_z7 zzHFyj7Hm!`-liy@sK`;9z^Qm}-?E74>H-uh0)ZM>Kbx7{3%%U9p_lD{P?lwraVQm& GUMMg$q-zQQ diff --git a/test_fixtures/masp_proofs/C788F9057C615CCE7B260BB8BF5CF776D5DE7C9153D67B7FDF22ED6F35558738.bin b/test_fixtures/masp_proofs/C788F9057C615CCE7B260BB8BF5CF776D5DE7C9153D67B7FDF22ED6F35558738.bin deleted file mode 100644 index d3c3a5c24181bdbe0be7e43634ce4e5cd0ac396a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24494 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 Dju1Rq delta 2749 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 PB=E_%(6MU1S&r1!^} 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 diff --git a/test_fixtures/masp_proofs/2E78CC40C345D7EA50407110784E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin b/test_fixtures/masp_proofs/F0471ECBD3AF04B4A373D2966781D5DEF92B9A9BB3A159947560514682CC3877.bin similarity index 71% rename from test_fixtures/masp_proofs/2E78CC40C345D7EA50407110784E2EF3EC23A99F9E8F0A5B95AB1BEB71B18583.bin rename to test_fixtures/masp_proofs/F0471ECBD3AF04B4A373D2966781D5DEF92B9A9BB3A159947560514682CC3877.bin index 84a26fb9c53d6d0fb35ffc72b4793cddd2bf5ba0..42007a8eaecbf55433122a228ce0ebf78b0c2c1b 100644 GIT binary patch delta 1200 zcmbPXHN$FxiAe0#t)glQ&rD{soR)I4x8B^x?%^{zn@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_1{UVqW`&a-wR=9Ndpy5qF>hV+oCQ+R4nO7zGR$PzHdp58W@p|nObV~0 ztZrtzH=gR<+MDTr`PlKk{8yjV^j;Nk1uS#byeqP1bGQIdRnTqz!<^TjP0L9t*0F22 zBJ_(r*v)eBQlYoJ;n&x7)L+`1EeuqZnwR=MRC(|5{uqO8J>DOJzY8xa*qT^;;PuRs z!VHbP#?AF&s?7D%mDoO>i>=~|`La@<@9I5~w-1<`qZIG{H~%fL^eXeOea461i)v}l zy=lG9`3c_wNzECzTHlo$%zUB2tyQC#p?W&m!b64i@+HBi(=V6*I#F$|8xsFz%J=^w zeiOfPxE}f&$7?>JL($LpO6fCpE7$o`YAgFLaMj%WdDN!AEG3`+UA>9m*7}`YyE1gY ziY#9+%U9Wa;;-Y@&0LeNFPs_H9erOfU9m{@rTFI4k1RZXD6ihHy5U3KS#^n52QND} zU8~$?Hf8&QCRh0-&u@Rw)$jlD=d(~@ozX_~)cnqwD*vtvXSAiLy|Iva-fn*Qk43?> zXH$GXz3Rz4!PEYH$}@|n*CnM7)IZ!Hs<-uLl+&-NzT9s%OWrcFN|?y-qjK^VyM=4o zJ_}zeTQBfMBw5G#+^<#F8bVz8*Dv@flgHP#cvIEJnAI=Wok%~&d}Gtb(zomkTf$=h zp552?cf-+D+)TOR#tN_J&wYJ%@@0={w?lfDD61AmSaVHx-%`cCUutur>J^^We19Rs z`YFuXUEuv>pN{X2+KWoj5f3J^C3h296RJ4j= zs+81}Z1>1N9{cu5&Fr$v<&LpMAV%C9aAH(iRC%^aH zyqweiHcRw7Wo5Ot#ws@LTN59M82-;Uy)&bMA%?&H$HJ*e=Wm{wH~Z+}j|UW(*6i^O zQ*Hit$l3ST#SfNypI%%d*JovSYaulpO#KI{B-U)r&lLtL*tp86WEX;C`dF3?q0pU2H1JGJ|W z*ZyUj`h8#Ih(~4Sr7vQOR_d|2y5h@S?nhG>F8SJ8St=U!_VHf3d^g*9_D*xJ>|s5( zPH^U%{OE*Vaf;uot9MvBIPCFGR4@8I_2$I=~knC(U@!$h?+g@)H>o0AR5(RsaA1 delta 1200 zcmbPXHN$FxiAbFH)`>d;b+!Dj<~g;yK3U;y&96K;n@0n?#2q#q5)(PGOKkiT&5hkC zvh1+H=3E8_2A2Cm!lx^J=d3)RDN~o=A^oz)^jpNGtvzh&yRt1_?O$QM*_rnXlLE^l zp59NEYi$f(G+jToKd);oZ{Mm-1&b%W{3*Fir1n<9=5PU^s{J+TGcIwzs%19bIx+iH zN|uSY@M7n_liMexDScs6lbyUdTNtP+@o?(4#Dhhr^a3+{FK5{-aawAzEWc^N4a1E; zB92xsG2UD+rpjEuOwDnJe_-t?GrzDCQzkr-DGqy-tSB1ZUuyq$%dZr}mC9{wrvsZ} zC%-MeZ(OkYvDGv_mjxHTZ|p45&-?wivulCLWEIt*ZM#|QVpLxDo;$ihzf?*2==4bLC%dQ$Bk4H@3>(3khrGCA}F;7;sePY`*6F#9cVvkZ^8J*uIl0KjRm~`a@ z&5FA_ooi+;y4@9jdCAEgN7|mcJgDavh;w<8T3~cw?Ik0-kd+>ak$K^PR-ueZpPG`_ zUb(XV)(HLznMC$dUiwo)%P4n zFQGljDcg=b_gr@}Dee51Mq}sOON3TNmAjq(Dr4Na>ly#_={HYsE8bsI?zhdhIQ{6e z*!rd`ucoWy&6v(LMb+V$>hY?d8=p-2xPdQR+^lsupJK^+#tTi~=l)){*HZIugH8%} z;g)-;OF4a(o`3f^?(p&I+lC6kNt%+YpSz_WYzG{@TSzClh)*gyQ?dK{j! zZ^b#@Vs{%x+nC*UVQl699wlDe@k)KcJEPuo1BN%N>I?2KYwk<$bp0H6<+RZ4 Date: Wed, 29 Nov 2023 13:20:21 +0000 Subject: [PATCH 36/81] Improve signing failure error msg --- apps/src/lib/config/genesis/transactions.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 25f8ac3455..94e0eefb33 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -773,7 +773,14 @@ impl Signed { None } }) - .unwrap(); + .unwrap_or_else(|| { + panic!( + "No signature could be produced for a transaction of type \ + {}. The most likely cause is a missing secret key, \ + public key hash or alias in your pre-genesis wallet.", + std::any::type_name::() + ); + }); for (ix, sig) in sigs.signatures.into_iter() { self.signatures.insert( StringEncoded::new(pks[ix as usize].clone()), From d1a9c9c0e98456f555178549fe8f5ee50d892e50 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 29 Nov 2023 15:16:00 +0000 Subject: [PATCH 37/81] Generate localnet script fixes --- scripts/gen_localnet.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/gen_localnet.py b/scripts/gen_localnet.py index 9e03c7d6cf..79f7285d55 100644 --- a/scripts/gen_localnet.py +++ b/scripts/gen_localnet.py @@ -5,6 +5,9 @@ import shutil import toml +def system(cmd): + if os.system(cmd) != 0: + exit(1) def move_genesis_wallet(genesis_wallet_toml : str, wallet_toml : str): genesis_wallet = toml.load(genesis_wallet_toml) @@ -58,7 +61,11 @@ def edit_parameters(params_toml, **kwargs): # Access the arguments if args.localnet_dir: - localnet_dir = namada_dir + '/' + args.localnet_dir + if args.localnet_dir[-1] == '/': + args.localnet_dir = args.localnet_dir[:-1] + print(os.path.basename(args.localnet_dir)) + localnet_dir = namada_dir + '/' + os.path.basename(args.localnet_dir) + shutil.copytree(args.localnet_dir, localnet_dir) if os.path.isdir(localnet_dir) and os.listdir(localnet_dir): print('Using localnet directory: ' + localnet_dir) @@ -137,7 +144,7 @@ def edit_parameters(params_toml, **kwargs): print(f"Cannot find wasm directory that is not empty at {WASM_PATH}") sys.exit(1) -os.system(f"{namadac_bin} --base-dir={BASE_DIR} utils init-network --chain-prefix {CHAIN_PREFIX} --genesis-time {GENESIS_TIME} --templates-path {TEMPLATES_PATH} --wasm-checksums-path {WASM_CHECKSUMS_PATH}") +system(f"{namadac_bin} --base-dir={BASE_DIR} utils init-network --chain-prefix {CHAIN_PREFIX} --genesis-time {GENESIS_TIME} --templates-path {TEMPLATES_PATH} --wasm-checksums-path {WASM_CHECKSUMS_PATH}") base_dir_files = os.listdir(BASE_DIR) CHAIN_ID="" @@ -158,7 +165,7 @@ def edit_parameters(params_toml, **kwargs): print(f"Cannot find pre-genesis directory that is not empty at {PRE_GENESIS_PATH}") sys.exit(1) -os.system(f"NAMADA_NETWORK_CONFIGS_DIR='{temp_dir}' {namadac_bin} --base-dir={BASE_DIR} utils join-network --chain-id {CHAIN_ID} --genesis-validator {GENESIS_VALIDATOR} --pre-genesis-path {PRE_GENESIS_PATH} --dont-prefetch-wasm") +system(f"NAMADA_NETWORK_CONFIGS_DIR='{temp_dir}' {namadac_bin} --base-dir={BASE_DIR} utils join-network --chain-id {CHAIN_ID} --genesis-validator {GENESIS_VALIDATOR} --pre-genesis-path {PRE_GENESIS_PATH} --dont-prefetch-wasm") shutil.rmtree(BASE_DIR + '/' + CHAIN_ID + '/wasm/') shutil.move(temp_dir + CHAIN_ID + '/wasm/', BASE_DIR + '/' + CHAIN_ID + '/wasm/') From 17c7d68910e89e85787256ed6aba3a3d3cd7563a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 30 Nov 2023 09:59:23 +0000 Subject: [PATCH 38/81] Rename sign genesis txs subcmd --- apps/src/lib/cli.rs | 20 ++++++++++---------- apps/src/lib/cli/client.rs | 2 +- apps/src/lib/client/utils.rs | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b74bb5c496..ffc5e9e420 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2186,7 +2186,7 @@ pub mod cmds { DefaultBaseDir(DefaultBaseDir), EpochSleep(EpochSleep), ValidateGenesisTemplates(ValidateGenesisTemplates), - SignGenesisTx(SignGenesisTx), + SignGenesisTxs(SignGenesisTxs), } impl SubCmd for Utils { @@ -2215,7 +2215,7 @@ pub mod cmds { let validate_genesis_templates = SubCmd::parse(matches).map(Self::ValidateGenesisTemplates); let genesis_tx = - SubCmd::parse(matches).map(Self::SignGenesisTx); + SubCmd::parse(matches).map(Self::SignGenesisTxs); join_network .or(fetch_wasms) .or(validate_wasm) @@ -2245,7 +2245,7 @@ pub mod cmds { .subcommand(DefaultBaseDir::def()) .subcommand(EpochSleep::def()) .subcommand(ValidateGenesisTemplates::def()) - .subcommand(SignGenesisTx::def()) + .subcommand(SignGenesisTxs::def()) .subcommand_required(true) .arg_required_else_help(true) } @@ -2416,21 +2416,21 @@ pub mod cmds { } #[derive(Clone, Debug)] - pub struct SignGenesisTx(pub args::SignGenesisTx); + pub struct SignGenesisTxs(pub args::SignGenesisTxs); - impl SubCmd for SignGenesisTx { - const CMD: &'static str = "sign-genesis-tx"; + impl SubCmd for SignGenesisTxs { + const CMD: &'static str = "sign-genesis-txs"; fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|matches| Self(args::SignGenesisTx::parse(matches))) + .map(|matches| Self(args::SignGenesisTxs::parse(matches))) } fn def() -> App { App::new(Self::CMD) .about("Sign genesis transaction(s).") - .add_args::() + .add_args::() } } @@ -6939,14 +6939,14 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct SignGenesisTx { + pub struct SignGenesisTxs { pub path: PathBuf, pub output: Option, pub validator_alias: Option, pub use_device: bool, } - impl Args for SignGenesisTx { + impl Args for SignGenesisTxs { fn parse(matches: &ArgMatches) -> Self { let path = PATH.parse(matches); let output = OUTPUT.parse(matches); diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 10679bfa3e..7acaf70e3d 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -663,7 +663,7 @@ impl CliApi { Utils::ValidateGenesisTemplates(ValidateGenesisTemplates( args, )) => utils::validate_genesis_templates(global_args, args), - Utils::SignGenesisTx(SignGenesisTx(args)) => { + Utils::SignGenesisTxs(SignGenesisTxs(args)) => { utils::sign_genesis_tx(global_args, args).await } }, diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index afb6dfef20..2015d91aeb 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -964,12 +964,12 @@ pub fn validate_genesis_templates( /// Sign genesis transactions. pub async fn sign_genesis_tx( global_args: args::Global, - args::SignGenesisTx { + args::SignGenesisTxs { path, output, validator_alias, use_device, - }: args::SignGenesisTx, + }: args::SignGenesisTxs, ) { let (mut wallet, _wallet_file) = load_pre_genesis_wallet_or_exit(&global_args.base_dir); From b679cf0732b304752602f377d477b32f97861a48 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 30 Nov 2023 10:29:35 +0000 Subject: [PATCH 39/81] Fix setup process on e2e tests --- tests/src/e2e/setup.rs | 160 +++++++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 54 deletions(-) diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 7852218ba8..3d8d21e823 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -23,10 +23,9 @@ use namada_apps::client::utils::{ self, validator_pre_genesis_dir, validator_pre_genesis_txs_file, }; use namada_apps::config::genesis::utils::read_toml; -use namada_apps::config::genesis::{templates, GenesisAddress}; +use namada_apps::config::genesis::{templates, transactions, GenesisAddress}; use namada_apps::config::{ethereum_bridge, genesis, Config}; use namada_apps::{config, wallet}; -use namada_core::types::address::Address; use namada_core::types::key::{RefTo, SchemeType}; use namada_core::types::string_encoding::StringEncoded; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; @@ -145,10 +144,23 @@ where // - add txs to genesis templates let wallet_path = base_dir.join("pre-genesis"); for val in 0..num { + // init validator dir + let validator_alias = format!("validator-{val}"); + let pre_genesis_path = + validator_pre_genesis_dir(base_dir, &validator_alias); + let pre_genesis_tx_path = + validator_pre_genesis_txs_file(&pre_genesis_path); + std::fs::create_dir(&pre_genesis_path).unwrap_or_else(|err| { + panic!( + "Failed to create the pre-genesis path for {validator_alias}: \ + {err}" + ); + }); + let pre_genesis_tx_path_str = pre_genesis_tx_path.to_string_lossy(); // generate a balance key let mut wallet = wallet::load(&wallet_path) .expect("Could not locate pre-genesis wallet used for e2e tests."); - let alias = format!("validator-{}-balance-key", val); + let alias = format!("validator-{val}-balance-key"); let (alias, sk) = wallet .gen_store_secret_key( SchemeType::Ed25519, @@ -161,35 +173,81 @@ where panic!("Could not generate new key for validator-{}", val) }); wallet::save(&wallet).unwrap(); - // assign balance to the key - genesis + let validator_address = { + use namada_apps::config::genesis::chain::DeriveEstablishedAddress; + let pre_genesis_tx = transactions::EstablishedAccountTx { + vp: "vp_user".to_string(), + threshold: 1, + public_keys: vec![StringEncoded::new(sk.ref_to())], + }; + let address = pre_genesis_tx.derive_established_address(); + println!( + "Initializing validator {validator_alias} with address \ + {address}" + ); + address + }; + // invoke `init-genesis-established-account` to generate a new + // established account with the generated balance key + let args = vec![ + "utils", + "init-genesis-established-account", + "--alias", + &alias, + "--path", + &pre_genesis_tx_path_str, + ]; + let mut init_established_account = run_cmd( + Bin::Client, + args, + Some(5), + &working_dir(), + base_dir, + format!("{}:{}", std::file!(), std::line!()), + ) + .unwrap(); + init_established_account.assert_success(); + // assign balance to the implicit addr (i.e. pubkey) + established acc + let nam_balances = genesis .balances .token .get_mut(&Alias::from_str("nam").expect("Infallible")) - .expect("NAM balances should exist in pre-genesis wallet already") - .0 - .insert( - GenesisAddress::PublicKey(StringEncoded::new(sk.ref_to())), - token::DenominatedAmount { - amount: token::Amount::from_uint( - 3000000, - NATIVE_MAX_DECIMAL_PLACES, - ) - .unwrap(), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, - ); - // invoke `init-genesis-validator` signed by balance key to generate - // validator pre-genesis wallet signed genesis txs - let validator_alias = format!("validator-{}", val); + .expect("NAM balances should exist in pre-genesis wallet already"); + nam_balances.0.insert( + GenesisAddress::PublicKey(StringEncoded::new(sk.ref_to())), + token::DenominatedAmount { + amount: token::Amount::from_uint( + 1000000, + NATIVE_MAX_DECIMAL_PLACES, + ) + .unwrap(), + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }, + ); + nam_balances.0.insert( + GenesisAddress::EstablishedAddress(validator_address.clone()), + token::DenominatedAmount { + amount: token::Amount::from_uint( + 2000000, + NATIVE_MAX_DECIMAL_PLACES, + ) + .unwrap(), + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }, + ); + // invoke `init-genesis-validator` to promote the generated established + // account to a validator account let net_addr = format!("127.0.0.1:{}", 27656 + port_offset(val)); + let validator_address_str = validator_address.to_string(); let args = vec![ "utils", "init-genesis-validator", - "--source", - &alias, "--alias", &validator_alias, + "--address", + &validator_address_str, + "--path", + &pre_genesis_tx_path_str, "--net-address", &net_addr, "--commission-rate", @@ -198,14 +256,10 @@ where "0.01", "--email", "null@null.net", - "--transfer-from-source-amount", - "2000000", "--self-bond-amount", "100000", "--unsafe-dont-encrypt", ]; - let validator_alias = format!("validator-{}", val); - // initialize the validator let mut init_genesis_validator = run_cmd( Bin::Client, args, @@ -216,11 +270,30 @@ where ) .unwrap(); init_genesis_validator.assert_success(); + // invoke `sign-genesis-txs` to sign the validator txs with + // the generated balance key + let args = vec![ + "utils", + "sign-genesis-txs", + "--alias", + &validator_alias, + "--path", + &pre_genesis_tx_path_str, + "--output", + &pre_genesis_tx_path_str, + ]; + let mut sign_pre_genesis_txs = run_cmd( + Bin::Client, + args, + Some(5), + &working_dir(), + base_dir, + format!("{}:{}", std::file!(), std::line!()), + ) + .unwrap(); + sign_pre_genesis_txs.assert_success(); + // initialize the validator // add generated txs to genesis - let pre_genesis_path = - validator_pre_genesis_dir(base_dir, &validator_alias); - let pre_genesis_tx_path = - validator_pre_genesis_txs_file(&pre_genesis_path); let pre_genesis_txs = read_toml(&pre_genesis_tx_path, "transactions.toml").unwrap(); genesis.transactions.merge(pre_genesis_txs); @@ -243,26 +316,6 @@ where genesis } -/// Remove self-bonds from default templates. They will be -/// regenerated later. -fn remove_self_bonds(genesis: &mut templates::All) { - let bonds = genesis.transactions.bond.take().unwrap(); - genesis.transactions.bond = Some( - bonds - .into_iter() - .filter(|bond| { - if let genesis::GenesisAddress::EstablishedAddress(address) = - &bond.data.source - { - Address::Established(address.clone()) != bond.data.validator - } else { - true - } - }) - .collect(), - ); -} - /// Setup a network with a single genesis validator node. pub fn single_node_net() -> Result { network( @@ -302,10 +355,9 @@ pub fn network( templates_dir.to_string_lossy() ) }); - // clear existing validator txs from genesis + // clear existing validator txs and bonds from genesis templates.transactions.validator_account = None; - // remove self-bonds from genesis - remove_self_bonds(&mut templates); + templates.transactions.bond = None; // Update the templates as needed templates.parameters.parameters.vp_whitelist = From 0f34f9c92da1e4aa9d690a4afb86fc574efb1403 Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 30 Nov 2023 15:04:56 +0100 Subject: [PATCH 40/81] [fix]: fix e2e test --- tests/src/e2e/setup.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 3d8d21e823..ce69f99b52 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -26,6 +26,7 @@ use namada_apps::config::genesis::utils::read_toml; use namada_apps::config::genesis::{templates, transactions, GenesisAddress}; use namada_apps::config::{ethereum_bridge, genesis, Config}; use namada_apps::{config, wallet}; +use namada_core::types::address::Address; use namada_core::types::key::{RefTo, SchemeType}; use namada_core::types::string_encoding::StringEncoded; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; @@ -172,7 +173,6 @@ where .unwrap_or_else(|_| { panic!("Could not generate new key for validator-{}", val) }); - wallet::save(&wallet).unwrap(); let validator_address = { use namada_apps::config::genesis::chain::DeriveEstablishedAddress; let pre_genesis_tx = transactions::EstablishedAccountTx { @@ -187,6 +187,12 @@ where ); address }; + wallet.insert_address( + validator_alias.clone(), + Address::Established(validator_address.clone()), + true, + ); + wallet::save(&wallet).unwrap(); // invoke `init-genesis-established-account` to generate a new // established account with the generated balance key let args = vec![ From 8a0fd60f09d9a52a53ea2078f276d2c149b9cffc Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 1 Dec 2023 16:07:41 +0100 Subject: [PATCH 41/81] more e2e fixes --- tests/src/e2e/ledger_tests.rs | 244 ++++++---------------------------- tests/src/e2e/setup.rs | 1 + wasm/checksums.json | 49 ++++--- 3 files changed, 68 insertions(+), 226 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 19784b2ddc..0ac79ce40d 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -654,7 +654,7 @@ fn ledger_txs_and_queries() -> Result<()> { } let christel = find_address(&test, CHRISTEL)?; // as setup in `genesis/e2e-tests-single-node.toml` - let christel_balance = token::Amount::native_whole(1000000); + let christel_balance = token::Amount::native_whole(2000000); let nam = find_address(&test, NAM)?; let storage_key = token::balance_key(&nam, &christel).to_string(); let query_args_and_expected_response = vec![ @@ -931,6 +931,8 @@ fn pos_bonds() -> Result<()> { }, None, )?; + allow_duplicate_ips(&test, &test.net.chain_id, &Who::Validator(0)); + allow_duplicate_ips(&test, &test.net.chain_id, &Who::Validator(1)); set_ethereum_bridge_mode( &test, &test.net.chain_id, @@ -946,26 +948,6 @@ fn pos_bonds() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // put money in the validator account from its balance account so that it - // can self-bond - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_0_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction applied with result:")?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); // 2. Submit a self-bond for the first genesis validator let tx_args = vec![ @@ -975,7 +957,7 @@ fn pos_bonds() -> Result<()> { "--amount", "10000.0", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -1033,7 +1015,7 @@ fn pos_bonds() -> Result<()> { "--amount", "5100.0", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -1112,7 +1094,7 @@ fn pos_bonds() -> Result<()> { "--validator", "validator-0", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -1197,26 +1179,6 @@ fn pos_rewards() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // Put money in the validator account from its balance account so that it - // can pay gas fees - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_0_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - // Wait some epochs let epoch = get_epoch(&test, &validator_0_rpc)?; let wait_epoch = epoch + 4_u64; @@ -1256,7 +1218,7 @@ fn pos_rewards() -> Result<()> { "--validator", "validator-0", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -1467,27 +1429,33 @@ All unbonds total withdrawable: 412.000000\r", #[test] fn pos_init_validator() -> Result<()> { let pipeline_len = 1; - let validator_stake = token::Amount::native_whole(20000_u64); + let validator_stake = token::Amount::native_whole(100000_u64); let test = setup::network( |mut genesis, base_dir: &_| { + + genesis.parameters.parameters.min_num_of_blocks = 4; + genesis.parameters.parameters.epochs_per_year = 31_536_000; + genesis.parameters.parameters.max_expected_time_per_block = 1; + genesis.parameters.pos_params.pipeline_len = pipeline_len; + genesis.parameters.pos_params.unbonding_len = 2; + let genesis = setup::set_validators(1, genesis, base_dir, default_port_offset); + println!("{:?}", genesis.transactions.bond); let stake = genesis .transactions .bond .as_ref() .unwrap() .iter() - .filter_map(|bond| { - (bond.data.validator.to_string() == *"validator-0").then( - || { - bond.data - .amount - .increase_precision( - NATIVE_MAX_DECIMAL_PLACES.into(), - ) - .unwrap() - .amount - }, - ) + .map(|bond| { + bond.data + .amount + .increase_precision( + NATIVE_MAX_DECIMAL_PLACES.into(), + ) + .unwrap() + .amount + + }) .sum::(); assert_eq!( @@ -1495,12 +1463,7 @@ fn pos_init_validator() -> Result<()> { "Assuming this stake, we give the same amount to the new \ validator to have half of voting power", ); - genesis.parameters.parameters.min_num_of_blocks = 4; - genesis.parameters.parameters.epochs_per_year = 31_536_000; - genesis.parameters.parameters.max_expected_time_per_block = 1; - genesis.parameters.pos_params.pipeline_len = pipeline_len; - genesis.parameters.pos_params.unbonding_len = 2; - setup::set_validators(1, genesis, base_dir, default_port_offset) + genesis }, None, )?; @@ -1800,26 +1763,6 @@ fn proposal_submission() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // put money in the validator account from its balance account so that it - // can pay gas fees - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_0_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - // 1.1 Delegate some token let tx_args = vec![ "bond", @@ -1890,7 +1833,7 @@ fn proposal_submission() -> Result<()> { ]; let mut client = run!(test, Bin::Client, query_balance_args, Some(40))?; - client.exp_string("nam: 979500")?; + client.exp_string("nam: 1999500")?; client.assert_success(); // 5. Query token balance governance @@ -1957,7 +1900,7 @@ fn proposal_submission() -> Result<()> { ]; let mut client = run!(test, Bin::Client, query_balance_args, Some(40))?; - client.exp_string("nam: 979500")?; + client.exp_string("nam: 1999500")?; client.assert_success(); // 9. Send a yay vote from a validator @@ -2043,7 +1986,7 @@ fn proposal_submission() -> Result<()> { let mut client = run!(test, Bin::Client, query_proposal, Some(15))?; client.exp_string("Proposal Id: 0")?; client.exp_string( - "passed with 120000.000000 yay votes and 900.000000 nay votes (0.%)", + "passed with 100000.000000 yay votes and 900.000000 nay votes (0.%)", )?; client.assert_success(); @@ -2065,7 +2008,7 @@ fn proposal_submission() -> Result<()> { ]; let mut client = run!(test, Bin::Client, query_balance_args, Some(30))?; - client.exp_string("nam: 980000")?; + client.exp_string("nam: 200000")?; client.assert_success(); // 13. Check if governance funds are 0 @@ -2145,26 +2088,6 @@ fn pgf_governance_proposal() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // put money in the validator account from its balance account so that it - // can pay gas fees - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_one_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - // Delegate some token let tx_args = vec![ "bond", @@ -2231,7 +2154,7 @@ fn pgf_governance_proposal() -> Result<()> { ]; client = run!(test, Bin::Client, query_balance_args, Some(40))?; - client.exp_string("nam: 979500")?; + client.exp_string("nam: 1999500")?; client.assert_success(); // Query token balance governance @@ -2336,7 +2259,7 @@ fn pgf_governance_proposal() -> Result<()> { ]; client = run!(test, Bin::Client, query_balance_args, Some(30))?; - client.exp_string("nam: 980000")?; + client.exp_string("nam: 2000000")?; client.assert_success(); // Check if governance funds are 0 @@ -2583,7 +2506,7 @@ fn proposal_offline() -> Result<()> { let mut client = run!(test, Bin::Client, tally_offline, Some(15))?; client.exp_string("Parsed 1 votes")?; - client.exp_string("rejected with 20900.000000 yay votes")?; + client.exp_string("rejected with 900.000000 yay votes")?; client.assert_success(); Ok(()) @@ -3183,7 +3106,8 @@ fn deactivate_and_reactivate_validator() -> Result<()> { }, None, )?; - + allow_duplicate_ips(&test, &test.net.chain_id, &Who::Validator(0)); + allow_duplicate_ips(&test, &test.net.chain_id, &Who::Validator(1)); set_ethereum_bridge_mode( &test, &test.net.chain_id, @@ -3203,27 +3127,6 @@ fn deactivate_and_reactivate_validator() -> Result<()> { let validator_1_rpc = get_actor_rpc(&test, &Who::Validator(1)); - // put money in the validator-1 account from its balance account so that it - // can deactivate and reactivate - let tx_args = vec![ - "transfer", - "--source", - "validator-1-balance-key", - "--target", - "validator-1-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_1_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(1), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction applied with result:")?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - // Check the state of validator-0 let tx_args = vec![ "validator-state", @@ -3242,7 +3145,7 @@ fn deactivate_and_reactivate_validator() -> Result<()> { "--validator", "validator-1", "--signing-keys", - "validator-1-validator-key", + "validator-1-balance-key", "--node", &validator_1_rpc, ]; @@ -3286,7 +3189,7 @@ fn deactivate_and_reactivate_validator() -> Result<()> { "--validator", "validator-1", "--signing-keys", - "validator-1-validator-key", + "validator-1-balance-key", "--node", &validator_1_rpc, ]; @@ -3347,26 +3250,6 @@ fn change_validator_metadata() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // put money in the validator account from its balance account so that it - // can pay gas fees - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_0_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - // 2. Query the validator metadata loaded from genesis let metadata_query_args = vec![ "validator-metadata", @@ -3507,47 +3390,6 @@ fn test_invalid_validator_txs() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_1_rpc = get_actor_rpc(&test, &Who::Validator(1)); - // put money in the validator-1 account from its balance account so that it - // can deactivate and reactivate - let tx_args = vec![ - "transfer", - "--source", - "validator-1-balance-key", - "--target", - "validator-1-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_1_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(1), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction applied with result:")?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - - // put money in the validator-0 account from its balance account so that it - // can deactivate and reactivate - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_0_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction applied with result:")?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); // Try to change validator-1 commission rate as validator-0 let tx_args = vec![ @@ -3557,7 +3399,7 @@ fn test_invalid_validator_txs() -> Result<()> { "--commission-rate", "0.06", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -3573,7 +3415,7 @@ fn test_invalid_validator_txs() -> Result<()> { "--validator", "validator-1", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -3591,7 +3433,7 @@ fn test_invalid_validator_txs() -> Result<()> { "--website", "theworstvalidator@namada.net", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; @@ -3607,7 +3449,7 @@ fn test_invalid_validator_txs() -> Result<()> { "--validator", "validator-1", "--signing-keys", - "validator-1-validator-key", + "validator-1-balance-key", "--node", &validator_1_rpc, ]; @@ -3651,7 +3493,7 @@ fn test_invalid_validator_txs() -> Result<()> { "--validator", "validator-1", "--signing-keys", - "validator-0-validator-key", + "validator-0-balance-key", "--node", &validator_0_rpc, ]; diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index ce69f99b52..4e471c1697 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -173,6 +173,7 @@ where .unwrap_or_else(|_| { panic!("Could not generate new key for validator-{}", val) }); + println!("alias: {}, pk: {}", alias, sk.ref_to()); let validator_address = { use namada_apps::config::genesis::chain::DeriveEstablishedAddress; let pre_genesis_tx = transactions::EstablishedAccountTx { diff --git a/wasm/checksums.json b/wasm/checksums.json index 8c94ff7a0d..e2eb7f0da5 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,27 +1,26 @@ { - "tx_bond.wasm": "tx_bond.a660b13a79bd5c50c75a055ab4fed8eddb1cc2ae6d93e5505fbeb876b307fc49.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.9564137202b291bd8930410700a5b0e08a1949bb4f79da5ee3e3dc9654266f4d.wasm", - "tx_change_consensus_key.wasm": "tx_change_consensus_key.4d2de1a762b4575925222fdeacadada429896a93fe3586f41657f247480161d9.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.457abeb00c021822e54674b77ae97535620555707efd2029e52b0d4335a32b55.wasm", - "tx_change_validator_metadata.wasm": "tx_change_validator_metadata.b56e2fbaefe19800d8a6d989883ab0ceac439bdeac96fb1299f6aa2ce424e521.wasm", - "tx_claim_rewards.wasm": "tx_claim_rewards.c4fc5d2ace6b0ee785f12f90a6cc600e3a0a8f19f7237ecf9374a32ba27eb99e.wasm", - "tx_deactivate_validator.wasm": "tx_deactivate_validator.c7cb406926241ffaa5df56e43c8cc43854bdcb0ea565144f63b6a3e2eb697a96.wasm", - "tx_ibc.wasm": "tx_ibc.14568fef2ec08752b87ec97cb2c87b76adec72e6eeea771274948d7c7ddb92a8.wasm", - "tx_init_account.wasm": "tx_init_account.0017279b252083d2d337d943bf84251d3f8223756ecfd9648e7ce66305c45796.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.cb9d7a33dd9a37675dd1d9cb761f3f87a48728eae38f377a623c0b8f9a97b612.wasm", - "tx_init_validator.wasm": "tx_init_validator.53fefa66ce6351d6b3e7de7a2c993bd4435fbd478b66caecf5c588b5efdcf669.wasm", - "tx_reactivate_validator.wasm": "tx_reactivate_validator.6a40f8daebdd6b9f6bcc9ab88686b40bb7a7882443cd07443c0795a3fae27cb5.wasm", - "tx_redelegate.wasm": "tx_redelegate.ded70e2609371fc9d81de875cf44b0ec60757fb46f61f23a26418cfa9f437b0f.wasm", - "tx_resign_steward.wasm": "tx_resign_steward.a881ed70174b53a15402cbbe6e64200f72431ec73604f7b173fd77ea80b5aedb.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.fbe760707aa0559c898cee47c91a5c7d0bc4b2f545cb47cc459ddcb8f97c455e.wasm", - "tx_transfer.wasm": "tx_transfer.aef75ec63198e4e6f95307a55e53fd0440448caf46617047f7a7557a7a2f1f4e.wasm", - "tx_unbond.wasm": "tx_unbond.1fd257addac3d91d77e1ae334b9394085335075581a6500ce2364cd403d028e9.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.ba0e378bb48d5ba577dd898e618478c917e80ee11e46f79b00bb72fea4f91c28.wasm", - "tx_update_account.wasm": "tx_update_account.aa4ef57619b8b7c7c3849fa71873448808f7342fcc8d612e4b5e688a708a174a.wasm", - "tx_update_steward_commission.wasm": "tx_update_steward_commission.e7bf45b9039a5b4ef399fe926a4b56e035e9a7eedc4fdd59fc89210b4a8bbf74.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.4b39fd9c6ccc8a9537a295d154a8b9e60b8d135fe995276a01d714bc8097be6a.wasm", - "tx_withdraw.wasm": "tx_withdraw.2f6bf8c3cafbd5d987849db1583bd63e10bbe06193f619859caa80ab10f2e2a9.wasm", - "vp_implicit.wasm": "vp_implicit.262b8660014cd140bcbbef2d187863160678cb0034903257815dc08d947b03fa.wasm", - "vp_user.wasm": "vp_user.d66a3a289c9c9e6353a2741335775eab1487e2e7ee4b8baa69d32e73083363d3.wasm", - "vp_validator.wasm": "vp_validator.d89911d9c27b2e0926499623cc88ee35c4396c8ef0563f897386ff431c8148e7.wasm" + "tx_become_validator.wasm": "tx_become_validator.5ccfc10950bd07526c194767d9936f76e3c99dcc96b651c5de5b768995834c52.wasm", + "tx_bond.wasm": "tx_bond.c128c53aa9493c5a77b28b765ff43010441179f4eb2b853464a9fcae02e0303d.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.c306667f44c80140785766f9aa865459fc5bedb4324d669d83137f20fb91cd88.wasm", + "tx_change_consensus_key.wasm": "tx_change_consensus_key.e1e10b5413f53cd0487da50b62e3282b5b4f665e5e0a3c0d68a67aab02fafe71.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.31dbd9f4b41e81d98a8254c90b74e589e7967ad5456ed86ea5c7b51081c68f9a.wasm", + "tx_change_validator_metadata.wasm": "tx_change_validator_metadata.62b37dd2737690da928c937c05a230c1b701d3f733cc5e9151e3915454e04396.wasm", + "tx_claim_rewards.wasm": "tx_claim_rewards.c9accdfb3a3df9d29bc35f0837f6cc21a60c8726a7bbe8a5d5b24fc1aa7d3049.wasm", + "tx_deactivate_validator.wasm": "tx_deactivate_validator.42cf7a81c059da39249e4913f2ec977fb01e9a23b13f518971c35fe4b06e4a88.wasm", + "tx_ibc.wasm": "tx_ibc.f95ae760d4c7a50a871b18f5ac1d32380ed2d02ccad6aa9856206bb007c9809b.wasm", + "tx_init_account.wasm": "tx_init_account.c452631ce986ae06f98bec86c69df7da1e1dad56cd569aabf3f77c577f227bc1.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.32de55454c0464e4854b63240221122168db17b876f4e8233445529df8b89e85.wasm", + "tx_reactivate_validator.wasm": "tx_reactivate_validator.a1fb89fc5315ec014a327eabccd9b9b09b44fe0133e23b448b6552c0979014c2.wasm", + "tx_redelegate.wasm": "tx_redelegate.6837eaa55ac88c97f0e10b1452938531a23e1db5d07f1a7b3626440d57e571c8.wasm", + "tx_resign_steward.wasm": "tx_resign_steward.a8267ce47823d7abdebd934d871457217c43c72f5e5bf8182874759b80686b79.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.515cf3de9063a47f651af4169ad218a4e17f7e230981eb58b28205ce6d30df76.wasm", + "tx_transfer.wasm": "tx_transfer.a71d9bb1c96b881602de6f680eb932c5ecb58ec316513b285141f06311c8098e.wasm", + "tx_unbond.wasm": "tx_unbond.2adac65b3f74dee53621de09a3abbfbc1f503de15b51953f09654bd467f7a7b4.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.ef5c2a214ae47bfe4f135e320422a766d36a96e79026375ad74b06277f12580c.wasm", + "tx_update_account.wasm": "tx_update_account.92ef1c5fa7a242bf1b4e96a0da58a0580c6417930bd24de53652a20803a7871b.wasm", + "tx_update_steward_commission.wasm": "tx_update_steward_commission.96d394e9a7edf19dfb290ea0433245462e897db970886c5586d98167a7bf412d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.6f5810560da53f5096ca5528500e97f9b18a9ab73f15f082ccf73f175c21feb4.wasm", + "tx_withdraw.wasm": "tx_withdraw.17aab3a6f3606d059118946447820d8e3cc1e65fa07234e334ea4b06f6a97da9.wasm", + "vp_implicit.wasm": "vp_implicit.5f24ee9c926050052f84a7194b2c6eb4f28a18c0ac173c89a6e701c093361fb1.wasm", + "vp_user.wasm": "vp_user.0f21d9e233e4fbacebd9c842314ab62add96182af1a517b4a323f61b4b322c80.wasm" } \ No newline at end of file From b8a26606a7c71815b5d10cf638f83cf750306368 Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 1 Dec 2023 17:19:24 +0100 Subject: [PATCH 42/81] [fix] Wallet can generated keys without genesis files --- apps/src/lib/cli/context.rs | 2 +- apps/src/lib/cli/wallet.rs | 2 +- sdk/src/wallet/mod.rs | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 51c8ba4eac..34dd187245 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -92,7 +92,7 @@ impl Context { pub fn new(global_args: args::Global) -> Result { let global_config = read_or_try_new_global_config(&global_args); - let chain = match global_config.default_chain_id.as_ref() { + let chain = match global_args.chain_id.as_ref() { Some(default_chain_id) => { tracing::info!("Default chain ID: {default_chain_id}"); let mut config = diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 4d22365f80..c5c76eca59 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -510,7 +510,7 @@ fn key_and_address_gen( }); let mut rng = OsRng; let (_mnemonic, seed) = Wallet::::gen_hd_seed( - None, &mut rng, + None, &mut rng, unsafe_dont_encrypt, ) .unwrap_or_else(|err| { edisplay_line!(io, "{}", err); diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index 28b25c1076..9a112485f6 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -587,6 +587,7 @@ impl Wallet { pub fn gen_hd_seed( passphrase: Option>, rng: &mut U::Rng, + unsafe_dont_encrypt: bool ) -> Result<(Mnemonic, Seed), GenRestoreKeyError> { const MNEMONIC_TYPE: MnemonicType = MnemonicType::Words24; let mnemonic = U::generate_mnemonic_code(MNEMONIC_TYPE, rng)?; @@ -596,8 +597,11 @@ impl Wallet { ); println!("{}", mnemonic.clone().into_phrase()); - let passphrase = - passphrase.unwrap_or_else(|| U::read_mnemonic_passphrase(true)); + let passphrase = if unsafe_dont_encrypt { + Zeroizing::new(String::new()) + } else { + passphrase.unwrap_or_else(|| U::read_mnemonic_passphrase(true)) + }; let seed = Seed::new(&mnemonic, &passphrase); Ok((mnemonic, seed)) } From e66fc31409f3d14fa0f3e9cba5e1532cf43a626e Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 4 Dec 2023 12:21:41 +0100 Subject: [PATCH 43/81] Fixed stupid epoch sleep and now pass chain-id to post-genesis ledger commands --- apps/src/lib/cli/client.rs | 6 +----- apps/src/lib/cli/context.rs | 12 +++++------ apps/src/lib/cli/wallet.rs | 4 +++- apps/src/lib/client/rpc.rs | 20 +++++++++++++++++- sdk/src/wallet/mod.rs | 2 +- tests/src/e2e/ledger_tests.rs | 40 +++++++++++++++++++---------------- 6 files changed, 51 insertions(+), 33 deletions(-) diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 7acaf70e3d..09b636af3b 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -650,15 +650,11 @@ impl CliApi { utils::default_base_dir(global_args, args) } Utils::EpochSleep(EpochSleep(args)) => { - let mut ctx = cli::Context::new::(global_args) - .expect("expected to construct a context"); let mut ledger_address = args.ledger_address.clone(); let client = C::from_tendermint_address(&mut ledger_address); client.wait_until_node_is_synced(io).await?; - let args = args.to_sdk(&mut ctx); - let namada = ctx.to_sdk(&client, io); - rpc::epoch_sleep(&namada, args).await; + rpc::epoch_sleep_ctxless(&client, io).await; } Utils::ValidateGenesisTemplates(ValidateGenesisTemplates( args, diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 34dd187245..8f78e87f09 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -93,12 +93,10 @@ impl Context { let global_config = read_or_try_new_global_config(&global_args); let chain = match global_args.chain_id.as_ref() { - Some(default_chain_id) => { - tracing::info!("Default chain ID: {default_chain_id}"); + Some(chain_id) => { let mut config = - Config::load(&global_args.base_dir, default_chain_id, None); - let chain_dir = - global_args.base_dir.join(default_chain_id.as_str()); + Config::load(&global_args.base_dir, chain_id, None); + let chain_dir = global_args.base_dir.join(chain_id.as_str()); let genesis = genesis::chain::Finalized::read_toml_files(&chain_dir) .expect("Missing genesis files"); @@ -187,8 +185,8 @@ impl Context { fn safe_exit_on_missing_chain_context() -> ! { eprintln!( - "No chain is configured. You may need to run `namada client utils \ - join-network` command." + "No chain-id was provided. If no chain is configured, you may need to \ + run `namada client utils join-network` command." ); utils::safe_exit(1) } diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index c5c76eca59..e8262ccfbf 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -510,7 +510,9 @@ fn key_and_address_gen( }); let mut rng = OsRng; let (_mnemonic, seed) = Wallet::::gen_hd_seed( - None, &mut rng, unsafe_dont_encrypt, + None, + &mut rng, + unsafe_dont_encrypt, ) .unwrap_or_else(|err| { edisplay_line!(io, "{}", err); diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 788e54a80b..7bb1860acc 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -54,7 +54,7 @@ use namada_sdk::rpc::{ self, enriched_bonds_and_unbonds, query_epoch, TxResponse, }; use namada_sdk::wallet::AddressVpType; -use namada_sdk::{display, display_line, edisplay_line, error, prompt, Namada}; +use namada_sdk::{display, display_line, edisplay_line, error, prompt, Namada, queries}; use tokio::time::Instant; use crate::cli::{self, args}; @@ -75,6 +75,12 @@ pub async fn query_tx_status<'a>( .unwrap() } +/// Query and print the epoch of the last committed block +pub async fn query_and_print_epoch_ctxless<'a, T: 'a + Io>(client: &(impl 'a + queries::Client + Sync), io: &T) -> Epoch { + let epoch = rpc::query_epoch(client).await.unwrap(); + display_line!(io, "Last committed epoch: {}", epoch); + epoch +} /// Query and print the epoch of the last committed block pub async fn query_and_print_epoch<'a>(context: &impl Namada<'a>) -> Epoch { let epoch = rpc::query_epoch(context.client()).await.unwrap(); @@ -2537,6 +2543,18 @@ pub async fn query_result<'a>( } } +pub async fn epoch_sleep_ctxless<'a, T: 'a + Io>(client: &(impl 'a + queries::Client + Sync), io: &T) { + let start_epoch = query_and_print_epoch_ctxless(client, io).await; + loop { + tokio::time::sleep(core::time::Duration::from_secs(1)).await; + let current_epoch = query_epoch(client).await.unwrap(); + if current_epoch > start_epoch { + display_line!(io, "Reached epoch {}", current_epoch); + break; + } + } +} + pub async fn epoch_sleep<'a>(context: &impl Namada<'a>, _args: args::Query) { let start_epoch = query_and_print_epoch(context).await; loop { diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index 9a112485f6..86a0e892d8 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -587,7 +587,7 @@ impl Wallet { pub fn gen_hd_seed( passphrase: Option>, rng: &mut U::Rng, - unsafe_dont_encrypt: bool + unsafe_dont_encrypt: bool, ) -> Result<(Mnemonic, Seed), GenRestoreKeyError> { const MNEMONIC_TYPE: MnemonicType = MnemonicType::Words24; let mnemonic = U::generate_mnemonic_code(MNEMONIC_TYPE, rng)?; diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 0ac79ce40d..41d76c30e0 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -60,8 +60,14 @@ fn start_namada_ledger_node( Some(idx) => Who::Validator(idx), _ => Who::NonValidator, }; - let mut node = - run_as!(test, who.clone(), Bin::Node, &["ledger"], timeout_sec)?; + let chain_id = test.net.chain_id.to_string(); + let mut node = run_as!( + test, + who.clone(), + Bin::Node, + &["--chain-id", &chain_id, "ledger"], + timeout_sec + )?; node.exp_string("Namada ledger node started")?; if let Who::Validator(_) = who { node.exp_string("This node is a validator")?; @@ -95,8 +101,11 @@ fn run_ledger() -> Result<()> { ethereum_bridge::ledger::Mode::Off, None, ); - - let cmd_combinations = vec![vec!["ledger"], vec!["ledger", "run"]]; + let chain_id = test.net.chain_id.to_string(); + let cmd_combinations = vec![ + vec!["--chain-id", &chain_id, "ledger"], + vec!["--chain-id", &chain_id, "ledger", "run"], + ]; // Start the ledger as a validator for args in &cmd_combinations { @@ -283,10 +292,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; - - // There should be no previous state - ledger.exp_string("No state could be found")?; + let mut ledger = start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; // Wait to commit a block ledger.exp_regex(r"Committed block hash.*, height: [0-9]+")?; let bg_ledger = ledger.background(); @@ -320,7 +326,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { test, Who::Validator(0), Bin::Node, - &["ledger", "reset"], + &["--chain-id", &test.net.chain_id.to_string(),"ledger", "reset"], Some(10), )?; session.exp_eof()?; @@ -948,7 +954,6 @@ fn pos_bonds() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // 2. Submit a self-bond for the first genesis validator let tx_args = vec![ "bond", @@ -1432,13 +1437,17 @@ fn pos_init_validator() -> Result<()> { let validator_stake = token::Amount::native_whole(100000_u64); let test = setup::network( |mut genesis, base_dir: &_| { - genesis.parameters.parameters.min_num_of_blocks = 4; genesis.parameters.parameters.epochs_per_year = 31_536_000; genesis.parameters.parameters.max_expected_time_per_block = 1; genesis.parameters.pos_params.pipeline_len = pipeline_len; genesis.parameters.pos_params.unbonding_len = 2; - let genesis = setup::set_validators(1, genesis, base_dir, default_port_offset); + let genesis = setup::set_validators( + 1, + genesis, + base_dir, + default_port_offset, + ); println!("{:?}", genesis.transactions.bond); let stake = genesis .transactions @@ -1449,13 +1458,9 @@ fn pos_init_validator() -> Result<()> { .map(|bond| { bond.data .amount - .increase_precision( - NATIVE_MAX_DECIMAL_PLACES.into(), - ) + .increase_precision(NATIVE_MAX_DECIMAL_PLACES.into()) .unwrap() .amount - - }) .sum::(); assert_eq!( @@ -3390,7 +3395,6 @@ fn test_invalid_validator_txs() -> Result<()> { let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_1_rpc = get_actor_rpc(&test, &Who::Validator(1)); - // Try to change validator-1 commission rate as validator-0 let tx_args = vec![ "change-commission-rate", From 55ec4839bace8b6562649bc1c92b865d5ffb0371 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 4 Dec 2023 13:54:38 +0000 Subject: [PATCH 44/81] Allow the chain id to be configured through an env var --- apps/src/lib/cli/client.rs | 6 +++++- apps/src/lib/cli/context.rs | 16 ++++++++++++++-- apps/src/lib/client/rpc.rs | 20 +------------------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 09b636af3b..7acaf70e3d 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -650,11 +650,15 @@ impl CliApi { utils::default_base_dir(global_args, args) } Utils::EpochSleep(EpochSleep(args)) => { + let mut ctx = cli::Context::new::(global_args) + .expect("expected to construct a context"); let mut ledger_address = args.ledger_address.clone(); let client = C::from_tendermint_address(&mut ledger_address); client.wait_until_node_is_synced(io).await?; - rpc::epoch_sleep_ctxless(&client, io).await; + let args = args.to_sdk(&mut ctx); + let namada = ctx.to_sdk(&client, io); + rpc::epoch_sleep(&namada, args).await; } Utils::ValidateGenesisTemplates(ValidateGenesisTemplates( args, diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 8f78e87f09..af26ae1bdf 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use color_eyre::eyre::Result; +use namada::core::types::chain::ChainId; use namada::ledger::ibc::storage::ibc_token; use namada::types::address::{Address, InternalAddress}; use namada::types::ethereum_events::EthAddress; @@ -28,6 +29,9 @@ use crate::{wallet, wasm_loader}; /// Env. var to set wasm directory pub const ENV_VAR_WASM_DIR: &str = "NAMADA_WASM_DIR"; +/// Env. var to read the Namada chain id from +pub const ENV_VAR_CHAIN_ID: &str = "NAMADA_CHAIN_ID"; + /// A raw address (bech32m encoding) or an alias of an address that may be found /// in the wallet pub type WalletAddress = FromContext
; @@ -92,7 +96,13 @@ impl Context { pub fn new(global_args: args::Global) -> Result { let global_config = read_or_try_new_global_config(&global_args); - let chain = match global_args.chain_id.as_ref() { + let chain_id = std::env::var(ENV_VAR_CHAIN_ID) + .ok() + .and_then(|chain_id| ChainId::from_str(&chain_id).ok()); + let chain_id = + chain_id.as_ref().or_else(|| global_args.chain_id.as_ref()); + + let chain = match chain_id { Some(chain_id) => { let mut config = Config::load(&global_args.base_dir, chain_id, None); @@ -186,7 +196,9 @@ impl Context { fn safe_exit_on_missing_chain_context() -> ! { eprintln!( "No chain-id was provided. If no chain is configured, you may need to \ - run `namada client utils join-network` command." + run `namada client utils join-network` command. If the chain is \ + configured, set the chain id with `--chain-id ` or + via the env var `{ENV_VAR_CHAIN_ID}`." ); utils::safe_exit(1) } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 7bb1860acc..788e54a80b 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -54,7 +54,7 @@ use namada_sdk::rpc::{ self, enriched_bonds_and_unbonds, query_epoch, TxResponse, }; use namada_sdk::wallet::AddressVpType; -use namada_sdk::{display, display_line, edisplay_line, error, prompt, Namada, queries}; +use namada_sdk::{display, display_line, edisplay_line, error, prompt, Namada}; use tokio::time::Instant; use crate::cli::{self, args}; @@ -75,12 +75,6 @@ pub async fn query_tx_status<'a>( .unwrap() } -/// Query and print the epoch of the last committed block -pub async fn query_and_print_epoch_ctxless<'a, T: 'a + Io>(client: &(impl 'a + queries::Client + Sync), io: &T) -> Epoch { - let epoch = rpc::query_epoch(client).await.unwrap(); - display_line!(io, "Last committed epoch: {}", epoch); - epoch -} /// Query and print the epoch of the last committed block pub async fn query_and_print_epoch<'a>(context: &impl Namada<'a>) -> Epoch { let epoch = rpc::query_epoch(context.client()).await.unwrap(); @@ -2543,18 +2537,6 @@ pub async fn query_result<'a>( } } -pub async fn epoch_sleep_ctxless<'a, T: 'a + Io>(client: &(impl 'a + queries::Client + Sync), io: &T) { - let start_epoch = query_and_print_epoch_ctxless(client, io).await; - loop { - tokio::time::sleep(core::time::Duration::from_secs(1)).await; - let current_epoch = query_epoch(client).await.unwrap(); - if current_epoch > start_epoch { - display_line!(io, "Reached epoch {}", current_epoch); - break; - } - } -} - pub async fn epoch_sleep<'a>(context: &impl Namada<'a>, _args: args::Query) { let start_epoch = query_and_print_epoch(context).await; loop { From f9cae1644f513d847de1edeefd8501485be50ce4 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 4 Dec 2023 13:54:54 +0000 Subject: [PATCH 45/81] Set chain id in e2e tests through env var --- tests/src/e2e/ledger_tests.rs | 24 +++++++++--------------- tests/src/e2e/setup.rs | 4 ++++ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 41d76c30e0..c2a6bc0e5c 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -60,14 +60,8 @@ fn start_namada_ledger_node( Some(idx) => Who::Validator(idx), _ => Who::NonValidator, }; - let chain_id = test.net.chain_id.to_string(); - let mut node = run_as!( - test, - who.clone(), - Bin::Node, - &["--chain-id", &chain_id, "ledger"], - timeout_sec - )?; + let mut node = + run_as!(test, who.clone(), Bin::Node, &["ledger"], timeout_sec)?; node.exp_string("Namada ledger node started")?; if let Who::Validator(_) = who { node.exp_string("This node is a validator")?; @@ -101,11 +95,8 @@ fn run_ledger() -> Result<()> { ethereum_bridge::ledger::Mode::Off, None, ); - let chain_id = test.net.chain_id.to_string(); - let cmd_combinations = vec![ - vec!["--chain-id", &chain_id, "ledger"], - vec!["--chain-id", &chain_id, "ledger", "run"], - ]; + + let cmd_combinations = vec![vec!["ledger"], vec!["ledger", "run"]]; // Start the ledger as a validator for args in &cmd_combinations { @@ -292,7 +283,10 @@ fn run_ledger_load_state_and_reset() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; + + // There should be no previous state + ledger.exp_string("No state could be found")?; // Wait to commit a block ledger.exp_regex(r"Committed block hash.*, height: [0-9]+")?; let bg_ledger = ledger.background(); @@ -326,7 +320,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { test, Who::Validator(0), Bin::Node, - &["--chain-id", &test.net.chain_id.to_string(),"ledger", "reset"], + &["ledger", "reset"], Some(10), )?; session.exp_eof()?; diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 4e471c1697..af810670b2 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -19,6 +19,7 @@ use expectrl::{ControlCode, Eof, WaitStatus}; use eyre::eyre; use itertools::{Either, Itertools}; use namada::types::chain::ChainId; +use namada_apps::cli::context::ENV_VAR_CHAIN_ID; use namada_apps::client::utils::{ self, validator_pre_genesis_dir, validator_pre_genesis_txs_file, }; @@ -548,6 +549,9 @@ pub fn network( copy_wasm_to_chain_dir(&working_dir, test_dir.path(), &net.chain_id); + // Set the chain id + std::env::set_var(ENV_VAR_CHAIN_ID, net.chain_id.to_string()); + Ok(Test { working_dir, test_dir, From 2a12a7a080a08920120d3adf07854021fed46960 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 4 Dec 2023 14:01:39 +0000 Subject: [PATCH 46/81] Appease clippy --- apps/src/lib/cli/context.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index af26ae1bdf..b17b8a34d4 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -99,8 +99,7 @@ impl Context { let chain_id = std::env::var(ENV_VAR_CHAIN_ID) .ok() .and_then(|chain_id| ChainId::from_str(&chain_id).ok()); - let chain_id = - chain_id.as_ref().or_else(|| global_args.chain_id.as_ref()); + let chain_id = chain_id.as_ref().or(global_args.chain_id.as_ref()); let chain = match chain_id { Some(chain_id) => { From fff1064423bb5c28308e3cadd9e82eb00495f111 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 4 Dec 2023 14:45:50 +0000 Subject: [PATCH 47/81] fixing more dumb shit --- tests/src/e2e/ledger_tests.rs | 54 ++++++++++++++--------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index c2a6bc0e5c..492a7ffd9f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -38,7 +38,8 @@ use setup::Test; use super::helpers::{ epochs_per_year_from_min_duration, get_established_addr_from_pregenesis, - get_height, wait_for_block_height, wait_for_wasm_pre_compile, + get_height, get_pregenesis_wallet, wait_for_block_height, + wait_for_wasm_pre_compile, }; use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode, NamadaCmd}; use crate::e2e::helpers::{ @@ -921,10 +922,13 @@ fn pos_bonds() -> Result<()> { default_port_offset, ); genesis.transactions.bond = Some({ + let wallet = get_pregenesis_wallet(base_dir); + let validator_1_address = wallet + .find_address("validator-1") + .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); - // NB: the last bond should be from `validator-1`. - // we will filter it out from the list of bonds - bonds.pop(); + bonds + .retain(|bond| bond.data.validator != *validator_1_address); bonds }); genesis @@ -1283,26 +1287,6 @@ fn test_bond_queries() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_alias = "validator-0"; - // put money in the validator account from its balance account so that it - // can pay gas fees - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_one_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction is valid.")?; - client.assert_success(); - // 2. Submit a delegation to the genesis validator let tx_args = vec![ "bond", @@ -2956,8 +2940,6 @@ fn implicit_account_reveal_pk() -> Result<()> { &["key", "gen", "--alias", &key_alias, "--unsafe-dont-encrypt"], Some(20), )?; - cmd.exp_string("Enter BIP39 passphrase (empty for none): ")?; - cmd.send_line("")?; cmd.assert_success(); // Apply the key_alias once the key is generated to obtain tx args @@ -3095,10 +3077,13 @@ fn deactivate_and_reactivate_validator() -> Result<()> { default_port_offset, ); genesis.transactions.bond = Some({ + let wallet = get_pregenesis_wallet(base_dir); + let validator_1_address = wallet + .find_address("validator-1") + .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); - // NB: the last bond should be from `validator-1`. - // we will filter it out from the list of bonds - bonds.pop(); + bonds + .retain(|bond| bond.data.validator != *validator_1_address); bonds }); genesis @@ -3126,7 +3111,7 @@ fn deactivate_and_reactivate_validator() -> Result<()> { let validator_1_rpc = get_actor_rpc(&test, &Who::Validator(1)); - // Check the state of validator-0 + // Check the state of validator-1 let tx_args = vec![ "validator-state", "--validator", @@ -3358,10 +3343,13 @@ fn test_invalid_validator_txs() -> Result<()> { default_port_offset, ); genesis.transactions.bond = Some({ + let wallet = get_pregenesis_wallet(base_dir); + let validator_1_address = wallet + .find_address("validator-1") + .expect("Failed to find validator-1 address"); let mut bonds = genesis.transactions.bond.unwrap(); - // NB: the last bond should be from `validator-1`. - // we will filter it out from the list of bonds - bonds.pop(); + bonds + .retain(|bond| bond.data.validator != *validator_1_address); bonds }); genesis From 92bdbbed3531e96b33b4e66782b3bf46ba750378 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 4 Dec 2023 15:25:14 +0000 Subject: [PATCH 48/81] Temporarily disable test_bond_queries --- tests/src/e2e/ledger_tests.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 492a7ffd9f..768ee5d995 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1264,6 +1264,7 @@ fn pos_rewards() -> Result<()> { /// 6. Wait for epoch 7 /// 7. Check the output of the bonds query #[test] +#[ignore] // TODO: fix this test fn test_bond_queries() -> Result<()> { let pipeline_len = 2; let unbonding_len = 4; @@ -1380,6 +1381,7 @@ fn test_bond_queries() -> Result<()> { // 6. Wait for withdraw_epoch loop { let epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + // NOTE: test passes from epoch ~13 onwards if epoch >= withdraw_epoch { break; } @@ -1389,8 +1391,8 @@ fn test_bond_queries() -> Result<()> { let tx_args = vec!["bonds", "--ledger-address", &validator_one_rpc]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string( - "All bonds total active: 120188.000000\r -All bonds total: 120188.000000\r + "All bonds total active: 100188.000000\r +All bonds total: 100188.000000\r All unbonds total active: 412.000000\r All unbonds total: 412.000000\r All unbonds total withdrawable: 412.000000\r", From d227c53d4ee52628c404a8759e52654c040e62c7 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 4 Dec 2023 15:25:24 +0000 Subject: [PATCH 49/81] Fix e2e wallet tests --- tests/src/e2e/wallet_tests.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/src/e2e/wallet_tests.rs b/tests/src/e2e/wallet_tests.rs index ad6b86f985..167d67202e 100644 --- a/tests/src/e2e/wallet_tests.rs +++ b/tests/src/e2e/wallet_tests.rs @@ -131,8 +131,6 @@ fn wallet_unencrypted_key_cmds() -> Result<()> { &["key", "gen", "--alias", key_alias, "--unsafe-dont-encrypt"], Some(20), )?; - cmd.exp_string("Enter BIP39 passphrase (empty for none): ")?; - cmd.send_line("")?; cmd.exp_string(&format!( "Successfully added a key and an address with alias: \"{}\"", key_alias @@ -181,8 +179,6 @@ fn wallet_address_cmds() -> Result<()> { ], Some(20), )?; - cmd.exp_string("Enter BIP39 passphrase (empty for none): ")?; - cmd.send_line("")?; cmd.exp_string(&format!( "Successfully added a key and an address with alias: \"{}\"", gen_address_alias From 9991b1a1404ddf22b2d6d31a31c6dde7042bb9f8 Mon Sep 17 00:00:00 2001 From: brentstone Date: Mon, 4 Dec 2023 12:58:01 -0500 Subject: [PATCH 50/81] fix `test_bond_queries` and trigger in CI --- .github/workflows/scripts/e2e.json | 1 + sdk/src/queries/vp/pos.rs | 4 ++-- tests/src/e2e/ledger_tests.rs | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index a0478a9322..0b121161cf 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -24,6 +24,7 @@ "e2e::ledger_tests::change_validator_metadata": 31, "e2e::ledger_tests::pos_rewards": 44, "e2e::ledger_tests::test_invalid_validator_txs": 73, + "e2e::ledger_tests::test_bond_queries": 95, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, diff --git a/sdk/src/queries/vp/pos.rs b/sdk/src/queries/vp/pos.rs index 0a4777514a..3938be5818 100644 --- a/sdk/src/queries/vp/pos.rs +++ b/sdk/src/queries/vp/pos.rs @@ -462,8 +462,8 @@ where next_result.map( |( lazy_map::NestedSubKey::Data { - key: withdraw_epoch, - nested_sub_key: lazy_map::SubKey::Data(bond_epoch), + key: bond_epoch, + nested_sub_key: lazy_map::SubKey::Data(withdraw_epoch), }, amount, )| ((bond_epoch, withdraw_epoch), amount), diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 768ee5d995..5ae380ba5e 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1264,7 +1264,6 @@ fn pos_rewards() -> Result<()> { /// 6. Wait for epoch 7 /// 7. Check the output of the bonds query #[test] -#[ignore] // TODO: fix this test fn test_bond_queries() -> Result<()> { let pipeline_len = 2; let unbonding_len = 4; From d9b7418e6bab1c3805891e88b745e148658fa9d2 Mon Sep 17 00:00:00 2001 From: brentstone Date: Mon, 4 Dec 2023 13:05:50 -0500 Subject: [PATCH 51/81] add other missing e2e tests to CI --- .github/workflows/scripts/e2e.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index 0b121161cf..6e736d16f1 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -25,6 +25,8 @@ "e2e::ledger_tests::pos_rewards": 44, "e2e::ledger_tests::test_invalid_validator_txs": 73, "e2e::ledger_tests::test_bond_queries": 95, + "e2e::ledger_tests::suspend_ledger": 30, + "e2e::ledger_tests::stop_ledger_at_height": 18, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, From 2eca765bb451ecbbb42530b406cb62f8b308aa48 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 09:56:07 +0000 Subject: [PATCH 52/81] End-to-end test fixes --- tests/src/e2e/helpers.rs | 2 +- tests/src/e2e/ibc_tests.rs | 4 ++-- tests/src/e2e/ledger_tests.rs | 26 ++------------------------ 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index d7cea1d79c..9fe5194da6 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -192,7 +192,7 @@ pub fn get_validator_pk(test: &Test, who: &Who) -> Option { }; let mut wallet = get_node_wallet(test, who); let sk = wallet - .find_secret_key(format!("validator-{index}-validator-key"), None) + .find_secret_key(format!("validator-{index}-balance-key"), None) .ok()?; Some(sk.ref_to()) } diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index 7aa6355087..668733e6da 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -1432,7 +1432,7 @@ fn check_balances( "balance", "--owner", ALBERT, "--token", NAM, "--node", &rpc_a, ]; let mut client = run!(test_a, Bin::Client, query_args, Some(40))?; - let expected = "nam: 880000".to_string(); + let expected = "nam: 1900000".to_string(); client.exp_string(&expected)?; client.assert_success(); @@ -1499,7 +1499,7 @@ fn check_balances_after_back( "balance", "--owner", ALBERT, "--token", NAM, "--node", &rpc_a, ]; let mut client = run!(test_a, Bin::Client, query_args, Some(40))?; - let expected = "nam: 930000".to_string(); + let expected = "nam: 1950000".to_string(); client.exp_string(&expected)?; client.assert_success(); diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 5ae380ba5e..985cb2982f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -217,7 +217,7 @@ fn test_node_connectivity_and_consensus() -> Result<()> { for ledger_rpc in &[validator_0_rpc, validator_1_rpc, non_validator_rpc] { let mut client = run!(test, Bin::Client, query_balance_args(ledger_rpc), Some(40))?; - client.exp_string("nam: 980010.1")?; + client.exp_string("nam: 2000010.1")?; client.assert_success(); } @@ -2587,28 +2587,6 @@ fn double_signing_gets_slashed() -> Result<()> { validator_3.exp_string("This node is a validator")?; let _bg_validator_3 = validator_3.background(); - let validator_zero_rpc = get_actor_rpc(&test, &Who::Validator(0)); - // put money in the validator account from its balance account so that it - // can pay gas fees - let tx_args = vec![ - "transfer", - "--source", - "validator-0-balance-key", - "--target", - "validator-0-validator-key", - "--amount", - "100.0", - "--token", - "NAM", - "--node", - &validator_zero_rpc, - ]; - let mut client = - run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; - client.exp_string("Transaction is valid.")?; - - client.assert_success(); - // 2. Copy the first genesis validator base-dir let validator_0_base_dir = test.get_base_dir(&Who::Validator(0)); let validator_0_base_dir_copy = test @@ -2716,7 +2694,7 @@ fn double_signing_gets_slashed() -> Result<()> { "--node", &validator_one_rpc, ]; - let _client = run!(test, Bin::Client, tx_args, Some(40))?; + let _client = run!(test, Bin::Client, tx_args, Some(100))?; // We don't wait for tx result - sometimes the node may crash before while // it's being applied, because the slashed validator will stop voting and // rewards calculation then fails with `InsufficientVotes`. From 9e2c6c170d51a12cea2d2e523f1fb1d9c85d0514 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Thu, 30 Nov 2023 14:07:49 +0000 Subject: [PATCH 53/81] gen_localnet working on mac os --- scripts/gen_localnet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/gen_localnet.py b/scripts/gen_localnet.py index 79f7285d55..ddfbad4bb9 100644 --- a/scripts/gen_localnet.py +++ b/scripts/gen_localnet.py @@ -144,7 +144,7 @@ def edit_parameters(params_toml, **kwargs): print(f"Cannot find wasm directory that is not empty at {WASM_PATH}") sys.exit(1) -system(f"{namadac_bin} --base-dir={BASE_DIR} utils init-network --chain-prefix {CHAIN_PREFIX} --genesis-time {GENESIS_TIME} --templates-path {TEMPLATES_PATH} --wasm-checksums-path {WASM_CHECKSUMS_PATH}") +system(f"{namadac_bin} --base-dir='{BASE_DIR}' utils init-network --chain-prefix {CHAIN_PREFIX} --genesis-time {GENESIS_TIME} --templates-path {TEMPLATES_PATH} --wasm-checksums-path {WASM_CHECKSUMS_PATH}") base_dir_files = os.listdir(BASE_DIR) CHAIN_ID="" @@ -165,7 +165,7 @@ def edit_parameters(params_toml, **kwargs): print(f"Cannot find pre-genesis directory that is not empty at {PRE_GENESIS_PATH}") sys.exit(1) -system(f"NAMADA_NETWORK_CONFIGS_DIR='{temp_dir}' {namadac_bin} --base-dir={BASE_DIR} utils join-network --chain-id {CHAIN_ID} --genesis-validator {GENESIS_VALIDATOR} --pre-genesis-path {PRE_GENESIS_PATH} --dont-prefetch-wasm") +system(f"NAMADA_NETWORK_CONFIGS_DIR='{temp_dir}' {namadac_bin} --base-dir='{BASE_DIR}' utils join-network --chain-id {CHAIN_ID} --genesis-validator {GENESIS_VALIDATOR} --pre-genesis-path {PRE_GENESIS_PATH} --dont-prefetch-wasm") shutil.rmtree(BASE_DIR + '/' + CHAIN_ID + '/wasm/') shutil.move(temp_dir + CHAIN_ID + '/wasm/', BASE_DIR + '/' + CHAIN_ID + '/wasm/') @@ -178,5 +178,5 @@ def edit_parameters(params_toml, **kwargs): shutil.rmtree(temp_dir) print("Run the ledger using the following command:") -print(f"{namada_bin} --base-dir={BASE_DIR} ledger run") +print(f"{namada_bin} --base-dir='{BASE_DIR}' --chain-id '{CHAIN_ID}' ledger run") From 47c1e3127dc0779432c4a6fe48a5b6730fa3a9db Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 10:14:27 +0000 Subject: [PATCH 54/81] Update masp proofs --- ...027024E9AC79985302DD15C96CB57743A74CC3.bin | Bin 9941 -> 9941 bytes ...7ECD8CFD237A237C63AA565FC44893414EE7FC.bin | Bin 9649 -> 9649 bytes ...27E2CFFC5F8055CE2E2FAFB4DE8E7C2216D5F8.bin | Bin 7448 -> 7448 bytes ...B62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin | Bin 7448 -> 7448 bytes ...1B5464810F6974083CC2FEE2756ED1E97B8143.bin | Bin 7448 -> 7448 bytes ...ECBE1A9EAF3496A30D69069DE2FBD293EEC978.bin | Bin 7448 -> 7448 bytes ...E8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin | Bin 24494 -> 24494 bytes ...6D2AEEF55A6F202D8EB4564017DB5BEF872107.bin | Bin 15597 -> 15597 bytes ...4A5C2232840271F032F756953233DB3E53E757.bin | Bin 9941 -> 9941 bytes ...5CC6F78791F8954A0219C07B15344AEE0D2E0F.bin | Bin 10382 -> 10382 bytes ...99481AB5F7C8F57502C5EAB2BF7594EA6CED8F.bin | Bin 15257 -> 15257 bytes ...699E65D4B40A4E1BC9E7A32D63CF28466A2F20.bin | Bin 17018 -> 17018 bytes ...81D5DEF92B9A9BB3A159947560514682CC3877.bin | Bin 7448 -> 7448 bytes 13 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test_fixtures/masp_proofs/30E148B3F9E8D21A41ABB09756027024E9AC79985302DD15C96CB57743A74CC3.bin b/test_fixtures/masp_proofs/30E148B3F9E8D21A41ABB09756027024E9AC79985302DD15C96CB57743A74CC3.bin index 5abe95051451598fa2d1f6a3bd271e04bcf7ed78..3c06d74bb61575d39627d35804b4dc3faf0e1301 100644 GIT binary patch delta 1742 zcmV;<1~K{7P1Q}XJ{cgG_WT#s)9owOkUKv0j z#U4-S{t$`LZtB16u`1!&moz-(LlY3N125pJUyGHFlfD^1AP9m=kW-iBE0k;eifGxn zdemR;M2Qb?vOH-6cG*Y(kCPr6K|$y}^UlfQBEGd|2`1xgl<%IhNrSk2nPHFkSJT%k z*@yyq&GKqFZ9eE#&Vyyijab{~@v|8mMHmpc?c7Ao;j#Dr?pM??ZD| z2o#_HqKE)@;UeuWF-h*J6%VolD6_O3qykXvG&6fLcZfOLFG`=fOqXb9O-IoK9Jw|& zwCMB{nY5ObQGp?X=I8PNfCa{VCY+L@85u}eY|tuyEzuKL0mc>YseV@<)GqxkNh56d z3F?iK87YyS2$R_$K!1eQsxw^YJ}N>EG28N_;LaVGf^YJeG16oIZtedJCj+p%(Rx5X zsPkVgK7H|NP{JO)2`r_4g7OWtolsLJ^+syCCn{u#wA;tAw82n1nh zJ04nozcdgyv-i6#OApHs&ZOoCwEl^>;|PJNt=D{VA5_+JJAY0IvTb=Qe^IM5Xo&n6 zr#B9)pkKAtTD|iTG=wLYxO8YN8F4j40Z!tq&SQ11j-i8AOUm}&W8GI+&N>N{vcy7= zYDRlHzY8=?Eo0VH3b?y0GuTcYn22uN7m5H!0hv_*Lo+m7!uu5O-1t}nEDs8?6LEE5 zEQ;wdEKu09^M9Q5+g~5JA)kLxYwaCK5G?QZA55mZ;#3w8DHPguj%k(P60vu@7+vJ> zmKTVh)8)27e!b#1s3V=xNaP~Z{h?0gF5|$NWq{2s=A4p@GYJCglze!*@di@O+HE(} z`g_WUsr8DSSfJ)?-&m3S6M%oOMoHDe1i5^y%+Ev~Xn)73VuBQZRYiQZl+k*#vbU8q z)tqc_tsnW1PS#q~A*!$pbKO0@BHhYj>Fk^NcvWKP{J0rt;SOb0tly;QiB5NoH<|$8B=g6O^_3I?|4rD6GJ)b(^{>SWWU*LN&HJ5Ea4f5mJ{@l^w3dhLN2WxqZlL|15>+^(Q6$E^k+ zntzfhNA2 z$ZF;YhxMF{KBY5dQ=mh?6OTc$>MQxhdnX!JuvI(yt|FT)hTi2KO`7@myU)(WRlMU! z0DZ^*Sm5K%3R4bp3uI1;VmxVEA> zB~v#%)58;@Boi&9^uNK@_-8V4#ysX4V;2#;n5G4zT;3<>7(;h8TG`vHaFdIOi8yn& zj{Q8GJxdH!kaDFbs`!tN;sB?SW`963?niu?i*slr2ND_p4NGGNhF$b8oDNiggDe8Bd4D&%)x{RV;ntL1hP*Qa?tX?u0YN4Mo7F zME9`8loCY3^#Dn)?7fa1N8urTu3}O&aNQ++;~JMG34nmMrU@P2g)H=Nh)G4;dGC@S z6?g6c;A?;4s)%)up$7%HJxvr^vwyu^CNZ@*I55Dq)~qkrFr}F&ZXOSw%W3wK3>6rR zT*$+C73|AL2q=!n$wyZ zaP_bPPv3kkOjyY%ZV(F_!?BBRrtjk2YoAx!EkNA+KQ2iel|E>EH)o=@XH==^7y4BH znqRn59JVHWq||d-k$j#iHLd}QwjU+6{2#aK;?;SXnitA4c@eija$QA{xU58$Yzqz% k!)@c@P`%d&3KVe(?JUY<{0qYVyqDdBMjhjR43n8AJkh^cz5oCK delta 1742 zcmV;<1~K{7P1Q}XJ{cfcTa9kWb6k@UCriG5F6VbRkV0T?`ll+cnCQ*xD_^UVUKv0j zgU{I=5(l=ZOGRgFVk(-V#M7*&*I~62^82qqYjJ4=lfD^1Al}PrgVDQ6Qw?N*$HI_J z8@9LNoIjiMZpsPFk4OmMA(I{&K|!6am$lgczALxIL8WY`?zL9%`@+?QQsL&apc?c7Aoq!#;K2Sfk2s<0 z<5It5U*`f;J;K zJSn=mzkdw}rGHg~F;DCQ8<@g~^Iu9WFxAAKVHRA_ghAyF^X99gAS`^HkK5%{yAkfZiAoPgs&{$V|C;t zzQW{(+vO!^4I*)oLi;C3M2EQb2E1FPYF{YO?E`24P|%7bqS1Wl<+S0eJu{{9|IAXRa^VJXcYTtAEA9X1EF!H1oC4tVp~N%T}+$!kHdu zAJ85CE9Em%aO+NxnY68#XOEr6pk6({I7_H|&G1efjkdptlXTg%)XD#Umtqd)^s2d4B$vBC=n~6P-Ij+Nh9NL^-c^PM}JJ9ni$p*-A)_*ctBET$T zjenkbK@?34h)Amq?`x5_t@y&{sk*k+?OpOwIrhTg`kXd(GYZ{Xu^%}FxMVXOhpn{! zgsS%Zv!>0>t%uf>th4;Mc%=IC+^|UwbDzFlOWu4|YU{86nw-|0OY|VcoS0AZwwYdWQoBE~xf6i|E9h zqy~F~D0P|?ERAehHWSL)vowq!C%~-zPTM*v4H+vdRss9VjT`v>6r$^TB#YFBmVaYb zM~S=2F9jVgZ-WRSFZ{|*Zk2W&b5*+-Nm(Iu17CY9@p7QwNSO*77g{kK(HJHjB7z(l zkQUyhkJ+^6YF-+u$TQ#KtAR6=5m=Uy-zbT5efO5dq3ob*%pOv(VoUGHoq(QJ*8zME zb0mT_u?q%!M&iKf$DWN;0`9^ym4AsvsHh%|^_2dDk@{1BgbYEAItZp9{S->WH}{WEkiClv6Z|{N#aet*Ip5X!*(Avlz{s>MC` z{-52WjnwEkB8pL@!X(&Q*qR@CWx#?I{)rj*X}Hk%u-J1|oY|<}xxL}FBYzL$6C+71%^EAw*qF_m`Ez;f*HYX9`m4NMOI*{3&>;xE zhL=9TGr8a-R33phyXCuV6_V}dy6-DO1)k-1toZ+<*J9<=K4L`04rb9KhY_+|b#-NX kkOiQUziAaxaQcWGA82^2Qwv#>;fPE8*5B-^2a}m5JYrs5!vFvP diff --git a/test_fixtures/masp_proofs/473EDF0B2908F047110AC52C7F7ECD8CFD237A237C63AA565FC44893414EE7FC.bin b/test_fixtures/masp_proofs/473EDF0B2908F047110AC52C7F7ECD8CFD237A237C63AA565FC44893414EE7FC.bin index 496db42382e4af1f954ae84cf94a2dbab3e79ac3..4b8a998a30a0254f84abe571d6a94700e71b2f08 100644 GIT binary patch delta 1676 zcmV;726OqbOR-C^niwFNXtJNn>xkmKOic@iK+$m-&s&C1ksAjBurnk~-tLu?x)?wp zL$9-p)8QM#_f|cPgE|5S=)t?Z1NttR)j#_TVGh#alNuR8VWXfB0igrd4hK{v#kO5q zfNc02JPugx`R21#GFiL@-7=;G@}yAH=G~idJZ~)9Sn;AOR?O7e!05A@5tVUQ*8+ue z%FZMKA4!?Ap_0}*Zlpm~ssKBbO@}6QBdDUztthj+8KeS#5g|@Cv0BVJR z5-%;!3Rxes9v~(G+I__RCY=6gh2$762*Y+}nZBV&>gscPWp-rJ(el?saVETxazMVC zk!y2TN4^va)=qsEU?2^)MR`QedkpaHDgx>_(M+>~gMiW6KM#QGJ|Er^_9o&PopO{L zBU5$`9Pp*H-yBv0e~QUk=JyMw;kX}dT2bw>b;ztDvb7oAw1R8Ku@R;T%GumYLHi1- z!HvEPJ^JnHO@b4;7%5l#Y+x{ z!km&Wc+3d8q|X=w28aO!#hveYl|RF06&Y@qZg*F)F1d)x#Eq&E{Ts1FtM@m^?7^k6 zpi5(3Aw{&#bdErG_91E}BAbGSGf%F=4Qq;I84c6FT+$olRh)MH4;VO5w@8!}uwb3^ zweI7G*@0r|e{ldjXO>&m+)h}!j%n1V1RShWe8sd3A9#MNZw6;SX(>Pspv9yAc7t#W zO(X5#VrMm|AO3RVs0ELccdRmV(q>nXjv$t2-ZMXNxqM`1OHIa)F57~lg^TW|TG+_* zlslzZz%X*&`I=66F8xtvBKYee*R_HmGLNjb9wE4$f0*F7X5%&jJu+b-ZLc+o6GFuV z5jmf!v%oh48plj!HTY3v((;-7^+nqXVfQV>_noi+b$T#n&?ibcWm!ff4t7Rk%FKFDe50h@dSQst>>YS+ZHF|4z267FQ3A z=_xazf33Dx4!L0hQQDOkb^caY*dh|)(dL_=K58=(c?|PG{&maG8cU?t9P{?MnC1xXgOYimi%xWR+eH5v3NR*^PTR_Z&iuckg8Nz z&U#riFe+kRQ8rfX02OHa9mDF$r%Ur$**#v7sgR}%a+TLz2Y-*rKRt-b^iOBB9efbQXD4an(MfY zdC1G=FA@3nNew#D{v1N6&7pHFJrcUme^51sX=O1Ll4F0g} z=7}n-wZ+1NyOnE5h(2aQheec9*J>RK4>#v<;9i^)O7-Sv(kOk@3SH~+cT9=(f9BQz z<0%_&h+zRAlI_bJ29`x@S}As%8?RfchS^H`(p7^Q;~1vt0f%>KAzQho+f^;A`N8+! z#l4&_5`OytK(h~|c>;3otEGDm0Sn-4{0$hd$TMR2x^HTo*Cv&Pw)wRVM>j{IlXp3< z83^&))WX$kN}5%}qK=txEMOmDf67K*Sx2R|BYrOt>=|UV(dOaGxbCJ4Op8{1ltP2+ z^PtO$wU1#Q!Oj(wS`5AXsw)3d#O8@iK|_pj^t=a!u)#S9(gT)rUkAq-ZyzcEgNGuc?o)2#vM1DTh?j-OO5RadGk1@= z3)K1H3M!<>DK6lEY54$*1U`C8zl00XqbNl%7?a%Y2Vm9mZmXGulTMX@g9 WvBd18SpkRlk`z@&EXDwnbtOEYq&TSn delta 1676 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 Ycl&QW6+G^+9Nnhqcsw$t2$SR-G~M9*(EtDd diff --git a/test_fixtures/masp_proofs/4CCB9ADD3188CD893508CBB4FCB62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin b/test_fixtures/masp_proofs/4CCB9ADD3188CD893508CBB4FCB62D08DAA52F4DE496209BDAEFD36E75EAE98D.bin index e95e73953ed6015ec87fbee783238a40bf2604e4..015abca093093f9449e9648b9c47e4dd5da08f4f 100644 GIT binary patch delta 1002 zcmV)YZLSWAnn>60u` z|3u&9p0j@zB?Etq)gThs0n=0+j!9?1aul&IQA_*;2cfL!P*HH2y@dpi9Z$+Fcsgi1 zvpp6>j&U!L#uzZ?Kk&m2cb>);W3x&qK4y+TTdFBR5Ji zl?Y@PW+V)=Zx|VX__N?mU>zMatRVf%yBADWz{=;R;Xi*4)Kw{NY17Lo1|KF5wxB2X zgLY?(rt#QwP`<41KWcoU=D#M9eA05D!~=qX^`xuu9Z{E<223{ZJ0i9SPTPLlfW|WK zHK5ht&SBN{S&Pn<0Wgav>lrhf0r(YIe1g0mU;8^64qYt&r52<~11j~skS|&6m>?+J zZQf8aBE^3rf~)Bd*_!(~QWCbrJ&~7PQHt`G@;_$U=t<}M8M$99t_Oyy5VsiRM&VMk ze(<2(-LoQFFOJL-zyeaef#b+PQ#}$7Eli|{B$Ts--vGtTShI7w9`2-gK-)y<%5+-v zx9O)H#EgpNY0fJdpEI%dIf9Zj?&l_#Atwh6_oIK_-2R8V&+=@!@T*pZWXrM7J)<4+ zsGX{>qus{r;X`(1tRaIod#wcMPP*k;vP5Wu``g2-am->DCEiMS^i%zCm z)qE839PM8>KB&U~s4VDzD+Dr+-_n>&i^J!i&z{n?j) YJF5T|oHfws&Y|jURDVVZ2$SR-G;sO!$N&HU delta 1002 zcmV|x^CkQTfacZx9Zb-;eauGJ#bF)_v^a3EBWhA@sZ2!t$i*x|- z7HDDbcT26AZa8zeEtWP(y<}{&YZLSWAczKFXqD>AzI$ml?61LzeH+{jPo2rU_;`o<+V+|KASFkMvn_E-~63Sbn_%zFj0p~?1f z21&~<$xMDaxNG6;kV5KCmmj0kJj7z8|F*YSUm{=XEzm(rXoS7xes=RH=o zm*nMa1Z1M0EuJd4Td;7K=Q$};Ubq=@d$iBjfeAjSHsh%;qYNnD zz$ST94)Zl%18F7U zF;a1VDg-)h?nSj+XNWtF?E=l2a$2AHZxF*Vwx@raJ0b$#&=Ld4l_aohKwzE!aj@4tZeA(|IGzWf#FYc<5{-Ts_IUP6@Y zo`4idsiQ&g9Yuv{BPDcO!wVZAtq~h2ftdqD-1J;|eX~}?|D8qO=5aE!s zVqbrmy(U=XNcN&>Dmv)_)Ijx2Gh~#m^T+gQtg~UYcytaSj5HGbq7aj1oNrQM_96It zCvYyF#J{7U{73@YHFGb7KvkTNmJ-c0^P^kVLyo&}!e$u|6tJrA!IR>Q_jyGf()#B( z5O4S<&OBZGY#7O$moo&l(>ceVKJ+JdO(=hfanwUkS%vTftqW>{cp?CP&3ZGBX3QA) zfw~N<7PY4gvzLZGHb@O)$`RAYOygn!xF1r@44vTcp$KU z_aa~hgibc5vgP)3ZC^#wvmKtayrd(LLH}?0Y0$<#T}{R9`ObvG>&T2^E!K(_|_6j3TDVlqp9q(Lk;u- zAoK&pqvyZcV};DRhc4n1`R0l!+$VLH#FaxnG8jp2ZnIYq^a3D`X9WD+CHF!is{J;B#iz2Pmd zgrwUD5^U$fb_snWJslZBpj5k7{olF4@XO(q$-#jw|?Apde=fel41CKf!N;l%iAJ^{mP`Y(}3!q2!6 z)jD(TxD6FxtTS|e-s>+cL1e?0>NtROUfo*-dXQg^N%3mw-jG5?9K^f4s@|`+9Su8G z-y&K{sokJz{akk>tp z?DoVF1_v#}4I!=t-sAndIPbg#COs+3z5^1B)0PQZ8Y!2b!dPIB6wo%hkhUIoc1Q-c zAX)bRcF{0m`Ag@$u0iwUI@xlWX1nDcN+CpAFKye)0_GAgIBUn$5un z&FO=BQDSzR`!WA}55K^82$H)C$e^rYD@wJS%BN$|GLi$#Wvk-e5-KTjIO&s*G$3;F zpj3h7IGsw(v4IfxJO+TV8XvS%OpnU*Q~x{}hDrJPrdKhVyu1Ba$3RjFIGUJi<-JDV z%x~nW3fnXaJ;GLqijwq+pMT4QRY5Jj$SVzoW*0y+mmP+YDfz9V00_;t(q3Ph`)ESW YZMJuM*AaR_1Q>&WbF#R@1e4?(G@(=P^Z)<= delta 1002 zcmVw(;TEJp{kLk;u- zAcRcp^V3wmQ|}IS4dy^R0&qql-Z$9Vi{I~#!H(^o(X&?&^a3D4Sx!N}%Z_44cAo`Y zy=pe?eCPl5T?kDj)_UknUJS~!YZLSWAW+TLGhB)VtZC`~u#Bbt>0Q=Q-IF12Hf zC6Tqn5VL<4B?Et%e6Rjj=;Fw+aEHFC)4-hq{7fdXl`UwzKO>NkajzKLCwjuE3o8Lw zLT0K|Ex>7EK@D*9P zp{*X|M(buZwxTDoj+4G@9Kc+I?YlO8Wi%7hRT(L3SZ!4sHT;TweU0<4`tZ_1uhOvB z5TXj{bpXwRz%lzsj-OoIFfJImnOdYBA&_tl`~kjMMk6-IiwMmWv;vO;x9!|fTiKVK zuRZyAG53E6CRcu=ouP6NNUyNOOR z1jV@59>z&J`YndjejBHrE8a*xYSz_CISZ!7(l$zdEmQ?S^tCw%U$lK4fk&-PoNboR z(~+p5y+gqcp6CTru>&qWkMzI~^YkS78Fsv`60d(2%Z#-7Gb)6p#b(|bKL*z1hv4?4 znhPbed<+xbqpF?&!hRr7g|Cc0H_}5Y_T^nlNok3Y!1gF1%B_?>$^}lZ)-B2M8x#N7 z1SzN%kK4ugZ{v;YD-TgBBPVo@UQG8B!d(!}L^F^)9{2tUu^)XJsXT!sh$`PAs~Do< zdrW_Idol(-u5XZrdIAZd=IJI$p&P-L*Ep=X-5lr1<60C6jJqV2^6!vU;pGt5|8Fl& zNMXlHm>IPNT|55%7sEJUl%h_ww2_L-ZL`0I107H`bl2Xp))CWztgU!W($OYy!l8&0 z*{L9uGIY1hZ>!y3`uJV1JnNZ@r}!p--rIk&=)(dSY;+n-1kAO8YkPE%LP@c5Y0>^m zJP2G^SOI2{(}_sW3~k8$R++wb@17anNKM=X3g;FI?;n7{(`bkHIANrr)cuuOsG(bM zXs;M|R^XrCr5>rBWXRNlTM&57l3`}Z>5_=}_Y*-6)1dV;LI_^q!CA?h&Jkm>BA-T< zAEewLc$hM%@gTq@1)AzMIz&IB=N9@mHBE1zMetbF@GiFb$6qE|4dhXKV)lpKCrV!P Y!b;0^W;0vLt#ao`UGte@36ta;G?NSSOaK4? diff --git a/test_fixtures/masp_proofs/8BA2DA741BF1FE1CDEC5295AE3ECBE1A9EAF3496A30D69069DE2FBD293EEC978.bin b/test_fixtures/masp_proofs/8BA2DA741BF1FE1CDEC5295AE3ECBE1A9EAF3496A30D69069DE2FBD293EEC978.bin index a8c9a560f847fe28c8dd1ca7848bf3d23eb5259c..8f5274df04dc2c6ceca2bf75b48b6f23ab85445a 100644 GIT binary patch delta 1002 zcmV3APzIcNb$ow!eT3*Pxk4{y|7^lp8@Oc- zgwlZ+UqKQMyl=6GK;C=Kk%U{vjy09en@2q^6Zb1ua11N9l)fJGc9MB6B}fi%iSf4d zmhX}vEIvo*h3(QJQ?;_O5+}erR5o&9adj5kC{^pl))nzFssbe&3{pqhq`~P4F$%BY zqKO?a)X9I&NykemRZFP<8rlV0;+ErJ9g+>%S%KhitK{SVAmdKd6A&n&^0lMA@j!?` zv$Y|`_aYSloWlcpimL8{X0t0n;R^lrf=Yk%1Sv67@1|p+F5_73h^Yx1I=*n75?8Q9QL!GVZpHO)+zSJ z)Zn|7>R#;Z|IV?40!cVUX3^99C4V^R9ggJv#26W2w#c43p#>G=PTd82|tP delta 1002 zcmVNf|LXnbipk`Xsj4HshLk;u- zAbW0aD~XRlyB2HyT5H(Dk)0Fdb{**fkXMi5TYeo7z_V8n^a3FKiEOpQL=96by)CG_)= z(i<`cS|`lQ-H_G<(sLT;#)32jLI_jk2wiW%z1q`sp4iB)?o{lnT^sW?!9#b2Dw zmd3%=0)c-Pd!J@mG>F+X)`Ol~ae+e~hRg{IC@^`bjFq6#(!%01>5Fp~B zZ);DfDyw49P_}9Aw(7pRH_9D=L2Ga8TQinv=k|XyaDkWHQ)B$OCY;CDX8!JLek0EC zr{Newr#HV1z`qR)TA0PM2QoWYmKpRvLexY051weVtT1HnS;))%Sqe-*jyonqORxtr!pW1 zJ6?bJlEeF}fGw2{L`VM3kM*n@Tx`deF%87aNa>0ldug)mvGmv6w^?W&=eDk1v!_;h zSvK;2t2mEpee3T$3gg26JsA_IWi}rB@L5Jlrjj^rolT7HU$7|SgryX39o4DE?ykOa z?dC{Ai%NFbivJkmq3nE;2)#eVVE<7-@(zD4Xdg^jC;8I6ft5_Q@#S!Z+;(;#@%nw8 zDvn6enE;wbFH59y7dbe4bM%|Sv%6%%C1WqBty_!+1wUA^g`r6qHF)pOv9@wn72X=b z66?sC4w2ZTi1s|P6Dk6)AB`K>2PKw}R^uTUR%tsK;VIa+JgBt+vW(j1PvmKfp}$6c zJ58xT)!!39=646|Fb>j)6VpmtPAjW(fHEPqh&`$NwaiyJG|N~ks0Hq)$ diff --git a/test_fixtures/masp_proofs/978C35E058808D61F0E265D72DE8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin b/test_fixtures/masp_proofs/978C35E058808D61F0E265D72DE8DD6A8E6226394EA6E3DFE1CFC10F69C0ACE0.bin index 06926fb5befcee3e11c47b388e13801d856b5362..a70424fea0369e855cbc6bb477f28250071e698c 100644 GIT binary patch delta 4170 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 UM!pth=8LHLbf9lR1Cuged<&NTNB{r; delta 4170 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 Myi(&v1e1|GTu*LRGynhq delta 2810 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 M5E3kJ1(T6HT>cVY5&!@I diff --git a/test_fixtures/masp_proofs/BB9AA71A4227C9E62948CBA8DB4A5C2232840271F032F756953233DB3E53E757.bin b/test_fixtures/masp_proofs/BB9AA71A4227C9E62948CBA8DB4A5C2232840271F032F756953233DB3E53E757.bin index ecdd51d5db955a7751dc46818651c02457d964b6..84b4675138ac522106edc9521cbd33e5ecb0b90d 100644 GIT binary patch delta 1742 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 diff --git a/test_fixtures/masp_proofs/C506FA6C2EFB37B06CD5A473AF5CC6F78791F8954A0219C07B15344AEE0D2E0F.bin b/test_fixtures/masp_proofs/C506FA6C2EFB37B06CD5A473AF5CC6F78791F8954A0219C07B15344AEE0D2E0F.bin index 43b47e8c43e260e618681bb3042860412c95f910..5b3f70956f0ae464ae32d48db67371263d1f16bc 100644 GIT binary patch delta 1781 zcmVs6^Nml7q})al z{CYx~(XI(wT%cHU>TxY)Inp*0ZFfU_PwdJwIRmh3RE;*WEhVMnhZ51;pGy?Lm;x0` zAqblHcgOz!An4&r|5zv?jH(@?rrxaW8Y|KkaI3SC9Hat&K;s_QL57AXo^I>qrADk# zi5(^hxDF&1KkG}JokivELNo-!pF=6Q9FRB(9rhP(r3A?*=&2vOL1^vcP%I;k1h^jP;# zp7`Zr%lMP&FcrZR>N`uJ&d3kF=kRK(|z~uVnlrE_1pt2HU_lYVRRy+)s(f z&f`(9JU=M4xHB}wsh;C&8H2x?4S4TkDZ*^~4`%YG8QKus{v_G`ijy%LDUqEBlgc7M ze~4bUep-imEa^GaF9VH|YVAwbRAuA%ei9PQ{2&D@QYHNp-p`;nwG0_)Z38x>xvXcx zI)xR-p_R!=cnUW0`X}0Ia-a0r90)v2MF4oyWX6_&uQBp*(18@~!cnW!-x5E(jBKeh z=n%oH-*1KjFm?R($Hm&>l{)cPdmv9he`KI18HKi6HMkmGAq4B}O>MOy2;}l7kO1DJ zn8qJil3nXlDmfdrnhTPuGv5~s>9jH&3Kx7gt08kjs>6Cuv$C!6aM77i<;%Ekc^aSTEYuvE^)& zlYk_@ss@dL0ey*+L*c%>oxGJb8ygxzA+Wy+_~) zkn}C?99n+LB(w>rCYYdscph^-e}2|OHn&-)VX%nRIMar-{9hx|+(qGSkP%M~F4PF;nJ4Lg1d;4z?g1ldf?x;rDxjD|w^ z4C#Q<is4hLsMX$lN;#rx=qc}Y_3u9*`LR9`Y)bIve?*N40h5PC z$~PZ!%GeM)JCLXVcY)&W{-bM-5G{#)N-_NVL$OJuKD45JcD>s6CBvrBUb!T5VX!5@ zUf)c4LQE^fFGh>St9Ns?Vc={bSO`j4PRcP}m3CBEo)nsPA}W);kq#uWt&3)M4F@I* zb2%+Dp@g!aReUb1YSF>|f0!)F*udxg4odB0jDWuuUwe8Qk{z~RL7YK9D$LtgpqpdQ zA0YSNlcxqY-|y0zf3SwK<6{Zu3OHw&G(<#V3l(xcXo(rf^0mxjT;l;L&yv}?Hz#TePk zFtG-o_UtpKO_;-VVf~T@G=bwBx6hh|I}Iy5AU`&4&wX@LsR0hGmwrMbN`Zhn{?2K; zyldQ51Z`|Wuk@Xbe=wO063F@-lkC|1ue&5s?k@#Jrs%H`4|-z*&ZnyRylY2Wcs*Pm z`qhvuW9ti-FSuzFawvmXvOYBKOXjFXm%KJpiHMTsrW}P+?TS$1m^)6UBBlPvWy2jG zZXv*AI6?0`2ce9D58h%xayI&H2%QoMU}?~zS2rwb^DU6qe*zY!$t~B;iGNh%i2CM% zt8vLw0lSOjREIXJRC*egT|6XB*4An733!V<62J!|4ag$f^+2GCaA`7qyWaRleTuVW zn&vO0x0lsmB`d1#RB+9H@6eYP+NHJM*7h)F@sMk1Ag?AbxpxnIO1DI!9pJ_-I)@UF znUVxgU^eFIf9`jfiS$1BgN>o4HUEo(V8QjxnhRjN^?j0rW(4ssWEiuJ9ow)1G0q9T zP;n#eMX?O~Cd6(+9B?EUjz>%97V;-If7xs4HTXufp2?!d9IETeC2}FL_E@f*Iha6t zF)wRHyu659M>Zn)+*3tj#%9u1!rmqHd~K6inq*qLN|A1fs`VW9VhU7C^OcFu4II$q zdG*;=o@ufvw2ej-hl0ILU3W!~A X7$gUiUMMJ&UMLg@1^@s600000nTc?v delta 1781 zcmV5-uacJ^<{;=Mh}T(qYX5}6sc+Mtk({az7-9znB{j~hTB zybO}7C2`vGqB_i4fst+|cYZrZ$*qy=RwCkIA@I!ulkXcrVIS}0N^JXK!?qj!_k_Q> zh*Ztw9CHXR6Uft3_{XgryZarwNpl3)2xByWS6odJ;OB+ji~i}DvQ1I-y-(Vs)dJ6L zx0g2b@b1Izd#FTff~J2q%dAvBmCryC(?pPlGrF^p9Hat&uRdA%s>2lm+E5EJ_v>SP z7c^eZqVWG`*S*P=N>8z`Za&$g%S)~maZxC<(An{tD-l1eh@*obOiAK&gDEO3h*tv~ z2<-B`FP@V;vLLxVW}f1YuMKSAG<7Mqnx|=Iq(YacfzjzT)*jRf(r?_AI;k1h^jP;# zp7`Zr%lMP(b%)IEIc=&t1*ij%IWwK6QI}yBpm=6+YESSa&1@C<#6fFei8_ zNtTSyd6Q@_w}}fD1i--Ld~vf|pU=dDSc_-i--NWnqBvD?!BqQ}?vpVaDUqEBlgc7M zf3f@1E$dK9@5iR$6b!T5lFHj*MaBhy}BRxK#Gw zx)I}{s1j8{ABw>#*KXI}!=XnMwtCp#f30Q{(93M0WrQ&=LY(KEL_xCki6}Bc3I463 zeu^qj_ZjJi?sM;WB&T`aQSs;lX&Vd-Lt zcku+p1|o+fhQ|O^Jg&<*<+$VK+3>Jb6z%ph?z?CdiNBch^B)3j~=6!y+2XEo)0OgWo+kNTR1v% ze=GZB{YPSN9^IxKNFD4c^_Q67f6?IW0-DiE3kwTGc=8;=q4_JEM?>^|MM@UUC8S20 zj#zP)wih+UoM{kzjaF=|tFp21ltMgLvPbIwjfq^0IW1BL?M^Mjg~5)~d(6IF)59`( zZ5TS_qs}{caGRwAMsXHQ;#cR8?#=(?ugypXBui!{XGk$lDmI(l|M?ube^aWb37MR0 zo;=^AMyos!VqFFScffoQK&{XRy5FEWu%CzFGkwNtJU=+WbN!G#2WCv~R-mmlix?v--2|&`-_(X8AFEykx@J*6;FAqLP!r8 zC82VmbBTQsb_(Gv?P^&vbaL821podFS9L*@0PJkG{=AbAenmI!v4kFT?hYj+ZFzu( ze;OL9QjFvu;h941Uk(bN#o1ptO+Ufea=rk3a{NulVf0^k^%%s!J1$8Av zVl=KqcYvse=2x*QXeqo7J15C!#x1(?Mqhjwe4{Q4?besORd9O2%BJcWUAmpkEt!2l z)C5ouFMNAbHoI*wymgoUt8<#5d#xCym2{pdRBBb6>1|!qP|_@7l4WzX;*IJ!*|{_t zq$;yqK(G}Ms^xrSfA}l4{ryT5p-NRQn|_z!PW2EYN zGbw|*z?IR!Pq(;BabLmVuig%x5lgBZo!ZvX%D0x2mavaGBkN2dq$evsE(10V5f)lH z751ZK8>@Qc%+P$}4Fr|w3bvV4_iHs+UKoUJQ3^i@6^JY1e+Je}lB&J$hgf^7r%-&b z-^4&SKFTQ9Jc5k8@I&x$^u+H^YuUlOac|aMLTO930j8%V$r~H+^pKsn1PGO+>r5}f z5E-|tbcCi@E-k?Q8nvOMM_&Sr$ATlbY5E&kfZF8dF}LvP_Fkyxt55G! zd6D(K+gsIG^rQ+HO0Db_GC16cllkUr9X|{jr_g^{N)K)C+d+Y3lS#6NdmRWU%N;f3 zP8IG0$O=hh=C#;!UO){_RwiL;7%3+6BJS*Z8&G&Ks*ry99}uj1yzo)Nl8{6fN+F%A XpA80+UMMJ&UMLg@1^@s600000nLBQe diff --git a/test_fixtures/masp_proofs/D007D1734AD42D34174F04566899481AB5F7C8F57502C5EAB2BF7594EA6CED8F.bin b/test_fixtures/masp_proofs/D007D1734AD42D34174F04566899481AB5F7C8F57502C5EAB2BF7594EA6CED8F.bin index ea4d0108ba2e8936875ef7502fa52fa5fb5e0b35..226549297567b3cadcb581d88e8979aec3103c66 100644 GIT binary patch delta 2749 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 DixFH6 delta 2749 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 Dju1Rq diff --git a/test_fixtures/masp_proofs/DEDF664AD06184041515677A72699E65D4B40A4E1BC9E7A32D63CF28466A2F20.bin b/test_fixtures/masp_proofs/DEDF664AD06184041515677A72699E65D4B40A4E1BC9E7A32D63CF28466A2F20.bin index ef38a4b22f3311265d8019360433c06e0aacf795..26a4dcbf70e9b1e42c001c618f68efa1c56fb74f 100644 GIT binary patch delta 3073 zcmV+c4F2=_gaP`50k9h;Akt+B`10OL!yFK=BA`-JFg5HZ52)A}U_`Dt1ULGv9g{mI zKp+5$-nD<9>=@++;#vAn_t6iU>}@rm7GidVpFq+(Eyk0ZCP888vC$f#0Oqcw(}m|V zy~L$K&^*aMnN;6ShUu?a=9SWCr3+RBHCar?^Wb+TR_LpQpL-Bad+Dh3+m(;t+hj}z zy4U5XR!QJ*1%i#kI+rTyqOC^>q$q1GFa)#pq15LbvppxI0)M#LQlYD@Tbnzr#aLWS zI9}E!r*@|ER?F(lbH2o9XNIa`%Zheh&9ZEI!?*S2oDq!QA$<2j9|+Co7b0q3 ze`gIy&?W@CPHTi)N|6?~ldlO!@T%fah7R13BUrNHh|XvKwsX+@`84i|zH$ZE?=D>$ zY9=&d~f_H6{%JS|QxYiD8&eYjcT-f!Z^!cr)@n3|8>H%)Z$e5zI z0D%(~UADrYmpv{8>8nIic&)ZDQ%r8aJL2Y%n-eddT;ld9T1#e}SU5yJ4r%H0MAqO4 zl60M!i0n&!vSlhEG#X0ni)Q;Kj9Bit-)6i3NQcelo73*|PN1`NEtLX)jr`}0X5K4A z*F|BID(A(m=Ez9dY@j_dI&Nfw7(_K@zZ?D9CU?dqsus*0TI9bup>48U(){qpnAu%b5~19dqK zmpoeI7x&YYsR}su-UAP_h%l7`e_$-DZ#|{v=vy;lu|Joi{pJ_fUe6tBWOUYB7;he@ zRskWc4P4~pj5u1LzT5`fD;+}+j`%op3Mq3_xu8hwfBSmYOusal3(wLUuuJu0jhLF!!1)GKuE#3uNHWLqvz#-P0)LhS zlKrBnyO*Ty3u~p)w%N=jNB%=$-lSM7sFra^>-j!}X5;1Ark!+3*L@9;9NHeeuu1i| za5A&{Rn7G#)IoiO01-l?_}Gp{*5>Pys$MJcJoo5Nwgs8dbW?V4yhdK&n1KB?w^(GeIJ2-el>$))Fnxv7p}@e*?L&I%=!J1MF6b?BCwq}z z9_X*!_X~2heu9tR6QY))TcKtyBZN!@lFQG#@>+@z)$=|$ek#RZ=e%uI`_^%``F?rs~m;idcgNCKC?+ zbZ{}Ab%4z#m>;W7Z9w>wPpb=$4{DcC31?a~czWZ!3J2zr5fEl#Kn)IpA0#`Kv}ko= z`T~PW0O_;lYVKnG|3(nndhfUfWa;(wEJRRPQ-QdSxB`TKANJj5Gbv5AbpK}i$N5kF zhM^|7;_A&AgR+W;pS?OO(44-e^D#d%A=kXA=fg4uKNiA9vglxSjBmt6y@CP#0ggx- zoqmP9kJxs^bc1!JNaeiX(A_xaXdasZh*kGRns4gcu_JmMlhNtL7t09BxwYh;rqnZy0T6{R-ZspOZE%Qus!}W^ zW{EW!4bAf>ua`$p!Ip8EjZ=prtjhnvOonZIZk6DF%JFBKxTT8P51W=c%r8J2sbx%6 z%g<^@ttGT$1w#g&6I5!W4EGm}?QdD%k4m3H*yJ+Sh0~5nx==yax)CB46ITqJS*~6f znNA+jHt82-s6p4*z#XK@y*@+l@CmSi$e8n)IcgzIFGqc?64DqZl)pxaYOKJTtka+9 zn?{CzzW727wZ!8KRLiS#O7FkDgUv~g(hE(j&Tyc_LMTR@)!OtEQC3odwePD1U?Ysq z9E1*sw7zaw{Yr#$QB6}sN+Q=z4~oz@$bhJzrq~7I;K1`1Xwu!za2JHpqsoUW31_!G z8F+q}4oqk6+CA71+=vFE+!xKvbrAXk9nH0Wu$Nhodx$Nl?K)JoK#p9$+;>S~pQ|V6 zkw92GS8?)JQb!wdq2X~jsEN`a;Y5xZNB0KlM+eTb@EsQn0kra1dLXOyikL%U zf2B@LME{HXw2UK1uG6=Twt3S}e5@o4l4;ks`Fcla0Im|4eFCy!ZLUqvT{91VD0Qg^ zjo1jeVw{ZvdJ~`&s#c-sRa&_gSAAA0sn%Y;nPRBh zZIR3VtoTyt_CLrBgzsl*R$kztqD6GIn{_0hG$l#VfIgP<;%-O#+pm!C& z;kAhNVzN`JON0%@1usSlPMEa?*zc11FKbUE#G)Wzr)kd~lNuEawc0>`<2=)2^n_?% z)xU>3yKe(D{d0l%UZs|9VMa$j8!oTZmDfzV=T>p!ES@y0++yZEPu{T<`V9hjt?i<* zCHqTTEB>eQZ$-IfliZRgdJR~#FkZ~}11`9Y^KSi$(7q|}HSnJHw_UV=P~!;xuhJ(Z z$zD?(gt631F^4p(yj?~K4TP<-cfH&eD$O7`3{#zi2)!et5Eg#@aYYo))JkAMMxZQ=E@b#UJ5tLxUy zFt$;}bbo_lQhE&YPbXgZPkRyE#NLlqxEl;N0`ju$6j)k>p_n&6w7yDV5XFBf}Q!*o>-a_&KRO?S?hfFvo{CyURjdYKF*7e z5A`X$S#5e4^0fq{om*?qz88|tzBP58%8j;aH6cHIDdjmmYhQ5CDQi{L$I_i4X0&LUUH2D13Z$ zxI*n0Bhyj#Dg;_UR<`0cahIuQU(*0dTz(azwQV>((hdESkkk$meB%9_ORc`Zet!{^ z1wD`Pnr0Fe43X!+5V!;5>iP9CD>=!_q5x{f&A#?fU3wb<2_S@N=)eI)^R8M|+RHp; zoZ~be7ID3<+uR2F%kOf)BAf_pgvKu;UBgPz+(0$HN2{9@B46DEv0N=W46`|Qw~*{p Pmu?b8)X@nClNLf`e=iEI delta 3073 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 YD&GnM_!Cb Date: Tue, 5 Dec 2023 12:50:40 +0000 Subject: [PATCH 55/81] Make `vp_user` a constant --- apps/src/lib/cli.rs | 3 ++- apps/src/lib/config/genesis.rs | 4 ++-- apps/src/lib/config/genesis/transactions.rs | 2 +- apps/src/lib/config/genesis/utils.rs | 3 +++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index ffc5e9e420..b7e2f9b1ea 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6779,8 +6779,9 @@ pub mod args { impl Args for InitGenesisEstablishedAccount { fn parse(matches: &ArgMatches) -> Self { + use crate::config::genesis::utils::VP_USER; let wallet_aliases = ALIAS_MANY.parse(matches); - let vp = VP.parse(matches).unwrap_or_else(|| "vp_user".to_string()); + let vp = VP.parse(matches).unwrap_or_else(|| VP_USER.to_string()); let threshold = THRESOLD.parse(matches).unwrap_or(1); let output_path = PATH.parse(matches); Self { diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index b16a337972..38128e403b 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -443,7 +443,7 @@ pub fn make_dev_genesis( genesis.transactions.established_account.as_mut().unwrap(); let tx = transactions::EstablishedAccountTx { - vp: "vp_user".to_string(), + vp: utils::VP_USER.to_string(), public_keys: vec![StringEncoded::new( consensus_keypair.ref_to(), )], @@ -458,7 +458,7 @@ pub fn make_dev_genesis( let validator_account_tx = transactions::ValidatorAccountTx { address: StringEncoded::new(address.clone()), - vp: "vp_user".to_string(), + vp: utils::VP_USER.to_string(), commission_rate: Dec::new(5, 2).expect("This can't fail"), max_commission_rate_change: Dec::new(1, 2) .expect("This can't fail"), diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 94e0eefb33..46ce5496d1 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -270,7 +270,7 @@ pub fn init_validator( validator_wallet.eth_cold_key.ref_to(), ), // No custom validator VPs yet - vp: "vp_user".to_string(), + vp: utils::VP_USER.to_string(), commission_rate, max_commission_rate_change, net_address, diff --git a/apps/src/lib/config/genesis/utils.rs b/apps/src/lib/config/genesis/utils.rs index 7918f8bc5b..cfa2dc8d23 100644 --- a/apps/src/lib/config/genesis/utils.rs +++ b/apps/src/lib/config/genesis/utils.rs @@ -14,6 +14,9 @@ use tokio::sync::RwLock; use crate::wallet::CliWalletUtils; +/// Validity predicaty assigned to established accounts. +pub const VP_USER: &str = "vp_user"; + pub fn read_toml( path: &Path, which_file: &str, From 9d6ed9f48c5a310e3b14ae3518ec9e1d3e7f74b7 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 12:54:06 +0000 Subject: [PATCH 56/81] Convert some static strs to consts --- apps/src/lib/wallet/defaults.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index 19c8eda366..7f5a92c03b 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -176,6 +176,13 @@ mod dev { VALIDATOR_WALLET.consensus_key.clone() } + /// The name of a file that is unique to the project's root directory. + const PROJECT_ROOT_UNIQUE_FILE: &str = "rust-toolchain.toml"; + + /// The pre-genesis directory of `validator-0`. + const VALIDATOR_0_PREGENESIS_DIR: &str = + "genesis/localnet/src/pre-genesis/validator-0"; + lazy_static! { static ref PREGENESIS_WALLET: Wallet = { let mut root_dir = std::env::current_dir() @@ -183,7 +190,7 @@ mod dev { .canonicalize() .expect("Current directory should exist"); // Find the project root dir - while !root_dir.join("rust-toolchain.toml").exists() { + while !root_dir.join(PROJECT_ROOT_UNIQUE_FILE).exists() { root_dir.pop(); } let path = root_dir.join("genesis/localnet/src/pre-genesis"); @@ -196,11 +203,11 @@ mod dev { .canonicalize() .expect("Current directory should exist"); // Find the project root dir - while !root_dir.join("rust-toolchain.toml").exists() { + while !root_dir.join(PROJECT_ROOT_UNIQUE_FILE).exists() { root_dir.pop(); } let path = - root_dir.join("genesis/localnet/src/pre-genesis/validator-0"); + root_dir.join(VALIDATOR_0_PREGENESIS_DIR); crate::wallet::pre_genesis::load(&path).unwrap() }; } From b3e90088184a5fc35539dbdd84726d7cf8818806 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 12:55:28 +0000 Subject: [PATCH 57/81] Fix typo --- apps/src/lib/config/genesis/transactions.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 46ce5496d1..6bc4692ab9 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -108,7 +108,7 @@ fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { salt: [0; 8], data: data.serialize_to_vec(), }); - let pk = get_sentinnel_pubkey(); + let pk = get_sentinel_pubkey(); tx.add_wrapper( Fee { amount_per_gas_unit: Default::default(), @@ -124,7 +124,7 @@ fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { /// Get a dummy public key. #[inline] -fn get_sentinnel_pubkey() -> common::PublicKey { +fn get_sentinel_pubkey() -> common::PublicKey { common::SecretKey::Ed25519(ed25519::SigScheme::from_bytes([0; 32])).ref_to() } @@ -708,7 +708,7 @@ impl Signed { account_public_keys_map: Some(pks.iter().cloned().collect()), public_keys: pks.clone(), threshold, - fee_payer: get_sentinnel_pubkey(), + fee_payer: get_sentinel_pubkey(), }; let mut tx = self.data.tx_to_sign(); @@ -737,7 +737,7 @@ impl Signed { _parts: HashSet, _user: (), ) -> Result { - if pubkey == get_sentinnel_pubkey() { + if pubkey == get_sentinel_pubkey() { Ok(tx) } else { Err(namada_sdk::error::Error::Other(format!( From a0194c76a17ab1daf781f295efd611c2c01f1640 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 13:13:03 +0000 Subject: [PATCH 58/81] Use a Unix epoch as a pre-genesis tx timestamp --- apps/src/lib/config/genesis/transactions.rs | 13 ++++++++----- core/src/types/time.rs | 10 ++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 6bc4692ab9..9fb2aedc2e 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -23,7 +23,7 @@ use namada::proto::{ use namada::types::address::nam; use namada::types::dec::Dec; use namada::types::key::{common, ed25519, RefTo, SigScheme}; -use namada::types::time::{DateTimeUtc, MIN_UTC}; +use namada::types::time::DateTimeUtc; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada::types::transaction::{pos, Fee, TxType}; @@ -47,9 +47,6 @@ use crate::wallet::CliWalletUtils; /// Dummy chain id used to sign [`Tx`] objects at pre-genesis. const NAMADA_GENESIS_TX_CHAIN_ID: &str = "namada-genesis"; -/// Timestamp used to sign pre-genesis [`Tx`] objects. -pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; - /// Helper trait to fetch tx data to sign. pub trait TxToSign { /// Return tx data to sign. @@ -94,11 +91,17 @@ fn get_tx_args(use_device: bool) -> TxArgs { } } +/// Timestamp used to sign pre-genesis [`Tx`] objects. +#[inline] +fn pre_genesis_tx_timestamp() -> DateTimeUtc { + DateTimeUtc::unix_epoch() +} + /// Return a ready to sign genesis [`Tx`]. fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { let mut tx = Tx::from_type(TxType::Raw); tx.header.chain_id = ChainId(NAMADA_GENESIS_TX_CHAIN_ID.to_string()); - tx.header.timestamp = PRE_GENESIS_TX_TIMESTAMP; + tx.header.timestamp = pre_genesis_tx_timestamp(); tx.set_code(Code { salt: [0; 8], code: Commitment::Hash(Default::default()), diff --git a/core/src/types/time.rs b/core/src/types/time.rs index 99ddd25d6d..ddce370061 100644 --- a/core/src/types/time.rs +++ b/core/src/types/time.rs @@ -152,6 +152,16 @@ impl DateTimeUtc { Self(Utc::now()) } + /// Returns a [`DateTimeUtc`] corresponding to the Unix epoch. + #[inline] + pub fn unix_epoch() -> Self { + Self(chrono::DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp_opt(0, 0) + .expect("This operation should be infallible"), + chrono::Utc, + )) + } + /// Returns an rfc3339 string or an error. pub fn to_rfc3339(&self) -> String { chrono::DateTime::to_rfc3339(&self.0) From b7d07137d7fddac9145c94ccc2b823c8665a0144 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 13:36:19 +0000 Subject: [PATCH 59/81] Revert "Use a Unix epoch as a pre-genesis tx timestamp" This reverts commit b6b2a04ee4c69d2bcb8a84d4c2f88895b36d9db5. --- apps/src/lib/config/genesis/transactions.rs | 13 +++++-------- core/src/types/time.rs | 10 ---------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 9fb2aedc2e..6bc4692ab9 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -23,7 +23,7 @@ use namada::proto::{ use namada::types::address::nam; use namada::types::dec::Dec; use namada::types::key::{common, ed25519, RefTo, SigScheme}; -use namada::types::time::DateTimeUtc; +use namada::types::time::{DateTimeUtc, MIN_UTC}; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada::types::transaction::{pos, Fee, TxType}; @@ -47,6 +47,9 @@ use crate::wallet::CliWalletUtils; /// Dummy chain id used to sign [`Tx`] objects at pre-genesis. const NAMADA_GENESIS_TX_CHAIN_ID: &str = "namada-genesis"; +/// Timestamp used to sign pre-genesis [`Tx`] objects. +pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; + /// Helper trait to fetch tx data to sign. pub trait TxToSign { /// Return tx data to sign. @@ -91,17 +94,11 @@ fn get_tx_args(use_device: bool) -> TxArgs { } } -/// Timestamp used to sign pre-genesis [`Tx`] objects. -#[inline] -fn pre_genesis_tx_timestamp() -> DateTimeUtc { - DateTimeUtc::unix_epoch() -} - /// Return a ready to sign genesis [`Tx`]. fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { let mut tx = Tx::from_type(TxType::Raw); tx.header.chain_id = ChainId(NAMADA_GENESIS_TX_CHAIN_ID.to_string()); - tx.header.timestamp = pre_genesis_tx_timestamp(); + tx.header.timestamp = PRE_GENESIS_TX_TIMESTAMP; tx.set_code(Code { salt: [0; 8], code: Commitment::Hash(Default::default()), diff --git a/core/src/types/time.rs b/core/src/types/time.rs index ddce370061..99ddd25d6d 100644 --- a/core/src/types/time.rs +++ b/core/src/types/time.rs @@ -152,16 +152,6 @@ impl DateTimeUtc { Self(Utc::now()) } - /// Returns a [`DateTimeUtc`] corresponding to the Unix epoch. - #[inline] - pub fn unix_epoch() -> Self { - Self(chrono::DateTime::from_utc( - chrono::NaiveDateTime::from_timestamp_opt(0, 0) - .expect("This operation should be infallible"), - chrono::Utc, - )) - } - /// Returns an rfc3339 string or an error. pub fn to_rfc3339(&self) -> String { chrono::DateTime::to_rfc3339(&self.0) From 508f0aec909b0ae2de16fba203d48ccfc81741d8 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 15:29:49 +0000 Subject: [PATCH 60/81] Genesis bond subcmd --- apps/src/lib/cli.rs | 68 +++++++++++++++++++++ apps/src/lib/cli/client.rs | 3 + apps/src/lib/client/utils.rs | 24 ++++++++ apps/src/lib/config/genesis.rs | 49 ++++++++------- apps/src/lib/config/genesis/transactions.rs | 17 ++++++ 5 files changed, 139 insertions(+), 22 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b7e2f9b1ea..2d645ce25c 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2180,6 +2180,7 @@ pub mod cmds { ValidateWasm(ValidateWasm), InitNetwork(InitNetwork), DeriveGenesisAddresses(DeriveGenesisAddresses), + GenesisBond(GenesisBond), InitGenesisEstablishedAccount(InitGenesisEstablishedAccount), InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), @@ -2203,6 +2204,8 @@ pub mod cmds { SubCmd::parse(matches).map(Self::InitNetwork); let derive_addresses = SubCmd::parse(matches).map(Self::DeriveGenesisAddresses); + let genesis_bond = + SubCmd::parse(matches).map(Self::GenesisBond); let init_established = SubCmd::parse(matches) .map(Self::InitGenesisEstablishedAccount); let init_genesis = @@ -2221,6 +2224,7 @@ pub mod cmds { .or(validate_wasm) .or(init_network) .or(derive_addresses) + .or(genesis_bond) .or(init_established) .or(init_genesis) .or(pk_to_tm_address) @@ -2239,6 +2243,7 @@ pub mod cmds { .subcommand(ValidateWasm::def()) .subcommand(InitNetwork::def()) .subcommand(DeriveGenesisAddresses::def()) + .subcommand(GenesisBond::def()) .subcommand(InitGenesisEstablishedAccount::def()) .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) @@ -2372,6 +2377,25 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct GenesisBond(pub args::GenesisBond); + + impl SubCmd for GenesisBond { + const CMD: &'static str = "genesis-bond"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::GenesisBond::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Bond to a validator at pre-genesis.") + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator(pub args::InitGenesisValidator); @@ -2925,6 +2949,7 @@ pub mod args { use super::utils::*; use super::{ArgGroup, ArgMatches}; use crate::client::utils::PRE_GENESIS_DIR; + use crate::config::genesis::GenesisAddress; use crate::config::{self, Action, ActionAtHeight}; use crate::facade::tendermint::Timeout; use crate::facade::tendermint_config::net::Address as TendermintAddress; @@ -3027,16 +3052,20 @@ pub mod args { denom: NATIVE_MAX_DECIMAL_PLACES.into(), }), ); + pub const GENESIS_BOND_SOURCE: ArgOpt = arg_opt("source"); pub const GENESIS_PATH: Arg = arg("genesis-path"); pub const GENESIS_TIME: Arg = arg("genesis-time"); pub const GENESIS_VALIDATOR: ArgOpt = arg("genesis-validator").opt(); + pub const GENESIS_VALIDATOR_ADDRESS: Arg = + arg("validator"); pub const HALT_ACTION: ArgFlag = flag("halt"); pub const HASH_LIST: Arg = arg("hash-list"); pub const HD_WALLET_DERIVATION_PATH: ArgDefault = arg_default("hd-path", DefaultFn(|| "default".to_string())); pub const HISTORIC: ArgFlag = flag("historic"); pub const IBC_TRANSFER_MEMO_PATH: ArgOpt = arg_opt("memo-path"); + pub const INPUT_OPT: ArgOpt = arg_opt("input"); pub const LEDGER_ADDRESS_ABOUT: &str = "Address of a ledger node as \"{scheme}://{host}:{port}\". If the \ scheme is not supplied, it is assumed to be TCP."; @@ -6813,6 +6842,45 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct GenesisBond { + pub source: GenesisAddress, + pub validator: EstablishedAddress, + pub bond_amount: token::DenominatedAmount, + pub output: PathBuf, + } + + impl Args for GenesisBond { + fn parse(matches: &ArgMatches) -> Self { + let validator = GENESIS_VALIDATOR_ADDRESS.parse(matches); + let source = + GENESIS_BOND_SOURCE.parse(matches).unwrap_or_else(|| { + GenesisAddress::EstablishedAddress(validator.clone()) + }); + let bond_amount = AMOUNT.parse(matches); + let output = PATH.parse(matches); + Self { + source, + validator, + bond_amount, + output, + } + } + + fn def(app: App) -> App { + app.arg(GENESIS_VALIDATOR_ADDRESS.def().help("Validator address.")) + .arg(AMOUNT.def().help("Amount of tokens to stake in a bond.")) + .arg(GENESIS_BOND_SOURCE.def().help( + "Source address for delegations. For self-bonds, the \ + validator is also the source.", + )) + .arg( + PATH.def() + .help("Output toml file to write transactions to."), + ) + } + } + #[derive(Clone, Debug)] pub struct InitGenesisValidator { pub alias: String, diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index 7acaf70e3d..b0a7287197 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -634,6 +634,9 @@ impl CliApi { Utils::InitNetwork(InitNetwork(args)) => { utils::init_network(global_args, args) } + Utils::GenesisBond(GenesisBond(args)) => { + utils::genesis_bond(args) + } Utils::DeriveGenesisAddresses(DeriveGenesisAddresses(args)) => { utils::derive_genesis_addresses(global_args, args) } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 2015d91aeb..faa6a249b5 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -730,6 +730,30 @@ pub fn init_genesis_established_account( println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); } +/// Bond to a validator at pre-genesis. +pub fn genesis_bond(args: args::GenesisBond) { + let args::GenesisBond { + source, + validator, + bond_amount, + output: toml_path, + } = args; + let txs = genesis::transactions::init_bond(source, validator, bond_amount); + + let toml_path_str = toml_path.to_string_lossy(); + + let genesis_part = toml::to_string(&txs).unwrap(); + fs::write(&toml_path, genesis_part).unwrap_or_else(|err| { + eprintln!( + "Couldn't write pre-genesis transactions file to {toml_path_str}. \ + Failed with: {err}", + ); + safe_exit(1) + }); + + println!("{}: {toml_path_str}", "Wrote genesis tx to".bold()); +} + /// Initialize genesis validator's address, consensus key and validator account /// key into a special "pre-genesis" wallet. pub fn init_genesis_validator( diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 38128e403b..f7da635257 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -7,6 +7,7 @@ pub mod utils; use std::collections::{BTreeMap, HashMap}; use std::fmt::{Display, Formatter}; +use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use derivative::Derivative; @@ -82,8 +83,6 @@ impl<'de> Deserialize<'de> for GenesisAddress { where D: serde::Deserializer<'de>, { - use std::str::FromStr; - struct FieldVisitor; impl<'de> serde::de::Visitor<'de> for FieldVisitor { @@ -99,25 +98,8 @@ impl<'de> Deserialize<'de> for GenesisAddress { where E: serde::de::Error, { - // Try to deserialize a PK first - let maybe_pk = - StringEncoded::::from_str(value); - match maybe_pk { - Ok(pk) => Ok(GenesisAddress::PublicKey(pk)), - Err(_) => { - // If that doesn't work, attempt to retrieve - // an established address - let address = Address::from_str(value) - .map_err(serde::de::Error::custom)?; - if let Address::Established(established) = address { - Ok(GenesisAddress::EstablishedAddress(established)) - } else { - Err(serde::de::Error::custom( - "expected an established address or public key", - )) - } - } - } + GenesisAddress::from_str(value) + .map_err(serde::de::Error::custom) } } @@ -136,6 +118,30 @@ impl Display for GenesisAddress { } } +impl FromStr for GenesisAddress { + type Err = String; + + fn from_str(value: &str) -> Result { + // Try to deserialize a PK first + let maybe_pk = StringEncoded::::from_str(value); + match maybe_pk { + Ok(pk) => Ok(GenesisAddress::PublicKey(pk)), + Err(_) => { + // If that doesn't work, attempt to retrieve + // an established address + let address = + Address::from_str(value).map_err(|err| err.to_string())?; + if let Address::Established(established) = address { + Ok(GenesisAddress::EstablishedAddress(established)) + } else { + Err("expected an established address or public key" + .to_string()) + } + } + } + } +} + #[derive(Debug, BorshSerialize, BorshDeserialize)] #[borsh(init=init)] pub struct Genesis { @@ -308,7 +314,6 @@ pub fn make_dev_genesis( target_chain_dir: std::path::PathBuf, ) -> Finalized { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - use std::str::FromStr; use std::time::Duration; use namada::ledger::eth_bridge::{Contracts, UpgradeableContract}; diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 6bc4692ab9..e582451bc6 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -234,6 +234,23 @@ pub fn init_established_account( (address, txs) } +/// Create a [`BondTx`] for a genesis validator. +pub fn init_bond( + source: GenesisAddress, + validator: EstablishedAddress, + bond_amount: token::DenominatedAmount, +) -> UnsignedTransactions { + let unsigned_tx = BondTx { + source, + validator: Address::Established(validator), + amount: bond_amount, + }; + UnsignedTransactions { + bond: Some(vec![unsigned_tx]), + ..Default::default() + } +} + /// Create [`UnsignedTransactions`] for a genesis validator. pub fn init_validator( GenesisValidatorData { From 0e6c434982a2aa065133eb7f94003ef081d3dd5a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 15:41:40 +0000 Subject: [PATCH 61/81] Set new pre-genesis txs timestamp --- apps/src/lib/config/genesis/transactions.rs | 17 ++++++++++++----- core/src/types/time.rs | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index e582451bc6..991692e79b 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -23,7 +23,7 @@ use namada::proto::{ use namada::types::address::nam; use namada::types::dec::Dec; use namada::types::key::{common, ed25519, RefTo, SigScheme}; -use namada::types::time::{DateTimeUtc, MIN_UTC}; +use namada::types::time::DateTimeUtc; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada::types::transaction::{pos, Fee, TxType}; @@ -47,9 +47,6 @@ use crate::wallet::CliWalletUtils; /// Dummy chain id used to sign [`Tx`] objects at pre-genesis. const NAMADA_GENESIS_TX_CHAIN_ID: &str = "namada-genesis"; -/// Timestamp used to sign pre-genesis [`Tx`] objects. -pub const PRE_GENESIS_TX_TIMESTAMP: DateTimeUtc = MIN_UTC; - /// Helper trait to fetch tx data to sign. pub trait TxToSign { /// Return tx data to sign. @@ -94,11 +91,21 @@ fn get_tx_args(use_device: bool) -> TxArgs { } } +/// Timestamp used to sign pre-genesis [`Tx`] objects. +#[inline] +fn pre_genesis_tx_timestamp() -> DateTimeUtc { + DateTimeUtc::from_unix_timestamp( + // Mon Jan 01 2001 01:01:01 UTC+0000 + 978310861, + ) + .unwrap() +} + /// Return a ready to sign genesis [`Tx`]. fn get_tx_to_sign(tag: impl AsRef, data: impl BorshSerialize) -> Tx { let mut tx = Tx::from_type(TxType::Raw); tx.header.chain_id = ChainId(NAMADA_GENESIS_TX_CHAIN_ID.to_string()); - tx.header.timestamp = PRE_GENESIS_TX_TIMESTAMP; + tx.header.timestamp = pre_genesis_tx_timestamp(); tx.set_code(Code { salt: [0; 8], code: Commitment::Hash(Default::default()), diff --git a/core/src/types/time.rs b/core/src/types/time.rs index 99ddd25d6d..f82c9e0fe4 100644 --- a/core/src/types/time.rs +++ b/core/src/types/time.rs @@ -152,6 +152,22 @@ impl DateTimeUtc { Self(Utc::now()) } + /// Returns a [`DateTimeUtc`] corresponding to the provided Unix timestamp. + #[inline] + pub fn from_unix_timestamp(timestamp: i64) -> Option { + Some(Self(chrono::DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp_opt(timestamp, 0)?, + chrono::Utc, + ))) + } + + /// Returns a [`DateTimeUtc`] corresponding to the Unix epoch. + #[inline] + pub fn unix_epoch() -> Self { + Self::from_unix_timestamp(0) + .expect("This operation should be infallible") + } + /// Returns an rfc3339 string or an error. pub fn to_rfc3339(&self) -> String { chrono::DateTime::to_rfc3339(&self.0) From f54d5385a105890b91d6b33a751965a1140a9008 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 15:47:08 +0000 Subject: [PATCH 62/81] Fix localnet template sigs --- genesis/localnet/src/pre-genesis/signed-transactions.toml | 2 +- .../src/pre-genesis/validator-0/signed-transactions.toml | 4 ++-- genesis/localnet/transactions.toml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/genesis/localnet/src/pre-genesis/signed-transactions.toml b/genesis/localnet/src/pre-genesis/signed-transactions.toml index 1fd02da9ff..3c7191f212 100644 --- a/genesis/localnet/src/pre-genesis/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/signed-transactions.toml @@ -19,4 +19,4 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" [bond.signatures] -tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qqrhz8puehekzc4960f322f2nm9vn9r47wavvqs33f6vv6uerycj8e2x89ljzmuyekn7rfg6enchhn2s0wm2r6czlhep5kj0an5ll4qqqkkgqz" +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qq48ks6ta5hmsl9z3nwvqdl0g30g7qdkxwzel9j7eyvye9a99aw2sy03ukykgz49pksmw2s640nrl2xw0h5mq7srz2pw4fxljyp77lg9tqdr29" diff --git a/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml b/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml index c3a9a6ec8c..679d74ff7e 100644 --- a/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml @@ -34,7 +34,7 @@ authorization = "signam1q8pmqdqcqh9v8djeqlrzy4mv845v9harnm9u5z54nwlrhwkyvc9y79sv email = "null@null.net" [validator_account.signatures] -tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qzxx53nx6tptrfp2qhr2du8lmlvhyrxuvcp3razw0yqha45redp7nqsx09d45jads34ecrny5mj2fplru9vyq8ghttksczj86vqx2tg0kwk2f9" +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qz3ylgxm3sr9d68ktwf078vt89dn9v8xj0c7tnytevnsd0v29kh6wmnqsfxd5mke8nrdlwve850l3uwcquxw2e7nltqt8eufds6ns6cqfzastc" [[bond]] source = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" @@ -42,4 +42,4 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "100000" [bond.signatures] -tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qrzf2xg5m2hudxc7p0zjnnzlnfrlsjkt5j0wpesw9le2nn7u4sgr3y8s9sr7xs5yzs5qt5fhmtmp2j5cmfr8ulzz2rhwmqzfslry56cymvurrg" +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qpe7tlwcl4xdnm67sra9cfc3754mnyvgpxxcdzrz7nuev3lhhv6ngsrhw0cwmumnkjc2f67prx82v650x9gvl609zg7ekspph8unzhs9dkz0st" diff --git a/genesis/localnet/transactions.toml b/genesis/localnet/transactions.toml index 76526a2ca6..3a292cbcf3 100644 --- a/genesis/localnet/transactions.toml +++ b/genesis/localnet/transactions.toml @@ -41,7 +41,7 @@ authorization = "signam1q8pmqdqcqh9v8djeqlrzy4mv845v9harnm9u5z54nwlrhwkyvc9y79sv email = "null@null.net" [validator_account.signatures] -tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qzxx53nx6tptrfp2qhr2du8lmlvhyrxuvcp3razw0yqha45redp7nqsx09d45jads34ecrny5mj2fplru9vyq8ghttksczj86vqx2tg0kwk2f9" +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qz3ylgxm3sr9d68ktwf078vt89dn9v8xj0c7tnytevnsd0v29kh6wmnqsfxd5mke8nrdlwve850l3uwcquxw2e7nltqt8eufds6ns6cqfzastc" [[bond]] source = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" @@ -49,7 +49,7 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "100000" [bond.signatures] -tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qrzf2xg5m2hudxc7p0zjnnzlnfrlsjkt5j0wpesw9le2nn7u4sgr3y8s9sr7xs5yzs5qt5fhmtmp2j5cmfr8ulzz2rhwmqzfslry56cymvurrg" +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qpe7tlwcl4xdnm67sra9cfc3754mnyvgpxxcdzrz7nuev3lhhv6ngsrhw0cwmumnkjc2f67prx82v650x9gvl609zg7ekspph8unzhs9dkz0st" # 2. @@ -74,4 +74,4 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" [bond.signatures] -tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qqrhz8puehekzc4960f322f2nm9vn9r47wavvqs33f6vv6uerycj8e2x89ljzmuyekn7rfg6enchhn2s0wm2r6czlhep5kj0an5ll4qqqkkgqz" +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qq48ks6ta5hmsl9z3nwvqdl0g30g7qdkxwzel9j7eyvye9a99aw2sy03ukykgz49pksmw2s640nrl2xw0h5mq7srz2pw4fxljyp77lg9tqdr29" From 14639f1b2f2cdeb950f42224f5c36e461f9a5d0b Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 16:40:02 +0000 Subject: [PATCH 63/81] Denominate amounts before signing them --- apps/src/lib/config/genesis/templates.rs | 2 +- apps/src/lib/config/genesis/transactions.rs | 45 ++++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index fb374c4866..62209dd24d 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -524,7 +524,7 @@ pub struct Validated {} pub trait TemplateValidation: Serialize { type Amount: for<'a> Deserialize<'a> + Serialize - + Into + + Into + Clone + std::fmt::Debug + BorshSerialize diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index 991692e79b..a21759ab10 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -878,7 +878,9 @@ where TX_BOND_WASM, pos::Bond { validator: self.validator.clone(), - amount: self.amount.clone().into(), + amount: denominate_amount::(self.amount.clone()) + .unwrap() + .amount, source: Some(self.source.address()), }, ) @@ -924,29 +926,36 @@ where impl BondTx { /// Add the correct denomination to the contained amount pub fn denominate(self) -> eyre::Result> { - let BondTx { - source, - validator, - amount, - } = self; - let amount = amount - .increase_precision(NATIVE_MAX_DECIMAL_PLACES.into()) - .map_err(|e| { - eprintln!( - "A bond amount in the transactions.toml file was \ - incorrectly formatted:\n{}", - e - ); - e - })?; + let amount = denominate_amount::(self.amount)?; Ok(BondTx { - source, - validator, + source: self.source, + validator: self.validator, amount, }) } } +/// Return the correctly denominated amount of bonded tokens +fn denominate_amount( + amount: T::Amount, +) -> eyre::Result +where + T: TemplateValidation, +{ + let amount = amount + .into() + .increase_precision(NATIVE_MAX_DECIMAL_PLACES.into()) + .map_err(|e| { + eprintln!( + "A bond amount in the transactions.toml file was incorrectly \ + formatted:\n{}", + e + ); + e + })?; + Ok(amount) +} + impl From> for SignedBondTx { #[inline] fn from(bond: BondTx) -> Self { From 1ba67ec9cb8ac5c79de19749ab38f289a9d9072d Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 5 Dec 2023 16:45:28 +0000 Subject: [PATCH 64/81] Fix signatures once again --- genesis/localnet/src/pre-genesis/signed-transactions.toml | 2 +- .../src/pre-genesis/validator-0/signed-transactions.toml | 2 +- genesis/localnet/transactions.toml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/genesis/localnet/src/pre-genesis/signed-transactions.toml b/genesis/localnet/src/pre-genesis/signed-transactions.toml index 3c7191f212..2337fc7845 100644 --- a/genesis/localnet/src/pre-genesis/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/signed-transactions.toml @@ -19,4 +19,4 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" [bond.signatures] -tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qq48ks6ta5hmsl9z3nwvqdl0g30g7qdkxwzel9j7eyvye9a99aw2sy03ukykgz49pksmw2s640nrl2xw0h5mq7srz2pw4fxljyp77lg9tqdr29" +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qpnkmht38f4ewksnu2e4kaue9vqynvujs7hghk358rjn5wt6ywm9zdet6m3txrh08qzc5up47vgskt7cz48dpqlpuan7waz7prlxe0sxn9zsn7" diff --git a/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml b/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml index 679d74ff7e..90c00d639c 100644 --- a/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml +++ b/genesis/localnet/src/pre-genesis/validator-0/signed-transactions.toml @@ -42,4 +42,4 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "100000" [bond.signatures] -tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qpe7tlwcl4xdnm67sra9cfc3754mnyvgpxxcdzrz7nuev3lhhv6ngsrhw0cwmumnkjc2f67prx82v650x9gvl609zg7ekspph8unzhs9dkz0st" +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qp7jrgg0q488vm3m4yzt8c44e40kaklc08fvsktwuayw69wzjgsvr9zlqs60wml5ewpnm8yydg0g5kga76zm3xmtcxauxszftrv7l9cpalu82v" diff --git a/genesis/localnet/transactions.toml b/genesis/localnet/transactions.toml index 3a292cbcf3..49c07a000e 100644 --- a/genesis/localnet/transactions.toml +++ b/genesis/localnet/transactions.toml @@ -49,7 +49,7 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "100000" [bond.signatures] -tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qpe7tlwcl4xdnm67sra9cfc3754mnyvgpxxcdzrz7nuev3lhhv6ngsrhw0cwmumnkjc2f67prx82v650x9gvl609zg7ekspph8unzhs9dkz0st" +tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj = "signam1qp7jrgg0q488vm3m4yzt8c44e40kaklc08fvsktwuayw69wzjgsvr9zlqs60wml5ewpnm8yydg0g5kga76zm3xmtcxauxszftrv7l9cpalu82v" # 2. @@ -74,4 +74,4 @@ validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" amount = "20000" [bond.signatures] -tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qq48ks6ta5hmsl9z3nwvqdl0g30g7qdkxwzel9j7eyvye9a99aw2sy03ukykgz49pksmw2s640nrl2xw0h5mq7srz2pw4fxljyp77lg9tqdr29" +tpknam1qrnw8mxyqlj60mykgevnldcj5mg2fya7fs5a8xqdkd2gwtxhef0zy8a2wha = "signam1qpnkmht38f4ewksnu2e4kaue9vqynvujs7hghk358rjn5wt6ywm9zdet6m3txrh08qzc5up47vgskt7cz48dpqlpuan7waz7prlxe0sxn9zsn7" From 2deefdacab4836e17bf9ed334daf8277bc5010bc Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 09:20:46 +0000 Subject: [PATCH 65/81] Output unsigned established account txs struct --- apps/src/lib/config/genesis/transactions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index a21759ab10..a7ceeb24fd 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -227,14 +227,14 @@ pub fn init_established_account( vp: String, public_keys: Vec>, threshold: u8, -) -> (Address, Transactions) { +) -> (Address, UnsignedTransactions) { let unsigned_tx = EstablishedAccountTx { vp, threshold, public_keys, }; let address = unsigned_tx.derive_address(); - let txs = Transactions { + let txs = UnsignedTransactions { established_account: Some(vec![unsigned_tx]), ..Default::default() }; From 2d1288f3af7e8bcdf55b5bf76a024c1695de9b7b Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 10:59:27 +0000 Subject: [PATCH 66/81] Improve genesis tx signing's janky code --- apps/src/lib/client/utils.rs | 156 ++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index faa6a249b5..e09701a6c0 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -985,6 +985,58 @@ pub fn validate_genesis_templates( } } +async fn append_signature_to_signed_toml( + input_txs: &Path, + wallet: &mut Wallet, + use_device: bool, +) -> genesis::transactions::Transactions { + // Parse signed txs toml to append new signatures to + let mut genesis_txs = genesis::templates::read_transactions(input_txs) + .unwrap_or_else(|_| { + eprintln!( + "Unable to parse the TOML from path: {}", + input_txs.to_string_lossy() + ); + safe_exit(1) + }); + // Sign bond txs and append signatures to toml file + if let Some(txs) = genesis_txs.bond { + let mut bonds = vec![]; + for tx in txs { + bonds.push( + sign_delegation_bond_tx( + tx, + wallet, + &genesis_txs.established_account, + use_device, + ) + .await, + ); + } + genesis_txs.bond = Some(bonds); + } + // Sign validator txs and append signatures to toml file + if let Some(txs) = genesis_txs.validator_account { + let mut validator_accounts = vec![]; + for tx in txs { + validator_accounts.push( + sign_validator_account_tx( + Either::Right(tx), + wallet, + genesis_txs.established_account.as_ref().expect( + "Established account txs required when signing \ + validator account txs", + ), + use_device, + ) + .await, + ); + } + genesis_txs.validator_account = Some(validator_accounts); + } + genesis_txs +} + /// Sign genesis transactions. pub async fn sign_genesis_tx( global_args: args::Global, @@ -1009,82 +1061,36 @@ pub async fn sign_genesis_tx( ); safe_exit(1) }); - let (signed, append) = - match genesis::transactions::parse_unsigned(&contents) { - Ok(unsigned) => ( - genesis::transactions::sign_txs( - unsigned, - &mut wallet, - maybe_pre_genesis_wallet.as_ref(), - use_device, - ) - .await, - true, - ), - Err(err) => { - let mut genesis_txs = - genesis::templates::read_transactions(&path) - .unwrap_or_else(|e| { - eprintln!( - "Unable to parse the TOML from {}. Could not \ - parse as unsigned with {err}. Could not \ - parse as signed with {e}.", - path.to_string_lossy() - ); - safe_exit(1) - }); - // Sign bond txs - if let Some(txs) = genesis_txs.bond { - let mut bonds = vec![]; - for tx in txs { - bonds.push( - sign_delegation_bond_tx( - tx, - &mut wallet, - &genesis_txs.established_account, - use_device, - ) - .await, - ); - } - genesis_txs.bond = Some(bonds); - } - // Sign validator txs - if let Some(txs) = genesis_txs.validator_account { - let mut validator_accounts = vec![]; - for tx in txs { - validator_accounts.push( - sign_validator_account_tx( - Either::Right(tx), - &mut wallet, - genesis_txs - .established_account - .as_ref() - .expect( - "Established account txs required \ - when signing validator account txs", - ), - use_device, - ) - .await, - ); - } - genesis_txs.validator_account = Some(validator_accounts); - } - (genesis_txs, false) - } - }; + // Sign a subset of the input txs (the ones whose keys we own) + let signed = if let Ok(unsigned) = + genesis::transactions::parse_unsigned(&contents) + { + let signed = genesis::transactions::sign_txs( + unsigned, + &mut wallet, + maybe_pre_genesis_wallet.as_ref(), + use_device, + ) + .await; + if let Some(output_path) = output.as_ref() { + // If the output path contains existing signed txs, we append + // the newly signed txs to the file + let mut prev_txs = + genesis::templates::read_transactions(output_path) + .unwrap_or_default(); + prev_txs.merge(signed); + prev_txs + } else { + signed + } + } else { + // In case we fail to parse unsigned txs, we will attempt to + // parse signed txs and append new signatures to the existing + // toml file + append_signature_to_signed_toml(&path, &mut wallet, use_device).await + }; match output { Some(output_path) => { - let signed = if append { - let mut prev_txs = - genesis::templates::read_transactions(&output_path) - .unwrap_or_default(); - prev_txs.merge(signed); - prev_txs - } else { - signed - }; let transactions = toml::to_vec(&signed).unwrap(); fs::write(&output_path, transactions).unwrap_or_else(|err| { eprintln!( @@ -1100,8 +1106,6 @@ pub async fn sign_genesis_tx( } None => { let transactions = toml::to_string(&signed).unwrap(); - println!("Your public signed transactions TOML:"); - println!(); println!("{transactions}"); } } From 6a53201f6dbddde86ea82ba403f97202d35d3c05 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 11:08:50 +0000 Subject: [PATCH 67/81] Check if two pubkeys are used for the same established acc --- apps/src/lib/config/genesis/transactions.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index a7ceeb24fd..482d305013 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -1250,6 +1250,19 @@ pub fn validate_established_account( ); is_valid = false; } + { + // check for duped pubkeys + let mut used_keys = HashSet::new(); + for key in tx.public_keys.iter() { + if !used_keys.insert(key) { + eprintln!( + "The same public key has been used twice in an \ + established account tx: {key}" + ); + is_valid = false; + } + } + } established_accounts.insert( established_address.clone(), ( From ab2244754be8c5d048299981525738c17a28e2fd Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 11:24:25 +0000 Subject: [PATCH 68/81] Change aliases arg --- apps/src/lib/cli.rs | 11 +++++------ apps/src/lib/cli/utils.rs | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 2d645ce25c..c1b7b82eda 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2958,7 +2958,7 @@ pub mod args { pub const ALIAS_OPT: ArgOpt = ALIAS.opt(); pub const ALIAS: Arg = arg("alias"); pub const ALIAS_FORCE: ArgFlag = flag("alias-force"); - pub const ALIAS_MANY: ArgMulti = arg_multi("alias"); + pub const ALIAS_MANY: ArgMulti = arg_multi("aliases"); pub const ALLOW_DUPLICATE_IP: ArgFlag = flag("allow-duplicate-ip"); pub const AMOUNT: Arg = arg("amount"); pub const ARCHIVE_DIR: ArgOpt = arg_opt("archive-dir"); @@ -6822,11 +6822,10 @@ pub mod args { } fn def(app: App) -> App { - app.arg( - ALIAS_MANY - .def() - .help("The aliases of the keys to use from the wallet."), - ) + app.arg(ALIAS_MANY.def().help( + "Comma separated list of aliases of the keys to use from the \ + wallet.", + )) .arg(THRESOLD.def().help( "The minimum number of signatures to be provided for \ authorization. Must be less than or equal to the maximum \ diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 1a92f97770..eaad2937d1 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -297,7 +297,8 @@ where pub fn def(&self) -> ClapArg { ClapArg::new(self.name) .long(self.name) - .action(ArgAction::Append) + .num_args(1..) + .value_delimiter(',') } pub fn parse(&self, matches: &ArgMatches) -> Vec { From 605bea0ef4405abff3287818e63839cddfc2c44e Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 11:27:13 +0000 Subject: [PATCH 69/81] Fix e2e test setup --- tests/src/e2e/setup.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index af810670b2..6c041b67a0 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -200,7 +200,7 @@ where let args = vec![ "utils", "init-genesis-established-account", - "--alias", + "--aliases", &alias, "--path", &pre_genesis_tx_path_str, From 763392a82855d73ae66a144cee46c8d82f63801a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 11:44:58 +0000 Subject: [PATCH 70/81] Fix bug in cli arg multi --- apps/src/lib/cli/utils.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index eaad2937d1..143227572d 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -252,7 +252,10 @@ where pub fn parse(&self, matches: &ArgMatches) -> Vec> { matches .get_many(self.name) - .unwrap_or_default() + .unwrap_or_else(|| { + eprintln!("Missing at least one argument to `--{}`", self.name); + safe_exit(1) + }) .map(|raw: &String| FromContext::new(raw.to_string())) .collect() } @@ -304,7 +307,10 @@ where pub fn parse(&self, matches: &ArgMatches) -> Vec { matches .get_many(self.name) - .unwrap_or_default() + .unwrap_or_else(|| { + eprintln!("Missing at least one argument to `--{}`", self.name); + safe_exit(1) + }) .map(|raw: &String| { raw.parse().unwrap_or_else(|e| { eprintln!( From 4ec49f2a647d39461bdf133a0a35145b56d61073 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 13:11:39 +0000 Subject: [PATCH 71/81] Revert "Fix bug in cli arg multi" This reverts commit 284b2a0cb62090d3701968e2065d90e96bca3597. --- apps/src/lib/cli/utils.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 143227572d..eaad2937d1 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -252,10 +252,7 @@ where pub fn parse(&self, matches: &ArgMatches) -> Vec> { matches .get_many(self.name) - .unwrap_or_else(|| { - eprintln!("Missing at least one argument to `--{}`", self.name); - safe_exit(1) - }) + .unwrap_or_default() .map(|raw: &String| FromContext::new(raw.to_string())) .collect() } @@ -307,10 +304,7 @@ where pub fn parse(&self, matches: &ArgMatches) -> Vec { matches .get_many(self.name) - .unwrap_or_else(|| { - eprintln!("Missing at least one argument to `--{}`", self.name); - safe_exit(1) - }) + .unwrap_or_default() .map(|raw: &String| { raw.parse().unwrap_or_else(|e| { eprintln!( From f9d855d8b8127be43526704d22b6e1e6b684f230 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 13:29:37 +0000 Subject: [PATCH 72/81] Add some lobsters or something --- apps/src/lib/cli.rs | 12 ++-- apps/src/lib/cli/utils.rs | 132 ++++++++++++++++++++++++++------------ 2 files changed, 99 insertions(+), 45 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index c1b7b82eda..96b53d8c95 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2958,7 +2958,7 @@ pub mod args { pub const ALIAS_OPT: ArgOpt = ALIAS.opt(); pub const ALIAS: Arg = arg("alias"); pub const ALIAS_FORCE: ArgFlag = flag("alias-force"); - pub const ALIAS_MANY: ArgMulti = arg_multi("aliases"); + pub const ALIAS_MANY: ArgMulti = arg_multi("aliases"); pub const ALLOW_DUPLICATE_IP: ArgFlag = flag("allow-duplicate-ip"); pub const AMOUNT: Arg = arg("amount"); pub const ARCHIVE_DIR: ArgOpt = arg_opt("archive-dir"); @@ -3106,7 +3106,8 @@ pub mod args { pub const PROTOCOL_KEY: ArgOpt = arg_opt("protocol-key"); pub const PRE_GENESIS_PATH: ArgOpt = arg_opt("pre-genesis-path"); pub const PUBLIC_KEY: Arg = arg("public-key"); - pub const PUBLIC_KEYS: ArgMulti = arg_multi("public-keys"); + pub const PUBLIC_KEYS: ArgMulti = + arg_multi("public-keys"); pub const PROPOSAL_ID: Arg = arg("proposal-id"); pub const PROPOSAL_ID_OPT: ArgOpt = arg_opt("proposal-id"); pub const PROPOSAL_VOTE_PGF_OPT: ArgOpt = arg_opt("pgf"); @@ -3129,8 +3130,9 @@ pub mod args { pub const SIGNER: ArgOpt = arg_opt("signer"); pub const SIGNING_KEY_OPT: ArgOpt = SIGNING_KEY.opt(); pub const SIGNING_KEY: Arg = arg("signing-key"); - pub const SIGNING_KEYS: ArgMulti = arg_multi("signing-keys"); - pub const SIGNATURES: ArgMulti = arg_multi("signatures"); + pub const SIGNING_KEYS: ArgMulti = + arg_multi("signing-keys"); + pub const SIGNATURES: ArgMulti = arg_multi("signatures"); pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); pub const STEWARD: Arg = arg("steward"); @@ -3154,7 +3156,7 @@ pub mod args { pub const VALIDATOR_OPT: ArgOpt = VALIDATOR.opt(); pub const VALIDATOR_ACCOUNT_KEY: ArgOpt = arg_opt("account-key"); - pub const VALIDATOR_ACCOUNT_KEYS: ArgMulti = + pub const VALIDATOR_ACCOUNT_KEYS: ArgMulti = arg_multi("account-keys"); pub const VALIDATOR_CONSENSUS_KEY: ArgOpt = arg_opt("consensus-key"); diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index eaad2937d1..b0a6493a2b 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -28,6 +28,14 @@ const RELAYER_KEY_ENV_VAR: &str = "NAMADA_RELAYER_KEY"; pub type App = clap::Command; pub type ClapArg = clap::Arg; +/// Mode of operation og [`ArgMulti`] where zero or +/// more arguments may be present (i.e. `*`). +pub enum GlobStar {} + +/// Mode of operation og [`ArgMulti`] where at least +/// one argument must be present (i.e. `+`). +pub enum GlobPlus {} + pub trait Cmd: Sized { fn add_sub(app: App) -> App; fn parse(matches: &ArgMatches) -> Option; @@ -93,9 +101,9 @@ pub struct ArgFlag { } #[allow(dead_code)] -pub struct ArgMulti { +pub struct ArgMulti { pub name: &'static str, - pub r#type: PhantomData, + pub r#type: PhantomData<(T, K)>, } pub const fn arg(name: &'static str) -> Arg { @@ -138,8 +146,7 @@ pub const fn flag(name: &'static str) -> ArgFlag { ArgFlag { name } } -#[allow(dead_code)] -pub const fn arg_multi(name: &'static str) -> ArgMulti { +pub const fn arg_multi(name: &'static str) -> ArgMulti { ArgMulti { name, r#type: PhantomData, @@ -163,7 +170,15 @@ impl Arg { } #[allow(dead_code)] - pub const fn multi(self) -> ArgMulti { + pub const fn multi_glob_star(self) -> ArgMulti { + ArgMulti { + name: self.name, + r#type: PhantomData, + } + } + + #[allow(dead_code)] + pub const fn multi_glob_plus(self) -> ArgMulti { ArgMulti { name: self.name, r#type: PhantomData, @@ -237,18 +252,20 @@ where } } -impl ArgMulti> -where - T: FromStr, - ::Err: Debug, -{ +impl ArgMulti { pub fn def(&self) -> ClapArg { ClapArg::new(self.name) .long(self.name) .num_args(1..) .value_delimiter(',') } +} +impl ArgMulti, GlobStar> +where + T: FromStr, + ::Err: Debug, +{ pub fn parse(&self, matches: &ArgMatches) -> Vec> { matches .get_many(self.name) @@ -258,53 +275,58 @@ where } } -impl ArgDefaultFromCtx> +impl ArgMulti, GlobPlus> where T: FromStr, ::Err: Debug, { - pub fn def(&self) -> ClapArg { - ClapArg::new(self.name).long(self.name).num_args(1) - } - - pub fn parse(&self, matches: &ArgMatches) -> FromContext { - let raw = parse_opt(matches, self.name).unwrap_or_else(|| { - let DefaultFn(default) = self.default; - default() - }); - FromContext::new(raw) + pub fn parse(&self, matches: &ArgMatches) -> Vec> { + matches + .get_many(self.name) + .unwrap_or_else(|| { + eprintln!("Missing at least one argument to `--{}`", self.name); + safe_exit(1) + }) + .map(|raw: &String| FromContext::new(raw.to_string())) + .collect() } } -impl ArgFlag { - pub fn def(&self) -> ClapArg { - ClapArg::new(self.name) - .long(self.name) - .action(ArgAction::SetTrue) - } - - pub fn parse(&self, matches: &ArgMatches) -> bool { - matches.get_flag(self.name) +impl ArgMulti +where + T: FromStr, + ::Err: Debug, +{ + pub fn parse(&self, matches: &ArgMatches) -> Vec { + matches + .get_many(self.name) + .unwrap_or_default() + .map(|raw: &String| { + raw.parse().unwrap_or_else(|e| { + eprintln!( + "Failed to parse the {} argument. Raw value: {}, \ + error: {:?}", + self.name, raw, e + ); + safe_exit(1) + }) + }) + .collect() } } -#[allow(dead_code)] -impl ArgMulti +impl ArgMulti where T: FromStr, ::Err: Debug, { - pub fn def(&self) -> ClapArg { - ClapArg::new(self.name) - .long(self.name) - .num_args(1..) - .value_delimiter(',') - } - pub fn parse(&self, matches: &ArgMatches) -> Vec { matches .get_many(self.name) - .unwrap_or_default() + .unwrap_or_else(|| { + eprintln!("Missing at least one argument to `--{}`", self.name); + safe_exit(1) + }) .map(|raw: &String| { raw.parse().unwrap_or_else(|e| { eprintln!( @@ -319,6 +341,36 @@ where } } +impl ArgDefaultFromCtx> +where + T: FromStr, + ::Err: Debug, +{ + pub fn def(&self) -> ClapArg { + ClapArg::new(self.name).long(self.name).num_args(1) + } + + pub fn parse(&self, matches: &ArgMatches) -> FromContext { + let raw = parse_opt(matches, self.name).unwrap_or_else(|| { + let DefaultFn(default) = self.default; + default() + }); + FromContext::new(raw) + } +} + +impl ArgFlag { + pub fn def(&self) -> ClapArg { + ClapArg::new(self.name) + .long(self.name) + .action(ArgAction::SetTrue) + } + + pub fn parse(&self, matches: &ArgMatches) -> bool { + matches.get_flag(self.name) + } +} + /// Extensions for defining commands and arguments. /// Every function here should have a matcher in [`ArgMatchesExt`]. pub trait AppExt { From c4af96a4ce312b4bcc990e51b50e471d53298a92 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 14:30:53 +0000 Subject: [PATCH 73/81] Make pre-genesis a global arg --- apps/src/lib/bench_utils.rs | 1 + apps/src/lib/cli.rs | 156 ++++-------------- apps/src/lib/cli/context.rs | 11 +- apps/src/lib/cli/wallet.rs | 71 +++----- .../lib/node/ledger/shell/testing/client.rs | 1 + sdk/src/args.rs | 38 ----- tests/src/integration/setup.rs | 1 + 7 files changed, 63 insertions(+), 216 deletions(-) diff --git a/apps/src/lib/bench_utils.rs b/apps/src/lib/bench_utils.rs index e0df703dd3..e122ad3ad1 100644 --- a/apps/src/lib/bench_utils.rs +++ b/apps/src/lib/bench_utils.rs @@ -748,6 +748,7 @@ impl Default for BenchShieldedCtx { wallet.save().unwrap(); let ctx = Context::new::(crate::cli::args::Global { + is_pre_genesis: false, chain_id: Some(shell.inner.chain_id.clone()), base_dir, wasm_dir: Some(WASM_DIR.into()), diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 96b53d8c95..9e4a5c7c30 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -670,7 +670,7 @@ pub mod cmds { GenPayAddr(MaspGenPayAddr), GenSpendKey(MaspGenSpendKey), AddAddrKey(MaspAddAddrKey), - ListPayAddrs(MaspListPayAddrs), + ListPayAddrs, ListKeys(MaspListKeys), FindAddrKey(MaspFindAddrKey), } @@ -683,7 +683,8 @@ pub mod cmds { let genpa = SubCmd::parse(matches).map(Self::GenPayAddr); let gensk = SubCmd::parse(matches).map(Self::GenSpendKey); let addak = SubCmd::parse(matches).map(Self::AddAddrKey); - let listpa = SubCmd::parse(matches).map(Self::ListPayAddrs); + let listpa = ::parse(matches) + .map(|_| Self::ListPayAddrs); let listsk = SubCmd::parse(matches).map(Self::ListKeys); let findak = SubCmd::parse(matches).map(Self::FindAddrKey); gensk.or(genpa).or(addak).or(listpa).or(listsk).or(findak) @@ -750,21 +751,18 @@ pub mod cmds { /// List all known payment addresses #[derive(Clone, Debug)] - pub struct MaspListPayAddrs(pub args::MaspListPayAddrs); + pub struct MaspListPayAddrs; impl SubCmd for MaspListPayAddrs { const CMD: &'static str = "list-addrs"; fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::MaspListPayAddrs::parse(matches))) + matches.subcommand_matches(Self::CMD).map(|_| Self) } fn def() -> App { App::new(Self::CMD) .about("Lists all payment addresses in the wallet") - .add_args::() } } @@ -835,7 +833,7 @@ pub mod cmds { Gen(AddressGen), Derive(AddressDerive), Find(AddressOrAliasFind), - List(AddressList), + List, Add(AddressAdd), } @@ -847,7 +845,8 @@ pub mod cmds { let gen = SubCmd::parse(matches).map(Self::Gen); let restore = SubCmd::parse(matches).map(Self::Derive); let find = SubCmd::parse(matches).map(Self::Find); - let list = SubCmd::parse(matches).map(Self::List); + let list = + ::parse(matches).map(|_| Self::List); let add = SubCmd::parse(matches).map(Self::Add); gen.or(restore).or(find).or(list).or(add) }) @@ -943,21 +942,17 @@ pub mod cmds { /// List known addresses #[derive(Clone, Debug)] - pub struct AddressList(pub args::AddressList); + pub struct AddressList; impl SubCmd for AddressList { const CMD: &'static str = "list"; fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::AddressList::parse(matches))) + matches.subcommand_matches(Self::CMD).map(|_| Self) } fn def() -> App { - App::new(Self::CMD) - .about("List all known addresses.") - .add_args::() + App::new(Self::CMD).about("List all known addresses.") } } @@ -3181,6 +3176,7 @@ pub mod args { /// Global command arguments #[derive(Clone, Debug)] pub struct Global { + pub is_pre_genesis: bool, pub chain_id: Option, pub base_dir: PathBuf, pub wasm_dir: Option, @@ -3189,10 +3185,12 @@ pub mod args { impl Global { /// Parse global arguments pub fn parse(matches: &ArgMatches) -> Self { + let is_pre_genesis = PRE_GENESIS.parse(matches); let chain_id = CHAIN_ID_OPT.parse(matches); let base_dir = BASE_DIR.parse(matches); let wasm_dir = WASM_DIR.parse(matches); Global { + is_pre_genesis, chain_id, base_dir, wasm_dir, @@ -3219,6 +3217,11 @@ pub mod args { `NAMADA_WASM_DIR` environment variable, but the argument \ takes precedence, if specified.", )) + .arg( + PRE_GENESIS + .def() + .help("Dispatch pre-genesis specific logic."), + ) } } @@ -6121,13 +6124,11 @@ pub mod args { let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let value = MASP_VALUE.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { alias, alias_force, value, - is_pre_genesis, unsafe_dont_encrypt, } } @@ -6146,10 +6147,6 @@ pub mod args { .def() .help("A spending key, viewing key, or payment address."), ) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) .arg(UNSAFE_DONT_ENCRYPT.def().help( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", @@ -6161,12 +6158,10 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { alias, alias_force, - is_pre_genesis, unsafe_dont_encrypt, } } @@ -6180,10 +6175,6 @@ pub mod args { .arg(ALIAS_FORCE.def().help( "Override the alias without confirmation if it already exists.", )) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) .arg(UNSAFE_DONT_ENCRYPT.def().help( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", @@ -6208,20 +6199,20 @@ pub mod args { safe_exit(1) }) }; - let viewing_key = if self.is_pre_genesis || ctx.chain.is_none() { - let wallet_path = - ctx.global_args.base_dir.join(PRE_GENESIS_DIR); - let mut wallet = crate::wallet::load_or_new(&wallet_path); - find_viewing_key(&mut wallet) - } else { - find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) - }; + let viewing_key = + if ctx.global_args.is_pre_genesis || ctx.chain.is_none() { + let wallet_path = + ctx.global_args.base_dir.join(PRE_GENESIS_DIR); + let mut wallet = crate::wallet::load_or_new(&wallet_path); + find_viewing_key(&mut wallet) + } else { + find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) + }; MaspPayAddrGen:: { alias: self.alias, alias_force: self.alias_force, viewing_key, pin: self.pin, - is_pre_genesis: self.is_pre_genesis, } } } @@ -6232,13 +6223,11 @@ pub mod args { let alias_force = ALIAS_FORCE.parse(matches); let viewing_key = VIEWING_KEY.parse(matches); let pin = PIN.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); Self { alias, alias_force, viewing_key, pin, - is_pre_genesis, } } @@ -6256,10 +6245,6 @@ pub mod args { "Require that the single transaction to this address be \ pinned.", )) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) } } @@ -6320,14 +6305,12 @@ pub mod args { let scheme = SCHEME.parse(matches); let alias = ALIAS_OPT.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let derivation_path = HD_WALLET_DERIVATION_PATH.parse(matches); Self { scheme, alias, alias_force, - is_pre_genesis, unsafe_dont_encrypt, derivation_path, } @@ -6346,10 +6329,6 @@ pub mod args { .arg(ALIAS_FORCE.def().help( "Override the alias without confirmation if it already exists.", )) - .arg(PRE_GENESIS.def().help( - "Generate a key for pre-genesis, instead of for the current \ - chain, if any.", - )) .arg(UNSAFE_DONT_ENCRYPT.def().help( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", @@ -6371,14 +6350,12 @@ pub mod args { let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); let alias = ALIAS_OPT.parse(matches); let value = VALUE.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { public_key, alias, value, - is_pre_genesis, unsafe_show_secret, } } @@ -6401,10 +6378,6 @@ pub mod args { .def() .help("A public key or alias associated with the keypair."), ) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) .arg( UNSAFE_SHOW_SECRET .def() @@ -6417,11 +6390,9 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); Self { alias, unsafe_show_secret, - is_pre_genesis, } } @@ -6432,31 +6403,21 @@ pub mod args { .def() .help("UNSAFE: Print the spending key values."), ) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current \ - chain, if any.", - )) } } impl Args for MaspKeysList { fn parse(matches: &ArgMatches) -> Self { let decrypt = DECRYPT.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { decrypt, - is_pre_genesis, unsafe_show_secret, } } fn def(app: App) -> App { app.arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current \ - chain, if any.", - )) .arg( UNSAFE_SHOW_SECRET .def() @@ -6465,38 +6426,18 @@ pub mod args { } } - impl Args for MaspListPayAddrs { - fn parse(matches: &ArgMatches) -> Self { - let is_pre_genesis = PRE_GENESIS.parse(matches); - Self { is_pre_genesis } - } - - fn def(app: App) -> App { - app.arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) - } - } - impl Args for KeyList { fn parse(matches: &ArgMatches) -> Self { let decrypt = DECRYPT.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { decrypt, - is_pre_genesis, unsafe_show_secret, } } fn def(app: App) -> App { app.arg(DECRYPT.def().help("Decrypt keys that are encrypted.")) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current \ - chain, if any.", - )) .arg( UNSAFE_SHOW_SECRET .def() @@ -6508,21 +6449,13 @@ pub mod args { impl Args for KeyExport { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); - Self { - alias, - is_pre_genesis, - } + Self { alias } } fn def(app: App) -> App { app.arg( ALIAS.def().help("The alias of the key you wish to export."), ) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) } } @@ -6530,12 +6463,7 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS_OPT.parse(matches); let address = RAW_ADDRESS_OPT.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); - Self { - alias, - address, - is_pre_genesis, - } + Self { alias, address } } fn def(app: App) -> App { @@ -6549,10 +6477,6 @@ pub mod args { .def() .help("The bech32m encoded address string."), ) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) .group( ArgGroup::new("find_flags") .args([ALIAS_OPT.name, RAW_ADDRESS_OPT.name]) @@ -6561,31 +6485,15 @@ pub mod args { } } - impl Args for AddressList { - fn parse(matches: &ArgMatches) -> Self { - let is_pre_genesis = PRE_GENESIS.parse(matches); - Self { is_pre_genesis } - } - - fn def(app: App) -> App { - app.arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) - } - } - impl Args for AddressAdd { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let address = RAW_ADDRESS.parse(matches); - let is_pre_genesis = PRE_GENESIS.parse(matches); Self { alias, alias_force, address, - is_pre_genesis, } } @@ -6603,10 +6511,6 @@ pub mod args { .def() .help("The bech32m encoded address string."), ) - .arg(PRE_GENESIS.def().help( - "Use pre-genesis wallet, instead of for the current chain, if \ - any.", - )) } } diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index b17b8a34d4..51dd0c0921 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -96,13 +96,16 @@ impl Context { pub fn new(global_args: args::Global) -> Result { let global_config = read_or_try_new_global_config(&global_args); - let chain_id = std::env::var(ENV_VAR_CHAIN_ID) + let env_var_chain_id = std::env::var(ENV_VAR_CHAIN_ID) .ok() .and_then(|chain_id| ChainId::from_str(&chain_id).ok()); - let chain_id = chain_id.as_ref().or(global_args.chain_id.as_ref()); + let chain_id = env_var_chain_id + .as_ref() + .or(global_args.chain_id.as_ref()) + .or(global_config.default_chain_id.as_ref()); let chain = match chain_id { - Some(chain_id) => { + Some(chain_id) if !global_args.is_pre_genesis => { let mut config = Config::load(&global_args.base_dir, chain_id, None); let chain_dir = global_args.base_dir.join(chain_id.as_str()); @@ -138,7 +141,7 @@ impl Context { native_token, }) } - None => None, + _ => None, }; Ok(Self { diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index e8262ccfbf..6028cf067e 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -77,9 +77,7 @@ impl CliApi { cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { address_or_alias_find(ctx, io, args) } - cmds::WalletAddress::List(cmds::AddressList(args)) => { - address_list(ctx, io, args) - } + cmds::WalletAddress::List => address_list(ctx, io), cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { address_add(ctx, io, args) } @@ -99,9 +97,9 @@ impl CliApi { cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { address_key_add(ctx, io, args) } - cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs( - args, - )) => payment_addresses_list(ctx, io, args), + cmds::WalletMasp::ListPayAddrs => { + payment_addresses_list(ctx, io) + } cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { spending_keys_list(ctx, io, args) } @@ -121,10 +119,9 @@ fn address_key_find( args::AddrKeyFind { alias, unsafe_show_secret, - is_pre_genesis, }: args::AddrKeyFind, ) { - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); let alias = alias.to_lowercase(); if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { // Check if alias is a viewing key @@ -160,11 +157,10 @@ fn spending_keys_list( io: &impl Io, args::MaspKeysList { decrypt, - is_pre_genesis, unsafe_show_secret, }: args::MaspKeysList, ) { - let wallet = load_wallet(ctx, is_pre_genesis); + let wallet = load_wallet(ctx); let known_view_keys = wallet.get_viewing_keys(); let known_spend_keys = wallet.get_spending_keys(); if known_view_keys.is_empty() { @@ -232,12 +228,8 @@ fn spending_keys_list( } /// List payment addresses. -fn payment_addresses_list( - ctx: Context, - io: &impl Io, - args::MaspListPayAddrs { is_pre_genesis }: args::MaspListPayAddrs, -) { - let wallet = load_wallet(ctx, is_pre_genesis); +fn payment_addresses_list(ctx: Context, io: &impl Io) { + let wallet = load_wallet(ctx); let known_addresses = wallet.get_payment_addrs(); if known_addresses.is_empty() { display_line!( @@ -262,11 +254,10 @@ fn spending_key_gen( args::MaspSpendKeyGen { alias, alias_force, - is_pre_genesis, unsafe_dont_encrypt, }: args::MaspSpendKeyGen, ) { - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); let alias = alias.to_lowercase(); let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let (alias, _key) = @@ -323,12 +314,11 @@ fn address_key_add( alias, alias_force, value, - is_pre_genesis, unsafe_dont_encrypt, }: args::MaspAddrKeyAdd, ) { let alias = alias.to_lowercase(); - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); let (alias, typ) = match value { MaspValue::FullViewingKey(viewing_key) => { let alias = wallet @@ -495,12 +485,11 @@ fn key_and_address_gen( scheme, alias, alias_force, - is_pre_genesis, unsafe_dont_encrypt, derivation_path, }: args::KeyAndAddressGen, ) { - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); let encryption_password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let derivation_path = decode_derivation_path(scheme, derivation_path) @@ -551,11 +540,10 @@ fn key_find( public_key, alias, value, - is_pre_genesis, unsafe_show_secret, }: args::KeyFind, ) { - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); let found_keypair = match public_key { Some(pk) => wallet.find_key_by_pk(&pk, None), None => { @@ -596,11 +584,10 @@ fn key_list( io: &impl Io, args::KeyList { decrypt, - is_pre_genesis, unsafe_show_secret, }: args::KeyList, ) { - let wallet = load_wallet(ctx, is_pre_genesis); + let wallet = load_wallet(ctx); let known_public_keys = wallet.get_public_keys(); if known_public_keys.is_empty() { display_line!( @@ -663,12 +650,9 @@ fn key_list( fn key_export( ctx: Context, io: &impl Io, - args::KeyExport { - alias, - is_pre_genesis, - }: args::KeyExport, + args::KeyExport { alias }: args::KeyExport, ) { - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); wallet .find_secret_key(alias.to_lowercase(), None) .map(|keypair| { @@ -686,12 +670,8 @@ fn key_export( } /// List all known addresses. -fn address_list( - ctx: Context, - io: &impl Io, - args::AddressList { is_pre_genesis }: args::AddressList, -) { - let wallet = load_wallet(ctx, is_pre_genesis); +fn address_list(ctx: Context, io: &impl Io) { + let wallet = load_wallet(ctx); let known_addresses = wallet.get_addresses(); if known_addresses.is_empty() { display_line!( @@ -717,13 +697,9 @@ fn address_list( fn address_or_alias_find( ctx: Context, io: &impl Io, - args::AddressOrAliasFind { - alias, - address, - is_pre_genesis, - }: args::AddressOrAliasFind, + args::AddressOrAliasFind { alias, address }: args::AddressOrAliasFind, ) { - let wallet = load_wallet(ctx, is_pre_genesis); + let wallet = load_wallet(ctx); if address.is_some() && alias.is_some() { panic!( "This should not be happening: clap should emit its own error \ @@ -762,10 +738,9 @@ fn address_add( alias, alias_force, address, - is_pre_genesis, }: args::AddressAdd, ) { - let mut wallet = load_wallet(ctx, is_pre_genesis); + let mut wallet = load_wallet(ctx); if wallet .insert_address(alias.to_lowercase(), address, alias_force) .is_none() @@ -784,9 +759,9 @@ fn address_add( } /// Load wallet for chain when `ctx.chain.is_some()` or pre-genesis wallet when -/// `is_pre_genesis || ctx.chain.is_none()`. -fn load_wallet(ctx: Context, is_pre_genesis: bool) -> Wallet { - if is_pre_genesis || ctx.chain.is_none() { +/// `ctx.global_args.is_pre_genesis || ctx.chain.is_none()`. +fn load_wallet(ctx: Context) -> Wallet { + if ctx.global_args.is_pre_genesis || ctx.chain.is_none() { let wallet_path = ctx.global_args.base_dir.join(PRE_GENESIS_DIR); wallet::load_or_new(&wallet_path) } else { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 5e4c04b07f..3ec257710a 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -18,6 +18,7 @@ pub fn run( let global = { let locked = node.shell.lock().unwrap(); Global { + is_pre_genesis: false, chain_id: Some(locked.chain_id.clone()), base_dir: locked.base_dir.clone(), wasm_dir: Some(locked.wasm_dir.clone()), diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 23f1643233..7dc708cc60 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -2047,9 +2047,6 @@ pub struct MaspAddrKeyAdd { pub alias_force: bool, /// Any MASP value pub value: MaspValue, - /// Add a MASP key / address pre-genesis instead - /// of a current chain - pub is_pre_genesis: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, } @@ -2061,8 +2058,6 @@ pub struct MaspSpendKeyGen { pub alias: String, /// Whether to force overwrite the alias pub alias_force: bool, - /// Generate spending key pre-genesis instead of a current chain - pub is_pre_genesis: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, } @@ -2078,8 +2073,6 @@ pub struct MaspPayAddrGen { pub viewing_key: C::ViewingKey, /// Pin pub pin: bool, - /// Generate an address pre-genesis instead of a current chain - pub is_pre_genesis: bool, } /// Wallet generate key and implicit address arguments @@ -2091,8 +2084,6 @@ pub struct KeyAndAddressGen { pub alias: Option, /// Whether to force overwrite the alias, if provided pub alias_force: bool, - /// Generate a key for pre-genesis, instead of a current chain - pub is_pre_genesis: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, /// BIP44 derivation path @@ -2125,8 +2116,6 @@ pub struct KeyFind { pub alias: Option, /// Public key hash to lookup keypair with pub value: Option, - /// Find a key pre-genesis instead of a current chain - pub is_pre_genesis: bool, /// Show secret keys to user pub unsafe_show_secret: bool, } @@ -2138,8 +2127,6 @@ pub struct AddrKeyFind { pub alias: String, /// Show secret keys to user pub unsafe_show_secret: bool, - /// Find shielded address / key pre-genesis instead of a current chain - pub is_pre_genesis: bool, } /// Wallet list shielded keys arguments @@ -2147,27 +2134,15 @@ pub struct AddrKeyFind { pub struct MaspKeysList { /// Don't decrypt spending keys pub decrypt: bool, - /// List shielded keys pre-genesis instead of a current chain - pub is_pre_genesis: bool, /// Show secret keys to user pub unsafe_show_secret: bool, } -/// Wallet list shielded payment addresses arguments -#[derive(Clone, Debug)] -pub struct MaspListPayAddrs { - /// List sheilded payment address pre-genesis instead - /// of a current chain - pub is_pre_genesis: bool, -} - /// Wallet list keys arguments #[derive(Clone, Debug)] pub struct KeyList { /// Don't decrypt keypairs pub decrypt: bool, - /// List keys pre-genesis instead of a current chain - pub is_pre_genesis: bool, /// Show secret keys to user pub unsafe_show_secret: bool, } @@ -2177,8 +2152,6 @@ pub struct KeyList { pub struct KeyExport { /// Key alias pub alias: String, - /// Export key pre-genesis instead of a current chain - pub is_pre_genesis: bool, } /// Wallet address lookup arguments @@ -2188,15 +2161,6 @@ pub struct AddressOrAliasFind { pub alias: Option, /// Address to find pub address: Option
, - /// Lookup address pre-genesis instead of a current chain - pub is_pre_genesis: bool, -} - -/// List wallet address -#[derive(Clone, Debug)] -pub struct AddressList { - /// List addresses pre-genesis instead of current chain - pub is_pre_genesis: bool, } /// Wallet address add arguments @@ -2208,8 +2172,6 @@ pub struct AddressAdd { pub alias_force: bool, /// Address to add pub address: Address, - /// Add an address pre-genesis instead of current chain - pub is_pre_genesis: bool, } /// Bridge pool batch recommendation. diff --git a/tests/src/integration/setup.rs b/tests/src/integration/setup.rs index c7c2bdbb5a..828f8be112 100644 --- a/tests/src/integration/setup.rs +++ b/tests/src/integration/setup.rs @@ -70,6 +70,7 @@ pub fn initialize_genesis() -> Result<(MockNode, MockServicesController)> { // addresses and update WASM checksums let wasm_checksums_path = working_dir.join("wasm/checksums.json"); let global_args = args::Global { + is_pre_genesis: true, chain_id: Some(chain_id.clone()), base_dir: test_dir.path().to_path_buf(), wasm_dir: Some(test_dir.path().join(chain_id.as_str()).join("wasm")), From 56107eb82a51e16f9b1233bff037a6ca15b75da1 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 14:47:36 +0000 Subject: [PATCH 74/81] Tune error message on invalid chain id --- apps/src/lib/cli/context.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 51dd0c0921..4214144da3 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -197,10 +197,13 @@ impl Context { fn safe_exit_on_missing_chain_context() -> ! { eprintln!( - "No chain-id was provided. If no chain is configured, you may need to \ - run `namada client utils join-network` command. If the chain is \ - configured, set the chain id with `--chain-id ` or - via the env var `{ENV_VAR_CHAIN_ID}`." + "Failed to construct Namada chain context. If no chain is configured, \ + you may need to run `namada client utils join-network`. If the chain \ + is configured, you may need to set the chain id with `--chain-id \ + `, via the env var `{ENV_VAR_CHAIN_ID}`, or configure the \ + default chain id in the `global-config.toml` file. If you do intend \ + to run pre-genesis operations, pass the `--pre-genesis` flag as the \ + first argument to the command." ); utils::safe_exit(1) } From 24591eeaa6af569caae7f823857d7532b53fbd6d Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 14:52:53 +0000 Subject: [PATCH 75/81] Pre-genesis wallet fixes --- apps/src/lib/cli/wallet.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 6028cf067e..6846339374 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -19,7 +19,6 @@ use namada::types::masp::{MaspValue, PaymentAddress}; use namada_sdk::masp::find_valid_diversifier; use namada_sdk::wallet::{ DecryptionError, DerivationPath, DerivationPathError, FindKeyError, Wallet, - WalletIo, WalletStorage, }; use namada_sdk::{display, display_line, edisplay_line}; use rand_core::OsRng; @@ -42,12 +41,7 @@ impl CliApi { match cmd { cmds::NamadaWallet::Key(sub) => match sub { cmds::WalletKey::Derive(cmds::KeyDerive(args)) => { - key_and_address_derive( - &mut ctx.borrow_mut_chain_or_exit().wallet, - io, - args, - ) - .await + key_and_address_derive(ctx, io, args).await } cmds::WalletKey::Gen(cmds::KeyGen(args)) => { key_and_address_gen(ctx, io, args) @@ -67,12 +61,7 @@ impl CliApi { key_and_address_gen(ctx, io, args) } cmds::WalletAddress::Derive(cmds::AddressDerive(args)) => { - key_and_address_derive( - &mut ctx.borrow_mut_chain_or_exit().wallet, - io, - args, - ) - .await + key_and_address_derive(ctx, io, args).await } cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { address_or_alias_find(ctx, io, args) @@ -88,11 +77,7 @@ impl CliApi { } cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { let args = args.to_sdk(&mut ctx); - payment_address_gen( - &mut ctx.borrow_mut_chain_or_exit().wallet, - io, - args, - ) + payment_address_gen(ctx, io, args) } cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { address_key_add(ctx, io, args) @@ -272,7 +257,7 @@ fn spending_key_gen( /// Generate a shielded payment address from the given key. fn payment_address_gen( - wallet: &mut Wallet, + ctx: Context, io: &impl Io, args::MaspPayAddrGen { alias, @@ -282,6 +267,7 @@ fn payment_address_gen( .. }: args::MaspPayAddrGen, ) { + let mut wallet = load_wallet(ctx); let alias = alias.to_lowercase(); let viewing_key = ExtendedFullViewingKey::from(viewing_key).fvk.vk; let (div, _g_d) = find_valid_diversifier(&mut OsRng); @@ -384,7 +370,7 @@ pub fn decode_derivation_path( /// Derives a keypair and an implicit address from the mnemonic code in the /// wallet. async fn key_and_address_derive( - wallet: &mut Wallet, + ctx: Context, io: &impl Io, args::KeyAndAddressDerive { scheme, @@ -395,6 +381,7 @@ async fn key_and_address_derive( use_device, }: args::KeyAndAddressDerive, ) { + let mut wallet = load_wallet(ctx); let derivation_path = decode_derivation_path(scheme, derivation_path) .unwrap_or_else(|err| { edisplay_line!(io, "{}", err); From ab92dfdade70154477bf77877fa42870a4565391 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Wed, 6 Dec 2023 14:55:50 +0000 Subject: [PATCH 76/81] Mandatory `--pre-genesis` flag --- apps/src/lib/cli.rs | 17 ++++++++--------- apps/src/lib/cli/wallet.rs | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 9e4a5c7c30..b62baea563 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6199,15 +6199,14 @@ pub mod args { safe_exit(1) }) }; - let viewing_key = - if ctx.global_args.is_pre_genesis || ctx.chain.is_none() { - let wallet_path = - ctx.global_args.base_dir.join(PRE_GENESIS_DIR); - let mut wallet = crate::wallet::load_or_new(&wallet_path); - find_viewing_key(&mut wallet) - } else { - find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) - }; + let viewing_key = if ctx.global_args.is_pre_genesis { + let wallet_path = + ctx.global_args.base_dir.join(PRE_GENESIS_DIR); + let mut wallet = crate::wallet::load_or_new(&wallet_path); + find_viewing_key(&mut wallet) + } else { + find_viewing_key(&mut ctx.borrow_mut_chain_or_exit().wallet) + }; MaspPayAddrGen:: { alias: self.alias, alias_force: self.alias_force, diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 6846339374..136d6e2683 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -746,9 +746,9 @@ fn address_add( } /// Load wallet for chain when `ctx.chain.is_some()` or pre-genesis wallet when -/// `ctx.global_args.is_pre_genesis || ctx.chain.is_none()`. +/// `ctx.global_args.is_pre_genesis`. fn load_wallet(ctx: Context) -> Wallet { - if ctx.global_args.is_pre_genesis || ctx.chain.is_none() { + if ctx.global_args.is_pre_genesis { let wallet_path = ctx.global_args.base_dir.join(PRE_GENESIS_DIR); wallet::load_or_new(&wallet_path) } else { From 3ff75cd89a190bb02830090bf22ac1d15e22b728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 6 Dec 2023 20:35:30 +0000 Subject: [PATCH 77/81] genesis: update README and update files --- genesis/localnet/README.md | 88 ++++++++++++++++--- .../src/pre-genesis/bond/bond-tx-albert.toml | 4 + .../established-account-tx-validator-0.toml | 4 - .../pre-genesis/unsigned-transactions.toml | 11 ++- 4 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 genesis/localnet/src/pre-genesis/bond/bond-tx-albert.toml delete mode 100644 genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml diff --git a/genesis/localnet/README.md b/genesis/localnet/README.md index e14cfdfb61..b50087d243 100644 --- a/genesis/localnet/README.md +++ b/genesis/localnet/README.md @@ -8,9 +8,7 @@ If you're modifying any of the files here, you can run this to ensure that the c cargo watch -x "test test_validate_localnet_genesis_templates" ``` -## balances.toml - -The pre-genesis balances wallet is located at [pre-genesis/wallet.toml](pre-genesis/wallet.toml) was used to setup the [balances.toml](balances.toml) and can be re-generated from the repo's root dir with: +The pre-genesis balances wallet is located at [pre-genesis/wallet.toml](pre-genesis/wallet.toml) and can be re-generated from the repo's root dir with: ```shell cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key gen \ @@ -22,46 +20,108 @@ cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key gen \ cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key gen \ --alias daewon --unsafe-dont-encrypt cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key gen \ - --alias validator-0-balance-key --unsafe-dont-encrypt + --alias validator-0-account-key --unsafe-dont-encrypt cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key gen \ --alias faucet-key --unsafe-dont-encrypt ``` -The [balances.toml file](balances.toml) contains token balances associated with the public keys. The public keys from the wallet can be found with: +Some keys are used to setup established accounts and some are directly assigned balances in the [balances.toml](#balancestoml) file to implicit addresses derived from these keys. + +## transactions.toml + +### Transaction to initialize an established account + +For example, Albert's account is created with: ```shell -cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key list +cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ + init-genesis-established-account \ + --path "genesis/localnet/src/pre-genesis/established/established-account-tx-albert.toml" \ + --aliases "albert-key" ``` -## transactions.toml +Note that the command will print out your `Derived established account address`. + +### Validator transactions -The pre-genesis validator wallet used to generate [validator transactions for transactions.toml](src/pre-genesis/validator-0/transactions.toml) is located at [src/pre-genesis/validator-0/validator-wallet.toml](src/pre-genesis/validator-0/validator-wallet.toml) and can be re-generated from the repo's root dir with: +To create a validator's account, first initialize an established account: + +```shell +cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ + init-genesis-established-account \ + --path "genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml" \ + --aliases "validator-0-account-key" +``` + +The `Derived established account address` and the transaction added to the TOML file from this command is used in the following command. + +The pre-genesis validator wallet used to generate [validator transactions for transactions.toml](src/pre-genesis/validator-0/transactions.toml) is located at [src/pre-genesis/validator-0/validator-wallet.toml](src/pre-genesis/validator-0/validator-wallet.toml) and can be re-generated: ```shell cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ init-genesis-validator \ - --source validator-0-balance-key \ --alias validator-0 \ + --address tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx \ + --path "genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml" \ --net-address "127.0.0.1:27656" \ --commission-rate 0.05 \ --max-commission-rate-change 0.01 \ --email "null@null.net" \ - --transfer-from-source-amount 200000 \ --self-bond-amount 100000 \ --unsafe-dont-encrypt ``` -The rest of the transactions are generated from [src/pre-genesis/unsigned-transactions.toml](src/pre-genesis/unsigned-transactions.toml) using: +### Delegations + +A delegation with e.g. 20 000 NAM tokens to a validator account whose address has to be known beforehand (here the validator-0 created above) is created with: + +```shell +cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ + genesis-bond \ + --validator tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx \ + --amount 20000 \ + --path "genesis/localnet/src/pre-genesis/bond/bond-tx-albert.toml" +``` + +### Signing + +The non-validator transactions are manually appended together in [src/pre-genesis/unsigned-transactions.toml](src/pre-genesis/unsigned-transactions.toml) and then signed to produce [src/pre-genesis/signed-transactions.toml](src/pre-genesis/signed-transactions.toml) using: ```shell cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ - sign-genesis-tx \ + sign-genesis-txs \ --path "genesis/localnet/src/pre-genesis/unsigned-transactions.toml" \ --output "genesis/localnet/src/pre-genesis/signed-transactions.toml" ``` -This command produces [src/pre-genesis/signed-transactions.toml](src/pre-genesis/signed-transactions.toml), which is then concatenated in [transactions.toml](transactiosn.toml) with the validator transactions. +The validator transactions are signed using (note the extra `--alias` argument needed to find the validator pre-genesis wallet): + +```shell +cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ + sign-genesis-txs \ + --path "genesis/localnet/src/pre-genesis/validator-0/unsigned-transactions.toml" \ + --output "genesis/localnet/src/validator-0/signed-transactions.toml" + --alias validator-0 +``` + +This non-validator [src/pre-genesis/signed-transactions.toml](src/pre-genesis/signed-transactions.toml) are joined together with [src/validator-0/signed-transactions.toml](src/validator-0/signed-transactions.toml) in [transactions.toml](transactions.toml). + +## balances.toml + +The [balances.toml file](balances.toml) contains token balances associated with public keys or established addresses which can be derived from genesis transactions. The public keys from the wallet can be found with: + +```shell +cargo run --bin namadaw -- --base-dir "genesis/localnet/src" key list +``` + +If you didn't note the address from your transactions, you can deterministically derive an established address from the TOML file again, run with the `--path` set to a transaction TOML file: + +```shell +cargo run --bin namadac -- --base-dir "genesis/localnet/src" utils \ + derive-genesis-addresses \ + --path "genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml" +``` ## Validation -A unit test `test_localnet_genesis_templates` is setup to check validity of the localnet setup. +A unit test `test_validate_localnet_genesis_templates` is setup to check validity of the localnet setup. diff --git a/genesis/localnet/src/pre-genesis/bond/bond-tx-albert.toml b/genesis/localnet/src/pre-genesis/bond/bond-tx-albert.toml new file mode 100644 index 0000000000..73b2960a07 --- /dev/null +++ b/genesis/localnet/src/pre-genesis/bond/bond-tx-albert.toml @@ -0,0 +1,4 @@ +[[bond]] +source = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +validator = "tnam1q9vhfdur7gadtwx4r223agpal0fvlqhywylf2mzx" +amount = "20000" diff --git a/genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml b/genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml deleted file mode 100644 index 562ffd2ba0..0000000000 --- a/genesis/localnet/src/pre-genesis/established/established-account-tx-validator-0.toml +++ /dev/null @@ -1,4 +0,0 @@ -[[established_account]] -vp = "vp_user" -threshold = 1 -public_keys = ["tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj"] diff --git a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml index c1f290bec5..32dc9fff47 100644 --- a/genesis/localnet/src/pre-genesis/unsigned-transactions.toml +++ b/genesis/localnet/src/pre-genesis/unsigned-transactions.toml @@ -1,15 +1,14 @@ # This file contains hand-written unsigned transactions for localnet with: # -# - MASP account -# - Faucet account that allows anyone to withdraw limited amount of tokens -# - 2 established accounts for "Albert" and "Bertha" +# - 3 established accounts for "Albert", "Bertha" and "Christel" +# - a bond from "Albert"'s established account to validator-0 # -# Note that 2 more localnet user accounts "Christel" and "Daewon" are left as -# implicit accounts, so their tokens are kept in the accounts derived from their +# Note that 1 localnet user account "Daewon" is left as +# implicit account, so their tokens are kept in the accounts derived from their # keys used in `balances.toml`. # # This file is used to produce `signed-transactions.toml` with -# the `sign-genesis-tx` command. +# the `sign-genesis-txs` command. # Albert [[established_account]] From ce7f498ad0aab34381e8055939937b620ec2b9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 6 Dec 2023 20:36:07 +0000 Subject: [PATCH 78/81] genesis: update starter README --- genesis/starter/README.md | 66 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/genesis/starter/README.md b/genesis/starter/README.md index 258eca8a9a..3d39fa6ca4 100644 --- a/genesis/starter/README.md +++ b/genesis/starter/README.md @@ -37,8 +37,38 @@ The public key can then be given some tokens in the [balances.toml file](balance tpknam1qz5ywdn47sdm8s7rkzjl5dud0k9c9ndd5agn4gu0u0ryrmtmyuxmk0h25th = 1_337_707.50 ``` +## Established accounts + +For example, an established account can be created with: + +```shell +namada client utils \ + init-genesis-established-account \ + --path "{acc_tx_file}.toml" \ + --aliases "{key_aliases_from_wallet}" +``` + +For a multisig also specify a value for `--threshold` (defaults to 1 for single-signature accounts). + +The command will print out a `Derived established account address`. It can be derived from the file again: + +```shell +namada client utils \ + derive-genesis-addresses \ + --path "{acc_tx_file}.toml" +``` + ## Validator accounts +To create a validator's account, first initialize an established account: + +```shell +namada client utils \ + init-genesis-established-account \ + --path "{validator_txs_file}.toml" \ + --aliases "{key_aliases_from_wallet}" +``` + For this step, you'll need to have a key with some native [token balance](#token-balances), from which you can sign the validator account creation genesis transaction. To generate a new validator pre-genesis wallet and produce signed transactions with it, use e.g.: @@ -46,8 +76,9 @@ To generate a new validator pre-genesis wallet and produce signed transactions w ```shell namada client utils \ init-genesis-validator \ - --source "my-key" \ --alias "my-validator" \ + --address "{address_derived_from_previous_cmd}" \ + --path "{validator_txs_file}.toml" \ --net-address "127.0.0.1:26656" \ --commission-rate 0.05 \ --max-commission-rate-change 0.01 \ @@ -57,6 +88,39 @@ namada client utils \ This will print the validator transactions that can be added to the [transactions.toml file](transactions.toml). +## Delegations + +A delegation with native tokens to a validator account whose address has to be known beforehand is created with: + +```shell +namada client utils \ + genesis-bond \ + --validator "{validator_address}" \ + --amount {amount} \ + --path "{bond_tx_file}.toml" +``` + +## Signing + +Non-validator transactions can be signed using: + +```shell +namada client utils \ + sign-genesis-txs \ + --path "{tx_file}.toml" \ + --output "{signed_tx_file}.toml" +``` + +Validator transactions require an extra `--alias` argument to find the validator pre-genesis wallet: + +```shell +namada client utils \ + sign-genesis-txs \ + --path "{validator_txs_file}.toml" \ + --output "{signed_validator_txs_file}.toml" + --alias my-validator +``` + ## Initialize the chain This is sufficient minimal configuration to initialize the chain with the single genesis validator. All that's left is to pick a chain ID prefix and genesis time: From 6df5ae644b7e0506f8caa57c85ba0d14f4c25791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 6 Dec 2023 20:36:19 +0000 Subject: [PATCH 79/81] benches: fix the default validator key for signing txs --- apps/src/lib/wallet/defaults.rs | 9 +++++++-- benches/vps.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index 7f5a92c03b..e632fe6269 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -4,8 +4,8 @@ pub use dev::{ addresses, albert_address, albert_keypair, bertha_address, bertha_keypair, christel_address, christel_keypair, daewon_address, daewon_keypair, - ester_address, ester_keypair, keys, tokens, validator_address, - validator_keypair, validator_keys, + ester_address, ester_keypair, keys, tokens, validator_account_keypair, + validator_address, validator_keypair, validator_keys, }; #[cfg(any(test, feature = "testing", feature = "benches"))] @@ -176,6 +176,11 @@ mod dev { VALIDATOR_WALLET.consensus_key.clone() } + /// Get the validator account keypair from the wallet. + pub fn validator_account_keypair() -> common::SecretKey { + get_unencrypted_keypair("validator-0-account-key") + } + /// The name of a file that is unique to the project's root directory. const PROJECT_ROOT_UNIQUE_FILE: &str = "rust-toolchain.toml"; diff --git a/benches/vps.rs b/benches/vps.rs index 366887d1fa..f5a035e542 100644 --- a/benches/vps.rs +++ b/benches/vps.rs @@ -341,7 +341,7 @@ fn vp_validator(c: &mut Criterion) { }, None, None, - vec![&defaults::validator_keypair()], + vec![&defaults::validator_account_keypair()], ); let received_transfer = shell.generate_tx( @@ -371,7 +371,7 @@ fn vp_validator(c: &mut Criterion) { .finalize_reset() .into(), )), - public_keys: vec![defaults::validator_keypair().to_public()], + public_keys: vec![defaults::validator_account_keypair().to_public()], threshold: None, }; let vp = shell.generate_tx( @@ -379,7 +379,7 @@ fn vp_validator(c: &mut Criterion) { data, None, Some(vec![extra_section]), - vec![&defaults::validator_keypair()], + vec![&defaults::validator_account_keypair()], ); let commission_rate = shell.generate_tx( @@ -390,7 +390,7 @@ fn vp_validator(c: &mut Criterion) { }, None, None, - vec![&defaults::validator_keypair()], + vec![&defaults::validator_account_keypair()], ); let vote = shell.generate_tx( @@ -403,7 +403,7 @@ fn vp_validator(c: &mut Criterion) { }, None, None, - vec![&defaults::validator_keypair()], + vec![&defaults::validator_account_keypair()], ); let pos = shell.generate_tx( @@ -415,7 +415,7 @@ fn vp_validator(c: &mut Criterion) { }, None, None, - vec![&defaults::validator_keypair()], + vec![&defaults::validator_account_keypair()], ); for (signed_tx, bench_name) in [ From c7e2b423695f567aed17bf68c28a37d887e3ef12 Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 6 Dec 2023 16:46:17 -0500 Subject: [PATCH 80/81] small grammar fixes --- apps/src/lib/cli/utils.rs | 4 ++-- apps/src/lib/client/tx.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index b0a6493a2b..0f50a0926a 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -28,11 +28,11 @@ const RELAYER_KEY_ENV_VAR: &str = "NAMADA_RELAYER_KEY"; pub type App = clap::Command; pub type ClapArg = clap::Arg; -/// Mode of operation og [`ArgMulti`] where zero or +/// Mode of operation of [`ArgMulti`] where zero or /// more arguments may be present (i.e. `*`). pub enum GlobStar {} -/// Mode of operation og [`ArgMulti`] where at least +/// Mode of operation of [`ArgMulti`] where at least /// one argument must be present (i.e. `+`). pub enum GlobPlus {} diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index fef1d52035..181bb4fc6c 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -169,7 +169,7 @@ pub async fn sign<'a, N: Namada<'a>>( let app = NamadaApp::new(TransportNativeHID::new(&hidapi).map_err( |err| { error::Error::Other(format!( - "Unble to connect to Ledger: {}", + "Unable to connect to Ledger: {}", err )) }, From 62730cbeb8862b144f8f790a899e4b6b6b247524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 6 Dec 2023 21:51:07 +0000 Subject: [PATCH 81/81] changelog: add #2186 --- .../unreleased/improvements/2186-remove-genesis-aliases.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changelog/unreleased/improvements/2186-remove-genesis-aliases.md diff --git a/.changelog/unreleased/improvements/2186-remove-genesis-aliases.md b/.changelog/unreleased/improvements/2186-remove-genesis-aliases.md new file mode 100644 index 0000000000..ddd071e92d --- /dev/null +++ b/.changelog/unreleased/improvements/2186-remove-genesis-aliases.md @@ -0,0 +1,4 @@ +- Changed pre-genesis established addresses to be derived from their data. + Improved signing of pre-genesis transactions to use the same format as + regular transactions. Genesis token balances now can be directly assigned to + established addresses. ([\#2186](https://github.com/anoma/namada/pull/2186)) \ No newline at end of file