use crate::address::EthereumAddress;
use crate::amount::EthereumAmount;
use crate::format::EthereumFormat;
use crate::network::EthereumNetwork;
use crate::public_key::EthereumPublicKey;
use anychain_core::ethereum_types::U256;
use anychain_core::utilities::crypto::keccak256;
use anychain_core::{hex, libsecp256k1, PublicKey, Transaction, TransactionError, TransactionId};
use core::{fmt, marker::PhantomData, str::FromStr};
use ethabi::ethereum_types::H160;
use ethabi::{Function, Param, ParamType, StateMutability, Token};
use rlp::{decode_list, RlpStream};
use std::convert::TryInto;
fn trim_leading_zeros(v: &Vec<u8>) -> &[u8] {
let mut cnt: usize = 0;
for byte in v {
if *byte != 0 {
break;
} else {
cnt += 1;
}
}
&v[cnt..]
}
fn pad_zeros(v: &mut Vec<u8>, to_len: usize) {
if v.len() < to_len {
let mut temp = v.clone();
let len = v.len();
v.clear();
v.resize(to_len - len, 0);
v.append(&mut temp);
}
}
pub fn encode_transfer(func_name: &str, address: &EthereumAddress, amount: U256) -> Vec<u8> {
#[allow(deprecated)]
let func = Function {
name: func_name.to_string(),
inputs: vec![
Param {
name: "address".to_string(),
kind: ParamType::Address,
internal_type: None,
},
Param {
name: "amount".to_string(),
kind: ParamType::Uint(256),
internal_type: None,
},
],
outputs: vec![],
constant: None,
state_mutability: StateMutability::Payable,
};
let tokens = vec![
Token::Address(H160::from_slice(&address.to_bytes().unwrap())),
Token::Uint(amount),
];
func.encode_input(&tokens).unwrap()
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EthereumTransactionParameters {
pub receiver: EthereumAddress,
pub amount: EthereumAmount,
pub gas: U256,
pub gas_price: EthereumAmount,
pub nonce: U256,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct EthereumTransactionSignature {
v: Vec<u8>,
r: Vec<u8>,
s: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EthereumTransactionId {
pub txid: Vec<u8>,
}
impl TransactionId for EthereumTransactionId {}
impl fmt::Display for EthereumTransactionId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x{}", hex::encode(&self.txid))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EthereumTransaction<N: EthereumNetwork> {
sender: Option<EthereumAddress>,
parameters: EthereumTransactionParameters,
signature: Option<EthereumTransactionSignature>,
_network: PhantomData<N>,
}
impl<N: EthereumNetwork> Transaction for EthereumTransaction<N> {
type Address = EthereumAddress;
type Format = EthereumFormat;
type PublicKey = EthereumPublicKey;
type TransactionId = EthereumTransactionId;
type TransactionParameters = EthereumTransactionParameters;
fn new(parameters: &Self::TransactionParameters) -> Result<Self, TransactionError> {
Ok(Self {
sender: None,
parameters: parameters.clone(),
signature: None,
_network: PhantomData,
})
}
fn sign(&mut self, rs: Vec<u8>, recid: u8) -> Result<Vec<u8>, TransactionError> {
let message = libsecp256k1::Message::parse_slice(&self.to_transaction_id()?.txid)?;
let recovery_id = libsecp256k1::RecoveryId::parse(recid)?;
let public_key = EthereumPublicKey::from_secp256k1_public_key(libsecp256k1::recover(
&message,
&libsecp256k1::Signature::parse_standard_slice(rs.as_slice())?,
&recovery_id,
)?);
self.sender = Some(public_key.to_address(&EthereumFormat::Standard)?);
self.signature = Some(EthereumTransactionSignature {
v: (u32::from(recid) + N::CHAIN_ID * 2 + 35)
.to_be_bytes()
.to_vec(), r: rs[..32].to_vec(),
s: rs[32..64].to_vec(),
});
self.to_bytes()
}
fn from_bytes(transaction: &[u8]) -> Result<Self, TransactionError> {
let list: Vec<Vec<u8>> = decode_list(transaction);
if list.len() != 9 {
return Err(TransactionError::InvalidRlpLength(list.len()));
}
let parameters = EthereumTransactionParameters {
receiver: EthereumAddress::from_str(&hex::encode(&list[3]))?,
amount: match list[4].is_empty() {
true => EthereumAmount::from_u256(U256::zero()),
false => EthereumAmount::from_u256(U256::from(list[4].as_slice())),
},
gas: match list[2].is_empty() {
true => U256::zero(),
false => U256::from(list[2].as_slice()),
},
gas_price: match list[1].is_empty() {
true => EthereumAmount::from_u256(U256::zero()),
false => EthereumAmount::from_u256(U256::from(list[1].as_slice())),
},
nonce: match list[0].is_empty() {
true => U256::zero(),
false => U256::from(list[0].as_slice()),
},
data: list[5].clone(),
};
match list[7].is_empty() && list[8].is_empty() {
true => {
Ok(Self {
sender: None,
parameters,
signature: None,
_network: PhantomData,
})
}
false => {
let mut v = list[6].clone();
pad_zeros(&mut v, 4);
let v: [u8; 4] = v.try_into().unwrap();
let v = u32::from_be_bytes(v);
let recovery_id =
libsecp256k1::RecoveryId::parse((v - N::CHAIN_ID * 2 - 35) as u8)?;
let mut r = list[7].clone();
pad_zeros(&mut r, 32);
let mut s = list[8].clone();
pad_zeros(&mut s, 32);
let signature = [r.clone(), s.clone()].concat();
let raw_transaction = Self {
sender: None,
parameters: parameters.clone(),
signature: None,
_network: PhantomData,
};
let message =
libsecp256k1::Message::parse_slice(&raw_transaction.to_transaction_id()?.txid)?;
let public_key =
EthereumPublicKey::from_secp256k1_public_key(libsecp256k1::recover(
&message,
&libsecp256k1::Signature::parse_standard_slice(signature.as_slice())?,
&recovery_id,
)?);
Ok(Self {
sender: Some(public_key.to_address(&EthereumFormat::Standard)?),
parameters,
signature: Some(EthereumTransactionSignature {
v: v.to_be_bytes().to_vec(),
r,
s,
}),
_network: PhantomData,
})
}
}
}
fn to_bytes(&self) -> Result<Vec<u8>, TransactionError> {
fn encode_transaction(
transaction_rlp: &mut RlpStream,
parameters: &EthereumTransactionParameters,
) -> Result<(), TransactionError> {
transaction_rlp.append(¶meters.nonce);
transaction_rlp.append(¶meters.gas_price.0);
transaction_rlp.append(¶meters.gas);
transaction_rlp.append(&hex::decode(¶meters.receiver.to_string()[2..])?);
transaction_rlp.append(¶meters.amount.0);
transaction_rlp.append(¶meters.data);
Ok(())
}
fn raw_transaction<N: EthereumNetwork>(
parameters: &EthereumTransactionParameters,
) -> Result<RlpStream, TransactionError> {
let mut transaction_rlp = RlpStream::new();
transaction_rlp.begin_list(9);
encode_transaction(&mut transaction_rlp, parameters)?;
let chain_id = N::CHAIN_ID.to_be_bytes().to_vec();
let chain_id = trim_leading_zeros(&chain_id);
transaction_rlp.append(&chain_id);
transaction_rlp.append(&0u8);
transaction_rlp.append(&0u8);
Ok(transaction_rlp)
}
fn signed_transaction(
parameters: &EthereumTransactionParameters,
signature: &EthereumTransactionSignature,
) -> Result<RlpStream, TransactionError> {
let mut transaction_rlp = RlpStream::new();
transaction_rlp.begin_list(9);
encode_transaction(&mut transaction_rlp, parameters)?;
let v = trim_leading_zeros(&signature.v);
transaction_rlp.append(&v);
let r = trim_leading_zeros(&signature.r);
transaction_rlp.append(&r);
let s = trim_leading_zeros(&signature.s);
transaction_rlp.append(&s);
Ok(transaction_rlp)
}
match &self.signature {
Some(signature) => Ok(signed_transaction(&self.parameters, signature)?
.out()
.to_vec()),
None => Ok(raw_transaction::<N>(&self.parameters)?.out().to_vec()),
}
}
fn to_transaction_id(&self) -> Result<Self::TransactionId, TransactionError> {
Ok(Self::TransactionId {
txid: Vec::<u8>::from(&keccak256(&self.to_bytes()?)[..]),
})
}
}
impl<N: EthereumNetwork> EthereumTransaction<N> {
pub fn get_from(&self) -> EthereumAddress {
self.sender.clone().unwrap()
}
pub fn get_to(&self) -> EthereumAddress {
self.parameters.receiver.clone()
}
pub fn get_amount(&self) -> EthereumAmount {
self.parameters.amount
}
pub fn get_gas_price(&self) -> EthereumAmount {
self.parameters.gas_price
}
pub fn get_gas_limit(&self) -> U256 {
self.parameters.gas
}
pub fn get_nonce(&self) -> U256 {
self.parameters.nonce
}
pub fn get_data(&self) -> Vec<u8> {
self.parameters.data.clone()
}
pub fn get_r(&self) -> String {
hex::encode(self.signature.clone().unwrap().r)
}
pub fn get_s(&self) -> String {
hex::encode(self.signature.clone().unwrap().s)
}
pub fn get_v(&self) -> u32 {
let v = self.signature.clone().unwrap().v;
let v: [u8; 4] = v.try_into().unwrap();
u32::from_be_bytes(v)
}
pub fn get_chain_id(&self) -> u32 {
N::CHAIN_ID
}
}
impl<N: EthereumNetwork> FromStr for EthereumTransaction<N> {
type Err = TransactionError;
fn from_str(tx: &str) -> Result<Self, Self::Err> {
let tx = match &tx[..2] {
"0x" => &tx[2..],
_ => tx,
};
Self::from_bytes(&hex::decode(tx)?)
}
}
impl<N: EthereumNetwork> fmt::Display for EthereumTransaction<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"0x{}",
&hex::encode(match self.to_bytes() {
Ok(transaction) => transaction,
_ => return Err(fmt::Error),
})
)
}
}