Skip to content

Commit

Permalink
wip: add interface for handling eth transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
neithanmo committed May 6, 2024
1 parent 8500554 commit 6f6a8a0
Show file tree
Hide file tree
Showing 21 changed files with 821 additions and 100 deletions.
2 changes: 2 additions & 0 deletions app/rust/include/rslib.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ parser_error_t _parse_sign_hash_tx(uint8_t *input, uint16_t len);

_clean_up_hash();

_computeV(parser_context_t *ctx, uint8_t parity, uint8_t *v);


86 changes: 76 additions & 10 deletions app/rust/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ use core::{mem::MaybeUninit, ptr::addr_of_mut};
use crate::constants::{BIP32_PATH_SUFFIX_DEPTH, SIGN_HASH_TX_SIZE};
use crate::handlers::avax::sign_hash::{Sign as SignHash, SignUI};
use crate::handlers::avax::signing::Sign;
use crate::parser::{parse_path_list, AvaxMessage, DisplayableItem, ObjectList, PathWrapper};
use crate::parser::{
parse_path_list, AvaxMessage, DisplayableItem, EthTransaction, ObjectList, PathWrapper,
};
use crate::parser::{FromBytes, ParserError, Transaction};
use crate::ZxError;

pub mod context;
pub mod message;
pub mod public_key;
pub mod sign_hash;
use context::{parser_context_t, Instruction};
Expand All @@ -42,11 +43,13 @@ macro_rules! avax_msg_from_state {
unsafe { &mut (*addr_of_mut!((*$ptr).tx_obj.state).cast::<MaybeUninit<AvaxMessage>>()) }
};
}
/// #Safety
/// Enough space was allocated to store an Avalanche Transaction
// unsafe fn parse_obj_from_state<'a>(tx: *mut parse_tx_t) -> Option<&'a mut Transaction<'a>> {
// ((*tx).state as *const u8 as *mut Transaction).as_mut()
// }

/// Cast a *mut u8 to a *mut Transaction
macro_rules! eth_tx_from_state {
($ptr:expr) => {
unsafe { &mut (*addr_of_mut!((*$ptr).tx_obj.state).cast::<MaybeUninit<EthTransaction>>()) }
};
}

#[no_mangle]
pub unsafe extern "C" fn _parser_init(
Expand All @@ -69,9 +72,7 @@ pub unsafe extern "C" fn _parser_init(
// our global state
let size = match ins {
Instruction::SignAvaxTx => core::mem::size_of::<MaybeUninit<Transaction>>() as u32,
Instruction::SignEthTx => {
return ParserError::UnexpectedError as u32;
}
Instruction::SignEthTx => core::mem::size_of::<MaybeUninit<EthTransaction>>() as u32,
Instruction::SignAvaxMsg => core::mem::size_of::<MaybeUninit<AvaxMessage>>() as u32,
Instruction::SignEthMsg => {
return ParserError::UnexpectedError as u32;
Expand Down Expand Up @@ -150,6 +151,14 @@ pub unsafe extern "C" fn _parser_read(ctx: *const parser_context_t) -> u32 {
}
}

Instruction::SignEthTx => {
let tx = eth_tx_from_state!(ctx as *mut parser_context_t);
match EthTransaction::from_bytes_into(data, tx) {
Ok(_) => ParserError::ParserOk as u32,
Err(_) => ParserError::InvalidTransactionType as u32,
}
}

Instruction::SignAvaxHash => {
if let Err(e) = SignHash::parse_hash(data) {
return e as u32;
Expand Down Expand Up @@ -217,6 +226,18 @@ pub unsafe extern "C" fn _getNumItems(ctx: *const parser_context_t, num_items: *
}
}

Instruction::SignEthTx => {
let tx = eth_tx_from_state!(ctx as *mut parser_context_t);
let obj = tx.assume_init_mut();
match obj.num_items() {
Ok(n) => {
*num_items = n;
ParserError::ParserOk as u32
}
Err(e) => e as u32,
}
}

_ => todo!(),
};

Expand Down Expand Up @@ -288,6 +309,18 @@ pub unsafe extern "C" fn _getItem(
}
}

Instruction::SignEthTx => {
let tx = eth_tx_from_state!(ctx as *mut parser_context_t);
let obj = tx.assume_init_mut();
match obj.render_item(display_idx, key, value, page_idx) {
Ok(page) => {
*page_count = page;
ParserError::ParserOk as _
}
Err(e) => e as _,
}
}

_ => todo!(),
}
}
Expand Down Expand Up @@ -323,3 +356,36 @@ pub unsafe extern "C" fn _tx_data_offset(

ZxError::Ok as _
}

// Use to compute the V component of the signature
// for eth transactions. this is required for the app_ethereum js application
// to compute the real V from this bytes
#[no_mangle]
pub unsafe extern "C" fn _computeV(ctx: *const parser_context_t, parity: u8, v: *mut u8) {
let tx = eth_tx_from_state!(ctx as *mut parser_context_t);
let tx = tx.assume_init_mut();

let chain_id = tx.chain_id();

// Check for typed transactions
if tx.raw_tx_type().is_some() {
//write V, which is the oddity of the signature
*v = parity;
} else if chain_id.is_none() {
// according to app-ethereum this is the legacy non eip155 conformant
// so V should be made before EIP155 which had
// 27 + {0, 1}
// 27, decided by the parity of Y
// see https://bitcoin.stackexchange.com/a/112489
// https://ethereum.stackexchange.com/a/113505
// https://eips.ethereum.org/EIPS/eip-155
*v = 27 + parity;
} else {
// app-ethereum reads the first 4 bytes then cast it to an u8
// this is not good but it relies on hw-eth-app lib from ledger
// to recover the right chain_id from the V component being computed here, and
// which is returned with the signature
let chain_id = chain_id.unwrap();
*v = (35 + parity as u32).saturating_add((chain_id as u32) << 1) as u8;
}
}
23 changes: 15 additions & 8 deletions app/rust/src/ffi/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ pub struct parse_tx_t {
pub len: u16,
}

#[repr(u8)]
// typedef enum {
// SignAvaxTx = 0x00,
// SignEthTx,
// SignAvaxMsg,
// SignEthMsg,
// SignAvaxHash
// } instruction_t;
#[repr(C)]
pub enum Instruction {
SignAvaxTx = 0x00,
SignEthTx,
Expand All @@ -46,15 +53,15 @@ impl TryFrom<u8> for Instruction {
type Error = ParserError;

fn try_from(value: u8) -> Result<Self, Self::Error> {
if value == 2 {
crate::zlog("SignAvaxMsg\x00");
if value == 1 {
crate::zlog("Instruction::SignEthTx\n");
}
match value {
0x00 => Ok(Instruction::SignAvaxTx),
0x01 => Ok(Instruction::SignEthTx),
0x02 => Ok(Instruction::SignAvaxMsg),
0x03 => Ok(Instruction::SignEthMsg),
0x04 => Ok(Instruction::SignAvaxHash),
0 => Ok(Instruction::SignAvaxTx),
1 => Ok(Instruction::SignEthTx),
2 => Ok(Instruction::SignAvaxMsg),
3 => Ok(Instruction::SignEthMsg),
4 => Ok(Instruction::SignAvaxHash),
_ => Err(ParserError::InvalidTransactionType),
}
}
Expand Down
16 changes: 9 additions & 7 deletions app/rust/src/handlers/eth/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@ impl Viewable for SignUI {
tx += 1;
} else {
let chain_id = self.tx.chain_id();
if chain_id.is_empty() {
// if chain_id.is_empty() {
if chain_id.is_none() {
// according to app-ethereum this is the legacy non eip155 conformant
// so V should be made before EIP155 which had
// 27 + {0, 1}
Expand All @@ -267,12 +268,13 @@ impl Viewable for SignUI {
// this is not good but it relies on hw-eth-app lib from ledger
// to recover the right chain_id from the V component being computed here, and
// which is returned with the signature
let len = core::cmp::min(U32_SIZE, chain_id.len());
if let Ok(chain_id) = bytes_to_u64(&chain_id[..len]) {
let v = (35 + flags.contains(ECCInfo::ParityOdd) as u32)
.saturating_add((chain_id as u32) << 1);
out[tx] = v as u8;
}
let chain_id = chain_id.unwrap();
// let len = core::cmp::min(U32_SIZE, chain_id.len());
// if let Ok(chain_id) = bytes_to_u64(&chain_id[..len]) {
let v = (35 + flags.contains(ECCInfo::ParityOdd) as u32)
.saturating_add((chain_id as u32) << 1);
out[tx] = v as u8;
// }
}
tx += 1;
}
Expand Down
6 changes: 5 additions & 1 deletion app/rust/src/parser/coreth/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ pub use eip1559::Eip1559;
mod eip2930;
pub use eip2930::Eip2930;

#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(test, derive(Debug))]
pub struct EthChainId<'b>(&'b [u8]);

/// Renders an u256 in bytes.
/// `input`: The big-indian bytes of the number to
/// be rendered
Expand Down Expand Up @@ -176,7 +180,7 @@ impl<'b> EthTransaction<'b> {
}
}

pub fn chain_id(&self) -> &'b [u8] {
pub fn chain_id(&self) -> Option<u64> {
match self {
Self::Legacy(t) => t.chain_id(),
Self::Eip1559(t) => t.chain_id(),
Expand Down
9 changes: 5 additions & 4 deletions app/rust/src/parser/coreth/native/eip1559.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::{
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(test, derive(Debug))]
pub struct Eip1559<'b> {
chain_id: &'b [u8],
chain_id: Option<u64>,
pub nonce: BorrowedU256<'b>,
pub priority_fee: BorrowedU256<'b>,
pub max_fee: BorrowedU256<'b>,
Expand All @@ -52,7 +52,7 @@ pub struct Eip1559<'b> {
}

impl<'b> Eip1559<'b> {
pub fn chain_id(&self) -> &'b [u8] {
pub fn chain_id(&self) -> Option<u64> {
self.chain_id
}
}
Expand Down Expand Up @@ -117,12 +117,13 @@ impl<'b> FromBytes<'b> for Eip1559<'b> {
return Err(ParserError::InvalidAssetCall.into());
}

let chain_id = super::bytes_to_u64(id_bytes)?;

// check for erc721 call and chainID
#[cfg(feature = "erc721")]
{
let data = unsafe { &*data_out.as_ptr() };
if matches!(data, EthData::Erc721(..)) {
let chain_id = super::bytes_to_u64(id_bytes)?;
let contract_chain_id = crate::parser::ERC721Info::get_nft_info()?.chain_id;
if chain_id != contract_chain_id {
return Err(ParserError::InvalidAssetCall.into());
Expand All @@ -144,7 +145,7 @@ impl<'b> FromBytes<'b> for Eip1559<'b> {
addr_of_mut!((*out).gas_limit).write(gas_limit);
addr_of_mut!((*out).to).write(address);
addr_of_mut!((*out).value).write(value);
addr_of_mut!((*out).chain_id).write(id_bytes);
addr_of_mut!((*out).chain_id).write(Some(chain_id));
addr_of_mut!((*out).access_list).write(access_list);
}

Expand Down
9 changes: 5 additions & 4 deletions app/rust/src/parser/coreth/native/eip2930.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub struct Eip2930<'b> {
// is an u32, u8, u64
// considering this might
// come from an avax C-Chain
chain_id: &'b [u8],
chain_id: Option<u64>,
pub base: BaseLegacy<'b>,
access_list: &'b [u8],
// R and S must be empty
Expand All @@ -37,7 +37,7 @@ pub struct Eip2930<'b> {
}

impl<'b> Eip2930<'b> {
pub fn chain_id(&self) -> &'b [u8] {
pub fn chain_id(&self) -> Option<u64> {
self.chain_id
}
}
Expand All @@ -64,12 +64,13 @@ impl<'b> FromBytes<'b> for Eip2930<'b> {
// access list
let (rem, access_list) = parse_rlp_item(rem)?;

let chain_id = super::bytes_to_u64(id_bytes)?;

// check for erc721 call and chainID
#[cfg(feature = "erc721")]
{
let base = unsafe { &*data_out.as_ptr() };
if matches!(base.data, crate::parser::EthData::Erc721(..)) {
let chain_id = super::bytes_to_u64(id_bytes)?;
let contract_chain_id = crate::parser::ERC721Info::get_nft_info()?.chain_id;
if chain_id != contract_chain_id {
return Err(ParserError::InvalidAssetCall.into());
Expand All @@ -78,7 +79,7 @@ impl<'b> FromBytes<'b> for Eip2930<'b> {
}

unsafe {
addr_of_mut!((*out).chain_id).write(id_bytes);
addr_of_mut!((*out).chain_id).write(Some(chain_id));
addr_of_mut!((*out).access_list).write(access_list);
}

Expand Down
12 changes: 8 additions & 4 deletions app/rust/src/parser/coreth/native/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use zemu_sys::ViewError;

use super::parse_rlp_item;
use super::BaseLegacy;
use super::EthChainId;
use crate::parser::U64_SIZE;
use crate::parser::{DisplayableItem, FromBytes, ParserError};

Expand All @@ -28,15 +29,15 @@ const MAX_CHAIN_LEN: usize = U64_SIZE;
#[cfg_attr(test, derive(Debug))]
pub struct Legacy<'b> {
pub base: BaseLegacy<'b>,
chain_id: &'b [u8],
chain_id: Option<u64>,
// R and S must be empty
// so do not put and empty
// field here, it is just to indicate
// that they are expected
}

impl<'b> Legacy<'b> {
pub fn chain_id(&self) -> &'b [u8] {
pub fn chain_id(&self) -> Option<u64> {
self.chain_id
}
}
Expand All @@ -61,7 +62,7 @@ impl<'b> FromBytes<'b> for Legacy<'b> {
unsafe {
// write an empty chain-id as it is used to compute the right V component
// when transaction is signed
addr_of_mut!((*out).chain_id).write(&[]);
addr_of_mut!((*out).chain_id).write(None);
}

return Ok(&[]);
Expand Down Expand Up @@ -92,6 +93,7 @@ impl<'b> FromBytes<'b> for Legacy<'b> {
// r and s if not empty, should contain only one value
// which must be zero.
if !r.is_empty() && !s.is_empty() {
crate::sys::zemu_log_stack("r_s_not_empty\x00");
let no_zero = r.iter().any(|v| *v != 0) && s.iter().any(|v| *v != 0);
if no_zero || r.len() != 1 || s.len() != 1 {
crate::sys::zemu_log_stack("Legacy::invalid_r_s\x00");
Expand All @@ -104,8 +106,10 @@ impl<'b> FromBytes<'b> for Legacy<'b> {
return Err(ParserError::InvalidChainId.into());
}

let id = super::bytes_to_u64(id_bytes)?;

unsafe {
addr_of_mut!((*out).chain_id).write(id_bytes);
addr_of_mut!((*out).chain_id).write(Some(id));
}

Ok(rem)
Expand Down
Loading

0 comments on commit 6f6a8a0

Please sign in to comment.