Skip to content

Commit

Permalink
sui-sdk-types: add doc comments for the top-level and Address
Browse files Browse the repository at this point in the history
  • Loading branch information
bmwill committed Jan 22, 2025
1 parent 9e4be95 commit 797f09b
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 18 deletions.
55 changes: 55 additions & 0 deletions crates/sui-sdk-types/src/address.rs
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
24 changes: 22 additions & 2 deletions crates/sui-sdk-types/src/crypto/zklogin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,28 @@ 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))]
pub struct ZkLoginPublicIdentifier {
Expand Down
125 changes: 112 additions & 13 deletions crates/sui-sdk-types/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ use blake2::Digest as DigestTrait;

type Blake2b256 = blake2::Blake2b<blake2::digest::consts::U32>;

/// 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<T: AsRef<[u8]>>(&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();
Expand All @@ -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<T: AsRef<[u8]>>(data: T) -> Digest {
let mut hasher = Self::new();
hasher.update(data);
Expand All @@ -45,6 +51,28 @@ impl std::io::Write for Hasher {
}

impl crate::Ed25519PublicKey {
/// 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);
Expand All @@ -59,6 +87,28 @@ impl crate::Ed25519PublicKey {
}

impl crate::Secp256k1PublicKey {
/// 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);
Expand All @@ -73,6 +123,28 @@ impl crate::Secp256k1PublicKey {
}

impl crate::Secp256r1PublicKey {
/// 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);
Expand All @@ -87,7 +159,11 @@ impl crate::Secp256r1PublicKey {
}

impl crate::ZkLoginPublicIdentifier {
/// Define as iss_bytes_len || iss_bytes || padded_32_byte_address_seed.
/// 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);
Expand All @@ -102,7 +178,11 @@ impl crate::ZkLoginPublicIdentifier {
hasher.update(self.address_seed().padded());
}

/// Define as iss_bytes_len || iss_bytes || unpadded_32_byte_address_seed.
/// 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()]);
Expand Down Expand Up @@ -134,6 +214,13 @@ impl crate::ZkLoginPublicIdentifier {
}

impl crate::PasskeyPublicKey {
/// 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);
Expand All @@ -148,14 +235,23 @@ 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.
/// [`ZkLoginPublicIdentifier`]: crate::ZkLoginPublicIdentifier
/// [`ZkLoginPublicIdentifier::derive_address_padded`]: crate::ZkLoginPublicIdentifier::derive_address_padded
pub fn derive_address(&self) -> Address {
use crate::MultisigMemberPublicKey::*;

Expand Down Expand Up @@ -198,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);
Expand Down Expand Up @@ -298,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)]
Expand Down
59 changes: 56 additions & 3 deletions crates/sui-sdk-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 797f09b

Please sign in to comment.