diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d60ee6ed3..1c4d896bbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` @@ -48,6 +50,7 @@ - `types::smart_contracts::InstanceInfo` - `types::smart_contracts::ContractContext` - `types::smart_contracts::InvokeContractResult` + - Support for sponsored transactions. ## 8.0.0 diff --git a/Cargo.toml b/Cargo.toml index cf5a0ef47b..f2e0a9634b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "concordium-rust-sdk" -version = "8.0.0" +version = "9.0.0-alpha.0" authors = ["Concordium "] edition = "2021" rust-version = "1.85" @@ -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" diff --git a/concordium-base b/concordium-base index 70667d9161..c541f60334 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 70667d91611edfd5c5d26b2407fa8b3c1e16f6e9 +Subproject commit c541f603343df033625e44025d71789b731ba0e0 diff --git a/examples/sponsored-plt-transfer.rs b/examples/sponsored-plt-transfer.rs new file mode 100644 index 0000000000..cc40fe7bd1 --- /dev/null +++ b/examples/sponsored-plt-transfer.rs @@ -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(()) +} diff --git a/src/lib.md b/src/lib.md index 07a3e948e3..d7699acaae 100644 --- a/src/lib.md +++ b/src/lib.md @@ -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 { @@ -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`] into [`Result`](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)?; ``` @@ -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). \ No newline at end of file +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). diff --git a/src/types/mod.rs b/src/types/mod.rs index ea468d5c3d..b314b7a65a 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -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], @@ -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(), @@ -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. @@ -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, /// Effects of the account transaction, if any. pub effects: Upward, } diff --git a/src/types/summary_helper.rs b/src/types/summary_helper.rs index 43a6293443..dd7573195f 100644 --- a/src/types/summary_helper.rs +++ b/src/types/summary_helper.rs @@ -16,7 +16,7 @@ use super::{ }; use crate::{ - types::Address, + types::{Address, SponsorDetails}, v2::upward::{UnknownDataError, Upward}, }; use concordium_base::{ @@ -42,6 +42,8 @@ pub(crate) struct BlockItemSummary { /// Sender, if available. The sender is always available for account /// transactions. sender: Option, + /// Optional sponsor of the transaction. + sponsor: Option, /// Hash of the transaction. hash: hashes::TransactionHash, /// The amount of CCD the transaction was charged to the sender. @@ -510,6 +512,7 @@ impl TryFrom for BlockItemSummary { super::AccountTransactionDetails { cost, sender, + sponsor, effects, }, ) => { @@ -905,6 +908,7 @@ impl TryFrom for BlockItemSummary { }; BlockItemSummary { sender: Some(sender), + sponsor: sponsor.map(|s| s.sponsor), hash: bi.hash, cost, energy_cost: bi.energy_cost, @@ -919,6 +923,7 @@ impl TryFrom for BlockItemSummary { reg_id, }) => BlockItemSummary { sender: None, + sponsor: None, hash: bi.hash, cost: Amount::zero(), energy_cost: bi.energy_cost, @@ -941,6 +946,7 @@ impl TryFrom for BlockItemSummary { let payload = payload.known_or_err()?; BlockItemSummary { sender: None, + sponsor: None, hash: bi.hash, cost: Amount::zero(), energy_cost: bi.energy_cost, @@ -967,6 +973,7 @@ impl TryFrom for BlockItemSummary { BlockItemSummary { sender: None, + sponsor: None, hash: bi.hash, cost: Amount::zero(), energy_cost: bi.energy_cost, @@ -1012,12 +1019,14 @@ fn convert_account_transaction( ty: Option, cost: Amount, sender: AccountAddress, + sponsor: Option, value: BlockItemResult, ) -> Result { 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), @@ -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), }) }; @@ -1509,12 +1519,14 @@ impl TryFrom 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, diff --git a/src/v2/conversions.rs b/src/v2/conversions.rs index c5ed1bd363..fb9e37e2c7 100644 --- a/src/v2/conversions.rs +++ b/src/v2/conversions.rs @@ -26,7 +26,10 @@ use concordium_base::{ updates, }; use cooldown::CooldownStatus; -use std::collections::{BTreeMap, BTreeSet}; +use std::{ + collections::{BTreeMap, BTreeSet}, + io::Cursor, +}; fn consume(bytes: &[u8]) -> Result { let mut cursor = std::io::Cursor::new(bytes); @@ -737,11 +740,10 @@ impl TryFrom for crate::id::secret_sharing::Threshold { fn try_from(value: ArThreshold) -> Result { if let Ok(v) = u8::try_from(value.value) { - if v == 0 { - Err(tonic::Status::internal("Unexpected zero AR threshold.")) - } else { - Ok(Self(v)) - } + Self::try_from(v).map_err(|e| { + let () = e; + tonic::Status::internal("Unexpected zero AR threshold.") + }) } else { Err(tonic::Status::internal("Unexpected AR threshold.")) } @@ -1294,6 +1296,56 @@ impl TryFrom } } +impl TryFrom + for concordium_base::transactions::AccountTransactionV1< + concordium_base::transactions::EncodedPayload, + > +{ + type Error = tonic::Status; + + fn try_from(value: AccountTransactionV1) -> Result { + let payload: concordium_base::transactions::EncodedPayload = + value.payload.require()?.try_into()?; + let payload_size = payload.size(); + let header = { + let header = value.header.require()?; + let sender = header.sender.require()?.try_into()?; + let sponsor = match header.sponsor { + Some(sponsor) => Some(sponsor.try_into()?), + None => None, + }; + let nonce = header.sequence_number.require()?.into(); + let energy_amount = header.energy_amount.require()?.into(); + let expiry = header.expiry.require()?.into(); + concordium_base::transactions::TransactionHeaderV1 { + sponsor, + sender, + nonce, + energy_amount, + payload_size, + expiry, + } + }; + Ok(Self { + signatures: concordium_base::common::types::TransactionSignaturesV1 { + sender: value + .signatures + .to_owned() + .require()? + .sender_signatures + .require()? + .try_into()?, + sponsor: match value.signatures.require()?.sponsor_signatures { + Some(sponsor_sigs) => Some(sponsor_sigs.try_into()?), + None => None, + }, + }, + header, + payload, + }) + } +} + impl TryFrom for crate::id::types::AccountCredentialMessage< crate::id::constants::IpPairing, @@ -1390,11 +1442,34 @@ impl TryFrom Item::CredentialDeployment(Box::new(cd.try_into()?)) } block_item::BlockItem::UpdateInstruction(ui) => Item::UpdateInstruction(ui.try_into()?), + block_item::BlockItem::AccountTransactionV1(atv1) => { + Item::AccountTransactionV1(atv1.try_into()?) + } + block_item::BlockItem::RawBlockItem(bytes) => { + match concordium_base::transactions::BlockItem::deserial(&mut Cursor::new(bytes)) { + Ok(bi) => Ok(bi), + Err(err) => Err(tonic::Status::new( + tonic::Code::InvalidArgument, + err.to_string(), + )), + }? + } }; Ok(out) } } +impl TryFrom for super::types::SponsorDetails { + type Error = tonic::Status; + + fn try_from(v: SponsorDetails) -> Result { + Ok(Self { + cost: v.cost.require()?.into(), + sponsor: v.sponsor.require()?.try_into()?, + }) + } +} + impl TryFrom for super::types::AccountTransactionDetails { type Error = tonic::Status; @@ -1402,6 +1477,10 @@ impl TryFrom for super::types::AccountTransactionDeta Ok(Self { cost: v.cost.require()?.into(), sender: v.sender.require()?.try_into()?, + sponsor: match v.sponsor { + None => None, + Some(sponsor) => Some(sponsor.try_into()?), + }, effects: Upward::from(v.effects.map(TryFrom::try_from).transpose()?), }) } diff --git a/src/v2/generated/concordium.v2.rs b/src/v2/generated/concordium.v2.rs index 2ae7b61800..5b74196090 100644 --- a/src/v2/generated/concordium.v2.rs +++ b/src/v2/generated/concordium.v2.rs @@ -2770,10 +2770,21 @@ pub mod update_payload { CreatePltUpdate(super::plt::CreatePlt), } } +/// Details about the sponsor of a transaction. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SponsorDetails { + /// The cost of the transaction. Paid by the sponsor. + #[prost(message, optional, tag = "1")] + pub cost: ::core::option::Option, + /// The sponsor of the transaction. + #[prost(message, optional, tag = "2")] + pub sponsor: ::core::option::Option, +} /// Details about an account transaction. #[derive(Clone, PartialEq, ::prost::Message)] pub struct AccountTransactionDetails { - /// The cost of the transaction. Paid by the sender. + /// The cost of the transaction. Paid by the sender. This will be zero if the + /// transaction is sponsored. #[prost(message, optional, tag = "1")] pub cost: ::core::option::Option, /// The sender of the transaction. @@ -2782,6 +2793,9 @@ pub struct AccountTransactionDetails { /// The effects of the transaction. #[prost(message, optional, tag = "3")] pub effects: ::core::option::Option, + /// The optional sponsor details of the transaction. + #[prost(message, optional, tag = "4")] + pub sponsor: ::core::option::Option, } /// Details of an account creation. These transactions are free, and we only /// ever get a response for them if the account is created, hence no failure @@ -4313,7 +4327,7 @@ pub mod node_info { #[derive(Clone, PartialEq, ::prost::Message)] pub struct SendBlockItemRequest { /// This field might be extended in future versions of the API. - #[prost(oneof = "send_block_item_request::BlockItem", tags = "1, 2, 3")] + #[prost(oneof = "send_block_item_request::BlockItem", tags = "1, 2, 3, 4, 5")] pub block_item: ::core::option::Option, } /// Nested message and enum types in `SendBlockItemRequest`. @@ -4333,6 +4347,13 @@ pub mod send_block_item_request { /// to make future update instructions. #[prost(message, tag = "3")] UpdateInstruction(super::UpdateInstruction), + /// Account transactions v1 are messages which are signed and paid for by + /// either an account or a sponsor. + #[prost(message, tag = "4")] + AccountTransactionV1(super::AccountTransactionV1), + /// A block item which has already been serialized to the format expected by the node. + #[prost(bytes, tag = "5")] + RawBlockItem(::prost::alloc::vec::Vec), } } /// Credential deployments create new accounts. They are not paid for @@ -4393,6 +4414,17 @@ pub struct AccountTransactionSignature { #[prost(map = "uint32, message", tag = "1")] pub signatures: ::std::collections::HashMap, } +/// Account transaction signatures from the sender and optionally the sponsor of +/// the transaction. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountTransactionV1Signatures { + /// Signatures from the sender of the transaction. + #[prost(message, optional, tag = "1")] + pub sender_signatures: ::core::option::Option, + /// The optional signature of the sponsor of the transaction. + #[prost(message, optional, tag = "2")] + pub sponsor_signatures: ::core::option::Option, +} /// Header of an account transaction that contains basic data to check whether /// the sender and the transaction are valid. The header is shared by all transaction types. #[derive(Clone, PartialEq, ::prost::Message)] @@ -4410,6 +4442,26 @@ pub struct AccountTransactionHeader { #[prost(message, optional, tag = "5")] pub expiry: ::core::option::Option, } +/// Header v1 of an account transaction with support for sponsored transactions. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountTransactionHeaderV1 { + /// Sender of the transaction. + #[prost(message, optional, tag = "1")] + pub sender: ::core::option::Option, + /// Sequence number of the transaction. + #[prost(message, optional, tag = "2")] + pub sequence_number: ::core::option::Option, + /// Maximum amount of energy the transaction can take to execute. + #[prost(message, optional, tag = "3")] + pub energy_amount: ::core::option::Option, + /// Latest time the transaction can included in a block. + #[prost(message, optional, tag = "5")] + pub expiry: ::core::option::Option, + /// The optional address of the account that sponsors the transaction, i.e. + /// pays the transaction fees associated with the execution. + #[prost(message, optional, tag = "6")] + pub sponsor: ::core::option::Option, +} /// Data required to initialize a new contract instance. #[derive(Clone, PartialEq, ::prost::Message)] pub struct InitContractPayload { @@ -4513,6 +4565,15 @@ pub struct PreAccountTransaction { #[prost(message, optional, tag = "2")] pub payload: ::core::option::Option, } +/// An unsigned account transaction v1. This is used with the +/// `GetTransactionSignHash` endpoint to obtain the message to sign. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PreAccountTransactionV1 { + #[prost(message, optional, tag = "1")] + pub header: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub payload: ::core::option::Option, +} /// Account transactions are messages which are signed and paid for by the sender /// account. #[derive(Clone, PartialEq, ::prost::Message)] @@ -4524,6 +4585,21 @@ pub struct AccountTransaction { #[prost(message, optional, tag = "3")] pub payload: ::core::option::Option, } +/// Account transactions v1 are messages which are signed and paid for by the sender +/// account or by the sponsor. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountTransactionV1 { + /// The signatures on the transaction by the source account and other + /// relevant parties such as sponsors. + #[prost(message, optional, tag = "1")] + pub signatures: ::core::option::Option, + /// The transaction header data. + #[prost(message, optional, tag = "2")] + pub header: ::core::option::Option, + /// The account transaction payload. + #[prost(message, optional, tag = "3")] + pub payload: ::core::option::Option, +} #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct UpdateInstructionHeader { #[prost(message, optional, tag = "1")] @@ -4869,7 +4945,7 @@ pub struct BlockItem { #[prost(message, optional, tag = "1")] pub hash: ::core::option::Option, /// This field might be extended in future versions of the API. - #[prost(oneof = "block_item::BlockItem", tags = "2, 3, 4")] + #[prost(oneof = "block_item::BlockItem", tags = "2, 3, 4, 5, 6")] pub block_item: ::core::option::Option, } /// Nested message and enum types in `BlockItem`. @@ -4889,6 +4965,13 @@ pub mod block_item { /// to make future update instructions. #[prost(message, tag = "4")] UpdateInstruction(super::UpdateInstruction), + /// Account transactions v1 are messages which are signed and paid for by + /// either an account or a sponsor. + #[prost(message, tag = "5")] + AccountTransactionV1(super::AccountTransactionV1), + /// A block item which has already been serialized to the format expected by the node. + #[prost(bytes, tag = "6")] + RawBlockItem(::prost::alloc::vec::Vec), } } /// Information about a particular validator with respect to @@ -7730,6 +7813,43 @@ pub mod queries_client { ); self.inner.unary(req, path, codec).await } + /// Get the hash to be signed for an account transaction v1. The + /// hash returned should be signed and the signatures included as an + /// AccountTransactionV1Signatures when calling `SendBlockItem`. This is + /// provided as a convenience to support cases where the right SDK is not + /// available for interacting with the node. If an SDK is available then it is + /// strongly recommended to compute this hash off-line using it. That reduces + /// the trust in the node, removes networking failure modes, and will perform + /// better. + pub async fn get_account_transaction_v1_sign_hash( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/concordium.v2.Queries/GetAccountTransactionV1SignHash", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "concordium.v2.Queries", + "GetAccountTransactionV1SignHash", + ), + ); + self.inner.unary(req, path, codec).await + } /// Get the values of chain parameters in effect in the given block. pub async fn get_block_chain_parameters( &mut self, diff --git a/src/v2/mod.rs b/src/v2/mod.rs index 3dfa3737c6..3f6d46970b 100644 --- a/src/v2/mod.rs +++ b/src/v2/mod.rs @@ -25,7 +25,7 @@ use concordium_base::{ base::{AccountIndex, BlockHeight, Epoch, GenesisIndex}, common::{ self, - types::{TransactionSignature, TransactionTime}, + types::{TransactionSignature, TransactionSignaturesV1, TransactionTime}, }, contracts_common::{ AccountAddress, AccountAddressParseError, Amount, ContractAddress, OwnedContractName, @@ -755,6 +755,18 @@ impl From<&transactions::TransactionHeader> for generated::AccountTransactionHea } } +impl From<&transactions::TransactionHeaderV1> for generated::AccountTransactionHeaderV1 { + fn from(v: &transactions::TransactionHeaderV1) -> Self { + Self { + sender: Some(generated::AccountAddress::from(v.sender)), + sponsor: v.sponsor.map(generated::AccountAddress::from), + sequence_number: Some(v.nonce.into()), + energy_amount: Some(v.energy_amount.into()), + expiry: Some(v.expiry.into()), + } + } +} + impl From for generated::AccountTransactionSignature { fn from(v: TransactionSignature) -> Self { (&v).into() @@ -789,6 +801,15 @@ impl From<&TransactionSignature> for generated::AccountTransactionSignature { } } +impl From<&TransactionSignaturesV1> for generated::AccountTransactionV1Signatures { + fn from(v: &TransactionSignaturesV1) -> Self { + Self { + sender_signatures: Some(v.sender.to_owned().into()), + sponsor_signatures: v.sponsor.to_owned().map(|s| s.into()), + } + } +} + impl IntoRequest for (&transactions::TransactionHeader, &transactions::Payload) { @@ -838,6 +859,24 @@ impl IntoRequest for &transacti generated::send_block_item_request::BlockItem::UpdateInstruction(v.into()), ), }, + transactions::BlockItem::AccountTransactionV1(v) => { + generated::SendBlockItemRequest { + block_item: Some( + generated::send_block_item_request::BlockItem::AccountTransactionV1( + generated::AccountTransactionV1 { + signatures: Some((&v.signatures).into()), + header: Some((&v.header).into()), + payload: { + let atp = generated::AccountTransactionPayload{ + payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())), + }; + Some(atp) + }, + }, + ), + ), + } + } }; tonic::Request::new(request) } diff --git a/src/v2/proto_schema_version.rs b/src/v2/proto_schema_version.rs index d756917118..21f14f6622 100644 --- a/src/v2/proto_schema_version.rs +++ b/src/v2/proto_schema_version.rs @@ -1 +1 @@ -pub const PROTO_SCHEMA_VERSION: &str = "6d04f703a2a8fbbd3f6a03e590cbaabf4ae99603"; +pub const PROTO_SCHEMA_VERSION: &str = "cc360366e47981bc1945ce39ba7f8fbd8902d423";