diff --git a/execution_engine_testing/test_support/src/wasm_test_builder.rs b/execution_engine_testing/test_support/src/wasm_test_builder.rs index 21ff75c465..387f51fb9f 100644 --- a/execution_engine_testing/test_support/src/wasm_test_builder.rs +++ b/execution_engine_testing/test_support/src/wasm_test_builder.rs @@ -1751,7 +1751,7 @@ where .expect("should have named keys") } - /// Gets [`Vec`]. + /// Gets [`BTreeMap`]. pub fn get_unbonds(&mut self) -> BTreeMap { let state_root_hash = self.get_post_state_hash(); diff --git a/node/src/components/rest_server.rs b/node/src/components/rest_server.rs index e6b0d23d61..bc8e786ab3 100644 --- a/node/src/components/rest_server.rs +++ b/node/src/components/rest_server.rs @@ -366,10 +366,8 @@ mod schema_tests { "{}/../resources/test/rest_schema_status.json", env!("CARGO_MANIFEST_DIR") ); - assert_schema( - schema_path, - serde_json::to_string_pretty(&schema_for!(GetStatusResult)).unwrap(), - ); + let pretty = serde_json::to_string_pretty(&schema_for!(GetStatusResult)).unwrap(); + assert_schema(schema_path, pretty); } #[test] diff --git a/node/src/reactor/main_reactor/tests/transactions.rs b/node/src/reactor/main_reactor/tests/transactions.rs index bf0e36b725..a79fc7289d 100644 --- a/node/src/reactor/main_reactor/tests/transactions.rs +++ b/node/src/reactor/main_reactor/tests/transactions.rs @@ -2948,7 +2948,6 @@ async fn add_and_withdraw_bid_transaction() { let (_, _bob_initial_balance, _) = test.get_balances(None); let (_txn_hash, _block_height, exec_result) = test.send_transaction(txn).await; - println!("{:?}", exec_result); assert!(exec_result_is_success(&exec_result)); test.fixture diff --git a/resources/test/sse_data_schema.json b/resources/test/sse_data_schema.json index 66ecbb2d34..f67455d8ff 100644 --- a/resources/test/sse_data_schema.json +++ b/resources/test/sse_data_schema.json @@ -2392,15 +2392,15 @@ "type": "object", "required": [ "amount", - "delegator_public_key", + "delegator_kind", "validator_public_key" ], "properties": { - "delegator_public_key": { + "delegator_kind": { "description": "Delegator's public key", "allOf": [ { - "$ref": "#/definitions/PublicKey" + "$ref": "#/definitions/DelegatorKind" } ] }, @@ -2428,6 +2428,44 @@ } ] }, + "DelegatorKind": { + "description": "Auction bid variants. Kinds of delegation bids.", + "oneOf": [ + { + "description": "Delegation from public key.", + "type": "object", + "required": [ + "PublicKey" + ], + "properties": { + "PublicKey": { + "$ref": "#/definitions/PublicKey" + } + }, + "additionalProperties": false + }, + { + "description": "Delegation from purse.", + "type": "object", + "required": [ + "Purse" + ], + "properties": { + "Purse": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "maxItems": 32, + "minItems": 32 + } + }, + "additionalProperties": false + } + ] + }, "TransferV1": { "description": "Represents a version 1 transfer from one purse to another.", "type": "object", @@ -2565,7 +2603,7 @@ ] }, "delegators": { - "description": "This validator's delegators, indexed by their public keys.", + "description": "This validator's delegators, indexed by their kind.", "allOf": [ { "$ref": "#/definitions/Array_of_PublicKeyAndDelegator" @@ -2622,7 +2660,7 @@ "description": "The public key of the delegator.", "allOf": [ { - "$ref": "#/definitions/PublicKey" + "$ref": "#/definitions/DelegatorKind" } ] }, @@ -2630,24 +2668,24 @@ "description": "The delegator details.", "allOf": [ { - "$ref": "#/definitions/Delegator" + "$ref": "#/definitions/DelegatorBid" } ] } } }, - "Delegator": { + "DelegatorBid": { "description": "Represents a party delegating their stake to a validator (or \"delegatee\")", "type": "object", "required": [ "bonding_purse", - "delegator_public_key", + "delegator_kind", "staked_amount", "validator_public_key" ], "properties": { - "delegator_public_key": { - "$ref": "#/definitions/PublicKey" + "delegator_kind": { + "$ref": "#/definitions/DelegatorKind" }, "staked_amount": { "$ref": "#/definitions/U512" @@ -2862,7 +2900,7 @@ ], "properties": { "Delegator": { - "$ref": "#/definitions/Delegator" + "$ref": "#/definitions/DelegatorBid" } }, "additionalProperties": false @@ -2905,6 +2943,19 @@ } }, "additionalProperties": false + }, + { + "description": "Unbond", + "type": "object", + "required": [ + "Unbond" + ], + "properties": { + "Unbond": { + "$ref": "#/definitions/Unbond" + } + }, + "additionalProperties": false } ] }, @@ -3065,20 +3116,20 @@ "type": "object", "required": [ "delegation_rate", - "delegator_public_key", + "delegator_kind", "validator_public_key" ], "properties": { - "delegator_public_key": { - "description": "Delegator public key", + "delegator_kind": { + "description": "Delegator kind.", "allOf": [ { - "$ref": "#/definitions/PublicKey" + "$ref": "#/definitions/DelegatorKind" } ] }, "validator_public_key": { - "description": "Validator public key", + "description": "Validator public key.", "allOf": [ { "$ref": "#/definitions/PublicKey" @@ -3086,7 +3137,7 @@ ] }, "delegation_rate": { - "description": "Individual delegation rate", + "description": "Individual delegation rate.", "type": "integer", "format": "uint8", "minimum": 0.0 @@ -3094,6 +3145,135 @@ }, "additionalProperties": false }, + "Unbond": { + "type": "object", + "required": [ + "eras", + "unbond_kind", + "validator_public_key" + ], + "properties": { + "validator_public_key": { + "description": "Validators public key.", + "allOf": [ + { + "$ref": "#/definitions/PublicKey" + } + ] + }, + "unbond_kind": { + "description": "Unbond kind.", + "allOf": [ + { + "$ref": "#/definitions/UnbondKind" + } + ] + }, + "eras": { + "description": "Unbond amounts per era.", + "type": "array", + "items": { + "$ref": "#/definitions/UnbondEra" + } + } + }, + "additionalProperties": false + }, + "UnbondKind": { + "description": "Unbond variants.", + "oneOf": [ + { + "type": "object", + "required": [ + "Validator" + ], + "properties": { + "Validator": { + "$ref": "#/definitions/PublicKey" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "DelegatedPublicKey" + ], + "properties": { + "DelegatedPublicKey": { + "$ref": "#/definitions/PublicKey" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "DelegatedPurse" + ], + "properties": { + "DelegatedPurse": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "maxItems": 32, + "minItems": 32 + } + }, + "additionalProperties": false + } + ] + }, + "UnbondEra": { + "description": "Unbond amounts per era.", + "type": "object", + "required": [ + "amount", + "bonding_purse", + "era_of_creation" + ], + "properties": { + "bonding_purse": { + "description": "Bonding Purse", + "allOf": [ + { + "$ref": "#/definitions/URef" + } + ] + }, + "era_of_creation": { + "description": "Era in which this unbonding request was created.", + "allOf": [ + { + "$ref": "#/definitions/EraId" + } + ] + }, + "amount": { + "description": "Unbonding Amount.", + "allOf": [ + { + "$ref": "#/definitions/U512" + } + ] + }, + "new_validator": { + "description": "The validator public key to re-delegate to.", + "anyOf": [ + { + "$ref": "#/definitions/PublicKey" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, "ExecutionResultV2": { "description": "The result of executing a single transaction.", "type": "object", diff --git a/storage/src/block_store/lmdb/lmdb_ext.rs b/storage/src/block_store/lmdb/lmdb_ext.rs index 1e8bbc4f6a..3f965f1270 100644 --- a/storage/src/block_store/lmdb/lmdb_ext.rs +++ b/storage/src/block_store/lmdb/lmdb_ext.rs @@ -17,7 +17,7 @@ use serde::de::DeserializeOwned; #[cfg(test)] use serde::Serialize; use thiserror::Error; -use tracing::warn; +use tracing::{error, warn}; use crate::block_store::types::{ApprovalsHashes, DeployMetadataV1}; use casper_types::{ @@ -103,7 +103,7 @@ pub(super) trait TransactionExt { /// Helper function to load a value from a database using the `bytesrepr` `ToBytes`/`FromBytes` /// serialization. - fn get_value_bytesrepr( + fn get_value_bytesrepr( &self, db: Database, key: &K, @@ -175,7 +175,7 @@ where } #[inline] - fn get_value_bytesrepr( + fn get_value_bytesrepr( &self, db: Database, key: &K, @@ -183,7 +183,13 @@ where let serialized_key = serialize_bytesrepr(key)?; match self.get(db, &serialized_key) { // Deserialization failures are likely due to storage corruption. - Ok(raw) => deserialize_bytesrepr(raw).map(Some), + Ok(raw) => match deserialize_bytesrepr(raw) { + Ok(ret) => Ok(Some(ret)), + Err(err) => { + error!(%key, %err, raw_len = raw.len(), "get_value_bytesrepr deserialization"); + Err(err) + } + }, Err(lmdb::Error::NotFound) => Ok(None), Err(err) => Err(err.into()), } @@ -368,10 +374,40 @@ pub(super) fn serialize_unbonding_purse(value: &T) -> Result(raw: &[u8]) -> Result { - T::from_bytes(raw) - .map(|val| val.0) - .map_err(|err| LmdbExtError::DataCorrupted(Box::new(BytesreprError(err)))) +pub(super) fn deserialize_bytesrepr(raw: &[u8]) -> Result { + match T::from_bytes(raw).map(|val| val.0) { + Ok(ret) => Ok(ret), + Err(err) => { + // unfortunately, type_name is unstable + let type_name = { + if TypeId::of::() == TypeId::of::() { + "DeployMetadataV1".to_string() + } else if TypeId::of::() == TypeId::of::() { + "BlockHeader".to_string() + } else if TypeId::of::() == TypeId::of::() { + "BlockBody".to_string() + } else if TypeId::of::() == TypeId::of::() { + "BlockSignatures".to_string() + } else if TypeId::of::() == TypeId::of::() { + "DeployHash".to_string() + } else if TypeId::of::() == TypeId::of::() { + "Deploy".to_string() + } else if TypeId::of::() == TypeId::of::() { + "ApprovalsHashes".to_string() + } else if TypeId::of::>() == TypeId::of::() { + "BTreeSet".to_string() + } else if TypeId::of::() == TypeId::of::() { + "ExecutionResult".to_string() + } else if TypeId::of::>() == TypeId::of::() { + "Transfers".to_string() + } else { + format!("{:?}", TypeId::of::()) + } + }; + error!("deserialize_bytesrepr failed to deserialize: {}", type_name); + Err(LmdbExtError::DataCorrupted(Box::new(BytesreprError(err)))) + } + } } /// Serializes into a buffer. diff --git a/storage/src/block_store/lmdb/versioned_databases.rs b/storage/src/block_store/lmdb/versioned_databases.rs index 3cc6e1ebb6..aecd297915 100644 --- a/storage/src/block_store/lmdb/versioned_databases.rs +++ b/storage/src/block_store/lmdb/versioned_databases.rs @@ -7,6 +7,7 @@ use serde::de::DeserializeOwned; #[cfg(test)] use serde::Serialize; use std::{collections::BTreeSet, marker::PhantomData}; +use tracing::error; use casper_types::{ bytesrepr::{FromBytes, ToBytes}, @@ -126,8 +127,8 @@ impl Copy for VersionedDatabases {} impl VersionedDatabases where - K: VersionedKey, - V: VersionedValue, + K: VersionedKey + std::fmt::Display, + V: VersionedValue + 'static, { pub(super) fn new( env: &Environment, @@ -156,8 +157,15 @@ where txn: &Tx, key: &K, ) -> Result, LmdbExtError> { - if let Some(value) = txn.get_value_bytesrepr(self.current, key)? { - return Ok(Some(value)); + match txn.get_value_bytesrepr(self.current, key) { + Ok(Some(value)) => return Ok(Some(value)), + Ok(None) => { + // check legacy db + } + Err(err) => { + error!(%err, "versioned_database: failed to retrieve record from current db"); + return Err(err); + } } let legacy_key = match key.legacy_key() { diff --git a/types/src/execution/execution_result.rs b/types/src/execution/execution_result.rs index 4c1aa29ade..2ac76f3411 100644 --- a/types/src/execution/execution_result.rs +++ b/types/src/execution/execution_result.rs @@ -9,6 +9,7 @@ use rand::Rng; #[cfg(feature = "json-schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use tracing::error; use super::{ExecutionResultV1, ExecutionResultV2}; #[cfg(any(feature = "testing", test))] @@ -109,7 +110,16 @@ impl ToBytes for ExecutionResult { impl FromBytes for ExecutionResult { fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { - let (tag, remainder) = u8::from_bytes(bytes)?; + if bytes.is_empty() { + error!("FromBytes for ExecutionResult: bytes length should not be 0"); + } + let (tag, remainder) = match u8::from_bytes(bytes) { + Ok((tag, rem)) => (tag, rem), + Err(err) => { + error!(%err, "FromBytes for ExecutionResult"); + return Err(err); + } + }; match tag { V1_TAG => { let (result, remainder) = ExecutionResultV1::from_bytes(remainder)?; @@ -119,7 +129,10 @@ impl FromBytes for ExecutionResult { let (result, remainder) = ExecutionResultV2::from_bytes(remainder)?; Ok((ExecutionResult::V2(result), remainder)) } - _ => Err(bytesrepr::Error::Formatting), + _ => { + error!(%tag, rem_len = remainder.len(), "FromBytes for ExecutionResult: unknown tag"); + Err(bytesrepr::Error::Formatting) + } } } } diff --git a/types/src/execution/transform.rs b/types/src/execution/transform.rs index 35b91f2a19..aab03c7a12 100644 --- a/types/src/execution/transform.rs +++ b/types/src/execution/transform.rs @@ -5,6 +5,7 @@ use datasize::DataSize; #[cfg(feature = "json-schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use tracing::error; use super::TransformKindV2; use crate::{ @@ -45,11 +46,6 @@ impl TransformV2 { } impl ToBytes for TransformV2 { - fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { - self.key.write_bytes(writer)?; - self.kind.write_bytes(writer) - } - fn to_bytes(&self) -> Result, bytesrepr::Error> { let mut buffer = bytesrepr::allocate_buffer(self)?; self.write_bytes(&mut buffer)?; @@ -59,12 +55,34 @@ impl ToBytes for TransformV2 { fn serialized_length(&self) -> usize { self.key.serialized_length() + self.kind.serialized_length() } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + self.key.write_bytes(writer)?; + if let Err(err) = self.kind.write_bytes(writer) { + error!(%err, "ToBytes for TransformV2"); + Err(err) + } else { + Ok(()) + } + } } impl FromBytes for TransformV2 { fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { - let (key, remainder) = Key::from_bytes(bytes)?; - let (transform, remainder) = TransformKindV2::from_bytes(remainder)?; + let (key, remainder) = match Key::from_bytes(bytes) { + Ok((k, rem)) => (k, rem), + Err(err) => { + error!(%err, "FromBytes for TransformV2: key"); + return Err(err); + } + }; + let (transform, remainder) = match TransformKindV2::from_bytes(remainder) { + Ok((tk, rem)) => (tk, rem), + Err(err) => { + error!(%err, "FromBytes for TransformV2: transform"); + return Err(err); + } + }; let transform_entry = TransformV2 { key, kind: transform, diff --git a/types/src/execution/transform_kind.rs b/types/src/execution/transform_kind.rs index 8640917dac..79a42ae82e 100644 --- a/types/src/execution/transform_kind.rs +++ b/types/src/execution/transform_kind.rs @@ -9,6 +9,7 @@ use rand::Rng; #[cfg(feature = "json-schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use tracing::error; use super::TransformError; use crate::{ @@ -238,6 +239,28 @@ impl TransformKindV2 { } impl ToBytes for TransformKindV2 { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn serialized_length(&self) -> usize { + U8_SERIALIZED_LENGTH + + match self { + TransformKindV2::Identity => 0, + TransformKindV2::Write(stored_value) => stored_value.serialized_length(), + TransformKindV2::AddInt32(value) => value.serialized_length(), + TransformKindV2::AddUInt64(value) => value.serialized_length(), + TransformKindV2::AddUInt128(value) => value.serialized_length(), + TransformKindV2::AddUInt256(value) => value.serialized_length(), + TransformKindV2::AddUInt512(value) => value.serialized_length(), + TransformKindV2::AddKeys(named_keys) => named_keys.serialized_length(), + TransformKindV2::Failure(error) => error.serialized_length(), + TransformKindV2::Prune(value) => value.serialized_length(), + } + } + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { match self { TransformKindV2::Identity => (TransformTag::Identity as u8).write_bytes(writer), @@ -279,33 +302,20 @@ impl ToBytes for TransformKindV2 { } } } - - fn to_bytes(&self) -> Result, bytesrepr::Error> { - let mut buffer = bytesrepr::allocate_buffer(self)?; - self.write_bytes(&mut buffer)?; - Ok(buffer) - } - - fn serialized_length(&self) -> usize { - U8_SERIALIZED_LENGTH - + match self { - TransformKindV2::Identity => 0, - TransformKindV2::Write(stored_value) => stored_value.serialized_length(), - TransformKindV2::AddInt32(value) => value.serialized_length(), - TransformKindV2::AddUInt64(value) => value.serialized_length(), - TransformKindV2::AddUInt128(value) => value.serialized_length(), - TransformKindV2::AddUInt256(value) => value.serialized_length(), - TransformKindV2::AddUInt512(value) => value.serialized_length(), - TransformKindV2::AddKeys(named_keys) => named_keys.serialized_length(), - TransformKindV2::Failure(error) => error.serialized_length(), - TransformKindV2::Prune(value) => value.serialized_length(), - } - } } impl FromBytes for TransformKindV2 { fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { - let (tag, remainder) = u8::from_bytes(bytes)?; + if bytes.is_empty() { + error!("FromBytes for TransformKindV2: bytes length should not be 0"); + } + let (tag, remainder) = match u8::from_bytes(bytes) { + Ok((tag, rem)) => (tag, rem), + Err(err) => { + error!(%err, "FromBytes for TransformKindV2"); + return Err(err); + } + }; match tag { tag if tag == TransformTag::Identity as u8 => { Ok((TransformKindV2::Identity, remainder)) @@ -346,7 +356,10 @@ impl FromBytes for TransformKindV2 { let (key, remainder) = Key::from_bytes(remainder)?; Ok((TransformKindV2::Prune(key), remainder)) } - _ => Err(bytesrepr::Error::Formatting), + _ => { + error!(%tag, rem_len = remainder.len(), "FromBytes for TransformKindV2: unknown tag"); + Err(bytesrepr::Error::Formatting) + } } } } diff --git a/types/src/key.rs b/types/src/key.rs index d1e47e4812..a3aa9ce8d9 100644 --- a/types/src/key.rs +++ b/types/src/key.rs @@ -31,7 +31,7 @@ use rand::{ #[cfg(feature = "json-schema")] use schemars::JsonSchema; use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer}; -use tracing::warn; +use tracing::{error, warn}; use crate::{ account::{AccountHash, ACCOUNT_HASH_LENGTH}, @@ -1585,7 +1585,16 @@ impl ToBytes for Key { impl FromBytes for Key { fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> { - let (tag, remainder) = KeyTag::from_bytes(bytes)?; + if bytes.is_empty() { + error!("FromBytes for Key: bytes length should not be 0"); + } + let (tag, remainder) = match KeyTag::from_bytes(bytes) { + Ok((tag, rem)) => (tag, rem), + Err(err) => { + error!(%err, "FromBytes for Key"); + return Err(err); + } + }; match tag { KeyTag::Account => { let (account_hash, rem) = AccountHash::from_bytes(remainder)?; diff --git a/types/src/system/auction/bid_addr.rs b/types/src/system/auction/bid_addr.rs index 6d2b2f8b72..092d70e6d4 100644 --- a/types/src/system/auction/bid_addr.rs +++ b/types/src/system/auction/bid_addr.rs @@ -342,16 +342,7 @@ impl BidAddr { | BidAddr::UnbondPurse { .. } => None, BidAddr::DelegatedAccount { delegator, .. } | BidAddr::ReservedDelegationAccount { delegator, .. } => Some(*delegator), - BidAddr::UnbondAccount { - validator, - unbonder, - } => { - if validator == unbonder { - Some(*validator) - } else { - None - } - } + BidAddr::UnbondAccount { unbonder, .. } => Some(*unbonder), } } @@ -533,6 +524,28 @@ impl FromBytes for BidAddr { remainder, )) } + tag if tag == BidAddrTag::UnbondAccount as u8 => { + let (validator, remainder) = AccountHash::from_bytes(remainder)?; + let (unbonder, remainder) = AccountHash::from_bytes(remainder)?; + Ok(( + BidAddr::UnbondAccount { + validator, + unbonder, + }, + remainder, + )) + } + tag if tag == BidAddrTag::UnbondPurse as u8 => { + let (validator, remainder) = AccountHash::from_bytes(remainder)?; + let (unbonder, remainder) = URefAddr::from_bytes(remainder)?; + Ok(( + BidAddr::UnbondPurse { + validator, + unbonder, + }, + remainder, + )) + } _ => Err(bytesrepr::Error::Formatting), } }