Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ impl Sdk {
trace!("document_replace: broadcasting and awaiting response");
let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();
trace!("document_replace: broadcast completed");

match proof_result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform/tokens/transitions/burn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenBalance(owner_id, remaining_balance) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform/tokens/transitions/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(document) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(document) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(doc) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenBalance(owner_id, balance) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(document) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/src/platform/tokens/transitions/freeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenIdentityInfo(owner_id_result, info) => {
Ok(FreezeResult::IdentityInfo(owner_id_result, info))
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/src/platform/tokens/transitions/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenBalance(recipient_id_result, new_balance) => {
Ok(MintResult::TokenBalance(recipient_id_result, new_balance))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenPricingSchedule(owner_id, schedule) => {
Ok(SetPriceResult::PricingSchedule(owner_id, schedule))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenIdentitiesBalances(balances) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/src/platform/tokens/transitions/unfreeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenIdentityInfo(owner_id_result, info) => {
Ok(UnfreezeResult::IdentityInfo(owner_id_result, info))
Expand Down
2 changes: 2 additions & 0 deletions packages/rs-sdk/src/platform/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod shield_from_asset_lock;
pub mod shielded_transfer;
#[cfg(feature = "shielded")]
pub mod shielded_withdrawal;
pub mod state_transition_result;
pub mod top_up_address;
pub mod top_up_identity;
pub mod top_up_identity_from_addresses;
Expand All @@ -32,4 +33,5 @@ pub(crate) mod validation;
pub mod vote;
pub mod waitable;
pub mod withdraw_from_identity;
pub use state_transition_result::StateTransitionResult;
pub use txid::TxId;
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ impl<S: Signer<PlatformAddress>> WithdrawAddressFunds<S> for Sdk {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedAddressInfos(address_infos_map) => {
let mut expected_addresses: BTreeSet<PlatformAddress> =
Expand Down
13 changes: 10 additions & 3 deletions packages/rs-sdk/src/platform/transition/broadcast.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::broadcast_request::BroadcastRequestForStateTransition;
use super::put_settings::PutSettings;
use super::state_transition_result::StateTransitionResult;
use crate::error::StateTransitionBroadcastError;
use crate::platform::block_info_from_metadata::block_info_from_metadata;
use crate::sync::retry;
Expand Down Expand Up @@ -30,7 +31,7 @@ pub trait BroadcastStateTransition {
&self,
sdk: &Sdk,
settings: Option<PutSettings>,
) -> Result<T, Error>;
) -> Result<StateTransitionResult<T>, Error>;
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -264,8 +265,14 @@ impl BroadcastStateTransition for StateTransition {
&self,
sdk: &Sdk,
settings: Option<PutSettings>,
) -> Result<T, Error> {
) -> Result<StateTransitionResult<T>, Error> {
trace!(state_transition = %self.name(), "broadcast_and_wait: start");

// Compute the transition hash deterministically BEFORE broadcast.
// This is a SHA-256 hash of the serialized StateTransition and does
// not depend on blockchain state, so there is no race condition.
let transition_hash = self.transaction_id()?;
Comment thread
thepastaclaw marked this conversation as resolved.

trace!("broadcast_and_wait: step 1 - broadcasting");
self.broadcast(sdk, settings).await?;
trace!("broadcast_and_wait: step 2 - waiting for response");
Expand All @@ -274,6 +281,6 @@ impl BroadcastStateTransition for StateTransition {
Ok(_) => trace!("broadcast_and_wait: complete success"),
Err(e) => warn!(error = ?e, "broadcast_and_wait: failed"),
}
result
result.map(|inner| StateTransitionResult::new(inner, transition_hash))
}
}
1 change: 1 addition & 0 deletions packages/rs-sdk/src/platform/transition/put_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ async fn put_identity_with_address_funding<
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedIdentityFullWithAddressInfos(
proved_identity,
Expand Down
103 changes: 103 additions & 0 deletions packages/rs-sdk/src/platform/transition/state_transition_result.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::ops::Deref;
Comment thread
thepastaclaw marked this conversation as resolved.

/// Wrapper that bundles a state transition proof result with the transition hash.
///
/// The transition hash (also known as the transaction ID) is computed deterministically
/// from the serialized `StateTransition` before broadcast, so it does not depend on
/// blockchain state and there is no race condition.
///
/// `StateTransitionResult<T>` implements `Deref<Target = T>`, so existing code that
/// only needs the inner result can use it transparently.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StateTransitionResult<T> {
Comment thread
thepastaclaw marked this conversation as resolved.
inner: T,
transition_hash: [u8; 32],
Comment thread
thepastaclaw marked this conversation as resolved.
}

impl<T> StateTransitionResult<T> {
/// Creates a new result bundling the proof result with the transition hash.
pub fn new(inner: T, transition_hash: [u8; 32]) -> Self {
Self {
inner,
transition_hash,
}
}

/// Returns the transition hash (transaction ID) as a 32-byte array.
pub fn transition_hash(&self) -> [u8; 32] {
self.transition_hash
}

/// Consumes this wrapper, returning the inner result and the transition hash.
pub fn into_parts(self) -> (T, [u8; 32]) {
(self.inner, self.transition_hash)
}

/// Consumes this wrapper, returning just the inner result.
pub fn into_inner(self) -> T {
self.inner
}

/// Maps the inner value, preserving the transition hash.
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> StateTransitionResult<U> {
StateTransitionResult {
inner: f(self.inner),
transition_hash: self.transition_hash,
}
}
}

impl<T> Deref for StateTransitionResult<T> {
type Target = T;

fn deref(&self) -> &T {
&self.inner
}
}
Comment thread
thepastaclaw marked this conversation as resolved.
Comment thread
thepastaclaw marked this conversation as resolved.

#[cfg(test)]
mod tests {
use super::StateTransitionResult;

fn sample_hash() -> [u8; 32] {
[7; 32]
}

#[test]
fn new_exposes_transition_hash() {
let result = StateTransitionResult::new("value", sample_hash());

assert_eq!(result.transition_hash(), sample_hash());
}

#[test]
fn into_parts_returns_inner_and_hash() {
let result = StateTransitionResult::new(42_u8, sample_hash());

assert_eq!(result.into_parts(), (42_u8, sample_hash()));
}

#[test]
fn into_inner_returns_inner_value() {
let result = StateTransitionResult::new(String::from("value"), sample_hash());

assert_eq!(result.into_inner(), "value");
}

#[test]
fn map_preserves_transition_hash() {
let result = StateTransitionResult::new(21_u8, sample_hash());

let mapped = result.map(|value| value * 2);

assert_eq!(mapped.into_parts(), (42_u8, sample_hash()));
}

#[test]
fn deref_exposes_inner_value() {
let result = StateTransitionResult::new(vec![1_u8, 2, 3], sample_hash());

assert_eq!(result.len(), 3);
assert_eq!(result[1], 2);
}
}
3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform/transition/top_up_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressesWithBalances {
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
let st_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?;
.await?
.into_inner();
match st_result {
StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
let expected_addresses =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ impl TopUpIdentity for Identity {
None,
)?;
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
let identity: PartialIdentity = state_transition.broadcast_and_wait(sdk, settings).await?;
let identity: PartialIdentity = state_transition
.broadcast_and_wait::<PartialIdentity>(sdk, settings)
.await?
.into_inner();

identity
.balance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl<S: Signer<PlatformAddress>> TopUpIdentityFromAddresses<S> for Identity {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedIdentityWithAddressInfos(
identity,
Expand Down
6 changes: 4 additions & 2 deletions packages/rs-sdk/src/platform/transition/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ impl TransferToIdentity for Identity {
)?;
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;

let (sender, receiver): (PartialIdentity, PartialIdentity) =
state_transition.broadcast_and_wait(sdk, settings).await?;
let (sender, receiver): (PartialIdentity, PartialIdentity) = state_transition
.broadcast_and_wait::<(PartialIdentity, PartialIdentity)>(sdk, settings)
.await?
.into_inner();

let sender_balance = sender.balance.ok_or_else(|| {
Error::Generic("expected an identity balance after transfer (sender)".to_string())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl<S: Signer<PlatformAddress>> TransferAddressFunds<S> for Sdk {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedAddressInfos(address_infos_map) => {
collect_address_infos_from_proof(address_infos_map, &expected_addresses)
Expand Down
Loading
Loading