ethereum_transaction/
lib.rs

1// Copyright (c) 2018-2020 jsonrpc-proxy contributors.
2//
3// This file is part of jsonrpc-proxy
4// (see https://github.com/tomusdrw/jsonrpc-proxy).
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <http://www.gnu.org/licenses/>.
18//! A set of primitives to construct ethereum transactions.
19
20use 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/// Hex-serialized shim for `Vec<u8>`.
29#[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    /// Adds chain id into v
203    pub fn add(v: u8, chain_id: u64) -> u64 {
204        v as u64 + 35 + chain_id * 2
205    }
206
207    /// Extracts chain_id from v
208    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}