use std::convert::From;
use std::fmt::Debug;
use std::mem::transmute;
use std::ptr;
use anyhow::{anyhow,
Error};
use rlp::{Encodable,
Prototype,
Rlp,
RlpStream};
use secp256k1::ecdsa::{RecoverableSignature,
RecoveryId};
use secp256k1::{Message,
Secp256k1,
SecretKey};
use sha3::{Digest,
Keccak256};
use web3::types::{Address,
H256,
H520,
U256,
U64};
use crate::reba_data::{RebaData,
RebaType};
#[derive(Debug, Clone, Copy)]
pub enum GasKind
{
MaxFeePerGas
{
max_fee_per_gas: U256, max_priority_fee_per_gas: U256
},
FixedGasPrice
{
gas_price: U256
}
}
#[derive(Debug)]
pub struct SignatureData
{
version: U256,
r_component: U256,
s_component: U256
}
#[derive(Debug)]
pub struct AccessList
{
address: Address,
storage_keys: Vec<H256>
}
impl Encodable for AccessList
{
fn rlp_append(&self, s: &mut RlpStream)
{
let mut rlp_stream = RlpStream::new_list(2);
rlp_stream.append(&self.address);
rlp_stream.append_list(&self.storage_keys);
let obj_out = rlp_stream.out().freeze();
s.append_raw(&obj_out, 1);
}
}
#[derive(Debug)]
pub struct Transaction
{
chain_id: u64,
nonce: u64,
pub hash: H256,
from: Address,
to: Address,
value: U256,
input: Vec<u8>,
gas_limit: U256,
block_number: Option<u64>,
gas: GasKind,
signature: Option<SignatureData>,
access_list: Vec<AccessList>,
tx_type: TxType
}
impl Transaction
{
pub fn block_number(&self) -> Option<u64>
{
self.block_number
}
pub fn new(hash: H256,
from: Address,
to: Address,
value: U256,
input: Vec<u8>,
gas_limit: U256,
block_number: Option<u64>,
gas: GasKind)
-> Self
{
let tx_type = match gas
{
GasKind::MaxFeePerGas { .. } => TxType::DynamicFee,
GasKind::FixedGasPrice { .. } => TxType::Legacy
};
Self { hash,
from,
to,
value,
input,
gas_limit,
block_number,
gas,
signature: None,
chain_id: 0,
nonce: 0,
access_list: Vec::default(),
tx_type }
}
pub fn chain_id(&self) -> u64
{
self.chain_id
}
pub fn nonce(&self) -> u64
{
self.nonce
}
pub fn tx_from(&self) -> &Address
{
&self.from
}
pub fn tx_to(&self) -> &Address
{
&self.to
}
pub fn value(&self) -> &U256
{
&self.value
}
pub fn input(&self) -> &Vec<u8>
{
&self.input
}
pub fn gas_limit(&self) -> &U256
{
&self.gas_limit
}
pub fn gas(&self) -> &GasKind
{
&self.gas
}
pub fn signature(&self) -> &Option<SignatureData>
{
&self.signature
}
pub fn access_list(&self) -> &Vec<AccessList>
{
&self.access_list
}
pub fn tx_type(&self) -> &TxType
{
&self.tx_type
}
}
pub struct NodeTx
{
pub tx: Transaction,
pub reba_type: RebaType
}
impl NodeTx
{
pub fn try_from_reba_data(data: &mut [u8]) -> Result<NodeTx, Error>
{
let reba_data = RebaData::try_from(data.as_ref())?;
let mut tx = Transaction::try_from_evm_bytes(&data[reba_data.bytes_size()..], false)?;
tx.from = Address::from(reba_data.from());
tx.hash = H256::from(reba_data.hash());
tx.block_number = reba_data.block_number();
Ok(NodeTx { tx,
reba_type: reba_data.reba_type() })
}
}
#[derive(Debug, PartialEq)]
pub enum TxType
{
Legacy,
AccessList,
DynamicFee,
Unknown
}
impl TryFrom<u8> for TxType
{
type Error = Error;
fn try_from(v: u8) -> Result<Self, Self::Error>
{
match v
{
0x80..=0xFF => Ok(TxType::Legacy),
1 => Ok(TxType::AccessList),
2 => Ok(TxType::DynamicFee),
_ => Err(anyhow!(format!("Invalid TxType value: {}", v)))
}
}
}
#[derive(Debug)]
pub enum TransactionErr
{
InvalidRebaType(u8),
NotEnoughData
{
actual: usize,
expected: usize
},
BadCast(&'static str),
InvalidSize
{
field: &'static str,
actual: usize,
expected: usize
},
NotImplemented(Box<dyn Debug>),
InvalidTxType(Error),
InvalidFormat(&'static str)
}
impl From<TransactionErr> for anyhow::Error
{
fn from(e: TransactionErr) -> Self
{
match e
{
TransactionErr::InvalidRebaType(num) => anyhow!("Invalid Reba Type: {}", num),
TransactionErr::NotEnoughData { actual,
expected } =>
{
anyhow!("Not enough data! expected min {}, found {}",
actual,
expected)
}
TransactionErr::BadCast(field) => anyhow!("Failed to cast field {}", field),
TransactionErr::InvalidSize { field,
actual,
expected } =>
{
anyhow!("'{}': Actual: {}, Expected: {}", field, actual, expected)
}
TransactionErr::NotImplemented(t) => anyhow!("Unimplemented type! {:?}", t),
TransactionErr::InvalidTxType(e) => anyhow!("Invalid TxType {}", e),
TransactionErr::InvalidFormat(msg) => anyhow!("Invalid Format! {}", msg)
}
}
}
fn parse_field<const N: usize, const F: &'static str>(rlp: &Rlp,
index: usize,
enforce_exact_length: bool)
-> Result<[u8; N], TransactionErr>
{
let slice = rlp.at(index).unwrap().data().unwrap();
if slice.len() != N && enforce_exact_length
{
return Err(TransactionErr::InvalidSize { field: F, actual: slice.len(), expected: N }.into());
}
let num_elements = std::cmp::min(slice.len(), N);
let mut array = [0; N];
unsafe {
std::ptr::copy_nonoverlapping(slice.as_ptr(),
array.as_mut_ptr().add(N - num_elements),
num_elements);
}
Ok(array)
}
fn parse_field_to_vec<const F: &'static str>(rlp: &Rlp, index: usize, size: usize) -> Result<Vec<u8>, TransactionErr>
{
let slice = rlp.at(index).unwrap().data().unwrap();
let vec_size = slice.len().min(size);
if slice.len() > size
{
return Err(TransactionErr::InvalidSize { field: F, actual: slice.len(), expected: size }.into());
}
let mut vec = Vec::with_capacity(vec_size);
unsafe {
std::ptr::copy_nonoverlapping(slice.as_ptr(), vec.as_mut_ptr(), vec_size);
vec.set_len(vec_size);
}
Ok(vec)
}
fn parse_access_list(rlp: &Rlp) -> Result<Vec<AccessList>, Error>
{
const ACCESS_LIST_INDEX: usize = 8;
let mut access_list_vec: Vec<AccessList> = Vec::default();
let access_list = rlp.at(ACCESS_LIST_INDEX).unwrap();
if let Ok(Prototype::List(..)) = access_list.prototype()
{
for item in access_list.iter()
{
let access_list_item = parse_access_list_item(&item)?;
access_list_vec.push(access_list_item);
}
}
else
{
return Err(TransactionErr::InvalidFormat("Access list is not a list").into());
}
Ok(access_list_vec)
}
fn parse_access_list_item(rlp: &Rlp) -> Result<AccessList, Error>
{
let addr_data: [u8; 20] = rlp.at(0)?.as_raw().try_into().unwrap();
let mut storage_keys = Vec::default();
for k in rlp.at(1)?.iter()
{
let key_data: [u8; 32] = k.as_raw().try_into().unwrap();
storage_keys.push(key_data.into());
}
Ok(AccessList { address: addr_data.into(),
storage_keys })
}
impl Transaction
{
pub fn try_from_evm_bytes(data: &[u8], calculate_sender: bool) -> Result<Transaction, Error>
{
const MIN_LENGTH: usize = 2;
let length = data.len();
if length < MIN_LENGTH
{
return Err(TransactionErr::NotEnoughData { actual: length, expected: MIN_LENGTH }.into());
}
TxType::try_from(data[0])
.and_then(|tx_type| {
let rlp_start = if tx_type == TxType::Legacy { 0 } else { 1 };
let rlp = &Rlp::new(&data[rlp_start..]);
match tx_type {
TxType::Legacy => Transaction::try_from_legacy_rlp(rlp),
TxType::DynamicFee => Transaction::try_from_dynamic_fee_rlp(rlp),
unimplemented_type => {
Err(TransactionErr::NotImplemented(Box::new(unimplemented_type)).into())
}
}
})
.and_then(|mut tx| {
if calculate_sender {
if let Some(sender) = tx.get_sender() {
tx.from = sender;
}
}
Ok(tx)
})
.or_else(|e| Err(e))
}
fn try_from_legacy_rlp(rlp: &Rlp) -> Result<Transaction, Error>
{
const EXPECTED_NUM_ELEMENTS: usize = 9;
match rlp.prototype()?
{
Prototype::List(n) if n != EXPECTED_NUM_ELEMENTS =>
{
return Err(TransactionErr::InvalidSize { field: "RLP", actual: n, expected: EXPECTED_NUM_ELEMENTS }.into())
}
Prototype::List(_) =>
{} _ => return Err(TransactionErr::InvalidFormat("RLP is not a list!").into())
}
if rlp.iter().any(|x| !x.is_data())
{
return Err(TransactionErr::InvalidFormat("RLP Elements are not all data type!").into());
}
let to = parse_field::<20, "TO">(rlp, 3, true).unwrap_or([0u8; 20]);
// Parse remaining fields
let value = parse_field::<32, "VALUE">(rlp, 4, false)?;
const MAX_INPUT_SIZE: usize = 65535;
let input = parse_field_to_vec::<"INPUT">(rlp, 5, MAX_INPUT_SIZE)?;
let nonce = parse_field::<8, "NONCE">(rlp, 0, false)?;
let gas_price = parse_field::<32, "GAS_PRICE">(rlp, 1, false)?;
let gas_limit = parse_field::<32, "GAS_LIMIT">(rlp, 2, false)?;
// Signature info
let v = parse_field::<32, "V">(rlp, 6, false)?;
let r = parse_field::<32, "R">(rlp, 7, false)?;
let s = parse_field::<32, "S">(rlp, 8, false)?;
let gas = GasKind::FixedGasPrice { gas_price: gas_price.into() };
let signature = SignatureData { version: v.as_slice().into(), r_component: r.into(), s_component: s.into() };
let chain_id = if signature.version < U256::from(35)
{
1u64
}
else
{
let intm = (signature.version - U256::from(35)) / U256::from(2);
if intm > U256::from(u64::MAX)
{
return Err(TransactionErr::InvalidSize { field: "V -> Chain ID to big",
actual: usize::MIN,
expected: usize::MAX }.into());
}
intm.as_u64()
};
Ok(Transaction { hash: [0; 32].into(),
nonce: u64::from_be_bytes(nonce),
chain_id,
access_list: Vec::default(),
from: Address::default(),
to: to.into(),
value: value.into(),
input: input.into(),
gas_limit: gas_limit.into(),
block_number: None,
gas,
signature: Some(signature),
tx_type: TxType::Legacy })
}
// See EIP-1559 for more info on dynamic fee transactions
fn try_from_dynamic_fee_rlp(rlp: &Rlp) -> Result<Transaction, Error>
{
const EXPECTED_NUM_ELEMENTS: usize = 12;
match rlp.prototype()?
{
Prototype::List(n) if n != EXPECTED_NUM_ELEMENTS =>
{
return Err(TransactionErr::InvalidSize { field: "RLP", actual: n, expected: EXPECTED_NUM_ELEMENTS }.into())
}
Prototype::List(_) =>
{} // continue parsing
_ => return Err(TransactionErr::InvalidFormat("RLP is not a list!").into())
}
let mut index = 0;
let mut found_invalid = false;
rlp.iter().for_each(|x| {
// The Access List (index 8 ) is not data, but ignore it since we dont' use it
if x.is_data() == false && index != 8
{
found_invalid = true;
}
index += 1;
});
if found_invalid == true
{
return Err(TransactionErr::InvalidFormat("RLP Elements (besides Access List) are not all data type!").into());
}
let chain_id = parse_field::<8, "CHAIN_ID">(rlp, 0, false)?;
let nonce = parse_field::<8, "NONCE">(rlp, 1, false)?;
let max_prio_gas = parse_field::<32, "MAX_PRIORITY_GAS">(rlp, 2, false)?;
let max_fee_gas = parse_field::<32, "MAX_FEE_GAS">(rlp, 3, false)?;
let gas =
GasKind::MaxFeePerGas { max_fee_per_gas: max_fee_gas.into(), max_priority_fee_per_gas: max_prio_gas.into() };
let gas_limit = parse_field::<32, "GAS_LIMIT">(rlp, 4, false)?;
let to = parse_field::<20, "TO">(rlp, 5, true).unwrap_or([0u8; 20]);
let value = parse_field::<32, "VALUE">(rlp, 6, false)?;
let input = parse_field_to_vec::<"INPUT">(rlp, 7, 65535)?;
let access_list_vec = parse_access_list(rlp)?;
let v = parse_field::<32, "V">(rlp, 9, false)?;
let r = parse_field::<32, "R">(rlp, 10, false)?;
let s = parse_field::<32, "S">(rlp, 11, false)?;
let signature = SignatureData { version: v.into(), r_component: r.into(), s_component: s.into() };
Ok(Transaction { hash: [0; 32].into(),
from: Address::default(),
to: to.into(),
value: value.into(),
input: input.into(),
block_number: None,
gas_limit: gas_limit.into(),
gas,
signature: Some(signature),
tx_type: TxType::DynamicFee,
access_list: access_list_vec,
// TODO: Refactor
nonce: u64::from_be_bytes(nonce),
chain_id: u64::from_be_bytes(chain_id) })
}
fn get_reba_bin_config(&self) -> Result<(u8, usize), Error>
{
const CONFIG_SIZE: usize = 1;
const HASH_SIZE: usize = 32;
const FROM_SIZE: usize = 20;
const TO_SIZE: usize = 20;
const VALUE_SIZE: usize = 32;
const GAS_LIMIT_SIZE: usize = 32;
// Add up all the above sizes
const BASE_SIZE: usize = CONFIG_SIZE + HASH_SIZE + FROM_SIZE + TO_SIZE + VALUE_SIZE + GAS_LIMIT_SIZE;
let mut variable_size: usize = 0;
let mut config_byte = 0u8;
// Input (Variuable Length)
match self.input.len()
{
x if x == 0 => (), // Do nothing if there is no input length
x if x > 0 && x <= u8::MAX.into() =>
{
config_byte |= 0x10;
variable_size += self.input.len() + 1;
}
x if x > u8::MAX.into() && x <= u16::MAX.into() =>
{
config_byte |= 0x20;
variable_size += self.input.len() + 2;
}
_ => config_byte |= 0x30 // The length is ommitted since it is too large
};
// Gas Kind
#[allow(unreachable_patterns)] // For unimplemented branch case
match self.gas
{
GasKind::MaxFeePerGas { .. } =>
{
const MAX_FEE_PER_GAS_SIZE: usize = 32;
const MAX_PRIO_FEE_PER_GAS_SIZE: usize = 32;
variable_size += MAX_FEE_PER_GAS_SIZE + MAX_PRIO_FEE_PER_GAS_SIZE;
config_byte |= 0x80;
}
GasKind::FixedGasPrice { .. } =>
{
const GAS_PRICE_SIZE: usize = 32;
variable_size += GAS_PRICE_SIZE;
// An unset bit indicates static gas
}
_ => return Err(TransactionErr::NotImplemented(Box::new(self.gas)).into())
};
// Block Number
if self.block_number.is_some()
{
const BLOCK_NUMBER_SIZE: usize = 8;
variable_size += BLOCK_NUMBER_SIZE;
config_byte |= 0x40;
};
let total_size = BASE_SIZE + variable_size;
Ok((config_byte, total_size))
}
pub fn try_to_reba_bytes(&self, buf: &mut [u8]) -> Result<usize, Error>
{
let (config_byte, size) = self.get_reba_bin_config()?;
let actual_size = buf.len();
if actual_size < size
{
return Err(TransactionErr::NotEnoughData { actual: actual_size, expected: size }.into());
}
const CONFIG_INDEX: usize = 0;
const HASH_INDEX: usize = 1;
const HASH_SIZE: usize = 32;
const FROM_INDEX: usize = 33;
const FROM_SIZE: usize = 20;
const TO_INDEX: usize = 53;
const TO_SIZE: usize = 20;
const VALUE_INDEX: usize = 73;
const VALUE_SIZE: usize = 32;
const GAS_LIMIT_INDEX: usize = 105;
const GAS_LIMIT_SIZE: usize = 32;
buf[CONFIG_INDEX] = config_byte;
buf[HASH_INDEX..HASH_INDEX + HASH_SIZE].clone_from_slice(self.hash.as_bytes());
buf[FROM_INDEX..FROM_INDEX + FROM_SIZE].clone_from_slice(self.from.as_bytes());
buf[TO_INDEX..TO_INDEX + TO_SIZE].clone_from_slice(self.to.as_bytes());
self.value.to_little_endian(&mut buf[VALUE_INDEX..VALUE_INDEX + VALUE_SIZE]);
self.gas_limit.to_little_endian(&mut buf[GAS_LIMIT_INDEX..GAS_LIMIT_INDEX + GAS_LIMIT_SIZE]);
let input_len_bytes: Vec<u8> = match config_byte & 0x30
{
0x00 | 0x30 => vec![0 as u8; 0],
0x10 => Vec::from((self.input.len() as u8).to_le_bytes()),
0x20 => Vec::from((self.input.len() as u16).to_le_bytes()),
_ => return Err(TransactionErr::NotImplemented(Box::new(config_byte & 0x30)).into()) // Branch should be unreachable
};
let mut offset = 0;
const VAR_INDEX_BASE: usize = 137;
if input_len_bytes.len() != 0
{
buf[VAR_INDEX_BASE + offset..VAR_INDEX_BASE + offset + input_len_bytes.len()]
.clone_from_slice(input_len_bytes.as_slice());
offset += input_len_bytes.len();
buf[VAR_INDEX_BASE + offset..VAR_INDEX_BASE + offset + self.input.len()].clone_from_slice(self.input.as_slice());
offset += self.input.len();
}
match self.gas
{
GasKind::FixedGasPrice { gas_price } =>
{
const GAS_PRICE_SIZE: usize = 32;
gas_price.to_little_endian(&mut buf[VAR_INDEX_BASE + offset..VAR_INDEX_BASE + offset + GAS_PRICE_SIZE]);
offset += GAS_PRICE_SIZE;
}
GasKind::MaxFeePerGas { max_fee_per_gas,
max_priority_fee_per_gas } =>
{
const MAX_FEE_GAS_SIZE: usize = 32;
const MAX_PRIO_FEE_GAS_SIZE: usize = 32;
max_fee_per_gas.to_little_endian(&mut buf[VAR_INDEX_BASE + offset..VAR_INDEX_BASE + offset + MAX_FEE_GAS_SIZE]);
offset += MAX_FEE_GAS_SIZE;
max_priority_fee_per_gas.to_little_endian(&mut buf[VAR_INDEX_BASE + offset
..VAR_INDEX_BASE + offset + MAX_PRIO_FEE_GAS_SIZE]);
offset += MAX_PRIO_FEE_GAS_SIZE;
}
}
match self.block_number
{
Some(bn) =>
#[allow(unused_assignments)] // For adding BLOCK_NUM_SIZE to offset
{
const BLOCK_NUM_SIZE: usize = 8;
#[cfg(target_endian = "little")]
let block_bytes: [u8; 8] = unsafe { transmute(bn) };
unsafe {
ptr::copy_nonoverlapping(block_bytes.as_ptr(),
buf[VAR_INDEX_BASE + offset..VAR_INDEX_BASE + offset + BLOCK_NUM_SIZE].as_mut_ptr(),
8);
}
offset += BLOCK_NUM_SIZE;
}
None => ()
};
Ok(size)
}
// This is the RLP used for creating signatures of the tx
fn make_hash_rlp(&self, protected: bool, sig: Option<SignatureData>) -> Option<Vec<u8>>
{
match self.tx_type
{
TxType::DynamicFee =>
{
let stream_size = if sig.is_some() { 12 } else { 9 };
let mut stream = RlpStream::new_list(stream_size);
stream.append(&self.chain_id);
stream.append(&self.nonce);
if let GasKind::MaxFeePerGas { max_fee_per_gas,
max_priority_fee_per_gas } = self.gas
{
stream.append(&max_priority_fee_per_gas);
stream.append(&max_fee_per_gas);
}
else
{
return None;
}
stream.append(&self.gas_limit);
stream.append(&self.to);
stream.append(&self.value);
stream.append(&self.input);
let mut access_list_stream = RlpStream::new_list(self.access_list.len());
self.access_list.iter().for_each(|i| {
access_list_stream.append(i);
});
let access_list_rlp_bytes = access_list_stream.out().freeze();
stream.append_raw(&access_list_rlp_bytes, 1);
if let Some(s) = sig
{
// Since the chain ID is encoded, we don't need it in V
stream.append(&s.version);
stream.append(&s.r_component);
stream.append(&s.s_component);
}
Some(stream.out().freeze().into())
}
TxType::Legacy =>
{
let size = match protected
{
true => 9,
false => 6
};
let mut stream = RlpStream::new_list(size);
stream.append(&self.nonce);
if let GasKind::FixedGasPrice { gas_price } = self.gas
{
stream.append(&gas_price);
}
else
{
return None;
}
stream.append(&self.gas_limit);
stream.append(&self.to);
stream.append(&self.value);
stream.append(&self.input);
if protected == true
{
stream.append(&self.chain_id);
stream.append(&U64::from(0));
stream.append(&U64::from(0));
}
Some(stream.out().freeze().into())
}
_ => None
}
}
fn get_sender(&self) -> Option<Address>
{
if let Some(s) = &self.signature
{
match self.tx_type
{
TxType::DynamicFee =>
{
// Add 27 to V
// let v_add = U256::from(27);
// let v = s.v + v_add;
// TODO: Verify Chain ID here? (Not sure why this is needed)
// The prefixed hash should not include V, R, S, so remove them from the RLP data.
let hash_data = match self.make_hash_rlp(false, None)
{
Some(d) => d,
None => return None
};
let hash = prefixed_hash(&[2], &hash_data);
recover_plain(&hash, &s.r_component, &s.s_component, &s.version)
}
TxType::Legacy =>
{
if s.version > U256::from(u64::MAX)
{
return None;
}
let u64_v: u64 = s.version.as_u64();
match u64_v
{
27 | 28 | 1 | 0 =>
// Transaction is not replay-protected
{
let hash_data = match self.make_hash_rlp(false, None)
{
Some(d) => d,
None => return None
};
let hash = rlp_hash(&hash_data);
recover_plain(&hash, &s.r_component, &s.s_component, &s.version)
}
_ =>
// Transaction is replay protected
{
// To compute the parity bit, subtract ( 2(ChainId) + 35 )
let new_v = u64_v - ((2 * self.chain_id) + 35);
let hash_data = match self.make_hash_rlp(true, None)
{
Some(d) => d,
None => return None
};
let hash = rlp_hash(&hash_data);
recover_plain(&hash, &s.r_component, &s.s_component, &U256::from(new_v))
}
}
}
_ => None // Not supported (Access List Type)
}
}
else
{
None
}
}
// Returns the signature as a vector of bytes
pub fn sign(&self, key: &SecretKey) -> Option<Vec<u8>>
{
match self.tx_type
{
TxType::DynamicFee =>
{
// Get the RLP of the transaction without the signature
let rlp_encoded = match self.make_hash_rlp(false, None)
{
None => return None,
Some(out) => out
};
// Sign the encoded RLP
let tx_to_sign = prefixed_hash(&[0x02u8], &rlp_encoded);
let sig = match sign_msg(key, tx_to_sign.as_bytes(), None)
{
Some(s) => s,
None => return None
};
// Get the RLP of the transaction with the signature
if let Some(mut tx_bytes) = self.make_hash_rlp(false, Some(sig))
{
let mut full_tx_bytes: Vec<u8> = vec![0x02];
full_tx_bytes.append(&mut tx_bytes);
Some(full_tx_bytes)
}
else
{
None
}
}
_ => None
}
}
}
fn sign_msg(key: &SecretKey, data: &[u8], chain_id: Option<u64>) -> Option<SignatureData>
{
let msg = match Message::from_slice(data)
{
Ok(m) => m,
Err(..) => return None
};
let signer = Secp256k1::new();
let (recovery_id, signature) = signer.sign_ecdsa_recoverable(&msg, key).serialize_compact();
let standard_v = recovery_id.to_i32() as u64;
let v = if let Some(chain_id) = chain_id { standard_v + 36 + (2 * chain_id) } else { standard_v };
let r = U256::from_big_endian(&signature[..32]);
let s = U256::from_big_endian(&signature[32..]);
Some(SignatureData { version: U256::from(v), r_component: r, s_component: s })
}
// Takes the RLP Config Data type and the RLP data and generates the Keccak256 Hash
fn prefixed_hash(config: &[u8; 1], rlp_data: &[u8]) -> H256
{
let mut hasher = Keccak256::default();
hasher.update(&config);
hasher.update(rlp_data);
let out: [u8; 32] = hasher.finalize().into();
out.into()
}
fn rlp_hash(rlp_data: &[u8]) -> H256
{
let mut hasher = Keccak256::default();
hasher.update(rlp_data);
let out: [u8; 32] = hasher.finalize().into();
out.into()
}
// Returns the Address of the sender from the tx signer message hash
// and the r s v values of the actual message signature
fn recover_plain(hash: &H256, r: &U256, s: &U256, v: &U256) -> Option<Address>
{
let v_byte = v.as_u32() as u8;
// TODO: Validate the signature values. For now, we assume the data is valid
// NOTE: We need to know if it is a homestead transaction in order to validate signatures
let mut signature: H520 = H520([0u8; 65]); // H520
// 0:32 is R. These should be 0 filled in the beginning
r.to_big_endian(&mut signature[0..32]);
// 32:64 is S. These should be 0 filled in the beginning
s.to_big_endian(&mut signature[32..64]);
signature[64..65].copy_from_slice(vec![v_byte].as_slice());
// Recover the public key
let public_key = match recover_pub_key(hash, &signature)
{
Some(k) => k,
None => return None
};
if public_key[0] != 4
{
return None;
}
// Compute the Keccak256 hash of Bytes 1..65
let mut hasher = Keccak256::default();
hasher.update(&public_key[1..]);
let out: [u8; 32] = hasher.finalize().into();
// Bytes 12-32 are the Address
let mut addr = [0u8; 20];
addr.copy_from_slice(&out[12..]);
Some(addr.into())
}
// Wrapper for secp256k1
fn recover_pub_key(hash: &H256, sig: &H520) -> Option<H520>
{
let msg = match Message::from_slice(hash.as_bytes())
{
Ok(m) => m,
Err(..) => return None
};
let recovery_id = match RecoveryId::from_i32(sig[64] as i32)
{
Ok(id) => id,
Err(..) => return None
};
let signature = match RecoverableSignature::from_compact(&sig[0..64], recovery_id)
{
Ok(s) => s,
Err(..) => return None
};
let signer = Secp256k1::verification_only();
match signer.recover_ecdsa(&msg, &signature)
{
Ok(pub_key) => Some(pub_key.serialize_uncompressed().into()),
Err(..) => return None
}
}
#[cfg(test)]
mod tests
{
use std::assert_matches::assert_matches;
use super::*;
#[test]
fn test_decode_dynamic_tx()
{
let dynamic_tx: Vec<u8> =
vec![0x02, 0xF8, 0x79, 0x82, 0xA8, 0x68, 0x03, 0x85, 0x17, 0x48, 0x76, 0xE8, 0x00, 0x86, 0x09, 0x18, 0x4E, 0x72, 0xA0,
0x00, 0x83, 0x1E, 0x84, 0x80, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x3B, 0x9A, 0xCA, 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05, 0xC0,
0x01, 0xA0, 0x9B, 0x25, 0x9B, 0xAB, 0xF4, 0x4B, 0xE4, 0x4E, 0x38, 0xB0, 0x3C, 0x91, 0x5D, 0xD5, 0x3A, 0xB7, 0xFA,
0x4F, 0xA3, 0x6D, 0x8F, 0x0F, 0x3D, 0x39, 0xEA, 0x79, 0xEA, 0x80, 0x83, 0xA6, 0x83, 0x00, 0xA0, 0x60, 0xCD, 0x92,
0x7B, 0xF9, 0xA8, 0xC9, 0x66, 0x17, 0xCB, 0x9D, 0x60, 0xA0, 0xD1, 0x25, 0x58, 0xCA, 0xB2, 0xDD, 0xF4, 0xFB, 0x71,
0xB5, 0xC4, 0x73, 0x6B, 0x73, 0x3F, 0xA1, 0x49, 0xDF, 0x58,];
let tx_res = Transaction::try_from_evm_bytes(dynamic_tx.as_slice(), false);
assert_matches!(tx_res, Ok(..));
if let Ok(tx) = tx_res
{
assert_eq!(tx.chain_id, 43112);
assert_eq!(tx.nonce, 3);
assert_eq!(tx.to, Address::from_slice(vec![0x00; 20].as_slice()));
assert_eq!(tx.value, U256::from_dec_str("1000000000").unwrap());
assert_matches!(tx.gas, GasKind::MaxFeePerGas { .. });
if let GasKind::MaxFeePerGas { max_fee_per_gas,
max_priority_fee_per_gas } = tx.gas
{
assert_eq!(max_fee_per_gas,
U256::from_dec_str("10000000000000").unwrap());
assert_eq!(max_priority_fee_per_gas,
U256::from_dec_str("100000000000").unwrap());
}
assert_eq!(tx.gas_limit, U256::from_dec_str("2000000").unwrap());
assert_eq!(tx.input, vec![0x01, 0x02, 0x03, 0x04, 0x05]);
}
}
#[test]
fn test_decode_legacy_tx()
{
let legacy_tx: Vec<u8> = vec![0xF8, 0x71, 0x03, 0x85, 0x74, 0x6A, 0x52, 0x88, 0x00, 0x83, 0x1E, 0x84, 0x80, 0x94, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x84, 0x3B, 0x9A, 0xCA, 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05,
0x83, 0x01, 0x50, 0xF4, 0xA0, 0x2B, 0x92, 0x3A, 0xE4, 0xA2, 0xE5, 0x0B, 0x83, 0x22, 0xE5,
0x8E, 0x55, 0x5D, 0x04, 0xFB, 0x0F, 0x42, 0xDC, 0xBA, 0x82, 0x4F, 0x12, 0xE7, 0x51, 0xBC,
0x5B, 0xE9, 0xE8, 0x0C, 0x09, 0x98, 0x49, 0xA0, 0x32, 0x34, 0xB5, 0xBD, 0x02, 0x3C, 0x3B,
0xB0, 0xA5, 0xAA, 0x72, 0xB0, 0x1E, 0xA7, 0x3A, 0x9C, 0xC5, 0x70, 0x20, 0x05, 0xFA, 0xC6,
0xF4, 0xC0, 0xED, 0x32, 0x1C, 0x68, 0x58, 0x20, 0x8D, 0xEA,];
let tx_res = Transaction::try_from_evm_bytes(legacy_tx.as_slice(), false);
assert_matches!(tx_res, Ok(..));
if let Ok(tx) = tx_res
{
assert_eq!(tx.chain_id, 43112);
assert_eq!(tx.nonce, 3);
assert_eq!(tx.to, Address::from_slice(vec![0x00; 20].as_slice()));
assert_eq!(tx.value, U256::from_dec_str("1000000000").unwrap());
assert_matches!(tx.gas, GasKind::FixedGasPrice { .. });
if let GasKind::FixedGasPrice { gas_price } = tx.gas
{
assert_eq!(gas_price, U256::from_dec_str("500000000000").unwrap());
}
assert_eq!(tx.gas_limit, U256::from_dec_str("2000000").unwrap());
assert_eq!(tx.input, vec![0x01, 0x02, 0x03, 0x04, 0x05]);
}
}
#[test]
fn test_decode_signature_dynamic_tx()
{
let dynamic_tx: Vec<u8> =
vec![0x02, 0xF8, 0x79, 0x82, 0xA8, 0x68, 0x03, 0x85, 0x17, 0x48, 0x76, 0xE8, 0x00, 0x86, 0x09, 0x18, 0x4E, 0x72, 0xA0,
0x00, 0x83, 0x1E, 0x84, 0x80, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x3B, 0x9A, 0xCA, 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05, 0xC0,
0x01, 0xA0, 0x9B, 0x25, 0x9B, 0xAB, 0xF4, 0x4B, 0xE4, 0x4E, 0x38, 0xB0, 0x3C, 0x91, 0x5D, 0xD5, 0x3A, 0xB7, 0xFA,
0x4F, 0xA3, 0x6D, 0x8F, 0x0F, 0x3D, 0x39, 0xEA, 0x79, 0xEA, 0x80, 0x83, 0xA6, 0x83, 0x00, 0xA0, 0x60, 0xCD, 0x92,
0x7B, 0xF9, 0xA8, 0xC9, 0x66, 0x17, 0xCB, 0x9D, 0x60, 0xA0, 0xD1, 0x25, 0x58, 0xCA, 0xB2, 0xDD, 0xF4, 0xFB, 0x71,
0xB5, 0xC4, 0x73, 0x6B, 0x73, 0x3F, 0xA1, 0x49, 0xDF, 0x58,];
let tx_res = Transaction::try_from_evm_bytes(dynamic_tx.as_slice(), true);
assert_matches!(tx_res, Ok(..));
if let Ok(tx) = tx_res
{
let expected_addr: Vec<u8> = vec![0x8D, 0xB9, 0x7C, 0x7C, 0xEC, 0xE2, 0x49, 0xC2, 0xB9, 0x8B, 0xDC, 0x02, 0x26, 0xCC,
0x4C, 0x2A, 0x57, 0xBF, 0x52, 0xFC,];
assert_eq!(tx.from, Address::from_slice(expected_addr.as_slice()));
}
}
#[test]
fn test_decode_signature_legacy_tx()
{
let legacy_tx: Vec<u8> = vec![0xF8, 0x71, 0x03, 0x85, 0x74, 0x6A, 0x52, 0x88, 0x00, 0x83, 0x1E, 0x84, 0x80, 0x94, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x84, 0x3B, 0x9A, 0xCA, 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05,
0x83, 0x01, 0x50, 0xF4, 0xA0, 0x2B, 0x92, 0x3A, 0xE4, 0xA2, 0xE5, 0x0B, 0x83, 0x22, 0xE5,
0x8E, 0x55, 0x5D, 0x04, 0xFB, 0x0F, 0x42, 0xDC, 0xBA, 0x82, 0x4F, 0x12, 0xE7, 0x51, 0xBC,
0x5B, 0xE9, 0xE8, 0x0C, 0x09, 0x98, 0x49, 0xA0, 0x32, 0x34, 0xB5, 0xBD, 0x02, 0x3C, 0x3B,
0xB0, 0xA5, 0xAA, 0x72, 0xB0, 0x1E, 0xA7, 0x3A, 0x9C, 0xC5, 0x70, 0x20, 0x05, 0xFA, 0xC6,
0xF4, 0xC0, 0xED, 0x32, 0x1C, 0x68, 0x58, 0x20, 0x8D, 0xEA,];
let tx_res = Transaction::try_from_evm_bytes(legacy_tx.as_slice(), true);
assert_matches!(tx_res, Ok(..));
if let Ok(tx) = tx_res
{
let expected_addr: Vec<u8> = vec![0x8D, 0xB9, 0x7C, 0x7C, 0xEC, 0xE2, 0x49, 0xC2, 0xB9, 0x8B, 0xDC, 0x02, 0x26, 0xCC,
0x4C, 0x2A, 0x57, 0xBF, 0x52, 0xFC,];
assert_eq!(tx.from, Address::from_slice(expected_addr.as_slice()));
}
}
#[test]
fn test_sign_dynamic_tx()
{
let from_addr: Vec<u8> = vec![0x8D, 0xB9, 0x7C, 0x7C, 0xEC, 0xE2, 0x49, 0xC2, 0xB9, 0x8B, 0xDC, 0x02, 0x26, 0xCC, 0x4C,
0x2A, 0x57, 0xBF, 0x52, 0xFC,];
let tx = Transaction { chain_id: 43112,
nonce: 3,
hash: [0; 32].into(),
from: Address::from_slice(from_addr.as_slice()),
to: [0; 20].into(),
value: U256::from_dec_str("1000000000").unwrap(),
input: vec![0x01, 0x02, 0x03, 0x04, 0x05],
gas_limit: U256::from_dec_str("2000000").unwrap(),
block_number: None,
gas: GasKind::MaxFeePerGas { max_fee_per_gas:
U256::from_dec_str("10000000000000").unwrap(),
max_priority_fee_per_gas:
U256::from_dec_str("100000000000").unwrap() },
signature: None,
access_list: Vec::default(),
tx_type: TxType::DynamicFee };
let key = SecretKey::from_slice(vec![0x56, 0x28, 0x9E, 0x99, 0xC9, 0x4B, 0x69, 0x12, 0xBF, 0xC1, 0x2A, 0xDC, 0x09, 0x3C,
0x9B, 0x51, 0x12, 0x4F, 0x0D, 0xC5, 0x4A, 0xC7, 0xA7, 0x66, 0xB2, 0xBC, 0x5C, 0xCF,
0x55, 0x8D, 0x80, 0x27,].as_slice()).unwrap();
let signed_tx = tx.sign(&key).unwrap();
let expected_signed_tx: Vec<u8> =
vec![0x02, 0xF8, 0x79, 0x82, 0xA8, 0x68, 0x03, 0x85, 0x17, 0x48, 0x76, 0xE8, 0x00, 0x86, 0x09, 0x18, 0x4E, 0x72, 0xA0,
0x00, 0x83, 0x1E, 0x84, 0x80, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x3B, 0x9A, 0xCA, 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05, 0xC0,
0x01, 0xA0, 0x9B, 0x25, 0x9B, 0xAB, 0xF4, 0x4B, 0xE4, 0x4E, 0x38, 0xB0, 0x3C, 0x91, 0x5D, 0xD5, 0x3A, 0xB7, 0xFA,
0x4F, 0xA3, 0x6D, 0x8F, 0x0F, 0x3D, 0x39, 0xEA, 0x79, 0xEA, 0x80, 0x83, 0xA6, 0x83, 0x00, 0xA0, 0x60, 0xCD, 0x92,
0x7B, 0xF9, 0xA8, 0xC9, 0x66, 0x17, 0xCB, 0x9D, 0x60, 0xA0, 0xD1, 0x25, 0x58, 0xCA, 0xB2, 0xDD, 0xF4, 0xFB, 0x71,
0xB5, 0xC4, 0x73, 0x6B, 0x73, 0x3F, 0xA1, 0x49, 0xDF, 0x58,];
assert_eq!(signed_tx, expected_signed_tx);
}
#[test]
fn test_encode_decode_dynamic_tx()
{
let from_addr: Vec<u8> = vec![0x8D, 0xB9, 0x7C, 0x7C, 0xEC, 0xE2, 0x49, 0xC2, 0xB9, 0x8B, 0xDC, 0x02, 0x26, 0xCC, 0x4C,
0x2A, 0x57, 0xBF, 0x52, 0xFC,];
let tx = Transaction { chain_id: 43112,
nonce: 3,
hash: [0; 32].into(),
from: Address::from_slice(from_addr.as_slice()),
to: [0; 20].into(),
value: U256::from_dec_str("1000000000").unwrap(),
input: vec![0x01, 0x02, 0x03, 0x04, 0x05],
gas_limit: U256::from_dec_str("2000000").unwrap(),
block_number: None,
gas: GasKind::MaxFeePerGas { max_fee_per_gas:
U256::from_dec_str("10000000000000").unwrap(),
max_priority_fee_per_gas:
U256::from_dec_str("100000000000").unwrap() },
signature: None,
access_list: Vec::default(),
tx_type: TxType::DynamicFee };
let key = SecretKey::from_slice(vec![0x56, 0x28, 0x9E, 0x99, 0xC9, 0x4B, 0x69, 0x12, 0xBF, 0xC1, 0x2A, 0xDC, 0x09, 0x3C,
0x9B, 0x51, 0x12, 0x4F, 0x0D, 0xC5, 0x4A, 0xC7, 0xA7, 0x66, 0xB2, 0xBC, 0x5C, 0xCF,
0x55, 0x8D, 0x80, 0x27,].as_slice()).unwrap();
let signed_tx = tx.sign(&key).unwrap();
let new_tx = Transaction::try_from_evm_bytes(&signed_tx, false).unwrap();
assert_eq!(new_tx.chain_id, 43112);
assert_eq!(new_tx.nonce, 3);
assert_eq!(new_tx.to, Address::from_slice(vec![0x00; 20].as_slice()));
assert_eq!(new_tx.value, U256::from_dec_str("1000000000").unwrap());
assert_matches!(new_tx.gas, GasKind::MaxFeePerGas { .. });
if let GasKind::MaxFeePerGas { max_fee_per_gas,
max_priority_fee_per_gas } = new_tx.gas
{
assert_eq!(max_fee_per_gas,
U256::from_dec_str("10000000000000").unwrap());
assert_eq!(max_priority_fee_per_gas,
U256::from_dec_str("100000000000").unwrap());
}
assert_eq!(new_tx.gas_limit, U256::from_dec_str("2000000").unwrap());
assert_eq!(new_tx.input, vec![0x01, 0x02, 0x03, 0x04, 0x05]);
}
#[test]
fn test_reba_bytes_dynamic_tx()
{
let from_addr: Vec<u8> = vec![0x8D, 0xB9, 0x7C, 0x7C, 0xEC, 0xE2, 0x49, 0xC2, 0xB9, 0x8B, 0xDC, 0x02, 0x26, 0xCC, 0x4C,
0x2A, 0x57, 0xBF, 0x52, 0xFC,];
let tx = Transaction { chain_id: 43112,
nonce: 3,
hash: [0; 32].into(),
from: Address::from_slice(from_addr.as_slice()),
to: [0; 20].into(),
value: U256::from_dec_str("1000000000").unwrap(),
input: vec![0x01, 0x02, 0x03, 0x04, 0x05],
gas_limit: U256::from_dec_str("2000000").unwrap(),
block_number: None,
gas: GasKind::MaxFeePerGas { max_fee_per_gas:
U256::from_dec_str("10000000000000").unwrap(),
max_priority_fee_per_gas:
U256::from_dec_str("100000000000").unwrap() },
signature: None,
access_list: Vec::default(),
tx_type: TxType::DynamicFee };
let mut buf = [0u8; 1024];
tx.try_to_reba_bytes(&mut buf).unwrap();
}
// AccessList
#[test]
fn test_access_list_rlp_encoding()
{
let address = Address::from_low_u64_be(0x1234567890ABCDEF);
let storage_keys = vec![H256::from_low_u64_be(0x1234567890ABCDEF), H256::from_low_u64_be(0xFEDCBA0987654321)];
let access_list = AccessList { address,
storage_keys };
let mut expected_rlp_stream = RlpStream::new_list(2);
expected_rlp_stream.append(&access_list.address);
expected_rlp_stream.append_list(&access_list.storage_keys);
let expected_output = expected_rlp_stream.out();
let mut rlp_stream = RlpStream::new();
access_list.rlp_append(&mut rlp_stream);
let output = rlp_stream.out();
assert_eq!(output, expected_output);
}
#[test]
fn test_try_from_legacy_rlp_errs()
{
// Create some RLP objects to test with
let empty_rlp = Rlp::new(&[]);
let short_rlp = Rlp::new(&[0x01]);
let long_rlp = Rlp::new(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]);
let invalid_rlp = Rlp::new(&[0xFF, 0xFF, 0xFF]);
let mut list_rlp = RlpStream::new_list(0);
list_rlp.append_raw(&[0x01, 0x02, 0x03, 0x04], 1);
list_rlp.append_raw(&[0x05, 0x06, 0x07, 0x08], 1);
let out = list_rlp.out();
let list_rlp = Rlp::new(&out);
let mut nested_rlp = RlpStream::new_list(2);
nested_rlp.append_raw(&empty_rlp.as_raw(), 1);
nested_rlp.append_raw(&list_rlp.as_raw(), 1);
let nested_out = nested_rlp.out();
let nested_rlp = Rlp::new(&nested_out);
// Call try_from_legacy_rlp and check that it returns an error for each of the RLP objects
assert!(Transaction::try_from_legacy_rlp(&empty_rlp).is_err());
assert!(Transaction::try_from_legacy_rlp(&short_rlp).is_err());
assert!(Transaction::try_from_legacy_rlp(&long_rlp).is_err());
assert!(Transaction::try_from_legacy_rlp(&invalid_rlp).is_err());
assert!(Transaction::try_from_legacy_rlp(&list_rlp).is_err());
assert!(Transaction::try_from_legacy_rlp(&nested_rlp).is_err());
}
#[test]
fn test_tx_with_funky_r_value()
{
let mut tx_data: [u8; 744] =
[0, 119, 234, 113, 253, 201, 194, 214, 227, 69, 212, 29, 135, 191, 238, 90, 115, 208, 44, 251, 172, 172, 34, 104, 116,
142, 199, 52, 109, 181, 0, 122, 223, 11, 211, 205, 15, 175, 219, 127, 185, 98, 191, 146, 116, 63, 142, 182, 17, 34,
137, 137, 228, 249, 2, 176, 131, 3, 47, 12, 133, 6, 252, 35, 172, 0, 131, 7, 161, 32, 148, 227, 19, 10, 215, 124, 80,
140, 163, 166, 130, 245, 157, 194, 28, 167, 186, 230, 11, 10, 138, 128, 185, 2, 68, 201, 128, 117, 57, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 161, 216, 215, 111, 55, 157, 25, 5, 151, 228, 204, 65, 179, 178, 246,
0, 1, 61, 123, 1, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 253, 250, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 3, 253, 250, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 3, 253, 250, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 237, 96, 118, 120, 167, 39, 8, 138, 113, 122, 163, 191, 86, 197, 141, 79, 137, 232, 10, 115, 134, 211, 129,
176, 142, 49, 239, 82, 134, 8, 185, 119, 241, 231, 122, 22, 171, 85, 48, 11, 140, 180, 137, 207, 122, 173, 206, 126,
107, 243, 190, 151, 236, 155, 34, 68, 83, 87, 98, 91, 55, 193, 142, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 47, 41, 161, 40, 46, 13, 229, 17, 2, 83, 206, 177, 191, 75, 106,
121, 222, 70, 220, 93, 65, 53, 215, 190, 54, 100, 22, 66, 139, 22, 115, 112, 28, 87, 155, 233, 17, 157, 14, 251, 24,
209, 112, 251, 201, 188, 34, 238, 116, 131, 41, 116, 122, 244, 8, 195, 223, 211, 171, 72, 215, 183, 200, 139, 131, 1,
80, 245, 159, 27, 226, 103, 52, 241, 192, 97, 17, 6, 18, 168, 236, 157, 39, 143, 18, 34, 163, 177, 214, 43, 80, 196,
141, 215, 220, 148, 163, 136, 108, 117, 160, 29, 133, 77, 104, 102, 36, 83, 254, 132, 63, 150, 44, 194, 115, 134, 5,
90, 161, 178, 190, 126, 185, 131, 102, 232, 247, 150, 175, 6, 34, 208, 56];
NodeTx::try_from_reba_data(tx_data.as_mut_slice()).unwrap();
}
#[test]
fn test_tx_with_panic_arithmetic()
{
let tx_data: [u8; 110] = [248, 108, 128, 133, 4, 168, 23, 200, 0, 130, 82, 8, 148, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 136, 13, 224, 182, 179, 167, 100, 0, 0, 128, 28, 160,
197, 3, 16, 220, 229, 179, 199, 17, 29, 239, 59, 199, 147, 160, 185, 70, 248, 105, 172, 67, 177,
211, 201, 56, 192, 219, 162, 250, 110, 189, 233, 154, 160, 45, 106, 21, 161, 28, 130, 211, 191,
88, 138, 4, 132, 153, 137, 167, 231, 184, 235, 85, 223, 171, 8, 115, 5, 107, 197, 254, 70, 63,
68, 226, 129];
Transaction::try_from_evm_bytes(tx_data.as_slice(), false).unwrap();
}
}