use rlp::{Encodable, RlpStream};
use std::fmt;
use tiny_keccak::Hasher;
use web3::types::{Address, Bytes, H256, U256};
#[derive(Debug)]
pub struct UnsignedTransaction {
pub nonce: U256,
pub gas_price: U256,
pub gas_limit: U256,
pub to: Option
,
pub value: U256,
pub data: Option,
}
struct Signature([u8; 64]);
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&&self.0[..], f)
}
}
#[derive(Debug)]
pub struct SignedTransaction<'a> {
unsigned_transaction: &'a UnsignedTransaction,
v: u8,
signature: Signature,
}
impl<'a> SignedTransaction<'a> {
pub(crate) fn new(
unsigned_transaction: &'a UnsignedTransaction,
v: u8,
signature: [u8; 64],
) -> Self {
SignedTransaction {
unsigned_transaction,
v,
signature: Signature(signature),
}
}
}
impl<'a> Encodable for SignedTransaction<'a> {
fn rlp_append(&self, stream: &mut RlpStream) {
// For some reason Ethereum thinks that the (r,s) of a ECDSA
// signature should be encoded as integers which means they
// cannot start with 0x00 and be a valid RLP encoding. So we
// wrap them in U256s so they RLP encode correctly. 🤦
let r = U256::from(&self.signature.0[0..32]);
let s = U256::from(&self.signature.0[32..64]);
stream
.append_internal(self.unsigned_transaction)
.append(&self.v)
.append(&r)
.append(&s);
}
}
impl<'a> From> for Bytes {
fn from(s: SignedTransaction<'_>) -> Self {
let mut stream = RlpStream::new();
let bytes = stream.append(&s).as_raw();
Bytes(bytes.to_vec())
}
}
impl Encodable for UnsignedTransaction {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(9)
.append(&self.nonce)
.append(&self.gas_price)
.append(&self.gas_limit);
match self.to {
Some(address) => s.append(&address),
None => s.append(&""),
};
s.append(&self.value).append(
&self
.data
.clone()
.map(|b| b.0)
.unwrap_or_else(|| [].to_vec()),
);
}
}
impl UnsignedTransaction {
pub(crate) fn hash(&self, chain_id: u8) -> H256 {
let mut stream = RlpStream::new();
let bytes = stream
.append_internal(self)
.append(&chain_id)
.append(&0u8)
.append(&0u8)
.as_raw();
let mut keccak = tiny_keccak::Keccak::v256();
keccak.update(bytes);
let mut tx_hash = [0u8; 32];
keccak.finalize(&mut tx_hash);
H256(tx_hash)
}
}