Skip to content

Commit

Permalink
Merge branch 'develop' into dev-tools/pull-upstream-3
Browse files Browse the repository at this point in the history
  • Loading branch information
DaughterOfMars committed Feb 17, 2025
2 parents 45da5e9 + 2ba6b29 commit 03a3675
Show file tree
Hide file tree
Showing 60 changed files with 13,089 additions and 43 deletions.
23 changes: 23 additions & 0 deletions crates/iota-rust-sdk/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Set the default target of this Makefile
.PHONY: all
all:: check-features clippy test wasm

.PHONY: check-features
check-features:
cargo hack check --feature-powerset --no-dev-deps

.PHONY: clippy
clippy:
cargo clippy --all-features --all-targets

.PHONY: test
test:
cargo nextest run --all-features
cargo test --doc

.PHONY: wasm
wasm:
CC=clang wasm-pack test --node --all-features

%:
$(MAKE) -C ../.. $@
365 changes: 365 additions & 0 deletions crates/iota-rust-sdk/src/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
use blake2::Digest as DigestTrait;

<<<<<<<< HEAD:crates/iota-sdk-types/src/hash.rs
use crate::{Address, Digest};
========
use crate::types::{Address, Digest};
>>>>>>>> develop:crates/iota-rust-sdk/src/hash.rs

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

#[derive(Debug, Default)]
pub struct Hasher(Blake2b256);

impl Hasher {
pub fn new() -> Self {
Self(Blake2b256::new())
}

pub fn update<T: AsRef<[u8]>>(&mut self, data: T) {
self.0.update(data)
}

/// Retrieve result and consume hasher instance.
pub fn finalize(self) -> Digest {
let mut buf = [0; Digest::LENGTH];
let result = self.0.finalize();

buf.copy_from_slice(result.as_slice());

Digest::new(buf)
}

pub fn digest<T: AsRef<[u8]>>(data: T) -> Digest {
let mut hasher = Self::new();
hasher.update(data);
hasher.finalize()
}
}

impl std::io::Write for Hasher {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.write(buf)
}

fn flush(&mut self) -> std::io::Result<()> {
self.0.flush()
}
}

impl crate::Ed25519PublicKey {
pub fn to_address(&self) -> Address {
let mut hasher = Hasher::new();
self.write_into_hasher(&mut hasher);
let digest = hasher.finalize();
Address::new(digest.into_inner())
}

fn write_into_hasher(&self, hasher: &mut Hasher) {
hasher.update([self.scheme().to_u8()]);
hasher.update(self.inner());
}
}

impl crate::Secp256k1PublicKey {
pub fn to_address(&self) -> Address {
let mut hasher = Hasher::new();
self.write_into_hasher(&mut hasher);
let digest = hasher.finalize();
Address::new(digest.into_inner())
}

fn write_into_hasher(&self, hasher: &mut Hasher) {
hasher.update([self.scheme().to_u8()]);
hasher.update(self.inner());
}
}

impl crate::Secp256r1PublicKey {
pub fn to_address(&self) -> Address {
let mut hasher = Hasher::new();
self.write_into_hasher(&mut hasher);
let digest = hasher.finalize();
Address::new(digest.into_inner())
}

fn write_into_hasher(&self, hasher: &mut Hasher) {
hasher.update([self.scheme().to_u8()]);
hasher.update(self.inner());
}
}

impl crate::ZkLoginPublicIdentifier {
/// Define as iss_bytes_len || iss_bytes || padded_32_byte_address_seed.
pub fn to_address_padded(&self) -> Address {
let mut hasher = Hasher::new();
self.write_into_hasher_padded(&mut hasher);
let digest = hasher.finalize();
Address::new(digest.into_inner())
}

fn write_into_hasher_padded(&self, hasher: &mut Hasher) {
hasher.update([self.scheme().to_u8()]);
hasher.update([self.iss().len() as u8]); // TODO enforce iss is less than 255 bytes
hasher.update(self.iss());
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 {
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
hasher.update(self.iss());
hasher.update(self.address_seed().unpadded());
let digest = hasher.finalize();
Address::new(digest.into_inner())
}
}

impl crate::PasskeyPublicKey {
pub fn to_address(&self) -> Address {
let mut hasher = Hasher::new();
self.write_into_hasher(&mut hasher);
let digest = hasher.finalize();
Address::new(digest.into_inner())
}

fn write_into_hasher(&self, hasher: &mut Hasher) {
hasher.update([self.scheme().to_u8()]);
hasher.update(self.inner().inner());
}
}

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`.
///
/// 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 {
use crate::MultisigMemberPublicKey::*;

let mut hasher = Hasher::new();
hasher.update([self.scheme().to_u8()]);
hasher.update(self.threshold().to_le_bytes());

for member in self.members() {
match member.public_key() {
Ed25519(p) => p.write_into_hasher(&mut hasher),
Secp256k1(p) => p.write_into_hasher(&mut hasher),
Secp256r1(p) => p.write_into_hasher(&mut hasher),
ZkLogin(p) => p.write_into_hasher_padded(&mut hasher),
}

hasher.update(member.weight().to_le_bytes());
}

let digest = hasher.finalize();
Address::new(digest.into_inner())
}
}

#[cfg(feature = "serde")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
mod type_digest {
use super::Hasher;
use crate::{
CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSummary, Digest,
Object, ObjectDigest, Transaction, TransactionDigest, TransactionEffects,
TransactionEffectsDigest, TransactionEvents, TransactionEventsDigest,
};

impl Object {
pub fn digest(&self) -> ObjectDigest {
const SALT: &str = "Object::";
let digest = type_digest(SALT, self);
ObjectDigest::new(digest.into_inner())
}
}

impl CheckpointSummary {
pub fn digest(&self) -> CheckpointDigest {
const SALT: &str = "CheckpointSummary::";
let digest = type_digest(SALT, self);
CheckpointDigest::new(digest.into_inner())
}
}

impl CheckpointContents {
pub fn digest(&self) -> CheckpointContentsDigest {
const SALT: &str = "CheckpointContents::";
let digest = type_digest(SALT, self);
CheckpointContentsDigest::new(digest.into_inner())
}
}

impl Transaction {
pub fn digest(&self) -> TransactionDigest {
const SALT: &str = "TransactionData::";
let digest = type_digest(SALT, self);
TransactionDigest::new(digest.into_inner())
}
}

impl TransactionEffects {
pub fn digest(&self) -> TransactionEffectsDigest {
const SALT: &str = "TransactionEffects::";
let digest = type_digest(SALT, self);
TransactionEffectsDigest::new(digest.into_inner())
}
}

impl TransactionEvents {
pub fn digest(&self) -> TransactionEventsDigest {
const SALT: &str = "TransactionEvents::";
let digest = type_digest(SALT, self);
TransactionEventsDigest::new(digest.into_inner())
}
}

fn type_digest<T: serde::Serialize>(salt: &str, ty: &T) -> Digest {
let mut hasher = Hasher::new();
hasher.update(salt);
bcs::serialize_into(&mut hasher, ty).unwrap();
hasher.finalize()
}
}

<<<<<<<< HEAD:crates/iota-sdk-types/src/hash.rs
#[cfg(feature = "serde")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
mod signing_message {
use crate::{
Digest, Intent, IntentAppId, IntentScope, IntentVersion, PersonalMessage, SigningDigest,
Transaction, hash::Hasher,
};

impl Transaction {
pub fn signing_digest(&self) -> SigningDigest {
const INTENT: Intent = Intent {
scope: IntentScope::TransactionData,
version: IntentVersion::V0,
app_id: IntentAppId::Iota,
};
let digest = signing_digest(INTENT, self);
digest.into_inner()
}
}

fn signing_digest<T: serde::Serialize + ?Sized>(intent: Intent, ty: &T) -> Digest {
let mut hasher = Hasher::new();
hasher.update(intent.to_bytes());
bcs::serialize_into(&mut hasher, ty).unwrap();
hasher.finalize()
}

impl<'a> PersonalMessage<'a> {
pub fn signing_digest(&self) -> SigningDigest {
const INTENT: Intent = Intent {
scope: IntentScope::PersonalMessage,
version: IntentVersion::V0,
app_id: IntentAppId::Iota,
};
let digest = signing_digest(INTENT, &self.0);
digest.into_inner()
}
}
}

========
>>>>>>>> develop:crates/iota-rust-sdk/src/hash.rs
/// A 1-byte domain separator for hashing Object ID in IOTA. 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)]
enum HashingIntent {
#[cfg(feature = "serde")]
ChildObjectId = 0xf0,
RegularObjectId = 0xf1,
}

impl crate::ObjectId {
/// Create an ObjectId from `TransactionDigest` and `count`.
///
/// `count` is the number of objects that have been created during a
/// transactions.
<<<<<<<< HEAD:crates/iota-sdk-types/src/hash.rs
pub fn derive_id(digest: crate::TransactionDigest, count: u64) -> Self {
========
pub fn derive_id(digest: crate::types::TransactionDigest, count: u64) -> Self {
>>>>>>>> develop:crates/iota-rust-sdk/src/hash.rs
let mut hasher = Hasher::new();
hasher.update([HashingIntent::RegularObjectId as u8]);
hasher.update(digest);
hasher.update(count.to_le_bytes());
let digest = hasher.finalize();
Self::new(digest.into_inner())
}

/// Derive an ObjectId for a Dynamic Child Object.
///
/// hash(parent || len(key) || key || key_type_tag)
#[cfg(feature = "serde")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub fn derive_dynamic_child_id(&self, key_type_tag: &crate::TypeTag, key_bytes: &[u8]) -> Self {
let mut hasher = Hasher::new();
hasher.update([HashingIntent::ChildObjectId as u8]);
hasher.update(self);
hasher.update(
u64::try_from(key_bytes.len())
.expect("key_bytes must fit into a u64")
.to_le_bytes(),
);
hasher.update(key_bytes);
bcs::serialize_into(&mut hasher, key_type_tag)
.expect("bcs serialization of `TypeTag` cannot fail");
let digest = hasher.finalize();

Self::new(digest.into_inner())
}
}

#[cfg(test)]
mod test {
use test_strategy::proptest;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;

use super::HashingIntent;
<<<<<<<< HEAD:crates/iota-sdk-types/src/hash.rs
use crate::SignatureScheme;
========
use crate::types::SignatureScheme;
>>>>>>>> develop:crates/iota-rust-sdk/src/hash.rs

impl HashingIntent {
fn from_byte(byte: u8) -> Result<Self, u8> {
match byte {
0xf0 => Ok(Self::ChildObjectId),
0xf1 => Ok(Self::RegularObjectId),
invalid => Err(invalid),
}
}
}

#[proptest]
fn hashing_intent_does_not_overlap_with_signature_scheme(intent: HashingIntent) {
SignatureScheme::from_byte(intent as u8).unwrap_err();
}

#[proptest]
fn signature_scheme_does_not_overlap_with_hashing_intent(scheme: SignatureScheme) {
HashingIntent::from_byte(scheme.to_u8()).unwrap_err();
}

#[proptest]
fn roundtrip_hashing_intent(intent: HashingIntent) {
assert_eq!(Ok(intent), HashingIntent::from_byte(intent as u8));
}
}
Loading

0 comments on commit 03a3675

Please sign in to comment.