diff --git a/Makefile b/Makefile index 387d71920..a5438b6d3 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,11 @@ check-features: ## Check feature flags for crates .PHONY: check-fmt check-fmt: ## Check code formatting - cargo fmt -- --config imports_granularity=Item --check + cargo fmt -- --config imports_granularity=Item --config format_code_in_doc_comments=true --check .PHONY: fmt fmt: ## Format code - cargo fmt -- --config imports_granularity=Item + cargo fmt -- --config imports_granularity=Item --config format_code_in_doc_comments=true .PHONY: clippy clippy: ## Run Clippy linter @@ -22,7 +22,7 @@ clippy: ## Run Clippy linter .PHONY: test test: ## Run unit tests cargo nextest run --all-features -p sui-sdk-types -p sui-crypto - cargo test --doc + cargo test --all-features --doc package_%.json: crates/sui-transaction-builder/tests/%/Move.toml crates/sui-transaction-builder/tests/%/sources/*.move ## Generate JSON files for tests cd crates/sui-transaction-builder/tests/$(*F) && sui move build --ignore-chain --dump-bytecode-as-base64 > ../../$@ @@ -38,7 +38,7 @@ wasm: ## Build WASM modules .PHONY: doc doc: ## Generate documentation - RUSTDOCFLAGS="--cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps + RUSTDOCFLAGS="-Dwarnings --cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps .PHONY: doc-open doc-open: ## Generate and open documentation diff --git a/crates/sui-graphql-client-build/README.md b/crates/sui-graphql-client-build/README.md index e6f20fbec..cbf199e1e 100644 --- a/crates/sui-graphql-client-build/README.md +++ b/crates/sui-graphql-client-build/README.md @@ -31,7 +31,7 @@ sui-graphql-client-build = { git = "https://github.com/mystenlabs/sui-rust-sdk", ``` 4. If using `cynic`, use the cynic generator to generate the Rust types from the GraphQL schema. \ - Go to https://generator.cynic-rs.dev/ and paste the URL to the GraphQL service or manually copy paste the schema. \ + Go to and paste the URL to the GraphQL service or manually copy paste the schema. \ Then you can select the fields in the query you want to have, and the generator will generate the Rust types for you. 5. In your Rust code, you can now use the custom query types generated by `cynic`. @@ -57,6 +57,6 @@ async fn main() { } ``` -6. For `UInt53`, you can use `u64` type directly as the `sui-graphql-client`'s schema implements the `impl_scalar`. Similarly for other types (Base64, DateTime). See more available types here: https://github.com/MystenLabs/sui-rust-sdk/blob/02639f6b09375fe03fa2243868be17bec1dfa33c/crates/sui-graphql-client/src/query_types/mod.rs?plain=1#L124-L126 +6. For `UInt53`, you can use `u64` type directly as the `sui-graphql-client`'s schema implements the `impl_scalar`. Similarly for other types (Base64, DateTime). See more available types here: 7. Read the `cynic` [documentation](https://cynic-rs.dev/) to learn how to work with it, particularly when it comes to passing arguments to the query. diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index 808e7467b..6e36986ef 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -67,7 +67,7 @@ async fn main() -> Result<()> { ``` ### Example for custom faucet service. -Note that this [`FaucetClient`] is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.sui.io`). +Note that this `FaucetClient` is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.sui.io`). ```rust, no_run use sui_graphql_client::faucet::FaucetClient; use sui_types::Address; diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index d1c7c0f81..6fa315754 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -444,7 +444,7 @@ impl Client { /// Get the list of active validators for the provided epoch, including related metadata. /// If no epoch is provided, it will return the active validators for the current epoch. - pub async fn active_validators<'a>( + pub async fn active_validators( &self, epoch: Option, pagination_filter: PaginationFilter, @@ -571,7 +571,7 @@ impl Client { /// /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`. - pub async fn coins<'a>( + pub async fn coins( &self, owner: Address, coin_type: Option<&str>, @@ -675,7 +675,7 @@ impl Client { } /// Get a page of [`CheckpointSummary`] for the provided parameters. - pub async fn checkpoints<'a>( + pub async fn checkpoints( &self, pagination_filter: PaginationFilter, ) -> Result> { @@ -742,12 +742,11 @@ impl Client { /// /// # Example /// ```rust,ignore - /// /// let client = sui_graphql_client::Client::new_devnet(); /// let address = Address::from_str("0x5").unwrap(); /// let df = client.dynamic_field_with_name(address, "u64", 2u64).await.unwrap(); /// - /// # alternatively, pass in the bcs bytes + /// // alternatively, pass in the bcs bytes /// let bcs = base64ct::Base64::decode_vec("AgAAAAAAAAA=").unwrap(); /// let df = client.dynamic_field(address, "u64", BcsName(bcs)).await.unwrap(); /// ``` @@ -824,7 +823,7 @@ impl Client { /// dynamic fields on wrapped objects. /// /// This returns [`Page`] of [`DynamicFieldOutput`]s. - pub async fn dynamic_fields<'a>( + pub async fn dynamic_fields( &self, address: Address, pagination_filter: PaginationFilter, @@ -1475,9 +1474,9 @@ impl Client { } /// Get a page of transactions based on the provided filters. - pub async fn transactions<'a>( + pub async fn transactions( &self, - filter: Option>, + filter: Option>, pagination_filter: PaginationFilter, ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; @@ -1513,9 +1512,9 @@ impl Client { } /// Get a page of transactions' effects based on the provided filters. - pub async fn transactions_effects<'a>( + pub async fn transactions_effects( &self, - filter: Option>, + filter: Option>, pagination_filter: PaginationFilter, ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; @@ -2002,7 +2001,7 @@ mod tests { _ => return, }; let key = Ed25519PublicKey::generate(rand::thread_rng()); - let address = key.to_address(); + let address = key.derive_address(); faucet.request_and_wait(address).await.unwrap(); const MAX_RETRIES: u32 = 10; diff --git a/crates/sui-graphql-client/src/streams.rs b/crates/sui-graphql-client/src/streams.rs index f16985e71..f28f0c07c 100644 --- a/crates/sui-graphql-client/src/streams.rs +++ b/crates/sui-graphql-client/src/streams.rs @@ -151,6 +151,7 @@ where /// Creates a new `PageStream` for a paginated query. /// /// Examples +/// /// ```rust,ignore /// use futures::StreamExt; /// use sui_graphql_client::streams::stream_paginated_query; @@ -160,8 +161,9 @@ where /// /// let client = Client::new_testnet(); /// let stream = stream_paginated_query(|pagination_filter, Direction::Forward| { -/// client.coins(owner, coin_type, pagination_filter }) +/// client.coins(owner, coin_type, pagination_filter) /// }); +/// /// while let Some(result) = stream.next().await { /// match result { /// Ok(coin) => println!("Got coin: {:?}", coin), diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 7be465548..0683929f9 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -57,7 +57,6 @@ test-strategy = { version = "0.4", optional = true } bcs = "0.1.6" serde_json = "1.0.128" num-bigint = "0.4.6" -jsonschema = { version = "0.20", default-features = false } paste = "1.0.15" [target.wasm32-unknown-unknown.dev-dependencies] diff --git a/crates/sui-sdk-types/src/address.rs b/crates/sui-sdk-types/src/address.rs index bb4572865..6e91b51e9 100644 --- a/crates/sui-sdk-types/src/address.rs +++ b/crates/sui-sdk-types/src/address.rs @@ -1,3 +1,58 @@ +/// Unique identifier for an Account on the Sui blockchain. +/// +/// An `Address` is a 32-byte pseudonymous identifier used to uniquely identify an account and +/// asset-ownership on the Sui blockchain. Often, human-readable addresses are encoded in +/// hexadecimal with a `0x` prefix. For example, this is a valid Sui address: +/// `0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331`. +/// +/// ``` +/// use sui_sdk_types::Address; +/// +/// let hex = "0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331"; +/// let address = Address::from_hex(hex).unwrap(); +/// println!("Address: {}", address); +/// assert_eq!(hex, address.to_string()); +/// ``` +/// +/// # Deriving an Address +/// +/// Addresses are cryptographically derived from a number of user account authenticators, the simplest +/// of which is an [`Ed25519PublicKey`](crate::Ed25519PublicKey). +/// +/// Deriving an address consists of the Blake2b256 hash of the sequence of bytes of its +/// corresponding authenticator, prefixed with a domain-separator. For each authenticator, this +/// domain-separator is the single byte-value of its [`SignatureScheme`](crate::SignatureScheme) +/// flag. E.g. `hash(signature schema flag || authenticator bytes)`. +/// +/// Each authenticator includes a convince method for deriving its `Address` as well as +/// documentation for the specifics of how the derivation is done. See +/// [`Ed25519PublicKey::derive_address`] for an example. +/// +/// [`Ed25519PublicKey::derive_address`]: crate::Ed25519PublicKey::derive_address +/// +/// ## Relationship to ObjectIds +/// +/// [`ObjectId`]s and `Address`es share the same 32-byte addressable space but are derived +/// leveraging different domain-separator values to ensure, cryptographically, that there won't be +/// any overlap, e.g. there can't be a valid `Object` who's `ObjectId` is equal to that of the +/// `Address` of a user account. +/// +/// [`ObjectId`]: crate::ObjectId +/// +/// # BCS +/// +/// An `Address`'s BCS serialized form is simply the sequence of 32-bytes of the address. +/// +/// ``` +/// use sui_sdk_types::Address; +/// +/// let bytes = [ +/// 0x02, 0xa2, 0x12, 0xde, 0x6a, 0x9d, 0xfa, 0x3a, 0x69, 0xe2, 0x23, 0x87, 0xac, 0xfb, 0xaf, +/// 0xbb, 0x1a, 0x9e, 0x59, 0x1b, 0xd9, 0xd6, 0x36, 0xe7, 0x89, 0x5d, 0xcf, 0xc8, 0xde, 0x05, +/// 0xf3, 0x31, +/// ]; +/// let address: Address = bcs::from_bytes(&bytes).unwrap(); +/// ``` #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/crypto/zklogin.rs b/crates/sui-sdk-types/src/crypto/zklogin.rs index c828a2b29..d9e9a7c79 100644 --- a/crates/sui-sdk-types/src/crypto/zklogin.rs +++ b/crates/sui-sdk-types/src/crypto/zklogin.rs @@ -63,11 +63,30 @@ pub struct CircomG1(pub [Bn254FieldElement; 3]); #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CircomG2(pub [[Bn254FieldElement; 2]; 3]); -/// A wrapper struct to retrofit in [enum PublicKey] for zkLogin. -/// Useful to construct [struct MultiSigPublicKey]. +/// Public Key equivalent for Zklogin authenticators +/// +/// A `ZkLoginPublicIdentifier` is the equivalent of a public key for other account authenticators, +/// and contains the information required to derive the onchain account [`Address`] for a Zklogin +/// authenticator. +/// +/// ## Note +/// +/// Due to a historical bug that was introduced in the Sui Typescript SDK when the zklogin +/// authenticator was first introduced, there are now possibly two "valid" addresses for each +/// zklogin authenticator depending on the bit-pattern of the `address_seed` value. +/// +/// The original bug incorrectly derived a zklogin's address by stripping any leading +/// zero-bytes that could have been present in the 32-byte length `address_seed` value prior to +/// hashing, leading to a different derived address. This incorrectly derived address was +/// presented to users of various wallets, leading them to sending funds to these addresses +/// that they couldn't access. Instead of letting these users lose any assets that were sent to +/// these addrsses, the Sui network decided to change the protocol to allow for a zklogin +/// authenticator who's `address_seed` value had leading zero-bytes be authorized to sign for +/// both the addresses derived from both the unpadded and padded `address_seed` value. +/// +/// [`Address`]: crate::Address #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] -//TODO ensure iss is less than 255 bytes long pub struct ZkLoginPublicIdentifier { iss: String, address_seed: Bn254FieldElement, @@ -297,7 +316,8 @@ mod serialization { } let Readable { iss, address_seed } = Deserialize::deserialize(deserializer)?; - Ok(Self { iss, address_seed }) + Self::new(iss, address_seed) + .ok_or_else(|| serde::de::Error::custom("invalid zklogin public identifier")) } else { let bytes: Cow<'de, [u8]> = Bytes::deserialize_as(deserializer)?; let iss_len = *bytes @@ -315,10 +335,8 @@ mod serialization { .map_err(serde::de::Error::custom) .map(Bn254FieldElement)?; - Ok(Self { - iss: iss.into(), - address_seed, - }) + Self::new(iss.into(), address_seed) + .ok_or_else(|| serde::de::Error::custom("invalid zklogin public identifier")) } } } diff --git a/crates/sui-sdk-types/src/hash.rs b/crates/sui-sdk-types/src/hash.rs index 8ae1ccfa1..432a68ecd 100644 --- a/crates/sui-sdk-types/src/hash.rs +++ b/crates/sui-sdk-types/src/hash.rs @@ -5,19 +5,23 @@ use blake2::Digest as DigestTrait; type Blake2b256 = blake2::Blake2b; +/// A Blake2b256 Hasher #[derive(Debug, Default)] pub struct Hasher(Blake2b256); impl Hasher { + /// Initialize a new Blake2b256 Hasher instance. pub fn new() -> Self { Self(Blake2b256::new()) } + /// Process the provided data, updating internal state. pub fn update>(&mut self, data: T) { self.0.update(data) } - /// Retrieve result and consume hasher instance. + /// Finalize hashing, consuming the Hasher instance and returning the resultant hash or + /// `Digest`. pub fn finalize(self) -> Digest { let mut buf = [0; Digest::LENGTH]; let result = self.0.finalize(); @@ -27,6 +31,8 @@ impl Hasher { Digest::new(buf) } + /// Convenience function for creating a new Hasher instance, hashing the provided data, and + /// returning the resultant `Digest` pub fn digest>(data: T) -> Digest { let mut hasher = Self::new(); hasher.update(data); @@ -45,7 +51,29 @@ impl std::io::Write for Hasher { } impl crate::Ed25519PublicKey { - pub fn to_address(&self) -> Address { + /// Derive an `Address` from this Public Key + /// + /// An `Address` can be derived from an `Ed25519PublicKey` by hashing the bytes of the public + /// key prefixed with the Ed25519 `SignatureScheme` flag (`0x00`). + /// + /// `hash( 0x00 || 32-byte ed25519 public key)` + /// + /// ``` + /// use sui_sdk_types::hash::Hasher; + /// use sui_sdk_types::Address; + /// use sui_sdk_types::Ed25519PublicKey; + /// + /// let public_key_bytes = [0; 32]; + /// let mut hasher = Hasher::new(); + /// hasher.update([0x00]); // The SignatureScheme flag for Ed25519 is `0` + /// hasher.update(public_key_bytes); + /// let address = Address::new(hasher.finalize().into_inner()); + /// println!("Address: {}", address); + /// + /// let public_key = Ed25519PublicKey::new(public_key_bytes); + /// assert_eq!(address, public_key.derive_address()); + /// ``` + pub fn derive_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); let digest = hasher.finalize(); @@ -59,7 +87,29 @@ impl crate::Ed25519PublicKey { } impl crate::Secp256k1PublicKey { - pub fn to_address(&self) -> Address { + /// Derive an `Address` from this Public Key + /// + /// An `Address` can be derived from a `Secp256k1PublicKey` by hashing the bytes of the public + /// key prefixed with the Secp256k1 `SignatureScheme` flag (`0x01`). + /// + /// `hash( 0x01 || 33-byte secp256k1 public key)` + /// + /// ``` + /// use sui_sdk_types::hash::Hasher; + /// use sui_sdk_types::Address; + /// use sui_sdk_types::Secp256k1PublicKey; + /// + /// let public_key_bytes = [0; 33]; + /// let mut hasher = Hasher::new(); + /// hasher.update([0x01]); // The SignatureScheme flag for Secp256k1 is `1` + /// hasher.update(public_key_bytes); + /// let address = Address::new(hasher.finalize().into_inner()); + /// println!("Address: {}", address); + /// + /// let public_key = Secp256k1PublicKey::new(public_key_bytes); + /// assert_eq!(address, public_key.derive_address()); + /// ``` + pub fn derive_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); let digest = hasher.finalize(); @@ -73,7 +123,29 @@ impl crate::Secp256k1PublicKey { } impl crate::Secp256r1PublicKey { - pub fn to_address(&self) -> Address { + /// Derive an `Address` from this Public Key + /// + /// An `Address` can be derived from a `Secp256r1PublicKey` by hashing the bytes of the public + /// key prefixed with the Secp256r1 `SignatureScheme` flag (`0x02`). + /// + /// `hash( 0x02 || 33-byte secp256r1 public key)` + /// + /// ``` + /// use sui_sdk_types::hash::Hasher; + /// use sui_sdk_types::Address; + /// use sui_sdk_types::Secp256r1PublicKey; + /// + /// let public_key_bytes = [0; 33]; + /// let mut hasher = Hasher::new(); + /// hasher.update([0x02]); // The SignatureScheme flag for Secp256r1 is `2` + /// hasher.update(public_key_bytes); + /// let address = Address::new(hasher.finalize().into_inner()); + /// println!("Address: {}", address); + /// + /// let public_key = Secp256r1PublicKey::new(public_key_bytes); + /// assert_eq!(address, public_key.derive_address()); + /// ``` + pub fn derive_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); let digest = hasher.finalize(); @@ -87,8 +159,12 @@ impl crate::Secp256r1PublicKey { } impl crate::ZkLoginPublicIdentifier { - /// Define as iss_bytes_len || iss_bytes || padded_32_byte_address_seed. - pub fn to_address_padded(&self) -> Address { + /// Derive an `Address` from this `ZkLoginPublicIdentifier` by hashing the byte length of the + /// `iss` followed by the `iss` bytes themselves and the full 32 byte `address_seed` value, all + /// prefixed with the zklogin `SignatureScheme` flag (`0x05`). + /// + /// `hash( 0x05 || iss_bytes_len || iss_bytes || 32_byte_address_seed )` + pub fn derive_address_padded(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher_padded(&mut hasher); let digest = hasher.finalize(); @@ -102,8 +178,12 @@ impl crate::ZkLoginPublicIdentifier { hasher.update(self.address_seed().padded()); } - /// Define as iss_bytes_len || iss_bytes || unpadded_32_byte_address_seed. - pub fn to_address_unpadded(&self) -> Address { + /// Derive an `Address` from this `ZkLoginPublicIdentifier` by hashing the byte length of the + /// `iss` followed by the `iss` bytes themselves and the `address_seed` bytes with any leading + /// zero-bytes stripped, all prefixed with the zklogin `SignatureScheme` flag (`0x05`). + /// + /// `hash( 0x05 || iss_bytes_len || iss_bytes || unpadded_32_byte_address_seed )` + pub fn derive_address_unpadded(&self) -> Address { let mut hasher = Hasher::new(); hasher.update([self.scheme().to_u8()]); hasher.update([self.iss().len() as u8]); // TODO enforce iss is less than 255 bytes @@ -112,10 +192,36 @@ impl crate::ZkLoginPublicIdentifier { let digest = hasher.finalize(); Address::new(digest.into_inner()) } + + /// Provides an iterator over the addresses that correspond to this zklogin authenticator. + /// + /// In the majority of instances this will only yield a single address, except for the + /// instances where the `address_seed` value has a leading zero-byte, in such cases the + /// returned iterator will yield two addresses. + pub fn derive_address(&self) -> impl Iterator { + let main_address = self.derive_address_padded(); + let mut addresses = [Some(main_address), None]; + // If address_seed starts with a zero byte then we know that this zklogin authenticator has + // two addresses + if self.address_seed().padded()[0] == 0 { + let secondary_address = self.derive_address_unpadded(); + + addresses[1] = Some(secondary_address); + } + + addresses.into_iter().flatten() + } } impl crate::PasskeyPublicKey { - pub fn to_address(&self) -> Address { + /// Derive an `Address` from this Passkey Public Key + /// + /// An `Address` can be derived from a `PasskeyPublicKey` by hashing the bytes of the + /// `Secp256r1PublicKey` that corresponds to this passkey prefixed with the Passkey + /// `SignatureScheme` flag (`0x06`). + /// + /// `hash( 0x06 || 33-byte secp256r1 public key)` + pub fn derive_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); let digest = hasher.finalize(); @@ -129,15 +235,24 @@ impl crate::PasskeyPublicKey { } impl crate::MultisigCommittee { - /// Derive an Address from a MultisigCommittee. A MultiSig address - /// is defined as the 32-byte Blake2b hash of serializing the flag, the - /// threshold, concatenation of all n flag, public keys and - /// its weight. `flag_MultiSig || threshold || flag_1 || pk_1 || weight_1 - /// || ... || flag_n || pk_n || weight_n`. + /// Derive an `Address` from this MultisigCommittee. + /// + /// A MultiSig address + /// is defined as the 32-byte Blake2b hash of serializing the `SignatureScheme` flag (0x03), the + /// threshold (in little endian), and the concatenation of all n flag, public keys and + /// its weight. + /// + /// `hash(0x03 || threshold || flag_1 || pk_1 || weight_1 + /// || ... || flag_n || pk_n || weight_n)`. + /// + /// When flag_i is ZkLogin, the pk_i for the [`ZkLoginPublicIdentifier`] refers to the same + /// input used when deriving the address using the + /// [`ZkLoginPublicIdentifier::derive_address_padded`] method (using the full 32-byte + /// `address_seed` value). /// - /// When flag_i is ZkLogin, pk_i refers to [struct ZkLoginPublicIdentifier] - /// derived from padded address seed in bytes and iss. - pub fn to_address(&self) -> Address { + /// [`ZkLoginPublicIdentifier`]: crate::ZkLoginPublicIdentifier + /// [`ZkLoginPublicIdentifier::derive_address_padded`]: crate::ZkLoginPublicIdentifier::derive_address_padded + pub fn derive_address(&self) -> Address { use crate::MultisigMemberPublicKey::*; let mut hasher = Hasher::new(); @@ -179,6 +294,9 @@ mod type_digest { use crate::TransactionEventsDigest; impl Object { + /// Calculate the digest of this `Object` + /// + /// This is done by hashing the BCS bytes of this `Object` prefixed pub fn digest(&self) -> ObjectDigest { const SALT: &str = "Object::"; let digest = type_digest(SALT, self); @@ -279,9 +397,9 @@ mod signing_message { } } -/// A 1-byte domain separator for hashing Object ID in Sui. It is starting from 0xf0 -/// to ensure no hashing collision for any ObjectId vs Address which is derived -/// as the hash of `flag || pubkey`. +/// A 1-byte domain separator for deriving `ObjectId`s in Sui. It is starting from `0xf0` to ensure +/// no hashing collision for any ObjectId vs Address which is derived as the hash of `flag || +/// pubkey`. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] #[repr(u8)] diff --git a/crates/sui-sdk-types/src/lib.rs b/crates/sui-sdk-types/src/lib.rs index 698cfa066..a7b4bc3f1 100644 --- a/crates/sui-sdk-types/src/lib.rs +++ b/crates/sui-sdk-types/src/lib.rs @@ -1,8 +1,57 @@ #![cfg_attr(doc_cfg, feature(doc_cfg))] -#[cfg(feature = "hash")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "hash")))] -pub mod hash; +//! Core type definitions for the [Sui] blockchain. +//! +//! [Sui] is a next-generation smart contract platform with high throughput, low latency, and an +//! asset-oriented programming model powered by the Move programming language. This crate provides +//! type definitions for working with the data that makes up the [Sui] blockchain. +//! +//! [Sui]: https://sui.io +//! +//! # BCS +//! +//! [BCS] is the serialization format used to represent the state of the blockchain and is used +//! extensively throughout the Sui ecosystem. In particular the BCS format is leveraged because it +//! _"guarantees canonical serialization, meaning that for any given data type, there is a +//! one-to-one correspondence between in-memory values and valid byte representations."_ One +//! benefit of this property of having a canonical serialized representation is to allow different +//! entities in the ecosystem to all agree on how a particular type should be interpreted and more +//! importantly define a deterministic representation for hashing and signing. +//! +//! This library strives to guarantee that the types defined are fully BCS-compatible with the data +//! that the network produces. The one caveat to this would be that as the Sui protocol evolves, +//! new type variants are added and older versions of this library may not support those newly +//! added variants. The expectation is that the most recent release of this library will support +//! new variants and types as they are released to Sui's `testnet` network. +//! +//! See the documentation for the various types defined by this crate for an example of their BCS +//! serialized representation. In addition to the format itself, some types have an extra layer of +//! verification and may impose additional restrictions on valid byte representations above and +//! beyond those already provided by BCS. In these instances the documentation for those types will +//! clearly specify these additional restrictions. +//! +//! [BCS]: https://docs.rs/bcs +//! +//! # Feature flags +//! +//! This library uses a set of [feature flags] to reduce the number of dependencies and amount of +//! compiled code. By default, no features are enabled which allows one to enable a subset +//! specifically for their use case. Below is a list of the available feature flags. +//! +//! - `serde`: Enables support for serializing and deserializing types to/from BCS utilizing +//! [serde] library. +//! - `rand`: Enables support for generating random instances of a number of types via the [rand] +//! library. +//! - `hash`: Enables support for hashing, which is required for deriving addresses and calculating +//! digests for various types. +//! - `proptest`: Enables support for the [proptest] library by providing implementations of +//! [proptest::arbitrary::Arbitrary] for many types. +//! +//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section +//! [serde]: https://docs.rs/serde +//! [rand]: https://docs.rs/rand +//! [proptest]: https://docs.rs/proptest +//! [proptest::arbitrary::Arbitrary]: https://docs.rs/proptest/latest/proptest/arbitrary/trait.Arbitrary.html mod address; mod checkpoint; @@ -19,6 +68,10 @@ mod transaction; mod type_tag; mod u256; +#[cfg(feature = "hash")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "hash")))] +pub mod hash; + pub use address::Address; pub use address::AddressParseError; pub use checkpoint::CheckpointCommitment; diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs index 050648d8f..5aa82376e 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/sui-transaction-builder/src/lib.rs @@ -27,7 +27,7 @@ use sui_types::Upgrade; use base64ct::Encoding; use serde::Serialize; -/// A builder for creating transactions. Use [`resolve`] to finalize the transaction data. +/// A builder for creating transactions. Use `resolve` to finalize the transaction data. #[derive(Clone, Default, Debug)] pub struct TransactionBuilder { /// The inputs to the transaction. @@ -95,7 +95,7 @@ impl TransactionBuilder { /// Add one or more gas objects to use to pay for the transaction. /// - /// Most commonly the gas can be passed as a reference to an owned/immutable [`Object`], + /// Most commonly the gas can be passed as a reference to an owned/immutable `Object`, /// or can created using one of the of the constructors of the [`unresolved::Input`] enum, /// e.g., [`unresolved::Input::owned`]. pub fn add_gas_objects(&mut self, gas: I) @@ -227,7 +227,7 @@ impl TransactionBuilder { /// /// let mut tx = TransactionBuilder::new(); /// let package_id = "0x...".parse().unwrap(); - /// let upgrade_cap = tx.input(unresolved::Input::by_id("0x...".parse().unwrap()); + /// let upgrade_cap = tx.input(unresolved::Input::by_id("0x...".parse().unwrap())); /// let upgrade_policy = tx.input(Serialized(&0u8)); /// // the digest of the new package that was compiled /// let package_digest: &[u8] = &[ @@ -286,7 +286,7 @@ impl TransactionBuilder { } /// Assuming everything is resolved, convert this transaction into the - /// resolved form. Returns a [`Transaction`] if successful, or an [`Error`] if not. + /// resolved form. Returns a [`Transaction`] if successful, or an `Error` if not. pub fn finish(self) -> Result { let Some(sender) = self.sender else { return Err(Error::MissingSender); @@ -553,7 +553,7 @@ mod tests { /// Generate a random private key and its corresponding address fn helper_address_pk() -> (Address, Ed25519PrivateKey) { let pk = Ed25519PrivateKey::generate(rand::thread_rng()); - let address = pk.public_key().to_address(); + let address = pk.public_key().derive_address(); (address, pk) }