use crate::message::SignedMessage;
use crate::shim::address::Address;
use crate::shim::crypto::SignatureType::Delegated;
use anyhow::ensure;
use derive_builder::Builder;
use num::BigInt;
use num_bigint::Sign;
use super::*;
pub const EIP_1559_SIG_LEN: usize = 65;
#[derive(PartialEq, Debug, Clone, Default, Builder)]
#[builder(setter(into))]
pub struct EthEip1559TxArgs {
pub chain_id: EthChainId,
pub nonce: u64,
pub to: Option<EthAddress>,
pub value: BigInt,
pub max_fee_per_gas: BigInt,
pub max_priority_fee_per_gas: BigInt,
pub gas_limit: u64,
pub input: Vec<u8>,
#[builder(setter(skip))]
pub v: BigInt,
#[builder(setter(skip))]
pub r: BigInt,
#[builder(setter(skip))]
pub s: BigInt,
}
impl EthEip1559TxArgs {
pub fn signature(&self) -> anyhow::Result<Signature> {
let r_bytes = self.r.to_bytes_be().1;
let s_bytes = self.s.to_bytes_be().1;
let v_bytes = self.v.to_bytes_be().1;
let mut sig = pad_leading_zeros(r_bytes, 32);
sig.extend(pad_leading_zeros(s_bytes, 32));
if v_bytes.is_empty() {
sig.push(0);
} else {
sig.push(*v_bytes.first().expect("failed to get first byte of V"));
}
ensure!(sig.len() == EIP_1559_SIG_LEN, "invalid signature length");
Ok(Signature {
sig_type: Delegated,
bytes: sig,
})
}
pub fn to_verifiable_signature(&self, sig: Vec<u8>) -> anyhow::Result<Vec<u8>> {
Ok(sig)
}
pub fn with_signature(mut self, signature: &Signature) -> anyhow::Result<Self> {
ensure!(
signature.signature_type() == Delegated,
"Signature is not delegated type, is {}",
signature.signature_type()
);
ensure!(
signature.bytes().len() == EIP_1559_SIG_LEN,
"Invalid signature length for EIP1559 transaction: {}",
signature.bytes().len()
);
self.r = BigInt::from_bytes_be(Sign::Plus, signature.bytes().get(..32).expect("infalible"));
self.s = BigInt::from_bytes_be(
Sign::Plus,
signature.bytes().get(32..64).expect("infalible"),
);
self.v = BigInt::from_bytes_be(
Sign::Plus,
signature
.bytes()
.get(64..EIP_1559_SIG_LEN)
.expect("infalible"),
);
Ok(self)
}
fn message_rlp_stream(&self) -> anyhow::Result<rlp::RlpStream> {
let prefix = [EIP_1559_TX_TYPE].as_slice();
let access_list: &[u8] = &[];
let mut stream = rlp::RlpStream::new_with_buffer(prefix.into());
stream
.begin_unbounded_list()
.append(&format_u64(self.chain_id))
.append(&format_u64(self.nonce))
.append(&format_bigint(&self.max_priority_fee_per_gas)?)
.append(&format_bigint(&self.max_fee_per_gas)?)
.append(&format_u64(self.gas_limit))
.append(&format_address(self.to.as_ref()))
.append(&format_bigint(&self.value)?)
.append(&self.input)
.append_list(access_list);
Ok(stream)
}
pub fn rlp_signed_message(&self) -> anyhow::Result<Vec<u8>> {
let mut stream = self.message_rlp_stream()?;
stream
.append(&format_bigint(&self.v)?)
.append(&format_bigint(&self.r)?)
.append(&format_bigint(&self.s)?)
.finalize_unbounded_list();
Ok(stream.out().to_vec())
}
pub fn rlp_unsigned_message(&self) -> anyhow::Result<Vec<u8>> {
let mut stream = self.message_rlp_stream()?;
stream.finalize_unbounded_list();
Ok(stream.out().to_vec())
}
pub fn get_signed_message(
&self,
from: Address,
eth_chain_id: EthChainId,
) -> anyhow::Result<SignedMessage> {
let message = self.get_unsigned_message(from, eth_chain_id)?;
let signature = self.signature()?;
Ok(SignedMessage { message, signature })
}
pub fn get_unsigned_message(
&self,
from: Address,
eth_chain_id: EthChainId,
) -> anyhow::Result<Message> {
ensure!(
self.chain_id == eth_chain_id,
"Invalid chain id, expected {}, got {}",
self.chain_id,
eth_chain_id
);
let method_info = get_filecoin_method_info(self.to.as_ref(), &self.input)?;
Ok(Message {
version: 0,
from,
to: method_info.to,
sequence: self.nonce,
value: self.value.clone().into(),
method_num: method_info.method,
params: method_info.params.into(),
gas_limit: self.gas_limit,
gas_fee_cap: self.max_fee_per_gas.clone().into(),
gas_premium: self.max_priority_fee_per_gas.clone().into(),
})
}
}
impl EthEip1559TxArgsBuilder {
pub fn unsigned_message(&mut self, message: &Message) -> anyhow::Result<&mut Self> {
let (params, to) = get_eth_params_and_recipient(message)?;
Ok(self
.nonce(message.sequence)
.value(message.value.clone())
.max_fee_per_gas(message.gas_fee_cap.clone())
.max_priority_fee_per_gas(message.gas_premium.clone())
.gas_limit(message.gas_limit)
.to(to)
.input(params))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shim::crypto::{Signature, SignatureType};
use num_bigint::ToBigInt;
fn create_eip1559_tx_args() -> EthEip1559TxArgs {
EthEip1559TxArgsBuilder::default()
.chain_id(42u64)
.nonce(1u64)
.to(Some(EthAddress::default()))
.value(100.to_bigint().unwrap())
.max_fee_per_gas(10.to_bigint().unwrap())
.max_priority_fee_per_gas(1.to_bigint().unwrap())
.gas_limit(1000u64)
.input(vec![1, 2, 3])
.build()
.unwrap()
}
#[test]
fn test_valid_eip1559_tx_args_with_signature() {
let args = create_eip1559_tx_args();
let signature = Signature::new(SignatureType::Delegated, vec![0u8; EIP_1559_SIG_LEN]);
args.with_signature(&signature).unwrap();
}
#[test]
fn test_invalid_eip1559_tx_args_not_delegated() {
let args = create_eip1559_tx_args();
let signature = Signature::new(SignatureType::Secp256k1, vec![0u8; EIP_1559_SIG_LEN]);
assert!(args.with_signature(&signature).is_err());
}
#[test]
fn test_invalid_eip1559_tx_args_invalid_signature_len() {
let args = create_eip1559_tx_args();
let signature = Signature::new(SignatureType::Delegated, vec![0u8; EIP_1559_SIG_LEN - 1]);
assert!(args.with_signature(&signature).is_err());
}
#[test]
fn test_signature() {
let args = create_eip1559_tx_args();
let signature = Signature::new(SignatureType::Delegated, vec![0u8; EIP_1559_SIG_LEN]);
args.clone().with_signature(&signature).unwrap();
let sig = args.signature().unwrap();
assert_eq!(sig, signature);
assert!(args.to_verifiable_signature(sig.bytes().to_vec()).is_ok());
}
}