Skip to content

Commit

Permalink
Merge #4645 #4653
Browse files Browse the repository at this point in the history
4645: Binary port balance query r=jacek-casper a=jacek-casper

This PR adds a new request to the binary port that allows querying for balance directly.
- I've added some additional types to `binary_port`, they're very similar to the existing `storage` types, with some slight adjustments to adapt them to the binary port:
  - added `BalanceResponse` for the new response payload type
  - added `PurseIdentifier` for specifying what balance to query, it's basically `BalanceIdentifier`, but without PenalizedAccount and Internal variants (these two look like they're for internal use only)
  - added `GlobalStateRequest::BalanceByStateRoot`, which has a state identifier, a purse identifier and a timestamp for holds lookup, it uses a `Timestamp` unlike the `storage` types which use `HoldsEpoch`, because `HoldsEpoch` requires a chainspec value to construct (balance hold interval)
  - added `GlobalStateRequest::BalanceByBlock`, which has a block identifier and a purse identifier, the holds timestamp is derived from the block

Also:
- boxed the `GetRequest::State` variant because it started triggering a lint
- added some missing handling for `WasmV1Result`

Related sidecar changes: casper-network/casper-sidecar#274

4653: Use custom Serialize/Deserialize for EntityAddr r=jacek-casper a=jacek-casper

The existing Serialize/Deserialize impls encode the address as a JSON array of bytes instead of the formatted string format. This PR fixes that.

There was also an issue with the schema, which I addressed here as well.

Co-authored-by: Jacek Malec <[email protected]>
  • Loading branch information
casperlabs-bors-ng[bot] and jacek-casper authored Apr 10, 2024
3 parents e84758a + 7c51708 + 2fcf5b2 commit 3cbba2c
Show file tree
Hide file tree
Showing 16 changed files with 760 additions and 154 deletions.
105 changes: 105 additions & 0 deletions binary_port/src/balance_response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use std::collections::BTreeMap;
#[cfg(test)]
use std::{collections::VecDeque, iter::FromIterator};

#[cfg(test)]
use casper_types::testing::TestRng;
use casper_types::{
bytesrepr::{self, FromBytes, ToBytes},
global_state::TrieMerkleProof,
system::mint::BalanceHoldAddrTag,
BlockTime, Key, StoredValue, U512,
};
#[cfg(test)]
use casper_types::{global_state::TrieMerkleProofStep, CLValue};
#[cfg(test)]
use rand::Rng;

/// Response to a balance query.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BalanceResponse {
/// The purses total balance, not considering holds.
pub total_balance: U512,
/// The available balance (total balance - sum of all active holds).
pub available_balance: U512,
/// A proof that the given value is present in the Merkle trie.
pub total_balance_proof: Box<TrieMerkleProof<Key, StoredValue>>,
/// Any time-relevant active holds on the balance.
pub balance_holds: BTreeMap<BlockTime, BalanceHoldsWithProof>,
}

impl BalanceResponse {
#[cfg(test)]
pub(crate) fn random(rng: &mut TestRng) -> Self {
BalanceResponse {
total_balance: rng.gen(),
available_balance: rng.gen(),
total_balance_proof: Box::new(TrieMerkleProof::new(
Key::URef(rng.gen()),
StoredValue::CLValue(CLValue::from_t(rng.gen::<i32>()).unwrap()),
VecDeque::from_iter([TrieMerkleProofStep::random(rng)]),
)),
balance_holds: BTreeMap::new(),
}
}
}

impl ToBytes for BalanceResponse {
fn to_bytes(&self) -> Result<Vec<u8>, casper_types::bytesrepr::Error> {
let mut buffer = bytesrepr::allocate_buffer(self)?;
self.write_bytes(&mut buffer)?;
Ok(buffer)
}

fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), casper_types::bytesrepr::Error> {
self.total_balance.write_bytes(writer)?;
self.available_balance.write_bytes(writer)?;
self.total_balance_proof.write_bytes(writer)?;
self.balance_holds.write_bytes(writer)
}

fn serialized_length(&self) -> usize {
self.total_balance.serialized_length()
+ self.available_balance.serialized_length()
+ self.total_balance_proof.serialized_length()
+ self.balance_holds.serialized_length()
}
}

impl FromBytes for BalanceResponse {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), casper_types::bytesrepr::Error> {
let (total_balance, remainder) = U512::from_bytes(bytes)?;
let (available_balance, remainder) = U512::from_bytes(remainder)?;
let (total_balance_proof, remainder) =
TrieMerkleProof::<Key, StoredValue>::from_bytes(remainder)?;
let (balance_holds, remainder) =
BTreeMap::<BlockTime, BalanceHoldsWithProof>::from_bytes(remainder)?;
Ok((
BalanceResponse {
total_balance,
available_balance,
total_balance_proof: Box::new(total_balance_proof),
balance_holds,
},
remainder,
))
}
}

/// Balance holds with Merkle proofs.
pub type BalanceHoldsWithProof =
BTreeMap<BalanceHoldAddrTag, (U512, TrieMerkleProof<Key, StoredValue>)>;

#[cfg(test)]
mod tests {
use super::*;
use casper_types::testing::TestRng;

#[test]
fn bytesrepr_roundtrip() {
let rng = &mut TestRng::new();

let val = BalanceResponse::random(rng);
bytesrepr::test_serialization_roundtrip(&val);
}
}
6 changes: 3 additions & 3 deletions binary_port/src/get_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub enum GetRequest {
key: Vec<u8>,
},
/// Retrieves data from the global state.
State(GlobalStateRequest),
State(Box<GlobalStateRequest>),
}

impl GetRequest {
Expand All @@ -44,7 +44,7 @@ impl GetRequest {
info_type_tag: rng.gen(),
key: rng.random_vec(16..32),
},
2 => GetRequest::State(GlobalStateRequest::random(rng)),
2 => GetRequest::State(Box::new(GlobalStateRequest::random(rng))),
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -122,7 +122,7 @@ impl FromBytes for GetRequest {
}
STATE_TAG => {
let (req, remainder) = FromBytes::from_bytes(remainder)?;
Ok((GetRequest::State(req), remainder))
Ok((GetRequest::State(Box::new(req)), remainder))
}
_ => Err(bytesrepr::Error::Formatting),
}
Expand Down
4 changes: 4 additions & 0 deletions binary_port/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A Rust library for types used by the binary port of a casper node.
mod balance_response;
mod binary_request;
mod binary_response;
mod binary_response_and_request;
Expand All @@ -12,11 +13,13 @@ mod information_request;
mod minimal_block_info;
mod node_status;
mod payload_type;
mod purse_identifier;
pub mod record_id;
mod speculative_execution_result;
mod state_request;
mod type_wrappers;

pub use balance_response::BalanceResponse;
pub use binary_request::{BinaryRequest, BinaryRequestHeader, BinaryRequestTag};
pub use binary_response::BinaryResponse;
pub use binary_response_and_request::BinaryResponseAndRequest;
Expand All @@ -29,6 +32,7 @@ pub use information_request::{InformationRequest, InformationRequestTag};
pub use minimal_block_info::MinimalBlockInfo;
pub use node_status::NodeStatus;
pub use payload_type::{PayloadEntity, PayloadType};
pub use purse_identifier::PurseIdentifier;
pub use record_id::{RecordId, UnknownRecordId};
pub use speculative_execution_result::SpeculativeExecutionResult;
pub use state_request::GlobalStateRequest;
Expand Down
17 changes: 15 additions & 2 deletions binary_port/src/payload_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::{
ConsensusStatus, ConsensusValidatorChanges, GetTrieFullResult, LastProgress, NetworkName,
ReactorStateName,
},
DictionaryQueryResult, RecordId, TransactionWithExecutionInfo, Uptime,
BalanceResponse, DictionaryQueryResult, RecordId, TransactionWithExecutionInfo, Uptime,
};

/// A type of the payload being returned in a binary response.
Expand Down Expand Up @@ -105,6 +105,8 @@ pub enum PayloadType {
NodeStatus,
/// Result of querying for a dictionary item.
DictionaryQueryResult,
/// Balance query response.
BalanceResponse,
}

impl PayloadType {
Expand All @@ -131,7 +133,7 @@ impl PayloadType {

#[cfg(test)]
pub(crate) fn random(rng: &mut TestRng) -> Self {
Self::try_from(rng.gen_range(0..33)).unwrap()
Self::try_from(rng.gen_range(0..37)).unwrap()
}
}

Expand Down Expand Up @@ -193,6 +195,8 @@ impl TryFrom<u8> for PayloadType {
x if x == PayloadType::DictionaryQueryResult as u8 => {
Ok(PayloadType::DictionaryQueryResult)
}
x if x == PayloadType::WasmV1Result as u8 => Ok(PayloadType::WasmV1Result),
x if x == PayloadType::BalanceResponse as u8 => Ok(PayloadType::BalanceResponse),
_ => Err(()),
}
}
Expand Down Expand Up @@ -245,6 +249,7 @@ impl fmt::Display for PayloadType {
PayloadType::NodeStatus => write!(f, "NodeStatus"),
PayloadType::WasmV1Result => write!(f, "WasmV1Result"),
PayloadType::DictionaryQueryResult => write!(f, "DictionaryQueryResult"),
PayloadType::BalanceResponse => write!(f, "BalanceResponse"),
}
}
}
Expand Down Expand Up @@ -285,6 +290,7 @@ const GET_TRIE_FULL_RESULT_TAG: u8 = 32;
const NODE_STATUS_TAG: u8 = 33;
const DICTIONARY_QUERY_RESULT_TAG: u8 = 34;
const WASM_V1_RESULT_TAG: u8 = 35;
const BALANCE_RESPONSE_TAG: u8 = 36;

impl ToBytes for PayloadType {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
Expand Down Expand Up @@ -335,6 +341,7 @@ impl ToBytes for PayloadType {
PayloadType::NodeStatus => NODE_STATUS_TAG,
PayloadType::WasmV1Result => WASM_V1_RESULT_TAG,
PayloadType::DictionaryQueryResult => DICTIONARY_QUERY_RESULT_TAG,
PayloadType::BalanceResponse => BALANCE_RESPONSE_TAG,
}
.write_bytes(writer)
}
Expand Down Expand Up @@ -379,6 +386,8 @@ impl FromBytes for PayloadType {
GET_TRIE_FULL_RESULT_TAG => PayloadType::GetTrieFullResult,
NODE_STATUS_TAG => PayloadType::NodeStatus,
DICTIONARY_QUERY_RESULT_TAG => PayloadType::DictionaryQueryResult,
WASM_V1_RESULT_TAG => PayloadType::WasmV1Result,
BALANCE_RESPONSE_TAG => PayloadType::BalanceResponse,
_ => return Err(bytesrepr::Error::Formatting),
};
Ok((record_id, remainder))
Expand Down Expand Up @@ -511,6 +520,10 @@ impl PayloadEntity for ConsensusStatus {
const PAYLOAD_TYPE: PayloadType = PayloadType::ConsensusStatus;
}

impl PayloadEntity for BalanceResponse {
const PAYLOAD_TYPE: PayloadType = PayloadType::BalanceResponse;
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
127 changes: 127 additions & 0 deletions binary_port/src/purse_identifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#[cfg(test)]
use casper_types::testing::TestRng;
#[cfg(test)]
use rand::Rng;

use casper_types::{
account::AccountHash,
bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
EntityAddr, PublicKey, URef,
};

const PAYMENT_PURSE_TAG: u8 = 0;
const ACCUMULATE_PURSE_TAG: u8 = 1;
const UREF_PURSE_TAG: u8 = 2;
const PUBLIC_KEY_PURSE_TAG: u8 = 3;
const ACCOUNT_PURSE_TAG: u8 = 4;
const ENTITY_PURSE_TAG: u8 = 5;

/// Identifier for balance lookup.
#[derive(Clone, Debug, PartialEq)]
pub enum PurseIdentifier {
Payment,
Accumulate,
Purse(URef),
PublicKey(PublicKey),
Account(AccountHash),
Entity(EntityAddr),
}

impl PurseIdentifier {
#[cfg(test)]
pub(crate) fn random(rng: &mut TestRng) -> Self {
match rng.gen_range(0..6) {
PAYMENT_PURSE_TAG => PurseIdentifier::Payment,
ACCUMULATE_PURSE_TAG => PurseIdentifier::Accumulate,
UREF_PURSE_TAG => PurseIdentifier::Purse(rng.gen()),
PUBLIC_KEY_PURSE_TAG => PurseIdentifier::PublicKey(PublicKey::random(rng)),
ACCOUNT_PURSE_TAG => PurseIdentifier::Account(rng.gen()),
ENTITY_PURSE_TAG => PurseIdentifier::Entity(rng.gen()),
_ => unreachable!(),
}
}
}

impl ToBytes for PurseIdentifier {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
let mut buffer = bytesrepr::allocate_buffer(self)?;
self.write_bytes(&mut buffer)?;
Ok(buffer)
}

fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
match self {
PurseIdentifier::Payment => PAYMENT_PURSE_TAG.write_bytes(writer),
PurseIdentifier::Accumulate => ACCUMULATE_PURSE_TAG.write_bytes(writer),
PurseIdentifier::Purse(uref) => {
UREF_PURSE_TAG.write_bytes(writer)?;
uref.write_bytes(writer)
}
PurseIdentifier::PublicKey(key) => {
PUBLIC_KEY_PURSE_TAG.write_bytes(writer)?;
key.write_bytes(writer)
}
PurseIdentifier::Account(account) => {
ACCOUNT_PURSE_TAG.write_bytes(writer)?;
account.write_bytes(writer)
}
PurseIdentifier::Entity(entity) => {
ENTITY_PURSE_TAG.write_bytes(writer)?;
entity.write_bytes(writer)
}
}
}

fn serialized_length(&self) -> usize {
U8_SERIALIZED_LENGTH
+ match self {
PurseIdentifier::Payment => 0,
PurseIdentifier::Accumulate => 0,
PurseIdentifier::Purse(uref) => uref.serialized_length(),
PurseIdentifier::PublicKey(key) => key.serialized_length(),
PurseIdentifier::Account(account) => account.serialized_length(),
PurseIdentifier::Entity(entity) => entity.serialized_length(),
}
}
}

impl FromBytes for PurseIdentifier {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
let (tag, remainder) = u8::from_bytes(bytes)?;
match tag {
PAYMENT_PURSE_TAG => Ok((PurseIdentifier::Payment, remainder)),
ACCUMULATE_PURSE_TAG => Ok((PurseIdentifier::Accumulate, remainder)),
UREF_PURSE_TAG => {
let (uref, remainder) = URef::from_bytes(remainder)?;
Ok((PurseIdentifier::Purse(uref), remainder))
}
PUBLIC_KEY_PURSE_TAG => {
let (key, remainder) = PublicKey::from_bytes(remainder)?;
Ok((PurseIdentifier::PublicKey(key), remainder))
}
ACCOUNT_PURSE_TAG => {
let (account, remainder) = AccountHash::from_bytes(remainder)?;
Ok((PurseIdentifier::Account(account), remainder))
}
ENTITY_PURSE_TAG => {
let (entity, remainder) = EntityAddr::from_bytes(remainder)?;
Ok((PurseIdentifier::Entity(entity), remainder))
}
_ => Err(bytesrepr::Error::Formatting),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use casper_types::testing::TestRng;

#[test]
fn bytesrepr_roundtrip() {
let rng = &mut TestRng::new();

let val = PurseIdentifier::random(rng);
bytesrepr::test_serialization_roundtrip(&val);
}
}
Loading

0 comments on commit 3cbba2c

Please sign in to comment.