Skip to content

Commit

Permalink
Merge pull request #129 from Concordium/dry-run
Browse files Browse the repository at this point in the history
Add dry-run support
  • Loading branch information
td202 authored Nov 21, 2023
2 parents 7d0be29 + d609743 commit 562cda6
Show file tree
Hide file tree
Showing 9 changed files with 1,891 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
## Unreleased changes


- The sdk now requires a `rustc` version at least 1.67 (Before it required version 1.66).
- Add a `contract_update` helper analogous to `contract_init` to extract an
execution tree from a smart contract update transaction.
- Add a `ccd_cost` helper to `ChainParameters` to convert NRG cost to CCD.
- Add support for `DryRun`. Requires a node version at least 6.2.

## 3.1.0

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ num-bigint = "0.4"
num-traits = "0.2"
tokio-postgres = { version = "^0.7.8", features = ["with-serde_json-1"], optional = true }
http = "0.2"
tokio-stream = "0.1"

concordium_base = { version = "3.1.0", path = "./concordium-base/rust-src/concordium_base/", features = ["encryption"] }
concordium-smart-contract-engine = { version = "3.0", path = "./concordium-base/smart-contracts/wasm-chain-integration/", default-features = false, features = ["async"]}
Expand Down
120 changes: 120 additions & 0 deletions examples/v2_dry_run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//! Example of dry-run functionality of the node.
use anyhow::Context;
use clap::AppSettings;
use concordium_base::{
base::Energy,
common::types::Timestamp,
contracts_common::{Address, Amount, ContractAddress, EntrypointName},
smart_contracts::{OwnedParameter, OwnedReceiveName},
transactions::Payload,
};
use concordium_rust_sdk::{
types::smart_contracts::ContractContext,
v2::{self, dry_run::DryRunTransaction, BlockIdentifier},
};
use structopt::StructOpt;

#[derive(StructOpt)]
struct App {
#[structopt(
long = "node",
help = "GRPC interface of the node.",
default_value = "http://localhost:20000"
)]
endpoint: v2::Endpoint,
}

/// Test all dry run operations.
async fn test_all(endpoint: v2::Endpoint) -> anyhow::Result<()> {
// Connect to endpoint.
let mut client = v2::Client::new(endpoint).await.context("Cannot connect.")?;
// Start the dry run session.
let mut dry_run = client.begin_dry_run().await?;
println!(
"Timeout: {:?}\nEnergy quota: {:?}",
dry_run.timeout(),
dry_run.energy_quota()
);
// Load the best block.
let fut1 = dry_run
.begin_load_block_state(BlockIdentifier::Best)
.await?;
// Load the last finalized block.
let fut2 = dry_run
.begin_load_block_state(BlockIdentifier::LastFinal)
.await?;
// Await the results of the loads in the reverse order.
let res2 = fut2.await?;
let res1 = fut1.await?;
println!(
"Best block: {} ({:?})",
res1.inner.block_hash, res1.inner.current_timestamp
);
println!(
"Last final: {} ({:?})",
res2.inner.block_hash, res2.inner.current_timestamp
);
// Get account info for account at index 0.
let res3 = dry_run
.get_account_info(&v2::AccountIdentifier::Index(0.into()))
.await?;
println!("Account 0: {}", res3.inner.account_address);
// Get contract info for contract at address <0,0>.
let contract_addr = ContractAddress {
index: 0,
subindex: 0,
};
let res4 = dry_run.get_instance_info(&contract_addr).await?;
println!(
"Instance {contract_addr}: {} {:?}",
res4.inner.name(),
res4.inner.entrypoints()
);
// Try to invoke the entrypoint "view" on the <0,0> contract.
let invoke_target = OwnedReceiveName::construct(
res4.inner.name().as_contract_name(),
EntrypointName::new("view")?,
)?;
let parameter = OwnedParameter::empty();
let context = ContractContext {
invoker: Some(Address::Account(res3.inner.account_address)),
contract: contract_addr,
amount: Amount::zero(),
method: invoke_target.clone(),
parameter: parameter.clone(),
energy: 10000.into(),
};
let res5 = dry_run.invoke_instance(&context).await;
println!("Invoked view on {contract_addr}: {:?}", res5);
// Mint to account 0.
let _res6 = dry_run
.mint_to_account(&res3.inner.account_address, Amount::from_ccd(21))
.await?;
// Update the timestamp to now.
let _fut7 = dry_run.begin_set_timestamp(Timestamp::now()).await?;
// Execute a transfer to the encrypted balance on account 0.
let payload = Payload::TransferToEncrypted {
amount: Amount::from_ccd(20),
};
let transaction =
DryRunTransaction::new(res3.inner.account_address, Energy::from(500), &payload);
let fut8 = dry_run.begin_run_transaction(transaction).await?;
// We are done sending requests, so close the request stream.
dry_run.close();
let res8 = fut8.await?;
println!("Transferred to encrypted: {:?}", res8);

Ok(())
}

#[tokio::main(flavor = "multi_thread")]
async fn main() -> anyhow::Result<()> {
let app = {
let app = App::clap().global_setting(AppSettings::ColoredHelp);
let matches = app.get_matches();
App::from_clap(&matches)
};

test_all(app.endpoint.clone()).await
}
2 changes: 1 addition & 1 deletion src/types/smart_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ impl ContractContext {
fn return_zero_amount() -> Amount { Amount::from_micro_ccd(0) }
fn return_default_invoke_energy() -> Energy { DEFAULT_INVOKE_ENERGY }

#[derive(SerdeDeserialize, SerdeSerialize, Debug, Clone, Into)]
#[derive(SerdeDeserialize, SerdeSerialize, Debug, Clone, Into, From)]
#[serde(transparent)]
pub struct ReturnValue {
#[serde(with = "crate::internal::byte_array_hex")]
Expand Down
28 changes: 21 additions & 7 deletions src/v2/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,14 @@ impl From<Timestamp> for concordium_base::common::types::Timestamp {
fn from(value: Timestamp) -> Self { value.value.into() }
}

impl From<concordium_base::common::types::Timestamp> for Timestamp {
fn from(value: concordium_base::common::types::Timestamp) -> Self {
Timestamp {
value: value.millis,
}
}
}

impl TryFrom<Timestamp> for chrono::DateTime<chrono::Utc> {
type Error = tonic::Status;

Expand Down Expand Up @@ -1193,6 +1201,18 @@ impl TryFrom<BlockItem>
}
}

impl TryFrom<AccountTransactionDetails> for super::types::AccountTransactionDetails {
type Error = tonic::Status;

fn try_from(v: AccountTransactionDetails) -> Result<Self, Self::Error> {
Ok(Self {
cost: v.cost.require()?.into(),
sender: v.sender.require()?.try_into()?,
effects: v.effects.require()?.try_into()?,
})
}
}

impl TryFrom<BlockItemSummary> for super::types::BlockItemSummary {
type Error = tonic::Status;

Expand All @@ -1203,13 +1223,7 @@ impl TryFrom<BlockItemSummary> for super::types::BlockItemSummary {
hash: value.hash.require()?.try_into()?,
details: match value.details.require()? {
block_item_summary::Details::AccountTransaction(v) => {
super::types::BlockItemSummaryDetails::AccountTransaction(
super::types::AccountTransactionDetails {
cost: v.cost.require()?.into(),
sender: v.sender.require()?.try_into()?,
effects: v.effects.require()?.try_into()?,
},
)
super::types::BlockItemSummaryDetails::AccountTransaction(v.try_into()?)
}
block_item_summary::Details::AccountCreation(v) => {
super::types::BlockItemSummaryDetails::AccountCreation(
Expand Down
Loading

0 comments on commit 562cda6

Please sign in to comment.