Skip to content

Commit

Permalink
Merge pull request #145 from Concordium/tx-sig-helpers
Browse files Browse the repository at this point in the history
Add helpers to retrieve transactions and check their signatures from the chain
  • Loading branch information
abizjak authored Dec 4, 2023
2 parents 31e3ee5 + 7cda85d commit 1dad853
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 4 deletions.
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

0 comments on commit 1dad853

Please sign in to comment.