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