1use impl_serde::serialize as bytes;
21use rlp::RlpStream;
22use serde::{Deserialize, Serialize};
23use std::borrow::Cow;
24use tiny_keccak::{Keccak, Hasher};
25
26pub use ethereum_types::{Address, U256};
27
28#[derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Clone, Default)]
30pub struct Bytes(#[serde(with = "bytes")] pub Vec<u8>);
31impl From<Vec<u8>> for Bytes {
32 fn from(s: Vec<u8>) -> Self {
33 Bytes(s)
34 }
35}
36
37impl std::ops::Deref for Bytes {
38 type Target = [u8];
39 fn deref(&self) -> &[u8] {
40 &self.0[..]
41 }
42}
43
44#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug, Default)]
45#[serde(rename_all = "camelCase")]
46pub struct Transaction {
47 pub from: Address,
48 pub to: Option<Address>,
49 pub nonce: U256,
50 pub gas: U256,
51 pub gas_price: U256,
52 pub value: U256,
53 pub data: Bytes,
54}
55
56#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
57#[serde(rename_all = "camelCase")]
58pub struct SignTransaction<'a> {
59 pub transaction: Cow<'a, Transaction>,
60 pub chain_id: u64,
61}
62
63impl<'a> SignTransaction<'a> {
64 pub fn owned(tx: Transaction, chain_id: u64) -> Self {
65 Self {
66 transaction: Cow::Owned(tx),
67 chain_id,
68 }
69 }
70
71 pub fn hash(&self) -> [u8; 32] {
72 SignedTransaction {
73 transaction: Cow::Borrowed(&*self.transaction),
74 v: self.chain_id,
75 r: 0.into(),
76 s: 0.into(),
77 }
78 .hash()
79 }
80}
81
82#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
83#[serde(rename_all = "camelCase")]
84pub struct SignedTransaction<'a> {
85 pub transaction: Cow<'a, Transaction>,
86 pub v: u64,
87 pub r: U256,
88 pub s: U256,
89}
90
91impl<'a> rlp::Decodable for SignedTransaction<'a> {
92 fn decode(d: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
93 if d.item_count()? != 9 {
94 return Err(rlp::DecoderError::RlpIncorrectListLen);
95 }
96
97 Ok(SignedTransaction {
98 transaction: Cow::Owned(Transaction {
99 nonce: d.val_at(0).map_err(|e| debug("nonce", e))?,
100 gas_price: d.val_at(1).map_err(|e| debug("gas_price", e))?,
101 gas: d.val_at(2).map_err(|e| debug("gas", e))?,
102 to: {
103 let to = d.at(3).map_err(|e| debug("to", e))?;
104 if to.is_empty() {
105 if to.is_data() {
106 None
107 } else {
108 return Err(rlp::DecoderError::RlpExpectedToBeData);
109 }
110 } else {
111 Some(to.as_val().map_err(|e| debug("to", e))?)
112 }
113 },
114 from: Default::default(),
115 value: d.val_at(4).map_err(|e| debug("value", e))?,
116 data: d.val_at::<Vec<u8>>(5).map_err(|e| debug("data", e))?.into(),
117 }),
118 v: d.val_at(6).map_err(|e| debug("v", e))?,
119 r: d.val_at(7).map_err(|e| debug("r", e))?,
120 s: d.val_at(8).map_err(|e| debug("s", e))?,
121 })
122 }
123}
124
125fn debug(s: &str, err: rlp::DecoderError) -> rlp::DecoderError {
126 log::error!("Error decoding field: {}: {:?}", s, err);
127 err
128}
129
130impl<'a> rlp::Encodable for SignedTransaction<'a> {
131 fn rlp_append(&self, s: &mut RlpStream) {
132 s.begin_list(9);
133 s.append(&self.transaction.nonce);
134 s.append(&self.transaction.gas_price);
135 s.append(&self.transaction.gas);
136 match self.transaction.to.as_ref() {
137 None => s.append(&""),
138 Some(addr) => s.append(addr),
139 };
140 s.append(&self.transaction.value);
141 s.append(&self.transaction.data.0);
142 s.append(&self.v);
143 s.append(&self.r);
144 s.append(&self.s);
145 }
146}
147
148impl<'a> SignedTransaction<'a> {
149 pub fn new(transaction: Cow<'a, Transaction>, chain_id: u64, v: u8, r: [u8; 32], s: [u8; 32]) -> Self {
150 let v = replay_protection::add(v, chain_id);
151 let r = U256::from_big_endian(&r);
152 let s = U256::from_big_endian(&s);
153
154 Self { transaction, v, r, s }
155 }
156
157 pub fn standard_v(&self) -> u8 {
158 match self.v {
159 v if v == 27 => 0,
160 v if v == 28 => 1,
161 v if v >= 35 => ((v - 1) % 2) as u8,
162 _ => 4,
163 }
164 }
165
166 pub fn chain_id(&self) -> Option<u64> {
167 replay_protection::chain_id(self.v)
168 }
169
170 pub fn hash(&self) -> [u8; 32] {
171 self.with_rlp(|s| {
172 let mut output = [0_u8; 32];
173 let mut k = Keccak::v256();
174 k.update(s.as_raw());
175 k.finalize(&mut output);
176 output
177 })
178 }
179
180 pub fn bare_hash(&self) -> [u8; 32] {
181 let chain_id = self.chain_id().unwrap_or_default();
182
183 SignTransaction {
184 transaction: std::borrow::Cow::Borrowed(&self.transaction),
185 chain_id,
186 }
187 .hash()
188 }
189
190 pub fn to_rlp(&self) -> Vec<u8> {
191 self.with_rlp(|s| s.out().to_vec())
192 }
193
194 fn with_rlp<R>(&self, f: impl FnOnce(RlpStream) -> R) -> R {
195 let mut s = RlpStream::new();
196 rlp::Encodable::rlp_append(self, &mut s);
197 f(s)
198 }
199}
200
201mod replay_protection {
202 pub fn add(v: u8, chain_id: u64) -> u64 {
204 v as u64 + 35 + chain_id * 2
205 }
206
207 pub fn chain_id(v: u64) -> Option<u64> {
209 match v {
210 v if v >= 35 => Some((v - 35) / 2),
211 _ => None,
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn transaction_rlp_round_trip() {
222 let transaction = Transaction {
223 from: Default::default(),
224 to: None,
225 nonce: 5.into(),
226 gas_price: 15.into(),
227 gas: 69.into(),
228 data: Default::default(),
229 value: 1_000.into(),
230 };
231 let t = SignedTransaction::new(Cow::Owned(transaction), 105, 0, [1; 32], [1; 32]);
232
233 let encoded = rlp::encode(&t);
234 let decoded: SignedTransaction = rlp::decode(&encoded).unwrap();
235
236 assert_eq!(t, decoded);
237 }
238
239 #[test]
240 fn transaction_rlp_round_trip2() {
241 let transaction = Transaction {
242 from: Default::default(),
243 to: Some(ethereum_types::H160::repeat_byte(5)),
244 nonce: 5.into(),
245 gas_price: 15.into(),
246 gas: 69.into(),
247 data: Default::default(),
248 value: 1_000.into(),
249 };
250 let t = SignedTransaction::new(Cow::Owned(transaction), 105, 0, [1; 32], [1; 32]);
251
252 let encoded = rlp::encode(&t);
253 let decoded: SignedTransaction = rlp::decode(&encoded).unwrap();
254
255 assert_eq!(t, decoded);
256 }
257}