diff --git a/Cargo.toml b/Cargo.toml index 4670d0a..bae46ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,11 @@ bcs = "0.1.6" cfg-if = "1.0.0" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "69d496c71fb37e3d22fe85e5bbfd4256d61422b9", package = "fastcrypto" } hyper = "1" -iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v1.20.1" } +iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", rev = "96196f0da231883ec69cda04892c600ef6afa982" } iota-sdk-types = { git = "https://github.com/iotaledger/iota-rust-sdk.git", rev = "e19c78a1bee17e0bf85fcd5b16a2f080cef26274", features = ["hash", "serde", "schemars"] } -move-binary-format = { git = "https://github.com/iotaledger/iota.git", package = "move-binary-format", tag = "v1.20.1" } -move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v1.20.1" } -move-bytecode-utils = { git = "https://github.com/iotaledger/iota.git", package = "move-bytecode-utils", tag = "v1.20.1" } +move-binary-format = { git = "https://github.com/iotaledger/iota.git", package = "move-binary-format", rev = "96196f0da231883ec69cda04892c600ef6afa982" } +move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", rev = "96196f0da231883ec69cda04892c600ef6afa982" } +move-bytecode-utils = { git = "https://github.com/iotaledger/iota.git", package = "move-bytecode-utils", rev = "96196f0da231883ec69cda04892c600ef6afa982" } phf = { version = "0.11.2", features = ["macros"] } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } serde = { version = "1.0", default-features = false, features = ["alloc", "derive", "rc"] } diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_interaction_ts/package-lock.json index 622054b..203e099 100644 --- a/bindings/wasm/iota_interaction_ts/package-lock.json +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -20,7 +20,7 @@ "node": ">=20" }, "peerDependencies": { - "@iota/iota-sdk": "^1.11.0" + "@iota/iota-sdk": "^1.13.0" } }, "node_modules/@0no-co/graphql.web": { @@ -105,9 +105,9 @@ } }, "node_modules/@iota/bcs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-1.5.0.tgz", - "integrity": "sha512-/hv395YtUcRNLY00v7Cl2O+KvVUaUajg4OucZENgSE4Xu1ygUGsLD3dU5FixOUVOn7Abo+n7+KYr9PE/1dsvWg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-1.6.0.tgz", + "integrity": "sha512-H8I9g+aPBQigSsHkydnnR6wmmTwOwI7iu/TghTOz6tikqVFM09cA2JlDQXRLDTSkW3Qlz6gONyVKzrBhoTkHxQ==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -115,14 +115,14 @@ } }, "node_modules/@iota/iota-sdk": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-1.11.0.tgz", - "integrity": "sha512-Fveg/4euheaBUzU1ybPyFGe7sSfLFUjLNHhPjNFUmSBOMR+l9q3LU1QdN2sLElcmgJZ+BLxAEmL8TZ0eX3Khpw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-1.13.0.tgz", + "integrity": "sha512-ay19PRu0z+1W9tnmGyexIR/Sp0Rpt7H0mUCuEZQ5B+7O6Qf61+Co8TrR1SRIrKwD7Fu90jPy5y5MwFXy/vLwKg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", - "@iota/bcs": "1.5.0", + "@iota/bcs": "1.6.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@scure/base": "^1.2.4", diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json index db2d93c..76e36b8 100644 --- a/bindings/wasm/iota_interaction_ts/package.json +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -40,7 +40,7 @@ "wasm-opt": "^1.4.0" }, "peerDependencies": { - "@iota/iota-sdk": "^1.11.0" + "@iota/iota-sdk": "^1.13.0" }, "engines": { "node": ">=20" diff --git a/iota_interaction/Cargo.toml b/iota_interaction/Cargo.toml index a827a83..a6e4bbd 100644 --- a/iota_interaction/Cargo.toml +++ b/iota_interaction/Cargo.toml @@ -31,8 +31,8 @@ thiserror.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] hyper.workspace = true -iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v1.20.1" } -move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v1.20.1" } +iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", rev = "96196f0da231883ec69cda04892c600ef6afa982" } +move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", rev = "96196f0da231883ec69cda04892c600ef6afa982" } tokio = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/iota_interaction/src/sdk_types/iota_types/auth_context/mod.rs b/iota_interaction/src/sdk_types/iota_types/auth_context/mod.rs index 873c1be..748de43 100644 --- a/iota_interaction/src/sdk_types/iota_types/auth_context/mod.rs +++ b/iota_interaction/src/sdk_types/iota_types/auth_context/mod.rs @@ -53,17 +53,21 @@ pub struct AuthContext { tx_inputs: Vec, /// The authentication commands to be executed sequentially. tx_commands: Vec, + /// The BCS-serialized `TransactionData` bytes. + tx_data_bytes: Vec, } impl AuthContext { pub fn new_from_components( auth_digest: MoveAuthenticatorDigest, ptb: &ProgrammableTransaction, + tx_data_bytes: Vec, ) -> Self { Self { auth_digest, tx_inputs: ptb.inputs.iter().map(MoveCallArg::from).collect(), tx_commands: ptb.commands.iter().map(MoveCommand::from).collect(), + tx_data_bytes, } } @@ -72,6 +76,7 @@ impl AuthContext { auth_digest: MoveAuthenticatorDigest::default(), tx_inputs: Vec::new(), tx_commands: Vec::new(), + tx_data_bytes: Vec::new(), } } @@ -87,6 +92,10 @@ impl AuthContext { &self.tx_commands } + pub fn tx_data_bytes(&self) -> &Vec { + &self.tx_data_bytes + } + pub fn to_bcs_bytes(&self) -> Vec { bcs::to_bytes(&self).unwrap() } @@ -136,10 +145,12 @@ impl AuthContext { auth_digest: MoveAuthenticatorDigest, tx_inputs: Vec, tx_commands: Vec, + tx_data_bytes: Vec, ) { self.auth_digest = auth_digest; self.tx_inputs = tx_inputs; self.tx_commands = tx_commands; + self.tx_data_bytes = tx_data_bytes; } } @@ -197,7 +208,8 @@ mod tests { }))], }; - let ctx = AuthContext::new_from_components(MoveAuthenticatorDigest::default(), &ptb); + let ctx = + AuthContext::new_from_components(MoveAuthenticatorDigest::default(), &ptb, vec![]); assert_eq!(ctx.tx_inputs().len(), 1); assert_eq!(ctx.tx_commands().len(), 1); @@ -231,6 +243,7 @@ mod tests { MoveAuthenticatorDigest::default(), vec![MoveCallArg::Pure(vec![1])], vec![], + vec![], ); let non_empty_bytes = ctx.to_bcs_bytes(); diff --git a/iota_interaction/src/sdk_types/iota_types/base_types.rs b/iota_interaction/src/sdk_types/iota_types/base_types.rs index 4bcd079..240e00d 100644 --- a/iota_interaction/src/sdk_types/iota_types/base_types.rs +++ b/iota_interaction/src/sdk_types/iota_types/base_types.rs @@ -12,16 +12,18 @@ use std::str::FromStr; use std::string::String; use std::vec::Vec; -use anyhow::anyhow; +use anyhow::{anyhow, bail}; use fastcrypto::encoding::{decode_bytes_hex, Encoding, Hex}; use fastcrypto::hash::HashFunction; use rand::Rng; use schemars::JsonSchema; use serde::ser::Error; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_with::{DeserializeAs, SerializeAs, serde_as}; use Result; +use iota_sdk_types::crypto::HashingIntentScope; + use crate::move_core_types::account_address::AccountAddress; use crate::move_core_types::identifier::IdentStr; use crate::move_core_types::language_storage::{ModuleId, StructTag, TypeTag}; @@ -35,7 +37,7 @@ use super::dynamic_field::DynamicFieldInfo; use super::error::{IotaError, IotaResult}; use super::gas_coin::{GasCoin, GAS}; use super::governance::{StakedIota, STAKED_IOTA_STRUCT_NAME, STAKING_POOL_MODULE_NAME}; -use super::iota_serde::{to_iota_struct_tag_string, HexAccountAddress, Readable}; +use super::iota_serde::{to_custom_deser_error, to_iota_struct_tag_string, Readable}; use super::object::Owner; use super::stardust::output::Nft; use super::timelock::timelock::{self, TimeLock}; @@ -691,6 +693,55 @@ impl ObjectID { } } + /// Create an ObjectID from `TransactionDigest` and `creation_num`. + /// Caller is responsible for ensuring that `creation_num` is fresh + pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self { + let mut hasher = DefaultHash::default(); + hasher.update([HashingIntentScope::RegularObjectId as u8]); + hasher.update(digest); + hasher.update(creation_num.to_le_bytes()); + let hash = hasher.finalize(); + + // truncate into an ObjectID. + // OK to access slice because digest should never be shorter than + // ObjectID::LENGTH. + ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap() + } + + /// Increment the ObjectID by one, assuming the ObjectID hex is a number + /// represented as an array of bytes + pub fn next_increment(&self) -> Result { + let mut prev_val = self.to_vec(); + let mx = [0xFF; Self::LENGTH]; + + if prev_val == mx { + bail!("Increment will cause overflow"); + } + + // This logic increments the integer representation of an ObjectID u8 array + for idx in (0..Self::LENGTH).rev() { + if prev_val[idx] == 0xFF { + prev_val[idx] = 0; + } else { + prev_val[idx] += 1; + break; + }; + } + ObjectID::try_from(prev_val.clone()).map_err(|w| w.into()) + } + + /// Create `count` object IDs starting with one at `offset` + pub fn in_range(offset: ObjectID, count: u64) -> Result, anyhow::Error> { + let mut ret = Vec::new(); + let mut prev = offset; + for o in 0..count { + if o != 0 { + prev = prev.next_increment()?; + } + ret.push(prev); + } + Ok(ret) + } /// Return the full hex string with 0x prefix without removing trailing 0s. /// Prefer this over [fn to_hex_literal] if the string needs to be fully @@ -797,6 +848,33 @@ impl From for AccountAddress { } } +/// Hex serde for AccountAddress +struct HexAccountAddress; + +impl SerializeAs for HexAccountAddress { + fn serialize_as(value: &AccountAddress, serializer: S) -> Result + where + S: Serializer, + { + Hex::serialize_as(value, serializer) + } +} + +impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if s.starts_with("0x") { + AccountAddress::from_hex_literal(&s) + } else { + AccountAddress::from_hex(&s) + } + .map_err(to_custom_deser_error::<'de, D, _>) + } +} + impl fmt::Display for MoveObjectType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { let s: StructTag = self.clone().into(); diff --git a/iota_interaction/src/sdk_types/iota_types/digests.rs b/iota_interaction/src/sdk_types/iota_types/digests.rs index ab3fcb7..bb21158 100644 --- a/iota_interaction/src/sdk_types/iota_types/digests.rs +++ b/iota_interaction/src/sdk_types/iota_types/digests.rs @@ -211,6 +211,7 @@ impl fmt::UpperHex for CheckpointContentsDigest { } } +/// A transaction will have a (unique) digest. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] pub struct TransactionDigest(Digest); @@ -261,6 +262,30 @@ impl TransactionDigest { } } +impl AsRef<[u8]> for TransactionDigest { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8; 32]> for TransactionDigest { + fn as_ref(&self) -> &[u8; 32] { + self.0.as_ref() + } +} + +impl From for [u8; 32] { + fn from(digest: TransactionDigest) -> Self { + digest.into_inner() + } +} + +impl From<[u8; 32]> for TransactionDigest { + fn from(digest: [u8; 32]) -> Self { + Self::new(digest) + } +} + impl fmt::Display for TransactionDigest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) diff --git a/iota_interaction/src/sdk_types/iota_types/iota_serde.rs b/iota_interaction/src/sdk_types/iota_types/iota_serde.rs index 4034831..449d3c7 100644 --- a/iota_interaction/src/sdk_types/iota_types/iota_serde.rs +++ b/iota_interaction/src/sdk_types/iota_types/iota_serde.rs @@ -13,7 +13,6 @@ use std::result::Result::Ok; use std::str::FromStr; use std::string::{String, ToString}; -use fastcrypto::encoding::Hex; use schemars::JsonSchema; use serde::de::{Deserializer, Error}; use serde::ser::{Error as SerError, Serializer}; @@ -91,7 +90,7 @@ impl From for ProtocolVersion { // ----------------------------------------------------------------------------------------- #[inline] -fn to_custom_error<'de, D, E>(e: E) -> D::Error +pub(crate) fn to_custom_deser_error<'de, D, E>(e: E) -> D::Error where E: Debug, D: Deserializer<'de>, @@ -153,37 +152,6 @@ impl<'de, R, H, T> DeserializeAs<'de, T> for Readable } } -/// custom serde for AccountAddress -pub struct HexAccountAddress; - -impl SerializeAs for HexAccountAddress { - fn serialize_as(value: &AccountAddress, serializer: S) -> Result - where - S: Serializer, - { - Hex::serialize_as(value, serializer) - } -} - -impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - if s.starts_with("0x") { - AccountAddress::from_hex_literal(&s) - } else { - AccountAddress::from_hex(&s) - } - .map_err(to_custom_error::<'de, D, _>) - } -} - -/// Serializes a bitmap according to the roaring bitmap on-disk standard. -/// -pub struct IotaBitmap; - pub struct IotaStructTag; impl SerializeAs for IotaStructTag { @@ -398,3 +366,4 @@ impl<'de> DeserializeAs<'de, ProtocolVersion> for AsProtocolVersion { Ok(ProtocolVersion::from(*b)) } } + diff --git a/iota_interaction/src/sdk_types/iota_types/transaction.rs b/iota_interaction/src/sdk_types/iota_types/transaction.rs index ef611f6..866ef43 100644 --- a/iota_interaction/src/sdk_types/iota_types/transaction.rs +++ b/iota_interaction/src/sdk_types/iota_types/transaction.rs @@ -18,8 +18,8 @@ use crate::move_core_types::language_storage::TypeTag; use super::base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber}; use super::error::{UserInputError, UserInputResult}; use super::{ - IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, IOTA_CLOCK_OBJECT_ID, - IOTA_CLOCK_OBJECT_SHARED_VERSION, IOTA_SYSTEM_STATE_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, + IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION, IOTA_SYSTEM_STATE_OBJECT_ID, + IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, }; pub const TEST_ONLY_GAS_UNIT_FOR_TRANSFER: u64 = 10_000; diff --git a/product_common/Cargo.toml b/product_common/Cargo.toml index 3e1d518..c1bb7ec 100644 --- a/product_common/Cargo.toml +++ b/product_common/Cargo.toml @@ -23,7 +23,7 @@ async-trait.workspace = true bcs = { workspace = true, optional = true } cfg-if.workspace = true fastcrypto = { workspace = true, optional = true } -iota-keys = { package = "iota-keys", git = "https://github.com/iotaledger/iota.git", tag = "v1.20.1", optional = true } +iota-keys = { package = "iota-keys", git = "https://github.com/iotaledger/iota.git", rev = "96196f0da231883ec69cda04892c600ef6afa982", optional = true } iota-sdk-types = { workspace = true, features = ["serde"] } itertools = { version = "0.13.0", optional = true } lazy_static = { version = "1.5.0", optional = true }