use ethereum_types::{H160, H256, U256};
use rlp::RlpStream;
use secp256k1::key::SecretKey;
use secp256k1::Message;
use secp256k1::Secp256k1;
use tiny_keccak::keccak256;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct RawTransaction {
pub nonce: U256,
pub to: Option<H160>,
pub value: U256,
#[serde(rename = "gasPrice")]
pub gas_price: U256,
pub gas: U256,
pub data: Vec<u8>,
}
impl RawTransaction {
pub fn sign(&self, private_key: &H256, chain_id: u8) -> Vec<u8> {
let hash = self.hash(chain_id);
let sig = ecdsa_sign(&hash, &private_key.0, chain_id);
let mut tx = RlpStream::new();
tx.begin_unbounded_list();
self.encode(&mut tx);
tx.append(&sig.v);
tx.append(&sig.r);
tx.append(&sig.s);
tx.complete_unbounded_list();
tx.out()
}
fn hash(&self, chain_id: u8) -> Vec<u8> {
let mut hash = RlpStream::new();
hash.begin_unbounded_list();
self.encode(&mut hash);
hash.append(&vec![chain_id]);
hash.append(&U256::zero());
hash.append(&U256::zero());
hash.complete_unbounded_list();
keccak256_hash(&hash.out())
}
fn encode(&self, s: &mut RlpStream) {
s.append(&self.nonce);
s.append(&self.gas_price);
s.append(&self.gas);
if let Some(ref t) = self.to {
s.append(t);
} else {
s.append(&vec![]);
}
s.append(&self.value);
s.append(&self.data);
}
}
fn keccak256_hash(bytes: &[u8]) -> Vec<u8> {
keccak256(bytes).iter().cloned().collect()
}
fn ecdsa_sign(hash: &[u8], private_key: &[u8], chain_id: u8) -> EcdsaSig {
let s = Secp256k1::signing_only();
let msg = Message::from_slice(hash).unwrap();
let key = SecretKey::from_slice(private_key).unwrap();
let (v, sig_bytes) = s.sign_recoverable(&msg, &key).serialize_compact();
EcdsaSig {
v: vec![v.to_i32() as u8 + chain_id * 2 + 35],
r: sig_bytes[0..32].to_vec(),
s: sig_bytes[32..64].to_vec(),
}
}
pub struct EcdsaSig {
v: Vec<u8>,
r: Vec<u8>,
s: Vec<u8>,
}