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::num::NonZeroU64;
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
16#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
17#[cfg_attr(feature = "reth-codec", derive(reth_codecs::Compact))]
18#[cfg_attr(test, reth_codecs::add_arbitrary_tests(compact, rlp))]
19pub struct TokenLimit {
20 pub token: Address,
22
23 pub limit: U256,
25
26 #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity"))]
30 pub period: u64,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
41#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
42#[cfg_attr(test, reth_codecs::add_arbitrary_tests(rlp))]
43pub struct CallScope {
44 pub target: Address,
46 #[cfg_attr(
48 feature = "serde",
49 serde(default, skip_serializing_if = "Vec::is_empty")
50 )]
51 pub selector_rules: Vec<SelectorRule>,
52}
53
54impl CallScope {
55 pub fn target(&self) -> Address {
57 self.target
58 }
59
60 pub fn allows_all_selectors(&self) -> bool {
62 self.selector_rules.is_empty()
63 }
64
65 pub fn selector_rules(&self) -> &[SelectorRule] {
67 &self.selector_rules
68 }
69
70 fn heap_size(&self) -> usize {
71 self.selector_rules.capacity() * size_of::<SelectorRule>()
72 + self
73 .selector_rules
74 .iter()
75 .map(SelectorRule::heap_size)
76 .sum::<usize>()
77 }
78}
79
80#[derive(Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
87#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
88#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
89#[cfg_attr(test, reth_codecs::add_arbitrary_tests(rlp))]
90pub struct SelectorRule {
91 #[cfg_attr(feature = "serde", serde(with = "selector_hex_serde"))]
93 pub selector: [u8; 4],
94 #[cfg_attr(
96 feature = "serde",
97 serde(default, skip_serializing_if = "Vec::is_empty")
98 )]
99 pub recipients: Vec<Address>,
100}
101
102impl SelectorRule {
103 pub fn selector(&self) -> [u8; 4] {
105 self.selector
106 }
107
108 pub fn recipients(&self) -> &[Address] {
110 &self.recipients
111 }
112
113 pub fn allows_all_recipients(&self) -> bool {
115 self.recipients.is_empty()
116 }
117
118 fn heap_size(&self) -> usize {
119 self.recipients.capacity() * size_of::<Address>()
120 }
121}
122
123use tempo_contracts::precompiles::IAccountKeychain::{
124 CallScope as AbiCallScope, SelectorRule as AbiSelectorRule,
125};
126
127impl From<AbiCallScope> for CallScope {
128 fn from(scope: AbiCallScope) -> Self {
129 Self {
130 target: scope.target,
131 selector_rules: scope.selectorRules.into_iter().map(Into::into).collect(),
132 }
133 }
134}
135
136impl From<CallScope> for AbiCallScope {
137 fn from(scope: CallScope) -> Self {
138 Self {
139 target: scope.target,
140 selectorRules: scope.selector_rules.into_iter().map(Into::into).collect(),
141 }
142 }
143}
144
145impl From<AbiSelectorRule> for SelectorRule {
146 fn from(rule: AbiSelectorRule) -> Self {
147 Self {
148 selector: rule.selector.into(),
149 recipients: rule.recipients,
150 }
151 }
152}
153
154impl From<SelectorRule> for AbiSelectorRule {
155 fn from(rule: SelectorRule) -> Self {
156 Self {
157 selector: rule.selector.into(),
158 recipients: rule.recipients,
159 }
160 }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)]
175#[rlp(trailing(canonical))]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
178#[cfg_attr(test, reth_codecs::add_arbitrary_tests(rlp))]
179pub struct KeyAuthorization {
180 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
183 pub chain_id: u64,
184
185 pub key_type: SignatureType,
187
188 pub key_id: Address,
190
191 #[cfg_attr(feature = "serde", serde(with = "serde_nonzero_quantity_opt"))]
198 pub expiry: Option<NonZeroU64>,
199
200 pub limits: Option<Vec<TokenLimit>>,
205
206 pub allowed_calls: Option<Vec<CallScope>>,
211}
212
213impl KeyAuthorization {
214 pub fn unrestricted(chain_id: u64, key_type: SignatureType, key_id: Address) -> Self {
217 Self {
218 chain_id,
219 key_type,
220 key_id,
221 expiry: None,
222 limits: None,
223 allowed_calls: None,
224 }
225 }
226
227 pub fn with_expiry(mut self, expiry: u64) -> Self {
229 self.expiry = NonZeroU64::new(expiry);
230 self
231 }
232
233 pub fn with_limits(mut self, limits: Vec<TokenLimit>) -> Self {
235 self.limits = Some(limits);
236 self
237 }
238
239 pub fn with_allowed_calls(mut self, allowed_calls: Vec<CallScope>) -> Self {
241 self.allowed_calls = Some(allowed_calls);
242 self
243 }
244
245 pub fn with_no_spending(mut self) -> Self {
247 self.limits = Some(Vec::new());
248 self
249 }
250
251 pub fn with_no_calls(mut self) -> Self {
253 self.allowed_calls = Some(Vec::new());
254 self
255 }
256
257 pub fn signature_hash(&self) -> B256 {
259 let mut buf = Vec::new();
260 self.encode(&mut buf);
261 keccak256(&buf)
262 }
263
264 pub fn has_periodic_limits(&self) -> bool {
266 self.limits
267 .as_ref()
268 .is_some_and(|limits| limits.iter().any(|limit| limit.period != 0))
269 }
270
271 pub fn has_call_scopes(&self) -> bool {
273 self.allowed_calls.is_some()
274 }
275
276 pub fn has_unlimited_spending(&self) -> bool {
278 self.limits.is_none()
279 }
280
281 pub fn never_expires(&self) -> bool {
283 self.expiry.is_none()
284 }
285
286 pub fn is_legacy_compatible(&self) -> bool {
288 !(self.has_periodic_limits() || self.has_call_scopes())
289 }
290
291 pub fn into_signed(self, signature: PrimitiveSignature) -> SignedKeyAuthorization {
293 SignedKeyAuthorization {
294 authorization: self,
295 signature,
296 }
297 }
298
299 pub fn validate_chain_id(
304 &self,
305 expected_chain_id: u64,
306 is_t1c: bool,
307 ) -> Result<(), KeyAuthorizationChainIdError> {
308 if is_t1c {
309 if self.chain_id != expected_chain_id {
310 return Err(KeyAuthorizationChainIdError {
311 expected: expected_chain_id,
312 got: self.chain_id,
313 });
314 }
315 } else if self.chain_id != 0 && self.chain_id != expected_chain_id {
316 return Err(KeyAuthorizationChainIdError {
317 expected: expected_chain_id,
318 got: self.chain_id,
319 });
320 }
321 Ok(())
322 }
323
324 pub fn size(&self) -> usize {
326 size_of::<Self>()
327 + self
328 .limits
329 .as_ref()
330 .map_or(0, |limits| limits.capacity() * size_of::<TokenLimit>())
331 + self.allowed_calls.as_ref().map_or(0, |scopes| {
332 scopes.capacity() * size_of::<CallScope>()
333 + scopes.iter().map(CallScope::heap_size).sum::<usize>()
334 })
335 }
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340pub struct KeyAuthorizationChainIdError {
341 pub expected: u64,
343 pub got: u64,
345}
346
347#[derive(
349 Clone,
350 Debug,
351 PartialEq,
352 Eq,
353 Hash,
354 alloy_rlp::RlpEncodable,
355 alloy_rlp::RlpDecodable,
356 derive_more::Deref,
357)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
360#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
361#[cfg_attr(test, reth_codecs::add_arbitrary_tests(compact, rlp))]
362pub struct SignedKeyAuthorization {
363 #[cfg_attr(feature = "serde", serde(flatten))]
365 #[deref]
366 pub authorization: KeyAuthorization,
367
368 pub signature: PrimitiveSignature,
370}
371
372impl SignedKeyAuthorization {
373 pub fn recover_signer(&self) -> Result<Address, RecoveryError> {
375 self.signature
376 .recover_signer(&self.authorization.signature_hash())
377 }
378
379 pub fn size(&self) -> usize {
381 self.authorization.size() + self.signature.size()
382 }
383}
384
385#[cfg(any(test, feature = "arbitrary"))]
386impl<'a> arbitrary::Arbitrary<'a> for KeyAuthorization {
387 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
388 Ok(Self {
389 chain_id: u.arbitrary()?,
390 key_type: u.arbitrary()?,
391 key_id: u.arbitrary()?,
392 expiry: u.arbitrary()?,
393 limits: u.arbitrary()?,
394 allowed_calls: u.arbitrary()?,
395 })
396 }
397}
398
399#[cfg(feature = "serde")]
400#[doc(hidden)]
401pub mod serde_nonzero_quantity_opt {
402 use core::num::NonZeroU64;
403
404 use serde::{Deserializer, Serializer, de::Error as _};
405
406 pub fn serialize<S>(value: &Option<NonZeroU64>, serializer: S) -> Result<S::Ok, S::Error>
407 where
408 S: Serializer,
409 {
410 alloy_serde::quantity::opt::serialize(&value.map(NonZeroU64::get), serializer)
411 }
412
413 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NonZeroU64>, D::Error>
414 where
415 D: Deserializer<'de>,
416 {
417 alloy_serde::quantity::opt::deserialize(deserializer).and_then(|value: Option<u64>| {
418 value
419 .map(|value| {
420 NonZeroU64::new(value)
421 .ok_or_else(|| D::Error::custom("expected non-zero quantity"))
422 })
423 .transpose()
424 })
425 }
426}
427
428mod rlp {
429 use super::*;
430 use alloy_rlp::{Decodable, Encodable};
431
432 #[derive(
433 Clone, Debug, PartialEq, Eq, Hash, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable,
434 )]
435 #[rlp(trailing(canonical))]
436 struct TokenLimitWire {
437 token: Address,
438 limit: U256,
439 period: Option<NonZeroU64>,
440 }
441
442 impl From<&TokenLimit> for TokenLimitWire {
443 fn from(value: &TokenLimit) -> Self {
444 let TokenLimit {
445 token,
446 limit,
447 period,
448 } = value;
449 Self {
450 token: *token,
451 limit: *limit,
452 period: NonZeroU64::new(*period),
453 }
454 }
455 }
456
457 impl From<TokenLimitWire> for TokenLimit {
458 fn from(value: TokenLimitWire) -> Self {
459 Self {
460 token: value.token,
461 limit: value.limit,
462 period: value.period.map(|period| period.get()).unwrap_or(0),
463 }
464 }
465 }
466
467 impl Decodable for TokenLimit {
468 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
469 Ok(TokenLimitWire::decode(buf)?.into())
470 }
471 }
472
473 impl Encodable for TokenLimit {
474 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
475 TokenLimitWire::from(self).encode(out)
476 }
477
478 fn length(&self) -> usize {
479 TokenLimitWire::from(self).length()
480 }
481 }
482}
483
484#[cfg(feature = "serde")]
485mod selector_hex_serde {
486 use alloy_primitives::FixedBytes;
487 use serde::{Deserialize, Deserializer, Serialize, Serializer};
488
489 #[derive(Deserialize)]
490 #[serde(untagged)]
491 enum SelectorValue {
492 Hex(FixedBytes<4>),
493 Array([u8; 4]),
494 }
495
496 pub(super) fn serialize<S>(selector: &[u8; 4], serializer: S) -> Result<S::Ok, S::Error>
497 where
498 S: Serializer,
499 {
500 FixedBytes::<4>::from(*selector).serialize(serializer)
501 }
502
503 pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 4], D::Error>
504 where
505 D: Deserializer<'de>,
506 {
507 Ok(match SelectorValue::deserialize(deserializer)? {
508 SelectorValue::Hex(selector) => selector.into(),
509 SelectorValue::Array(selector) => selector,
510 })
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use crate::transaction::{
518 TempoSignature,
519 tt_authorization::tests::{generate_secp256k1_keypair, sign_hash},
520 };
521 use alloy_rlp::{Decodable, Encodable};
522
523 fn nonzero(value: u64) -> NonZeroU64 {
524 NonZeroU64::new(value).expect("test expiry must be non-zero")
525 }
526
527 fn make_auth(expiry: Option<u64>, limits: Option<Vec<TokenLimit>>) -> KeyAuthorization {
528 KeyAuthorization {
529 chain_id: 1,
530 key_type: SignatureType::Secp256k1,
531 key_id: Address::random(),
532 expiry: expiry.and_then(NonZeroU64::new),
533 limits,
534 allowed_calls: None,
535 }
536 }
537
538 #[test]
539 fn test_signature_hash_and_recover_signer() {
540 let (signing_key, expected_address) = generate_secp256k1_keypair();
541
542 let auth = make_auth(Some(1000), None);
543
544 let hash1 = auth.signature_hash();
546 let hash2 = auth.signature_hash();
547 assert_eq!(hash1, hash2, "signature_hash should be deterministic");
548 assert_ne!(hash1, B256::ZERO);
549
550 let auth2 = make_auth(Some(2000), None);
552 assert_ne!(auth.signature_hash(), auth2.signature_hash());
553
554 let signature = sign_hash(&signing_key, &auth.signature_hash());
556 let inner_sig = match signature {
557 TempoSignature::Primitive(p) => p,
558 _ => panic!("Expected primitive signature"),
559 };
560 let signed = auth.clone().into_signed(inner_sig);
561
562 let recovered = signed.recover_signer();
564 assert!(recovered.is_ok());
565 assert_eq!(recovered.unwrap(), expected_address);
566
567 let wrong_sig = sign_hash(&signing_key, &B256::random());
569 let wrong_inner = match wrong_sig {
570 TempoSignature::Primitive(p) => p,
571 _ => panic!("Expected primitive signature"),
572 };
573 let bad_signed = auth.into_signed(wrong_inner);
574 let bad_recovered = bad_signed.recover_signer();
575 assert!(bad_recovered.is_ok());
576 assert_ne!(bad_recovered.unwrap(), expected_address);
577 }
578
579 #[test]
580 fn test_spending_expiry_and_size() {
581 assert!(make_auth(None, None).has_unlimited_spending());
583 assert!(!make_auth(None, Some(vec![])).has_unlimited_spending());
584 assert!(
585 !make_auth(
586 None,
587 Some(vec![TokenLimit {
588 token: Address::ZERO,
589 limit: U256::from(100),
590 period: 0,
591 }])
592 )
593 .has_unlimited_spending()
594 );
595
596 assert!(make_auth(None, None).never_expires());
598 assert!(!make_auth(Some(1000), None).never_expires());
599 assert_eq!(NonZeroU64::new(0), None);
600 }
601
602 #[test]
603 fn test_size_does_not_double_count_call_scope_structs() {
604 let recipients = vec![Address::repeat_byte(0x11), Address::repeat_byte(0x22)];
605 let mut rules = Vec::with_capacity(3);
606 rules.push(SelectorRule {
607 selector: [1, 2, 3, 4],
608 recipients,
609 });
610
611 let mut scopes = Vec::with_capacity(2);
612 scopes.push(CallScope {
613 target: Address::repeat_byte(0x33),
614 selector_rules: rules,
615 });
616
617 let auth =
618 KeyAuthorization::unrestricted(1, SignatureType::Secp256k1, Address::repeat_byte(0x44))
619 .with_allowed_calls(scopes);
620
621 let scope_rules = auth.allowed_calls.as_ref().unwrap();
622 let selector_rules = &scope_rules[0].selector_rules;
623 let recipients = &selector_rules[0].recipients;
624
625 let expected = size_of::<KeyAuthorization>()
626 + scope_rules.capacity() * size_of::<CallScope>()
627 + selector_rules.capacity() * size_of::<SelectorRule>()
628 + recipients.capacity() * size_of::<Address>();
629
630 assert_eq!(auth.size(), expected);
631 }
632
633 #[test]
634 fn test_zero_expiry_is_unrepresentable() {
635 assert_eq!(NonZeroU64::new(0), None);
636 assert_eq!(Some(NonZeroU64::get(nonzero(1))), Some(1));
637 }
638
639 fn make_auth_with_chain_id(chain_id: u64) -> KeyAuthorization {
640 KeyAuthorization {
641 chain_id,
642 key_type: SignatureType::Secp256k1,
643 key_id: Address::random(),
644 expiry: None,
645 limits: None,
646 allowed_calls: None,
647 }
648 }
649
650 #[test]
651 fn test_token_limit_legacy_decode_defaults_period_to_zero() {
652 let token = Address::random();
653 let limit = U256::from(42);
654
655 let mut encoded = Vec::new();
657 alloy_rlp::Header {
658 list: true,
659 payload_length: token.length() + limit.length(),
660 }
661 .encode(&mut encoded);
662 token.encode(&mut encoded);
663 limit.encode(&mut encoded);
664
665 let decoded: TokenLimit =
666 Decodable::decode(&mut encoded.as_slice()).expect("decode legacy token limit");
667 assert_eq!(decoded.token, token);
668 assert_eq!(decoded.limit, limit);
669 assert_eq!(decoded.period, 0);
670 }
671
672 #[test]
673 fn test_token_limit_encoding_omits_zero_period() {
674 let token_limit = TokenLimit {
675 token: Address::random(),
676 limit: U256::from(1234),
677 period: 0,
678 };
679
680 let mut encoded = Vec::new();
681 token_limit.encode(&mut encoded);
682
683 let mut payload = &encoded[..];
684 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
685 assert!(header.list);
686 assert_eq!(
687 header.payload_length,
688 token_limit.token.length() + token_limit.limit.length()
689 );
690 }
691
692 #[test]
693 fn test_token_limit_decode_accepts_explicit_zero_period_field() {
694 let token = Address::random();
695 let limit = U256::from(42);
696
697 let mut encoded = Vec::new();
698 alloy_rlp::Header {
699 list: true,
700 payload_length: token.length() + limit.length(),
701 }
702 .encode(&mut encoded);
703 token.encode(&mut encoded);
704 limit.encode(&mut encoded);
705
706 let decoded: TokenLimit =
707 <TokenLimit as Decodable>::decode(&mut encoded.as_slice()).expect("decode token limit");
708 assert_eq!(decoded.token, token);
709 assert_eq!(decoded.limit, limit);
710 assert_eq!(decoded.period, 0);
711 }
712
713 #[test]
714 fn test_key_authorization_roundtrip_preserves_explicit_nested_allow_all_lists() {
715 let auth =
716 KeyAuthorization::unrestricted(1, SignatureType::Secp256k1, Address::repeat_byte(0x11))
717 .with_allowed_calls(vec![
718 CallScope {
719 target: Address::repeat_byte(0x22),
720 selector_rules: vec![],
721 },
722 CallScope {
723 target: Address::repeat_byte(0x33),
724 selector_rules: vec![SelectorRule {
725 selector: [0xaa, 0xbb, 0xcc, 0xdd],
726 recipients: vec![],
727 }],
728 },
729 ]);
730
731 let mut encoded = Vec::new();
732 auth.encode(&mut encoded);
733
734 let decoded =
735 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
736
737 let mut reencoded = Vec::new();
738 decoded.encode(&mut reencoded);
739
740 assert_eq!(reencoded, encoded);
741 }
742
743 #[test]
744 fn test_call_scope_decode_rejects_omitted_selector_rules() {
745 let target = Address::repeat_byte(0x11);
746
747 let mut encoded = Vec::new();
748 alloy_rlp::Header {
749 list: true,
750 payload_length: target.length(),
751 }
752 .encode(&mut encoded);
753 target.encode(&mut encoded);
754
755 <CallScope as Decodable>::decode(&mut encoded.as_slice())
756 .expect_err("omitted selector_rules should be rejected");
757 }
758
759 #[test]
760 fn test_call_scope_explicit_empty_selector_rules_roundtrip() {
761 let scope = CallScope {
762 target: Address::repeat_byte(0x11),
763 selector_rules: Vec::new(),
764 };
765
766 let mut encoded = Vec::new();
767 scope.encode(&mut encoded);
768
769 let mut payload = &encoded[..];
770 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
771 assert!(header.list);
772 assert_eq!(
773 header.payload_length,
774 scope.target.length() + Vec::<SelectorRule>::new().length()
775 );
776
777 let decoded =
778 <CallScope as Decodable>::decode(&mut encoded.as_slice()).expect("decode scope");
779 assert_eq!(decoded, scope);
780 }
781
782 #[test]
783 fn test_call_scope_decode_accepts_explicit_empty_selector_rules_list() {
784 let target = Address::repeat_byte(0x11);
785
786 let mut encoded = Vec::new();
787 alloy_rlp::Header {
788 list: true,
789 payload_length: target.length() + Vec::<SelectorRule>::new().length(),
790 }
791 .encode(&mut encoded);
792 target.encode(&mut encoded);
793 Vec::<SelectorRule>::new().encode(&mut encoded);
794
795 let decoded =
796 <CallScope as Decodable>::decode(&mut encoded.as_slice()).expect("decode scope");
797 assert_eq!(decoded.target, target);
798 assert!(decoded.selector_rules.is_empty());
799
800 let mut reencoded = Vec::new();
801 decoded.encode(&mut reencoded);
802 assert_eq!(reencoded, encoded);
803 }
804
805 #[test]
806 fn test_selector_rule_decode_rejects_omitted_recipients() {
807 let selector = [0xaa, 0xbb, 0xcc, 0xdd];
808
809 let mut encoded = Vec::new();
810 alloy_rlp::Header {
811 list: true,
812 payload_length: selector.length(),
813 }
814 .encode(&mut encoded);
815 selector.encode(&mut encoded);
816
817 <SelectorRule as Decodable>::decode(&mut encoded.as_slice())
818 .expect_err("omitted recipients should be rejected");
819 }
820
821 #[test]
822 fn test_selector_rule_empty_recipients_roundtrip() {
823 let rule = SelectorRule {
824 selector: [0xaa, 0xbb, 0xcc, 0xdd],
825 recipients: Vec::new(),
826 };
827
828 let mut encoded = Vec::new();
829 rule.encode(&mut encoded);
830
831 let mut payload = &encoded[..];
832 let header = alloy_rlp::Header::decode(&mut payload).expect("decode list header");
833 assert!(header.list);
834 assert_eq!(
835 header.payload_length,
836 rule.selector.length() + Vec::<Address>::new().length()
837 );
838
839 let decoded =
840 <SelectorRule as Decodable>::decode(&mut encoded.as_slice()).expect("decode rule");
841 assert_eq!(decoded, rule);
842 }
843
844 #[test]
845 fn test_selector_rule_decode_accepts_explicit_empty_recipient_list() {
846 let selector = [0xaa, 0xbb, 0xcc, 0xdd];
847
848 let mut encoded = Vec::new();
849 alloy_rlp::Header {
850 list: true,
851 payload_length: selector.length() + Vec::<Address>::new().length(),
852 }
853 .encode(&mut encoded);
854 selector.encode(&mut encoded);
855 Vec::<Address>::new().encode(&mut encoded);
856
857 let decoded =
858 <SelectorRule as Decodable>::decode(&mut encoded.as_slice()).expect("decode rule");
859 assert_eq!(decoded.selector, selector);
860 assert!(decoded.recipients.is_empty());
861
862 let mut reencoded = Vec::new();
863 decoded.encode(&mut reencoded);
864 assert_eq!(reencoded, encoded);
865 }
866
867 #[test]
868 fn test_selector_rule_roundtrip_preserves_non_empty_recipient_list() {
869 let rule = SelectorRule {
870 selector: [0xaa, 0xbb, 0xcc, 0xdd],
871 recipients: vec![Address::repeat_byte(0x11), Address::repeat_byte(0x22)],
872 };
873
874 let mut encoded = Vec::new();
875 rule.encode(&mut encoded);
876
877 let decoded =
878 <SelectorRule as Decodable>::decode(&mut encoded.as_slice()).expect("decode rule");
879 assert_eq!(decoded, rule);
880 }
881
882 #[cfg(feature = "serde")]
883 #[test]
884 fn test_token_limit_json_defaults_period_to_zero() {
885 let token = Address::repeat_byte(0x11);
886
887 let decoded: TokenLimit = serde_json::from_value(serde_json::json!({
888 "token": token,
889 "limit": "0x2a",
890 }))
891 .expect("deserialize legacy JSON token limit");
892
893 assert_eq!(decoded.token, token);
894 assert_eq!(decoded.limit, U256::from(42));
895 assert_eq!(decoded.period, 0);
896 }
897
898 #[cfg(feature = "serde")]
899 #[test]
900 fn test_token_limit_json_serializes_period_as_quantity() {
901 let value = serde_json::to_value(TokenLimit {
902 token: Address::repeat_byte(0x11),
903 limit: U256::from(42),
904 period: 7,
905 })
906 .expect("serialize token limit");
907
908 assert_eq!(value["period"], serde_json::json!("0x7"));
909 }
910
911 #[cfg(feature = "serde")]
912 #[test]
913 fn test_selector_rule_json_accepts_hex_selector() {
914 let recipient = Address::repeat_byte(0x11);
915
916 let decoded: SelectorRule = serde_json::from_value(serde_json::json!({
917 "selector": "0xaabbccdd",
918 "recipients": [recipient],
919 }))
920 .expect("deserialize selector rule with hex selector");
921
922 assert_eq!(decoded.selector, [0xaa, 0xbb, 0xcc, 0xdd]);
923 assert_eq!(decoded.recipients, vec![recipient]);
924 }
925
926 #[cfg(feature = "serde")]
927 #[test]
928 fn test_selector_rule_json_accepts_legacy_selector_array() {
929 let decoded: SelectorRule = serde_json::from_value(serde_json::json!({
930 "selector": [170, 187, 204, 221],
931 "recipients": [],
932 }))
933 .expect("deserialize selector rule with legacy selector array");
934
935 assert_eq!(decoded.selector, [0xaa, 0xbb, 0xcc, 0xdd]);
936 assert!(decoded.recipients.is_empty());
937 }
938
939 #[cfg(feature = "serde")]
940 #[test]
941 fn test_selector_rule_json_serializes_selector_as_hex() {
942 let value = serde_json::to_value(SelectorRule {
943 selector: [0xaa, 0xbb, 0xcc, 0xdd],
944 recipients: Vec::new(),
945 })
946 .expect("serialize selector rule");
947
948 assert_eq!(value["selector"], serde_json::json!("0xaabbccdd"));
949 }
950
951 #[cfg(feature = "serde")]
952 #[test]
953 fn test_key_authorization_json_rejects_zero_expiry() {
954 let err = serde_json::from_value::<KeyAuthorization>(serde_json::json!({
955 "chainId": "0x1",
956 "keyType": "secp256k1",
957 "keyId": Address::repeat_byte(0x11),
958 "expiry": "0x0",
959 }))
960 .expect_err("zero expiry must be rejected");
961
962 assert!(err.to_string().contains("expected non-zero quantity"));
963 }
964
965 #[test]
966 fn test_key_authorization_decode_accepts_explicit_unrestricted_allowed_calls_field() {
967 let chain_id = 1u64;
968 let key_type = SignatureType::Secp256k1;
969 let key_id = Address::random();
970
971 let mut payload = Vec::new();
972 chain_id.encode(&mut payload);
973 key_type.encode(&mut payload);
974 key_id.encode(&mut payload);
975
976 let mut encoded = Vec::new();
977 alloy_rlp::Header {
978 list: true,
979 payload_length: payload.len(),
980 }
981 .encode(&mut encoded);
982 encoded.extend_from_slice(&payload);
983
984 let decoded =
985 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
986 assert_eq!(decoded.chain_id, chain_id);
987 assert_eq!(decoded.key_type, key_type);
988 assert_eq!(decoded.key_id, key_id);
989 assert_eq!(decoded.expiry, None);
990 assert_eq!(decoded.limits, None);
991 assert_eq!(decoded.allowed_calls, None);
992
993 let mut reencoded = Vec::new();
994 decoded.encode(&mut reencoded);
995 assert_eq!(reencoded.len(), encoded.len());
996 }
997
998 #[test]
999 fn test_key_authorization_decode_accepts_explicit_deny_all_allowed_calls_field() {
1000 let chain_id = 1u64;
1001 let key_type = SignatureType::Secp256k1;
1002 let key_id = Address::random();
1003
1004 let mut payload = Vec::new();
1005 chain_id.encode(&mut payload);
1006 key_type.encode(&mut payload);
1007 key_id.encode(&mut payload);
1008 payload.extend_from_slice(&[
1009 alloy_rlp::EMPTY_STRING_CODE,
1010 alloy_rlp::EMPTY_STRING_CODE,
1011 0xc0,
1012 ]);
1013
1014 let mut encoded = Vec::new();
1015 alloy_rlp::Header {
1016 list: true,
1017 payload_length: payload.len(),
1018 }
1019 .encode(&mut encoded);
1020 encoded.extend_from_slice(&payload);
1021
1022 let decoded =
1023 <KeyAuthorization as Decodable>::decode(&mut encoded.as_slice()).expect("decode auth");
1024 assert_eq!(decoded.chain_id, chain_id);
1025 assert_eq!(decoded.key_type, key_type);
1026 assert_eq!(decoded.key_id, key_id);
1027 assert_eq!(decoded.expiry, None);
1028 assert_eq!(decoded.limits, None);
1029 assert_eq!(decoded.allowed_calls, Some(vec![]));
1030
1031 let mut reencoded = Vec::new();
1032 decoded.encode(&mut reencoded);
1033 assert_eq!(reencoded, encoded);
1034 }
1035
1036 #[test]
1037 fn test_validate_chain_id_pre_t1c() {
1038 let expected = 42431;
1039
1040 assert!(
1042 make_auth_with_chain_id(expected)
1043 .validate_chain_id(expected, false)
1044 .is_ok()
1045 );
1046
1047 assert!(
1049 make_auth_with_chain_id(0)
1050 .validate_chain_id(expected, false)
1051 .is_ok()
1052 );
1053
1054 let err = make_auth_with_chain_id(999)
1056 .validate_chain_id(expected, false)
1057 .unwrap_err();
1058 assert_eq!(err.expected, expected);
1059 assert_eq!(err.got, 999);
1060 }
1061
1062 #[test]
1063 fn test_validate_chain_id_post_t1c() {
1064 let expected = 42431;
1065
1066 assert!(
1068 make_auth_with_chain_id(expected)
1069 .validate_chain_id(expected, true)
1070 .is_ok()
1071 );
1072
1073 let err = make_auth_with_chain_id(0)
1075 .validate_chain_id(expected, true)
1076 .unwrap_err();
1077 assert_eq!(err.expected, expected);
1078 assert_eq!(err.got, 0);
1079
1080 let err = make_auth_with_chain_id(999)
1082 .validate_chain_id(expected, true)
1083 .unwrap_err();
1084 assert_eq!(err.expected, expected);
1085 assert_eq!(err.got, 999);
1086 }
1087
1088 #[test]
1089 fn test_call_scope_accessors() {
1090 let target = Address::repeat_byte(0x11);
1091 let rule = SelectorRule {
1092 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1093 recipients: vec![Address::repeat_byte(0x22)],
1094 };
1095 let scope = CallScope {
1096 target,
1097 selector_rules: vec![rule],
1098 };
1099
1100 assert_eq!(scope.target(), target);
1101 assert!(!scope.allows_all_selectors());
1102 assert_eq!(scope.selector_rules().len(), 1);
1103 }
1104
1105 #[test]
1106 fn test_call_scope_allows_all_selectors_when_empty() {
1107 let scope = CallScope {
1108 target: Address::repeat_byte(0x11),
1109 selector_rules: vec![],
1110 };
1111 assert!(scope.allows_all_selectors());
1112 }
1113
1114 #[test]
1115 fn test_selector_rule_accessors() {
1116 let selector = [0x12, 0x34, 0x56, 0x78];
1117 let recipients = vec![Address::repeat_byte(0x33), Address::repeat_byte(0x44)];
1118 let rule = SelectorRule {
1119 selector,
1120 recipients: recipients.clone(),
1121 };
1122
1123 assert_eq!(rule.selector(), selector);
1124 assert_eq!(rule.recipients(), &recipients);
1125 assert!(!rule.allows_all_recipients());
1126 }
1127
1128 #[test]
1129 fn test_selector_rule_allows_all_recipients_when_empty() {
1130 let rule = SelectorRule {
1131 selector: [0xaa, 0xbb, 0xcc, 0xdd],
1132 recipients: vec![],
1133 };
1134 assert!(rule.allows_all_recipients());
1135 }
1136}
1137
1138#[cfg(all(test, feature = "reth-codec"))]
1139mod compact_tests {
1140 use super::*;
1141 use alloy_primitives::{address, hex};
1142 use reth_codecs::Compact;
1143
1144 #[test]
1149 fn compact_types_have_unused_bits() {
1150 assert_ne!(TokenLimit::bitflag_unused_bits(), 0, "TokenLimit");
1151 }
1152
1153 #[test]
1154 fn token_limit_compact_roundtrip() {
1155 let token_limit = TokenLimit {
1156 token: address!("0x0000000000000000000000000000000000000042"),
1157 limit: U256::from(1_000_000u64),
1158 period: 86400,
1159 };
1160
1161 let expected = hex!("c30000000000000000000000000000000000000000420f4240015180");
1162
1163 let mut buf = vec![];
1164 let len = token_limit.to_compact(&mut buf);
1165 assert_eq!(buf, expected, "TokenLimit compact encoding changed");
1166 assert_eq!(len, expected.len());
1167
1168 let (decoded, _) = TokenLimit::from_compact(&expected, expected.len());
1169 assert_eq!(decoded, token_limit);
1170 }
1171}