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

Add helpers to retrieve transactions and check their signatures from the chain #145

Merged
merged 3 commits into from
Dec 4, 2023
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased changes

- Add a `From<&AccountInfo>` instance for `AccountAccessStructure` to ease verification of signatures using `GetAccountInfo` response.
- Add a `get_finalized_block_item` method to the `Client` to retrieve a finalized block item from the node.
- Remove the V1 API.
- Add `Display` implementation to `BlockIdentifier`.
- Add `Display` and `FromStr` implementations for `AccountIdentifier`.
Expand Down
56 changes: 55 additions & 1 deletion src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use concordium_base::{
Buffer, Deserial, Get, ParseResult, ReadBytesExt, SerdeDeserialize, SerdeSerialize, Serial,
Versioned,
},
contracts_common::Duration,
contracts_common::{Duration, EntrypointName, Parameter},
encrypted_transfers,
encrypted_transfers::types::{
AggregatedDecryptedAmount, EncryptedAmountTransferData, SecToPubAmountTransferData,
Expand Down Expand Up @@ -329,6 +329,30 @@ pub struct AccountInfo {
pub account_address: AccountAddress,
}

impl From<&AccountInfo> for AccountAccessStructure {
fn from(value: &AccountInfo) -> Self {
Self {
keys: value
.account_credentials
.iter()
.map(|(idx, v)| {
let key = match v.value {
crate::id::types::AccountCredentialWithoutProofs::Initial { ref icdv } => {
icdv.cred_account.clone()
}
crate::id::types::AccountCredentialWithoutProofs::Normal {
ref cdv,
..
} => cdv.cred_key_info.clone(),
};
(*idx, key)
})
.collect(),
threshold: value.account_threshold,
}
}
}

#[derive(SerdeSerialize, SerdeDeserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
/// The state of consensus parameters, and allowed participants (i.e., bakers).
Expand Down Expand Up @@ -1246,6 +1270,36 @@ pub enum ExecutionTree {
V1(ExecutionTreeV1),
}

impl ExecutionTree {
/// Return the name of the top-level entrypoint that was invoked.
pub fn entrypoint(&self) -> EntrypointName {
match self {
ExecutionTree::V0(v0) => v0
.top_level
.receive_name
.as_receive_name()
.entrypoint_name(),
ExecutionTree::V1(v1) => v1.receive_name.as_receive_name().entrypoint_name(),
}
}

/// Return the name of the top-level contract that was invoked.
pub fn address(&self) -> ContractAddress {
match self {
ExecutionTree::V0(v0) => v0.top_level.address,
ExecutionTree::V1(v1) => v1.address,
}
}

/// Return parameter to the top-level contract call.
pub fn parameter(&self) -> Parameter {
match self {
ExecutionTree::V0(v0) => v0.top_level.message.as_parameter(),
ExecutionTree::V1(v1) => v1.message.as_parameter(),
}
}
}

/// Convert the trace elements into an [`ExecutionTree`].
/// This will fail if the list was not generated correctly, but if the list of
/// trace elements is coming from the node it will always be in the correct
Expand Down
35 changes: 32 additions & 3 deletions src/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::{
ContractContext, InstanceInfo, InvokeContractResult, ModuleReference, WasmModule,
},
transactions::{self, InitContractPayload, UpdateContractPayload, UpdateInstruction},
AbsoluteBlockHeight, AccountInfo, CredentialRegistrationID, Energy, Memo, Nonce,
RegisteredData, SpecialTransactionOutcome, TransactionStatus, UpdateSequenceNumber,
AbsoluteBlockHeight, AccountInfo, BlockItemSummary, CredentialRegistrationID, Energy, Memo,
Nonce, RegisteredData, SpecialTransactionOutcome, TransactionStatus, UpdateSequenceNumber,
},
};
use concordium_base::{
Expand All @@ -39,7 +39,7 @@ use concordium_base::{
},
};
pub use endpoints::{QueryError, QueryResult, RPCError, RPCResult};
use futures::{Stream, StreamExt};
use futures::{Stream, StreamExt, TryStreamExt};
pub use http::uri::Scheme;
use num::{BigUint, ToPrimitive};
use std::{collections::HashMap, num::ParseIntError, str::FromStr};
Expand Down Expand Up @@ -2104,6 +2104,35 @@ impl Client {
})
}

/// Get the specific **block item** if it is finalized.
/// If the transaction does not exist in a finalized block
/// [`QueryError::NotFound`] is returned.
///
/// **Note that this is not an efficient method** since the node API does
/// not allow for retrieving just the specific block item, but rather
/// requires retrieving the full block. Use it for testing and debugging
/// only.
///
/// The return value is a triple of the [`BlockItem`], the hash of the block
/// in which it is finalized, and the outcome in the form of
/// [`BlockItemSummary`].
pub async fn get_finalized_block_item(
&mut self,
th: TransactionHash,
) -> endpoints::QueryResult<(BlockItem<EncodedPayload>, BlockHash, BlockItemSummary)> {
let status = self.get_block_item_status(&th).await?;
let Some((bh, status)) = status.is_finalized() else {
return Err(QueryError::NotFound);
};
let mut response = self.get_block_items(bh).await?.response;
while let Some(tx) = response.try_next().await? {
if tx.hash() == th {
return Ok((tx, *bh, status.clone()));
}
}
Err(endpoints::QueryError::NotFound)
}

/// Shut down the node.
/// Return a GRPC error if the shutdown failed.
pub async fn shutdown(&mut self) -> endpoints::RPCResult<()> {
Expand Down