Skip to content

Commit

Permalink
Binary port balance query (#274)
Browse files Browse the repository at this point in the history
* Use binary port balance request

* Point at branch for now

* Add holds to the response

* Cleanup

* Point at feat-2.0

* Add missing PurseIdentifier variant
  • Loading branch information
jacek-casper authored Apr 11, 2024
1 parent c7988fa commit c87274d
Show file tree
Hide file tree
Showing 10 changed files with 728 additions and 404 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ anyhow = "1"
async-stream = "0.3.4"
async-trait = "0.1.77"
casper-types = { git = "https://github.com/casper-network/casper-node", branch = "feat-2.0" }
casper-binary-port = { git = "https://github.com/casper-network/casper-node", branch = "feat-2.0" }
casper-event-sidecar = { path = "./event_sidecar", version = "1.0.0" }
casper-event-types = { path = "./types", version = "1.0.0" }
casper-rpc-sidecar = { path = "./rpc_sidecar", version = "1.0.0" }
Expand Down
206 changes: 204 additions & 2 deletions resources/test/rpc_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,7 @@
"type": "string"
},
"balance": {
"description": "The balance represented in motes.",
"description": "The available balance in motes (total balance - sum of all active holds).",
"$ref": "#/components/schemas/U512"
}
}
Expand Down Expand Up @@ -1190,6 +1190,111 @@
}
]
},
{
"name": "query_balance_details",
"summary": "query for full balance information using a purse identifier and a state identifier",
"params": [
{
"name": "purse_identifier",
"schema": {
"description": "The identifier to obtain the purse corresponding to balance query.",
"$ref": "#/components/schemas/PurseIdentifier"
},
"required": true
},
{
"name": "state_identifier",
"schema": {
"description": "The identifier for the state used for the query, if none is passed, the latest block will be used.",
"anyOf": [
{
"$ref": "#/components/schemas/BalanceStateIdentifier"
},
{
"type": "null"
}
]
},
"required": false
}
],
"result": {
"name": "query_balance_details_result",
"schema": {
"description": "Result for \"query_balance\" RPC response.",
"type": "object",
"required": [
"api_version",
"available_balance",
"holds",
"total_balance",
"total_balance_proof"
],
"properties": {
"api_version": {
"description": "The RPC API version.",
"type": "string"
},
"total_balance": {
"description": "The purses total balance, not considering holds.",
"$ref": "#/components/schemas/U512"
},
"available_balance": {
"description": "The available balance in motes (total balance - sum of all active holds).",
"$ref": "#/components/schemas/U512"
},
"total_balance_proof": {
"description": "A proof that the given value is present in the Merkle trie.",
"type": "string"
},
"holds": {
"description": "Holds active at the requested point in time.",
"type": "array",
"items": {
"$ref": "#/components/schemas/BalanceHoldWithProof"
}
}
}
}
},
"examples": [
{
"name": "query_balance_details_example",
"params": [
{
"name": "state_identifier",
"value": {
"block": {
"Hash": "0707070707070707070707070707070707070707070707070707070707070707"
}
}
},
{
"name": "purse_identifier",
"value": {
"main_purse_under_account_hash": "account-hash-0909090909090909090909090909090909090909090909090909090909090909"
}
}
],
"result": {
"name": "query_balance_details_example_result",
"value": {
"api_version": "2.0.0",
"total_balance": "123456",
"available_balance": "123456",
"total_balance_proof": "01000000006ef2e0949ac76e55812421f755abe129b6244fe7168b77f47a72536147614625016ef2e0949ac76e55812421f755abe129b6244fe7168b77f47a72536147614625000000003529cde5c621f857f75f3810611eb4af3f998caaa9d4a3413cf799f99c67db0307010000006ef2e0949ac76e55812421f755abe129b6244fe7168b77f47a7253614761462501010102000000006e06000000000074769d28aac597a36a03a932d4b43e4f10bf0403ee5c41dd035102553f5773631200b9e173e8f05361b681513c14e25e3138639eb03232581db7557c9e8dbbc83ce94500226a9a7fe4f2b7b88d5103a4fc7400f02bf89c860c9ccdd56951a2afe9be0e0267006d820fb5676eb2960e15722f7725f3f8f41030078f8b2e44bf0dc03f71b176d6e800dc5ae9805068c5be6da1a90b2528ee85db0609cc0fb4bd60bbd559f497a98b67f500e1e3e846592f4918234647fca39830b7e1e6ad6f5b7a99b39af823d82ba1873d000003000000010186ff500f287e9b53f823ae1582b1fa429dfede28015125fd233a31ca04d5012002015cc42669a55467a1fdf49750772bfc1aed59b9b085558eb81510e9b015a7c83b0301e3cf4a34b1db6bfa58808b686cb8fe21ebe0c1bcbcee522649d2b135fe510fe3",
"holds": [
{
"time": 0,
"amount": "123456",
"proof": "01000000006ef2e0949ac76e55812421f755abe129b6244fe7168b77f47a72536147614625016ef2e0949ac76e55812421f755abe129b6244fe7168b77f47a72536147614625000000003529cde5c621f857f75f3810611eb4af3f998caaa9d4a3413cf799f99c67db0307010000006ef2e0949ac76e55812421f755abe129b6244fe7168b77f47a7253614761462501010102000000006e06000000000074769d28aac597a36a03a932d4b43e4f10bf0403ee5c41dd035102553f5773631200b9e173e8f05361b681513c14e25e3138639eb03232581db7557c9e8dbbc83ce94500226a9a7fe4f2b7b88d5103a4fc7400f02bf89c860c9ccdd56951a2afe9be0e0267006d820fb5676eb2960e15722f7725f3f8f41030078f8b2e44bf0dc03f71b176d6e800dc5ae9805068c5be6da1a90b2528ee85db0609cc0fb4bd60bbd559f497a98b67f500e1e3e846592f4918234647fca39830b7e1e6ad6f5b7a99b39af823d82ba1873d000003000000010186ff500f287e9b53f823ae1582b1fa429dfede28015125fd233a31ca04d5012002015cc42669a55467a1fdf49750772bfc1aed59b9b085558eb81510e9b015a7c83b0301e3cf4a34b1db6bfa58808b686cb8fe21ebe0c1bcbcee522649d2b135fe510fe3"
}
]
}
}
}
]
},
{
"name": "info_get_peers",
"summary": "returns a list of peers connected to the node",
Expand Down Expand Up @@ -1927,7 +2032,7 @@
"type": "string"
},
"balance_value": {
"description": "The balance value.",
"description": "The available balance in motes (total balance - sum of all active holds). The active holds are determined by the current timestamp and not the state root hash. If you need to account for holds at a specific time, you should use the `query_balance_details` RPC.",
"$ref": "#/components/schemas/U512"
},
"merkle_proof": {
Expand Down Expand Up @@ -7085,6 +7190,19 @@
},
"additionalProperties": false
},
{
"description": "The main purse of the account identified by this entity address.",
"type": "object",
"required": [
"main_purse_under_entity_addr"
],
"properties": {
"main_purse_under_entity_addr": {
"$ref": "#/components/schemas/EntityAddr"
}
},
"additionalProperties": false
},
{
"description": "The purse identified by this URef.",
"type": "object",
Expand All @@ -7100,6 +7218,90 @@
}
]
},
"BalanceStateIdentifier": {
"description": "Identifier of a balance.",
"oneOf": [
{
"description": "The balance at a specific block.",
"type": "object",
"required": [
"block"
],
"properties": {
"block": {
"$ref": "#/components/schemas/BlockIdentifier"
}
},
"additionalProperties": false
},
{
"description": "The balance at a specific state root.",
"type": "object",
"required": [
"state_root"
],
"properties": {
"state_root": {
"type": "object",
"required": [
"state_root_hash",
"timestamp"
],
"properties": {
"state_root_hash": {
"description": "The state root hash.",
"allOf": [
{
"$ref": "#/components/schemas/Digest"
}
]
},
"timestamp": {
"description": "Timestamp for holds lookup.",
"allOf": [
{
"$ref": "#/components/schemas/Timestamp"
}
]
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"BalanceHoldWithProof": {
"type": "object",
"required": [
"amount",
"proof",
"time"
],
"properties": {
"time": {
"description": "The block time at which the hold was created.",
"allOf": [
{
"$ref": "#/components/schemas/BlockTime"
}
]
},
"amount": {
"description": "The amount in the hold.",
"allOf": [
{
"$ref": "#/components/schemas/U512"
}
]
},
"proof": {
"description": "A proof that the given value is present in the Merkle trie.",
"type": "string"
}
}
},
"Peers": {
"description": "Map of peer IDs to network addresses.",
"type": "array",
Expand Down
4 changes: 2 additions & 2 deletions rpc_sidecar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ bincode = "1"
bytes = "1.5.0"
casper-json-rpc = { version = "1.0.0", path = "../json_rpc" }
casper-types = { workspace = true, features = ["datasize", "json-schema", "std"] }
casper-binary-port = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0" }
casper-binary-port.workspace = true
datasize = { workspace = true, features = ["detailed", "fake_clock-types"] }
futures = { workspace = true }
http = "0.2.1"
Expand All @@ -45,7 +45,7 @@ warp = { version = "0.3.6", features = ["compression"] }
[dev-dependencies]
assert-json-diff = "2"
casper-types = { workspace = true, features = ["datasize", "json-schema", "std", "testing"] }
casper-binary-port = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0", features = ["testing"] }
casper-binary-port = { workspace = true, features = ["testing"] }
pretty_assertions = "0.7.2"
regex = "1"
tempfile = "3"
Expand Down
5 changes: 3 additions & 2 deletions rpc_sidecar/src/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use casper_json_rpc::{CorsOrigin, RequestHandlersBuilder};
use crate::{
rpcs::{
info::{GetPeers, GetStatus, GetTransaction},
state::GetAddressableEntity,
state::{GetAddressableEntity, QueryBalanceDetails},
},
NodeClient,
};
Expand Down Expand Up @@ -62,7 +62,8 @@ pub async fn run(
ListRpcs::register_as_handler(node.clone(), &mut handlers);
GetDictionaryItem::register_as_handler(node.clone(), &mut handlers);
GetChainspec::register_as_handler(node.clone(), &mut handlers);
QueryBalance::register_as_handler(node, &mut handlers);
QueryBalance::register_as_handler(node.clone(), &mut handlers);
QueryBalanceDetails::register_as_handler(node, &mut handlers);
let handlers = handlers.build();

match cors_origin.as_str() {
Expand Down
47 changes: 40 additions & 7 deletions rpc_sidecar/src/node_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ use std::{
};

use casper_binary_port::{
BinaryRequest, BinaryRequestHeader, BinaryResponse, BinaryResponseAndRequest,
BalanceResponse, BinaryRequest, BinaryRequestHeader, BinaryResponse, BinaryResponseAndRequest,
ConsensusValidatorChanges, DictionaryItemIdentifier, DictionaryQueryResult, ErrorCode,
GetRequest, GetTrieFullResult, GlobalStateQueryResult, GlobalStateRequest, InformationRequest,
NodeStatus, PayloadEntity, RecordId, SpeculativeExecutionResult, TransactionWithExecutionInfo,
NodeStatus, PayloadEntity, PurseIdentifier, RecordId, SpeculativeExecutionResult,
TransactionWithExecutionInfo,
};
use casper_types::{
bytesrepr::{self, FromBytes, ToBytes},
AvailableBlockRange, BlockHash, BlockHeader, BlockIdentifier, ChainspecRawBytes, Digest,
GlobalStateIdentifier, Key, KeyTag, Peers, ProtocolVersion, SignedBlock, StoredValue,
Transaction, TransactionHash, Transfer,
Timestamp, Transaction, TransactionHash, Transfer,
};
use juliet::{
io::IoCoreBuilder,
Expand Down Expand Up @@ -75,7 +76,7 @@ pub trait NodeClient: Send + Sync {
path,
};
let resp = self
.send_request(BinaryRequest::Get(GetRequest::State(req)))
.send_request(BinaryRequest::Get(GetRequest::State(Box::new(req))))
.await?;
parse_response::<GlobalStateQueryResult>(&resp.into())
}
Expand All @@ -90,15 +91,47 @@ pub trait NodeClient: Send + Sync {
key_tag,
};
let resp = self
.send_request(BinaryRequest::Get(GetRequest::State(get)))
.send_request(BinaryRequest::Get(GetRequest::State(Box::new(get))))
.await?;
parse_response::<Vec<StoredValue>>(&resp.into())?.ok_or(Error::EmptyEnvelope)
}

async fn get_balance_by_state_root(
&self,
state_identifier: Option<GlobalStateIdentifier>,
purse_identifier: PurseIdentifier,
timestamp: Timestamp,
) -> Result<BalanceResponse, Error> {
let get = GlobalStateRequest::BalanceByStateRoot {
state_identifier,
purse_identifier,
timestamp,
};
let resp = self
.send_request(BinaryRequest::Get(GetRequest::State(Box::new(get))))
.await?;
parse_response::<BalanceResponse>(&resp.into())?.ok_or(Error::EmptyEnvelope)
}

async fn get_balance_by_block(
&self,
block_identifier: Option<BlockIdentifier>,
purse_identifier: PurseIdentifier,
) -> Result<BalanceResponse, Error> {
let get = GlobalStateRequest::BalanceByBlock {
block_identifier,
purse_identifier,
};
let resp = self
.send_request(BinaryRequest::Get(GetRequest::State(Box::new(get))))
.await?;
parse_response::<BalanceResponse>(&resp.into())?.ok_or(Error::EmptyEnvelope)
}

async fn read_trie_bytes(&self, trie_key: Digest) -> Result<Option<Vec<u8>>, Error> {
let req = GlobalStateRequest::Trie { trie_key };
let resp = self
.send_request(BinaryRequest::Get(GetRequest::State(req)))
.send_request(BinaryRequest::Get(GetRequest::State(Box::new(req))))
.await?;
let res = parse_response::<GetTrieFullResult>(&resp.into())?.ok_or(Error::EmptyEnvelope)?;
Ok(res.into_inner().map(<Vec<u8>>::from))
Expand All @@ -114,7 +147,7 @@ pub trait NodeClient: Send + Sync {
identifier,
};
let resp = self
.send_request(BinaryRequest::Get(GetRequest::State(get)))
.send_request(BinaryRequest::Get(GetRequest::State(Box::new(get))))
.await?;
parse_response::<DictionaryQueryResult>(&resp.into())
}
Expand Down
Loading

0 comments on commit c87274d

Please sign in to comment.