Skip to content

Commit

Permalink
Some improvements to avoid panicking and enhance remainder handling i…
Browse files Browse the repository at this point in the history
…n case of multisig transactions (#173)

More improvements in project structure

Ensure to parse all contract arguments avoiding potential errors during transaction review

Increase recursion limit to 8 nested clarity values, this does not hit any limits regarding nanos2

add snapshots

feat/ add debug implementation

Restructure the post-conditions into its own folder and module

Update zxformat to use ledger-zxlib when compiling to ledger targets

Add test to parsed transaction containing post-conditions

Bump app version

Update zxlib dependency

Update snapshots
  • Loading branch information
neithanmo authored Oct 23, 2024
1 parent b9b941c commit 858f9a0
Show file tree
Hide file tree
Showing 243 changed files with 1,576 additions and 1,120 deletions.
2 changes: 1 addition & 1 deletion app/Makefile.version
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
APPVERSION_M=0
APPVERSION_N=24
APPVERSION_P=4
APPVERSION_P=5
24 changes: 24 additions & 0 deletions app/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions app/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ hex = { version = "0.4", default-features = false }
serde-json-core = { version = "0.4.0", default-features = false }
serde = { version = "1.0", default-features = false, features = ["derive"] }
nom = { version = "7.1.2", default-features = false }
lexical-core = { version = "0.7", features = [
"libm",
], default-features = false }


[dependencies.arrayvec]
Expand Down
3 changes: 3 additions & 0 deletions app/rust/src/parser/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,12 @@ pub unsafe extern "C" fn _last_block_ptr(
) -> u16 {
if let Some(tx) = parsed_obj_from_state(tx_t as _).and_then(|obj| obj.transaction()) {
let block = tx.last_transaction_block();

*block_ptr = block.as_ptr();
return block.len() as _;
}

*block_ptr = core::ptr::null_mut();
0
}

Expand Down
14 changes: 13 additions & 1 deletion app/rust/src/parser/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const BYTE_STRING_HEADER_LEN: usize = "\x17Stacks Signed Message:\n".as_bytes().
const MAX_ASCII_LEN: usize = 270;

#[repr(C)]
#[cfg_attr(test, derive(Debug))]
pub struct Message<'a>(ByteString<'a>);

impl<'a> Message<'a> {
Expand Down Expand Up @@ -45,9 +46,20 @@ impl<'a> Message<'a> {
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Copy, Clone, PartialEq)]
pub struct ByteString<'a>(&'a [u8]);

#[cfg(test)]
impl<'a> core::fmt::Debug for ByteString<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "ByteString(\"")?;
for byte in self.0 {
write!(f, "{:02x}", byte)?;
}
write!(f, "\")")
}
}

impl<'a> ByteString<'a> {
pub fn is_msg(data: &'a [u8]) -> bool {
Self::contain_header(data)
Expand Down
6 changes: 4 additions & 2 deletions app/rust/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ mod jwt;
mod message;
mod parsed_obj;
mod parser_common;
mod post_condition;
mod post_conditions;
mod principal;
mod spending_condition;
mod structured_msg;
mod transaction;
mod transaction_auth;
mod transaction_payload;
mod tx_post_conditions;
mod utils;
mod value;
pub use error::ParserError;
Expand All @@ -20,10 +21,11 @@ pub use jwt::Jwt;
pub use message::{ByteString, Message};
pub use parsed_obj::{ParsedObj, Tag};
pub use parser_common::*;
pub use post_condition::{FungibleConditionCode, TransactionPostCondition};
pub use post_conditions::{FungibleConditionCode, TransactionPostCondition};
pub use principal::*;
pub use structured_msg::{Domain, StructuredMsg};
pub use transaction::Transaction;
pub use transaction_auth::TransactionAuth;
pub use tx_post_conditions::{PostConditions, TransactionPostConditionMode};
pub use utils::*;
pub use value::{Int128, Tuple, UInt128, Value, ValueId};
53 changes: 53 additions & 0 deletions app/rust/src/parser/parsed_obj.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![allow(non_camel_case_types, non_snake_case, clippy::missing_safety_doc)]

use crate::bolos::c_zemu_log_stack;

use super::{error::ParserError, transaction::Transaction, Message};
use super::{Jwt, StructuredMsg};

Expand Down Expand Up @@ -56,6 +58,7 @@ impl<'a> ParsedObj<'a> {
}

pub fn read(&mut self, data: &'a [u8]) -> Result<(), ParserError> {
c_zemu_log_stack("ParsedObj::read\x00");
if data.is_empty() {
return Err(ParserError::NoData);
}
Expand All @@ -65,15 +68,19 @@ impl<'a> ParsedObj<'a> {

unsafe {
if Message::is_message(data) {
c_zemu_log_stack("Tag::Msg\x00");
self.tag = Tag::Message;
self.obj.read_msg(data)
} else if Jwt::is_jwt(data) {
c_zemu_log_stack("Tag::Jwt\x00");
self.tag = Tag::Jwt;
self.obj.read_jwt(data)
} else if StructuredMsg::is_msg(data) {
c_zemu_log_stack("Tag::StructuredMsg\x00");
self.tag = Tag::StructuredMsg;
self.obj.read_structured_msg(data)
} else {
c_zemu_log_stack("Tag::Transaction\x00");
self.tag = Tag::Transaction;
self.obj.read_tx(data)
}
Expand All @@ -99,6 +106,7 @@ impl<'a> ParsedObj<'a> {
value: &mut [u8],
page_idx: u8,
) -> Result<u8, ParserError> {
c_zemu_log_stack("ParsedObj::get_item\x00");
unsafe {
match self.tag {
Tag::Transaction => {
Expand Down Expand Up @@ -259,6 +267,38 @@ impl<'a> Obj<'a> {
}
}

#[cfg(test)]
impl<'a> core::fmt::Debug for ParsedObj<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("ParsedObj");
debug_struct.field("tag", &self.tag);

// Safety: We're matching on the tag to ensure we access the correct
// union variant. The union is guaranteed to be initialized with the
// correct variant and that variant won't change during the lifetime
// of the object.
match self.tag {
Tag::Transaction => unsafe {
debug_struct.field("obj", &self.obj.tx);
},
Tag::Message => unsafe {
debug_struct.field("obj", &self.obj.msg);
},
Tag::Jwt => unsafe {
debug_struct.field("obj", &self.obj.jwt);
},
Tag::StructuredMsg => unsafe {
debug_struct.field("obj", &self.obj.structured_msg);
},
Tag::Invalid => {
debug_struct.field("obj", &"<invalid>");
}
}

debug_struct.finish()
}
}

#[cfg(test)]
mod test {
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -793,4 +833,17 @@ mod test {
msg.read(&bytes).unwrap();
ParsedObj::validate(&mut msg).unwrap();
}

#[test]
fn test_swap_tx() {
let input = "000000000104009ef3889fd070159edcd8ef88a0ec87cea1592c83000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000302000000060002169ef3889fd070159edcd8ef88a0ec87cea1592c830100000000000027100003167c5f674a8fd08efa61dd9b11121e046dd2c892730a756e6976322d636f72650300000000000000000103167c5f674a8fd08efa61dd9b11121e046dd2c892730a756e6976322d636f7265168c5e2f8d25627d6edebeb6d10fa3300f5acc8441086c6f6e67636f696e086c6f6e67636f696e0300000000000000000103167c5f674a8fd08efa61dd9b11121e046dd2c892730a756e6976322d636f7265168c5e2f8d25627d6edebeb6d10fa3300f5acc8441086c6f6e67636f696e086c6f6e67636f696e0300000000000000000102169ef3889fd070159edcd8ef88a0ec87cea1592c83168c5e2f8d25627d6edebeb6d10fa3300f5acc8441086c6f6e67636f696e086c6f6e67636f696e030000000000000000010316402da2c079e5d31d58b9cfc7286d1b1eb2f7834e0f616d6d2d7661756c742d76322d303116402da2c079e5d31d58b9cfc7286d1b1eb2f7834e0a746f6b656e2d616c657804616c65780300000000011c908a02162ec1a2dc2904ebc8b408598116c75e42c51afa2617726f757465722d76656c61722d616c65782d762d312d320d737761702d68656c7065722d6100000007010000000000000000000000000000271001000000000000000000000000011c908a040c00000002016106167c5f674a8fd08efa61dd9b11121e046dd2c892730477737478016206168c5e2f8d25627d6edebeb6d10fa3300f5acc8441086c6f6e67636f696e06167c5f674a8fd08efa61dd9b11121e046dd2c8927312756e6976322d73686172652d6665652d746f0c0000000201610616402da2c079e5d31d58b9cfc7286d1b1eb2f7834e0b746f6b656e2d776c6f6e6701620616402da2c079e5d31d58b9cfc7286d1b1eb2f7834e0a746f6b656e2d616c65780c0000000101610100000000000000000000000005f5e100";
let bytes = hex::decode(input).unwrap();
let mut msg = ParsedObj::from_bytes(&bytes).unwrap();

msg.read(&bytes).unwrap();

ParsedObj::validate(&mut msg).unwrap();

std::println!("tx: {:?}", msg);
}
}
5 changes: 3 additions & 2 deletions app/rust/src/parser/parser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ pub const C32_ENCODED_ADDRS_LENGTH: usize = 48;
pub const NUM_SUPPORTED_POST_CONDITIONS: usize = 16;
pub const SIGNATURE_LEN: usize = 65;
pub const PUBKEY_LEN: usize = 33;
pub const TOKEN_TRANSFER_MEMO_LEN: usize = 34;
pub const MEMO_LEN: usize = 34;
pub const AMOUNT_LEN: usize = 8;

// A recursion limit use to control ram usage when parsing
// contract-call arguments that comes in a transaction
pub const TX_DEPTH_LIMIT: u8 = 3;
pub const TX_DEPTH_LIMIT: u8 = 8;

// Use to limit recursion when parsing nested clarity values that comes as part of a structured
// message. the limit is higher than the one use when parsing contract-args in transactions
Expand Down
Loading

0 comments on commit 858f9a0

Please sign in to comment.