From e59a585bc715cba43cdd11eabadd26b91e32d1da Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Wed, 1 Jun 2022 23:08:29 -0700 Subject: [PATCH] feat: support stakedNodeId in addition to stakedAccountId in AccountCreate and AccountUpdate transactions --- Cargo.lock | 2 + sdk/rust/Cargo.toml | 2 +- .../src/account/account_create_transaction.rs | 33 +++++++++++-- .../src/account/account_update_transaction.rs | 37 +++++++++++--- sdk/rust/src/lib.rs | 1 - sdk/rust/src/serde/duration.rs | 38 -------------- sdk/rust/src/serde/duration_opt.rs | 49 ------------------- sdk/rust/src/serde/mod.rs | 6 --- sdk/rust/src/transaction/any.rs | 14 ++++-- sdk/rust/src/transaction/mod.rs | 12 +++-- .../Hedera/AccountCreateTransaction.swift | 17 +++++++ .../Hedera/AccountUpdateTransaction.swift | 17 +++++++ 12 files changed, 117 insertions(+), 111 deletions(-) delete mode 100644 sdk/rust/src/serde/duration.rs delete mode 100644 sdk/rust/src/serde/duration_opt.rs delete mode 100644 sdk/rust/src/serde/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 220b10f2..4cd36a02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1420,6 +1420,7 @@ dependencies = [ "hex", "serde", "serde_with_macros", + "time 0.3.9", ] [[package]] @@ -1679,6 +1680,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" dependencies = [ + "itoa", "libc", "num_threads", "serde", diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 76a3ad49..fdd9add6 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -17,7 +17,7 @@ time = { version = "0.3.9", features = ["serde"] } tokio = { version = "1.18.1" } hedera-proto = { path = "../../protobufs/rust", features = ["serde", "time_0_3"] } serde_json = "1.0.79" -serde_with = { version = "1.12.1", features = ["hex", "base64"] } +serde_with = { version = "1.12.1", features = ["hex", "base64", "time_0_3"] } tonic = { version = "0.7.1", features = ["compression"] } async-trait = "0.1.53" prost = "0.10.1" diff --git a/sdk/rust/src/account/account_create_transaction.rs b/sdk/rust/src/account/account_create_transaction.rs index 49555e5d..1c724898 100644 --- a/sdk/rust/src/account/account_create_transaction.rs +++ b/sdk/rust/src/account/account_create_transaction.rs @@ -34,7 +34,7 @@ pub struct AccountCreateTransactionData { pub receiver_signature_required: bool, /// The account is charged to extend its expiration date every this many seconds. - #[serde_as(as = "Option")] + #[serde_as(as = "Option>")] pub auto_renew_period: Option, /// The memo associated with the account. @@ -47,8 +47,13 @@ pub struct AccountCreateTransactionData { pub max_automatic_token_associations: u16, /// ID of the account to which this account is staking. + /// This is mutually exclusive with `staked_node_id`. pub staked_account_id: Option, + /// ID of the node this account is staked to. + /// This is mutually exclusive with `staked_account_id`. + pub staked_node_id: Option, + /// If true, the account declines receiving a staking reward. The default value is false. pub decline_staking_reward: bool, } @@ -63,6 +68,7 @@ impl Default for AccountCreateTransactionData { account_memo: String::new(), max_automatic_token_associations: 0, staked_account_id: None, + staked_node_id: None, decline_staking_reward: false, } } @@ -106,11 +112,19 @@ impl AccountCreateTransaction { } /// Set the ID of the account to which this account is staking. + /// This is mutually exclusive with `staked_node_id`. pub fn staked_account_id(&mut self, id: impl Into) -> &mut Self { self.body.data.staked_account_id = Some(id.into()); self } + /// Set the ID of the node to which this account is staking. + /// This is mutually exclusive with `staked_account_id`. + pub fn staked_node_id(&mut self, id: u64) -> &mut Self { + self.body.data.staked_node_id = Some(id); + self + } + /// Set to true, the account declines receiving a staking reward. The default value is false. pub fn decline_staking_reward(&mut self, decline: bool) -> &mut Self { self.body.data.decline_staking_reward = decline; @@ -137,9 +151,20 @@ impl ToTransactionDataProtobuf for AccountCreateTransactionData { ) -> services::transaction_body::Data { let key = self.key.as_ref().map(Key::to_protobuf); let auto_renew_period = self.auto_renew_period.as_ref().map(Duration::to_protobuf); - let staked_id = self.staked_account_id.as_ref().map(|id| { - services::crypto_create_transaction_body::StakedId::StakedAccountId(id.to_protobuf()) - }); + + let staked_id = match (&self.staked_account_id, self.staked_node_id) { + (_, Some(node_id)) => Some( + services::crypto_create_transaction_body::StakedId::StakedNodeId(node_id as i64), + ), + + (Some(account_id), _) => { + Some(services::crypto_create_transaction_body::StakedId::StakedAccountId( + account_id.to_protobuf(), + )) + } + + _ => None, + }; services::transaction_body::Data::CryptoCreateAccount( #[allow(deprecated)] diff --git a/sdk/rust/src/account/account_update_transaction.rs b/sdk/rust/src/account/account_update_transaction.rs index bd4aced2..9d7a8078 100644 --- a/sdk/rust/src/account/account_update_transaction.rs +++ b/sdk/rust/src/account/account_update_transaction.rs @@ -36,7 +36,7 @@ pub struct AccountUpdateTransactionData { pub receiver_signature_required: Option, /// The account is charged to extend its expiration date every this many seconds. - #[serde_as(as = "Option")] + #[serde_as(as = "Option>")] pub auto_renew_period: Option, /// The new expiration time to extend to (ignored if equal to or before the current one). @@ -53,8 +53,13 @@ pub struct AccountUpdateTransactionData { pub max_automatic_token_associations: Option, /// ID of the account to which this account is staking. + /// This is mutually exclusive with `staked_node_id`. pub staked_account_id: Option, + /// ID of the node this account is staked to. + /// This is mutually exclusive with `staked_account_id`. + pub staked_node_id: Option, + /// If true, the account declines receiving a staking reward. The default value is false. pub decline_staking_reward: Option, } @@ -103,11 +108,19 @@ impl AccountUpdateTransaction { } /// Set the ID of the account to which this account is staking. + /// This is mutually exclusive with `staked_node_id`. pub fn staked_account_id(&mut self, id: impl Into) -> &mut Self { self.body.data.staked_account_id = Some(id.into()); self } + /// Set the ID of the node to which this account is staking. + /// This is mutually exclusive with `staked_account_id`. + pub fn staked_node_id(&mut self, id: u64) -> &mut Self { + self.body.data.staked_node_id = Some(id); + self + } + /// Set to true, the account declines receiving a staking reward. The default value is false. pub fn decline_staking_reward(&mut self, decline: bool) -> &mut Self { self.body.data.decline_staking_reward = Some(decline); @@ -141,9 +154,19 @@ impl ToTransactionDataProtobuf for AccountUpdateTransactionData { services::crypto_update_transaction_body::ReceiverSigRequiredField::ReceiverSigRequiredWrapper(required) }); - let staked_id = self.staked_account_id.as_ref().map(|id| { - services::crypto_update_transaction_body::StakedId::StakedAccountId(id.to_protobuf()) - }); + let staked_id = match (&self.staked_account_id, self.staked_node_id) { + (_, Some(node_id)) => Some( + services::crypto_update_transaction_body::StakedId::StakedNodeId(node_id as i64), + ), + + (Some(account_id), _) => { + Some(services::crypto_update_transaction_body::StakedId::StakedAccountId( + account_id.to_protobuf(), + )) + } + + _ => None, + }; services::transaction_body::Data::CryptoUpdateAccount( #[allow(deprecated)] @@ -155,12 +178,14 @@ impl ToTransactionDataProtobuf for AccountUpdateTransactionData { auto_renew_period, expiration_time, memo: self.account_memo.clone(), - max_automatic_token_associations: self.max_automatic_token_associations.into(), + max_automatic_token_associations: self + .max_automatic_token_associations + .map(Into::into), decline_reward: self.decline_staking_reward, send_record_threshold_field: None, receive_record_threshold_field: None, receiver_sig_required_field: receiver_signature_required, - staked_id: staked_id, + staked_id, }, ) } diff --git a/sdk/rust/src/lib.rs b/sdk/rust/src/lib.rs index dce28129..f3e0aa45 100644 --- a/sdk/rust/src/lib.rs +++ b/sdk/rust/src/lib.rs @@ -24,7 +24,6 @@ mod file; mod key; mod query; mod schedule; -mod serde; mod signature; mod signer; mod token; diff --git a/sdk/rust/src/serde/duration.rs b/sdk/rust/src/serde/duration.rs deleted file mode 100644 index f7e42505..00000000 --- a/sdk/rust/src/serde/duration.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::fmt::{self, Formatter}; - -use serde::de::{self, Visitor}; -use serde::{Deserializer, Serializer}; -use time::Duration; - -#[allow(dead_code)] -pub fn serialize(duration: &Duration, serializer: S) -> Result -where - S: Serializer, -{ - serializer.serialize_i64(duration.whole_seconds()) -} - -#[allow(dead_code)] -pub fn deserialize<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - struct DurationSecondsVisitor; - - impl<'de> Visitor<'de> for DurationSecondsVisitor { - type Value = Duration; - - fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { - formatter.write_str("an integer between -2^63 and 2^63") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - Ok(Duration::seconds(value)) - } - } - - deserializer.deserialize_i64(DurationSecondsVisitor) -} diff --git a/sdk/rust/src/serde/duration_opt.rs b/sdk/rust/src/serde/duration_opt.rs deleted file mode 100644 index 6f043012..00000000 --- a/sdk/rust/src/serde/duration_opt.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::fmt::{self, Formatter}; - -use serde::de::{self, Visitor}; -use serde::{Deserializer, Serializer}; -use time::Duration; - -#[allow(dead_code)] -pub fn serialize(duration: &Option, serializer: S) -> Result -where - S: Serializer, -{ - match duration { - Some(duration) => serializer.serialize_i64(duration.whole_seconds()), - - None => serializer.serialize_none(), - } -} - -#[allow(dead_code)] -pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - struct DurationSecondsVisitor; - - impl<'de> Visitor<'de> for DurationSecondsVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { - formatter.write_str("an integer between -2^63 and 2^63") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - Ok(Some(Duration::seconds(value))) - } - - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } - - deserializer.deserialize_i64(DurationSecondsVisitor) -} diff --git a/sdk/rust/src/serde/mod.rs b/sdk/rust/src/serde/mod.rs deleted file mode 100644 index 474fc158..00000000 --- a/sdk/rust/src/serde/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub(crate) mod duration; -pub(crate) mod duration_opt; - -pub(crate) fn skip_if_string_empty(s: &str) -> bool { - s.is_empty() -} diff --git a/sdk/rust/src/transaction/any.rs b/sdk/rust/src/transaction/any.rs index 4f8dce1e..08286084 100644 --- a/sdk/rust/src/transaction/any.rs +++ b/sdk/rust/src/transaction/any.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use hedera_proto::services; use serde::{Deserialize, Deserializer}; -use serde_with::skip_serializing_none; +use serde_with::{serde_as, skip_serializing_none, DurationSeconds}; use time::Duration; use tonic::transport::Channel; use tonic::{Response, Status}; @@ -142,22 +142,30 @@ impl TransactionExecute for AnyTransactionData { // we create a proxy type that has the same layout but is only for AnyQueryData and does // derive(Deserialize). +#[serde_as] #[skip_serializing_none] #[derive(serde::Deserialize, serde::Serialize, Debug, Default)] #[serde(rename_all = "camelCase")] pub(crate) struct AnyTransactionBody { #[serde(flatten)] data: D, + #[serde(default)] node_account_ids: Option>, - #[serde(default, with = "crate::serde::duration_opt")] + + #[serde_as(as = "Option>")] + #[serde(default)] transaction_valid_duration: Option, + #[serde(default)] max_transaction_fee: Option, - #[serde(default, skip_serializing_if = "crate::serde::skip_if_string_empty")] + + #[serde(default, skip_serializing_if = "String::is_empty")] transaction_memo: String, + #[serde(default)] payer_account_id: Option, + #[serde(default)] transaction_id: Option, } diff --git a/sdk/rust/src/transaction/mod.rs b/sdk/rust/src/transaction/mod.rs index 6f092811..9f2819ce 100644 --- a/sdk/rust/src/transaction/mod.rs +++ b/sdk/rust/src/transaction/mod.rs @@ -1,7 +1,7 @@ use std::fmt; use std::fmt::{Debug, Formatter}; -use serde_with::{serde_as, skip_serializing_none, FromInto}; +use serde_with::{serde_as, skip_serializing_none, DurationSeconds, FromInto}; use time::Duration; use crate::execute::execute; @@ -42,13 +42,19 @@ where #[serde(flatten)] #[serde_as(as = "FromInto")] pub(crate) data: D, + pub(crate) node_account_ids: Option>, - #[serde(with = "crate::serde::duration_opt")] + + #[serde_as(as = "Option>")] pub(crate) transaction_valid_duration: Option, + pub(crate) max_transaction_fee: Option, - #[serde(skip_serializing_if = "crate::serde::skip_if_string_empty")] + + #[serde(skip_serializing_if = "String::is_empty")] pub(crate) transaction_memo: String, + pub(crate) payer_account_id: Option, + pub(crate) transaction_id: Option, } diff --git a/sdk/swift/Sources/Hedera/AccountCreateTransaction.swift b/sdk/swift/Sources/Hedera/AccountCreateTransaction.swift index 9d734547..21ac0ab4 100644 --- a/sdk/swift/Sources/Hedera/AccountCreateTransaction.swift +++ b/sdk/swift/Sources/Hedera/AccountCreateTransaction.swift @@ -73,9 +73,11 @@ public final class AccountCreateTransaction: Transaction { } /// ID of the account to which this account is staking. + /// This is mutually exclusive with `stakedNodeId`. public private(set) var stakedAccountId: AccountIdOrAlias? /// Sets the ID of the account to which this account is staking. + /// This is mutually exclusive with `stakedNodeId`. @discardableResult public func stakedAccountId(_ stakedAccountId: AccountIdOrAlias) -> Self { self.stakedAccountId = stakedAccountId @@ -83,6 +85,19 @@ public final class AccountCreateTransaction: Transaction { return self } + /// ID of the node this account is staked to. + /// This is mutually exclusive with `staked_account_id`. + public private(set) var stakedNodeId: UInt64 + + /// Sets the ID of the node this account is staked to. + /// This is mutually exclusive with `staked_account_id`. + @discardableResult + public func stakedNodeId(_ stakedNodeId: UInt64) -> Self { + self.stakedNodeId = stakedNodeId + + return self + } + /// If true, the account declines receiving a staking reward. The default value is false. public private(set) var declineStakingReward: Bool = false @@ -101,6 +116,7 @@ public final class AccountCreateTransaction: Transaction { case autoRenewPeriod case maxAutomaticTokenAssociations case stakedAccountId + case stakedNodeId case declineStakingReward } @@ -114,6 +130,7 @@ public final class AccountCreateTransaction: Transaction { try data.encodeIfPresent(autoRenewPeriod?.wholeSeconds, forKey: .autoRenewPeriod) try data.encode(maxAutomaticTokenAssociations, forKey: .maxAutomaticTokenAssociations) try data.encodeIfPresent(stakedAccountId, forKey: .stakedAccountId) + try data.encodeIfPresent(stakedNodeId, forKey: .stakedNodeId) try data.encode(declineStakingReward, forKey: .declineStakingReward) try super.encode(to: encoder) diff --git a/sdk/swift/Sources/Hedera/AccountUpdateTransaction.swift b/sdk/swift/Sources/Hedera/AccountUpdateTransaction.swift index 4f5d0565..bdff4f46 100644 --- a/sdk/swift/Sources/Hedera/AccountUpdateTransaction.swift +++ b/sdk/swift/Sources/Hedera/AccountUpdateTransaction.swift @@ -89,9 +89,11 @@ public class AccountUpdateTransaction: Transaction { } /// ID of the account to which this account is staking. + /// This is mutually exclusive with `stakedNodeId`. public private(set) var stakedAccountId: AccountIdOrAlias? /// Sets the ID of the account to which this account is staking. + /// This is mutually exclusive with `stakedNodeId`. @discardableResult public func stakedAccountId(_ stakedAccountId: AccountIdOrAlias) -> Self { self.stakedAccountId = stakedAccountId @@ -99,6 +101,19 @@ public class AccountUpdateTransaction: Transaction { return self } + /// ID of the node this account is staked to. + /// This is mutually exclusive with `staked_account_id`. + public private(set) var stakedNodeId: UInt64 + + /// Sets the ID of the node this account is staked to. + /// This is mutually exclusive with `staked_account_id`. + @discardableResult + public func stakedNodeId(_ stakedNodeId: UInt64) -> Self { + self.stakedNodeId = stakedNodeId + + return self + } + /// If true, the account declines receiving a staking reward. The default value is false. public private(set) var declineStakingReward: Bool? @@ -118,6 +133,7 @@ public class AccountUpdateTransaction: Transaction { case expiresAt case maxAutomaticTokenAssociations case stakedAccountId + case stakedNodeId case declineStakingReward } @@ -131,6 +147,7 @@ public class AccountUpdateTransaction: Transaction { try data.encodeIfPresent(expiresAt?.unixTimestampNanos, forKey: .expiresAt) try data.encodeIfPresent(maxAutomaticTokenAssociations, forKey: .maxAutomaticTokenAssociations) try data.encodeIfPresent(stakedAccountId, forKey: .stakedAccountId) + try data.encodeIfPresent(stakedNodeId, forKey: .stakedNodeId) try data.encodeIfPresent(declineStakingReward, forKey: .declineStakingReward) try super.encode(to: encoder)