pallet_revive/evm/api/
signature.rs1use super::*;
20use sp_core::{H160, U256};
21use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256};
22
23impl TransactionLegacySigned {
24 fn extract_recovery_id(&self) -> Option<u8> {
27 if let Some(chain_id) = self.transaction_legacy_unsigned.chain_id {
28 let v: u64 = self.v.try_into().ok()?;
30 let chain_id: u64 = chain_id.try_into().ok()?;
31 let r = v.checked_sub(chain_id.checked_mul(2)?)?.checked_sub(35)?;
32 r.try_into().ok()
33 } else {
34 self.v.try_into().ok()
35 }
36 }
37}
38
39impl TransactionUnsigned {
40 pub fn from_signed(tx: TransactionSigned) -> Self {
42 match tx {
43 TransactionSigned::TransactionLegacySigned(signed) =>
44 Self::TransactionLegacyUnsigned(signed.transaction_legacy_unsigned),
45 TransactionSigned::Transaction7702Signed(signed) =>
46 Self::Transaction7702Unsigned(signed.transaction_7702_unsigned),
47 TransactionSigned::Transaction4844Signed(signed) =>
48 Self::Transaction4844Unsigned(signed.transaction_4844_unsigned),
49 TransactionSigned::Transaction1559Signed(signed) =>
50 Self::Transaction1559Unsigned(signed.transaction_1559_unsigned),
51 TransactionSigned::Transaction2930Signed(signed) =>
52 Self::Transaction2930Unsigned(signed.transaction_2930_unsigned),
53 }
54 }
55
56 pub fn with_signature(self, signature: [u8; 65]) -> TransactionSigned {
58 let r = U256::from_big_endian(&signature[..32]);
59 let s = U256::from_big_endian(&signature[32..64]);
60 let recovery_id = signature[64];
61
62 match self {
63 TransactionUnsigned::Transaction7702Unsigned(transaction_7702_unsigned) =>
64 Transaction7702Signed {
65 transaction_7702_unsigned,
66 r,
67 s,
68 v: None,
69 y_parity: U256::from(recovery_id),
70 }
71 .into(),
72 TransactionUnsigned::Transaction2930Unsigned(transaction_2930_unsigned) =>
73 Transaction2930Signed {
74 transaction_2930_unsigned,
75 r,
76 s,
77 v: None,
78 y_parity: U256::from(recovery_id),
79 }
80 .into(),
81 TransactionUnsigned::Transaction1559Unsigned(transaction_1559_unsigned) =>
82 Transaction1559Signed {
83 transaction_1559_unsigned,
84 r,
85 s,
86 v: None,
87 y_parity: U256::from(recovery_id),
88 }
89 .into(),
90
91 TransactionUnsigned::Transaction4844Unsigned(transaction_4844_unsigned) =>
92 Transaction4844Signed {
93 transaction_4844_unsigned,
94 r,
95 s,
96 y_parity: U256::from(recovery_id),
97 }
98 .into(),
99
100 TransactionUnsigned::TransactionLegacyUnsigned(transaction_legacy_unsigned) => {
101 let v = transaction_legacy_unsigned
102 .chain_id
103 .map(|chain_id| {
104 chain_id
105 .saturating_mul(U256::from(2))
106 .saturating_add(U256::from(35u32 + recovery_id as u32))
107 })
108 .unwrap_or_else(|| U256::from(27u32 + recovery_id as u32));
109
110 TransactionLegacySigned { transaction_legacy_unsigned, r, s, v }.into()
111 },
112 }
113 }
114}
115
116impl TransactionSigned {
117 pub fn raw_signature(&self) -> Result<[u8; 65], ()> {
119 use TransactionSigned::*;
120 let (r, s, v) = match self {
121 TransactionLegacySigned(tx) => (tx.r, tx.s, tx.extract_recovery_id().ok_or(())?),
122 Transaction7702Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
123 Transaction4844Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
124 Transaction1559Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
125 Transaction2930Signed(tx) => (tx.r, tx.s, tx.y_parity.try_into().map_err(|_| ())?),
126 };
127 let mut sig = [0u8; 65];
128 r.write_as_big_endian(sig[0..32].as_mut());
129 s.write_as_big_endian(sig[32..64].as_mut());
130 sig[64] = v;
131 Ok(sig)
132 }
133
134 pub fn recover_eth_address(&self) -> Result<H160, ()> {
136 use TransactionSigned::*;
137
138 let mut s = rlp::RlpStream::new();
139 match self {
140 TransactionLegacySigned(tx) => {
141 let tx = &tx.transaction_legacy_unsigned;
142 s.append(tx);
143 },
144 Transaction7702Signed(tx) => {
145 let tx = &tx.transaction_7702_unsigned;
146 s.append(&tx.r#type.value());
147 s.append(tx);
148 },
149 Transaction4844Signed(tx) => {
150 let tx = &tx.transaction_4844_unsigned;
151 s.append(&tx.r#type.value());
152 s.append(tx);
153 },
154 Transaction1559Signed(tx) => {
155 let tx = &tx.transaction_1559_unsigned;
156 s.append(&tx.r#type.value());
157 s.append(tx);
158 },
159 Transaction2930Signed(tx) => {
160 let tx = &tx.transaction_2930_unsigned;
161 s.append(&tx.r#type.value());
162 s.append(tx);
163 },
164 }
165 let bytes = s.out().to_vec();
166 let signature = self.raw_signature()?;
167
168 let hash = keccak_256(&bytes);
169 let mut addr = H160::default();
170 let pk = secp256k1_ecdsa_recover(&signature, &hash).map_err(|_| ())?;
171 addr.assign_from_slice(&keccak_256(&pk[..])[12..]);
172 Ok(addr)
173 }
174}
175
176#[test]
177fn sign_and_recover_work() {
178 use crate::evm::TransactionUnsigned;
179 let txs = [
180 "f86080808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d87808026a07b2e762a17a71a46b422e60890a04512cf0d907ccf6b78b5bd6e6977efdc2bf5a01ea673d50bbe7c2236acb498ceb8346a8607c941f0b8cbcde7cf439aa9369f1f",
182 "01f89b0180808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0c45a61b3d1d00169c649e7326e02857b850efb96e587db4b9aad29afc80d0752a070ae1eb47ab4097dbed2f19172ae286492621b46ac737ee6c32fb18a00c94c9c",
184 "02f89c018080018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a055d72bbc3047d4b9d3e4b8099f187143202407746118204cc2e0cb0c85a68baea04f6ef08a1418c70450f53398d9f0f2d78d9e9d6b8a80cba886b67132c4a744f2",
186 "03f8bf018002018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080e1a0000000000000000000000000000000000000000000000000000000000000000001a0672b8bac466e2cf1be3148c030988d40d582763ecebbc07700dfc93bb070d8a4a07c635887005b11cb58964c04669ac2857fa633aa66f662685dadfd8bcacb0f21",
188 ];
189 let account = Account::from_secret_key(hex_literal::hex!(
190 "a872f6cbd25a0e04a08b1e21098017a9e6194d101d75e13111f71410c59cd57f"
191 ));
192
193 for tx in txs {
194 let raw_tx = alloy_core::hex::decode(tx).unwrap();
195 let tx = TransactionSigned::decode(&raw_tx).unwrap();
196
197 let address = tx.recover_eth_address();
198 assert_eq!(address.unwrap(), account.address());
199
200 let unsigned = TransactionUnsigned::from_signed(tx.clone());
201 let signed = account.sign_transaction(unsigned);
202 assert_eq!(tx, signed);
203 }
204}