Skip to content

Commit

Permalink
rlp encoding for eip1559
Browse files Browse the repository at this point in the history
  • Loading branch information
ermvrs committed Nov 26, 2024
1 parent 581e73f commit fb38cd0
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 62 deletions.
1 change: 1 addition & 0 deletions .snfoundry_cache/.prev_tests_failed
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
rosettacontracts::accounts::encoding::tests::encode_transaction
rosettacontracts_integrationtest::factory_tests::deploy_check_initials
52 changes: 52 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,55 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "alexandria_bytes"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#95d98a5182001d07673b856a356eff0e6bd05354"
dependencies = [
"alexandria_data_structures",
"alexandria_math",
]

[[package]]
name = "alexandria_data_structures"
version = "0.2.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#95d98a5182001d07673b856a356eff0e6bd05354"
dependencies = [
"alexandria_encoding",
]

[[package]]
name = "alexandria_encoding"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#95d98a5182001d07673b856a356eff0e6bd05354"
dependencies = [
"alexandria_bytes",
"alexandria_math",
"alexandria_numeric",
]

[[package]]
name = "alexandria_math"
version = "0.2.1"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#95d98a5182001d07673b856a356eff0e6bd05354"

[[package]]
name = "alexandria_numeric"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#95d98a5182001d07673b856a356eff0e6bd05354"
dependencies = [
"alexandria_math",
"alexandria_searching",
]

[[package]]
name = "alexandria_searching"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#95d98a5182001d07673b856a356eff0e6bd05354"
dependencies = [
"alexandria_data_structures",
]

[[package]]
name = "openzeppelin"
version = "0.17.0"
Expand Down Expand Up @@ -119,6 +168,9 @@ checksum = "sha256:36d93e353f42fd6b824abcd8b4b51c3f5d02c893c5f886ae81403b0368aa5
name = "rosettacontracts"
version = "0.1.0"
dependencies = [
"alexandria_data_structures",
"alexandria_encoding",
"alexandria_numeric",
"openzeppelin",
"snforge_std",
]
Expand Down
3 changes: 3 additions & 0 deletions Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ sierra = true
[dependencies]
starknet = "2.8.2"
openzeppelin = "0.17.0"
alexandria_encoding = { git = "https://github.com/keep-starknet-strange/alexandria.git" }
alexandria_data_structures = { git = "https://github.com/keep-starknet-strange/alexandria.git" }
alexandria_numeric = { git = "https://github.com/keep-starknet-strange/alexandria.git" }

[dev-dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" }
Expand Down
124 changes: 124 additions & 0 deletions src/accounts/encoding.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use starknet::{EthAddress};
use core::byte_array::{ByteArrayTrait};
use crate::utils::transaction::eip2930::{AccessListItem, AccessListItemTrait};
use crate::utils::bytes::{ByteArrayExTrait};
use alexandria_encoding::rlp::{RLPItem, RLPTrait};

#[derive(Copy, Drop, Clone, PartialEq, Serde)]
pub struct Eip1559Transaction {
chain_id: u64,
nonce: u64,
max_priority_fee_per_gas: u128,
max_fee_per_gas: u128,
gas_limit: u64,
to: EthAddress,
value: u256,
input: Span<u8>, // u256 or felt?
access_list: Span<AccessListItem>,
}

pub fn rlp_encode_eip1559(tx: Eip1559Transaction) -> Span<u8> {
let chain_id = RLPItem::String(deserialize_bytes_non_zeroes(tx.chain_id.into(),8));
let nonce = RLPItem::String(deserialize_bytes_non_zeroes(tx.nonce.into(),8));
let max_priority_fee_per_gas = RLPItem::String(deserialize_bytes_non_zeroes(tx.max_priority_fee_per_gas.into(), 16));
let max_fee_per_gas = RLPItem::String(deserialize_bytes_non_zeroes(tx.max_fee_per_gas.into(), 16));
let gas_limit = RLPItem::String(deserialize_bytes_non_zeroes(tx.gas_limit.into(), 8));
let to = RLPItem::String(deserialize_bytes(tx.to.into(), 20));
let value = RLPItem::String(deserialize_bytes_non_zeroes(tx.value.try_into().unwrap(), 32));
let input = RLPItem::String(tx.input);

let mut access_arr = array![];
let mut access_list_items = tx.access_list;
loop {
match access_list_items.pop_front() {
Option::None => { break; },
Option::Some(item) => {
access_arr.append(item.to_rlp_items());
}
};
};

let access_list = RLPItem::List(access_arr.span());


let mut rlp_inputs = RLPItem::List(array![chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, input, access_list].span());
let mut encoded_tx = array![2_u8];

encoded_tx.append_span(RLPTrait::encode(array![rlp_inputs].span()).unwrap());
encoded_tx.span()
}

pub fn deserialize_bytes(value: felt252, len: usize) -> Span<u8> {
let mut ba = Default::default();
ba.append_word(value, len);
ba.into_bytes()
}

pub fn deserialize_bytes_non_zeroes(value: felt252, len: usize) -> Span<u8> {
let mut ba = Default::default();
ba.append_word(value, len);
ba.into_bytes_without_initial_zeroes()
}


#[cfg(test)]
mod tests {
use crate::accounts::encoding::{Eip1559Transaction, rlp_encode_eip1559, deserialize_bytes_non_zeroes};
#[test]
fn encode_transaction() {
let tx = Eip1559Transaction {
chain_id: 2933,
nonce: 1,
max_priority_fee_per_gas: 1000000000,
max_fee_per_gas: 1000000000,
gas_limit: 21000,
to: 0x11655f4Ee2A5B66F9DCbe758e9FcdCd3eBF95eE5.try_into().unwrap(),
value: 0x0,
input: array![0xAB, 0xCA, 0xBC].span(),
access_list: array![].span()
};

let encoded = rlp_encode_eip1559(tx);
assert_eq!(encoded.len(), 92);
assert_eq!(*encoded.at(0), 0x02);
assert_eq!(*encoded.at(1), 0xEC);
assert_eq!(*encoded.at(2), 0x82);
assert_eq!(*encoded.at(3), 0x0B);
assert_eq!(*encoded.at(4), 0x75);
}

#[test]
fn test_tx_bytes_decoding() {
let value = 0x567312;

let decoded_value: Span<u8> = deserialize_bytes_non_zeroes(value, 8);

assert_eq!(*decoded_value.at(0), 0x56);
assert_eq!(*decoded_value.at(1), 0x73);
assert_eq!(*decoded_value.at(2), 0x12);
}

#[test]
fn test_tx_bytes_decoding_initial_zeroes() {
let value = 0x00567312;

let decoded_value: Span<u8> = deserialize_bytes_non_zeroes(value, 8);

assert_eq!(*decoded_value.at(0), 0x56);
assert_eq!(*decoded_value.at(1), 0x73);
assert_eq!(*decoded_value.at(2), 0x12);
}

#[test]
fn test_tx_bytes_decoding_zeroes() {
let value = 0x005673120055;

let decoded_value: Span<u8> = deserialize_bytes_non_zeroes(value, 8);

assert_eq!(*decoded_value.at(0), 0x56);
assert_eq!(*decoded_value.at(1), 0x73);
assert_eq!(*decoded_value.at(2), 0x12);
assert_eq!(*decoded_value.at(3), 0x00);
assert_eq!(*decoded_value.at(4), 0x55);
}
}
2 changes: 1 addition & 1 deletion src/accounts/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::utils::traits::SpanDefault;
use crate::errors::{EthTransactionError, RLPError, RLPErrorTrait};
use crate::utils::bytes::{U8SpanExTrait};
use starknet::eth_signature::{verify_eth_signature, public_key_point_to_eth_address};
use core::num::traits::SaturatingSub;

#[derive(Copy, Drop, Serde)]
pub struct EthSignature {
Expand Down Expand Up @@ -131,6 +130,7 @@ pub fn decode_encoded_eip1559_transaction(ref encoded_tx: Span<u8>) -> Result<Ro
Result::Ok(tx)
}


pub fn verify_transaction(tx: RosettanetTransaction, signature: RosettanetSignature) -> bool {
true
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod lens;
pub mod accounts {
pub mod base;
pub mod utils;
pub mod encoding;
}

pub mod utils;
Expand Down
19 changes: 18 additions & 1 deletion src/utils/bytes.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::cmp::min;
use core::keccak::{cairo_keccak};
use core::num::traits::{Zero, One, Bounded, BitSize, SaturatingAdd};
use core::traits::{BitAnd};
use crate::utils::constants::{POW_2, POW_256_1, POW_256_REV};
use crate::utils::constants::{POW_256_1};
use crate::utils::math::{Bitshift};
use crate::utils::integer::{BytesUsedTrait, ByteSize, U256Trait};

Expand Down Expand Up @@ -526,4 +526,21 @@ pub impl ByteArrayExt of ByteArrayExTrait {
};
output.span()
}

fn into_bytes_without_initial_zeroes(self: ByteArray) -> Span<u8> {
let mut output: Array<u8> = Default::default();
let mut firstValue = false;
for i in 0..self.len() {
let val = self[i];
if(val == 0 && !firstValue) {
continue;
}
output.append(val);
if(!firstValue) {
firstValue = true;
continue;
};
};
output.span()
}
}
2 changes: 1 addition & 1 deletion src/utils/helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use core::pedersen::PedersenTrait;
use core::starknet::{EthAddress, ContractAddress, ClassHash};
use core::traits::TryInto;
use crate::utils::constants::{CONTRACT_ADDRESS_PREFIX, MAX_ADDRESS};
use crate::utils::constants::{POW_2, POW_256_1, POW_256_REV};
use crate::utils::constants::{POW_2, POW_256_REV};
use crate::utils::array::{ArrayExtTrait};
use crate::utils::{U256TryIntoContractAddress, EthAddressIntoU256, BoolIntoNumeric};

Expand Down
75 changes: 16 additions & 59 deletions src/utils/transaction/eip2930.cairo
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use core::starknet::EthAddress;
use crate::errors::{EthTransactionError, RLPError, RLPErrorTrait};
use crate::utils::transaction::common::TxKind;
use crate::utils::rlp::{RLPItem, RLPHelpersTrait};
use alexandria_encoding::rlp::{RLPItem, RLPTrait};
use crate::utils::traits::SpanDefault;
use crate::accounts::encoding::{deserialize_bytes, deserialize_bytes_non_zeroes};


#[derive(Copy, Drop, Serde, PartialEq, Debug)]
Expand All @@ -23,6 +24,20 @@ pub impl AccessListItemImpl of AccessListItemTrait {

storage_keys_arr.span()
}

fn to_rlp_items(self: @AccessListItem) -> RLPItem {
let AccessListItem { ethereum_address, mut storage_keys } = *self;

let mut storage_keys_arr = array![];
for storage_key in storage_keys {
storage_keys_arr.append(RLPItem::String(deserialize_bytes((*storage_key).try_into().unwrap(), 32)));
};

let addr = RLPItem::String(deserialize_bytes(ethereum_address.into(), 20));
let keys = RLPItem::List(storage_keys_arr.span());

RLPItem::List(array![addr, keys].span())
}
}


Expand Down Expand Up @@ -68,61 +83,3 @@ pub struct TxEip2930 {
/// input data of the message call;
pub input: Span<u8>,
}


#[generate_trait]
pub impl _impl of TxEip2930Trait {
/// Decodes the RLP-encoded fields into a TxEip2930 struct.
///
/// # Arguments
///
/// * `data` - A span of RLPItems containing the encoded transaction fields
///
/// # Returns
///
/// A Result containing either the decoded TxEip2930 struct or an EthTransactionError
fn decode_fields(ref data: Span<RLPItem>) -> Result<TxEip2930, EthTransactionError> {
let boxed_fields = data
.multi_pop_front::<8>()
.ok_or(EthTransactionError::RLPError(RLPError::InputTooShort))?;
let [
chain_id_encoded,
nonce_encoded,
gas_price_encoded,
gas_limit_encoded,
to_encoded,
value_encoded,
input_encoded,
access_list_encoded
] =
(*boxed_fields)
.unbox();

let chain_id = chain_id_encoded.parse_u64_from_string().map_err()?;
let nonce = nonce_encoded.parse_u64_from_string().map_err()?;
let gas_price = gas_price_encoded.parse_u128_from_string().map_err()?;
let gas_limit = gas_limit_encoded.parse_u64_from_string().map_err()?;
let to = to_encoded.try_parse_address_from_string().map_err()?;
let value = value_encoded.parse_u256_from_string().map_err()?;
let input = input_encoded.parse_bytes_from_string().map_err()?;
let access_list = access_list_encoded.parse_access_list().map_err()?;

let txkind_to = match to {
Option::Some(to) => { TxKind::Call(to) },
Option::None => { TxKind::Create }
};

Result::Ok(
TxEip2930 {
chain_id: chain_id,
nonce,
gas_price,
gas_limit,
input,
access_list,
to: txkind_to,
value,
}
)
}
}

0 comments on commit fb38cd0

Please sign in to comment.