Skip to content

Commit 31262d8

Browse files
authored
Merge pull request #296 from Concordium/forward-compatible
Forward-compatible Rust SDK
2 parents fb0c3cf + ed919d7 commit 31262d8

31 files changed

+1749
-1021
lines changed

CHANGELOG.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,88 @@
11
## Unreleased changes
2+
23
- Removed authorization from `TokenClient` validation.
34
- Added `validate_mint`, `validate_burn`, `validate_allow_list_update`, `validate_deny_list_update` methods to `TokenClient`.
45
- Added `update_token_info` method to `TokenClient`.
56
- Added `Validation` as a separate enum for `TokenClient` operations.
67
- Remove use of `CborTokenHolder` wrapper.
78

9+
- Introduce `ProtocolVersionInt` newtype, wrapping the `u64` representation of the `ProtocolVersion`. This type is forward-compatible, meaning future protocol versions can be represented using this type.
10+
- BREAKING: Change type `ProtocolVersion` to `ProtocolVersionInt` for field `protocol_version` in the types listed below. Now introducing new protocol versions in `ProtocolVersion` does not result in RPC parsing errors, and consumers of this library can write more applications that are more forward-compatible.
11+
- `BlockInfo`
12+
- `ConsensusInfo`
13+
- `CommonRewardData`
14+
- Introduce `Upward<A, R = ()>` for representing types, which might get extended in a future version of the Concordium Node API and allows the consumer of this library to decide how to handle some unknown future data, like new transaction types and chain events.
15+
- Use the `WasmVersionInt` defined in `concordium-base` for the wasm version (smart contract version) to make it forward-compatible.
16+
- Changed the `Indexer` module to use a new `OnFinalizationError` and the new result types `OnFinalizationResult`/`TraverseResult` when traversing and processing blocks. The indexer's errors/results can now represent the `Unknown` types as part of adding forward-compatibility.
17+
- BREAKING: Change types related to gRPC API responses to wrap `Upward` for values which might be extended in a future version of the API of the Concordium Node.
18+
19+
The changes are for:
20+
- Type `BlockItemSummary` field `details`.
21+
- Method `BlockItemSummary::affected_addresses` return value.
22+
- Method `BlockItemSummary::affected_contracts` return value.
23+
- Type `AccountTransactionDetails` field `effects`.
24+
- Method `AccountTransactionDetails::transaction_type` return value.
25+
- Method `Client::get_block_special_events` response stream items.
26+
- Associated type `Indexer::Data` for `indexer::BlockEventsIndexer`.
27+
- Method `Client::get_block_items` response stream items.
28+
- Method `Client::get_finalized_block_item` return type.
29+
- Type `PendingUpdate` field `effect`.
30+
- Type `ViewError::QueryFailed`.
31+
- Type `ContractInitError::Failed`.
32+
- Type `ContractUpdateError::Failed`.
33+
- Type `ContractInitHandle::Failed`.
34+
- Type `Cis2DryRunError::NodeRejected`.
35+
- Type `Cis2QueryError::NodeRejected`.
36+
- Type `Cis3PermitDryRunError::NodeRejected`.
37+
- Type `Cis3SupportsPermitError::NodeRejected`.
38+
- Type `Cis4QueryError::NodeRejected`.
39+
- Method `Cis4QueryError::is_contract_error` return value.
40+
- Type `Cis4TransactionError::NodeRejected`.
41+
- Type `ModuleDeployError::Failed`.
42+
- Type `DryRunModuleDeployError::Failed`.
43+
- Type `RejectedTransaction` field `reason`.
44+
- Method `ContractClient::view<P, A, E>` require `E` to implement `From<v2::Upward<RejectReason>>`.
45+
- Method `ContractClient::view_raw<A, E>` require `E` to implement `From<v2::Upward<RejectReason>>`.
46+
- Method `ContractClient::invoke_raw<E>` require `E` to implement `From<v2::Upward<RejectReason>>`.
47+
- Method `ContractClient::dry_run_update<P, E>` require `E` to implement `From<v2::Upward<RejectReason>>`.
48+
- Method `ContractClient::dry_run_update<P, E>` require `E` to implement `From<v2::Upward<RejectReason>>`.
49+
- Method `BlockItemSummary::is_rejected_account_transaction` return value.
50+
- Method `BlockItemSummaryDetails::is_rejected` return value.
51+
- Method `AccountTransactionEffects::is_rejected` return value.
52+
- Type `AccountTransactionEffects` field `reject_reason`.
53+
- Type `InvokeContractResult` field `reason`.
54+
- Type `UpdateInstruction` field `payload` now needs to be decoded on-demand, ensuring errors due to new variants for `UpdatePayload` can be handled separately and the rest of `UpdateInstruction` can still be read.
55+
- Type `UpdateDetails` field `payload` is wrapped.
56+
- Method `UpdateDetails::update_type` return type is wrapped.
57+
- Type `AccountTransactionEffects::BakerConfigured` field `data` from `Vec<BakerEvent>` to `Vec<Upward<BakerEvent>>`.
58+
- Type `AccountTransactionEffects::DelegationConfigured` field `data` from `Vec<DelegationEvent>` to `Vec<Upward<DelegationEvent>>`.
59+
- Type `InvokeContractResult` field `events` of `Success` variant is now `Vec<Upward<ContractTraceElement>>`.
60+
- Type `InvokeInstanceSuccess` field `events` is now `Vec<Upward<ContractTraceElement>>`.
61+
- Method `ContractUpdateBuilder::events` return type from `&[ContractTraceElement]` to `&[Upward<ContractTraceElement>]`.
62+
- Associated type `Indexer::Data` for `AffectedContractIndexer` now wraps the affected contract addresses in `Upward`.
63+
- Type `AccountTransactionEffects` field `effects` of `ContractUpdateIssued` variant is now `Vec<Upward<ContractTraceElement>>`.
64+
- Method `BlockItemSummary::contract_update_logs` now wraps the iterator items in `Upward`.
65+
- Method `BlockItemSummaryDetails::contract_update_logs` now wraps the iterator items in `Upward`.
66+
- Method `AccountTransactionEffects::affected_addresses` now wraps the return type in `Upward`.
67+
- Method `ExecutionTree::affected_addresses` now wraps the return type in `Upward`.
68+
- Method `ExecutionTree::events` now wraps the `Iterator::Item` in `Upward`.
69+
- Method `ExecutionTree::execution_tree` return type was changed from `Option<ExecutionTree>` to `Option<Upward<ExecutionTree>>`
70+
- Method `ExecutionTree::contract_update` now wraps return type in `Upward`.
71+
- Function `execution_tree` parameter changed from `Vec<Upward<ContractTraceElement>>` to `Vec<ContractTraceElement>`.
72+
- Type `ExecutionTreeV0` field `rest` change from `Vec<TraceV0>` to `Vec<Upward<TraceV0>>`.
73+
- Type `ExecutionTreeV1` field `events` change from `Vec<TraceV1>` to `Vec<Upward<TraceV1>>`.
74+
- Type `NodeDetails` variant `Node` is now wrapped in `Upward`.
75+
- Type `NodeInfo` field `details` is now wrapped in `Upward`.
76+
- Type `Peer` field `consensus_info` is now wrapped in `Upward`.
77+
- Type `PeerConsensusInfo::Node` unnamed field is now wrapped in `Upward`.
78+
- Type `AccountInfo` field `account_stake` changes from `Option<AccountStakingInfo>` to `Option<Upward<AccountStakingInfo>>`.
79+
- Type `Cooldown` field `status` is now wrapped in `Upward`.
80+
- Type `BakerEvent::BakerSetOpenStatus` field `open_status` is now wrapped in `Upward`.
81+
- Type `AccountInfo` field `account_credentials` change from `BTreeMap<CredentialIndex,Versioned<AccountCredentialWithoutProofs<ArCurve, AttributeKind>>>` to `BTreeMap<CredentialIndex,Versioned<Upward<AccountCredentialWithoutProofs<ArCurve, AttributeKind>>>>`.
82+
- Type `BakerPoolInfo` moved from `concordium-base` to the `rust-sdk`.
83+
- Type `Event`/`BakerPoolInfo` field `open_status` is now wrapped in `Upward`.
84+
- Bubble `Upward` from new variants of `VerifyKey` to `Upward<AccountCredentialWithoutProofs<...>>` in `AccountInfo::account_credentials`.
85+
886
## 7.0.0
987

1088
Adds support for integrating with Concordium nodes running protocol version 9.

examples/balance-summary.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use clap::AppSettings;
55
use concordium_rust_sdk::{
66
common::types::Amount,
7+
types::AccountStakingInfo,
78
v2::{self, BlockIdentifier},
89
};
910
use futures::{Future, TryStreamExt};
@@ -107,11 +108,13 @@ async fn main() -> anyhow::Result<()> {
107108
let mut client = closure_client.clone();
108109
async move {
109110
let info = client.get_account_info(&acc.into(), &block).await?.response;
110-
let additional_stake = info
111-
.account_stake
112-
.map_or(Amount::zero(), |baker_delegator| {
113-
baker_delegator.staked_amount()
114-
});
111+
let additional_stake =
112+
info.account_stake
113+
.map_or(Amount::zero(), |baker_delegator| {
114+
baker_delegator
115+
.as_ref()
116+
.map_or(Amount::zero(), AccountStakingInfo::staked_amount)
117+
});
115118
let additional_liquid_amount = info.account_amount
116119
- std::cmp::max(additional_stake, info.account_release_schedule.total);
117120
Ok::<_, anyhow::Error>((

examples/block-stats.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ async fn main() -> anyhow::Result<()> {
8989
.await?
9090
.response
9191
.try_fold(0, |count, ev| async move {
92+
let v2::Upward::Known(ev) = ev else {
93+
return Ok(count);
94+
};
9295
let add = match ev {
9396
SpecialTransactionOutcome::PaydayFoundationReward { .. } => 1u32,
9497
SpecialTransactionOutcome::PaydayAccountReward { .. } => 1u32,

examples/find-account.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Find when an account was created on the chain.
22
//! That is, the block in which the account creation transaction is committed.
33
use clap::AppSettings;
4-
use concordium_rust_sdk::{id::types::AccountAddress, types::BlockItemSummaryDetails, v2};
4+
use concordium_rust_sdk::{
5+
id::types::AccountAddress, types::BlockItemSummaryDetails, v2, v2::upward::Upward,
6+
};
57
use futures::stream::StreamExt;
68
use structopt::StructOpt;
79

@@ -31,12 +33,12 @@ async fn main() -> anyhow::Result<()> {
3133
println!("Account not found.");
3234
return Ok(());
3335
};
34-
println!("Account created in block {}.", bh);
36+
println!("Account created in block {bh}.");
3537
let bi = client.get_block_info(&bh).await?.response;
3638
println!("Timestamp of this block {}.", bi.block_slot_time);
3739
let mut block_summary = client.get_block_transaction_events(&bh).await?.response;
3840
while let Some(summary) = block_summary.next().await.transpose()? {
39-
if let BlockItemSummaryDetails::AccountCreation(ac) = &summary.details {
41+
if let Upward::Known(BlockItemSummaryDetails::AccountCreation(ac)) = &summary.details {
4042
if ac.address == app.account {
4143
println!("Created by transaction hash {}", summary.hash);
4244
break;

examples/list-account-balances.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use concordium_rust_sdk::{
77
id,
88
id::types::AccountAddress,
99
types::{AccountStakingInfo, CredentialType},
10-
v2::{self, BlockIdentifier},
10+
v2::{self, upward::UnknownDataError, BlockIdentifier},
1111
};
1212
use futures::TryStreamExt;
1313
use serde::Serializer;
@@ -111,15 +111,14 @@ async fn main() -> anyhow::Result<()> {
111111
for res in futures::future::join_all(handles).await {
112112
let (acc, info) = res??;
113113
let is_baker = if let Some(account_stake) = info.account_stake {
114-
match account_stake {
114+
match account_stake.known_or_err()? {
115115
AccountStakingInfo::Baker { staked_amount, .. } => {
116116
num_bakers += 1;
117117
total_staked_amount += staked_amount;
118118
true
119119
}
120120
AccountStakingInfo::Delegated { staked_amount, .. } => {
121121
total_delegated_amount += staked_amount;
122-
123122
false
124123
}
125124
}
@@ -129,18 +128,18 @@ async fn main() -> anyhow::Result<()> {
129128

130129
total_amount += info.account_amount;
131130

132-
let acc_type =
133-
info.account_credentials
134-
.get(&0.into())
135-
.map_or(CredentialType::Normal, |cdi| match cdi.value {
136-
id::types::AccountCredentialWithoutProofs::Initial { .. } => {
137-
num_initial += 1;
138-
CredentialType::Initial
139-
}
140-
id::types::AccountCredentialWithoutProofs::Normal { .. } => {
141-
CredentialType::Normal
142-
}
143-
});
131+
let acc_type = info.account_credentials.get(&0.into()).map_or(
132+
Ok::<_, UnknownDataError>(CredentialType::Normal),
133+
|cdi| match cdi.value.as_ref().known_or_err()? {
134+
id::types::AccountCredentialWithoutProofs::Initial { .. } => {
135+
num_initial += 1;
136+
Ok(CredentialType::Initial)
137+
}
138+
id::types::AccountCredentialWithoutProofs::Normal { .. } => {
139+
Ok(CredentialType::Normal)
140+
}
141+
},
142+
)?;
144143

145144
let row = Row {
146145
address: acc,

examples/list-account-creations.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::AppSettings;
33
use concordium_rust_sdk::{
44
indexer::{TransactionIndexer, TraverseConfig},
55
types::{AbsoluteBlockHeight, BlockItemSummary, BlockItemSummaryDetails, CredentialType},
6-
v2,
6+
v2::{self, upward::Upward},
77
};
88
use structopt::StructOpt;
99

@@ -60,6 +60,9 @@ async fn main() -> anyhow::Result<()> {
6060
}
6161
}
6262
for BlockItemSummary { details, .. } in summaries {
63+
let Upward::Known(details) = details else {
64+
continue;
65+
};
6366
match details {
6467
BlockItemSummaryDetails::AccountTransaction(_) => {}
6568
BlockItemSummaryDetails::AccountCreation(x) => {

examples/list-all-transactions.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use concordium_rust_sdk::{
88
types::{
99
AbsoluteBlockHeight, AccountTransactionEffects, BlockItemSummaryDetails, TransactionType,
1010
},
11-
v2,
11+
v2::{self, upward::Upward},
1212
};
1313
use std::collections::HashSet;
1414
use structopt::StructOpt;
@@ -82,18 +82,24 @@ async fn main() -> anyhow::Result<()> {
8282
}
8383
}
8484
for bisummary in summary {
85-
if let BlockItemSummaryDetails::AccountTransaction(at) = &bisummary.details {
86-
if types.is_empty() || at.transaction_type().is_some_and(|tt| types.contains(&tt)) {
87-
let is_success = !matches!(&at.effects, AccountTransactionEffects::None { .. });
88-
let type_string = at
89-
.transaction_type()
90-
.map_or_else(|| "N/A".into(), |tt| tt.to_string());
91-
println!(
92-
"{}, {}, {}, {}, {}",
93-
bi.block_slot_time, bi.block_hash, bisummary.hash, is_success, type_string
94-
)
95-
}
85+
let Upward::Known(BlockItemSummaryDetails::AccountTransaction(at)) = &bisummary.details
86+
else {
87+
continue;
88+
};
89+
let Upward::Known(effects) = &at.effects else {
90+
continue;
91+
};
92+
let Some(transaction_type) = effects.transaction_type() else {
93+
continue;
94+
};
95+
if !types.is_empty() && !types.contains(&transaction_type) {
96+
continue;
9697
}
98+
let is_success = !matches!(effects, AccountTransactionEffects::None { .. });
99+
println!(
100+
"{}, {}, {}, {}, {}",
101+
bi.block_slot_time, bi.block_hash, bisummary.hash, is_success, transaction_type
102+
);
97103
}
98104
}
99105
Ok(())

examples/list-initial-accounts.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! List initial accounts created between two given timestamps.
22
use anyhow::Context;
33
use clap::AppSettings;
4-
use concordium_rust_sdk::{types::AccountInfo, v2};
4+
use concordium_rust_sdk::{
5+
types::AccountInfo,
6+
v2::{self, upward},
7+
};
58
use futures::TryStreamExt;
69
use std::{collections::BTreeSet, io::Write, path::PathBuf};
710
use structopt::StructOpt;
@@ -122,17 +125,18 @@ async fn main() -> anyhow::Result<()> {
122125
}
123126
for ainfo in futures::future::join_all(handles).await {
124127
let ainfo: AccountInfo = ainfo?.response;
125-
let is_initial = ainfo
126-
.account_credentials
127-
.get(&0.into())
128-
.is_some_and(|cdi| match &cdi.value {
128+
let is_initial = if let Some(acred) = ainfo.account_credentials.get(&0.into()) {
129+
match acred.value.as_ref().known_or_err()? {
129130
concordium_rust_sdk::id::types::AccountCredentialWithoutProofs::Initial {
130131
..
131-
} => true,
132+
} => Ok::<_, upward::UnknownDataError>(true),
132133
concordium_rust_sdk::id::types::AccountCredentialWithoutProofs::Normal {
133134
..
134-
} => false,
135-
});
135+
} => Ok(false),
136+
}
137+
} else {
138+
Ok(false)
139+
}?;
136140
if is_initial && !start_block_accounts.contains(&ainfo.account_address) {
137141
writeln!(&mut out, "{}", ainfo.account_address)?;
138142
}

examples/protocol-updates.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ async fn send_and_wait(
320320
pv: ProtocolVersion,
321321
) -> anyhow::Result<bool> {
322322
let ci = client.get_consensus_info().await?;
323-
if ci.protocol_version == pv {
323+
if ci.protocol_version == pv.into() {
324324
let submission_id = client
325325
.send_block_item(block_item)
326326
.await
@@ -341,7 +341,7 @@ async fn send_and_wait(
341341
loop {
342342
interval.tick().await;
343343
let ci = client.get_consensus_info().await?;
344-
if ci.protocol_version > pv {
344+
if ci.protocol_version > pv.into() {
345345
break;
346346
}
347347
}

0 commit comments

Comments
 (0)