Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(platform): fixed Platform State deserialization issue #2227

Merged
merged 5 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions packages/rs-dpp/src/core_types/validator/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ impl Encode for ValidatorV0 {
// Encode each field in the order they appear in the struct

// Encode ProTxHash
self.pro_tx_hash.to_byte_array().to_vec().encode(encoder)?;
let pro_tx_hash_bytes = self.pro_tx_hash.as_byte_array();
pro_tx_hash_bytes.encode(encoder)?;

// Encode Option<BlsPublicKey>
match &self.public_key {
Expand All @@ -64,7 +65,8 @@ impl Encode for ValidatorV0 {
self.node_ip.encode(encoder)?;

// Encode node_id
self.node_id.to_byte_array().to_vec().encode(encoder)?;
let node_id_bytes = self.node_id.as_byte_array();
node_id_bytes.encode(encoder)?;

// Encode core_port, platform_http_port, and platform_p2p_port as u16
self.core_port.encode(encoder)?;
Expand All @@ -84,14 +86,14 @@ impl Decode for ValidatorV0 {
// Decode each field in the same order as they were encoded

// Decode ProTxHash
let pro_tx_hash_bytes = Vec::<u8>::decode(decoder)?;
let pro_tx_hash_bytes = <[u8; 32]>::decode(decoder)?;
let pro_tx_hash = ProTxHash::from_slice(&pro_tx_hash_bytes)
.map_err(|_| DecodeError::OtherString("Failed to decode ProTxHash".to_string()))?;

// Decode Option<BlsPublicKey>
let has_public_key = bool::decode(decoder)?;
let public_key = if has_public_key {
let public_key_bytes = Vec::<u8>::decode(decoder)?;
let public_key_bytes = <[u8; 48]>::decode(decoder)?;
Some(BlsPublicKey::from_bytes(&public_key_bytes).map_err(|_| {
DecodeError::OtherString("Failed to decode BlsPublicKey".to_string())
})?)
Expand All @@ -103,7 +105,7 @@ impl Decode for ValidatorV0 {
let node_ip = String::decode(decoder)?;

// Decode node_id
let node_id_bytes = Vec::<u8>::decode(decoder)?;
let node_id_bytes = <[u8; 20]>::decode(decoder)?;
let node_id = PubkeyHash::from_slice(&node_id_bytes)
.map_err(|_| DecodeError::OtherString("Failed to decode NodeId".to_string()))?;

Expand Down Expand Up @@ -250,3 +252,45 @@ impl ValidatorV0Setters for ValidatorV0 {
self.is_banned = is_banned;
}
}

#[cfg(test)]
mod tests {
use super::*;
use bincode::config;

#[test]
fn test_serialize_deserialize_validator_v0() {
// Sample data for testing
let pro_tx_hash = ProTxHash::from_slice(&[1; 32]).unwrap();
let public_key = Some(BlsPublicKey::generate());
let node_ip = "127.0.0.1".to_string();
let node_id = PubkeyHash::from_slice(&[3; 20]).unwrap();
let core_port = 9999;
let platform_http_port = 8888;
let platform_p2p_port = 7777;
let is_banned = false;

// Create a ValidatorV0 instance
let validator = ValidatorV0 {
pro_tx_hash,
public_key,
node_ip,
node_id,
core_port,
platform_http_port,
platform_p2p_port,
is_banned,
};

// Serialize the ValidatorV0 instance
let encoded = bincode::encode_to_vec(&validator, config::standard()).unwrap();

// Deserialize the data back into a ValidatorV0 instance
let decoded: ValidatorV0 = bincode::decode_from_slice(&encoded, config::standard())
.unwrap()
.0;

// Verify that the deserialized instance matches the original instance
assert_eq!(validator, decoded);
}
}
94 changes: 77 additions & 17 deletions packages/rs-dpp/src/core_types/validator_set/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl Display for ValidatorSetV0 {
impl Encode for ValidatorSetV0 {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
// Encode each field in the order they appear in the struct
let quorum_hash_bytes = self.quorum_hash.as_byte_array().to_vec();
let quorum_hash_bytes = self.quorum_hash.as_byte_array();
quorum_hash_bytes.encode(encoder)?;
self.quorum_index.encode(encoder)?;
self.core_height.encode(encoder)?;
Expand All @@ -85,7 +85,7 @@ impl Encode for ValidatorSetV0 {

// Custom encoding for BlsPublicKey if needed
// Assuming BlsPublicKey can be serialized to a byte slice
let public_key_bytes = self.threshold_public_key.to_bytes();
let public_key_bytes = *self.threshold_public_key.to_bytes();
public_key_bytes.encode(encoder)?;
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved

Ok(())
Expand All @@ -95,8 +95,8 @@ impl Encode for ValidatorSetV0 {
#[cfg(feature = "core-types-serialization")]
impl Decode for ValidatorSetV0 {
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
// Decode each field in the same order as they were encoded
let quorum_hash = Vec::<u8>::decode(decoder)?;
// Decode the quorum hash directly as a [u8; 32] array
let quorum_hash = <[u8; 32]>::decode(decoder)?;
let quorum_index = Option::<u32>::decode(decoder)?;
let core_height = u32::decode(decoder)?;

Expand All @@ -114,17 +114,16 @@ impl Decode for ValidatorSetV0 {
})
.collect::<Result<_, bincode::error::DecodeError>>()?;

// Custom decoding for BlsPublicKey if needed
// Assuming BlsPublicKey can be deserialized from a byte slice
let public_key_bytes = Vec::<u8>::decode(decoder)?;
// Decode the [u8; 48] directly
let mut public_key_bytes = [0u8; 48];
let bytes = <[u8; 48]>::decode(decoder)?;
public_key_bytes.copy_from_slice(&bytes);
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved
let threshold_public_key = BlsPublicKey::from_bytes(&public_key_bytes).map_err(|_| {
bincode::error::DecodeError::OtherString("Failed to decode BlsPublicKey".to_string())
})?;

Ok(ValidatorSetV0 {
quorum_hash: QuorumHash::from_slice(&quorum_hash).map_err(|_| {
bincode::error::DecodeError::OtherString("Failed to decode QuorumHash".to_string())
})?,
quorum_hash: QuorumHash::from_byte_array(quorum_hash),
quorum_index,
core_height,
members,
Expand All @@ -138,8 +137,8 @@ impl<'de> BorrowDecode<'de> for ValidatorSetV0 {
fn borrow_decode<D: Decoder>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
// Decode each field in the same order as they were encoded

// Decode quorum_hash as Vec<u8>
let quorum_hash = Vec::<u8>::decode(decoder)?;
// Decode the quorum hash directly as a [u8; 32] array
let quorum_hash = <[u8; 32]>::decode(decoder)?;
// Decode quorum_index as Option<u32>
let quorum_index = Option::<u32>::decode(decoder)?;
// Decode core_height as u32
Expand All @@ -160,15 +159,17 @@ impl<'de> BorrowDecode<'de> for ValidatorSetV0 {
.collect::<Result<_, bincode::error::DecodeError>>()?;

// Custom decoding for BlsPublicKey if needed
let public_key_bytes = Vec::<u8>::decode(decoder)?;
let mut public_key_bytes = [0u8; 48];
let bytes = <[u8; 48]>::decode(decoder)?;
public_key_bytes.copy_from_slice(&bytes);
let threshold_public_key = BlsPublicKey::from_bytes(&public_key_bytes).map_err(|_| {
bincode::error::DecodeError::OtherString("Failed to decode BlsPublicKey".to_string())
bincode::error::DecodeError::OtherString(
"Failed to decode BlsPublicKey in borrow decode".to_string(),
)
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved
})?;

Ok(ValidatorSetV0 {
quorum_hash: QuorumHash::from_slice(&quorum_hash).map_err(|_| {
bincode::error::DecodeError::OtherString("Failed to decode QuorumHash".to_string())
})?,
quorum_hash: QuorumHash::from_byte_array(quorum_hash),
quorum_index,
core_height,
members,
Expand Down Expand Up @@ -278,3 +279,62 @@ impl ValidatorSetV0Setters for ValidatorSetV0 {
self.threshold_public_key = threshold_public_key;
}
}

#[cfg(test)]
mod tests {
use super::*;
use bincode::config;
use dashcore::PubkeyHash;
use std::collections::BTreeMap;

#[test]
fn test_serialize_deserialize_validator_set_v0() {
// Sample data for testing
let quorum_hash = QuorumHash::from_slice(&[1; 32]).unwrap();
let quorum_index = Some(42);
let core_height = 1000;

// Create a sample ProTxHash and ValidatorV0 instance
let pro_tx_hash = ProTxHash::from_slice(&[2; 32]).unwrap();
let public_key = Some(BlsPublicKey::generate());
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved
let node_ip = "192.168.1.1".to_string();
let node_id = PubkeyHash::from_slice(&[4; 20]).unwrap();
let validator = ValidatorV0 {
pro_tx_hash: pro_tx_hash.clone(),
public_key,
node_ip,
node_id,
core_port: 8080,
platform_http_port: 9090,
platform_p2p_port: 10010,
is_banned: false,
};

// Create a BTreeMap with one entry for the ValidatorSetV0
let mut members = BTreeMap::new();
members.insert(pro_tx_hash, validator);

// Create a sample threshold public key
let threshold_public_key = BlsPublicKey::generate();

// Create the ValidatorSetV0 instance
let validator_set = ValidatorSetV0 {
quorum_hash,
quorum_index,
core_height,
members,
threshold_public_key,
};

// Serialize the ValidatorSetV0 instance
let encoded = bincode::encode_to_vec(&validator_set, config::standard()).unwrap();

// Deserialize the data back into a ValidatorSetV0 instance
let decoded: ValidatorSetV0 = bincode::decode_from_slice(&encoded, config::standard())
.unwrap()
.0;

// Verify that the deserialized instance matches the original instance
assert_eq!(validator_set, decoded);
}
}
61 changes: 23 additions & 38 deletions packages/rs-dpp/src/fee/default_costs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,26 @@
// MIT LICENSE
//
// Copyright (c) 2021 Dash Core Group
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without
// limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice
// shall be included in all copies or substantial portions
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

//! Fee costs
//!
//! Fee costs for Known Platform operations
//!

use crate::block::epoch::{Epoch, EpochIndex};
use crate::fee::Credits;
use platform_version::version::fee::FeeVersion;
use platform_version::version::PlatformVersion;
use platform_version::version::fee::{
FeeVersion, FeeVersionFieldsBeforeVersion4, FeeVersionNumber,
};
use std::collections::BTreeMap;

pub mod constants;

pub type CachedEpochIndexFeeVersions = BTreeMap<EpochIndex, FeeVersion>;
pub type CachedEpochIndexFeeVersions = BTreeMap<EpochIndex, &'static FeeVersion>;
pub type EpochIndexFeeVersionsForStorage = BTreeMap<EpochIndex, FeeVersionNumber>;

// This is type only meant for deserialization because of an issue
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved
// The issue was that the platform state was stored with FeeVersions in it before version 1.4
// When we would add new fields we would be unable to deserialize
// This FeeProcessingVersionFieldsBeforeVersion4 is how things were before version 1.4 was released
pub type CachedEpochIndexFeeVersionsFieldsBeforeVersion4 =
BTreeMap<EpochIndex, FeeVersionFieldsBeforeVersion4>;

/// A Known Cost Item is an item that changes costs depending on the Epoch
#[derive(Eq, PartialEq, Copy, Clone, Hash)]
Expand Down Expand Up @@ -143,7 +123,10 @@ impl KnownCostItem {
pub trait EpochCosts {
/// Get the closest epoch in the past that has a cost table
/// This is where the base costs last changed
fn active_fee_version(&self, cached_fee_version: &CachedEpochIndexFeeVersions) -> FeeVersion;
fn active_fee_version(
&self,
cached_fee_version: &CachedEpochIndexFeeVersions,
) -> &'static FeeVersion;
/// Get the cost for the known cost item
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved
fn cost_for_known_cost_item(
&self,
Expand All @@ -154,18 +137,20 @@ pub trait EpochCosts {

impl EpochCosts for Epoch {
/// Get the active fee version for an epoch
fn active_fee_version(&self, cached_fee_version: &CachedEpochIndexFeeVersions) -> FeeVersion {
fn active_fee_version(
&self,
cached_fee_version: &CachedEpochIndexFeeVersions,
) -> &'static FeeVersion {
// If the exact EpochIndex is matching to a FeeVersion update
if let Some(fee_version) = cached_fee_version.get(&self.index) {
return fee_version.clone();
return fee_version;
}
// else return the FeeVersion at lower adjacent EpochIndex (if available, else the FeeVersion of first PlatformVersion)
cached_fee_version
.range(..=self.index)
.next_back()
.map(|(_, fee_version)| fee_version)
.unwrap_or_else(|| &PlatformVersion::first().fee_version)
.clone()
.map(|(_, fee_version)| *fee_version)
.unwrap_or_else(|| FeeVersion::first())
}
QuantumExplorer marked this conversation as resolved.
Show resolved Hide resolved

/// Get the cost for the known cost item
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-dpp/src/fee/fee_result/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,10 @@ impl IntoIterator for FeeRefunds {
mod tests {
use super::*;
use once_cell::sync::Lazy;
use platform_version::version::PlatformVersion;
use platform_version::version::fee::FeeVersion;

static EPOCH_CHANGE_FEE_VERSION_TEST: Lazy<CachedEpochIndexFeeVersions> =
Lazy::new(|| BTreeMap::from([(0, PlatformVersion::first().fee_version.clone())]));
Lazy::new(|| BTreeMap::from([(0, FeeVersion::first())]));

mod from_storage_removal {
use super::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ mod refund_tests {

platform_state
.previous_fee_versions_mut()
.insert(5, platform_version_with_higher_fees.fee_version.clone());
.insert(5, platform_version_with_higher_fees.fee_version.as_static());

let (mut fee_results, _) = process_state_transitions(
&platform,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,19 @@ impl<C> Platform<C> {
// If cached_fee_version is non-empty
if let Some((_, last_fee_version)) = previous_fee_versions_map.iter().last() {
// Insert the new (epoch_index, fee_version) only if the new fee_version is different from the last_fee_version.
if *last_fee_version != platform_version.fee_version {
if last_fee_version.fee_version_number
!= platform_version.fee_version.fee_version_number
{
previous_fee_versions_map.insert(
epoch_info.current_epoch_index(),
platform_version.fee_version.clone(),
&platform_version.fee_version,
);
}
// In case of empty cached_fee_version, insert the new (epoch_index, fee_version)
} else {
previous_fee_versions_map.insert(
epoch_info.current_epoch_index(),
platform_version.fee_version.clone(),
&platform_version.fee_version,
);
}

Expand Down
Loading
Loading