1use crate::Address;
2use crate::Digest;
3
4use blake2::Digest as DigestTrait;
5
6type Blake2b256 = blake2::Blake2b<blake2::digest::consts::U32>;
7
8#[derive(Debug, Default)]
10pub struct Hasher(Blake2b256);
11
12impl Hasher {
13 pub fn new() -> Self {
15 Self(Blake2b256::new())
16 }
17
18 pub fn update<T: AsRef<[u8]>>(&mut self, data: T) {
20 self.0.update(data)
21 }
22
23 pub fn finalize(self) -> Digest {
26 let mut buf = [0; Digest::LENGTH];
27 let result = self.0.finalize();
28
29 buf.copy_from_slice(result.as_ref());
30
31 Digest::new(buf)
32 }
33
34 pub fn digest<T: AsRef<[u8]>>(data: T) -> Digest {
37 let mut hasher = Self::new();
38 hasher.update(data);
39 hasher.finalize()
40 }
41}
42
43impl std::io::Write for Hasher {
44 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
45 self.0.write(buf)
46 }
47
48 fn flush(&mut self) -> std::io::Result<()> {
49 self.0.flush()
50 }
51}
52
53impl crate::Ed25519PublicKey {
54 pub fn derive_address(&self) -> Address {
77 let mut hasher = Hasher::new();
78 self.write_into_hasher(&mut hasher);
79 let digest = hasher.finalize();
80 Address::new(digest.into_inner())
81 }
82
83 fn write_into_hasher(&self, hasher: &mut Hasher) {
84 hasher.update([self.scheme().to_u8()]);
85 hasher.update(self.inner());
86 }
87}
88
89impl crate::Secp256k1PublicKey {
90 pub fn derive_address(&self) -> Address {
113 let mut hasher = Hasher::new();
114 self.write_into_hasher(&mut hasher);
115 let digest = hasher.finalize();
116 Address::new(digest.into_inner())
117 }
118
119 fn write_into_hasher(&self, hasher: &mut Hasher) {
120 hasher.update([self.scheme().to_u8()]);
121 hasher.update(self.inner());
122 }
123}
124
125impl crate::Secp256r1PublicKey {
126 pub fn derive_address(&self) -> Address {
149 let mut hasher = Hasher::new();
150 self.write_into_hasher(&mut hasher);
151 let digest = hasher.finalize();
152 Address::new(digest.into_inner())
153 }
154
155 fn write_into_hasher(&self, hasher: &mut Hasher) {
156 hasher.update([self.scheme().to_u8()]);
157 hasher.update(self.inner());
158 }
159}
160
161impl crate::ZkLoginPublicIdentifier {
162 pub fn derive_address_padded(&self) -> Address {
168 let mut hasher = Hasher::new();
169 self.write_into_hasher_padded(&mut hasher);
170 let digest = hasher.finalize();
171 Address::new(digest.into_inner())
172 }
173
174 fn write_into_hasher_padded(&self, hasher: &mut Hasher) {
175 hasher.update([self.scheme().to_u8()]);
176 hasher.update([self.iss().len() as u8]); hasher.update(self.iss());
178 hasher.update(self.address_seed().padded());
179 }
180
181 pub fn derive_address_unpadded(&self) -> Address {
187 let mut hasher = Hasher::new();
188 hasher.update([self.scheme().to_u8()]);
189 hasher.update([self.iss().len() as u8]); hasher.update(self.iss());
191 hasher.update(self.address_seed().unpadded());
192 let digest = hasher.finalize();
193 Address::new(digest.into_inner())
194 }
195
196 pub fn derive_address(&self) -> impl ExactSizeIterator<Item = Address> {
202 self.internal_derive_addresses()
203 }
204
205 fn internal_derive_addresses(&self) -> DerivedAddressIter {
207 let primary = self.derive_address_padded();
208 let mut addresses = DerivedAddressIter::new(primary);
209
210 if self.address_seed().padded()[0] == 0 {
213 let secondary_address = self.derive_address_unpadded();
214
215 addresses.extra = Some(secondary_address);
216 }
217
218 addresses
219 }
220}
221
222impl crate::ZkLoginAuthenticator {
223 pub fn derive_address_padded(&self) -> Address {
224 self.inputs.public_identifier().derive_address_padded()
225 }
226
227 pub fn derive_address_unpadded(&self) -> Address {
228 self.inputs.public_identifier().derive_address_unpadded()
229 }
230
231 pub fn derive_address(&self) -> impl ExactSizeIterator<Item = Address> {
232 self.inputs.public_identifier().derive_address()
233 }
234}
235
236impl crate::MlDsa65PublicKey {
237 pub fn derive_address(&self) -> Address {
241 let mut hasher = Hasher::new();
242 self.write_into_hasher(&mut hasher);
243 let digest = hasher.finalize();
244 Address::new(digest.into_inner())
245 }
246
247 fn write_into_hasher(&self, hasher: &mut Hasher) {
248 hasher.update([self.scheme().to_u8()]);
249 hasher.update(self.inner());
250 }
251}
252
253impl crate::PasskeyPublicKey {
254 pub fn derive_address(&self) -> Address {
262 let mut hasher = Hasher::new();
263 self.write_into_hasher(&mut hasher);
264 let digest = hasher.finalize();
265 Address::new(digest.into_inner())
266 }
267
268 fn write_into_hasher(&self, hasher: &mut Hasher) {
269 hasher.update([self.scheme().to_u8()]);
270 hasher.update(self.inner().inner());
271 }
272}
273
274impl crate::PasskeyAuthenticator {
275 pub fn derive_address(&self) -> Address {
276 self.public_key().derive_address()
277 }
278}
279
280impl crate::MultisigCommittee {
281 pub fn derive_address(&self) -> Address {
299 use crate::MultisigMemberPublicKey::*;
300
301 let mut hasher = Hasher::new();
302 hasher.update([self.scheme().to_u8()]);
303 hasher.update(self.threshold().to_le_bytes());
304
305 for member in self.members() {
306 match member.public_key() {
307 Ed25519(p) => p.write_into_hasher(&mut hasher),
308 Secp256k1(p) => p.write_into_hasher(&mut hasher),
309 Secp256r1(p) => p.write_into_hasher(&mut hasher),
310 ZkLogin(p) => p.write_into_hasher_padded(&mut hasher),
311 Passkey(p) => p.write_into_hasher(&mut hasher),
312 MlDsa65(p) => p.write_into_hasher(&mut hasher),
313 }
314
315 hasher.update(member.weight().to_le_bytes());
316 }
317
318 let digest = hasher.finalize();
319 Address::new(digest.into_inner())
320 }
321}
322
323impl crate::MultisigAggregatedSignature {
324 pub fn derive_address(&self) -> Address {
325 self.committee().derive_address()
326 }
327}
328
329impl crate::SimpleSignature {
330 pub fn derive_address(&self) -> Address {
331 match self {
332 crate::SimpleSignature::Ed25519 { public_key, .. } => public_key.derive_address(),
333 crate::SimpleSignature::Secp256k1 { public_key, .. } => public_key.derive_address(),
334 crate::SimpleSignature::Secp256r1 { public_key, .. } => public_key.derive_address(),
335 crate::SimpleSignature::MlDsa65 { public_key, .. } => public_key.derive_address(),
336 }
337 }
338}
339
340impl crate::UserSignature {
341 pub fn derive_address(&self) -> Address {
342 self.derive_addresses().next().unwrap()
343 }
344
345 pub fn derive_addresses(&self) -> impl ExactSizeIterator<Item = Address> {
346 match self {
347 crate::UserSignature::Simple(simple) => {
348 DerivedAddressIter::new(simple.derive_address())
349 }
350 crate::UserSignature::Multisig(multisig) => {
351 DerivedAddressIter::new(multisig.derive_address())
352 }
353 crate::UserSignature::ZkLogin(zk) => {
354 zk.inputs.public_identifier().internal_derive_addresses()
355 }
356 crate::UserSignature::Passkey(passkey) => {
357 DerivedAddressIter::new(passkey.derive_address())
358 }
359 }
360 }
361}
362
363struct DerivedAddressIter {
364 primary: Option<Address>,
365 extra: Option<Address>,
366}
367
368impl DerivedAddressIter {
369 fn new(primary: Address) -> Self {
370 Self {
371 primary: Some(primary),
372 extra: None,
373 }
374 }
375}
376
377impl ExactSizeIterator for DerivedAddressIter {}
378impl Iterator for DerivedAddressIter {
379 type Item = Address;
380
381 fn next(&mut self) -> Option<Self::Item> {
382 if self.primary.is_some() {
383 self.primary.take()
384 } else if self.extra.is_some() {
385 self.extra.take()
386 } else {
387 None
388 }
389 }
390
391 fn size_hint(&self) -> (usize, Option<usize>) {
392 let len = self.primary.iter().len() + self.extra.iter().len();
393 (len, Some(len))
394 }
395}
396
397#[cfg(feature = "serde")]
398#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
399mod type_digest {
400 use super::Hasher;
401 use crate::Digest;
402
403 impl crate::Object {
404 pub fn digest(&self) -> Digest {
408 const SALT: &str = "Object::";
409 type_digest(SALT, self)
410 }
411 }
412
413 impl crate::CheckpointSummary {
414 pub fn digest(&self) -> Digest {
415 const SALT: &str = "CheckpointSummary::";
416 type_digest(SALT, self)
417 }
418 }
419
420 impl crate::CheckpointContents {
421 pub fn digest(&self) -> Digest {
422 const SALT: &str = "CheckpointContents::";
423 type_digest(SALT, self)
424 }
425 }
426
427 impl crate::Transaction {
428 pub fn digest(&self) -> Digest {
429 const SALT: &str = "TransactionData::";
430 type_digest(SALT, self)
431 }
432 }
433
434 impl crate::TransactionEffects {
435 pub fn digest(&self) -> Digest {
436 const SALT: &str = "TransactionEffects::";
437 type_digest(SALT, self)
438 }
439 }
440
441 impl crate::TransactionEvents {
442 pub fn digest(&self) -> Digest {
443 const SALT: &str = "TransactionEvents::";
444 type_digest(SALT, self)
445 }
446 }
447
448 impl crate::Event {
449 pub fn digest(&self) -> Digest {
462 let mut hasher = Hasher::new();
463 bcs::serialize_into(&mut hasher, self).unwrap();
464 hasher.finalize()
465 }
466 }
467
468 fn type_digest<T: serde::Serialize>(salt: &str, ty: &T) -> Digest {
469 let mut hasher = Hasher::new();
470 hasher.update(salt);
471 bcs::serialize_into(&mut hasher, ty).unwrap();
472 hasher.finalize()
473 }
474}
475
476#[cfg(feature = "serde")]
477#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
478mod signing_message {
479 use crate::Digest;
480 use crate::Intent;
481 use crate::IntentAppId;
482 use crate::IntentScope;
483 use crate::IntentVersion;
484 use crate::PersonalMessage;
485 use crate::SigningDigest;
486 use crate::Transaction;
487 use crate::hash::Hasher;
488
489 impl Transaction {
490 pub fn signing_digest(&self) -> SigningDigest {
491 const INTENT: Intent = Intent {
492 scope: IntentScope::TransactionData,
493 version: IntentVersion::V0,
494 app_id: IntentAppId::Sui,
495 };
496 let digest = signing_digest(INTENT, self);
497 digest.into_inner()
498 }
499 }
500
501 fn signing_digest<T: serde::Serialize + ?Sized>(intent: Intent, ty: &T) -> Digest {
502 let mut hasher = Hasher::new();
503 hasher.update(intent.to_bytes());
504 bcs::serialize_into(&mut hasher, ty).unwrap();
505 hasher.finalize()
506 }
507
508 impl PersonalMessage<'_> {
509 pub fn signing_digest(&self) -> SigningDigest {
510 const INTENT: Intent = Intent {
511 scope: IntentScope::PersonalMessage,
512 version: IntentVersion::V0,
513 app_id: IntentAppId::Sui,
514 };
515 let digest = signing_digest(INTENT, &self.0);
516 digest.into_inner()
517 }
518 }
519
520 impl crate::CheckpointSummary {
521 pub fn signing_message(&self) -> Vec<u8> {
522 const INTENT: Intent = Intent {
523 scope: IntentScope::CheckpointSummary,
524 version: IntentVersion::V0,
525 app_id: IntentAppId::Sui,
526 };
527 let mut message = Vec::new();
528 message.extend(INTENT.to_bytes());
529 bcs::serialize_into(&mut message, self).unwrap();
530 bcs::serialize_into(&mut message, &self.epoch).unwrap();
531 message
532 }
533 }
534}
535
536#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
540#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
541#[repr(u8)]
542enum HashingIntent {
543 #[cfg(feature = "serde")]
544 ChildObjectId = 0xf0,
545 RegularObjectId = 0xf1,
546}
547
548impl crate::Address {
549 pub fn derive_id(digest: crate::Digest, count: u64) -> Self {
553 let mut hasher = Hasher::new();
554 hasher.update([HashingIntent::RegularObjectId as u8]);
555 hasher.update(digest);
556 hasher.update(count.to_le_bytes());
557 let digest = hasher.finalize();
558 Self::new(digest.into_inner())
559 }
560
561 #[cfg(feature = "serde")]
565 #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
566 pub fn derive_dynamic_child_id(&self, key_type_tag: &crate::TypeTag, key_bytes: &[u8]) -> Self {
567 let mut hasher = Hasher::new();
568 hasher.update([HashingIntent::ChildObjectId as u8]);
569 hasher.update(self);
570 hasher.update(
571 u64::try_from(key_bytes.len())
572 .expect("key_bytes must fit into a u64")
573 .to_le_bytes(),
574 );
575 hasher.update(key_bytes);
576 bcs::serialize_into(&mut hasher, key_type_tag)
577 .expect("bcs serialization of `TypeTag` cannot fail");
578 let digest = hasher.finalize();
579
580 Self::new(digest.into_inner())
581 }
582
583 #[cfg(feature = "serde")]
587 #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
588 pub fn derive_object_id(&self, key_type_tag: &crate::TypeTag, key_bytes: &[u8]) -> Self {
589 use crate::Identifier;
590 use crate::StructTag;
591
592 let struct_tag = StructTag::new(
593 Address::from_static("0x2"),
594 Identifier::from_static("derived_object"),
595 Identifier::from_static("DerivedObjectKey"),
596 vec![key_type_tag.clone()],
597 );
598
599 self.derive_dynamic_child_id(&struct_tag.into(), key_bytes)
600 }
601}
602
603#[cfg(test)]
604mod test {
605 use super::HashingIntent;
606 use crate::Address;
607 use crate::SignatureScheme;
608 use crate::TypeTag;
609 use test_strategy::proptest;
610
611 #[cfg(target_arch = "wasm32")]
612 use wasm_bindgen_test::wasm_bindgen_test as test;
613
614 impl HashingIntent {
615 fn from_byte(byte: u8) -> Result<Self, u8> {
616 match byte {
617 0xf0 => Ok(Self::ChildObjectId),
618 0xf1 => Ok(Self::RegularObjectId),
619 invalid => Err(invalid),
620 }
621 }
622 }
623
624 #[proptest]
625 fn hashing_intent_does_not_overlap_with_signature_scheme(intent: HashingIntent) {
626 SignatureScheme::from_byte(intent as u8).unwrap_err();
627 }
628
629 #[proptest]
630 fn signature_scheme_does_not_overlap_with_hashing_intent(scheme: SignatureScheme) {
631 HashingIntent::from_byte(scheme.to_u8()).unwrap_err();
632 }
633
634 #[proptest]
635 fn roundtrip_hashing_intent(intent: HashingIntent) {
636 assert_eq!(Ok(intent), HashingIntent::from_byte(intent as u8));
637 }
638
639 #[test]
644 #[cfg(feature = "serde")]
645 fn event_digest_is_unsalted() {
646 use crate::Event;
647 use crate::Identifier;
648 use crate::StructTag;
649 use crate::hash::Hasher;
650
651 let event = Event {
652 package_id: Address::TWO,
653 module: Identifier::from_static("clock"),
654 sender: Address::TWO,
655 type_: StructTag::new(
656 Address::TWO,
657 Identifier::from_static("clock"),
658 Identifier::from_static("Tick"),
659 vec![],
660 ),
661 contents: vec![0xde, 0xad, 0xbe, 0xef],
662 };
663
664 let expected = Hasher::digest(bcs::to_bytes(&event).unwrap());
665 assert_eq!(event.digest(), expected);
666 }
667
668 #[test]
671 #[cfg(feature = "serde")]
672 fn test_derive_object_snapshot() {
673 let key_bytes = bcs::to_bytes("foo").unwrap();
675 let key_type_tag = TypeTag::Vector(Box::new(TypeTag::U8));
676
677 let id = Address::from_hex("0x2")
678 .unwrap()
679 .derive_object_id(&key_type_tag, &key_bytes);
680
681 assert_eq!(
682 id,
683 Address::from_hex("0xa2b411aa9588c398d8e3bc97dddbdd430b5ded7f81545d05e33916c3ca0f30c3")
684 .unwrap()
685 );
686 }
687}