primitives/transaction/
authorization.rs

1use cfx_types::{Address, BigEndianHash, H256, U256};
2use cfxkey::{public_to_address, Signature};
3use keccak_hash::keccak;
4use rlp::RlpStream;
5use rlp_derive::{RlpDecodable, RlpEncodable};
6use serde::{Deserialize, Serialize};
7
8pub const AUTH_MAGIC: u8 = 0x05;
9
10pub const CODE_PREFIX_7702: &'static [u8] = b"\xef\x01\x00";
11
12#[derive(
13    Debug,
14    Clone,
15    PartialEq,
16    Eq,
17    Serialize,
18    Deserialize,
19    RlpEncodable,
20    RlpDecodable,
21)]
22#[serde(rename_all = "camelCase")]
23pub struct AuthorizationListItem {
24    pub chain_id: U256,
25    pub address: Address,
26    pub nonce: u64,
27    pub y_parity: u8,
28    pub r: U256,
29    pub s: U256,
30}
31
32pub type AuthorizationList = Vec<AuthorizationListItem>;
33
34impl AuthorizationListItem {
35    pub fn is_valid_y_parity(&self) -> bool {
36        self.y_parity == 0 || self.y_parity == 1
37    }
38
39    pub fn is_chain_id_valid(&self, chain_id: u64) -> bool {
40        self.chain_id == U256::from(chain_id) || self.chain_id.is_zero()
41    }
42
43    pub fn is_nonce_valid(&self) -> bool { self.nonce < u64::MAX }
44
45    pub fn hash(&self) -> H256 {
46        let mut rlp = RlpStream::new_list(3);
47        rlp.append(&self.chain_id)
48            .append(&self.address)
49            .append(&self.nonce);
50
51        let mut hash_input = vec![AUTH_MAGIC];
52        hash_input.extend_from_slice(rlp.as_raw());
53
54        keccak(hash_input)
55    }
56
57    pub fn signature(&self) -> Option<Signature> {
58        let r: H256 = BigEndianHash::from_uint(&self.r);
59        let s: H256 = BigEndianHash::from_uint(&self.s);
60        let signature = Signature::from_rsv(&r, &s, self.y_parity);
61        if !signature.is_low_s() || !signature.is_valid() {
62            return None;
63        }
64        Some(signature)
65    }
66
67    pub fn authority(&self) -> Option<Address> {
68        let signature = self.signature()?;
69        let hash = self.hash();
70        if let Ok(public) = cfxkey::recover(&signature, &hash) {
71            Some(public_to_address(&public, false))
72        } else {
73            None
74        }
75    }
76}
77
78pub fn extract_7702_payload(code: &[u8]) -> Option<Address> {
79    if code.starts_with(CODE_PREFIX_7702) {
80        let (_prefix, payload) = code.split_at(CODE_PREFIX_7702.len());
81        if payload.len() == Address::len_bytes() {
82            Some(Address::from_slice(payload))
83        } else {
84            None
85        }
86    } else {
87        None
88    }
89}