Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased changes

## 9.0.0-alpha.0

- Support for `ProtocolVersion::P10`.
- The flag `serde_deprecated` now guards `serde::Serialize` and `serde::Deserialize` implemetations on the following types. The implementations will eventually be removed.
- `protocol_level_tokens::AccountToken`
Expand Down Expand Up @@ -48,6 +50,7 @@
- `types::smart_contracts::InstanceInfo`
- `types::smart_contracts::ContractContext`
- `types::smart_contracts::InvokeContractResult`
- Support for sponsored transactions.

## 8.0.0

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "concordium-rust-sdk"
version = "8.0.0"
version = "9.0.0-alpha.0"
authors = ["Concordium <[email protected]>"]
edition = "2021"
rust-version = "1.85"
Expand Down Expand Up @@ -36,7 +36,7 @@ num-traits = "0.2"
http = "1.2.0"
tokio-stream = "0.1"

concordium_base = { version = "9.0.0", path = "./concordium-base/rust-src/concordium_base/", features = ["encryption"] }
concordium_base = { version = "10.0.0-alpha.0", path = "./concordium-base/rust-src/concordium_base/", features = ["encryption"] }
concordium-smart-contract-engine = { version = "6.0", path = "./concordium-base/smart-contracts/wasm-chain-integration/", default-features = false, features = ["async"]}
aes-gcm = { version = "0.10", features = ["std"] }
tracing = "0.1"
Expand Down
2 changes: 1 addition & 1 deletion concordium-base
Submodule concordium-base updated 32 files
+1 −1 concordium-grpc-api
+61 −21 haskell-src/Concordium/GRPC2.hs
+1 −1 haskell-src/Concordium/Types/DummyData.hs
+39 −15 haskell-src/Concordium/Types/Execution.hs
+46 −1 haskell-src/Concordium/Types/ProtocolVersion.hs
+35 −27 haskell-src/Concordium/Types/Queries.hs
+390 −67 haskell-src/Concordium/Types/Transactions.hs
+47 −17 haskell-tests/Generators.hs
+90 −6 haskell-tests/Types/TransactionSerializationSpec.hs
+1 −1 identity-provider-service/Cargo.lock
+1 −1 idiss/Cargo.lock
+1 −1 mobile_wallet/Cargo.lock
+4 −4 mobile_wallet/src/lib.rs
+1 −1 rust-bins/Cargo.lock
+8 −8 rust-bins/src/bin/client.rs
+2 −2 rust-bins/src/bin/generate_testdata.rs
+6 −6 rust-bins/src/bin/user_cli.rs
+1 −1 rust-src/Cargo.lock
+5 −0 rust-src/concordium_base/CHANGELOG.md
+1 −1 rust-src/concordium_base/Cargo.toml
+1 −1 rust-src/concordium_base/benches/compute_message.rs
+10 −4 rust-src/concordium_base/benches/verify_cdi.rs
+4 −0 rust-src/concordium_base/src/common/serialize.rs
+87 −19 rust-src/concordium_base/src/common/types.rs
+7 −3 rust-src/concordium_base/src/id/account_holder.rs
+4 −4 rust-src/concordium_base/src/id/constants.rs
+1 −1 rust-src/concordium_base/src/id/ffi.rs
+86 −13 rust-src/concordium_base/src/id/secret_sharing.rs
+4 −2 rust-src/concordium_base/src/id/test.rs
+456 −6 rust-src/concordium_base/src/transactions.rs
+1 −1 rust-src/wallet_library/src/credential.rs
+2 −2 rust-src/wallet_library/src/identity.rs
122 changes: 122 additions & 0 deletions examples/sponsored-plt-transfer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! Example that shows how to create a sponsored PLT token transfer.
use anyhow::Context;
use clap::AppSettings;
use concordium_base::{
contracts_common::AccountAddress,
protocol_level_tokens::{operations, ConversionRule, TokenAmount, TokenId},
};
use concordium_rust_sdk::{
common::types::TransactionTime,
types::{transactions::BlockItem, WalletAccount},
v2::{
BlockIdentifier, {self},
},
};
use rust_decimal::Decimal;
use std::{path::PathBuf, str::FromStr};
use structopt::*;

#[derive(StructOpt)]
struct App {
#[structopt(
long = "node",
help = "V2 GRPC interface of the node.",
default_value = "http://localhost:20000"
)]
endpoint: v2::Endpoint,
#[structopt(long = "sender", help = "Path to the sender account key file.")]
sender_account: PathBuf,
#[structopt(long = "sponsor", help = "Path to the sponsor account key file.")]
sponsor_account: PathBuf,
#[structopt(long = "receiver", help = "Receiver address.")]
receiver: String,
#[structopt(long = "token", help = "Token id of token.")]
token_id: String,
#[structopt(long = "amount", help = "Amount to send.", default_value = "100.0")]
amount: Decimal,
}

#[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)
};
let mut client = v2::Client::new(app.endpoint).await?;

// Token id of the PLT token to transfer
let token_id = TokenId::try_from(app.token_id.clone())?;

// Token info, we need the number of decimals in the token amount representation
let token_info = client
.get_token_info(token_id.clone(), BlockIdentifier::LastFinal)
.await?
.response;

// Amount of tokens to send. The number of decimals in the TokenAmount
// must be the same as the number of decimals in the TokenInfo
let token_amount = TokenAmount::try_from_rust_decimal(
app.amount,
token_info.token_state.decimals,
ConversionRule::AllowRounding,
)?;
println!("Token amount: {}", token_amount,);

// Receiver of the tokens
let receiver_address = AccountAddress::from_str(&app.receiver)?;

// Load account keys and address for sender and sponsor from a file
let sender_keys: WalletAccount = WalletAccount::from_json_file(app.sender_account)
.context("Could not read the sender account keys file.")?;
let sponsor_keys: WalletAccount = WalletAccount::from_json_file(app.sponsor_account)
.context("Could not read the sponsor account keys file.")?;

// Get the initial nonce at the last finalized block.
let nonce = client
.get_next_account_sequence_number(&sender_keys.address)
.await?
.nonce;

// Set expiry to now + 5min
let expiry: TransactionTime =
TransactionTime::from_seconds((chrono::Utc::now().timestamp() + 300) as u64);

// Create transfer tokens transaction
let operation = operations::transfer_tokens(receiver_address, token_amount);

// Compose operation to transaction
let txn = concordium_base::transactions::construct::token_update_operations(
1,
sender_keys.address,
nonce,
expiry,
token_id,
[operation].into_iter().collect(),
)?
// Extend the transaction and add a sponsor.
.extend()
.add_sponsor(sponsor_keys.address, 1)
.expect("Can add sponsor account")
// Sender signs the transaction.
.sign(&sender_keys)
// Sponsor signs the now sponsored transaction.
.sponsor(&sponsor_keys)
.expect("Can sponsor the transaction")
.finalize()
.expect("Transaction is well-formed");

let item = BlockItem::AccountTransactionV1(txn);

// Submit the transaction to the chain
let transaction_hash = client.send_block_item(&item).await?;
println!(
"Transaction {} submitted (nonce = {}).",
transaction_hash, nonce,
);
let (bh, bs) = client.wait_until_finalized(&transaction_hash).await?;
println!("Transaction finalized in block {}.", bh);
println!("The outcome is {:#?}", bs);

Ok(())
}
8 changes: 4 additions & 4 deletions src/lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ It makes potentially extended information explicit in the types and allows each
Several types and fields in the SDK are now wrapped in [`Upward`] and the wrapper provides several methods to ease the migration depending on the desired behavior of the application.
For some situations unknown information should cause an error, where in other situations it is safe to ignore, and maybe logging warnings to be handled in a future iteration of the application.

```rust,no_compile
```rust,ignore
if let Upward::Known(details) = block_item_summary.details {
// Data is familiar so we handle details as usual.
} else {
Expand All @@ -47,13 +47,13 @@ if let Upward::Known(details) = block_item_summary.details {

To produce an error in the case of unknown data use [`known_or_err`], converting [`Upward<A>`] into [`Result<A, UnknownDataError>`](v2::upward::UnknownDataError).

```rust,no_compile
```rust,ignore
let details = block_item_summary.details.known_or_err()?;
```

Alternatively [`known_or`] or similarly named variants can be used for directly mapping the unknown data case to an error.

```rust,no_compile
```rust,ignore
let details = block_item_summary.details.known_or(MyError::UnknownData)?;
```

Expand Down Expand Up @@ -81,4 +81,4 @@ The `ChainParameters::micro_ccd_per_energy()` and `ChainParameters::ccd_cost()`
Instead, use [`energy_rate()`](types::chain_parameters::ChainParameters::energy_rate) to obtain an [`EnergyRate`](types::chain_parameters::EnergyRate), which encapsulates the [`micro_ccd_per_energy`](types::chain_parameters::EnergyRate::micro_ccd_per_energy) exchange rate.
`EnergyRate` also provides [`ccd_cost()`](types::chain_parameters::EnergyRate::ccd_cost), which should be used in place of the former `ChainParameters::ccd_cost()`.

Finally, the `ChainParameters::foundation_account()` getter function was removed, and should be replaced by directly accessing [`ChainParameters::foundation_account`](types::chain_parameters::ChainParameters::foundation_account).
Finally, the `ChainParameters::foundation_account()` getter function was removed, and should be replaced by directly accessing [`ChainParameters::foundation_account`](types::chain_parameters::ChainParameters::foundation_account).
18 changes: 17 additions & 1 deletion src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1914,7 +1914,7 @@ impl BlockItemSummaryDetails {
let Upward::Known(effects) = &at.effects else {
return Upward::Unknown(());
};
match effects {
let mut affected_accounts = match effects {
AccountTransactionEffects::None { .. } => vec![at.sender],
AccountTransactionEffects::ModuleDeployed { .. } => vec![at.sender],
AccountTransactionEffects::ContractInitialized { .. } => vec![at.sender],
Expand Down Expand Up @@ -1992,7 +1992,12 @@ impl BlockItemSummaryDetails {
add_token_event_addresses(&mut addresses, events);
addresses.into_iter().collect()
}
};
// Add the optional sponsor account to the affected accounts.
if let Some(sponsor) = at.sponsor.as_ref() {
affected_accounts.push(sponsor.sponsor);
}
affected_accounts
}
BlockItemSummaryDetails::AccountCreation(_) => Vec::new(),
BlockItemSummaryDetails::Update(_) => Vec::new(),
Expand All @@ -2006,6 +2011,15 @@ impl BlockItemSummaryDetails {
}
}

#[derive(Debug, Clone)]
/// Details about the sponsor of a transaction.
pub struct SponsorDetails {
/// The cost of the transaction. Paid by the sponsor.
pub sponsor: AccountAddress,
/// The sponsor of the transaction.
pub cost: Amount,
}

#[derive(Debug, Clone)]
/// Details of an account transaction. This always has a sender and is paid for,
/// and it might have some other effects on the state of the chain.
Expand All @@ -2015,6 +2029,8 @@ pub struct AccountTransactionDetails {
pub cost: Amount,
/// Sender of the transaction.
pub sender: AccountAddress,
/// Optional sponsor of the transaction
pub sponsor: Option<SponsorDetails>,
/// Effects of the account transaction, if any.
pub effects: Upward<AccountTransactionEffects>,
}
Expand Down
16 changes: 14 additions & 2 deletions src/types/summary_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::{
};

use crate::{
types::Address,
types::{Address, SponsorDetails},
v2::upward::{UnknownDataError, Upward},
};
use concordium_base::{
Expand All @@ -42,6 +42,8 @@ pub(crate) struct BlockItemSummary {
/// Sender, if available. The sender is always available for account
/// transactions.
sender: Option<AccountAddress>,
/// Optional sponsor of the transaction.
sponsor: Option<AccountAddress>,
/// Hash of the transaction.
hash: hashes::TransactionHash,
/// The amount of CCD the transaction was charged to the sender.
Expand Down Expand Up @@ -510,6 +512,7 @@ impl TryFrom<super::BlockItemSummary> for BlockItemSummary {
super::AccountTransactionDetails {
cost,
sender,
sponsor,
effects,
},
) => {
Expand Down Expand Up @@ -905,6 +908,7 @@ impl TryFrom<super::BlockItemSummary> for BlockItemSummary {
};
BlockItemSummary {
sender: Some(sender),
sponsor: sponsor.map(|s| s.sponsor),
hash: bi.hash,
cost,
energy_cost: bi.energy_cost,
Expand All @@ -919,6 +923,7 @@ impl TryFrom<super::BlockItemSummary> for BlockItemSummary {
reg_id,
}) => BlockItemSummary {
sender: None,
sponsor: None,
hash: bi.hash,
cost: Amount::zero(),
energy_cost: bi.energy_cost,
Expand All @@ -941,6 +946,7 @@ impl TryFrom<super::BlockItemSummary> for BlockItemSummary {
let payload = payload.known_or_err()?;
BlockItemSummary {
sender: None,
sponsor: None,
hash: bi.hash,
cost: Amount::zero(),
energy_cost: bi.energy_cost,
Expand All @@ -967,6 +973,7 @@ impl TryFrom<super::BlockItemSummary> for BlockItemSummary {

BlockItemSummary {
sender: None,
sponsor: None,
hash: bi.hash,
cost: Amount::zero(),
energy_cost: bi.energy_cost,
Expand Down Expand Up @@ -1012,12 +1019,14 @@ fn convert_account_transaction(
ty: Option<TransactionType>,
cost: Amount,
sender: AccountAddress,
sponsor: Option<AccountAddress>,
value: BlockItemResult,
) -> Result<super::AccountTransactionDetails, ConversionError> {
let mk_none = |reject_reason| {
Ok(super::AccountTransactionDetails {
cost,
sender,
sponsor: sponsor.map(|s| SponsorDetails { sponsor: s, cost }),
effects: Upward::Known(super::AccountTransactionEffects::None {
transaction_type: ty,
reject_reason: Upward::Known(reject_reason),
Expand All @@ -1029,6 +1038,7 @@ fn convert_account_transaction(
Ok(super::AccountTransactionDetails {
cost,
sender,
sponsor: sponsor.map(|s| SponsorDetails { sponsor: s, cost }),
effects: Upward::Known(effects),
})
};
Expand Down Expand Up @@ -1509,12 +1519,14 @@ impl TryFrom<BlockItemSummary> for super::BlockItemSummary {
let index = value.index;
let energy_cost = value.energy_cost;
let hash = value.hash;
let sponsor = value.sponsor;
let sender = value
.sender
.ok_or(ConversionError::InvalidTransactionResult(
"Expect `BlockItemType::Account` to have a sender".to_string(),
))?;
let details = convert_account_transaction(ty, value.cost, sender, value.result)?;
let details =
convert_account_transaction(ty, value.cost, sender, sponsor, value.result)?;
Ok(super::BlockItemSummary {
index,
energy_cost,
Expand Down
Loading