1use super::SignatureType;
2use crate::transaction::PrimitiveSignature;
3use alloc::vec::Vec;
4use alloy_consensus::crypto::RecoveryError;
5use alloy_primitives::{Address, B256, U256, keccak256};
6use alloy_rlp::Encodable;
7use core::{
8 hash::{Hash, Hasher},
9 num::NonZeroU64,
10};
11
12#[cfg(not(feature = "std"))]
13use once_cell::race::OnceBox as OnceLock;
14#[cfg(feature = "std")]
15use std::sync::OnceLock;
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
24#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
25#[cfg_attr(feature = "reth-codec", derive(reth_codecs::Compact))]
26#[cfg_attr(test, reth_codecs::add_arbitrary_tests(compact, rlp))]
27pub struct TokenLimit {
28 pub token: Address,
30
31 pub limit: U256,
33
34 #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity"))]
38 pub period: u64,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
49#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
50#[cfg_attr(test, reth_codecs::add_arbitrary_tests(rlp))]
51pub struct CallScope {
52 pub target: Address,
54 #[cfg_attr(
56 feature = "serde",
57 serde(default, skip_serializing_if = "Vec::is_empty")
58 )]
59 pub selector_rules: Vec<SelectorRule>,
60}
61
62impl CallScope {
63 pub fn target(&self) -> Address {
65 self.target
66 }
67
68 pub fn allows_all_selectors(&self) -> bool {
70 self.selector_rules.is_empty()
71 }
72
73 pub fn selector_rules(&self) -> &[SelectorRule] {
75 &self.selector_rules
76 }
77
78 fn heap_size(&self) -> usize {
79 self.selector_rules.capacity() * size_of::<SelectorRule>()
80 + self
81 .selector_rules
82 .iter()
83 .map(SelectorRule::heap_size)
84 .sum::<usize>()
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
96#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
97#[cfg_attr(test, reth_codecs::add_arbitrary_tests(rlp))]
98pub struct SelectorRule {
99 #[cfg_attr(feature = "serde", serde(with = "selector_hex_serde"))]
101 pub selector: [u8; 4],
102 #[cfg_attr(
104 feature = "serde",
105 serde(default, skip_serializing_if = "Vec::is_empty")
106 )]
107 pub recipients: Vec<Address>,
108}
109
110impl SelectorRule {
111 pub fn selector(&self) -> [u8; 4] {
113 self.selector
114 }
115
116 pub fn recipients(&self) -> &[Address] {
118 &self.recipients
119 }
120
121 pub fn allows_all_recipients(&self) -> bool {
123 self.recipients.is_empty()
124 }
125
126 fn heap_size(&self) -> usize {
127 self.recipients.capacity() * size_of::<Address>()
128 }
129}
130
131use tempo_contracts::precompiles::IAccountKeychain::{
132 CallScope as AbiCallScope, SelectorRule as AbiSelectorRule,
133};
134
135impl From<AbiCallScope> for CallScope {
136 fn from(scope: AbiCallScope) -> Self {
137 Self {
138 target: scope.target,
139 selector_rules: scope.selectorRules.into_iter().map(Into::into).collect(),
140 }
141 }
142}
143
144impl From<CallScope> for AbiCallScope {
145 fn from(scope: CallScope) -> Self {
146 Self {
147 target: scope.target,
148 selectorRules: scope.selector_rules.into_iter().map(Into::into).collect(),
149 }
150 }
151}
152
153impl From<AbiSelectorRule> for SelectorRule {
154 fn from(rule: AbiSelectorRule) -> Self {
155 Self {
156 selector: rule.selector.into(),
157 recipients: rule.recipients,
158 }
159 }
160}
161
162impl From<SelectorRule> for AbiSelectorRule {
163 fn from(rule: SelectorRule) -> Self {
164 Self {
165 selector: rule.selector.into(),
166 recipients: rule.recipients,
167 }
168 }
169}
170
171#[derive(Clone, Debug, PartialEq, Eq, Hash)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
188#[cfg_attr(test, reth_codecs::add_arbitrary_tests(rlp))]
189pub struct KeyAuthorization {
190 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
193 pub chain_id: u64,
194
195 pub key_type: SignatureType,
197
198 pub key_id: Address,
200
201 #[cfg_attr(feature = "serde", serde(with = "serde_nonzero_quantity_opt"))]
208 pub expiry: Option<NonZeroU64>,
209
210 pub limits: Option<Vec<TokenLimit>>,
215
216 pub allowed_calls: Option<Vec<CallScope>>,
221
222 pub witness: Option<B256>,
227
228 #[cfg_attr(feature = "serde", serde(default))]
230 pub is_admin: bool,
231
232 pub account: Option<Address>,
237}
238
239impl KeyAuthorization {
240 pub fn unrestricted(chain_id: u64, key_type: SignatureType, key_id: Address) -> Self {
243 Self {
244 chain_id,
245 key_type,
246 key_id,
247 expiry: None,
248 limits: None,
249 allowed_calls: None,
250 witness: None,
251 is_admin: false,
252 account: None,
253 }
254 }
255
256 pub fn with_expiry(mut self, expiry: u64) -> Self {
258 self.expiry = NonZeroU64::new(expiry);
259 self
260 }
261
262 pub fn with_limits(mut self, limits: Vec<TokenLimit>) -> Self {
264 self.limits = Some(limits);
265 self
266 }
267
268 pub fn with_allowed_calls(mut self, allowed_calls: Vec<CallScope>) -> Self {
270 self.allowed_calls = Some(allowed_calls);
271 self
272 }
273
274 pub fn with_no_spending(mut self) -> Self {
276 self.limits = Some(Vec::new());
277 self
278 }
279
280 pub fn with_no_calls(mut self) -> Self {
282 self.allowed_calls = Some(Vec::new());
283 self
284 }
285
286 pub fn with_witness(mut self, witness: B256) -> Self {
288 self.witness = Some(witness);
289 self
290 }
291
292 pub fn witness(&self) -> Option<B256> {
294 self.witness
295 }
296
297 pub fn into_admin(mut self, account: Address) -> Self {
299 self.is_admin = true;
300 self.account = Some(account);
301 self
302 }
303
304 pub fn with_account(mut self, account: Address) -> Self {
306 self.account = Some(account);
307 self
308 }
309
310 pub fn is_admin(&self) -> bool {
312 self.is_admin
313 }
314
315 pub fn signature_hash(&self) -> B256 {
317 let mut buf = Vec::new();
318 self.encode(&mut buf);
319 keccak256(&buf)
320 }
321
322 pub fn has_periodic_limits(&self) -> bool {
324 self.limits
325 .as_ref()
326 .is_some_and(|limits| limits.iter().any(|limit| limit.period != 0))
327 }
328
329 pub fn has_call_scopes(&self) -> bool {
331 self.allowed_calls.is_some()
332 }
333
334 pub fn has_witness(&self) -> bool {
336 self.witness.is_some()
337 }
338
339 pub fn has_unlimited_spending(&self) -> bool {
341 self.limits.is_none()
342 }
343
344 pub fn never_expires(&self) -> bool {
346 self.expiry.is_none()
347 }
348
349 pub fn is_legacy_compatible(&self) -> bool {
351 !(self.has_periodic_limits()
352 || self.has_call_scopes()
353 || self.has_witness()
354 || self.is_admin
355 || self.account.is_some())
356 }
357
358 pub fn into_signed(self, signature: PrimitiveSignature) -> SignedKeyAuthorization {
360 SignedKeyAuthorization::new(self, signature)
361 }
362
363 pub fn validate_chain_id(
368 &self,
369 expected_chain_id: u64,
370 is_t1c: bool,
371 ) -> Result<(), KeyAuthorizationChainIdError> {
372 if is_t1c {
373 if self.chain_id != expected_chain_id {
374 return Err(KeyAuthorizationChainIdError {
375 expected: expected_chain_id,
376 got: self.chain_id,
377 });
378 }
379 } else if self.chain_id != 0 && self.chain_id != expected_chain_id {
380 return Err(KeyAuthorizationChainIdError {
381 expected: expected_chain_id,
382 got: self.chain_id,
383 });
384 }
385 Ok(())
386 }
387
388 pub fn size(&self) -> usize {
390 size_of::<Self>()
391 + self
392 .limits
393 .as_ref()
394 .map_or(0, |limits| limits.capacity() * size_of::<TokenLimit>())
395 + self.allowed_calls.as_ref().map_or(0, |scopes| {
396 scopes.capacity() * size_of::<CallScope>()
397 + scopes.iter().map(CallScope::heap_size).sum::<usize>()
398 })
399 }
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404pub struct KeyAuthorizationChainIdError {
405 pub expected: u64,
407 pub got: u64,
409}
410
411#[derive(Clone, Debug, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable, derive_more::Deref)]
413#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
414#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
415#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
416#[cfg_attr(test, reth_codecs::add_arbitrary_tests(compact, rlp))]
417pub struct SignedKeyAuthorization {
418 #[cfg_attr(feature = "serde", serde(flatten))]
420 #[deref]
421 pub authorization: KeyAuthorization,
422
423 pub signature: PrimitiveSignature,
425
426 #[cfg_attr(feature = "serde", serde(skip))]
430 #[cfg_attr(any(test, feature = "arbitrary"), arbitrary(default))]
431 #[rlp(skip, default)]
432 signer: OnceLock<Address>,
433}
434
435impl SignedKeyAuthorization {
436 pub fn new(authorization: KeyAuthorization, signature: PrimitiveSignature) -> Self {
438 Self {
439 authorization,
440 signature,
441 signer: OnceLock::new(),
442 }
443 }
444
445 pub fn recover_signer(&self) -> Result<Address, RecoveryError> {
447 if let Some(signer) = self.signer.get() {
448 return Ok(*signer);
449 }
450
451 let signer = self
452 .signature
453 .recover_signer(&self.authorization.signature_hash())?;
454 self.cache_signer(signer);
455
456 Ok(signer)
457 }
458
459 #[cfg(feature = "std")]
460 fn cache_signer(&self, signer: Address) {
461 let _ = self.signer.set(signer);
462 }
463
464 #[cfg(not(feature = "std"))]
465 fn cache_signer(&self, signer: Address) {
466 let _ = self.signer.set(alloc::boxed::Box::new(signer));
467 }
468
469 pub fn size(&self) -> usize {
471 self.authorization.size() + self.signature.size()
472 }
473}
474
475impl PartialEq for SignedKeyAuthorization {
476 fn eq(&self, other: &Self) -> bool {
477 self.authorization == other.authorization && self.signature == other.signature
478 }
479}
480
481impl Eq for SignedKeyAuthorization {}
482
483impl Hash for SignedKeyAuthorization {
484 fn hash<H: Hasher>(&self, state: &mut H) {
485 self.authorization.hash(state);
486 self.signature.hash(state);
487 }
488}
489
490#[cfg(any(test, feature = "arbitrary"))]
491impl<'a> arbitrary::Arbitrary<'a> for KeyAuthorization {
492 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
493 Ok(Self {
494 chain_id: u.arbitrary()?,
495 key_type: u.arbitrary()?,
496 key_id: u.arbitrary()?,
497 expiry: u.arbitrary()?,
498 limits: u.arbitrary()?,
499 allowed_calls: u.arbitrary()?,
500 witness: u.arbitrary::<Option<[u8; 32]>>()?.map(B256::from),
501 is_admin: u.arbitrary()?,
502 account: u.arbitrary()?,
503 })
504 }
505}
506
507#[cfg(feature = "serde")]
508#[doc(hidden)]
509pub mod serde_nonzero_quantity_opt {
510 use core::num::NonZeroU64;
511
512 use serde::{Deserializer, Serializer, de::Error as _};
513
514 pub fn serialize<S>(value: &Option<NonZeroU64>, serializer: S) -> Result<S::Ok, S::Error>
515 where
516 S: Serializer,
517 {
518 alloy_serde::quantity::opt::serialize(&value.map(NonZeroU64::get), serializer)
519 }
520
521 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NonZeroU64>, D::Error>
522 where
523 D: Deserializer<'de>,
524 {
525 alloy_serde::quantity::opt::deserialize(deserializer).and_then(|value: Option<u64>| {
526 value
527 .map(|value| {
528 NonZeroU64::new(value)
529 .ok_or_else(|| D::Error::custom("expected non-zero quantity"))
530 })
531 .transpose()
532 })
533 }
534}
535
536mod rlp {
537 use super::*;
538 use alloy_rlp::{Decodable, Encodable};
539
540 #[derive(
541 Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable,
542 )]
543 #[rlp(trailing(canonical))]
544 struct KeyAuthorizationWire {
545 chain_id: u64,
546 key_type: SignatureType,
547 key_id: Address,
548 expiry: Option<NonZeroU64>,
549 limits: Option<Vec<TokenLimit>>,
550 allowed_calls: Option<Vec<CallScope>>,
551 witness: Option<B256>,
552 is_admin: Option<NonZeroU64>,
553 account: Option<Address>,
554 }
555
556 impl From<&KeyAuthorization> for KeyAuthorizationWire {
557 fn from(value: &KeyAuthorization) -> Self {
558 let KeyAuthorization {
559 chain_id,
560 key_type,
561 key_id,
562 expiry,
563 limits,
564 allowed_calls,
565 witness,
566 is_admin,
567 account,
568 } = value;
569
570 Self {
571 chain_id: *chain_id,
572 key_type: *key_type,
573 key_id: *key_id,
574 expiry: *expiry,
575 limits: limits.clone(),
576 allowed_calls: allowed_calls.clone(),
577 witness: *witness,
578 is_admin: is_admin.then_some(NonZeroU64::MIN),
579 account: *account,
580 }
581 }
582 }
583
584 impl TryFrom<KeyAuthorizationWire> for KeyAuthorization {
585 type Error = alloy_rlp::Error;
586
587 fn try_from(value: KeyAuthorizationWire) -> alloy_rlp::Result<Self> {
588 if value.is_admin.is_some_and(|marker| marker.get() != 1) {
589 return Err(alloy_rlp::Error::Custom(
590 "invalid admin key authorization marker",
591 ));
592 }
593
594 Ok(Self {
595 chain_id: value.chain_id,
596 key_type: value.key_type,
597 key_id: value.key_id,
598 expiry: value.expiry,
599 limits: value.limits,
600 allowed_calls: value.allowed_calls,
601 witness: value.witness,
602 is_admin: value.is_admin.is_some(),
603 account: value.account,
604 })
605 }
606 }
607
608 impl Decodable for KeyAuthorization {
609 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
610 KeyAuthorizationWire::decode(buf).and_then(TryInto::try_into)
611 }
612 }
613
614 impl Encodable for KeyAuthorization {
615 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
616 KeyAuthorizationWire::from(self).encode(out);
617 }
618
619 fn length(&self) -> usize {
620 KeyAuthorizationWire::from(self).length()
621 }
622 }
623
624 #[derive(
625 Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable,
626 )]
627 #[rlp(trailing(canonical))]
628 struct TokenLimitWire {
629 token: Address,
630 limit: U256,
631 period: Option<NonZeroU64>,
632 }
633
634 impl From<&TokenLimit> for TokenLimitWire {
635 fn from(value: &TokenLimit) -> Self {
636 let TokenLimit {
637 token,
638 limit,
639 period,
640 } = value;
641 Self {
642 token: *token,
643 limit: *limit,
644 period: NonZeroU64::new(*period),
645 }
646 }
647 }
648
649 impl From<TokenLimitWire> for TokenLimit {
650 fn from(value: TokenLimitWire) -> Self {
651 Self {
652 token: value.token,
653 limit: value.limit,
654 period: value.period.map(|period| period.get()).unwrap_or(0),
655 }
656 }
657 }
658
659 impl Decodable for TokenLimit {
660 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
661 Ok(TokenLimitWire::decode(buf)?.into())
662 }
663 }
664
665 impl Encodable for TokenLimit {
666 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
667 TokenLimitWire::from(self).encode(out)
668 }
669
670 fn length(&self) -> usize {
671 TokenLimitWire::from(self).length()
672 }
673 }
674}
675
676#[cfg(feature = "serde")]
677mod selector_hex_serde {
678 use alloy_primitives::FixedBytes;
679 use serde::{Deserialize, Deserializer, Serialize, Serializer};
680
681 #[derive(Deserialize)]
682 #[serde(untagged)]
683 enum SelectorValue {
684 Hex(FixedBytes<4>),
685 Array([u8; 4]),
686 }
687
688 pub(super) fn serialize<S>(selector: &[u8; 4], serializer: S) -> Result<S::Ok, S::Error>
689 where
690 S: Serializer,
691 {
692 FixedBytes::<4>::from(*selector).serialize(serializer)
693 }
694
695 pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 4], D::Error>
696 where
697 D: Deserializer<'de>,
698 {
699 Ok(match SelectorValue::deserialize(deserializer)? {
700 SelectorValue::Hex(selector) => selector.into(),
701 SelectorValue::Array(selector) => selector,
702 })
703 }
704}
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709 use crate::transaction::{
710 TempoSignature,
711 tt_authorization::tests::{generate_secp256k1_keypair, sign_hash},
712 };
713 use alloy_rlp::{Decodable, Encodable};
714
715 fn nonzero(value: u64) -> NonZeroU64 {
716 NonZeroU64::new(value).expect("test expiry must be non-zero")
717 }
718
719 fn make_auth(expiry: Option<u64>, limits: Option<Vec<TokenLimit>>) -> KeyAuthorization {
720 KeyAuthorization {
721 chain_id: 1,
722 key_type: SignatureType::Secp256k1,
723 key_id: Address::random(),
724 expiry: expiry.and_then(NonZeroU64::new),
725 limits,
726 allowed_calls: None,
727 witness: None,
728 is_admin: false,
729 account: None,
730 }
731 }
732
733 #[test]
734 fn test_zero_witness_roundtrip_and_changes_signature_hash() {
735 let auth = make_auth(None, None);
736 let zero_witness_auth = auth.clone().with_witness(B256::ZERO);
737
738 let mut encoded = Vec::new();
739 zero_witness_auth.encode(&mut encoded);
740
741 assert_eq!(zero_witness_auth.witness(), Some(B256::ZERO));
742 assert!(zero_witness_auth.has_witness());
743 assert_ne!(zero_witness_auth.signature_hash(), auth.signature_hash());
744
745 let decoded =
746 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
747 assert_eq!(decoded, zero_witness_auth);
748
749 let mut reencoded = Vec::new();
750 decoded.encode(&mut reencoded);
751 assert_eq!(reencoded, encoded);
752 }
753
754 #[test]
755 fn test_witness_roundtrip_and_changes_signature_hash() {
756 let auth = make_auth(None, None);
757 let witness = B256::repeat_byte(0x53);
758 let witness_auth = auth.clone().with_witness(witness);
759
760 assert_eq!(witness_auth.witness(), Some(witness));
761 assert!(witness_auth.has_witness());
762 assert_ne!(witness_auth.signature_hash(), auth.signature_hash());
763
764 let mut encoded = Vec::new();
765 witness_auth.encode(&mut encoded);
766
767 let decoded =
768 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
769 assert_eq!(decoded, witness_auth);
770
771 let mut reencoded = Vec::new();
772 decoded.encode(&mut reencoded);
773 assert_eq!(reencoded, encoded);
774 }
775
776 #[test]
777 fn test_account_roundtrip_and_signature_binding() {
778 let account = Address::repeat_byte(0x11);
779 let other_account = Address::repeat_byte(0x22);
780 let key_id = Address::repeat_byte(0x33);
781 let witness = B256::repeat_byte(0x44);
782
783 let normal = KeyAuthorization::unrestricted(1, SignatureType::Secp256k1, key_id)
784 .with_witness(witness);
785 let admin = normal.clone().into_admin(account);
786 let other_admin = normal.clone().into_admin(other_account);
787 let account_bound = normal.clone().with_account(account);
788 let other_account_bound = normal.clone().with_account(other_account);
789
790 assert!(!normal.is_admin());
791 assert!(admin.is_admin());
792 assert!(admin.is_admin);
793 assert_eq!(admin.account, Some(account));
794 assert!(!admin.is_legacy_compatible());
795 assert!(!account_bound.is_admin());
796 assert!(!account_bound.is_admin);
797 assert_eq!(account_bound.account, Some(account));
798 assert!(!account_bound.is_legacy_compatible());
799
800 let mut encoded = Vec::new();
801 admin.encode(&mut encoded);
802 let decoded =
803 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
804 assert_eq!(decoded, admin);
805 assert_eq!(decoded.witness(), Some(witness));
806 assert!(decoded.is_admin);
807 assert_eq!(decoded.account, Some(account));
808
809 assert_ne!(admin.signature_hash(), normal.signature_hash());
810 assert_ne!(admin.signature_hash(), other_admin.signature_hash());
811 assert_ne!(account_bound.signature_hash(), normal.signature_hash());
812 assert_ne!(
813 account_bound.signature_hash(),
814 other_account_bound.signature_hash()
815 );
816 }
817
818 #[test]
819 fn test_witness_encoding_preserves_prior_absent_trailing_fields() {
820 let witness = B256::repeat_byte(0x53);
821 let auth = make_auth(None, None).with_witness(witness);
822
823 let mut encoded = Vec::new();
824 auth.encode(&mut encoded);
825
826 let mut payload = &encoded[..];
827 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
828 assert!(header.list);
829 assert_eq!(header.payload_length, payload.len());
830
831 let fixed_fields_len =
832 auth.chain_id.length() + auth.key_type.length() + auth.key_id.length();
833 assert_eq!(
834 &payload[fixed_fields_len..fixed_fields_len + 3],
835 &[alloy_rlp::EMPTY_STRING_CODE; 3],
836 "expiry, limits, and allowed_calls must be explicit empty placeholders before witness"
837 );
838
839 let mut witness_payload = &payload[fixed_fields_len + 3..];
840 let decoded_witness = B256::decode(&mut witness_payload).expect("decode witness field");
841 assert_eq!(decoded_witness, witness);
842 assert!(witness_payload.is_empty());
843 }
844
845 #[test]
846 fn test_decode_accepts_explicit_zero_witness() {
847 let auth = make_auth(None, None);
848 let mut encoded = Vec::new();
849 let payload_length = auth.chain_id.length()
850 + auth.key_type.length()
851 + auth.key_id.length()
852 + 3
853 + B256::ZERO.length();
854 alloy_rlp::Header {
855 list: true,
856 payload_length,
857 }
858 .encode(&mut encoded);
859 auth.chain_id.encode(&mut encoded);
860 auth.key_type.encode(&mut encoded);
861 auth.key_id.encode(&mut encoded);
862 encoded.extend_from_slice(&[alloy_rlp::EMPTY_STRING_CODE; 3]);
863 B256::ZERO.encode(&mut encoded);
864
865 let decoded =
866 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
867 assert_eq!(decoded.witness(), Some(B256::ZERO));
868 }
869
870 #[test]
871 fn test_decode_rejects_explicit_absent_witness_field() {
872 let auth = make_auth(None, None);
873 let mut encoded = Vec::new();
874 let payload_length =
875 auth.chain_id.length() + auth.key_type.length() + auth.key_id.length() + 4;
876 alloy_rlp::Header {
877 list: true,
878 payload_length,
879 }
880 .encode(&mut encoded);
881 auth.chain_id.encode(&mut encoded);
882 auth.key_type.encode(&mut encoded);
883 auth.key_id.encode(&mut encoded);
884 encoded.extend_from_slice(&[alloy_rlp::EMPTY_STRING_CODE; 4]);
885
886 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice())
887 .expect_err("absent witness field must be omitted, not encoded as 0x80");
888 }
889
890 #[test]
891 fn test_signature_hash_and_recover_signer() {
892 let (signing_key, expected_address) = generate_secp256k1_keypair();
893
894 let auth = make_auth(Some(1000), None);
895
896 let hash1 = auth.signature_hash();
898 let hash2 = auth.signature_hash();
899 assert_eq!(hash1, hash2, "signature_hash should be deterministic");
900 assert_ne!(hash1, B256::ZERO);
901
902 let auth2 = make_auth(Some(2000), None);
904 assert_ne!(auth.signature_hash(), auth2.signature_hash());
905
906 let signature = sign_hash(&signing_key, &auth.signature_hash());
908 let inner_sig = match signature {
909 TempoSignature::Primitive(p) => p,
910 _ => panic!("Expected primitive signature"),
911 };
912 let signed = auth.clone().into_signed(inner_sig);
913
914 let recovered = signed.recover_signer();
916 assert!(recovered.is_ok());
917 assert_eq!(recovered.unwrap(), expected_address);
918
919 let wrong_sig = sign_hash(&signing_key, &B256::random());
921 let wrong_inner = match wrong_sig {
922 TempoSignature::Primitive(p) => p,
923 _ => panic!("Expected primitive signature"),
924 };
925 let bad_signed = auth.into_signed(wrong_inner);
926 let bad_recovered = bad_signed.recover_signer();
927 assert!(bad_recovered.is_ok());
928 assert_ne!(bad_recovered.unwrap(), expected_address);
929 }
930
931 #[test]
932 fn test_spending_expiry_and_size() {
933 assert!(make_auth(None, None).has_unlimited_spending());
935 assert!(!make_auth(None, Some(vec![])).has_unlimited_spending());
936 assert!(
937 !make_auth(
938 None,
939 Some(vec![TokenLimit {
940 token: Address::ZERO,
941 limit: U256::from(100),
942 period: 0,
943 }])
944 )
945 .has_unlimited_spending()
946 );
947
948 assert!(make_auth(None, None).never_expires());
950 assert!(!make_auth(Some(1000), None).never_expires());
951 assert_eq!(NonZeroU64::new(0), None);
952 }
953
954 #[test]
955 fn test_size_does_not_double_count_call_scope_structs() {
956 let recipients = vec![Address::repeat_byte(0x11), Address::repeat_byte(0x22)];
957 let mut rules = Vec::with_capacity(3);
958 rules.push(SelectorRule {
959 selector: [1, 2, 3, 4],
960 recipients,
961 });
962
963 let mut scopes = Vec::with_capacity(2);
964 scopes.push(CallScope {
965 target: Address::repeat_byte(0x33),
966 selector_rules: rules,
967 });
968
969 let auth =
970 KeyAuthorization::unrestricted(1, SignatureType::Secp256k1, Address::repeat_byte(0x44))
971 .with_allowed_calls(scopes);
972
973 let scope_rules = auth.allowed_calls.as_ref().unwrap();
974 let selector_rules = &scope_rules[0].selector_rules;
975 let recipients = &selector_rules[0].recipients;
976
977 let expected = size_of::<KeyAuthorization>()
978 + scope_rules.capacity() * size_of::<CallScope>()
979 + selector_rules.capacity() * size_of::<SelectorRule>()
980 + recipients.capacity() * size_of::<Address>();
981
982 assert_eq!(auth.size(), expected);
983 }
984
985 #[test]
986 fn test_zero_expiry_is_unrepresentable() {
987 assert_eq!(NonZeroU64::new(0), None);
988 assert_eq!(Some(NonZeroU64::get(nonzero(1))), Some(1));
989 }
990
991 fn make_auth_with_chain_id(chain_id: u64) -> KeyAuthorization {
992 KeyAuthorization {
993 chain_id,
994 key_type: SignatureType::Secp256k1,
995 key_id: Address::random(),
996 expiry: None,
997 limits: None,
998 allowed_calls: None,
999 witness: None,
1000 is_admin: false,
1001 account: None,
1002 }
1003 }
1004
1005 #[test]
1006 fn test_token_limit_legacy_decode_defaults_period_to_zero() {
1007 let token = Address::random();
1008 let limit = U256::from(42);
1009
1010 let mut encoded = Vec::new();
1012 alloy_rlp::Header {
1013 list: true,
1014 payload_length: token.length() + limit.length(),
1015 }
1016 .encode(&mut encoded);
1017 token.encode(&mut encoded);
1018 limit.encode(&mut encoded);
1019
1020 let decoded: TokenLimit =
1021 Decodable::decode(&mut encoded.as_slice()).expect("decode legacy token limit");
1022 assert_eq!(decoded.token, token);
1023 assert_eq!(decoded.limit, limit);
1024 assert_eq!(decoded.period, 0);
1025 }
1026
1027 #[test]
1028 fn test_token_limit_encoding_omits_zero_period() {
1029 let token_limit = TokenLimit {
1030 token: Address::random(),
1031 limit: U256::from(1234),
1032 period: 0,
1033 };
1034
1035 let mut encoded = Vec::new();
1036 token_limit.encode(&mut encoded);
1037
1038 let mut payload = &encoded[..];
1039 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
1040 assert!(header.list);
1041 assert_eq!(
1042 header.payload_length,
1043 token_limit.token.length() + token_limit.limit.length()
1044 );
1045 }
1046
1047 #[test]
1048 fn test_token_limit_decode_accepts_explicit_zero_period_field() {
1049 let token = Address::random();
1050 let limit = U256::from(42);
1051
1052 let mut encoded = Vec::new();
1053 alloy_rlp::Header {
1054 list: true,
1055 payload_length: token.length() + limit.length(),
1056 }
1057 .encode(&mut encoded);
1058 token.encode(&mut encoded);
1059 limit.encode(&mut encoded);
1060
1061 let decoded: TokenLimit =
1062 <TokenLimit as Decodable>::decode(&mut encoded.as_slice()).expect("decode token limit");
1063 assert_eq!(decoded.token, token);
1064 assert_eq!(decoded.limit, limit);
1065 assert_eq!(decoded.period, 0);
1066 }
1067
1068 #[test]
1069 fn test_key_authorization_roundtrip_preserves_explicit_nested_allow_all_lists() {
1070 let auth =
1071 KeyAuthorization::unrestricted(1, SignatureType::Secp256k1, Address::repeat_byte(0x11))
1072 .with_allowed_calls(vec![
1073 CallScope {
1074 target: Address::repeat_byte(0x22),
1075 selector_rules: vec![],
1076 },
1077 CallScope {
1078 target: Address::repeat_byte(0x33),
1079 selector_rules: vec![SelectorRule {
1080 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1081 recipients: vec![],
1082 }],
1083 },
1084 ]);
1085
1086 let mut encoded = Vec::new();
1087 auth.encode(&mut encoded);
1088
1089 let decoded =
1090 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
1091
1092 let mut reencoded = Vec::new();
1093 decoded.encode(&mut reencoded);
1094
1095 assert_eq!(reencoded, encoded);
1096 }
1097
1098 #[test]
1099 fn test_call_scope_decode_rejects_omitted_selector_rules() {
1100 let target = Address::repeat_byte(0x11);
1101
1102 let mut encoded = Vec::new();
1103 alloy_rlp::Header {
1104 list: true,
1105 payload_length: target.length(),
1106 }
1107 .encode(&mut encoded);
1108 target.encode(&mut encoded);
1109
1110 <CallScope as Decodable>::decode(&mut encoded.as_slice())
1111 .expect_err("omitted selector_rules should be rejected");
1112 }
1113
1114 #[test]
1115 fn test_call_scope_explicit_empty_selector_rules_roundtrip() {
1116 let scope = CallScope {
1117 target: Address::repeat_byte(0x11),
1118 selector_rules: Vec::new(),
1119 };
1120
1121 let mut encoded = Vec::new();
1122 scope.encode(&mut encoded);
1123
1124 let mut payload = &encoded[..];
1125 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
1126 assert!(header.list);
1127 assert_eq!(
1128 header.payload_length,
1129 scope.target.length() + Vec::<SelectorRule>::new().length()
1130 );
1131
1132 let decoded =
1133 <CallScope as Decodable>::decode(&mut encoded.as_slice()).expect("decode scope");
1134 assert_eq!(decoded, scope);
1135 }
1136
1137 #[test]
1138 fn test_call_scope_decode_accepts_explicit_empty_selector_rules_list() {
1139 let target = Address::repeat_byte(0x11);
1140
1141 let mut encoded = Vec::new();
1142 alloy_rlp::Header {
1143 list: true,
1144 payload_length: target.length() + Vec::<SelectorRule>::new().length(),
1145 }
1146 .encode(&mut encoded);
1147 target.encode(&mut encoded);
1148 Vec::<SelectorRule>::new().encode(&mut encoded);
1149
1150 let decoded =
1151 <CallScope as Decodable>::decode(&mut encoded.as_slice()).expect("decode scope");
1152 assert_eq!(decoded.target, target);
1153 assert!(decoded.selector_rules.is_empty());
1154
1155 let mut reencoded = Vec::new();
1156 decoded.encode(&mut reencoded);
1157 assert_eq!(reencoded, encoded);
1158 }
1159
1160 #[test]
1161 fn test_selector_rule_decode_rejects_omitted_recipients() {
1162 let selector = [0xaa, 0xbb, 0xcc, 0xdd];
1163
1164 let mut encoded = Vec::new();
1165 alloy_rlp::Header {
1166 list: true,
1167 payload_length: selector.length(),
1168 }
1169 .encode(&mut encoded);
1170 selector.encode(&mut encoded);
1171
1172 <SelectorRule as Decodable>::decode(&mut encoded.as_slice())
1173 .expect_err("omitted recipients should be rejected");
1174 }
1175
1176 #[test]
1177 fn test_selector_rule_empty_recipients_roundtrip() {
1178 let rule = SelectorRule {
1179 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1180 recipients: Vec::new(),
1181 };
1182
1183 let mut encoded = Vec::new();
1184 rule.encode(&mut encoded);
1185
1186 let mut payload = &encoded[..];
1187 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
1188 assert!(header.list);
1189 assert_eq!(
1190 header.payload_length,
1191 rule.selector.length() + Vec::<Address>::new().length()
1192 );
1193
1194 let decoded =
1195 <SelectorRule as Decodable>::decode(&mut encoded.as_slice()).expect("decode rule");
1196 assert_eq!(decoded, rule);
1197 }
1198
1199 #[test]
1200 fn test_selector_rule_decode_accepts_explicit_empty_recipient_list() {
1201 let selector = [0xaa, 0xbb, 0xcc, 0xdd];
1202
1203 let mut encoded = Vec::new();
1204 alloy_rlp::Header {
1205 list: true,
1206 payload_length: selector.length() + Vec::<Address>::new().length(),
1207 }
1208 .encode(&mut encoded);
1209 selector.encode(&mut encoded);
1210 Vec::<Address>::new().encode(&mut encoded);
1211
1212 let decoded =
1213 <SelectorRule as Decodable>::decode(&mut encoded.as_slice()).expect("decode rule");
1214 assert_eq!(decoded.selector, selector);
1215 assert!(decoded.recipients.is_empty());
1216
1217 let mut reencoded = Vec::new();
1218 decoded.encode(&mut reencoded);
1219 assert_eq!(reencoded, encoded);
1220 }
1221
1222 #[test]
1223 fn test_selector_rule_roundtrip_preserves_non_empty_recipient_list() {
1224 let rule = SelectorRule {
1225 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1226 recipients: vec![Address::repeat_byte(0x11), Address::repeat_byte(0x22)],
1227 };
1228
1229 let mut encoded = Vec::new();
1230 rule.encode(&mut encoded);
1231
1232 let decoded =
1233 <SelectorRule as Decodable>::decode(&mut encoded.as_slice()).expect("decode rule");
1234 assert_eq!(decoded, rule);
1235 }
1236
1237 #[cfg(feature = "serde")]
1238 #[test]
1239 fn test_token_limit_json_defaults_period_to_zero() {
1240 let token = Address::repeat_byte(0x11);
1241
1242 let decoded: TokenLimit = serde_json::from_value(serde_json::json!({
1243 "token": token,
1244 "limit": "0x2a",
1245 }))
1246 .expect("deserialize legacy JSON token limit");
1247
1248 assert_eq!(decoded.token, token);
1249 assert_eq!(decoded.limit, U256::from(42));
1250 assert_eq!(decoded.period, 0);
1251 }
1252
1253 #[cfg(feature = "serde")]
1254 #[test]
1255 fn test_token_limit_json_serializes_period_as_quantity() {
1256 let value = serde_json::to_value(TokenLimit {
1257 token: Address::repeat_byte(0x11),
1258 limit: U256::from(42),
1259 period: 7,
1260 })
1261 .expect("serialize token limit");
1262
1263 assert_eq!(value["period"], serde_json::json!("0x7"));
1264 }
1265
1266 #[cfg(feature = "serde")]
1267 #[test]
1268 fn test_selector_rule_json_accepts_hex_selector() {
1269 let recipient = Address::repeat_byte(0x11);
1270
1271 let decoded: SelectorRule = serde_json::from_value(serde_json::json!({
1272 "selector": "0xaabbccdd",
1273 "recipients": [recipient],
1274 }))
1275 .expect("deserialize selector rule with hex selector");
1276
1277 assert_eq!(decoded.selector, [0xaa, 0xbb, 0xcc, 0xdd]);
1278 assert_eq!(decoded.recipients, vec![recipient]);
1279 }
1280
1281 #[cfg(feature = "serde")]
1282 #[test]
1283 fn test_selector_rule_json_accepts_legacy_selector_array() {
1284 let decoded: SelectorRule = serde_json::from_value(serde_json::json!({
1285 "selector": [170, 187, 204, 221],
1286 "recipients": [],
1287 }))
1288 .expect("deserialize selector rule with legacy selector array");
1289
1290 assert_eq!(decoded.selector, [0xaa, 0xbb, 0xcc, 0xdd]);
1291 assert!(decoded.recipients.is_empty());
1292 }
1293
1294 #[cfg(feature = "serde")]
1295 #[test]
1296 fn test_selector_rule_json_serializes_selector_as_hex() {
1297 let value = serde_json::to_value(SelectorRule {
1298 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1299 recipients: Vec::new(),
1300 })
1301 .expect("serialize selector rule");
1302
1303 assert_eq!(value["selector"], serde_json::json!("0xaabbccdd"));
1304 }
1305
1306 #[cfg(feature = "serde")]
1307 #[test]
1308 fn test_key_authorization_json_rejects_zero_expiry() {
1309 let err = serde_json::from_value::<KeyAuthorization>(serde_json::json!({
1310 "chainId": "0x1",
1311 "keyType": "secp256k1",
1312 "keyId": Address::repeat_byte(0x11),
1313 "expiry": "0x0",
1314 }))
1315 .expect_err("zero expiry must be rejected");
1316
1317 assert!(err.to_string().contains("expected non-zero quantity"));
1318 }
1319
1320 #[test]
1321 fn test_key_authorization_decode_accepts_explicit_unrestricted_allowed_calls_field() {
1322 let chain_id = 1u64;
1323 let key_type = SignatureType::Secp256k1;
1324 let key_id = Address::random();
1325
1326 let mut payload = Vec::new();
1327 chain_id.encode(&mut payload);
1328 key_type.encode(&mut payload);
1329 key_id.encode(&mut payload);
1330
1331 let mut encoded = Vec::new();
1332 alloy_rlp::Header {
1333 list: true,
1334 payload_length: payload.len(),
1335 }
1336 .encode(&mut encoded);
1337 encoded.extend_from_slice(&payload);
1338
1339 let decoded =
1340 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
1341 assert_eq!(decoded.chain_id, chain_id);
1342 assert_eq!(decoded.key_type, key_type);
1343 assert_eq!(decoded.key_id, key_id);
1344 assert_eq!(decoded.expiry, None);
1345 assert_eq!(decoded.limits, None);
1346 assert_eq!(decoded.allowed_calls, None);
1347
1348 let mut reencoded = Vec::new();
1349 decoded.encode(&mut reencoded);
1350 assert_eq!(reencoded.len(), encoded.len());
1351 }
1352
1353 #[test]
1354 fn test_key_authorization_decode_accepts_explicit_deny_all_allowed_calls_field() {
1355 let chain_id = 1u64;
1356 let key_type = SignatureType::Secp256k1;
1357 let key_id = Address::random();
1358
1359 let mut payload = Vec::new();
1360 chain_id.encode(&mut payload);
1361 key_type.encode(&mut payload);
1362 key_id.encode(&mut payload);
1363 payload.extend_from_slice(&[
1364 alloy_rlp::EMPTY_STRING_CODE,
1365 alloy_rlp::EMPTY_STRING_CODE,
1366 0xc0,
1367 ]);
1368
1369 let mut encoded = Vec::new();
1370 alloy_rlp::Header {
1371 list: true,
1372 payload_length: payload.len(),
1373 }
1374 .encode(&mut encoded);
1375 encoded.extend_from_slice(&payload);
1376
1377 let decoded =
1378 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
1379 assert_eq!(decoded.chain_id, chain_id);
1380 assert_eq!(decoded.key_type, key_type);
1381 assert_eq!(decoded.key_id, key_id);
1382 assert_eq!(decoded.expiry, None);
1383 assert_eq!(decoded.limits, None);
1384 assert_eq!(decoded.allowed_calls, Some(vec![]));
1385
1386 let mut reencoded = Vec::new();
1387 decoded.encode(&mut reencoded);
1388 assert_eq!(reencoded, encoded);
1389 }
1390
1391 #[test]
1392 fn test_validate_chain_id_pre_t1c() {
1393 let expected = 42431;
1394
1395 assert!(
1397 make_auth_with_chain_id(expected)
1398 .validate_chain_id(expected, false)
1399 .is_ok()
1400 );
1401
1402 assert!(
1404 make_auth_with_chain_id(0)
1405 .validate_chain_id(expected, false)
1406 .is_ok()
1407 );
1408
1409 let err = make_auth_with_chain_id(999)
1411 .validate_chain_id(expected, false)
1412 .unwrap_err();
1413 assert_eq!(err.expected, expected);
1414 assert_eq!(err.got, 999);
1415 }
1416
1417 #[test]
1418 fn test_validate_chain_id_post_t1c() {
1419 let expected = 42431;
1420
1421 assert!(
1423 make_auth_with_chain_id(expected)
1424 .validate_chain_id(expected, true)
1425 .is_ok()
1426 );
1427
1428 let err = make_auth_with_chain_id(0)
1430 .validate_chain_id(expected, true)
1431 .unwrap_err();
1432 assert_eq!(err.expected, expected);
1433 assert_eq!(err.got, 0);
1434
1435 let err = make_auth_with_chain_id(999)
1437 .validate_chain_id(expected, true)
1438 .unwrap_err();
1439 assert_eq!(err.expected, expected);
1440 assert_eq!(err.got, 999);
1441 }
1442
1443 #[test]
1444 fn test_call_scope_accessors() {
1445 let target = Address::repeat_byte(0x11);
1446 let rule = SelectorRule {
1447 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1448 recipients: vec![Address::repeat_byte(0x22)],
1449 };
1450 let scope = CallScope {
1451 target,
1452 selector_rules: vec![rule],
1453 };
1454
1455 assert_eq!(scope.target(), target);
1456 assert!(!scope.allows_all_selectors());
1457 assert_eq!(scope.selector_rules().len(), 1);
1458 }
1459
1460 #[test]
1461 fn test_call_scope_allows_all_selectors_when_empty() {
1462 let scope = CallScope {
1463 target: Address::repeat_byte(0x11),
1464 selector_rules: vec![],
1465 };
1466 assert!(scope.allows_all_selectors());
1467 }
1468
1469 #[test]
1470 fn test_selector_rule_accessors() {
1471 let selector = [0x12, 0x34, 0x56, 0x78];
1472 let recipients = vec![Address::repeat_byte(0x33), Address::repeat_byte(0x44)];
1473 let rule = SelectorRule {
1474 selector,
1475 recipients: recipients.clone(),
1476 };
1477
1478 assert_eq!(rule.selector(), selector);
1479 assert_eq!(rule.recipients(), &recipients);
1480 assert!(!rule.allows_all_recipients());
1481 }
1482
1483 #[test]
1484 fn test_selector_rule_allows_all_recipients_when_empty() {
1485 let rule = SelectorRule {
1486 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1487 recipients: vec![],
1488 };
1489 assert!(rule.allows_all_recipients());
1490 }
1491}
1492
1493#[cfg(all(test, feature = "reth-codec"))]
1494mod compact_tests {
1495 use super::*;
1496 use alloy_primitives::{address, hex};
1497 use reth_codecs::Compact;
1498
1499 #[test]
1504 fn compact_types_have_unused_bits() {
1505 assert_ne!(TokenLimit::bitflag_unused_bits(), 0, "TokenLimit");
1506 }
1507
1508 #[test]
1509 fn token_limit_compact_roundtrip() {
1510 let token_limit = TokenLimit {
1511 token: address!("0x0000000000000000000000000000000000000042"),
1512 limit: U256::from(1_000_000u64),
1513 period: 86400,
1514 };
1515
1516 let expected = hex!("c30000000000000000000000000000000000000000420f4240015180");
1517
1518 let mut buf = vec![];
1519 let len = token_limit.to_compact(&mut buf);
1520 assert_eq!(buf, expected, "TokenLimit compact encoding changed");
1521 assert_eq!(len, expected.len());
1522
1523 let (decoded, _) = TokenLimit::from_compact(&expected, expected.len());
1524 assert_eq!(decoded, token_limit);
1525 }
1526}