diff --git a/codec/src/block.rs b/codec/src/block.rs index b898534c..592a480f 100644 --- a/codec/src/block.rs +++ b/codec/src/block.rs @@ -1,5 +1,5 @@ use acropolis_common::{ - GenesisDelegate, HeavyDelegate, crypto::keyhash_224, queries::blocks::BlockIssuer, + GenesisDelegate, HeavyDelegate, PoolId, crypto::keyhash_224, queries::blocks::BlockIssuer, }; use pallas_primitives::byron::BlockSig::DlgSig; use pallas_traverse::MultiEraHeader; @@ -7,8 +7,8 @@ use std::collections::HashMap; pub fn map_to_block_issuer( header: &MultiEraHeader, - byron_heavy_delegates: &HashMap, HeavyDelegate>, - shelley_genesis_delegates: &HashMap, GenesisDelegate>, + byron_heavy_delegates: &HashMap, + shelley_genesis_delegates: &HashMap, ) -> Option { match header.issuer_vkey() { Some(vkey) => match header { diff --git a/codec/src/map_parameters.rs b/codec/src/map_parameters.rs index f7328bdb..f470d3f8 100644 --- a/codec/src/map_parameters.rs +++ b/codec/src/map_parameters.rs @@ -12,6 +12,7 @@ use pallas::ledger::{ *, }; +use acropolis_common::hash::Hash; use acropolis_common::{ protocol_params::{Nonce, NonceVariant, ProtocolVersion}, rational_number::RationalNumber, @@ -28,6 +29,34 @@ pub fn map_network(network: addresses::Network) -> Result { } } +/// Convert a Pallas Hash reference to an Acropolis Hash (owned) +/// Works for any hash size N +pub fn to_hash(pallas_hash: &pallas_primitives::Hash) -> Hash { + Hash::try_from(pallas_hash.as_ref()).unwrap() +} + +/// Convert a Pallas Hash reference to an Acropolis Hash (owned) +/// Works for any hash size N +pub fn genesis_to_hash(pallas_hash: &pallas_primitives::Genesishash) -> Hash<32> { + Hash::try_from(pallas_hash.as_ref()).unwrap() +} + +/// Convert a Pallas Hash reference to an Acropolis Hash (owned) +/// Works for any hash size N +pub fn genesis_delegate_to_hash(pallas_hash: &pallas_primitives::GenesisDelegateHash) -> PoolId { + PoolId::try_from(pallas_hash.as_ref()).unwrap() +} + +/// Convert a Pallas Hash<28> reference to an Acropolis PoolId +pub fn to_pool_id(pallas_hash: &pallas_primitives::Hash<28>) -> PoolId { + to_hash(pallas_hash).into() +} + +/// Convert a Pallas Hash<32> reference to an Acropolis VRFKey +pub fn to_vrf_key(pallas_hash: &pallas_primitives::Hash<32>) -> VrfKeyHash { + VrfKeyHash::try_from(pallas_hash.as_ref()).unwrap() +} + /// Derive our Address from a Pallas address // This is essentially a 1:1 mapping but makes the Message definitions independent // of Pallas @@ -42,20 +71,20 @@ pub fn map_address(address: &addresses::Address) -> Result
{ payment: match shelley_address.payment() { addresses::ShelleyPaymentPart::Key(hash) => { - ShelleyAddressPaymentPart::PaymentKeyHash(hash.to_vec()) + ShelleyAddressPaymentPart::PaymentKeyHash(to_hash(hash)) } addresses::ShelleyPaymentPart::Script(hash) => { - ShelleyAddressPaymentPart::ScriptHash(hash.to_vec()) + ShelleyAddressPaymentPart::ScriptHash(to_hash(hash)) } }, delegation: match shelley_address.delegation() { addresses::ShelleyDelegationPart::Null => ShelleyAddressDelegationPart::None, addresses::ShelleyDelegationPart::Key(hash) => { - ShelleyAddressDelegationPart::StakeKeyHash(hash.to_vec()) + ShelleyAddressDelegationPart::StakeKeyHash(to_hash(hash)) } addresses::ShelleyDelegationPart::Script(hash) => { - ShelleyAddressDelegationPart::ScriptHash(hash.to_vec()) + ShelleyAddressDelegationPart::ScriptHash(to_hash(hash)) } addresses::ShelleyDelegationPart::Pointer(pointer) => { ShelleyAddressDelegationPart::Pointer(ShelleyAddressPointer { @@ -70,8 +99,8 @@ pub fn map_address(address: &addresses::Address) -> Result
{ addresses::Address::Stake(stake_address) => Ok(Address::Stake(StakeAddress { network: map_network(stake_address.network())?, credential: match stake_address.payload() { - addresses::StakePayload::Stake(hash) => StakeCredential::AddrKeyHash(hash.to_vec()), - addresses::StakePayload::Script(hash) => StakeCredential::ScriptHash(hash.to_vec()), + addresses::StakePayload::Stake(hash) => StakeCredential::AddrKeyHash(to_hash(hash)), + addresses::StakePayload::Script(hash) => StakeCredential::ScriptHash(to_hash(hash)), }, })), } @@ -81,10 +110,10 @@ pub fn map_address(address: &addresses::Address) -> Result
{ pub fn map_stake_credential(cred: &PallasStakeCredential) -> StakeCredential { match cred { PallasStakeCredential::AddrKeyhash(key_hash) => { - StakeCredential::AddrKeyHash(key_hash.to_vec()) + StakeCredential::AddrKeyHash(to_hash(key_hash)) } PallasStakeCredential::ScriptHash(script_hash) => { - StakeCredential::ScriptHash(script_hash.to_vec()) + StakeCredential::ScriptHash(to_hash(script_hash)) } } } @@ -93,10 +122,10 @@ pub fn map_stake_credential(cred: &PallasStakeCredential) -> StakeCredential { pub fn map_stake_address(cred: &PallasStakeCredential, network_id: NetworkId) -> StakeAddress { let payload = match cred { PallasStakeCredential::AddrKeyhash(key_hash) => { - StakeCredential::AddrKeyHash(key_hash.to_vec()) + StakeCredential::AddrKeyHash(to_hash(key_hash)) } PallasStakeCredential::ScriptHash(script_hash) => { - StakeCredential::ScriptHash(script_hash.to_vec()) + StakeCredential::ScriptHash(to_hash(script_hash)) } }; @@ -106,8 +135,8 @@ pub fn map_stake_address(cred: &PallasStakeCredential, network_id: NetworkId) -> /// Map a Pallas DRep to our DRepChoice pub fn map_drep(drep: &conway::DRep) -> DRepChoice { match drep { - conway::DRep::Key(key_hash) => DRepChoice::Key(key_hash.to_vec()), - conway::DRep::Script(script_hash) => DRepChoice::Script(script_hash.to_vec()), + conway::DRep::Key(key_hash) => DRepChoice::Key(to_hash(key_hash)), + conway::DRep::Script(script_hash) => DRepChoice::Script(to_hash(script_hash)), conway::DRep::Abstain => DRepChoice::Abstain, conway::DRep::NoConfidence => DRepChoice::NoConfidence, } @@ -169,7 +198,7 @@ pub fn map_nullable_gov_action_id( fn map_constitution(constitution: &conway::Constitution) -> Constitution { Constitution { anchor: map_anchor(&constitution.anchor), - guardrail_script: map_nullable(|x| x.to_vec(), &constitution.guardrail_script), + guardrail_script: map_nullable(|x| to_hash(x), &constitution.guardrail_script), } } @@ -231,7 +260,7 @@ pub fn map_certificate( alonzo::Certificate::StakeDelegation(cred, pool_key_hash) => Ok(TxCertificateWithPos { cert: TxCertificate::StakeDelegation(StakeDelegation { stake_address: map_stake_address(cred, network_id), - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), }), tx_identifier, cert_index: cert_index.try_into().unwrap(), @@ -248,8 +277,8 @@ pub fn map_certificate( pool_metadata, } => Ok(TxCertificateWithPos { cert: TxCertificate::PoolRegistration(PoolRegistration { - operator: operator.to_vec(), - vrf_key_hash: vrf_keyhash.to_vec(), + operator: to_pool_id(operator), + vrf_key_hash: to_vrf_key(vrf_keyhash), pledge: *pledge, cost: *cost, margin: Ratio { @@ -261,7 +290,7 @@ pub fn map_certificate( .iter() .map(|v| { StakeAddress::new( - StakeCredential::AddrKeyHash(v.to_vec()), + StakeCredential::AddrKeyHash(to_hash(v)), network_id.clone(), ) }) @@ -280,7 +309,7 @@ pub fn map_certificate( }), alonzo::Certificate::PoolRetirement(pool_key_hash, epoch) => Ok(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), epoch: *epoch, }), tx_identifier, @@ -292,9 +321,9 @@ pub fn map_certificate( vrf_key_hash, ) => Ok(TxCertificateWithPos { cert: TxCertificate::GenesisKeyDelegation(GenesisKeyDelegation { - genesis_hash: genesis_hash.to_vec(), - genesis_delegate_hash: genesis_delegate_hash.to_vec(), - vrf_key_hash: vrf_key_hash.to_vec(), + genesis_hash: genesis_to_hash(genesis_hash), + genesis_delegate_hash: genesis_delegate_to_hash(genesis_delegate_hash), + vrf_key_hash: to_vrf_key(vrf_key_hash), }), tx_identifier, cert_index: cert_index as u64, @@ -348,7 +377,7 @@ pub fn map_certificate( Ok(TxCertificateWithPos { cert: TxCertificate::StakeDelegation(StakeDelegation { stake_address: map_stake_address(cred, network_id), - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), }), tx_identifier, cert_index: cert_index.try_into().unwrap(), @@ -368,8 +397,8 @@ pub fn map_certificate( pool_metadata, } => Ok(TxCertificateWithPos { cert: TxCertificate::PoolRegistration(PoolRegistration { - operator: operator.to_vec(), - vrf_key_hash: vrf_keyhash.to_vec(), + operator: to_pool_id(operator), + vrf_key_hash: to_vrf_key(vrf_keyhash), pledge: *pledge, cost: *cost, margin: Ratio { @@ -388,7 +417,7 @@ pub fn map_certificate( .into_iter() .map(|v| { StakeAddress::new( - StakeCredential::AddrKeyHash(v.to_vec()), + StakeCredential::AddrKeyHash(to_hash(v)), network_id.clone(), ) }) @@ -408,7 +437,7 @@ pub fn map_certificate( conway::Certificate::PoolRetirement(pool_key_hash, epoch) => { Ok(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), epoch: *epoch, }), tx_identifier, @@ -447,7 +476,7 @@ pub fn map_certificate( Ok(TxCertificateWithPos { cert: TxCertificate::StakeAndVoteDelegation(StakeAndVoteDelegation { stake_address: map_stake_address(cred, network_id), - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), drep: map_drep(drep), }), tx_identifier, @@ -460,7 +489,7 @@ pub fn map_certificate( cert: TxCertificate::StakeRegistrationAndDelegation( StakeRegistrationAndDelegation { stake_address: map_stake_address(cred, network_id), - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), deposit: *coin, }, ), @@ -486,7 +515,7 @@ pub fn map_certificate( cert: TxCertificate::StakeRegistrationAndStakeAndVoteDelegation( StakeRegistrationAndStakeAndVoteDelegation { stake_address: map_stake_address(cred, network_id), - operator: pool_key_hash.to_vec(), + operator: to_pool_id(pool_key_hash), drep: map_drep(drep), deposit: *coin, }, @@ -881,14 +910,14 @@ pub fn map_governance_proposals_procedures( fn map_voter(voter: &conway::Voter) -> Voter { match voter { conway::Voter::ConstitutionalCommitteeKey(key_hash) => { - Voter::ConstitutionalCommitteeKey(key_hash.to_vec()) + Voter::ConstitutionalCommitteeKey(to_hash(key_hash).into()) } conway::Voter::ConstitutionalCommitteeScript(script_hash) => { - Voter::ConstitutionalCommitteeScript(script_hash.to_vec()) + Voter::ConstitutionalCommitteeScript(to_hash(script_hash).into()) } - conway::Voter::DRepKey(addr_key_hash) => Voter::DRepKey(addr_key_hash.to_vec()), - conway::Voter::DRepScript(script_hash) => Voter::DRepScript(script_hash.to_vec()), - conway::Voter::StakePoolKey(key_hash) => Voter::StakePoolKey(key_hash.to_vec()), + conway::Voter::DRepKey(addr_key_hash) => Voter::DRepKey(to_hash(addr_key_hash).into()), + conway::Voter::DRepScript(script_hash) => Voter::DRepScript(to_hash(script_hash).into()), + conway::Voter::StakePoolKey(key_hash) => Voter::StakePoolKey(to_pool_id(key_hash)), } } diff --git a/common/src/address.rs b/common/src/address.rs index 46cee671..7a3bfa40 100644 --- a/common/src/address.rs +++ b/common/src/address.rs @@ -3,8 +3,7 @@ #![allow(dead_code)] use crate::cip19::{VarIntDecoder, VarIntEncoder}; -use crate::types::{KeyHash, ScriptHash}; -use crate::{Credential, NetworkId, StakeCredential}; +use crate::{Credential, KeyHash, NetworkId, ScriptHash, StakeCredential}; use anyhow::{anyhow, bail, Result}; use crc::{Crc, CRC_32_ISO_HDLC}; use minicbor::data::IanaTag; @@ -106,7 +105,7 @@ pub enum ShelleyAddressPaymentPart { impl Default for ShelleyAddressPaymentPart { fn default() -> Self { - Self::PaymentKeyHash(Vec::new()) + Self::PaymentKeyHash(KeyHash::default()) } } @@ -156,7 +155,7 @@ pub enum ShelleyAddressDelegationPart { /// Delegation to stake key #[n(1)] - StakeKeyHash(#[n(0)] Vec), + StakeKeyHash(#[n(0)] KeyHash), /// Delegation to script key hash #[n(2)] @@ -213,14 +212,30 @@ impl ShelleyAddress { let header = *header; let payment_part = match (header >> 4) & 0x01 { - 0 => ShelleyAddressPaymentPart::PaymentKeyHash(data[1..29].to_vec()), - 1 => ShelleyAddressPaymentPart::ScriptHash(data[1..29].to_vec()), + 0 => ShelleyAddressPaymentPart::PaymentKeyHash( + data[1..29] + .try_into() + .map_err(|e| anyhow!("Failed to parse payment key hash: {}", e))?, + ), + 1 => ShelleyAddressPaymentPart::ScriptHash( + data[1..29] + .try_into() + .map_err(|e| anyhow!("Failed to parse payment script hash: {}", e))?, + ), _ => panic!(), }; let delegation_part = match (header >> 5) & 0x03 { - 0 => ShelleyAddressDelegationPart::StakeKeyHash(data[29..57].to_vec()), - 1 => ShelleyAddressDelegationPart::ScriptHash(data[29..57].to_vec()), + 0 => ShelleyAddressDelegationPart::StakeKeyHash( + data[29..57] + .try_into() + .map_err(|e| anyhow!("Failed to parse stake key hash: {}", e))?, + ), + 1 => ShelleyAddressDelegationPart::ScriptHash( + data[29..57] + .try_into() + .map_err(|e| anyhow!("Failed to parse stake script hash: {}", e))?, + ), 2 => { let mut decoder = VarIntDecoder::new(&data[29..]); let slot = decoder.read()?; @@ -254,27 +269,27 @@ impl ShelleyAddress { NetworkId::Testnet => (bech32::Hrp::parse("addr_test")?, 0u8), }; - let (payment_hash, payment_bits): (&Vec, u8) = match &self.payment { + let (payment_hash, payment_bits): (&KeyHash, u8) = match &self.payment { ShelleyAddressPaymentPart::PaymentKeyHash(data) => (data, 0), ShelleyAddressPaymentPart::ScriptHash(data) => (data, 1), }; - let (delegation_hash, delegation_bits): (&Vec, u8) = match &self.delegation { - ShelleyAddressDelegationPart::None => (&Vec::new(), 3), - ShelleyAddressDelegationPart::StakeKeyHash(hash) => (hash, 0), - ShelleyAddressDelegationPart::ScriptHash(hash) => (hash, 1), + let (delegation_hash, delegation_bits): (Vec, u8) = match &self.delegation { + ShelleyAddressDelegationPart::None => (Vec::new(), 3), + ShelleyAddressDelegationPart::StakeKeyHash(hash) => (hash.to_vec(), 0), + ShelleyAddressDelegationPart::ScriptHash(hash) => (hash.to_vec(), 1), ShelleyAddressDelegationPart::Pointer(pointer) => { let mut encoder = VarIntEncoder::new(); encoder.push(pointer.slot); encoder.push(pointer.tx_index); encoder.push(pointer.cert_index); - (&encoder.to_vec(), 2) + (encoder.to_vec(), 2) } }; let mut data = vec![network_bits | (payment_bits << 4) | (delegation_bits << 5)]; - data.extend(payment_hash); - data.extend(delegation_hash); + data.extend(payment_hash.as_ref()); + data.extend(&delegation_hash); Ok(bech32::encode::(hrp, &data)?) } @@ -284,7 +299,7 @@ impl ShelleyAddress { NetworkId::Testnet => 0u8, }; - let (payment_hash, payment_bits): (&Vec, u8) = match &self.payment { + let (payment_hash, payment_bits): (&KeyHash, u8) = match &self.payment { ShelleyAddressPaymentPart::PaymentKeyHash(data) => (data, 0), ShelleyAddressPaymentPart::ScriptHash(data) => (data, 1), }; @@ -298,24 +313,24 @@ impl ShelleyAddress { ShelleyAddressDelegationPart::None => { let header = build_header(3); data.push(header); - data.extend(payment_hash); + data.extend_from_slice(payment_hash.as_ref()); } ShelleyAddressDelegationPart::StakeKeyHash(hash) => { let header = build_header(0); data.push(header); - data.extend(payment_hash); - data.extend(hash); + data.extend_from_slice(payment_hash.as_ref()); + data.extend_from_slice(hash.as_ref()); } ShelleyAddressDelegationPart::ScriptHash(hash) => { let header = build_header(1); data.push(header); - data.extend(payment_hash); - data.extend(hash); + data.extend_from_slice(payment_hash.as_ref()); + data.extend_from_slice(hash.as_ref()); } ShelleyAddressDelegationPart::Pointer(pointer) => { let header = build_header(2); data.push(header); - data.extend(payment_hash); + data.extend_from_slice(payment_hash.as_ref()); let mut encoder = VarIntEncoder::new(); encoder.push(pointer.slot); @@ -338,14 +353,14 @@ impl ShelleyAddress { ShelleyAddressDelegationPart::StakeKeyHash(key_hash) => { let mut data = Vec::with_capacity(29); data.push(network_bit | (0b1110 << 4)); - data.extend_from_slice(key_hash); + data.extend_from_slice(key_hash.as_ref()); let stake = StakeAddress::from_binary(&data)?.to_string()?; Ok(Some(stake)) } ShelleyAddressDelegationPart::ScriptHash(script_hash) => { let mut data = Vec::with_capacity(29); data.push(network_bit | (0b1111 << 4)); - data.extend_from_slice(script_hash); + data.extend_from_slice(script_hash.as_ref()); let stake = StakeAddress::from_binary(&data)?.to_string()?; Ok(Some(stake)) } @@ -363,7 +378,7 @@ pub struct StakeAddress { pub network: NetworkId, /// Credential - pub credential: StakeCredential, // payload: StakePayload? + pub credential: StakeCredential, } impl StakeAddress { @@ -374,7 +389,7 @@ impl StakeAddress { } } - pub fn get_hash(&self) -> &[u8] { + pub fn get_hash(&self) -> &KeyHash { match &self.credential { StakeCredential::AddrKeyHash(hash) => hash, StakeCredential::ScriptHash(hash) => hash, @@ -396,7 +411,7 @@ impl StakeAddress { }; let data = self.to_binary(); - Ok(bech32::encode::(hrp, &data)?) + Ok(bech32::encode::(hrp, &data.as_slice())?) } /// Read from a string format ("stake1xxx...") @@ -409,8 +424,14 @@ impl StakeAddress { }; let credential = match (header >> 4) & 0x0Fu8 { - 0b1110 => StakeCredential::AddrKeyHash(data[1..].to_vec()), - 0b1111 => StakeCredential::ScriptHash(data[1..].to_vec()), + 0b1110 => StakeCredential::AddrKeyHash( + data[1..].try_into().map_err(|e| anyhow!("Failed to parse key hash: {}", e))?, + ), + 0b1111 => StakeCredential::ScriptHash( + data[1..] + .try_into() + .map_err(|e| anyhow!("Failed to parse script hash: {}", e))?, + ), _ => return Err(anyhow!("Unknown header {header} in stake address")), }; @@ -430,9 +451,9 @@ impl StakeAddress { NetworkId::Testnet => 0b0u8, }; - let (stake_bits, stake_hash): (u8, &Vec) = match &self.credential { - StakeCredential::AddrKeyHash(data) => (0b1110, data), - StakeCredential::ScriptHash(data) => (0b1111, data), + let (stake_bits, stake_hash) = match &self.credential { + StakeCredential::AddrKeyHash(data) => (0b1110, data.as_ref()), + StakeCredential::ScriptHash(data) => (0b1111, data.as_ref()), }; let mut data = vec![network_bits | (stake_bits << 4)]; @@ -452,8 +473,12 @@ impl StakeAddress { }; let credential = match (data[0] >> 4) & 0x0F { - 0b1110 => StakeCredential::AddrKeyHash(data[1..].to_vec()), - 0b1111 => StakeCredential::ScriptHash(data[1..].to_vec()), + 0b1110 => StakeCredential::AddrKeyHash( + data[1..].try_into().map_err(|_| anyhow!("Invalid key hash size"))?, + ), + 0b1111 => StakeCredential::ScriptHash( + data[1..].try_into().map_err(|_| anyhow!("Invalid script hash size"))?, + ), _ => bail!("Unknown header byte {:x} in stake address", data[0]), }; @@ -466,8 +491,8 @@ impl StakeAddress { pub fn to_bytes_key(&self) -> Result> { let mut out = Vec::new(); let (bits, hash): (u8, &[u8]) = match &self.credential { - StakeCredential::AddrKeyHash(h) => (0b1110, h), - StakeCredential::ScriptHash(h) => (0b1111, h), + StakeCredential::AddrKeyHash(h) => (0b1110, h.as_slice()), + StakeCredential::ScriptHash(h) => (0b1111, h.as_slice()), }; let net_bit = match self.network { @@ -494,7 +519,8 @@ impl minicbor::Encode for StakeAddress { e: &mut minicbor::Encoder, _ctx: &mut C, ) -> Result<(), minicbor::encode::Error> { - e.bytes(&self.to_binary())?; + let data = self.to_binary(); + e.bytes(&data.as_slice())?; Ok(()) } } @@ -514,7 +540,7 @@ impl Default for StakeAddress { fn default() -> Self { StakeAddress { network: NetworkId::Mainnet, - credential: StakeCredential::AddrKeyHash(vec![0u8; 28]), + credential: StakeCredential::AddrKeyHash(KeyHash::default()), } } } @@ -640,32 +666,32 @@ mod tests { } // Standard keys from CIP-19 - fn test_payment_key_hash() -> Vec { + fn test_payment_key_hash() -> KeyHash { let payment_key = "addr_vk1w0l2sr2zgfm26ztc6nl9xy8ghsk5sh6ldwemlpmp9xylzy4dtf7st80zhd"; let (_, pubkey) = bech32::decode(payment_key).expect("Invalid Bech32 string"); // pubkey is the raw key - we need the Blake2B hash let hash = keyhash_224(&pubkey); assert_eq!(28, hash.len()); - hash + hash.as_slice().try_into().expect("Invalid hash size") } - fn test_stake_key_hash() -> Vec { + fn test_stake_key_hash() -> KeyHash { let stake_key = "stake_vk1px4j0r2fk7ux5p23shz8f3y5y2qam7s954rgf3lg5merqcj6aetsft99wu"; let (_, pubkey) = bech32::decode(stake_key).expect("Invalid Bech32 string"); // pubkey is the raw key - we need the Blake2B hash let hash = keyhash_224(&pubkey); assert_eq!(28, hash.len()); - hash + hash.as_slice().try_into().expect("Invalid hash size") } - fn test_script_hash() -> Vec { + fn test_script_hash() -> KeyHash { let script_hash = "script1cda3khwqv60360rp5m7akt50m6ttapacs8rqhn5w342z7r35m37"; let (_, hash) = bech32::decode(script_hash).expect("Invalid Bech32 string"); // This is already a hash assert_eq!(28, hash.len()); - hash + hash.as_slice().try_into().expect("Invalid hash size") } fn test_pointer() -> ShelleyAddressPointer { @@ -813,7 +839,7 @@ mod tests { fn shelley_type_14() { let address = Address::Stake(StakeAddress { network: NetworkId::Mainnet, - credential: StakeCredential::AddrKeyHash(test_stake_key_hash()), + credential: StakeCredential::AddrKeyHash(KeyHash::from(test_stake_key_hash())), }); let text = address.to_string().unwrap(); @@ -830,7 +856,7 @@ mod tests { fn shelley_type_15() { let address = Address::Stake(StakeAddress { network: NetworkId::Mainnet, - credential: StakeCredential::ScriptHash(test_script_hash()), + credential: StakeCredential::ScriptHash(KeyHash::from(test_script_hash())), }); let text = address.to_string().unwrap(); @@ -937,7 +963,7 @@ mod tests { // - 0x1d: Length of 29 bytes (the stake address data) // - [29 bytes]: The actual stake address (network header + 28-byte hash) // Total: 31 bytes (2-byte CBOR framing + 29-byte payload) - let expected = [[0x58, 0x1d].as_slice(), &binary].concat(); + let expected = [[0x58, 0x1d].as_slice(), &binary.as_slice()].concat(); let mut actual = Vec::new(); let mut encoder = minicbor::Encoder::new(&mut actual); @@ -951,7 +977,7 @@ mod tests { fn stake_addresses_decode_mainnet_stake() { let binary = { let mut v = vec![0x58, 0x1d]; - v.extend_from_slice(&mainnet_stake_address().to_binary()); + v.extend_from_slice(&mainnet_stake_address().to_binary().as_slice()); v }; diff --git a/common/src/crypto.rs b/common/src/crypto.rs index 0e66038e..f5a0d2cf 100644 --- a/common/src/crypto.rs +++ b/common/src/crypto.rs @@ -1,18 +1,22 @@ //! Common cryptography helper functions for Acropolis -use crate::types::KeyHash; +use crate::hash::Hash; use cryptoxide::hashing::blake2b::Blake2b; /// Get a Blake2b-256 hash of a key -pub fn keyhash_256(key: &[u8]) -> KeyHash { +/// +/// Returns a 32-byte hash. +pub fn keyhash_256(key: &[u8]) -> Hash<32> { let mut context = Blake2b::<256>::new(); context.update_mut(key); - context.finalize().to_vec() + Hash::new(context.finalize().into()) } /// Get a Blake2b-224 hash of a key -pub fn keyhash_224(key: &[u8]) -> KeyHash { +/// +/// Returns a 28-byte hash. +pub fn keyhash_224(key: &[u8]) -> Hash<28> { let mut context = Blake2b::<224>::new(); context.update_mut(key); - context.finalize().to_vec() + Hash::new(context.finalize().into()) } diff --git a/common/src/hash.rs b/common/src/hash.rs index de8b8efd..6a33306e 100644 --- a/common/src/hash.rs +++ b/common/src/hash.rs @@ -1,9 +1,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{fmt, ops::Deref, str::FromStr}; -/// Data that is a cryptographic hash of `BYTES` long. -/// -/// This is a generic wrapper around a fixed-size byte array.: +/// data that is a cryptographic [`struct@Hash`] of `BYTES` long. /// /// # Common Hash Sizes in Cardano /// @@ -55,6 +53,11 @@ impl Hash { pub fn into_inner(self) -> [u8; BYTES] { self.0 } + + #[inline] + pub fn as_inner(&self) -> &[u8; BYTES] { + &self.0 + } } impl From<[u8; BYTES]> for Hash { @@ -122,7 +125,6 @@ impl fmt::Debug for Hash { } impl fmt::Display for Hash { - /// Formats the hash as a lowercase hexadecimal string. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&hex::encode(self)) } diff --git a/common/src/ledger_state.rs b/common/src/ledger_state.rs index d44c1fab..648c6595 100644 --- a/common/src/ledger_state.rs +++ b/common/src/ledger_state.rs @@ -1,5 +1,5 @@ use crate::{ - KeyHash, MultiHostName, PoolRegistration, Ratio, Relay, SingleHostAddr, SingleHostName, + MultiHostName, PoolId, PoolRegistration, Ratio, Relay, SingleHostAddr, SingleHostName, }; use anyhow::{bail, Context, Result}; use minicbor::data::Tag; @@ -31,11 +31,11 @@ pub struct ParametersState {} )] pub struct SPOState { #[n(0)] - pub pools: BTreeMap, + pub pools: BTreeMap, #[n(1)] - pub updates: BTreeMap, + pub updates: BTreeMap, #[n(2)] - pub retiring: BTreeMap, + pub retiring: BTreeMap, } pub struct DRepState {} diff --git a/common/src/messages.rs b/common/src/messages.rs index 558fcfb1..25b24fea 100644 --- a/common/src/messages.rs +++ b/common/src/messages.rs @@ -179,7 +179,7 @@ pub struct EpochActivityMessage { pub total_fees: u64, /// Map of SPO IDs to blocks produced - pub spo_blocks: Vec<(KeyHash, usize)>, + pub spo_blocks: Vec<(PoolId, usize)>, /// Nonce pub nonce: Option, @@ -233,7 +233,7 @@ pub struct SPOStakeDistributionMessage { pub epoch: u64, /// SPO stake distribution by operator ID - pub spos: Vec<(KeyHash, DelegatedStake)>, + pub spos: Vec<(PoolId, DelegatedStake)>, } #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] @@ -242,7 +242,7 @@ pub struct SPORewardsMessage { pub epoch: u64, /// SPO rewards by operator ID (total rewards before distribution, pool operator's rewards) - pub spos: Vec<(KeyHash, SPORewards)>, + pub spos: Vec<(PoolId, SPORewards)>, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -272,7 +272,7 @@ pub struct SPOStateMessage { pub spos: Vec, /// SPOs in the above list which retired at the start of this epoch, by operator ID - pub retired_spos: Vec, + pub retired_spos: Vec, } /// Cardano message enum diff --git a/common/src/protocol_params.rs b/common/src/protocol_params.rs index bb970609..8cc1a5a6 100644 --- a/common/src/protocol_params.rs +++ b/common/src/protocol_params.rs @@ -2,7 +2,7 @@ use crate::{ genesis_values::GenesisValues, rational_number::{ChameleonFraction, RationalNumber}, BlockHash, BlockVersionData, Committee, Constitution, CostModel, DRepVotingThresholds, Era, - ExUnitPrices, ExUnits, GenesisDelegate, HeavyDelegate, NetworkId, PoolVotingThresholds, + ExUnitPrices, ExUnits, GenesisDelegate, HeavyDelegate, NetworkId, PoolId, PoolVotingThresholds, ProtocolConsts, }; use anyhow::{bail, Result}; @@ -34,7 +34,7 @@ pub struct ByronParams { pub start_time: u64, #[serde_as(as = "Vec<(_, _)>")] - pub heavy_delegation: HashMap, HeavyDelegate>, + pub heavy_delegation: HashMap, } // @@ -131,7 +131,7 @@ pub struct ShelleyParams { pub update_quorum: u32, #[serde_as(as = "HashMap")] - pub gen_delegs: HashMap, GenesisDelegate>, + pub gen_delegs: HashMap, } #[serde_as] diff --git a/common/src/queries/accounts.rs b/common/src/queries/accounts.rs index 4f8fa2bd..0bef0201 100644 --- a/common/src/queries/accounts.rs +++ b/common/src/queries/accounts.rs @@ -33,13 +33,13 @@ pub enum AccountsStateQuery { // Epochs-related queries GetActiveStakes {}, GetSPDDByEpoch { epoch: u64 }, - GetSPDDByEpochAndPool { epoch: u64, pool_id: KeyHash }, + GetSPDDByEpochAndPool { epoch: u64, pool_id: PoolId }, // Pools related queries GetOptimalPoolSizing, - GetPoolsLiveStakes { pools_operators: Vec> }, - GetPoolDelegators { pool_operator: KeyHash }, - GetPoolLiveStake { pool_operator: KeyHash }, + GetPoolsLiveStakes { pools_operators: Vec }, + GetPoolDelegators { pool_operator: PoolId }, + GetPoolLiveStake { pool_operator: PoolId }, // Dreps related queries GetDrepDelegators { drep: DRepChoice }, @@ -59,15 +59,15 @@ pub enum AccountsStateQueryResponse { AccountAssets(AccountAssets), AccountAssetsTotals(AccountAssetsTotals), AccountUTxOs(AccountUTxOs), - AccountsUtxoValuesMap(HashMap, u64>), + AccountsUtxoValuesMap(HashMap), AccountsUtxoValuesSum(u64), - AccountsBalancesMap(HashMap, u64>), + AccountsBalancesMap(HashMap), AccountsBalancesSum(u64), // Epochs-related responses ActiveStakes(u64), /// Vec<(PoolId, StakeKey, ActiveStakeAmount)> - SPDDByEpoch(Vec<(KeyHash, KeyHash, u64)>), + SPDDByEpoch(Vec<(PoolId, KeyHash, u64)>), /// Vec<(StakeKey, ActiveStakeAmount)> SPDDByEpochAndPool(Vec<(KeyHash, u64)>), @@ -79,7 +79,7 @@ pub enum AccountsStateQueryResponse { // DReps-related responses DrepDelegators(DrepDelegators), - AccountsDrepDelegationsMap(HashMap, Option>), + AccountsDrepDelegationsMap(HashMap>), NotFound, Error(String), @@ -89,7 +89,7 @@ pub enum AccountsStateQueryResponse { pub struct AccountInfo { pub utxo_value: u64, pub rewards: u64, - pub delegated_spo: Option, + pub delegated_spo: Option, pub delegated_drep: Option, } diff --git a/common/src/queries/epochs.rs b/common/src/queries/epochs.rs index 5ef179f2..8b3abee5 100644 --- a/common/src/queries/epochs.rs +++ b/common/src/queries/epochs.rs @@ -1,4 +1,4 @@ -use crate::{messages::EpochActivityMessage, protocol_params::ProtocolParams, KeyHash}; +use crate::{messages::EpochActivityMessage, protocol_params::ProtocolParams, PoolId}; pub const DEFAULT_EPOCHS_QUERY_TOPIC: (&str, &str) = ("epochs-state-query-topic", "cardano.query.epochs"); @@ -11,7 +11,7 @@ pub enum EpochsStateQuery { GetPreviousEpochs { epoch_number: u64 }, GetEpochStakeDistribution { epoch_number: u64 }, GetEpochStakeDistributionByPool { epoch_number: u64 }, - GetLatestEpochBlocksMintedByPool { spo_id: KeyHash }, + GetLatestEpochBlocksMintedByPool { spo_id: PoolId }, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/common/src/queries/pools.rs b/common/src/queries/pools.rs index 0cf53e78..04d9f59f 100644 --- a/common/src/queries/pools.rs +++ b/common/src/queries/pools.rs @@ -1,6 +1,6 @@ use crate::{ queries::governance::VoteRecord, rational_number::RationalNumber, KeyHash, PoolEpochState, - PoolMetadata, PoolRegistration, PoolRetirement, PoolUpdateEvent, Relay, + PoolId, PoolMetadata, PoolRegistration, PoolRetirement, PoolUpdateEvent, Relay, }; pub const DEFAULT_POOLS_QUERY_TOPIC: (&str, &str) = @@ -13,52 +13,52 @@ pub enum PoolsStateQuery { GetPoolsRetiredList, GetPoolsRetiringList, GetPoolActiveStakeInfo { - pool_operator: KeyHash, + pool_operator: PoolId, epoch: u64, }, GetPoolsActiveStakes { - pools_operators: Vec, + pools_operators: Vec, epoch: u64, }, GetPoolsTotalBlocksMinted { - pools_operators: Vec, + pools_operators: Vec, }, GetPoolInfo { - pool_id: KeyHash, + pool_id: PoolId, }, GetPoolHistory { - pool_id: KeyHash, + pool_id: PoolId, }, GetPoolMetadata { - pool_id: KeyHash, + pool_id: PoolId, }, GetPoolRelays { - pool_id: KeyHash, + pool_id: PoolId, }, GetPoolDelegators { - pool_id: KeyHash, + pool_id: PoolId, }, GetPoolTotalBlocksMinted { - pool_id: KeyHash, + pool_id: PoolId, }, GetBlocksByPool { - pool_id: KeyHash, + pool_id: PoolId, }, GetBlocksByPoolAndEpoch { - pool_id: KeyHash, + pool_id: PoolId, epoch: u64, }, GetPoolUpdates { - pool_id: KeyHash, + pool_id: PoolId, }, GetPoolVotes { - pool_id: KeyHash, + pool_id: PoolId, }, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum PoolsStateQueryResponse { - PoolsList(Vec), + PoolsList(Vec), PoolsListWithInfo(PoolsListWithInfo), PoolsRetiredList(Vec), PoolsRetiringList(Vec), @@ -83,7 +83,7 @@ pub enum PoolsStateQueryResponse { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PoolsListWithInfo { - pub pools: Vec<(KeyHash, PoolRegistration)>, + pub pools: Vec<(PoolId, PoolRegistration)>, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/common/src/serialization.rs b/common/src/serialization.rs index 8bbd57bf..709cfdf6 100644 --- a/common/src/serialization.rs +++ b/common/src/serialization.rs @@ -1,5 +1,6 @@ use std::marker::PhantomData; +use crate::PoolId; use anyhow::anyhow; use bech32::{Bech32, Hrp}; use serde::{ser::SerializeMap, Deserialize, Serializer}; @@ -60,7 +61,29 @@ impl HrpPrefix for AddrPrefix { // Generic Bech32 converter with HRP parameter pub struct DisplayFromBech32(PhantomData); -// Serialization implementation +// PoolID serialization implementation +impl SerializeAs for DisplayFromBech32 { + fn serialize_as(source: &PoolId, serializer: S) -> Result + where + S: Serializer, + { + let bech32_string = source.to_bech32().map_err(serde::ser::Error::custom)?; + serializer.serialize_str(&bech32_string) + } +} + +// PoolID deserialization implementation +impl<'de> DeserializeAs<'de, PoolId> for DisplayFromBech32 { + fn deserialize_as(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + PoolId::from_bech32(&s).map_err(serde::de::Error::custom) + } +} + +// Vec serialization implementation impl SerializeAs> for DisplayFromBech32 where PREFIX: HrpPrefix, diff --git a/common/src/snapshot/pool_params.rs b/common/src/snapshot/pool_params.rs index 49d5a312..ac85acca 100644 --- a/common/src/snapshot/pool_params.rs +++ b/common/src/snapshot/pool_params.rs @@ -13,14 +13,15 @@ // limitations under the License. use super::streaming_snapshot::{ - cbor, Coin, Nullable, PoolId, PoolMetadata, Relay, RewardAccount, Set, UnitInterval, VrfKeyhash, + cbor, Coin, Nullable, PoolMetadata, Relay, RewardAccount, Set, UnitInterval, }; -use crate::AddrKeyhash; +use crate::types::AddrKeyhash; +use crate::{PoolId, VrfKeyHash}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct PoolParams { pub id: PoolId, - pub vrf: VrfKeyhash, + pub vrf: VrfKeyHash, pub pledge: Coin, pub cost: Coin, pub margin: UnitInterval, diff --git a/common/src/snapshot/streaming_snapshot.rs b/common/src/snapshot/streaming_snapshot.rs index 02e3d792..673acb12 100644 --- a/common/src/snapshot/streaming_snapshot.rs +++ b/common/src/snapshot/streaming_snapshot.rs @@ -66,13 +66,21 @@ impl<'b, C> minicbor::decode::Decode<'b, C> for StakeCredential { 0 => { // ScriptHash variant (first in enum) - decode bytes directly let bytes = d.bytes()?; - let key_hash = bytes.to_vec(); + let key_hash = bytes.try_into().map_err(|_| { + minicbor::decode::Error::message( + "invalid length for ScriptHash in StakeCredential", + ) + })?; Ok(StakeCredential::ScriptHash(key_hash)) } 1 => { - // AddrKeyHash variant (second in enum) - decode bytes directly + // AddrKeyHash variant (second in enum) - decodes bytes directly let bytes = d.bytes()?; - let key_hash = bytes.to_vec(); + let key_hash = bytes.try_into().map_err(|_| { + minicbor::decode::Error::message( + "invalid length for AddrKeyHash in StakeCredential", + ) + })?; Ok(StakeCredential::AddrKeyHash(key_hash)) } _ => Err(minicbor::decode::Error::message( @@ -299,21 +307,15 @@ impl<'b, C> minicbor::Decode<'b, C> for Account { // Type aliases for pool_params compatibility // ----------------------------------------------------------------------------- +pub use crate::types::AddrKeyhash; +pub use crate::types::ScriptHash; +use crate::PoolId; /// Alias minicbor as cbor for pool_params module pub use minicbor as cbor; /// Coin amount (Lovelace) pub type Coin = u64; -/// Pool ID (28-byte hash) -pub type PoolId = Hash<28>; - -pub type AddrKeyhash = Hash<28>; -pub type ScriptHash = Hash<28>; - -/// VRF key hash (32-byte hash) -pub type VrfKeyhash = Hash<32>; - /// Reward account (stake address bytes) - wrapper to handle CBOR bytes encoding #[derive(Debug, Clone, PartialEq, Eq)] pub struct RewardAccount(pub Vec); @@ -1079,15 +1081,15 @@ impl StreamingSnapshotParser { // Convert SPO delegation from StrictMaybe to Option // PoolId is Hash<28>, we need to convert to Vec let delegated_spo = match &account.pool { - StrictMaybe::Just(pool_id) => Some(pool_id.as_ref().to_vec()), + StrictMaybe::Just(pool_id) => Some(*pool_id), StrictMaybe::Nothing => None, }; // Convert DRep delegation from StrictMaybe to Option let delegated_drep = match &account.drep { StrictMaybe::Just(drep) => Some(match drep { - DRep::Key(hash) => crate::DRepChoice::Key(hash.as_ref().to_vec()), - DRep::Script(hash) => crate::DRepChoice::Script(hash.as_ref().to_vec()), + DRep::Key(hash) => crate::DRepChoice::Key(*hash), + DRep::Script(hash) => crate::DRepChoice::Script(*hash), DRep::Abstain => crate::DRepChoice::Abstain, DRep::NoConfidence => crate::DRepChoice::NoConfidence, }), @@ -1445,15 +1447,15 @@ impl StreamingSnapshotParser { // Convert SPO delegation from StrictMaybe to Option let delegated_spo = match &account.pool { - StrictMaybe::Just(pool_id) => Some(pool_id.as_ref().to_vec()), + StrictMaybe::Just(pool_id) => Some(*pool_id), StrictMaybe::Nothing => None, }; // Convert DRep delegation from StrictMaybe to Option let delegated_drep = match &account.drep { StrictMaybe::Just(drep) => Some(match drep { - DRep::Key(hash) => crate::DRepChoice::Key(hash.as_ref().to_vec()), - DRep::Script(hash) => crate::DRepChoice::Script(hash.as_ref().to_vec()), + DRep::Key(hash) => crate::DRepChoice::Key(*hash), + DRep::Script(hash) => crate::DRepChoice::Script(*hash), DRep::Abstain => crate::DRepChoice::Abstain, DRep::NoConfidence => crate::DRepChoice::NoConfidence, }), @@ -2006,7 +2008,7 @@ impl StreamingSnapshotParser { cost: params.cost, margin: (params.margin.numerator as f64) / (params.margin.denominator as f64), reward_account: hex::encode(¶ms.reward_account.0), - pool_owners: params.owners.iter().map(|h| hex::encode(h)).collect(), + pool_owners: params.owners.iter().map(|h| h.to_string()).collect(), relays, pool_metadata, retirement_epoch, diff --git a/common/src/stake_addresses.rs b/common/src/stake_addresses.rs index fff09af9..7eaeb44f 100644 --- a/common/src/stake_addresses.rs +++ b/common/src/stake_addresses.rs @@ -1,20 +1,19 @@ -use std::{ - collections::{ - hash_map::{Entry, Iter, Values}, - BTreeMap, HashMap, - }, - sync::atomic::AtomicU64, -}; - use crate::{ math::update_value_with_delta, messages::DRepDelegationDistribution, DRepChoice, - DRepCredential, DelegatedStake, KeyHash, Lovelace, PoolLiveStakeInfo, StakeAddress, + DRepCredential, DelegatedStake, KeyHash, Lovelace, PoolId, PoolLiveStakeInfo, StakeAddress, StakeAddressDelta, Withdrawal, }; use anyhow::Result; use dashmap::DashMap; use rayon::prelude::*; use serde_with::{hex::Hex, serde_as}; +use std::{ + collections::{ + hash_map::{Entry, Iter, Values}, + BTreeMap, HashMap, + }, + sync::atomic::AtomicU64, +}; use tracing::{error, warn}; /// State of an individual stake address @@ -32,7 +31,7 @@ pub struct StakeAddressState { /// SPO ID they are delegated to ("operator" ID) #[serde_as(as = "Option")] - pub delegated_spo: Option, + pub delegated_spo: Option, /// DRep they are delegated to pub delegated_drep: Option, @@ -116,7 +115,7 @@ impl StakeAddressMap { } /// Get Pool's Live Stake Info - pub fn get_pool_live_stake_info(&self, spo: &KeyHash) -> PoolLiveStakeInfo { + pub fn get_pool_live_stake_info(&self, spo: &PoolId) -> PoolLiveStakeInfo { let total_live_stakes = AtomicU64::new(0); let live_stake = AtomicU64::new(0); let live_delegators = AtomicU64::new(0); @@ -144,11 +143,11 @@ impl StakeAddressMap { } /// Get Pool's Live Stake (same order as spos) - pub fn get_pools_live_stakes(&self, spos: &[KeyHash]) -> Vec { - let mut live_stakes_map = HashMap::::new(); + pub fn get_pools_live_stakes(&self, spos: &[PoolId]) -> Vec { + let mut live_stakes_map = HashMap::::new(); // Collect the SPO keys and UTXO - let sas_data: Vec<(KeyHash, u64)> = self + let sas_data: Vec<(PoolId, u64)> = self .inner .values() .filter_map(|sas| sas.delegated_spo.as_ref().map(|spo| (spo.clone(), sas.utxo_value))) @@ -167,15 +166,15 @@ impl StakeAddressMap { } /// Get Pool Delegators with live_stakes - pub fn get_pool_delegators(&self, pool_operator: &KeyHash) -> Vec<(KeyHash, u64)> { + pub fn get_pool_delegators(&self, pool_operator: &PoolId) -> Vec<(KeyHash, u64)> { // Find stake addresses delegated to pool_operator let delegators: Vec<(KeyHash, u64)> = self .inner .iter() - .filter_map(|(stake_key, sas)| match sas.delegated_spo.as_ref() { + .filter_map(|(stake_address, sas)| match sas.delegated_spo.as_ref() { Some(delegated_spo) => { if delegated_spo.eq(pool_operator) { - Some((stake_key.to_binary().clone(), sas.utxo_value + sas.rewards)) + Some((*stake_address.get_hash(), sas.utxo_value + sas.rewards)) } else { None } @@ -196,7 +195,7 @@ impl StakeAddressMap { .filter_map(|(stake_address, sas)| match sas.delegated_drep.as_ref() { Some(delegated_drep) => { if delegated_drep.eq(drep) { - Some((stake_address.to_binary(), sas.utxo_value)) + Some((*stake_address.get_hash(), sas.utxo_value)) } else { None } @@ -213,13 +212,14 @@ impl StakeAddressMap { pub fn get_accounts_utxo_values_map( &self, stake_addresses: &[StakeAddress], - ) -> Option, u64>> { + ) -> Option> { let mut map = HashMap::new(); for stake_address in stake_addresses { let account = self.get(stake_address)?; let utxo_value = account.utxo_value; - map.insert(stake_address.to_binary().clone(), utxo_value); + let key_hash = stake_address.get_hash(); + map.insert(*key_hash, utxo_value); } Some(map) @@ -230,13 +230,14 @@ impl StakeAddressMap { pub fn get_accounts_balances_map( &self, stake_addresses: &[StakeAddress], - ) -> Option, u64>> { + ) -> Option> { let mut map = HashMap::new(); for stake_address in stake_addresses { let account = self.get(stake_address)?; let balance = account.utxo_value + account.rewards; - map.insert(stake_address.to_binary().clone(), balance); + let key_hash = stake_address.get_hash(); + map.insert(*key_hash, balance); } Some(map) @@ -247,13 +248,14 @@ impl StakeAddressMap { pub fn get_drep_delegations_map( &self, stake_addresses: &[StakeAddress], - ) -> Option, Option>> { + ) -> Option>> { let mut map = HashMap::new(); for stake_address in stake_addresses { let account = self.get(stake_address)?; let maybe_drep = account.delegated_drep.clone(); - map.insert(stake_address.to_binary().clone(), maybe_drep); + let key_hash = stake_address.get_hash(); + map.insert(*key_hash, maybe_drep); } Some(map) @@ -284,15 +286,15 @@ impl StakeAddressMap { /// Derive the Stake Pool Delegation Distribution (SPDD) - a map of total stake values /// (both with and without rewards) for each active SPO /// And Stake Pool Reward State (rewards and delegators_count for each pool) - /// DelegatedStake>;Key of returned map is the SPO 'operator' ID - pub fn generate_spdd(&self) -> BTreeMap { + /// DelegatedStake>;Key of returned map is the SPO 'operator' ID + pub fn generate_spdd(&self) -> BTreeMap { // Shareable Dashmap with referenced keys - let spo_stakes = DashMap::::new(); + let spo_stakes = DashMap::::new(); // Total stake across all addresses in parallel, first collecting into a vector // because imbl::OrdMap doesn't work in Rayon // Collect the SPO keys and UTXO, reward values - let sas_data: Vec<(KeyHash, (u64, u64))> = self + let sas_data: Vec<(PoolId, (u64, u64))> = self .inner .values() .filter_map(|sas| { @@ -324,7 +326,7 @@ impl StakeAddressMap { /// Dump current Stake Pool Delegation Distribution State /// (Stake Key, Active Stakes Amount)> - pub fn dump_spdd_state(&self) -> HashMap> { + pub fn dump_spdd_state(&self) -> HashMap> { let entries: Vec<_> = self .inner .par_iter() @@ -333,7 +335,7 @@ impl StakeAddressMap { }) .collect(); - let mut result: HashMap> = HashMap::new(); + let mut result: HashMap> = HashMap::new(); for (spo, entry) in entries { result.entry(spo).or_default().push((entry.0.get_credential().get_hash(), entry.1)); } @@ -431,7 +433,7 @@ impl StakeAddressMap { } /// Record a stake delegation - pub fn record_stake_delegation(&mut self, stake_address: &StakeAddress, spo: &KeyHash) -> bool { + pub fn record_stake_delegation(&mut self, stake_address: &StakeAddress, spo: &PoolId) -> bool { if let Some(sas) = self.get_mut(stake_address) { if sas.registered { sas.delegated_spo = Some(spo.clone()); @@ -551,23 +553,20 @@ impl StakeAddressMap { #[cfg(test)] mod tests { - use crate::{NetworkId, StakeAddress, StakeCredential}; - use super::*; + use crate::hash::Hash; + use crate::{NetworkId, StakeAddress, StakeCredential}; - const STAKE_KEY_HASH: [u8; 28] = [0x99; 28]; - const STAKE_KEY_HASH_2: [u8; 28] = [0xaa; 28]; - const STAKE_KEY_HASH_3: [u8; 28] = [0xbb; 28]; + const STAKE_KEY_HASH: KeyHash = KeyHash::new([0x99; 28]); + const STAKE_KEY_HASH_2: KeyHash = KeyHash::new([0xaa; 28]); + const STAKE_KEY_HASH_3: KeyHash = KeyHash::new([0xbb; 28]); - const SPO_HASH: [u8; 28] = [0x01; 28]; - const SPO_HASH_2: [u8; 28] = [0x02; 28]; - const DREP_HASH: [u8; 28] = [0xca; 28]; + const SPO_HASH: PoolId = PoolId::new(Hash::new([0xbb_u8; 28])); + const SPO_HASH_2: PoolId = PoolId::new(Hash::new([0x02_u8; 28])); + const DREP_HASH: KeyHash = KeyHash::new([0xca; 28]); - fn create_stake_address(hash: &[u8]) -> StakeAddress { - StakeAddress::new( - StakeCredential::AddrKeyHash(hash.to_vec()), - NetworkId::Mainnet, - ) + fn create_stake_address(hash: KeyHash) -> StakeAddress { + StakeAddress::new(StakeCredential::AddrKeyHash(hash), NetworkId::Mainnet) } mod registration_tests { @@ -576,7 +575,7 @@ mod tests { #[test] fn test_register_success() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(stake_addresses.register_stake_address(&stake_address)); assert_eq!(stake_addresses.len(), 1); @@ -586,7 +585,7 @@ mod tests { #[test] fn test_double_registration_fails() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(stake_addresses.register_stake_address(&stake_address)); assert!(!stake_addresses.register_stake_address(&stake_address)); @@ -596,7 +595,7 @@ mod tests { #[test] fn test_deregister_success() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); assert!(stake_addresses.deregister_stake_address(&stake_address)); @@ -606,7 +605,7 @@ mod tests { #[test] fn test_deregister_unregistered_fails() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); // Create an entry but don't register stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -620,7 +619,7 @@ mod tests { #[test] fn test_deregister_unknown_fails() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(!stake_addresses.deregister_stake_address(&stake_address)); } @@ -628,14 +627,14 @@ mod tests { #[test] fn test_stake_address_lifecycle() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); // Register assert!(stake_addresses.register_stake_address(&stake_address)); // Delegate - stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH.to_vec()); - let drep_choice = DRepChoice::Key(DREP_HASH.to_vec()); + stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH); + let drep_choice = DRepChoice::Key(DREP_HASH); stake_addresses.record_drep_delegation(&stake_address, &drep_choice); // Deregister @@ -650,23 +649,23 @@ mod tests { #[test] fn test_spo_delegation_success() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); - assert!(stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH.to_vec())); + assert!(stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH)); assert_eq!( stake_addresses.get(&stake_address).unwrap().delegated_spo, - Some(SPO_HASH.to_vec()) + Some(SPO_HASH) ); } #[test] fn test_drep_delegation_success() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); - let drep_choice = DRepChoice::Key(DREP_HASH.to_vec()); + let drep_choice = DRepChoice::Key(DREP_HASH); assert!(stake_addresses.record_drep_delegation(&stake_address, &drep_choice)); assert_eq!( stake_addresses.get(&stake_address).unwrap().delegated_drep, @@ -677,12 +676,12 @@ mod tests { #[test] fn test_delegation_requires_registration() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); // Test unknown address - assert!(!stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH.to_vec())); + assert!(!stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH)); assert!(!stake_addresses - .record_drep_delegation(&stake_address, &DRepChoice::Key(DREP_HASH.to_vec()))); + .record_drep_delegation(&stake_address, &DRepChoice::Key(DREP_HASH))); // Create an unregistered entry with UTXO value stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -691,30 +690,30 @@ mod tests { }); // Delegation should still fail for unregistered address - assert!(!stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH.to_vec())); + assert!(!stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH)); assert!(!stake_addresses - .record_drep_delegation(&stake_address, &DRepChoice::Key(DREP_HASH.to_vec()))); + .record_drep_delegation(&stake_address, &DRepChoice::Key(DREP_HASH))); } #[test] fn test_re_delegation() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); // First SPO delegation - stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH.to_vec()); + stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH); assert_eq!( stake_addresses.get(&stake_address).unwrap().delegated_spo, - Some(SPO_HASH.to_vec()) + Some(SPO_HASH) ); // Re-delegate to different pool - stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH_2.to_vec()); + stake_addresses.record_stake_delegation(&stake_address, &SPO_HASH_2); assert_eq!( stake_addresses.get(&stake_address).unwrap().delegated_spo, - Some(SPO_HASH_2.to_vec()) + Some(SPO_HASH_2) ); // First DRep delegation @@ -739,7 +738,7 @@ mod tests { #[test] fn test_positive_delta_accumulates() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); @@ -757,7 +756,7 @@ mod tests { #[test] fn test_negative_delta_reduces() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); @@ -777,7 +776,7 @@ mod tests { #[test] fn test_negative_delta_underflow_prevented() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); @@ -803,7 +802,7 @@ mod tests { #[test] fn test_utxo_and_rewards_tracked_independently() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); @@ -821,7 +820,7 @@ mod tests { #[test] fn test_add_to_reward() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); stake_addresses.add_to_reward(&stake_address, 100); @@ -834,7 +833,7 @@ mod tests { #[test] fn test_update_reward_positive_delta() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(stake_addresses.update_reward(&stake_address, 100).is_ok()); assert_eq!(stake_addresses.get(&stake_address).unwrap().rewards, 100); @@ -843,7 +842,7 @@ mod tests { #[test] fn test_update_reward_negative_delta() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.update_reward(&stake_address, 100).unwrap(); assert!(stake_addresses.update_reward(&stake_address, -50).is_ok()); @@ -853,7 +852,7 @@ mod tests { #[test] fn test_update_reward_underflow() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.update_reward(&stake_address, 50).unwrap(); @@ -865,7 +864,7 @@ mod tests { #[test] fn test_update_reward_creates_entry_if_missing() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(stake_addresses.update_reward(&stake_address, 100).is_ok()); assert_eq!(stake_addresses.len(), 1); @@ -881,7 +880,7 @@ mod tests { #[test] fn test_withdrawal_success() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); stake_addresses.add_to_reward(&stake_address, 100); @@ -899,7 +898,7 @@ mod tests { #[test] fn test_withdrawal_prevents_underflow() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); stake_addresses.add_to_reward(&stake_address, 12); @@ -926,7 +925,7 @@ mod tests { #[test] fn test_zero_withdrawal() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&stake_address); stake_addresses.add_to_reward(&stake_address, 100); @@ -944,7 +943,7 @@ mod tests { #[test] fn test_withdrawal_unknown_address() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); let withdrawal = Withdrawal { address: stake_address.clone(), @@ -963,7 +962,7 @@ mod tests { #[test] fn test_update_utxo_value_positive_delta() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(stake_addresses.update_utxo_value(&stake_address, 500).is_ok()); assert_eq!(stake_addresses.get(&stake_address).unwrap().utxo_value, 500); @@ -972,7 +971,7 @@ mod tests { #[test] fn test_update_utxo_value_negative_delta() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.update_utxo_value(&stake_address, 500).unwrap(); assert!(stake_addresses.update_utxo_value(&stake_address, -200).is_ok()); @@ -982,7 +981,7 @@ mod tests { #[test] fn test_update_utxo_value_underflow() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); stake_addresses.update_utxo_value(&stake_address, 100).unwrap(); @@ -994,7 +993,7 @@ mod tests { #[test] fn test_update_utxo_value_creates_entry_if_missing() { let mut stake_addresses = StakeAddressMap::new(); - let stake_address = create_stake_address(&STAKE_KEY_HASH); + let stake_address = create_stake_address(STAKE_KEY_HASH); assert!(stake_addresses.update_utxo_value(&stake_address, 500).is_ok()); assert_eq!(stake_addresses.len(), 1); @@ -1009,13 +1008,13 @@ mod tests { fn test_generate_spdd_single_pool() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); - stake_addresses.record_stake_delegation(&addr1, &SPO_HASH.to_vec()); - stake_addresses.record_stake_delegation(&addr2, &SPO_HASH.to_vec()); + stake_addresses.record_stake_delegation(&addr1, &SPO_HASH); + stake_addresses.record_stake_delegation(&addr2, &SPO_HASH); stake_addresses.process_stake_delta(&StakeAddressDelta { address: addr1.clone(), @@ -1031,7 +1030,7 @@ mod tests { let spdd = stake_addresses.generate_spdd(); - let pool_stake = spdd.get(SPO_HASH.as_slice()).unwrap(); + let pool_stake = spdd.get(&SPO_HASH).unwrap(); assert_eq!(pool_stake.active, 3000); // utxo only assert_eq!(pool_stake.live, 3150); // utxo + rewards assert_eq!(pool_stake.active_delegators_count, 2); @@ -1041,13 +1040,13 @@ mod tests { fn test_generate_spdd_multiple_pools() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); - stake_addresses.record_stake_delegation(&addr1, &SPO_HASH.to_vec()); - stake_addresses.record_stake_delegation(&addr2, &SPO_HASH_2.to_vec()); + stake_addresses.record_stake_delegation(&addr1, &SPO_HASH); + stake_addresses.record_stake_delegation(&addr2, &SPO_HASH_2); stake_addresses.process_stake_delta(&StakeAddressDelta { address: addr1.clone(), @@ -1061,14 +1060,14 @@ mod tests { let spdd = stake_addresses.generate_spdd(); assert_eq!(spdd.len(), 2); - assert_eq!(spdd.get(SPO_HASH.as_slice()).unwrap().active, 1000); - assert_eq!(spdd.get(SPO_HASH_2.as_slice()).unwrap().active, 2000); + assert_eq!(spdd.get(&SPO_HASH).unwrap().active, 1000); + assert_eq!(spdd.get(&SPO_HASH_2).unwrap().active, 2000); } #[test] fn test_generate_spdd_no_delegations() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); + let addr1 = create_stake_address(STAKE_KEY_HASH); stake_addresses.register_stake_address(&addr1); stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -1084,9 +1083,9 @@ mod tests { fn test_generate_drdd_with_special_choices() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); - let addr3 = create_stake_address(&STAKE_KEY_HASH_3); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); + let addr3 = create_stake_address(STAKE_KEY_HASH_3); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); @@ -1094,7 +1093,7 @@ mod tests { stake_addresses.record_drep_delegation(&addr1, &DRepChoice::Abstain); stake_addresses.record_drep_delegation(&addr2, &DRepChoice::NoConfidence); - stake_addresses.record_drep_delegation(&addr3, &DRepChoice::Key(DREP_HASH.to_vec())); + stake_addresses.record_drep_delegation(&addr3, &DRepChoice::Key(DREP_HASH)); stake_addresses.process_stake_delta(&StakeAddressDelta { address: addr1.clone(), @@ -1114,13 +1113,13 @@ mod tests { }); stake_addresses.add_to_reward(&addr3, 150); - let dreps = vec![(DRepCredential::AddrKeyHash(DREP_HASH.to_vec()), 500)]; + let dreps = vec![(DRepCredential::AddrKeyHash(DREP_HASH), 500)]; let drdd = stake_addresses.generate_drdd(&dreps); assert_eq!(drdd.abstain, 1050); // 1000 + 50 assert_eq!(drdd.no_confidence, 2100); // 2000 + 100 - let drep_cred = DRepCredential::AddrKeyHash(DREP_HASH.to_vec()); + let drep_cred = DRepCredential::AddrKeyHash(DREP_HASH); let drep_stake = drdd .dreps .iter() @@ -1139,13 +1138,13 @@ mod tests { fn test_get_pool_live_stake_info() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); - stake_addresses.record_stake_delegation(&addr1, &SPO_HASH.to_vec()); - stake_addresses.record_stake_delegation(&addr2, &SPO_HASH_2.to_vec()); + stake_addresses.record_stake_delegation(&addr1, &SPO_HASH); + stake_addresses.record_stake_delegation(&addr2, &SPO_HASH_2); stake_addresses.process_stake_delta(&StakeAddressDelta { address: addr1.clone(), @@ -1159,7 +1158,7 @@ mod tests { }); stake_addresses.add_to_reward(&addr2, 100); - let info = stake_addresses.get_pool_live_stake_info(&SPO_HASH.to_vec()); + let info = stake_addresses.get_pool_live_stake_info(&SPO_HASH); assert_eq!(info.live_stake, 1050); // utxo + rewards for pool 1 assert_eq!(info.live_delegators, 1); @@ -1170,13 +1169,13 @@ mod tests { fn test_get_pools_live_stakes() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); - stake_addresses.record_stake_delegation(&addr1, &SPO_HASH.to_vec()); - stake_addresses.record_stake_delegation(&addr2, &SPO_HASH_2.to_vec()); + stake_addresses.record_stake_delegation(&addr1, &SPO_HASH); + stake_addresses.record_stake_delegation(&addr2, &SPO_HASH_2); stake_addresses.process_stake_delta(&StakeAddressDelta { address: addr1.clone(), @@ -1187,7 +1186,7 @@ mod tests { delta: 2000, }); - let pools = vec![SPO_HASH.to_vec(), SPO_HASH_2.to_vec()]; + let pools = vec![SPO_HASH, SPO_HASH_2]; let stakes = stake_addresses.get_pools_live_stakes(&pools); assert_eq!(stakes, vec![1000, 2000]); @@ -1197,13 +1196,13 @@ mod tests { fn test_get_pool_delegators() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); - stake_addresses.record_stake_delegation(&addr1, &SPO_HASH.to_vec()); - stake_addresses.record_stake_delegation(&addr2, &SPO_HASH.to_vec()); + stake_addresses.record_stake_delegation(&addr1, &SPO_HASH); + stake_addresses.record_stake_delegation(&addr2, &SPO_HASH); stake_addresses.process_stake_delta(&StakeAddressDelta { address: addr1.clone(), @@ -1216,7 +1215,7 @@ mod tests { delta: 2000, }); - let delegators = stake_addresses.get_pool_delegators(&SPO_HASH.to_vec()); + let delegators = stake_addresses.get_pool_delegators(&SPO_HASH); assert_eq!(delegators.len(), 2); assert!(delegators.iter().any(|(_, stake)| *stake == 1050)); @@ -1231,8 +1230,8 @@ mod tests { fn test_get_accounts_utxo_values_map_success() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); @@ -1252,15 +1251,15 @@ mod tests { let map = stake_addresses.get_accounts_utxo_values_map(&keys).unwrap(); assert_eq!(map.len(), 2); - assert_eq!(map.get(&addr1.to_binary()).copied().unwrap(), 1000); - assert_eq!(map.get(&addr2.to_binary()).copied().unwrap(), 2000); + assert_eq!(map.get(&addr1.get_hash()).copied().unwrap(), 1000); + assert_eq!(map.get(&addr2.get_hash()).copied().unwrap(), 2000); } #[test] fn test_get_accounts_utxo_values_map_missing_account() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -1287,8 +1286,8 @@ mod tests { fn test_get_accounts_utxo_values_sum_success() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); @@ -1313,8 +1312,8 @@ mod tests { #[test] fn test_get_accounts_utxo_values_sum_missing_account() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -1345,8 +1344,8 @@ mod tests { fn test_get_accounts_balances_map_includes_rewards() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); @@ -1366,15 +1365,15 @@ mod tests { let map = stake_addresses.get_accounts_balances_map(&addresses).unwrap(); assert_eq!(map.len(), 2); - assert_eq!(map.get(&addr1.to_binary()).copied().unwrap(), 1100); - assert_eq!(map.get(&addr2.to_binary()).copied().unwrap(), 2000); + assert_eq!(map.get(&addr1.get_hash()).copied().unwrap(), 1100); + assert_eq!(map.get(&addr2.get_hash()).copied().unwrap(), 2000); } #[test] fn test_get_accounts_balances_map_missing_account() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -1401,8 +1400,8 @@ mod tests { fn test_get_account_balances_sum_includes_rewards() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); @@ -1427,8 +1426,8 @@ mod tests { #[test] fn test_get_account_balances_sum_missing_account() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.process_stake_delta(&StakeAddressDelta { @@ -1459,37 +1458,37 @@ mod tests { fn test_get_drep_delegations_map_various_choices() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); - let addr3 = create_stake_address(&STAKE_KEY_HASH_3); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); + let addr3 = create_stake_address(STAKE_KEY_HASH_3); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); stake_addresses.register_stake_address(&addr3); stake_addresses.record_drep_delegation(&addr1, &DRepChoice::Abstain); - stake_addresses.record_drep_delegation(&addr2, &DRepChoice::Key(DREP_HASH.to_vec())); + stake_addresses.record_drep_delegation(&addr2, &DRepChoice::Key(DREP_HASH)); let addresses = vec![addr1.clone(), addr2.clone(), addr3.clone()]; let map = stake_addresses.get_drep_delegations_map(&addresses).unwrap(); assert_eq!(map.len(), 3); assert_eq!( - map.get(&addr1.to_binary()).unwrap(), + map.get(&addr1.get_hash()).unwrap(), &Some(DRepChoice::Abstain) ); assert_eq!( - map.get(&addr2.to_binary()).unwrap(), - &Some(DRepChoice::Key(DREP_HASH.to_vec())) + map.get(&addr2.get_hash()).unwrap(), + &Some(DRepChoice::Key(DREP_HASH)) ); - assert_eq!(map.get(&addr3.to_binary()).unwrap(), &None); + assert_eq!(map.get(&addr3.get_hash()).unwrap(), &None); } #[test] fn test_get_drep_delegations_map_missing_account() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); stake_addresses.register_stake_address(&addr1); stake_addresses.record_drep_delegation(&addr1, &DRepChoice::NoConfidence); @@ -1513,15 +1512,15 @@ mod tests { fn test_get_drep_delegators() { let mut stake_addresses = StakeAddressMap::new(); - let addr1 = create_stake_address(&STAKE_KEY_HASH); - let addr2 = create_stake_address(&STAKE_KEY_HASH_2); - let addr3 = create_stake_address(&STAKE_KEY_HASH_3); + let addr1 = create_stake_address(STAKE_KEY_HASH); + let addr2 = create_stake_address(STAKE_KEY_HASH_2); + let addr3 = create_stake_address(STAKE_KEY_HASH_3); stake_addresses.register_stake_address(&addr1); stake_addresses.register_stake_address(&addr2); stake_addresses.register_stake_address(&addr3); - let drep_choice = DRepChoice::Key(DREP_HASH.to_vec()); + let drep_choice = DRepChoice::Key(DREP_HASH); stake_addresses.record_drep_delegation(&addr1, &drep_choice); stake_addresses.record_drep_delegation(&addr2, &drep_choice); stake_addresses.record_drep_delegation(&addr3, &DRepChoice::Abstain); diff --git a/common/src/types.rs b/common/src/types.rs index e530c97e..0de8c9fc 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -3,6 +3,7 @@ #![allow(dead_code)] use crate::hash::Hash; +use crate::serialization::Bech32Conversion; use crate::{ address::{Address, ShelleyAddress, StakeAddress}, declare_hash_type, declare_hash_type_with_bech32, protocol_params, @@ -470,24 +471,27 @@ impl Default for UTXODelta { } } -/// Key hash used for pool IDs etc. -pub type KeyHash = Vec; +/// Key hash +pub type KeyHash = Hash<28>; -pub type PoolId = Vec; - -/// Script identifier +/// Script hash pub type ScriptHash = KeyHash; /// Address key hash pub type AddrKeyhash = KeyHash; -pub type GenesisKeyhash = Vec; +/// Script identifier +pub type GenesisKeyhash = Hash<28>; declare_hash_type!(BlockHash, 32); declare_hash_type!(TxHash, 32); declare_hash_type_with_bech32!(VrfKeyHash, 32, "vrf_vk"); -declare_hash_type_with_bech32!(DrepKey, 28, "drep"); -declare_hash_type_with_bech32!(DrepScriptKey, 28, "drep_script"); +declare_hash_type_with_bech32!(PoolId, 28, "pool"); + +declare_hash_type_with_bech32!(ConstitutionalCommitteeKeyHash, 28, "cc_hot"); +declare_hash_type_with_bech32!(ConstitutionalCommitteeScriptHash, 28, "cc_hot_script"); +declare_hash_type_with_bech32!(DrepKeyHash, 28, "drep"); +declare_hash_type_with_bech32!(DRepScriptHash, 28, "drep_script"); /// Data hash used for metadata, anchors (SHA256) pub type DataHash = Vec; @@ -676,10 +680,10 @@ pub struct PotDelta { )] pub enum Credential { /// Script hash. NOTE: Order matters when parsing Haskell Node Snapshot data. - ScriptHash(#[serde_as(as = "Hex")] KeyHash), + ScriptHash(#[serde_as(as = "Hex")] ScriptHash), /// Address key hash - AddrKeyHash(#[serde_as(as = "Hex")] KeyHash), + AddrKeyHash(#[serde_as(as = "Hex")] AddrKeyhash), } impl Credential { @@ -691,7 +695,10 @@ impl Credential { hex_str )) } else { - Ok(key_hash) + key_hash + .as_slice() + .try_into() + .map_err(|e| anyhow!("Failed to convert to KeyHash {}", e)) } } @@ -723,7 +730,7 @@ impl Credential { .clone() } - pub fn from_drep_bech32(bech32_str: &str) -> Result { + pub fn from_drep_bech32(bech32_str: &str) -> Result { let (hrp, data) = bech32::decode(bech32_str)?; if data.len() != 28 { return Err(anyhow!( @@ -732,7 +739,7 @@ impl Credential { )); } - let hash: KeyHash = data; + let hash = data.try_into().expect("failed to convert to fixed-size array"); match hrp.as_str() { "drep" => Ok(Credential::AddrKeyHash(hash)), @@ -771,7 +778,7 @@ impl Credential { let mut address_bytes = [0u8; 29]; address_bytes[0] = header; - address_bytes[1..].copy_from_slice(&hash); + address_bytes[1..].copy_from_slice(hash.as_ref()); let hrp = Hrp::parse("stake").map_err(|e| anyhow!("HRP parse error: {e}"))?; bech32::encode::(hrp, &address_bytes) @@ -784,8 +791,8 @@ pub type StakeCredential = Credential; impl StakeCredential { pub fn to_string(&self) -> Result { let (hrp, data) = match &self { - Self::AddrKeyHash(data) => (Hrp::parse("stake_vkh")?, data), - Self::ScriptHash(data) => (Hrp::parse("script")?, data), + Self::AddrKeyHash(data) => (Hrp::parse("stake_vkh")?, data.as_slice()), + Self::ScriptHash(data) => (Hrp::parse("script")?, data.as_slice()), }; Ok(bech32::encode::(hrp, data)?) @@ -870,12 +877,12 @@ pub struct PoolRegistration { /// Operator pool key hash - used as ID #[serde_as(as = "Hex")] #[n(0)] - pub operator: KeyHash, + pub operator: PoolId, /// VRF key hash #[serde_as(as = "Hex")] #[n(1)] - pub vrf_key_hash: KeyHash, + pub vrf_key_hash: VrfKeyHash, /// Pledged Ada #[n(2)] @@ -910,7 +917,7 @@ pub struct PoolRegistration { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PoolRetirement { /// Operator pool key hash - used as ID - pub operator: KeyHash, + pub operator: PoolId, /// Epoch it will retire at the end of pub epoch: u64, @@ -976,7 +983,7 @@ pub struct StakeDelegation { pub stake_address: StakeAddress, /// Pool ID to delegate to - pub operator: KeyHash, + pub operator: PoolId, } /// SPO total delegation data (for SPDD) @@ -1006,13 +1013,13 @@ pub struct SPORewards { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct GenesisKeyDelegation { /// Genesis hash - pub genesis_hash: KeyHash, + pub genesis_hash: Hash<32>, /// Genesis delegate hash - pub genesis_delegate_hash: KeyHash, + pub genesis_delegate_hash: PoolId, /// VRF key hash - pub vrf_key_hash: KeyHash, + pub vrf_key_hash: VrfKeyHash, } /// Source of a MIR @@ -1092,7 +1099,7 @@ pub struct StakeAndVoteDelegation { pub stake_address: StakeAddress, /// Pool - pub operator: KeyHash, + pub operator: PoolId, // DRep vote pub drep: DRepChoice, @@ -1105,7 +1112,7 @@ pub struct StakeRegistrationAndDelegation { pub stake_address: StakeAddress, /// Pool - pub operator: KeyHash, + pub operator: PoolId, // Deposit paid pub deposit: Lovelace, @@ -1133,7 +1140,7 @@ pub struct StakeRegistrationAndStakeAndVoteDelegation { pub stake_address: StakeAddress, /// Pool - pub operator: KeyHash, + pub operator: PoolId, /// DRep choice pub drep: DRepChoice, @@ -1374,7 +1381,7 @@ pub struct HeavyDelegate { #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct GenesisDelegate { #[serde_as(as = "Hex")] - pub delegate: Vec, + pub delegate: Hash<28>, #[serde_as(as = "Hex")] pub vrf: Vec, } @@ -1695,31 +1702,30 @@ impl GovernanceAction { serde::Serialize, serde::Deserialize, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Hash, )] pub enum Voter { - ConstitutionalCommitteeKey(AddrKeyhash), - ConstitutionalCommitteeScript(ScriptHash), - DRepKey(AddrKeyhash), - DRepScript(ScriptHash), - StakePoolKey(AddrKeyhash), + ConstitutionalCommitteeKey(ConstitutionalCommitteeKeyHash), + ConstitutionalCommitteeScript(ConstitutionalCommitteeScriptHash), + DRepKey(DrepKeyHash), + DRepScript(DRepScriptHash), + StakePoolKey(PoolId), } impl Voter { - pub fn to_bech32(&self, hrp: &str, buf: &[u8]) -> String { - let voter_hrp: Hrp = Hrp::parse(hrp).unwrap(); - bech32::encode::(voter_hrp, buf) - .unwrap_or_else(|e| format!("Cannot convert {:?} to bech32: {e}", self)) + pub fn to_bech32(&self) -> Result { + match self { + Voter::ConstitutionalCommitteeKey(h) => h.to_bech32(), + Voter::ConstitutionalCommitteeScript(s) => s.to_bech32(), + Voter::DRepKey(k) => k.to_bech32(), + Voter::DRepScript(s) => s.to_bech32(), + Voter::StakePoolKey(k) => k.to_bech32(), + } } } impl Display for Voter { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Voter::ConstitutionalCommitteeKey(h) => write!(f, "{}", self.to_bech32("cc_hot", h)), - Voter::ConstitutionalCommitteeScript(s) => { - write!(f, "{}", self.to_bech32("cc_hot_script", s)) - } - Voter::DRepKey(k) => write!(f, "{}", self.to_bech32("drep", k)), - Voter::DRepScript(s) => write!(f, "{}", self.to_bech32("drep_script", s)), - Voter::StakePoolKey(k) => write!(f, "{}", self.to_bech32("pool", k)), + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.to_bech32() { + Ok(addr) => write!(f, "{}", addr), + Err(e) => write!(f, "", e), } } } @@ -2071,6 +2077,7 @@ impl AddressTotals { #[cfg(test)] mod tests { use super::*; + use crate::hash::Hash; use anyhow::Result; #[test] @@ -2101,10 +2108,12 @@ mod tests { } fn make_committee_credential(addr_key_hash: bool, val: u8) -> CommitteeCredential { + // Create a 28-byte array filled with the value + let hash_bytes = [val; 28]; if addr_key_hash { - Credential::AddrKeyHash(vec![val]) + Credential::AddrKeyHash(KeyHash::from(hash_bytes)) } else { - Credential::ScriptHash(vec![val]) + Credential::ScriptHash(KeyHash::from(hash_bytes)) } } @@ -2113,8 +2122,11 @@ mod tests { let gov_action_id = GovActionId::default(); let mut voting = VotingProcedures::default(); + // Create a test hash with pattern [1, 2, 3, 4, 0, 0, ...] + let mut test_hash_bytes = [0u8; 28]; + test_hash_bytes[0..4].copy_from_slice(&[1, 2, 3, 4]); voting.votes.insert( - Voter::StakePoolKey(vec![1, 2, 3, 4]), + Voter::StakePoolKey(test_hash_bytes.into()), SingleVoterVotes::default(), ); @@ -2128,7 +2140,7 @@ mod tests { }, ); voting.votes.insert( - Voter::StakePoolKey(vec![1, 2, 3, 4]), + Voter::StakePoolKey(PoolId::new(Hash::new(test_hash_bytes))), SingleVoterVotes::default(), ); println!("Json: {}", serde_json::to_string(&voting)?); diff --git a/modules/accounts_state/src/monetary.rs b/modules/accounts_state/src/monetary.rs index b1f1d9d0..76ad9584 100644 --- a/modules/accounts_state/src/monetary.rs +++ b/modules/accounts_state/src/monetary.rs @@ -108,11 +108,11 @@ fn calculate_monetary_expansion( mod tests { use super::*; use acropolis_common::rational_number::rational_number_from_f32; - use acropolis_common::NetworkId; use acropolis_common::{ protocol_params::{Nonce, NonceVariant, ProtocolVersion, ShelleyProtocolParams}, GenesisDelegate, }; + use acropolis_common::{NetworkId, PoolId}; use chrono::{DateTime, Utc}; use std::collections::HashMap; @@ -175,7 +175,7 @@ mod tests { slots_per_kes_period: 129600, system_start: DateTime::::default(), update_quorum: 5, - gen_delegs: HashMap::, GenesisDelegate>::new(), + gen_delegs: HashMap::::new(), } } diff --git a/modules/accounts_state/src/rewards.rs b/modules/accounts_state/src/rewards.rs index 5bd3e9f5..fb5b6f64 100644 --- a/modules/accounts_state/src/rewards.rs +++ b/modules/accounts_state/src/rewards.rs @@ -2,10 +2,9 @@ use crate::snapshot::{Snapshot, SnapshotSPO}; use acropolis_common::{ - protocol_params::ShelleyParams, rational_number::RationalNumber, KeyHash, Lovelace, SPORewards, - StakeAddress, + protocol_params::ShelleyParams, rational_number::RationalNumber, Lovelace, PoolId, RewardType, + SPORewards, StakeAddress, }; -use acropolis_common::{PoolId, RewardType}; use anyhow::{bail, Result}; use bigdecimal::{BigDecimal, One, ToPrimitive, Zero}; use std::cmp::min; @@ -40,10 +39,10 @@ pub struct RewardsResult { pub total_paid: u64, /// Rewards to be paid - pub rewards: BTreeMap>, + pub rewards: BTreeMap>, /// SPO rewards - pub spo_rewards: Vec<(KeyHash, SPORewards)>, + pub spo_rewards: Vec<(PoolId, SPORewards)>, } /// Calculate rewards for a given epoch based on current rewards state and protocol parameters @@ -234,7 +233,7 @@ pub fn calculate_rewards( /// Calculate rewards for an individual SPO #[allow(clippy::too_many_arguments)] fn calculate_spo_rewards( - operator_id: &KeyHash, + operator_id: &PoolId, spo: &SnapshotSPO, blocks_produced: u64, total_blocks: usize, @@ -389,7 +388,7 @@ fn calculate_spo_rewards( account: delegator_stake_address.clone(), rtype: RewardType::Member, amount: to_pay, - pool: operator_id.to_vec(), + pool: *operator_id, }); total_paid += to_pay; delegators_paid += 1; @@ -407,7 +406,7 @@ fn calculate_spo_rewards( account: spo.reward_account.clone(), rtype: RewardType::Leader, amount: spo_benefit, - pool: operator_id.to_vec(), + pool: *operator_id, }); } else { info!( diff --git a/modules/accounts_state/src/snapshot.rs b/modules/accounts_state/src/snapshot.rs index a503d894..bd0154fa 100644 --- a/modules/accounts_state/src/snapshot.rs +++ b/modules/accounts_state/src/snapshot.rs @@ -2,7 +2,7 @@ use crate::state::{Pots, RegistrationChange}; use acropolis_common::{ - stake_addresses::StakeAddressMap, KeyHash, Lovelace, PoolRegistration, Ratio, StakeAddress, + stake_addresses::StakeAddressMap, Lovelace, PoolId, PoolRegistration, Ratio, StakeAddress, }; use imbl::OrdMap; use std::collections::HashMap; @@ -47,7 +47,7 @@ pub struct Snapshot { pub epoch: u64, /// Map of SPOs by operator ID - pub spos: HashMap, + pub spos: HashMap, /// Persistent pot values pub pots: Pots, @@ -65,8 +65,8 @@ impl Snapshot { pub fn new( epoch: u64, stake_addresses: &StakeAddressMap, - spos: &OrdMap, - spo_block_counts: &HashMap, + spos: &OrdMap, + spo_block_counts: &HashMap, pots: &Pots, blocks: usize, registration_changes: Vec, @@ -168,7 +168,7 @@ impl Snapshot { /// Get the total stake held by a vector of stake addresses for a particular SPO (by ID) pub fn get_stake_delegated_to_spo_by_addresses( &self, - spo: &KeyHash, + spo: &PoolId, addresses: &[StakeAddress], ) -> Lovelace { let Some(snapshot_spo) = self.spos.get(spo) else { @@ -195,23 +195,21 @@ mod tests { use super::*; use acropolis_common::stake_addresses::StakeAddressState; use acropolis_common::NetworkId::Mainnet; - use acropolis_common::{StakeAddress, StakeCredential}; + use acropolis_common::{PoolId, StakeAddress, StakeCredential}; - // Helper function to create stake addresses for testing fn create_test_stake_address(id: u8) -> StakeAddress { - let mut hash = vec![0u8; 28]; + let mut hash = [0u8; 28]; hash[0] = id; StakeAddress { network: Mainnet, - credential: StakeCredential::AddrKeyHash(hash), + credential: StakeCredential::AddrKeyHash(hash.into()), } } - // Helper function to create SPO key hashes for testing - fn create_test_spo_hash(id: u8) -> KeyHash { - let mut hash = vec![0u8; 28]; + fn create_test_spo_hash(id: u8) -> PoolId { + let mut hash = [0u8; 28]; hash[0] = id; - hash + hash.into() } #[test] @@ -272,10 +270,10 @@ mod tests { }, ); - let mut spos: OrdMap = OrdMap::new(); + let mut spos: OrdMap = OrdMap::new(); spos.insert(spo1.clone(), PoolRegistration::default()); spos.insert(spo2.clone(), PoolRegistration::default()); - let spo_block_counts: HashMap = HashMap::new(); + let spo_block_counts: HashMap = HashMap::new(); let snapshot = Snapshot::new( 42, &stake_addresses, diff --git a/modules/accounts_state/src/spo_distribution_publisher.rs b/modules/accounts_state/src/spo_distribution_publisher.rs index 7c963bc5..47a8efdc 100644 --- a/modules/accounts_state/src/spo_distribution_publisher.rs +++ b/modules/accounts_state/src/spo_distribution_publisher.rs @@ -1,5 +1,5 @@ use acropolis_common::messages::{CardanoMessage, Message, SPOStakeDistributionMessage}; -use acropolis_common::{BlockInfo, DelegatedStake, KeyHash}; +use acropolis_common::{BlockInfo, DelegatedStake, PoolId}; use caryatid_sdk::Context; use std::collections::BTreeMap; use std::sync::Arc; @@ -23,7 +23,7 @@ impl SPODistributionPublisher { pub async fn publish_spdd( &mut self, block: &BlockInfo, - spos: BTreeMap, + spos: BTreeMap, ) -> anyhow::Result<()> { self.context .message_bus @@ -32,7 +32,7 @@ impl SPODistributionPublisher { Arc::new(Message::Cardano(( block.clone(), CardanoMessage::SPOStakeDistribution(SPOStakeDistributionMessage { - epoch: block.epoch - 1, // End of previous epoch + epoch: block.epoch - 1, // End of the previous epoch spos: spos.into_iter().collect(), }), ))), diff --git a/modules/accounts_state/src/spo_distribution_store.rs b/modules/accounts_state/src/spo_distribution_store.rs index 9ae3a783..cdf6b02e 100644 --- a/modules/accounts_state/src/spo_distribution_store.rs +++ b/modules/accounts_state/src/spo_distribution_store.rs @@ -1,13 +1,14 @@ use std::collections::HashMap; -use acropolis_common::{AddrKeyhash, PoolId}; +use acropolis_common::types::AddrKeyhash; +use acropolis_common::PoolId; use anyhow::Result; use fjall::{Config, Keyspace, PartitionCreateOptions}; -const POOL_ID_LEN: usize = 28; +const POOL_ID_LENGTH: usize = 28; const STAKE_KEY_LEN: usize = 28; const EPOCH_LEN: usize = 8; -const TOTAL_KEY_LEN: usize = EPOCH_LEN + POOL_ID_LEN + STAKE_KEY_LEN; +const TOTAL_KEY_LEN: usize = EPOCH_LEN + POOL_ID_LENGTH + STAKE_KEY_LEN; // Batch size balances commit overhead vs memory usage // ~720KB per batch (72 bytes × 10,000) @@ -17,22 +18,22 @@ const BATCH_SIZE: usize = 10_000; fn encode_key(epoch: u64, pool_id: &PoolId, stake_key: &AddrKeyhash) -> Vec { let mut key = Vec::with_capacity(TOTAL_KEY_LEN); key.extend_from_slice(&epoch.to_be_bytes()); - key.extend_from_slice(pool_id); - key.extend_from_slice(stake_key); + key.extend_from_slice(pool_id.as_ref()); + key.extend_from_slice(stake_key.as_ref()); key } fn encode_epoch_pool_prefix(epoch: u64, pool_id: &PoolId) -> Vec { - let mut prefix = Vec::with_capacity(EPOCH_LEN + POOL_ID_LEN); + let mut prefix = Vec::with_capacity(EPOCH_LEN + POOL_ID_LENGTH); prefix.extend_from_slice(&epoch.to_be_bytes()); - prefix.extend_from_slice(pool_id); + prefix.extend_from_slice(pool_id.as_ref()); prefix } fn decode_key(key: &[u8]) -> Result<(u64, PoolId, AddrKeyhash)> { let epoch = u64::from_be_bytes(key[..EPOCH_LEN].try_into()?); - let pool_id = key[EPOCH_LEN..EPOCH_LEN + POOL_ID_LEN].to_vec(); - let stake_key = key[EPOCH_LEN + POOL_ID_LEN..].to_vec(); + let pool_id = key[EPOCH_LEN..EPOCH_LEN + POOL_ID_LENGTH].try_into()?; + let stake_key = key[EPOCH_LEN + POOL_ID_LENGTH..].try_into()?; Ok((epoch, pool_id, stake_key)) } @@ -222,29 +223,42 @@ impl SPDDStore { #[cfg(test)] mod tests { use super::*; + use acropolis_common::crypto::keyhash_224; + use acropolis_common::types::AddrKeyhash; + use acropolis_common::PoolId; const DB_PATH: &str = "spdd_db"; + fn test_pool_hash(byte: u8) -> PoolId { + keyhash_224(&vec![byte]).into() + } + + fn test_addr_hash(byte: u8) -> AddrKeyhash { + keyhash_224(&vec![byte]) + } + #[test] fn test_store_spdd_state() { let mut spdd_store = SPDDStore::new(std::path::Path::new(DB_PATH), 1).expect("Failed to create SPDD store"); let mut spdd_state: HashMap> = HashMap::new(); + spdd_state.insert( - vec![0x01; 28], - vec![(vec![0x10; 28], 100), (vec![0x11; 28], 150)], + test_pool_hash(0x01), + vec![(test_addr_hash(0x10), 100), (test_addr_hash(0x11), 150)], ); spdd_state.insert( - vec![0x02; 28], - vec![(vec![0x20; 28], 200), (vec![0x21; 28], 250)], + test_pool_hash(0x02), + vec![(test_addr_hash(0x20), 200), (test_addr_hash(0x21), 250)], ); assert!(spdd_store.store_spdd(1, spdd_state).is_ok()); let result = spdd_store.query_by_epoch(1).unwrap(); assert_eq!(result.len(), 4); - let result = spdd_store.query_by_epoch_and_pool(1, &vec![0x01; 28]).unwrap(); + + let result = spdd_store.query_by_epoch_and_pool(1, &test_pool_hash(0x01)).unwrap(); assert_eq!(result.len(), 2); - let result = spdd_store.query_by_epoch_and_pool(1, &vec![0x02; 28]).unwrap(); + let result = spdd_store.query_by_epoch_and_pool(1, &test_pool_hash(0x02)).unwrap(); assert_eq!(result.len(), 2); } @@ -255,9 +269,13 @@ mod tests { for epoch in 1..=3 { let mut spdd_state: HashMap> = HashMap::new(); + spdd_state.insert( - vec![epoch as u8; 28], - vec![(vec![0x10; 28], epoch * 100), (vec![0x11; 28], epoch * 150)], + test_pool_hash(epoch as u8), + vec![ + (test_addr_hash(0x10), epoch * 100), + (test_addr_hash(0x11), epoch * 150), + ], ); spdd_store.store_spdd(epoch, spdd_state).expect("Failed to store SPDD state"); } diff --git a/modules/accounts_state/src/spo_rewards_publisher.rs b/modules/accounts_state/src/spo_rewards_publisher.rs index 04161b50..42ace388 100644 --- a/modules/accounts_state/src/spo_rewards_publisher.rs +++ b/modules/accounts_state/src/spo_rewards_publisher.rs @@ -1,5 +1,5 @@ use acropolis_common::messages::{CardanoMessage, Message, SPORewardsMessage}; -use acropolis_common::{BlockInfo, KeyHash, SPORewards}; +use acropolis_common::{BlockInfo, PoolId, SPORewards}; use caryatid_sdk::Context; use std::sync::Arc; @@ -22,7 +22,7 @@ impl SPORewardsPublisher { pub async fn publish_spo_rewards( &mut self, block: &BlockInfo, - spo_rewards: Vec<(KeyHash, SPORewards)>, + spo_rewards: Vec<(PoolId, SPORewards)>, ) -> anyhow::Result<()> { self.context .message_bus diff --git a/modules/accounts_state/src/state.rs b/modules/accounts_state/src/state.rs index a711c347..787ed0d2 100644 --- a/modules/accounts_state/src/state.rs +++ b/modules/accounts_state/src/state.rs @@ -90,11 +90,11 @@ pub struct RegistrationChange { /// Overall state - stored per block #[derive(Debug, Default, Clone)] pub struct State { - /// Map of active SPOs by operator ID - spos: OrdMap, + /// Map of active SPOs by pool ID + spos: OrdMap, - /// List of SPOs (by operator ID) retiring in the current epoch - retiring_spos: Vec, + /// List of SPOs (by pool ID) retiring in the current epoch + retiring_spos: Vec, /// Map of staking address values /// Wrapped in an Arc so it doesn't get cloned in full by StateHistory @@ -160,17 +160,17 @@ impl State { } /// Get Pool Live Stake Info - pub fn get_pool_live_stake_info(&self, pool_operator: &KeyHash) -> PoolLiveStakeInfo { + pub fn get_pool_live_stake_info(&self, pool_operator: &PoolId) -> PoolLiveStakeInfo { self.stake_addresses.lock().unwrap().get_pool_live_stake_info(pool_operator) } /// Get Pools Live stake - pub fn get_pools_live_stakes(&self, pool_operators: &[KeyHash]) -> Vec { + pub fn get_pools_live_stakes(&self, pool_operators: &[PoolId]) -> Vec { self.stake_addresses.lock().unwrap().get_pools_live_stakes(pool_operators) } /// Get Pool Delegators with live_stakes - pub fn get_pool_delegators(&self, pool_operator: &KeyHash) -> Vec<(KeyHash, u64)> { + pub fn get_pool_delegators(&self, pool_operator: &PoolId) -> Vec<(KeyHash, u64)> { self.stake_addresses.lock().unwrap().get_pool_delegators(pool_operator) } @@ -183,7 +183,7 @@ impl State { pub fn get_accounts_utxo_values_map( &self, stake_keys: &[StakeAddress], - ) -> Option, u64>> { + ) -> Option> { let stake_addresses = self.stake_addresses.lock().ok()?; // If lock fails, return None stake_addresses.get_accounts_utxo_values_map(stake_keys) } @@ -198,7 +198,7 @@ impl State { pub fn get_accounts_balances_map( &self, stake_keys: &[StakeAddress], - ) -> Option, u64>> { + ) -> Option> { let stake_addresses = self.stake_addresses.lock().ok()?; // If lock fails, return None stake_addresses.get_accounts_balances_map(stake_keys) } @@ -251,7 +251,7 @@ impl State { &mut self, epoch: u64, total_fees: u64, - spo_block_counts: HashMap, + spo_block_counts: HashMap, verifier: &Verifier, ) -> Result> { // TODO HACK! Investigate why this differs to our calculated reserves after AVVM @@ -554,12 +554,12 @@ impl State { /// (both with and without rewards) for each active SPO /// And Stake Pool Reward State (rewards and delegators_count for each pool) /// Key of returned map is the SPO 'operator' ID - pub fn generate_spdd(&self) -> BTreeMap { + pub fn generate_spdd(&self) -> BTreeMap { let stake_addresses = self.stake_addresses.lock().unwrap(); stake_addresses.generate_spdd() } - pub fn dump_spdd_state(&self) -> HashMap> { + pub fn dump_spdd_state(&self) -> HashMap> { let stake_addresses = self.stake_addresses.lock().unwrap(); stake_addresses.dump_spdd_state() } @@ -596,8 +596,8 @@ impl State { &mut self, ea_msg: &EpochActivityMessage, verifier: &Verifier, - ) -> Result<(Vec<(KeyHash, SPORewards)>, Vec)> { - let mut spo_rewards: Vec<(KeyHash, SPORewards)> = Vec::new(); + ) -> Result<(Vec<(PoolId, SPORewards)>, Vec)> { + let mut spo_rewards: Vec<(PoolId, SPORewards)> = Vec::new(); // Collect stake addresses reward deltas let mut reward_deltas = Vec::::new(); @@ -666,7 +666,7 @@ impl State { }; // Map block counts, filtering out SPOs we don't know (OBFT in early Shelley) - let spo_blocks: HashMap = ea_msg + let spo_blocks: HashMap = ea_msg .spo_blocks .iter() .filter(|(hash, _)| self.spos.contains_key(hash)) @@ -688,7 +688,7 @@ impl State { /// epoch pub fn handle_spo_state(&mut self, spo_msg: &SPOStateMessage) -> Result<()> { // Capture current SPOs, mapped by operator ID - let new_spos: OrdMap = + let new_spos: OrdMap = spo_msg.spos.iter().cloned().map(|spo| (spo.operator.clone(), spo)).collect(); // Get pool deposit amount from parameters, or default @@ -764,7 +764,7 @@ impl State { // Schedule to retire - we need them to still be in place when we count // blocks for the previous epoch - self.retiring_spos.push(id.to_vec()); + self.retiring_spos.push(*id); } self.spos = new_spos; @@ -835,7 +835,7 @@ impl State { } /// Record a stake delegation - fn record_stake_delegation(&mut self, stake_address: &StakeAddress, spo: &KeyHash) { + fn record_stake_delegation(&mut self, stake_address: &StakeAddress, spo: &PoolId) { let mut stake_addresses = self.stake_addresses.lock().unwrap(); stake_addresses.record_stake_delegation(stake_address, spo); } @@ -964,13 +964,14 @@ impl State { #[cfg(test)] mod tests { use super::*; + use acropolis_common::crypto::{keyhash_224, keyhash_256}; use acropolis_common::{ protocol_params::ConwayParams, rational_number::RationalNumber, Anchor, Committee, Constitution, CostModel, DRepVotingThresholds, NetworkId, PoolVotingThresholds, Pot, PotDelta, Ratio, Registration, StakeAddress, StakeAddressDelta, StakeAndVoteDelegation, StakeCredential, StakeRegistrationAndStakeAndVoteDelegation, StakeRegistrationAndVoteDelegation, TxCertificateWithPos, TxIdentifier, VoteDelegation, - Withdrawal, + VrfKeyHash, Withdrawal, }; // Helper to create a StakeAddress from a byte slice @@ -979,10 +980,22 @@ mod tests { full_hash[..hash.len().min(28)].copy_from_slice(&hash[..hash.len().min(28)]); StakeAddress { network: NetworkId::Mainnet, - credential: StakeCredential::AddrKeyHash(full_hash), + credential: StakeCredential::AddrKeyHash(full_hash.try_into().unwrap()), } } + fn test_keyhash(byte: u8) -> KeyHash { + keyhash_224(&vec![byte]) + } + + fn test_keyhash_from_bytes(bytes: &[u8]) -> KeyHash { + keyhash_224(bytes) + } + + fn test_vrf_keyhash(byte: u8) -> VrfKeyHash { + keyhash_256(&vec![byte]).into() + } + const STAKE_KEY_HASH: [u8; 3] = [0x99, 0x0f, 0x00]; const DREP_HASH: [u8; 4] = [0xca, 0xfe, 0xd0, 0x0d]; @@ -1036,8 +1049,11 @@ mod tests { fn spdd_from_delegation_with_utxo_values_and_pledge() { let mut state = State::default(); - let spo1: KeyHash = vec![0x01]; - let spo2: KeyHash = vec![0x02]; + let spo1 = test_keyhash(0x01).into(); + let spo2 = test_keyhash(0x02).into(); + + let vrf_key_hash_1 = test_vrf_keyhash(0x03); + let vrf_key_hash_2 = test_vrf_keyhash(0x04); // Create the SPOs state @@ -1045,8 +1061,8 @@ mod tests { epoch: 1, spos: vec![ PoolRegistration { - operator: spo1.clone(), - vrf_key_hash: spo1.clone(), + operator: spo1, + vrf_key_hash: vrf_key_hash_1, pledge: 26, cost: 0, margin: Ratio { @@ -1059,8 +1075,8 @@ mod tests { pool_metadata: None, }, PoolRegistration { - operator: spo2.clone(), - vrf_key_hash: spo2.clone(), + operator: spo2, + vrf_key_hash: vrf_key_hash_2, pledge: 47, cost: 10, margin: Ratio { @@ -1300,7 +1316,7 @@ mod tests { fn drdd_includes_initial_deposit() { let mut state = State::default(); - let drep_addr_cred = DRepCredential::AddrKeyHash(DREP_HASH.to_vec()); + let drep_addr_cred = DRepCredential::AddrKeyHash(test_keyhash_from_bytes(&DREP_HASH)); state.handle_drep_state(&DRepStateMessage { epoch: 1337, dreps: vec![(drep_addr_cred.clone(), 1_000_000)], @@ -1321,8 +1337,8 @@ mod tests { fn drdd_respects_different_delegations() -> Result<()> { let mut state = State::default(); - let drep_addr_cred = DRepCredential::AddrKeyHash(DREP_HASH.to_vec()); - let drep_script_cred = DRepCredential::ScriptHash(DREP_HASH.to_vec()); + let drep_addr_cred = DRepCredential::AddrKeyHash(test_keyhash_from_bytes(&DREP_HASH)); + let drep_script_cred = DRepCredential::ScriptHash(test_keyhash_from_bytes(&DREP_HASH)); state.handle_drep_state(&DRepStateMessage { epoch: 1337, dreps: vec![ @@ -1338,6 +1354,11 @@ mod tests { let tx_identifier = TxIdentifier::default(); + // Get the KeyHash once + let spo1_hash = spo1.get_hash(); + let drep_key_hash = test_keyhash_from_bytes(&DREP_HASH); + let pool_id_1 = (*spo1_hash).into(); + let certificates = vec![ // register the first two SPOs separately from their delegation TxCertificateWithPos { @@ -1359,7 +1380,7 @@ mod tests { TxCertificateWithPos { cert: TxCertificate::VoteDelegation(VoteDelegation { stake_address: spo1.clone(), - drep: DRepChoice::Key(DREP_HASH.to_vec()), + drep: DRepChoice::Key(drep_key_hash), }), tx_identifier, cert_index: 0, @@ -1367,8 +1388,8 @@ mod tests { TxCertificateWithPos { cert: TxCertificate::StakeAndVoteDelegation(StakeAndVoteDelegation { stake_address: spo2.clone(), - operator: spo1.get_hash().to_vec(), - drep: DRepChoice::Script(DREP_HASH.to_vec()), + operator: pool_id_1, + drep: DRepChoice::Script(drep_key_hash), }), tx_identifier, cert_index: 0, @@ -1388,7 +1409,7 @@ mod tests { cert: TxCertificate::StakeRegistrationAndStakeAndVoteDelegation( StakeRegistrationAndStakeAndVoteDelegation { stake_address: spo4.clone(), - operator: spo1.get_hash().to_vec(), + operator: pool_id_1, drep: DRepChoice::NoConfidence, deposit: 1, }, diff --git a/modules/accounts_state/src/verifier.rs b/modules/accounts_state/src/verifier.rs index 34b114c0..b3f162d2 100644 --- a/modules/accounts_state/src/verifier.rs +++ b/modules/accounts_state/src/verifier.rs @@ -1,7 +1,7 @@ //! Verification of calculated values against captured CSV from Haskell node / DBSync use crate::rewards::{RewardDetail, RewardsResult}; use crate::state::Pots; -use acropolis_common::{KeyHash, RewardType, StakeAddress}; +use acropolis_common::{PoolId, RewardType, StakeAddress}; use hex::FromHex; use itertools::EitherOrBoth::{Both, Left, Right}; use itertools::Itertools; @@ -131,7 +131,7 @@ impl Verifier { }; // Expect CSV header: spo,address,type,amount - let mut expected_rewards: BTreeMap> = BTreeMap::new(); + let mut expected_rewards: BTreeMap> = BTreeMap::new(); for result in reader.deserialize() { let (spo, address, rtype, amount): (String, String, String, u64) = match result { Ok(row) => row, @@ -141,10 +141,13 @@ impl Verifier { } }; - let Ok(spo) = Vec::from_hex(&spo) else { - error!("Bad hex in {path} for SPO: {spo} - skipping"); + let Some(spo) = + Vec::from_hex(&spo).ok().and_then(|bytes| PoolId::try_from(bytes).ok()) + else { + error!("Bad hex/SPO in {path} for SPO: {spo} - skipping"); continue; }; + let Ok(account) = Vec::from_hex(&address) else { error!("Bad hex in {path} for address: {address} - skipping"); continue; @@ -167,7 +170,7 @@ impl Verifier { continue; }; - expected_rewards.entry(spo.clone()).or_default().push(RewardDetail { + expected_rewards.entry(spo).or_default().push(RewardDetail { account: stake_address, rtype, amount, diff --git a/modules/chain_store/src/chain_store.rs b/modules/chain_store/src/chain_store.rs index 3d145be5..a358b895 100644 --- a/modules/chain_store/src/chain_store.rs +++ b/modules/chain_store/src/chain_store.rs @@ -12,7 +12,7 @@ use acropolis_common::{ }, queries::misc::Order, state_history::{StateHistory, StateHistoryStore}, - BechOrdAddress, BlockHash, GenesisDelegate, HeavyDelegate, TxHash, VrfKeyHash, + BechOrdAddress, BlockHash, GenesisDelegate, HeavyDelegate, PoolId, TxHash, }; use anyhow::{bail, Result}; use caryatid_sdk::{module, Context, Module}; @@ -413,7 +413,7 @@ impl ChainStore { tx_count: decoded.tx_count() as u64, output, fees, - block_vrf: header.vrf_vkey().map(|key| VrfKeyHash::try_from(key).ok().unwrap()), + block_vrf: header.vrf_vkey().map(|key| key.try_into().ok().unwrap()), op_cert, op_cert_counter, previous_block: header.previous_hash().map(|h| BlockHash::from(*h)), @@ -525,8 +525,8 @@ impl ChainStore { #[derive(Default, Debug, Clone)] pub struct State { - pub byron_heavy_delegates: HashMap, HeavyDelegate>, - pub shelley_genesis_delegates: HashMap, GenesisDelegate>, + pub byron_heavy_delegates: HashMap, + pub shelley_genesis_delegates: HashMap, } impl State { diff --git a/modules/drep_state/src/state.rs b/modules/drep_state/src/state.rs index 4268624d..6f48b4a0 100644 --- a/modules/drep_state/src/state.rs +++ b/modules/drep_state/src/state.rs @@ -261,8 +261,8 @@ impl State { for (tx_hash, voting_procedures) in voting_procedures { for (voter, single_votes) in &voting_procedures.votes { let drep_cred = match voter { - Voter::DRepKey(k) => DRepCredential::AddrKeyHash(k.to_vec()), - Voter::DRepScript(s) => DRepCredential::ScriptHash(s.to_vec()), + Voter::DRepKey(k) => DRepCredential::AddrKeyHash(k.into_inner().into()), + Voter::DRepScript(s) => DRepCredential::ScriptHash(s.into_inner().into()), _ => continue, }; @@ -584,7 +584,7 @@ mod tests { #[test] fn test_drep_process_one_certificate() { - let tx_cred = Credential::AddrKeyHash(CRED_1.to_vec()); + let tx_cred = Credential::AddrKeyHash(CRED_1.into()); let tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepRegistration(DRepRegistration { credential: tx_cred.clone(), @@ -609,7 +609,7 @@ mod tests { #[test] fn test_drep_do_not_replace_existing_certificate() { - let tx_cred = Credential::AddrKeyHash(CRED_1.to_vec()); + let tx_cred = Credential::AddrKeyHash(CRED_1.into()); let tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepRegistration(DRepRegistration { credential: tx_cred.clone(), @@ -647,7 +647,7 @@ mod tests { #[test] fn test_drep_update_certificate() { - let tx_cred = Credential::AddrKeyHash(CRED_1.to_vec()); + let tx_cred = Credential::AddrKeyHash(CRED_1.into()); let tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepRegistration(DRepRegistration { credential: tx_cred.clone(), @@ -688,7 +688,7 @@ mod tests { #[test] fn test_drep_do_not_update_nonexistent_certificate() { - let tx_cred = Credential::AddrKeyHash(CRED_1.to_vec()); + let tx_cred = Credential::AddrKeyHash(CRED_1.into()); let tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepRegistration(DRepRegistration { credential: tx_cred.clone(), @@ -707,7 +707,7 @@ mod tests { }; let update_anchor_tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepUpdate(DRepUpdate { - credential: Credential::AddrKeyHash(CRED_2.to_vec()), + credential: Credential::AddrKeyHash(CRED_2.into()), anchor: Some(anchor.clone()), }), tx_identifier: TxIdentifier::default(), @@ -728,7 +728,7 @@ mod tests { #[test] fn test_drep_deregister() { - let tx_cred = Credential::AddrKeyHash(CRED_1.to_vec()); + let tx_cred = Credential::AddrKeyHash(CRED_1.into()); let tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepRegistration(DRepRegistration { credential: tx_cred.clone(), @@ -756,7 +756,7 @@ mod tests { #[test] fn test_drep_do_not_deregister_nonexistent_cert() { - let tx_cred = Credential::AddrKeyHash(CRED_1.to_vec()); + let tx_cred = Credential::AddrKeyHash(CRED_1.into()); let tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepRegistration(DRepRegistration { credential: tx_cred.clone(), @@ -771,7 +771,7 @@ mod tests { let unregister_tx_cert = TxCertificateWithPos { cert: TxCertificate::DRepDeregistration(DRepDeregistration { - credential: Credential::AddrKeyHash(CRED_2.to_vec()), + credential: Credential::AddrKeyHash(CRED_2.into()), refund: 500000000, }), tx_identifier: TxIdentifier::default(), diff --git a/modules/epochs_state/src/state.rs b/modules/epochs_state/src/state.rs index 2b79059b..a2a29a55 100644 --- a/modules/epochs_state/src/state.rs +++ b/modules/epochs_state/src/state.rs @@ -6,7 +6,7 @@ use acropolis_common::{ messages::{BlockTxsMessage, EpochActivityMessage, ProtocolParamsMessage}, params::EPOCH_LENGTH, protocol_params::{Nonces, PraosParams}, - BlockHash, BlockInfo, KeyHash, + BlockHash, BlockInfo, PoolId, }; use anyhow::Result; use imbl::HashMap; @@ -39,8 +39,8 @@ pub struct State { // last block height last_block_height: u64, - // Map of counts by VRF key hashes - blocks_minted: HashMap, + // Map of counts by Pool ID + blocks_minted: HashMap, // blocks seen this epoch epoch_blocks: usize, @@ -188,7 +188,7 @@ impl State { self.last_block_time = block_info.timestamp; self.last_block_height = block_info.number; self.epoch_blocks += 1; - let spo_id = keyhash_224(issuer_vkey); + let spo_id = PoolId::from(keyhash_224(issuer_vkey)); // Count one on this hash *(self.blocks_minted.entry(spo_id.clone()).or_insert(0)) += 1; @@ -255,7 +255,7 @@ impl State { } } - pub fn get_latest_epoch_blocks_minted_by_pool(&self, spo_id: &KeyHash) -> u64 { + pub fn get_latest_epoch_blocks_minted_by_pool(&self, spo_id: &PoolId) -> u64 { self.blocks_minted.get(spo_id).map(|v| *v as u64).unwrap_or(0) } } @@ -336,7 +336,10 @@ mod tests { assert_eq!(state.epoch_blocks, 2); assert_eq!(state.blocks_minted.len(), 1); - assert_eq!(state.blocks_minted.get(&keyhash_224(issuer)), Some(&2)); + assert_eq!( + state.blocks_minted.get(&keyhash_224(issuer).into()), + Some(&2) + ); } #[test] @@ -355,7 +358,7 @@ mod tests { state .blocks_minted .iter() - .find(|(k, _)| *k == &keyhash_224(b"issuer_1")) + .find(|(k, _)| *k == &keyhash_224(b"issuer_1").into()) .map(|(_, v)| *v), Some(1) ); @@ -363,12 +366,13 @@ mod tests { state .blocks_minted .iter() - .find(|(k, _)| *k == &keyhash_224(b"issuer_2")) + .find(|(k, _)| *k == &keyhash_224(b"issuer_2").into()) .map(|(_, v)| *v), Some(2) ); - let blocks_minted = state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_2")); + let blocks_minted = + state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_2").into()); assert_eq!(blocks_minted, 2); } @@ -424,7 +428,10 @@ mod tests { assert_eq!(ea.total_fees, 123); assert_eq!(ea.spo_blocks.len(), 1); assert_eq!( - ea.spo_blocks.iter().find(|(k, _)| k == &keyhash_224(b"issuer_1")).map(|(_, v)| *v), + ea.spo_blocks + .iter() + .find(|(k, _)| k == &keyhash_224(b"issuer_1").into()) + .map(|(_, v)| *v), Some(1) ); assert_eq!(ea.epoch_start_time, genesis.byron_timestamp); @@ -445,7 +452,8 @@ mod tests { assert_eq!(state.last_block_time, block.timestamp); assert_eq!(state.last_block_height, block.number); - let blocks_minted = state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"vrf_1")); + let blocks_minted = + state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"vrf_1").into()); assert_eq!(blocks_minted, 0); } @@ -480,7 +488,7 @@ mod tests { }, ); assert_eq!( - state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_1")), + state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_1").into()), 2 ); history.lock().await.commit(block.number, state); @@ -497,11 +505,11 @@ mod tests { }, ); assert_eq!( - state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_1")), + state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_1").into()), 0 ); assert_eq!( - state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_2")), + state.get_latest_epoch_blocks_minted_by_pool(&keyhash_224(b"issuer_2").into()), 1 ); history.lock().await.commit(block.number, state); diff --git a/modules/governance_state/src/conway_voting.rs b/modules/governance_state/src/conway_voting.rs index c10c176e..049cd982 100644 --- a/modules/governance_state/src/conway_voting.rs +++ b/modules/governance_state/src/conway_voting.rs @@ -1,10 +1,10 @@ use crate::voting_state::VotingRegistrationState; use acropolis_common::protocol_params::ConwayParams; use acropolis_common::{ - BlockInfo, DRepCredential, DelegatedStake, EnactStateElem, GovActionId, GovernanceAction, - GovernanceOutcome, GovernanceOutcomeVariant, KeyHash, Lovelace, ProposalProcedure, - SingleVoterVotes, TreasuryWithdrawalsAction, TxHash, Vote, VoteCount, VoteResult, Voter, - VotingOutcome, VotingProcedure, + AddrKeyhash, BlockInfo, DRepCredential, DelegatedStake, EnactStateElem, GovActionId, + GovernanceAction, GovernanceOutcome, GovernanceOutcomeVariant, Lovelace, PoolId, + ProposalProcedure, ScriptHash, SingleVoterVotes, TreasuryWithdrawalsAction, TxHash, Vote, + VoteCount, VoteResult, Voter, VotingOutcome, VotingProcedure, }; use anyhow::{anyhow, bail, Result}; use hex::ToHex; @@ -170,7 +170,7 @@ impl ConwayVoting { voting_state: &VotingRegistrationState, action_id: &GovActionId, drep_stake: &HashMap, - spo_stake: &HashMap, + spo_stake: &HashMap, ) -> Result { let (_epoch, proposal) = self .proposals @@ -215,7 +215,7 @@ impl ConwayVoting { new_epoch: u64, action_id: &GovActionId, drep_stake: &HashMap, - spo_stake: &HashMap, + spo_stake: &HashMap, ) -> Result> { let mut votes = VoteResult:: { committee: VoteCount::zero(), @@ -253,22 +253,30 @@ impl ConwayVoting { if tracing::enabled!(tracing::Level::DEBUG) { debug!( "Vote for {action_id}, epoch start {new_epoch}: {voter} = {:?}", - drep_stake.get(&DRepCredential::AddrKeyHash(key.clone())) + drep_stake.get(&DRepCredential::AddrKeyHash(AddrKeyhash::from( + key.into_inner() + ))) ); } drep_stake - .get(&DRepCredential::AddrKeyHash(key.clone())) + .get(&DRepCredential::AddrKeyHash(AddrKeyhash::from( + key.into_inner(), + ))) .inspect(|v| *vd += *v); } Voter::DRepScript(script) => { if tracing::enabled!(tracing::Level::DEBUG) { debug!( "Vote for {action_id}, epoch start {new_epoch}: {voter} = {:?}", - drep_stake.get(&DRepCredential::ScriptHash(script.clone())) + drep_stake.get(&DRepCredential::ScriptHash(ScriptHash::from( + script.into_inner() + ))) ); } drep_stake - .get(&DRepCredential::ScriptHash(script.clone())) + .get(&DRepCredential::ScriptHash(ScriptHash::from( + script.into_inner(), + ))) .inspect(|v| *vd += *v); } Voter::StakePoolKey(pool) => { @@ -331,7 +339,7 @@ impl ConwayVoting { voting_state: &VotingRegistrationState, action_id: &GovActionId, drep_stake: &HashMap, - spo_stake: &HashMap, + spo_stake: &HashMap, ) -> Result> { let outcome = self.is_finally_accepted(new_epoch, voting_state, action_id, drep_stake, spo_stake)?; @@ -431,7 +439,7 @@ impl ConwayVoting { new_block: &BlockInfo, voting_state: &VotingRegistrationState, drep_stake: &HashMap, - spo_stake: &HashMap, + spo_stake: &HashMap, ) -> Result> { let mut outcome = Vec::::new(); let actions = self.proposals.keys().cloned().collect::>(); diff --git a/modules/governance_state/src/conway_voting_test.rs b/modules/governance_state/src/conway_voting_test.rs index 09688e23..eb070bdc 100644 --- a/modules/governance_state/src/conway_voting_test.rs +++ b/modules/governance_state/src/conway_voting_test.rs @@ -3,9 +3,11 @@ mod tests { use crate::conway_voting::ConwayVoting; use crate::voting_state::VotingRegistrationState; use acropolis_common::{ - protocol_params::ProtocolParams, rational_number::RationalNumber, Credential, - DRepCredential, DelegatedStake, GovActionId, KeyHash, Lovelace, ProposalProcedure, - SingleVoterVotes, TxHash, Vote, VoteCount, VoteResult, Voter, VotingProcedure, + protocol_params::ProtocolParams, rational_number::RationalNumber, + ConstitutionalCommitteeKeyHash, ConstitutionalCommitteeScriptHash, Credential, + DRepCredential, DRepScriptHash, DelegatedStake, DrepKeyHash, GovActionId, KeyHash, + Lovelace, PoolId, ProposalProcedure, SingleVoterVotes, TxHash, Vote, VoteCount, VoteResult, + Voter, VotingProcedure, }; use anyhow::{anyhow, bail, Result}; @@ -32,9 +34,9 @@ mod tests { #[serde_as] #[derive(Clone, Debug, serde::Deserialize)] struct PoolRecord( - #[serde_as(as = "Base64")] KeyHash, // key - u64, // active stake - u64, // live stake + #[serde_as(as = "Base64")] PoolId, // key + u64, // active stake + u64, // live stake ); #[serde_as] @@ -54,13 +56,17 @@ mod tests { impl VotingRecord { pub fn to_voter(&self) -> Result { - let key = self.1.to_vec(); + let key = self.1; match &self.0 { - 0 => Ok(Voter::DRepKey(key)), - 1 => Ok(Voter::DRepScript(key)), - 2 => Ok(Voter::StakePoolKey(key)), - 3 => Ok(Voter::ConstitutionalCommitteeKey(key)), - 4 => Ok(Voter::ConstitutionalCommitteeScript(key)), + 0 => Ok(Voter::DRepKey(DrepKeyHash::from(key))), + 1 => Ok(Voter::DRepScript(DRepScriptHash::from(key))), + 2 => Ok(Voter::StakePoolKey(PoolId::from(key))), + 3 => Ok(Voter::ConstitutionalCommitteeKey( + ConstitutionalCommitteeKeyHash::from(key), + )), + 4 => Ok(Voter::ConstitutionalCommitteeScript( + ConstitutionalCommitteeScriptHash::from(key), + )), _ => bail!("Unknown voter key type {}", self.0), } } @@ -86,7 +92,7 @@ mod tests { } } - fn read_pools(pools_json: &[u8]) -> Result>> { + fn read_pools(pools_json: &[u8]) -> Result>> { let pools = serde_json::from_slice::)>>(pools_json)?; let res = HashMap::from_iter(pools.iter().map(|(epoch, distr)| { ( @@ -118,7 +124,7 @@ mod tests { distr .iter() .map(|DRepRecord(dt, key, lvl)| { - Ok((parse_drep_credential(*dt, key.to_vec())?, *lvl)) + Ok((parse_drep_credential(*dt, *key)?, *lvl)) }) .collect::>>()?, ), diff --git a/modules/governance_state/src/state.rs b/modules/governance_state/src/state.rs index 1a1e6b0a..1735cbbf 100644 --- a/modules/governance_state/src/state.rs +++ b/modules/governance_state/src/state.rs @@ -5,7 +5,7 @@ use acropolis_common::{ CardanoMessage, DRepStakeDistributionMessage, GovernanceOutcomesMessage, GovernanceProceduresMessage, Message, ProtocolParamsMessage, SPOStakeDistributionMessage, }, - BlockInfo, DRepCredential, DelegatedStake, Era, GovActionId, KeyHash, Lovelace, + BlockInfo, DRepCredential, DelegatedStake, Era, GovActionId, Lovelace, PoolId, ProposalProcedure, TxHash, Voter, VotingProcedure, }; use anyhow::{anyhow, bail, Result}; @@ -29,7 +29,7 @@ pub struct State { drep_stake: HashMap, drep_no_confidence: u64, drep_abstain: u64, - spo_stake: HashMap, + spo_stake: HashMap, alonzo_babbage_voting: AlonzoBabbageVoting, conway_voting: ConwayVoting, diff --git a/modules/historical_accounts_state/src/immutable_historical_account_store.rs b/modules/historical_accounts_state/src/immutable_historical_account_store.rs index 545b9722..74fa0f5b 100644 --- a/modules/historical_accounts_state/src/immutable_historical_account_store.rs +++ b/modules/historical_accounts_state/src/immutable_historical_account_store.rs @@ -153,8 +153,10 @@ impl ImmutableHistoricalAccountStore { &self, account: &StakeAddress, ) -> Result>> { - let mut immutable_rewards = - self.collect_partition::(&self.rewards_history, account.get_hash())?; + let mut immutable_rewards = self.collect_partition::( + &self.rewards_history, + account.get_hash().as_ref(), + )?; self.merge_pending( account, @@ -172,7 +174,7 @@ impl ImmutableHistoricalAccountStore { ) -> Result>> { let mut immutable_active_stake = self.collect_partition::( &self.active_stake_history, - account.get_hash(), + account.get_hash().as_ref(), )?; self.merge_pending( @@ -191,7 +193,7 @@ impl ImmutableHistoricalAccountStore { ) -> Result>> { let mut immutable_registrations = self.collect_partition::( &self.registration_history, - account.get_hash(), + account.get_hash().as_ref(), )?; self.merge_pending( @@ -208,8 +210,10 @@ impl ImmutableHistoricalAccountStore { &self, account: &StakeAddress, ) -> Result>> { - let mut immutable_delegations = self - .collect_partition::(&self.delegation_history, account.get_hash())?; + let mut immutable_delegations = self.collect_partition::( + &self.delegation_history, + account.get_hash().as_ref(), + )?; self.merge_pending( account, @@ -225,8 +229,10 @@ impl ImmutableHistoricalAccountStore { &self, account: &StakeAddress, ) -> Result>> { - let mut immutable_mirs = - self.collect_partition::(&self.mir_history, account.get_hash())?; + let mut immutable_mirs = self.collect_partition::( + &self.mir_history, + account.get_hash().as_ref(), + )?; self.merge_pending(account, |e| e.mir_history.as_ref(), &mut immutable_mirs).await; @@ -237,8 +243,10 @@ impl ImmutableHistoricalAccountStore { &self, account: &StakeAddress, ) -> Result>> { - let mut immutable_withdrawals = self - .collect_partition::(&self.withdrawal_history, account.get_hash())?; + let mut immutable_withdrawals = self.collect_partition::( + &self.withdrawal_history, + account.get_hash().as_ref(), + )?; self.merge_pending( account, @@ -255,7 +263,7 @@ impl ImmutableHistoricalAccountStore { account: &StakeAddress, ) -> Result>> { let mut immutable_addresses = - self.collect_partition::(&self.addresses, account.get_hash())?; + self.collect_partition::(&self.addresses, account.get_hash().as_ref())?; self.merge_pending(account, |e| e.addresses.as_ref(), &mut immutable_addresses).await; @@ -318,7 +326,7 @@ impl ImmutableHistoricalAccountStore { fn make_epoch_key(account: &StakeAddress, epoch: u32) -> [u8; 32] { let mut key = [0u8; 32]; - key[..28].copy_from_slice(&account.get_credential().get_hash()); + key[..28].copy_from_slice(&account.get_credential().get_hash().as_ref()); key[28..32].copy_from_slice(&epoch.to_be_bytes()); key } diff --git a/modules/parameters_state/src/genesis_params.rs b/modules/parameters_state/src/genesis_params.rs index 895b8a67..f0561b17 100644 --- a/modules/parameters_state/src/genesis_params.rs +++ b/modules/parameters_state/src/genesis_params.rs @@ -3,7 +3,7 @@ use acropolis_common::{ protocol_params::{AlonzoParams, BabbageParams, ByronParams, ConwayParams, ShelleyParams}, rational_number::{rational_number_from_f32, RationalNumber}, Anchor, BlockVersionData, Committee, Constitution, CostModel, Credential, DRepVotingThresholds, - Era, HeavyDelegate, PoolVotingThresholds, ProtocolConsts, SoftForkRule, TxFeePolicy, + Era, HeavyDelegate, PoolId, PoolVotingThresholds, ProtocolConsts, SoftForkRule, TxFeePolicy, }; use anyhow::{anyhow, bail, Result}; use base64::prelude::*; @@ -107,7 +107,7 @@ fn map_drep_thresholds(thresholds: &conway::DRepVotingThresholds) -> Result Result { Ok(Constitution { anchor: map_anchor(&constitution.anchor)?, - guardrail_script: Some(decode_hex_string(&constitution.script, 28)?), + guardrail_script: Some(decode_hex_string(&constitution.script, 28)?.try_into().unwrap()), }) } @@ -183,13 +183,13 @@ fn map_byron(genesis: &byron::GenesisFile) -> Result { .heavy_delegation .iter() .map(|(k, v)| { - let k = hex::decode(k)?; + let k = PoolId::try_from(decode(k)?)?; let v = HeavyDelegate { - cert: hex::decode(v.cert.clone())?, + cert: decode(v.cert.clone())?, delegate_pk: BASE64_STANDARD.decode(v.delegate_pk.clone())?, issuer_pk: BASE64_STANDARD.decode(v.issuer_pk.clone())?, }; - Ok::<(Vec, HeavyDelegate), anyhow::Error>((k, v)) + Ok::<(PoolId, HeavyDelegate), anyhow::Error>((k, v)) }) .collect::>()?; Ok(ByronParams { diff --git a/modules/rest_blockfrost/src/handlers/accounts.rs b/modules/rest_blockfrost/src/handlers/accounts.rs index f7e86265..bc663b4e 100644 --- a/modules/rest_blockfrost/src/handlers/accounts.rs +++ b/modules/rest_blockfrost/src/handlers/accounts.rs @@ -7,7 +7,7 @@ use acropolis_common::queries::blocks::{ BlocksStateQuery, BlocksStateQueryResponse, TransactionHashes, }; use acropolis_common::queries::utils::query_state; -use acropolis_common::serialization::Bech32WithHrp; +use acropolis_common::serialization::{Bech32Conversion, Bech32WithHrp}; use acropolis_common::{DRepChoice, StakeAddress}; use anyhow::{anyhow, Result}; use caryatid_sdk::Context; @@ -73,7 +73,7 @@ pub async fn handle_single_account_blockfrost( }; let delegated_spo = match &account.delegated_spo { - Some(spo) => match spo.to_bech32_with_hrp("pool") { + Some(spo) => match spo.to_bech32() { Ok(val) => Some(val), Err(e) => { return Ok(RESTResponse::with_text( @@ -287,7 +287,7 @@ pub async fn handle_account_delegations_blockfrost( )); }; - let pool_id = match r.pool.to_bech32_with_hrp("pool") { + let pool_id = match r.pool.to_bech32() { Ok(p) => p, Err(e) => { return Ok(RESTResponse::with_text( @@ -581,6 +581,7 @@ fn map_drep_choice(drep: &DRepChoice) -> Result { match drep { DRepChoice::Key(hash) => { let val = hash + .to_vec() .to_bech32_with_hrp("drep") .map_err(|e| anyhow!("Bech32 encoding failed for DRep Key: {e}"))?; Ok(DRepChoiceRest { @@ -590,6 +591,7 @@ fn map_drep_choice(drep: &DRepChoice) -> Result { } DRepChoice::Script(hash) => { let val = hash + .to_vec() .to_bech32_with_hrp("drep_script") .map_err(|e| anyhow!("Bech32 encoding failed for DRep Script: {e}"))?; Ok(DRepChoiceRest { diff --git a/modules/rest_blockfrost/src/handlers/epochs.rs b/modules/rest_blockfrost/src/handlers/epochs.rs index 3842a247..089bf972 100644 --- a/modules/rest_blockfrost/src/handlers/epochs.rs +++ b/modules/rest_blockfrost/src/handlers/epochs.rs @@ -4,6 +4,7 @@ use crate::{ EpochActivityRest, ProtocolParamsRest, SPDDByEpochAndPoolItemRest, SPDDByEpochItemRest, }, }; +use acropolis_common::serialization::Bech32Conversion; use acropolis_common::{ messages::{Message, RESTResponse, StateQuery, StateQueryResponse}, queries::{ @@ -14,8 +15,7 @@ use acropolis_common::{ spdd::{SPDDStateQuery, SPDDStateQueryResponse}, utils::query_state, }, - serialization::Bech32WithHrp, - NetworkId, StakeAddress, StakeCredential, + NetworkId, PoolId, StakeAddress, StakeCredential, }; use anyhow::{anyhow, Result}; use caryatid_sdk::Context; @@ -534,7 +534,7 @@ pub async fn handle_epoch_pool_stakes_blockfrost( } }; - let Ok(pool_id) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(pool_id) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -679,7 +679,7 @@ pub async fn handle_epoch_pool_blocks_blockfrost( } }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id_param, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id_param) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id_param}"), @@ -689,7 +689,7 @@ pub async fn handle_epoch_pool_blocks_blockfrost( // query Pool's Blocks by epoch from spo-state let msg = Arc::new(Message::StateQuery(StateQuery::Pools( PoolsStateQuery::GetBlocksByPoolAndEpoch { - pool_id: spo.clone(), + pool_id: spo, epoch: epoch_number, }, ))); diff --git a/modules/rest_blockfrost/src/handlers/pools.rs b/modules/rest_blockfrost/src/handlers/pools.rs index 7bb09a79..8d402c40 100644 --- a/modules/rest_blockfrost/src/handlers/pools.rs +++ b/modules/rest_blockfrost/src/handlers/pools.rs @@ -1,4 +1,13 @@ //! REST handlers for Acropolis Blockfrost /pools endpoints +use crate::{ + handlers_config::HandlersConfig, + types::{PoolDelegatorRest, PoolInfoRest, PoolRelayRest, PoolUpdateEventRest, PoolVoteRest}, +}; +use crate::{ + types::{PoolEpochStateRest, PoolExtendedRest, PoolMetadataRest, PoolRetirementRest}, + utils::{fetch_pool_metadata_as_bytes, verify_pool_metadata_hash, PoolMetadataJson}, +}; +use acropolis_common::serialization::Bech32Conversion; use acropolis_common::{ messages::{Message, RESTResponse, StateQuery, StateQueryResponse}, queries::{ @@ -8,8 +17,7 @@ use acropolis_common::{ utils::query_state, }, rest_helper::ToCheckedF64, - serialization::Bech32WithHrp, - PoolRetirement, PoolUpdateAction, StakeCredential, TxIdentifier, + PoolId, PoolRetirement, PoolUpdateAction, StakeCredential, TxIdentifier, }; use anyhow::Result; use caryatid_sdk::Context; @@ -18,15 +26,6 @@ use std::{sync::Arc, time::Duration}; use tokio::join; use tracing::warn; -use crate::{ - handlers_config::HandlersConfig, - types::{PoolDelegatorRest, PoolInfoRest, PoolRelayRest, PoolUpdateEventRest, PoolVoteRest}, -}; -use crate::{ - types::{PoolEpochStateRest, PoolExtendedRest, PoolMetadataRest, PoolRetirementRest}, - utils::{fetch_pool_metadata_as_bytes, verify_pool_metadata_hash, PoolMetadataJson}, -}; - /// Handle `/pools` Blockfrost-compatible endpoint pub async fn handle_pools_list_blockfrost( context: Arc>, @@ -63,7 +62,7 @@ pub async fn handle_pools_list_blockfrost( let pool_ids = pool_operators .iter() - .map(|operator| operator.to_bech32_with_hrp("pool")) + .map(|operator| operator.to_bech32()) .collect::, _>>(); match pool_ids { @@ -102,7 +101,7 @@ pub async fn handle_pools_extended_retired_retiring_single_blockfrost( "retiring" => { handle_pools_retiring_blockfrost(context.clone(), handlers_config.clone()).await } - _ => match Vec::::from_bech32_with_hrp(param, "pool") { + _ => match PoolId::from_bech32(param) { Ok(pool_id) => { handle_pools_spo_blockfrost(context.clone(), pool_id, handlers_config.clone()).await } @@ -190,7 +189,7 @@ async fn handle_pools_extended_blockfrost( let latest_epoch = latest_epoch_info.epoch; let optimal_pool_sizing = optimal_pool_sizing?; - // if pools are empty, return empty list + // if pools are empty, return an empty list if pools_list_with_info.is_empty() { return Ok(RESTResponse::with_json(200, "[]")); } @@ -207,7 +206,7 @@ async fn handle_pools_extended_blockfrost( .map(|(pool_operator, _)| pool_operator.clone()) .collect::>(); - // Get active stake for each pool from spo-state + // Get an active stake for each pool from spo-state let pools_active_stakes_msg = Arc::new(Message::StateQuery(StateQuery::Pools( PoolsStateQuery::GetPoolsActiveStakes { pools_operators: pools_operators.clone(), @@ -292,8 +291,8 @@ async fn handle_pools_extended_blockfrost( .enumerate() .map(|(i, (pool_operator, pool_registration))| { Ok(PoolExtendedRest { - pool_id: pool_operator.to_bech32_with_hrp("pool")?, - hex: pool_operator.clone(), + pool_id: pool_operator.to_bech32()?, + hex: pool_operator.to_vec(), active_stake: pools_active_stakes .as_ref() .map(|active_stakes| active_stakes[i]), @@ -353,7 +352,7 @@ async fn handle_pools_retired_blockfrost( let retired_pools_rest = retired_pools .iter() .filter_map(|PoolRetirement { operator, epoch }| { - let pool_id = operator.to_bech32_with_hrp("pool").ok()?; + let pool_id = operator.to_bech32().ok()?; Some(PoolRetirementRest { pool_id, epoch: *epoch, @@ -399,7 +398,7 @@ async fn handle_pools_retiring_blockfrost( let retiring_pools_rest = retiring_pools .iter() .filter_map(|PoolRetirement { operator, epoch }| { - let pool_id = operator.to_bech32_with_hrp("pool").ok()?; + let pool_id = operator.to_bech32().ok()?; Some(PoolRetirementRest { pool_id, epoch: *epoch, @@ -418,7 +417,7 @@ async fn handle_pools_retiring_blockfrost( async fn handle_pools_spo_blockfrost( context: Arc>, - pool_operator: Vec, + pool_operator: PoolId, handlers_config: Arc, ) -> Result { // Get PoolRegistration from spo state @@ -669,7 +668,7 @@ async fn handle_pools_spo_blockfrost( let active_stakes_info = active_stakes_info?; let live_pledge = live_pledge?; - let pool_id = pool_info.operator.to_bech32_with_hrp("pool").unwrap(); + let pool_id = pool_info.operator.to_bech32()?; let reward_account = pool_info.reward_account.get_credential().to_stake_bech32(); let Ok(reward_account) = reward_account else { return Ok(RESTResponse::with_text(404, "Invalid Reward Account")); @@ -684,7 +683,7 @@ async fn handle_pools_spo_blockfrost( }; let pool_info_rest: PoolInfoRest = PoolInfoRest { pool_id, - hex: pool_info.operator, + hex: *pool_info.operator, vrf_key: pool_info.vrf_key_hash, blocks_minted: total_blocks_minted, blocks_epoch: epoch_blocks_minted, @@ -727,7 +726,7 @@ pub async fn handle_pool_history_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -801,7 +800,7 @@ pub async fn handle_pool_metadata_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -882,7 +881,7 @@ pub async fn handle_pool_relays_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -936,7 +935,7 @@ pub async fn handle_pool_delegators_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -1033,7 +1032,7 @@ pub async fn handle_pool_blocks_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -1085,7 +1084,7 @@ pub async fn handle_pool_updates_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), @@ -1145,7 +1144,7 @@ pub async fn handle_pool_votes_blockfrost( return Ok(RESTResponse::with_text(400, "Missing pool ID parameter")); }; - let Ok(spo) = Vec::::from_bech32_with_hrp(pool_id, "pool") else { + let Ok(spo) = PoolId::from_bech32(pool_id) else { return Ok(RESTResponse::with_text( 400, &format!("Invalid Bech32 stake pool ID: {pool_id}"), diff --git a/modules/rest_blockfrost/src/types.rs b/modules/rest_blockfrost/src/types.rs index 56d2eb02..f798c6c4 100644 --- a/modules/rest_blockfrost/src/types.rs +++ b/modules/rest_blockfrost/src/types.rs @@ -6,7 +6,7 @@ use acropolis_common::{ rest_helper::ToCheckedF64, serialization::{Bech32WithHrp, DisplayFromBech32, PoolPrefix}, AssetAddressEntry, AssetMetadataStandard, AssetMintRecord, KeyHash, PolicyAsset, - PoolEpochState, PoolUpdateAction, Relay, TxHash, Vote, + PoolEpochState, PoolId, PoolUpdateAction, Relay, TxHash, Vote, VrfKeyHash, }; use anyhow::Result; use num_traits::ToPrimitive; @@ -62,7 +62,7 @@ pub struct BlockInfoREST(pub BlockInfo); pub struct SPDDByEpochItemRest { pub stake_address: String, #[serde_as(as = "DisplayFromBech32")] - pub pool_id: KeyHash, + pub pool_id: PoolId, #[serde_as(as = "DisplayFromStr")] pub amount: u64, } @@ -430,7 +430,7 @@ pub struct PoolInfoRest { #[serde_as(as = "Hex")] pub hex: KeyHash, #[serde_as(as = "Hex")] - pub vrf_key: KeyHash, + pub vrf_key: VrfKeyHash, pub blocks_minted: u64, pub blocks_epoch: u64, #[serde_as(as = "DisplayFromStr")] diff --git a/modules/spdd_state/src/rest.rs b/modules/spdd_state/src/rest.rs index 4cc059dd..330a8f8a 100644 --- a/modules/spdd_state/src/rest.rs +++ b/modules/spdd_state/src/rest.rs @@ -1,5 +1,5 @@ use crate::state::State; -use acropolis_common::serialization::Bech32WithHrp; +use acropolis_common::serialization::Bech32Conversion; use acropolis_common::DelegatedStake; use acropolis_common::{extract_strict_query_params, messages::RESTResponse}; use anyhow::Result; @@ -33,12 +33,7 @@ pub async fn handle_spdd( if let Some(spdd) = spdd_opt { let spdd: HashMap = spdd .iter() - .map(|(k, v)| { - ( - k.to_bech32_with_hrp("pool").unwrap_or_else(|_| hex::encode(k)), - *v, - ) - }) + .map(|(k, v)| (k.to_bech32().unwrap_or_else(|_| hex::encode(k)), *v)) .collect(); match serde_json::to_string(&spdd) { diff --git a/modules/spdd_state/src/state.rs b/modules/spdd_state/src/state.rs index 178e12b0..80bf570e 100644 --- a/modules/spdd_state/src/state.rs +++ b/modules/spdd_state/src/state.rs @@ -1,12 +1,12 @@ use acropolis_common::{ state_history::{StateHistory, StateHistoryStore}, - DelegatedStake, KeyHash, + DelegatedStake, PoolId, }; use imbl::{OrdMap, OrdSet}; use tracing::info; pub struct State { - spdd_history: StateHistory>, + spdd_history: StateHistory>, } impl State { @@ -18,7 +18,7 @@ impl State { pub fn apply_spdd_snapshot(&mut self, epoch: u64, snapshot: I) where - I: IntoIterator, + I: IntoIterator, { let mut next = self.spdd_history.get_rolled_back_state(epoch); @@ -34,8 +34,7 @@ impl State { present.insert(k); } - let to_remove: Vec<_> = - next.keys().filter(|k| !present.contains::<[u8]>((**k).as_slice())).cloned().collect(); + let to_remove: Vec<_> = next.keys().filter(|k| !present.contains(*k)).cloned().collect(); for k in to_remove { next.remove(&k); } @@ -44,12 +43,12 @@ impl State { } #[allow(dead_code)] - pub fn get_latest(&self) -> Option<&OrdMap> { + pub fn get_latest(&self) -> Option<&OrdMap> { self.spdd_history.current() } #[allow(dead_code)] - pub fn get_epoch(&self, epoch: u64) -> Option<&OrdMap> { + pub fn get_epoch(&self, epoch: u64) -> Option<&OrdMap> { self.spdd_history.get_by_index(epoch) } diff --git a/modules/spo_state/src/epochs_history.rs b/modules/spo_state/src/epochs_history.rs index 29213277..bc57b506 100644 --- a/modules/spo_state/src/epochs_history.rs +++ b/modules/spo_state/src/epochs_history.rs @@ -2,9 +2,9 @@ use acropolis_common::messages::EpochActivityMessage; use acropolis_common::messages::SPORewardsMessage; use acropolis_common::messages::SPOStakeDistributionMessage; use acropolis_common::rational_number::RationalNumber; -use acropolis_common::BlockInfo; use acropolis_common::KeyHash; use acropolis_common::PoolEpochState; +use acropolis_common::{BlockInfo, PoolId}; use dashmap::DashMap; use rayon::prelude::*; use std::collections::BTreeMap; @@ -100,7 +100,7 @@ impl EpochsHistoryState { /// Return None if any of pool operators active stake is None pub fn get_pools_active_stakes( &self, - pool_operators: &Vec, + pool_operators: &Vec, epoch: u64, ) -> Option> { let epochs_history = self.epochs_history.as_ref()?; @@ -167,7 +167,7 @@ impl EpochsHistoryState { &self, _block: &BlockInfo, epoch_activity_message: &EpochActivityMessage, - spos: &[(KeyHash, usize)], + spos: &[(PoolId, usize)], ) { let Some(epochs_history) = self.epochs_history.as_ref() else { return; @@ -214,19 +214,22 @@ mod tests { #[test] fn get_pool_history_returns_none_when_spo_is_not_found() { + let key_hash = [1; 28].into(); let epochs_history = EpochsHistoryState::new(save_history_store_config()); - let pool_history = epochs_history.get_pool_history(&vec![1]); + let pool_history = epochs_history.get_pool_history(&key_hash); assert!(pool_history.is_none()); } #[test] fn get_pool_history_returns_data() { let epochs_history = EpochsHistoryState::new(save_history_store_config()); + let pool_id = [1; 28].into(); + let spo_block_key_hash = [2; 28].into(); let block = new_block(2); let mut spdd_msg = new_spdd_message(1); spdd_msg.spos = vec![( - vec![1], + pool_id, DelegatedStake { active: 1, active_delegators_count: 1, @@ -236,14 +239,14 @@ mod tests { epochs_history.handle_spdd(&block, &spdd_msg); let mut epoch_activity_msg = new_epoch_activity_message(1); - epoch_activity_msg.spo_blocks = vec![(vec![11], 1)]; + epoch_activity_msg.spo_blocks = vec![(spo_block_key_hash, 1)]; epoch_activity_msg.total_blocks = 1; epoch_activity_msg.total_fees = 10; - epochs_history.handle_epoch_activity(&block, &epoch_activity_msg, &[(vec![1], 1)]); + epochs_history.handle_epoch_activity(&block, &epoch_activity_msg, &vec![(pool_id, 1)]); let mut spo_rewards_msg = new_spo_rewards_message(1); spo_rewards_msg.spos = vec![( - vec![1], + pool_id, SPORewards { total_rewards: 100, operator_rewards: 10, @@ -251,7 +254,7 @@ mod tests { )]; epochs_history.handle_spo_rewards(&block, &spo_rewards_msg); - let pool_history = epochs_history.get_pool_history(&vec![1]).unwrap(); + let pool_history = epochs_history.get_pool_history(&pool_id).unwrap(); assert_eq!(2, pool_history.len()); let first_epoch = pool_history.first().unwrap(); let third_epoch = pool_history.get(1).unwrap(); diff --git a/modules/spo_state/src/retired_pools_history.rs b/modules/spo_state/src/retired_pools_history.rs index 00fe423b..37648cc1 100644 --- a/modules/spo_state/src/retired_pools_history.rs +++ b/modules/spo_state/src/retired_pools_history.rs @@ -1,6 +1,5 @@ -use acropolis_common::BlockInfo; -use acropolis_common::KeyHash; use acropolis_common::PoolRetirement; +use acropolis_common::{BlockInfo, PoolId}; use dashmap::DashMap; use std::sync::Arc; @@ -9,7 +8,7 @@ use crate::store_config::StoreConfig; #[derive(Debug, Clone)] pub struct RetiredPoolsHistoryState { /// keyed by epoch - retired_pools_history: Option>>>, + retired_pools_history: Option>>>, } impl RetiredPoolsHistoryState { @@ -54,7 +53,7 @@ impl RetiredPoolsHistoryState { /// Handle Retired SPOs /// Update retired_pools_history with deregistrations /// - pub fn handle_deregistrations(&self, block: &BlockInfo, retired_spos: &[KeyHash]) { + pub fn handle_deregistrations(&self, block: &BlockInfo, retired_spos: &[PoolId]) { let Some(retired_pools_history) = self.retired_pools_history.as_ref() else { return; }; @@ -91,14 +90,16 @@ mod tests { let state = RetiredPoolsHistoryState::new(save_retired_pools_store_config()); let block = new_block(2); - let retired_spos = vec![vec![1], vec![2]]; + let spo_1 = [1; 28].into(); + let spo_2 = [2; 28].into(); + let retired_spos = vec![spo_1, spo_2]; state.handle_deregistrations(&block, &retired_spos); let retired_pools = state.get_retired_pools(); assert_eq!(2, retired_pools.len()); assert_eq!(2, retired_pools[0].epoch); assert_eq!(2, retired_pools[1].epoch); - assert_eq!(vec![1], retired_pools[0].operator); - assert_eq!(vec![2], retired_pools[1].operator); + assert_eq!(spo_1, retired_pools[0].operator); + assert_eq!(spo_2, retired_pools[1].operator); } } diff --git a/modules/spo_state/src/spo_state.rs b/modules/spo_state/src/spo_state.rs index a3570165..f157ca00 100644 --- a/modules/spo_state/src/spo_state.rs +++ b/modules/spo_state/src/spo_state.rs @@ -13,7 +13,7 @@ use acropolis_common::{ }, rational_number::RationalNumber, state_history::{StateHistory, StateHistoryStore}, - BlockInfo, BlockStatus, Era, KeyHash, + BlockInfo, BlockStatus, Era, PoolId, }; use anyhow::Result; use caryatid_sdk::{module, Context, Module, Subscription}; @@ -284,7 +284,7 @@ impl SPOState { span.in_scope(|| { Self::check_sync(¤t_block, block_info); // update epochs_history - let spos: Vec<(KeyHash, usize)> = epoch_activity_message + let spos: Vec<(PoolId, usize)> = epoch_activity_message .spo_blocks .iter() .map(|(hash, count)| (hash.clone(), *count)) diff --git a/modules/spo_state/src/state.rs b/modules/spo_state/src/state.rs index 6abc878b..94b5ca4f 100644 --- a/modules/spo_state/src/state.rs +++ b/modules/spo_state/src/state.rs @@ -10,8 +10,8 @@ use acropolis_common::{ params::TECHNICAL_PARAMETER_POOL_RETIRE_MAX_EPOCH, queries::governance::VoteRecord, stake_addresses::StakeAddressMap, - BlockInfo, KeyHash, PoolMetadata, PoolRegistration, PoolRetirement, PoolUpdateEvent, Relay, - StakeAddress, TxCertificate, TxHash, TxIdentifier, Voter, VotingProcedures, + BlockInfo, KeyHash, PoolId, PoolMetadata, PoolRegistration, PoolRetirement, PoolUpdateEvent, + Relay, StakeAddress, TxCertificate, TxHash, TxIdentifier, Voter, VotingProcedures, }; use anyhow::Result; use imbl::HashMap; @@ -29,19 +29,19 @@ pub struct State { epoch: u64, - spos: HashMap, PoolRegistration>, + spos: HashMap, - pending_updates: HashMap, PoolRegistration>, + pending_updates: HashMap, - pending_deregistrations: HashMap>>, + pending_deregistrations: HashMap>, // Total blocks minted till block number // Keyed by pool_id - total_blocks_minted: HashMap, + total_blocks_minted: HashMap, /// historical spo state - /// keyed by pool operator id - historical_spos: Option>, + /// keyed by pool_id + historical_spos: Option>, /// stake_addresses (We save stake_addresses according to store_config) stake_addresses: Option>>, @@ -97,7 +97,7 @@ impl State { impl From for State { fn from(value: SPOState) -> Self { - let spos: HashMap = value.pools.into(); + let spos: HashMap = value.pools.into(); let pending_deregistrations = value.retiring.into_iter().fold(HashMap::new(), |mut acc, (key_hash, epoch)| { acc.entry(epoch).or_insert_with(Vec::new).push(key_hash); @@ -133,7 +133,7 @@ impl From<&State> for SPOState { key_hashes .iter() .map(|key_hash| (key_hash.clone(), *epoch)) - .collect::, u64)>>() + .collect::>() }) .collect(), } @@ -142,12 +142,12 @@ impl From<&State> for SPOState { impl State { #[allow(dead_code)] - pub fn get(&self, pool_id: &KeyHash) -> Option<&PoolRegistration> { + pub fn get(&self, pool_id: &PoolId) -> Option<&PoolRegistration> { self.spos.get(pool_id) } /// Get total blocks minted by pools - pub fn get_total_blocks_minted_by_pools(&self, pools_operators: &[KeyHash]) -> Vec { + pub fn get_total_blocks_minted_by_pools(&self, pools_operators: &[PoolId]) -> Vec { pools_operators .iter() .map(|pool_operator| *self.total_blocks_minted.get(pool_operator).unwrap_or(&0)) @@ -155,27 +155,27 @@ impl State { } /// Get total blocks minted by pool - pub fn get_total_blocks_minted_by_pool(&self, pool_operator: &KeyHash) -> u64 { + pub fn get_total_blocks_minted_by_pool(&self, pool_operator: &PoolId) -> u64 { *self.total_blocks_minted.get(pool_operator).unwrap_or(&0) } /// Get all Stake Pool operators' operator hashes - pub fn list_pool_operators(&self) -> Vec { + pub fn list_pool_operators(&self) -> Vec { self.spos.keys().cloned().collect() } /// Get all Stake Pool Operators' operator hashes and their registration information - pub fn list_pools_with_info(&self) -> Vec<(KeyHash, PoolRegistration)> { + pub fn list_pools_with_info(&self) -> Vec<(PoolId, PoolRegistration)> { self.spos.iter().map(|(k, v)| (k.clone(), v.clone())).collect() } /// Get pool metadata - pub fn get_pool_metadata(&self, pool_id: &KeyHash) -> Option { + pub fn get_pool_metadata(&self, pool_id: &PoolId) -> Option { self.spos.get(pool_id).and_then(|p| p.pool_metadata.clone()) } /// Get Pool Delegators - pub fn get_pool_delegators(&self, pool_operator: &KeyHash) -> Option> { + pub fn get_pool_delegators(&self, pool_operator: &PoolId) -> Option> { let stake_addresses = self.stake_addresses.as_ref()?; let historical_spos = self.historical_spos.as_ref()?; @@ -192,28 +192,28 @@ impl State { /// Get Blocks by Pool /// Return Vector of block heights /// Return None when store_blocks not enabled - pub fn get_blocks_by_pool(&self, pool_id: &KeyHash) -> Option> { + pub fn get_blocks_by_pool(&self, pool_id: &PoolId) -> Option> { self.historical_spos.as_ref()?.get(pool_id).and_then(|s| s.get_all_blocks()) } /// Get Blocks by Pool and Epoch /// Return None when store_blocks not enabled - pub fn get_blocks_by_pool_and_epoch(&self, pool_id: &KeyHash, epoch: u64) -> Option> { + pub fn get_blocks_by_pool_and_epoch(&self, pool_id: &PoolId, epoch: u64) -> Option> { self.historical_spos.as_ref()?.get(pool_id).and_then(|s| s.get_blocks_by_epoch(epoch)) } /// Get Pool Updates - pub fn get_pool_updates(&self, pool_id: &KeyHash) -> Option> { + pub fn get_pool_updates(&self, pool_id: &PoolId) -> Option> { self.historical_spos.as_ref()?.get(pool_id).and_then(|s| s.updates.clone()) } /// Get Pool Votes - pub fn get_pool_votes(&self, pool_id: &KeyHash) -> Option> { + pub fn get_pool_votes(&self, pool_id: &PoolId) -> Option> { self.historical_spos.as_ref()?.get(pool_id).and_then(|s| s.votes.clone()) } /// Get pool relay - pub fn get_pool_relays(&self, pool_id: &KeyHash) -> Option> { + pub fn get_pool_relays(&self, pool_id: &PoolId) -> Option> { self.spos.get(pool_id).map(|p| p.relays.clone()) } @@ -247,7 +247,7 @@ impl State { // Handle block's minting. pub fn handle_mint(&mut self, block_info: &BlockInfo, issuer_vkey: &[u8]) -> bool { - let pool_id = keyhash_224(issuer_vkey); + let pool_id = PoolId::from(keyhash_224(issuer_vkey)); if self.spos.get(&pool_id).is_none() { return false; } @@ -279,7 +279,7 @@ impl State { self.pending_updates.clear(); // Deregister any pending - let mut retired_spos: Vec = Vec::new(); + let mut retired_spos: Vec = Vec::new(); let deregistrations = self.pending_deregistrations.remove(&self.epoch); if let Some(deregistrations) = deregistrations { for dr in deregistrations { @@ -456,7 +456,7 @@ impl State { /// Record a stake delegation /// Update historical_spo_state's delegators - fn record_stake_delegation(&mut self, stake_address: &StakeAddress, spo: &KeyHash) { + fn record_stake_delegation(&mut self, stake_address: &StakeAddress, spo: &PoolId) { let Some(stake_addresses) = self.stake_addresses.as_ref() else { return; }; @@ -671,16 +671,25 @@ mod tests { use acropolis_common::{ state_history::{StateHistory, StateHistoryStore}, PoolRetirement, Ratio, StakeAddress, TxCertificate, TxCertificateWithPos, TxIdentifier, + VrfKeyHash, }; use tokio::sync::Mutex; + fn test_pool_id(byte: u8) -> PoolId { + [byte; 28].into() + } + + fn test_pool_id_from_bytes(bytes: &[u8]) -> PoolId { + keyhash_224(bytes).into() + } + fn default_pool_registration( - operator: Vec, - vrf_key_hash: Option>, + operator: PoolId, + vrf_key_hash: Option, ) -> PoolRegistration { PoolRegistration { - operator: operator.clone(), - vrf_key_hash: vrf_key_hash.unwrap_or_else(|| vec![0]), + operator, + vrf_key_hash: vrf_key_hash.unwrap_or_else(|| VrfKeyHash::default()), pledge: 0, cost: 0, margin: Ratio { @@ -697,7 +706,7 @@ mod tests { #[test] fn get_returns_none_on_empty_state() { let state = State::default(); - assert!(state.get(&vec![0]).is_none()); + assert!(state.get(&test_pool_id(0)).is_none()); } #[test] @@ -719,25 +728,27 @@ mod tests { async fn spo_gets_registered() { let mut state = State::default(); let mut msg = new_certs_msg(); + let pool_id = test_pool_id(0); msg.certificates.push(TxCertificateWithPos { - cert: TxCertificate::PoolRegistration(default_pool_registration(vec![0], None)), + cert: TxCertificate::PoolRegistration(default_pool_registration(pool_id, None)), tx_identifier: TxIdentifier::default(), cert_index: 1, }); let block = new_block(1); assert!(state.handle_tx_certs(&block, &msg).is_ok()); assert_eq!(1, state.spos.len()); - let spo = state.spos.get(&vec![0]); - assert!(spo.is_some()); + let spo = state.spos.get(&pool_id); + assert!(!spo.is_none()); } #[tokio::test] async fn pending_deregistration_gets_queued() { let mut state = State::default(); let mut msg = new_certs_msg(); + let pool_id = test_pool_id(0); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![0], + operator: pool_id, epoch: 1, }), tx_identifier: TxIdentifier::default(), @@ -750,7 +761,7 @@ mod tests { assert!(drs.is_some()); if let Some(drs) = drs { assert_eq!(1, drs.len()); - assert!(drs.contains(&vec![0])); + assert!(drs.contains(&pool_id)); } } @@ -759,9 +770,11 @@ mod tests { let mut state = State::default(); let mut block = new_block(0); let mut msg = new_certs_msg(); + let pool_id_0 = test_pool_id(0); + let pool_id_1 = test_pool_id(1); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![0], + operator: pool_id_0, epoch: 2, }), tx_identifier: TxIdentifier::default(), @@ -773,7 +786,7 @@ mod tests { msg = new_certs_msg(); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![1], + operator: pool_id_1, epoch: 2, }), tx_identifier: TxIdentifier::default(), @@ -786,8 +799,8 @@ mod tests { assert!(drs.is_some()); if let Some(drs) = drs { assert_eq!(2, drs.len()); - assert!(drs.contains(&vec![0u8])); - assert!(drs.contains(&vec![1u8])); + assert!(drs.contains(&pool_id_0)); + assert!(drs.contains(&pool_id_1)); } } @@ -800,9 +813,12 @@ mod tests { let mut state = history.lock().await.get_current_state(); let mut block = new_block(0); let mut msg = new_certs_msg(); + let pool_id_0 = test_pool_id(0); + let pool_id_1 = test_pool_id(1); + msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![0], + operator: pool_id_0, epoch: 2, }), tx_identifier: TxIdentifier::default(), @@ -816,7 +832,7 @@ mod tests { msg = new_certs_msg(); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![1], + operator: pool_id_1, epoch: 2, }), tx_identifier: TxIdentifier::default(), @@ -834,7 +850,7 @@ mod tests { assert!(drs.is_some()); if let Some(drs) = drs { assert_eq!(1, drs.len()); - assert!(drs.contains(&vec![0])); + assert!(drs.contains(&pool_id_0)); } } @@ -843,22 +859,23 @@ mod tests { let mut state = State::default(); let mut block = new_block(0); let mut msg = new_certs_msg(); + let pool_id = test_pool_id(0); msg.certificates.push(TxCertificateWithPos { - cert: TxCertificate::PoolRegistration(default_pool_registration(vec![0], None)), + cert: TxCertificate::PoolRegistration(default_pool_registration(pool_id, None)), tx_identifier: TxIdentifier::default(), cert_index: 0, }); assert!(state.handle_tx_certs(&block, &msg).is_ok()); assert_eq!(1, state.spos.len()); - let spo = state.spos.get(&vec![0u8]); + let spo = state.spos.get(&pool_id); assert!(spo.is_some()); block.number = 1; let mut msg = new_certs_msg(); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![0], + operator: pool_id, epoch: 1, }), tx_identifier: TxIdentifier::default(), @@ -882,14 +899,16 @@ mod tests { let mut state = history.lock().await.get_current_state(); let mut block = new_block(0); let mut msg = new_certs_msg(); + let pool_id = test_pool_id(0); + msg.certificates.push(TxCertificateWithPos { - cert: TxCertificate::PoolRegistration(default_pool_registration(vec![0], None)), + cert: TxCertificate::PoolRegistration(default_pool_registration(pool_id, None)), tx_identifier: TxIdentifier::default(), cert_index: 0, }); assert!(state.handle_tx_certs(&block, &msg).is_ok()); assert_eq!(1, state.spos.len()); - let spo = state.spos.get(&vec![0u8]); + let spo = state.spos.get(&pool_id); assert!(spo.is_some()); history.lock().await.commit(block.number, state); @@ -898,7 +917,7 @@ mod tests { msg = new_certs_msg(); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![0], + operator: pool_id, epoch: 1, }), tx_identifier: TxIdentifier::default(), @@ -921,7 +940,7 @@ mod tests { let mut state = history.lock().await.get_rolled_back_state(block.number); assert!(state.handle_tx_certs(&block, &msg).is_ok()); assert_eq!(1, state.spos.len()); - let spo = state.spos.get(&vec![0]); + let spo = state.spos.get(&pool_id); assert!(spo.is_some()); } @@ -936,9 +955,12 @@ mod tests { let mut state = State::default(); let mut block = new_block(0); let mut msg = new_certs_msg(); + let pool_id_0 = test_pool_id(0); + let pool_id_1 = test_pool_id(1); + msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![0], + operator: pool_id_0, epoch: 2, }), tx_identifier: TxIdentifier::default(), @@ -950,7 +972,7 @@ mod tests { msg = new_certs_msg(); msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRetirement(PoolRetirement { - operator: vec![1], + operator: pool_id_1, epoch: 3, }), tx_identifier: TxIdentifier::default(), @@ -960,17 +982,18 @@ mod tests { let mut retiring_pools = state.get_retiring_pools(); retiring_pools.sort_by_key(|p| p.epoch); assert_eq!(2, retiring_pools.len()); - assert_eq!(vec![0], retiring_pools[0].operator); + assert_eq!(pool_id_0, retiring_pools[0].operator); assert_eq!(2, retiring_pools[0].epoch); - assert_eq!(vec![1], retiring_pools[1].operator); + assert_eq!(pool_id_1, retiring_pools[1].operator); assert_eq!(3, retiring_pools[1].epoch); } #[test] fn get_total_blocks_minted_returns_zeros_when_state_is_new() { let state = State::default(); - assert_eq!(0, state.get_total_blocks_minted_by_pools(&[vec![0]])[0]); - assert_eq!(0, state.get_total_blocks_minted_by_pool(&vec![0])); + let pool_id = test_pool_id(0); + assert_eq!(0, state.get_total_blocks_minted_by_pools(&[pool_id])[0]); + assert_eq!(0, state.get_total_blocks_minted_by_pool(&pool_id)); } #[test] @@ -978,7 +1001,8 @@ mod tests { let mut state = State::new(&save_blocks_store_config()); let mut block = new_block(0); let mut msg = new_certs_msg(); - let spo_id = keyhash_224(&[1_u8]); + let spo_id = test_pool_id_from_bytes(&[1]); + msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRegistration(default_pool_registration(spo_id.clone(), None)), tx_identifier: TxIdentifier::default(), @@ -998,7 +1022,8 @@ mod tests { #[test] fn get_blocks_returns_none_when_blocks_not_enabled() { let state = State::default(); - assert!(state.get_blocks_by_pool(&vec![0]).is_none()); + let pool_id = test_pool_id(0); + assert!(state.get_blocks_by_pool(&pool_id).is_none()); } #[test] @@ -1013,7 +1038,14 @@ mod tests { let mut state = State::new(&save_blocks_store_config()); let mut block = new_block(0); let mut msg = new_certs_msg(); - let spo_id = keyhash_224(&[1_u8]); + let spo_id = test_pool_id_from_bytes(&[1]); + msg.certificates.push(TxCertificateWithPos { + cert: TxCertificate::PoolRegistration(default_pool_registration(spo_id.clone(), None)), + tx_identifier: TxIdentifier::default(), + cert_index: 0, + }); + let spo_id = test_pool_id_from_bytes(&[1]); + msg.certificates.push(TxCertificateWithPos { cert: TxCertificate::PoolRegistration(default_pool_registration(spo_id.clone(), None)), tx_identifier: TxIdentifier::default(), diff --git a/modules/stake_delta_filter/src/utils.rs b/modules/stake_delta_filter/src/utils.rs index 7855edd9..e920d159 100644 --- a/modules/stake_delta_filter/src/utils.rs +++ b/modules/stake_delta_filter/src/utils.rs @@ -401,12 +401,14 @@ pub fn process_message( #[cfg(test)] mod test { use crate::*; + use acropolis_common::hash::Hash; use acropolis_common::{ messages::AddressDeltasMessage, Address, AddressDelta, BlockHash, BlockInfo, BlockStatus, ByronAddress, Era, ShelleyAddress, ShelleyAddressDelegationPart, ShelleyAddressPaymentPart, ShelleyAddressPointer, StakeAddress, StakeCredential, UTxOIdentifier, ValueDelta, }; use bech32::{Bech32, Hrp}; + use pallas::ledger::addresses::{PaymentKeyHash, ScriptHash, StakeKeyHash}; fn parse_addr(s: &str) -> Result { let a = pallas::ledger::addresses::Address::from_bech32(s)?; @@ -427,6 +429,18 @@ mod test { } } + pub fn script_to_hash(pallas_hash: ScriptHash) -> Hash<28> { + pallas_hash.as_ref().try_into().unwrap() + } + + pub fn stake_to_hash(pallas_hash: StakeKeyHash) -> Hash<28> { + pallas_hash.as_ref().try_into().unwrap() + } + + pub fn payment_to_hash(pallas_hash: PaymentKeyHash) -> Hash<28> { + pallas_hash.as_ref().try_into().unwrap() + } + /// Derive our Address from a Pallas address // This is essentially a 1:1 mapping but makes the Message definitions independent // of Pallas @@ -442,20 +456,20 @@ mod test { payment: match shelley_address.payment() { addresses::ShelleyPaymentPart::Key(hash) => { - ShelleyAddressPaymentPart::PaymentKeyHash(hash.to_vec()) + ShelleyAddressPaymentPart::PaymentKeyHash(payment_to_hash(*hash)) } addresses::ShelleyPaymentPart::Script(hash) => { - ShelleyAddressPaymentPart::ScriptHash(hash.to_vec()) + ShelleyAddressPaymentPart::ScriptHash(script_to_hash(*hash)) } }, delegation: match shelley_address.delegation() { addresses::ShelleyDelegationPart::Null => ShelleyAddressDelegationPart::None, addresses::ShelleyDelegationPart::Key(hash) => { - ShelleyAddressDelegationPart::StakeKeyHash(hash.to_vec()) + ShelleyAddressDelegationPart::StakeKeyHash(stake_to_hash(*hash)) } addresses::ShelleyDelegationPart::Script(hash) => { - ShelleyAddressDelegationPart::ScriptHash(hash.to_vec()) + ShelleyAddressDelegationPart::ScriptHash(script_to_hash(*hash)) } addresses::ShelleyDelegationPart::Pointer(pointer) => { ShelleyAddressDelegationPart::Pointer(ShelleyAddressPointer { @@ -471,10 +485,10 @@ mod test { network: map_network(stake_address.network())?, credential: match stake_address.payload() { addresses::StakePayload::Stake(hash) => { - StakeCredential::AddrKeyHash(hash.to_vec()) + StakeCredential::AddrKeyHash(stake_to_hash(*hash)) } addresses::StakePayload::Script(hash) => { - StakeCredential::ScriptHash(hash.to_vec()) + StakeCredential::ScriptHash(script_to_hash(*hash)) } }, })), diff --git a/modules/tx_unpacker/src/tx_unpacker.rs b/modules/tx_unpacker/src/tx_unpacker.rs index c42afcc3..291dfcfe 100644 --- a/modules/tx_unpacker/src/tx_unpacker.rs +++ b/modules/tx_unpacker/src/tx_unpacker.rs @@ -49,16 +49,23 @@ impl TxUnpacker { enactment_epoch: epoch, }; - for (hash, vote) in proposals.iter() { + for (hash_bytes, vote) in proposals.iter() { + let hash = match GenesisKeyhash::try_from(hash_bytes.as_ref()) { + Ok(h) => h, + Err(e) => { + error!("Invalid genesis keyhash in protocol parameter update: {e}"); + continue; + } + }; + match map(vote) { - Ok(upd) => update.proposals.push((hash.to_vec(), upd)), - Err(e) => error!("Cannot convert alonzo protocol param update {vote:?}: {e}"), + Ok(upd) => update.proposals.push((hash, upd)), + Err(e) => error!("Cannot convert protocol param update {vote:?}: {e}"), } } dest.push(update); } - /// Main init function pub async fn init(&self, context: Arc>, config: Arc) -> Result<()> { // Get configuration