From f050ea8ed5d7aa2649acd76e4b1dd3cef51daf2f Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Wed, 26 Jul 2023 10:28:19 +0100 Subject: [PATCH 1/7] EVM: Rename ethereum/signatures to ethereum/tx_common In a follow-up commit a transaction signature will be moved to a separate file. --- src/kernel_evm/ethereum/src/lib.rs | 2 +- .../src/{signatures.rs => tx_common.rs} | 0 src/kernel_evm/evm_execution/src/lib.rs | 17 ++++++++--------- src/kernel_evm/kernel/src/apply.rs | 2 +- src/kernel_evm/kernel/src/block.rs | 2 +- src/kernel_evm/kernel/src/blueprint.rs | 2 +- src/kernel_evm/kernel/src/error.rs | 2 +- src/kernel_evm/kernel/src/inbox.rs | 2 +- src/kernel_evm/kernel/src/parsing.rs | 2 +- src/kernel_evm/kernel/src/tick_model.rs | 2 +- 10 files changed, 16 insertions(+), 17 deletions(-) rename src/kernel_evm/ethereum/src/{signatures.rs => tx_common.rs} (100%) diff --git a/src/kernel_evm/ethereum/src/lib.rs b/src/kernel_evm/ethereum/src/lib.rs index 07ea24a25a5e..ab5f89f4ec5a 100644 --- a/src/kernel_evm/ethereum/src/lib.rs +++ b/src/kernel_evm/ethereum/src/lib.rs @@ -5,6 +5,6 @@ pub mod block; pub mod eth_gen; pub mod rlp_helpers; -pub mod signatures; pub mod transaction; +pub mod tx_common; pub mod wei; diff --git a/src/kernel_evm/ethereum/src/signatures.rs b/src/kernel_evm/ethereum/src/tx_common.rs similarity index 100% rename from src/kernel_evm/ethereum/src/signatures.rs rename to src/kernel_evm/ethereum/src/tx_common.rs diff --git a/src/kernel_evm/evm_execution/src/lib.rs b/src/kernel_evm/evm_execution/src/lib.rs index a8e74453639e..e97e7ad9ff23 100644 --- a/src/kernel_evm/evm_execution/src/lib.rs +++ b/src/kernel_evm/evm_execution/src/lib.rs @@ -171,7 +171,7 @@ mod test { use host::runtime::Runtime; use primitive_types::{H160, H256}; use std::str::FromStr; - use tezos_ethereum::signatures::EthereumTransactionCommon; + use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_smart_rollup_mock::MockHost; // The compiled initialization code for the Ethereum demo contract given @@ -643,7 +643,7 @@ mod test { #[test] //this is based on https://eips.ethereum.org/EIPS/eip-155 fn test_signatures() { - let (sk, _address) = tezos_ethereum::signatures::string_to_sk_and_address( + let (sk, _address) = tezos_ethereum::tx_common::string_to_sk_and_address( "4646464646464646464646464646464646464646464646464646464646464646" .to_string(), ) @@ -685,7 +685,7 @@ mod test { ]; test_list.iter().fold((), |_, (s, ea)| { let (_, a) = - tezos_ethereum::signatures::string_to_sk_and_address(s.to_string()) + tezos_ethereum::tx_common::string_to_sk_and_address(s.to_string()) .unwrap(); let value: [u8; 20] = hex::decode(ea).unwrap().try_into().unwrap(); let ea = value.into(); @@ -695,12 +695,11 @@ mod test { #[test] fn test_caller_classic() { - let (_sk, address_from_sk) = - tezos_ethereum::signatures::string_to_sk_and_address( - "4646464646464646464646464646464646464646464646464646464646464646" - .to_string(), - ) - .unwrap(); + let (_sk, address_from_sk) = tezos_ethereum::tx_common::string_to_sk_and_address( + "4646464646464646464646464646464646464646464646464646464646464646" + .to_string(), + ) + .unwrap(); let encoded = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83".to_string(); let transaction = EthereumTransactionCommon::from_rlp(encoded).unwrap(); diff --git a/src/kernel_evm/kernel/src/apply.rs b/src/kernel_evm/kernel/src/apply.rs index af2d3d5dac06..f8c8209a87ff 100644 --- a/src/kernel_evm/kernel/src/apply.rs +++ b/src/kernel_evm/kernel/src/apply.rs @@ -11,8 +11,8 @@ use evm_execution::precompiles::PrecompileBTreeMap; use evm_execution::run_transaction; use primitive_types::{H160, H256, U256}; use tezos_ethereum::block::BlockConstants; -use tezos_ethereum::signatures::EthereumTransactionCommon; use tezos_ethereum::transaction::TransactionHash; +use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; use tezos_smart_rollup_host::runtime::Runtime; diff --git a/src/kernel_evm/kernel/src/block.rs b/src/kernel_evm/kernel/src/block.rs index 019e6e2e72d5..498135e360e8 100644 --- a/src/kernel_evm/kernel/src/block.rs +++ b/src/kernel_evm/kernel/src/block.rs @@ -188,10 +188,10 @@ mod tests { use std::collections::VecDeque; use std::ops::Rem; use std::str::FromStr; - use tezos_ethereum::signatures::EthereumTransactionCommon; use tezos_ethereum::transaction::{ TransactionHash, TransactionStatus, TRANSACTION_HASH_SIZE, }; + use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_smart_rollup_mock::MockHost; fn blueprint(transactions: Vec) -> QueueElement { diff --git a/src/kernel_evm/kernel/src/blueprint.rs b/src/kernel_evm/kernel/src/blueprint.rs index ca4a9d3d0b1f..829bea9679b4 100644 --- a/src/kernel_evm/kernel/src/blueprint.rs +++ b/src/kernel_evm/kernel/src/blueprint.rs @@ -79,7 +79,7 @@ mod tests { use crate::inbox::TransactionContent::Ethereum; use primitive_types::{H160, H256, U256}; use tezos_ethereum::{ - signatures::EthereumTransactionCommon, transaction::TRANSACTION_HASH_SIZE, + transaction::TRANSACTION_HASH_SIZE, tx_common::EthereumTransactionCommon, }; fn address_from_str(s: &str) -> Option { diff --git a/src/kernel_evm/kernel/src/error.rs b/src/kernel_evm/kernel/src/error.rs index 88888046eb0a..3424289d7934 100644 --- a/src/kernel_evm/kernel/src/error.rs +++ b/src/kernel_evm/kernel/src/error.rs @@ -7,7 +7,7 @@ use core::str::Utf8Error; use evm_execution::{DurableStorageError, EthereumError}; use primitive_types::U256; use rlp::DecoderError; -use tezos_ethereum::signatures::SigError; +use tezos_ethereum::tx_common::SigError; use tezos_smart_rollup_host::path::PathError; use tezos_smart_rollup_host::runtime::RuntimeError; use thiserror::Error; diff --git a/src/kernel_evm/kernel/src/inbox.rs b/src/kernel_evm/kernel/src/inbox.rs index 13f53d538f18..5ef1404b3db9 100644 --- a/src/kernel_evm/kernel/src/inbox.rs +++ b/src/kernel_evm/kernel/src/inbox.rs @@ -21,8 +21,8 @@ use rlp::{Decodable, DecoderError, Encodable}; use sha3::{Digest, Keccak256}; use tezos_crypto_rs::hash::ContractKt1Hash; use tezos_ethereum::rlp_helpers::{decode_field, decode_tx_hash, next}; -use tezos_ethereum::signatures::EthereumTransactionCommon; use tezos_ethereum::transaction::TransactionHash; +use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; use tezos_smart_rollup_host::runtime::Runtime; diff --git a/src/kernel_evm/kernel/src/parsing.rs b/src/kernel_evm/kernel/src/parsing.rs index d9c5e4eae70c..8818d8a5a7f5 100644 --- a/src/kernel_evm/kernel/src/parsing.rs +++ b/src/kernel_evm/kernel/src/parsing.rs @@ -9,7 +9,7 @@ use crate::inbox::{Deposit, KernelUpgrade, Transaction, TransactionContent}; use primitive_types::{H160, U256}; use tezos_crypto_rs::hash::ContractKt1Hash; use tezos_ethereum::{ - signatures::EthereumTransactionCommon, transaction::TransactionHash, + transaction::TransactionHash, tx_common::EthereumTransactionCommon, wei::eth_from_mutez, }; use tezos_evm_logging::{log, Level::*}; diff --git a/src/kernel_evm/kernel/src/tick_model.rs b/src/kernel_evm/kernel/src/tick_model.rs index bfb371e6c4e3..300e5212c939 100644 --- a/src/kernel_evm/kernel/src/tick_model.rs +++ b/src/kernel_evm/kernel/src/tick_model.rs @@ -4,7 +4,7 @@ use crate::{apply::TransactionReceiptInfo, inbox::Transaction}; use evm::Config; -use tezos_ethereum::signatures::EthereumTransactionCommon; +use tezos_ethereum::tx_common::EthereumTransactionCommon; /////////////////////////////////////////////////////////////////////////////// /// TICK MODEL CONSTANTS -- GitLab From 2b2b26d72ff433026f26c18d0779ec9ac5cd0dd4 Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Wed, 26 Jul 2023 10:33:41 +0100 Subject: [PATCH 2/7] EVM: Remove unused EthereumTransactionType --- src/kernel_evm/ethereum/src/tx_common.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/kernel_evm/ethereum/src/tx_common.rs b/src/kernel_evm/ethereum/src/tx_common.rs index 3d639b661c38..ca37f19d3b7a 100644 --- a/src/kernel_evm/ethereum/src/tx_common.rs +++ b/src/kernel_evm/ethereum/src/tx_common.rs @@ -78,18 +78,6 @@ pub fn string_to_sk_and_address(s: String) -> Result<(SecretKey, H160), SigError Ok((sk, value.into())) } -/// the type of a transaction -#[derive(Debug, PartialEq, Eq, Clone)] - -pub enum EthereumTransactionType { - /// transfer - EthereumTransfer, - ///create - EthereumCreate, - /// call - EthereumCall, -} - /// Data common to all Ethereum transaction types #[derive(Debug, PartialEq, Eq, Clone)] -- GitLab From 97cca83432da87e98e799b34ade47d425621bf81 Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Wed, 26 Jul 2023 10:36:13 +0100 Subject: [PATCH 3/7] EVM: Add better comment for EthereumTransactionCommon --- src/kernel_evm/ethereum/src/tx_common.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/kernel_evm/ethereum/src/tx_common.rs b/src/kernel_evm/ethereum/src/tx_common.rs index ca37f19d3b7a..9607b14a4d8b 100644 --- a/src/kernel_evm/ethereum/src/tx_common.rs +++ b/src/kernel_evm/ethereum/src/tx_common.rs @@ -78,9 +78,12 @@ pub fn string_to_sk_and_address(s: String) -> Result<(SecretKey, H160), SigError Ok((sk, value.into())) } -/// Data common to all Ethereum transaction types +/// Data common for all kind of Ethereum transactions +/// (transfers, contract creation and contract invocation). +/// All transaction versions (Legacy, EIP-2930 and EIP-1559) +/// are parsed to this common type. +/// This type is common for both signed and unsigned transactions as well. #[derive(Debug, PartialEq, Eq, Clone)] - pub struct EthereumTransactionCommon { /// the id of the chain /// see `` for values @@ -120,7 +123,7 @@ pub struct EthereumTransactionCommon { impl EthereumTransactionCommon { /// Extracts the Keccak encoding of a message from an EthereumTransactionCommon - pub fn message(&self) -> Message { + fn message(&self) -> Message { let to_sign = EthereumTransactionCommon { v: self.chain_id, r: H256::zero(), -- GitLab From 5601431d5c6ecf1b17b5d5a5452be5f0ac25edea Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Wed, 26 Jul 2023 11:17:04 +0100 Subject: [PATCH 4/7] EVM: Fix support of EIP-155 transactions. Previously, signed transactions with v < 36 were considered valid, which doesn't conform EIP-155. With EIP-155 a tx has to have a chain_id and v > 36, now this semantic supported correctly. --- src/kernel_evm/ethereum/src/tx_common.rs | 84 +++++++++++++----------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/src/kernel_evm/ethereum/src/tx_common.rs b/src/kernel_evm/ethereum/src/tx_common.rs index 9607b14a4d8b..d36812b766da 100644 --- a/src/kernel_evm/ethereum/src/tx_common.rs +++ b/src/kernel_evm/ethereum/src/tx_common.rs @@ -187,9 +187,9 @@ impl EthereumTransactionCommon { /// compute v from parity and chain_id fn compute_v(&self, parity: u8) -> Option { - if self.chain_id == U256::zero() { - // parity is 0 or 1 - Some((27 + parity).into()) + if self.chain_id.is_zero() { + // we don't support transactions without a chain_id + None } else { let chain_id_encoding = self .chain_id @@ -276,11 +276,25 @@ impl Decodable for EthereumTransactionCommon { let s: H256 = decode_field_h256(&next(&mut it)?, "s")?; // in a rlp encoded unsigned eip-155 transaction, v is used to store the chainid // in a rlp encoded signed eip-155 transaction, v is {0,1} + CHAIN_ID * 2 + 35 - let chain_id: U256 = if v > U256::from(35) { - (v - U256::from(35)) / U256::from(2) + + let is_unsigned = r == H256::zero() && s == H256::zero(); + let chain_id = if is_unsigned { + // in a rlp encoded unsigned eip-155 transaction, v is used to store the chainid + Ok(v) } else { - v - }; + // It's a **signed eip-155 transaction** with 9 fields, + // it means v has to be {0,1} + CHAIN_ID * 2 + 35 according to + // https://eips.ethereum.org/EIPS/eip-155 + // v > 36 (not v > 35) because we support only chain_id which is strictly greater than 0 + if v > U256::from(36) { + Ok((v - 35) / 2) + } else { + Err(DecoderError::Custom( + "v has to be greater than 36 for a signed EIP-155 transaction", + )) + } + }?; + Ok(Self { chain_id, nonce, @@ -1053,18 +1067,28 @@ mod test { } #[test] - fn test_rlp_decode_encode() { - let strings = - ["f86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae52c1ef3300f44df0bcfd1341c232ed6134672b16e35699ae3f5fe2493379a023d23d2955a239dd6f61c4e8b2678d174356ff424eac53da53e17706c43ef871".to_string(), - "f86a8302ae2a7b82f618948e998a00253cb1747679ac25e69a8d870b52d8898802c68af0bb140000802da0cd2d976eb691dc16a397462c828975f0b836e1b448ecb8f00d9765cf5032cecca066247d13fc2b65fd70a2931b5897fff4b3079e9587e69ac8a0036c99eb5ea927".to_string()]; - - strings.iter().fold((), |_, str| { - let e = EthereumTransactionCommon::from_rlp(str.clone()).unwrap(); - let encoded = e.rlp_bytes(); - assert_eq!(hex::encode(&encoded), *str); - }); + fn test_rlp_decode_fails_without_chain_id() { + // This transaction is signed but its v doesn't equal to CHAIN_ID * 2 + 35 + {0, 1} + // but equal to 27/28 as in "old" (before https://eips.ethereum.org/EIPS/eip-155) + // six fields encoding + let malformed_tx = "f86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae52c1ef3300f44df0bcfd1341c232ed6134672b16e35699ae3f5fe2493379a023d23d2955a239dd6f61c4e8b2678d174356ff424eac53da53e17706c43ef871".to_string(); + let e = EthereumTransactionCommon::from_rlp(malformed_tx); + assert_eq!( + e.err(), + Some(DecoderError::Custom( + "v has to be greater than 36 for a signed EIP-155 transaction", + )) + ) } + #[test] + fn test_rlp_decode_encode_with_valid_chain_id() { + let wellformed_tx = + "f86a8302ae2a7b82f618948e998a00253cb1747679ac25e69a8d870b52d8898802c68af0bb140000802da0cd2d976eb691dc16a397462c828975f0b836e1b448ecb8f00d9765cf5032cecca066247d13fc2b65fd70a2931b5897fff4b3079e9587e69ac8a0036c99eb5ea927".to_string(); + let e = EthereumTransactionCommon::from_rlp(wellformed_tx.clone()).unwrap(); + let encoded = e.rlp_bytes(); + assert_eq!(hex::encode(encoded), wellformed_tx); + } #[test] fn test_decoding_not_eip_155_fails_gracefully() { // decoding of a transaction that is not eip 155, ie v = 28 / 27 @@ -1082,31 +1106,17 @@ mod test { // setup let signed_tx = "f901cc8086010000000000830250008080b90178608060405234801561001057600080fd5b50602a600081905550610150806100286000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea26469706673582212204d6c1853cec27824f5dbf8bcd0994714258d22fc0e0dc8a2460d87c70e3e57a564736f6c634300081200331ca06d851632958801b6919ba534b4b1feb1bdfaabd0d42890bce200a11ac735d58da0219b058d7169d7a4839c5cdd555b0820b545797365287a81ba409419912de7b1"; - let r = string_to_h256_unsafe( - "6d851632958801b6919ba534b4b1feb1bdfaabd0d42890bce200a11ac735d58d", - ); - let s = string_to_h256_unsafe( - "219b058d7169d7a4839c5cdd555b0820b545797365287a81ba409419912de7b1", - ); - // act let tx = hex::decode(signed_tx).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // sanity check - assert!(decoded.is_ok(), "testing the decoding went ok"); - let decoded_transaction = decoded.unwrap(); - assert_eq!(U256::from(28), decoded_transaction.v, "testing v"); - assert_eq!(r, decoded_transaction.r, "testing r"); - assert_eq!(s, decoded_transaction.s, "testing s"); - - // check signature fails gracefully - assert!( - decoded_transaction.signature().is_err(), - "testing signature" + assert_eq!( + decoded.err(), + Some(DecoderError::Custom( + "v has to be greater than 36 for a signed EIP-155 transaction", + )) ); - assert!(decoded_transaction.caller().is_err(), "testing caller"); } #[test] -- GitLab From 7aaabfda21c9b5e4ec19a5683cee5692c969ed35 Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Thu, 20 Jul 2023 12:49:50 +0100 Subject: [PATCH 5/7] EVM: Introduce TxSignature This is done in order to better represent the fact if a tx is signed or not. --- src/kernel_evm/ethereum/src/lib.rs | 1 + src/kernel_evm/ethereum/src/transaction.rs | 43 +- src/kernel_evm/ethereum/src/tx_common.rs | 418 ++++++++---------- src/kernel_evm/ethereum/src/tx_signature.rs | 186 ++++++++ src/kernel_evm/evm_execution/src/lib.rs | 5 +- src/kernel_evm/kernel/src/apply.rs | 31 +- src/kernel_evm/kernel/src/block.rs | 13 +- .../kernel/src/block_in_progress.rs | 23 +- src/kernel_evm/kernel/src/blueprint.rs | 6 +- 9 files changed, 416 insertions(+), 310 deletions(-) create mode 100644 src/kernel_evm/ethereum/src/tx_signature.rs diff --git a/src/kernel_evm/ethereum/src/lib.rs b/src/kernel_evm/ethereum/src/lib.rs index ab5f89f4ec5a..84c473e02d13 100644 --- a/src/kernel_evm/ethereum/src/lib.rs +++ b/src/kernel_evm/ethereum/src/lib.rs @@ -7,4 +7,5 @@ pub mod eth_gen; pub mod rlp_helpers; pub mod transaction; pub mod tx_common; +pub mod tx_signature; pub mod wei; diff --git a/src/kernel_evm/ethereum/src/transaction.rs b/src/kernel_evm/ethereum/src/transaction.rs index cf7b990c06af..590b8331075b 100644 --- a/src/kernel_evm/ethereum/src/transaction.rs +++ b/src/kernel_evm/ethereum/src/transaction.rs @@ -2,7 +2,10 @@ // // SPDX-License-Identifier: MIT -use crate::rlp_helpers::*; +use crate::{ + rlp_helpers::*, + tx_signature::{rlp_append_opt, rlp_decode_opt, TxSignature}, +}; use primitive_types::{H160, H256, U256}; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -191,7 +194,7 @@ impl TryFrom<&[u8]> for TransactionReceipt { } } -/// Transaction receipt, https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyhash +/// Transaction object, https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyhash /// There a lot of redundancy between a transaction object and a transaction /// receipt. In fact, transaction objects should not be stored in the kernel /// but rather in the EVM node. Duplicating the code instead of sharing fields @@ -221,12 +224,8 @@ pub struct TransactionObject { pub index: u32, /// Value transferred in Wei. pub value: U256, - /// ECDSA recovery id - pub v: U256, - /// ECDSA signature r - pub r: H256, - /// ECDSA signature s - pub s: H256, + /// ECDSA signature + pub signature: Option, } impl Decodable for TransactionObject { @@ -246,9 +245,7 @@ impl Decodable for TransactionObject { let to: Option = decode_option(&next(&mut it)?, "to")?; let index: u32 = decode_field(&next(&mut it)?, "index")?; let value: U256 = decode_field_u256_le(&next(&mut it)?, "value")?; - let v: U256 = decode_field_u256_le(&next(&mut it)?, "v")?; - let r: H256 = decode_field_h256(&next(&mut it)?, "r")?; - let s: H256 = decode_field_h256(&next(&mut it)?, "s")?; + let signature = rlp_decode_opt(&mut it)?; Ok(Self { block_hash, block_number, @@ -261,9 +258,7 @@ impl Decodable for TransactionObject { to, index, value, - v, - r, - s, + signature, }) } else { Err(DecoderError::RlpIncorrectListLen) @@ -291,9 +286,7 @@ impl Encodable for TransactionObject { }; stream.append(&self.index); append_u256_le(stream, self.value); - append_u256_le(stream, self.v); - stream.append(&self.r); - stream.append(&self.s); + rlp_append_opt(&self.signature, stream); } } @@ -372,13 +365,21 @@ mod test { to: Some(address_of_str("3635353535353535353535353535353535353536")), index: 15u32, value: U256::from(0), - v: U256::from(1337), - r: H256::from_low_u64_be(0), - s: H256::from_low_u64_be(1), + signature: Some( + TxSignature::new( + U256::from(1337), + H256::from_low_u64_be(1), + H256::from_low_u64_be(2), + ) + .unwrap(), + ), }; object_encoding_roundtrip(v.clone()); - let v1 = TransactionObject { to: None, ..v }; + let v1 = TransactionObject { + to: None, + ..v.clone() + }; object_encoding_roundtrip(v1); let v2 = TransactionObject { diff --git a/src/kernel_evm/ethereum/src/tx_common.rs b/src/kernel_evm/ethereum/src/tx_common.rs index d36812b766da..48beb29ffd15 100644 --- a/src/kernel_evm/ethereum/src/tx_common.rs +++ b/src/kernel_evm/ethereum/src/tx_common.rs @@ -12,29 +12,20 @@ use std::array::TryFromSliceError; use hex::FromHexError; -use libsecp256k1::{ - curve::Scalar, recover, sign, verify, Message, PublicKey, RecoveryId, SecretKey, - Signature, -}; +use libsecp256k1::{recover, sign, Message, PublicKey, RecoveryId, SecretKey, Signature}; use primitive_types::{H160, H256, U256}; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use sha3::{Digest, Keccak256}; use thiserror::Error; -use crate::rlp_helpers::{ - append_h256, append_option, append_vec, decode_field, decode_field_h256, - decode_option, next, +use crate::{ + rlp_helpers::{ + append_h256, append_option, append_vec, decode_field, decode_field_h256, + decode_option, next, + }, + tx_signature::{TxSigError, TxSignature}, }; -#[derive(Error, Debug, PartialEq)] -pub enum ParityError { - #[error("Couldn't reconstruct V from chain_id: {0}")] - ChainId(U256), - - #[error("Couldn't reconstruct parity from V: {0}")] - V(U256), -} - #[derive(Error, Debug, PartialEq)] pub enum SigError { #[error("Error reading a hex string: {0}")] @@ -49,14 +40,11 @@ pub enum SigError { #[error("Error manipulating ECDSA key: {0}")] ECDSAError(libsecp256k1::Error), - #[error("Error recomputing parity of signature: {0}")] - Parity(ParityError), -} + #[error("Signature error: {0}")] + TxSigError(TxSigError), -impl From for SigError { - fn from(e: libsecp256k1::Error) -> Self { - Self::ECDSAError(e) - } + #[error("Transaction doesn't have a signature")] + UnsignedTransactionError, } impl From for SigError { @@ -65,6 +53,18 @@ impl From for SigError { } } +impl From for SigError { + fn from(e: TxSigError) -> Self { + SigError::TxSigError(e) + } +} + +impl From for SigError { + fn from(e: libsecp256k1::Error) -> Self { + Self::ECDSAError(e) + } +} + /// produces address from a secret key pub fn string_to_sk_and_address(s: String) -> Result<(SecretKey, H160), SigError> { let mut data: [u8; 32] = [0u8; 32]; @@ -111,63 +111,33 @@ pub struct EthereumTransactionCommon { pub value: U256, /// the transaction data. In principle this can be large pub data: Vec, - /// Signature x-axis part of point on elliptic curve. See yellow paper, appendix F - pub r: H256, - /// Signature, See yellow paper appendix F - pub s: H256, - /// the parity (recovery id) of the signature See yellow paper appendix F - /// used to recompute chain_id is applicable + /// If transaction is unsigned then this field is None /// See encoding details in - pub v: U256, + pub signature: Option, } impl EthereumTransactionCommon { /// Extracts the Keccak encoding of a message from an EthereumTransactionCommon fn message(&self) -> Message { let to_sign = EthereumTransactionCommon { - v: self.chain_id, - r: H256::zero(), - s: H256::zero(), + signature: None, ..self.clone() }; - let bytes = to_sign.rlp_bytes(); + + let bytes = to_sign.to_rlp_bytes(); let hash: [u8; 32] = Keccak256::digest(bytes).into(); Message::parse(&hash) } - /// recompute parity from v and chain_id - fn compute_parity(&self) -> Option { - let chain_id_encoding = self - .chain_id - .checked_mul(U256::from(2))? - .checked_add(U256::from(35))?; - self.v.checked_sub(chain_id_encoding) - } - /// Extracts the signature from an EthereumTransactionCommon pub fn signature(&self) -> Result<(Signature, RecoveryId), SigError> { - // copy r to Scalar - let r: H256 = self.r; - let r1: [u8; 32] = r.into(); - let mut r = Scalar([0; 8]); - let _ = r.set_b32(&r1); - // copy s to Scalar - let s: H256 = self.s; - let s1: [u8; 32] = s.into(); - let mut s = Scalar([0; 8]); - let _ = s.set_b32(&s1); - if s.is_high() { - // if s > secp256k1n / 2 the signature is invalid - // cf EIP2 (part 2) https://eips.ethereum.org/EIPS/eip-2 - Err(SigError::ECDSAError(libsecp256k1::Error::InvalidSignature)) - } else { - // recompute parity from v and chain_id - let ri_val = self - .compute_parity() - .ok_or(SigError::Parity(ParityError::V(self.v)))?; - let ri = RecoveryId::parse(ri_val.byte(0))?; - Ok((Signature { r, s }, ri)) - } + let tx_signature = self + .signature + .as_ref() + .ok_or(SigError::UnsignedTransactionError)?; + tx_signature + .signature(self.chain_id) + .map_err(SigError::TxSigError) } /// Find the caller address from r and s of the common data @@ -185,20 +155,6 @@ impl EthereumTransactionCommon { Ok(value.into()) } - /// compute v from parity and chain_id - fn compute_v(&self, parity: u8) -> Option { - if self.chain_id.is_zero() { - // we don't support transactions without a chain_id - None - } else { - let chain_id_encoding = self - .chain_id - .checked_mul(U256::from(2))? - .checked_add(U256::from(35))?; - U256::from(parity).checked_add(chain_id_encoding) - } - } - ///produce a signed EthereumTransactionCommon. If the initial one was signed /// you should get the same thing. pub fn sign_transaction(&self, string_sk: String) -> Result { @@ -206,29 +162,14 @@ impl EthereumTransactionCommon { let sk = SecretKey::parse_slice(hex)?; let mes = self.message(); let (sig, ri) = sign(&mes, &sk); - let Signature { r, s } = sig; - let (r, s) = (H256::from(r.b32()), H256::from(s.b32())); + let signature = TxSignature::reconstruct_from_legacy(sig, ri, self.chain_id)?; - let parity: u8 = ri.into(); - let v = self - .compute_v(parity) - .ok_or(SigError::Parity(ParityError::ChainId(self.chain_id)))?; Ok(EthereumTransactionCommon { - v, - r, - s, + signature: Some(signature), ..self.clone() }) } - /// verifies the signature - pub fn verify_signature(self) -> Result { - let mes = self.message(); - let (sig, ri) = self.signature()?; - let pk = recover(&mes, &sig, &ri)?; - Ok(verify(&mes, &sig, &pk)) - } - /// Unserialize bytes as a RLP encoded legacy transaction. pub fn from_rlp_bytes( bytes: &[u8], @@ -238,17 +179,21 @@ impl EthereumTransactionCommon { } /// Unserialize an hex string as a RLP encoded legacy transaction. - pub fn from_rlp(e: String) -> Result { + pub fn from_rlp_hex(e: String) -> Result { let tx = hex::decode(e).or(Err(DecoderError::Custom("Couldn't parse hex value")))?; Self::from_rlp_bytes(&tx) } + + pub fn to_rlp_bytes(self) -> Vec { + self.rlp_bytes().into() + } } impl From for EthereumTransactionCommon { /// Decode a transaction in hex format. Unsafe, to be used only in tests : panics when fails fn from(e: String) -> Self { - EthereumTransactionCommon::from_rlp(e).unwrap() + EthereumTransactionCommon::from_rlp_hex(e).unwrap() } } @@ -274,8 +219,6 @@ impl Decodable for EthereumTransactionCommon { let v: U256 = decode_field(&next(&mut it)?, "v")?; let r: H256 = decode_field_h256(&next(&mut it)?, "r")?; let s: H256 = decode_field_h256(&next(&mut it)?, "s")?; - // in a rlp encoded unsigned eip-155 transaction, v is used to store the chainid - // in a rlp encoded signed eip-155 transaction, v is {0,1} + CHAIN_ID * 2 + 35 let is_unsigned = r == H256::zero() && s == H256::zero(); let chain_id = if is_unsigned { @@ -295,6 +238,15 @@ impl Decodable for EthereumTransactionCommon { } }?; + let signature = if is_unsigned { + None + } else { + Some( + TxSignature::new(v, r, s) + .map_err(|_| DecoderError::Custom("Invalid signature"))?, + ) + }; + Ok(Self { chain_id, nonce, @@ -303,9 +255,7 @@ impl Decodable for EthereumTransactionCommon { to, value, data, - v, - r, - s, + signature, }) } else { Err(DecoderError::RlpIncorrectListLen) @@ -325,26 +275,36 @@ impl Encodable for EthereumTransactionCommon { append_option(stream, self.to); stream.append(&self.value); append_vec(stream, self.data.clone()); - stream.append(&self.v); - append_h256(stream, self.r); - append_h256(stream, self.s); + match self.signature { + None => { + stream.append(&self.chain_id); + append_h256(stream, H256::zero()); + append_h256(stream, H256::zero()); + } + Some(ref sig) => Encodable::rlp_append(sig, stream), + } } } #[allow(clippy::from_over_into)] impl Into> for EthereumTransactionCommon { fn into(self) -> Vec { - self.rlp_bytes().to_vec() + self.to_rlp_bytes() } } // cargo test ethereum::signatures::test --features testing #[cfg(test)] mod test { + use std::ops::Neg; + use libsecp256k1::curve::Scalar; + use crate::rlp_helpers::decode_h256; + use crate::tx_signature::TxSignature; + use super::*; fn address_from_str(s: &str) -> Option { let data = &hex::decode(s).unwrap(); @@ -367,21 +327,21 @@ mod test { to: address_from_str("3535353535353535353535353535353535353535"), value: U256::from(1000000000000000000u64), data: vec![], - v: U256::from(37), - r: string_to_h256_unsafe( - "28EF61340BD939BC2195FE537567866003E1A15D3C71FF63E1590620AA636276", - ), - s: string_to_h256_unsafe( - "67CBE9D8997F761AECB703304B3800CCF555C9F3DC64214B297FB1966A3B6D83", - ), + signature: Some(TxSignature::new_unsafe( + 37, + string_to_h256_unsafe( + "28EF61340BD939BC2195FE537567866003E1A15D3C71FF63E1590620AA636276", + ), + string_to_h256_unsafe( + "67CBE9D8997F761AECB703304B3800CCF555C9F3DC64214B297FB1966A3B6D83", + ), + )), } } fn basic_eip155_transaction_unsigned() -> EthereumTransactionCommon { EthereumTransactionCommon { - v: U256::one(), - r: H256::zero(), - s: H256::zero(), + signature: None, ..basic_eip155_transaction() } } @@ -454,7 +414,7 @@ mod test { address_from_str("9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F").unwrap(); // act - let transaction = EthereumTransactionCommon::from_rlp(encoded).unwrap(); + let transaction = EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); let address = transaction.caller().unwrap(); // assert @@ -470,8 +430,7 @@ mod test { // act let tx = hex::decode(signing_data).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); assert!(decoded.is_ok(), "testing the decoding went ok"); // assert @@ -486,8 +445,7 @@ mod test { // act let tx = hex::decode(signed_tx).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // assert assert!(decoded.is_ok(), "testing the decoding went ok"); @@ -500,10 +458,10 @@ mod test { let signing_data = "ec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080"; // act - let encoded = expected_transaction.rlp_bytes(); + let encoded = expected_transaction.to_rlp_bytes(); // assert - assert_eq!(signing_data, hex::encode(&encoded)); + assert_eq!(signing_data, hex::encode(encoded)); } pub fn string_to_h256_unsafe(s: &str) -> H256 { @@ -524,7 +482,6 @@ mod test { let value = U256::from(1000000000u64); let data: Vec = hex::decode("ffff").unwrap(); let chain_id = U256::one(); - let v = U256::from(38); let r = string_to_h256_unsafe( "e9637495be4c216a833ef390b1f6798917c8a102ab165c5085cced7ca1f2eb3a", ); @@ -539,9 +496,7 @@ mod test { to, value, data, - v, - r, - s, + signature: Some(TxSignature::new_unsafe(38, r, s)), } } @@ -552,10 +507,10 @@ mod test { let expected_encoded = "f8572e8506c50218ba8304312280843b9aca0082ffff26a0e9637495be4c216a833ef390b1f6798917c8a102ab165c5085cced7ca1f2eb3aa057854e7044a8fee7bccb6a2c32c4229dd9cbacad74350789e0ce75bf40b6f713"; // act - let encoded = transaction.rlp_bytes(); + let encoded = transaction.to_rlp_bytes(); // assert - assert_eq!(expected_encoded, hex::encode(&encoded)); + assert_eq!(expected_encoded, hex::encode(encoded)); } #[test] @@ -566,8 +521,7 @@ mod test { // act let tx = hex::decode(signed_tx).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // assert assert!(decoded.is_ok()); @@ -581,10 +535,10 @@ mod test { let signed_tx = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"; // act - let encoded = expected_transaction.rlp_bytes(); + let encoded = expected_transaction.to_rlp_bytes(); // assert - assert_eq!(signed_tx, hex::encode(&encoded)); + assert_eq!(signed_tx, hex::encode(encoded)); } #[test] @@ -597,7 +551,6 @@ mod test { let to = address_from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea"); let value = U256::from(5000000000000000u64); let data: Vec = hex::decode("deace8f5000000000000000000000000000000000000000000000000000000000000a4b100000000000000000000000041bca408a6b4029b42883aeb2c25087cab76cb58000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000002357a49c7d75f600000000000000000000000000000000000000000000000000000000640b5549000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d5640000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let v = U256::from(37); let r = string_to_h256_unsafe( "25dd6c973368c45ddfc17f5148e3f468a2e3f2c51920cbe9556a64942b0ab2eb", ); @@ -612,16 +565,13 @@ mod test { to, value, data, - v, - r, - s, + signature: Some(TxSignature::new_unsafe(37, r, s)), }; let signed_data = "f90150808509502f900082520894423163e58aabec5daa3dd1130b759d24bef0f6ea8711c37937e08000b8e4deace8f5000000000000000000000000000000000000000000000000000000000000a4b100000000000000000000000041bca408a6b4029b42883aeb2c25087cab76cb58000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000002357a49c7d75f600000000000000000000000000000000000000000000000000000000640b5549000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d564000000000000000000000000000000000000000000000000000000000000000025a025dd6c973368c45ddfc17f5148e3f468a2e3f2c51920cbe9556a64942b0ab2eba031da07ce40c24b0a01f46fb2abc028b5ccd70dbd1cb330725323edc49a2a9558"; // act let tx = hex::decode(signed_data).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // assert assert_eq!(Ok(expected_transaction), decoded) @@ -635,8 +585,7 @@ mod test { // act let tx = hex::decode(signed_data).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // assert assert!(decoded.is_ok(), "testing the decoding went ok"); @@ -660,7 +609,6 @@ mod test { let to = address_from_str("ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"); let value = U256::from(760460536160301065u64); // /!\ > 2^53 -1 let data: Vec = hex::decode("3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064023c1700000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a8db2d41b89b009000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002ab0c205a56c1e000000000000000000000000000000000000000000000000000000a8db2d41b89b00900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009eb6299e4bb6669e42cb295a254c8492f67ae2c6000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let v = U256::from(37); let r = string_to_h256_unsafe( "c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45a", ); @@ -675,16 +623,13 @@ mod test { to, value, data, - v, - r, - s, + signature: Some(TxSignature::new_unsafe(37, r, s)), }; // act let signed_data = "f903732e8506c50218ba8304312294ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b880a8db2d41b89b009b903043593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064023c1700000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a8db2d41b89b009000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002ab0c205a56c1e000000000000000000000000000000000000000000000000000000a8db2d41b89b00900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009eb6299e4bb6669e42cb295a254c8492f67ae2c600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000025a0c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45aa05721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06"; let tx = hex::decode(signed_data).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // assert assert_eq!(Ok(expected_transaction), decoded); @@ -706,7 +651,6 @@ mod test { let to = address_from_str("ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"); let value = U256::from(760460536160301065u64); // /!\ > 2^53 -1 let data: Vec = hex::decode("3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064023c1700000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a8db2d41b89b009000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002ab0c205a56c1e000000000000000000000000000000000000000000000000000000a8db2d41b89b00900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009eb6299e4bb6669e42cb295a254c8492f67ae2c6000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let v = U256::from(37); let r = string_to_h256_unsafe( "c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45a", ); @@ -721,17 +665,15 @@ mod test { to, value, data, - v, - r, - s, + signature: Some(TxSignature::new_unsafe(37, r, s)), }; let signed_data = "f903732e8506c50218ba8304312294ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b880a8db2d41b89b009b903043593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064023c1700000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a8db2d41b89b009000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002ab0c205a56c1e000000000000000000000000000000000000000000000000000000a8db2d41b89b00900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009eb6299e4bb6669e42cb295a254c8492f67ae2c600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000025a0c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45aa05721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06"; // act - let encoded = expected_transaction.rlp_bytes(); + let encoded = expected_transaction.to_rlp_bytes(); // assert - assert_eq!(signed_data, hex::encode(&encoded)); + assert_eq!(signed_data, hex::encode(encoded)); } #[test] @@ -758,20 +700,21 @@ mod test { to: address_from_str("4e1b2c985d729ae6e05ef7974013eeb48f394449"), value: U256::from(1000000000u64), data: vec![], - v: U256::from(38), - r: string_to_h256_unsafe( - "bb03310570362eef497a09dd6e4ef42f56374965cfb09cc4e055a22a2eeac7ad", - ), - s: string_to_h256_unsafe( - "6053c1bd83abb30c109801844709202208736d598649afe2a53f024b61b3383f", - ), + signature: Some(TxSignature::new_unsafe( + 38, + string_to_h256_unsafe( + "bb03310570362eef497a09dd6e4ef42f56374965cfb09cc4e055a22a2eeac7ad", + ), + string_to_h256_unsafe( + "6053c1bd83abb30c109801844709202208736d598649afe2a53f024b61b3383f", + ), + )), }; let signed_data = "f869018506fc23ac0083100000944e1b2c985d729ae6e05ef7974013eeb48f394449843b9aca008026a0bb03310570362eef497a09dd6e4ef42f56374965cfb09cc4e055a22a2eeac7ada06053c1bd83abb30c109801844709202208736d598649afe2a53f024b61b3383f"; // act let tx = hex::decode(signed_data).unwrap(); - let decoder = Rlp::new(&tx); - let decoded = EthereumTransactionCommon::decode(&decoder); + let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); // assert assert_eq!(Ok(expected_transaction), decoded); @@ -800,13 +743,15 @@ mod test { to: address_from_str("4e1b2c985d729ae6e05ef7974013eeb48f394449"), value: U256::from(1000000000u64), data: vec![], - v: U256::from(38), - r: string_to_h256_unsafe( - "bb03310570362eef497a09dd6e4ef42f56374965cfb09cc4e055a22a2eeac7ad", - ), - s: string_to_h256_unsafe( - "6053c1bd83abb30c109801844709202208736d598649afe2a53f024b61b3383f", - ), + signature: Some(TxSignature::new_unsafe( + 38, + string_to_h256_unsafe( + "bb03310570362eef497a09dd6e4ef42f56374965cfb09cc4e055a22a2eeac7ad", + ), + string_to_h256_unsafe( + "6053c1bd83abb30c109801844709202208736d598649afe2a53f024b61b3383f", + ), + )), }; // assert @@ -841,21 +786,20 @@ mod test { to: address_from_str("4e1b2c985d729ae6e05ef7974013eeb48f394449"), value: U256::from(1000000000u64), data: vec![], - v: U256::one(), - r: H256::zero(), - s: H256::zero(), + signature: None, }; // act - let signed = transaction + let signature = transaction .sign_transaction( "cb9db6b5878db2fa20586e23b7f7b51c22a7c6ed0530daafc2615b116f170cd3" .to_string(), ) + .unwrap() + .signature .unwrap(); // assert - let v = U256::from(38); let r = string_to_h256_unsafe( "bb03310570362eef497a09dd6e4ef42f56374965cfb09cc4e055a22a2eeac7ad", ); @@ -863,9 +807,9 @@ mod test { "6053c1bd83abb30c109801844709202208736d598649afe2a53f024b61b3383f", ); - assert_eq!(v, signed.v, "checking v"); - assert_eq!(r, signed.r, "checking r"); - assert_eq!(s, signed.s, "checking s"); + assert_eq!(U256::from(38), signature.v(), "checking v"); + assert_eq!(&r, signature.r(), "checking r"); + assert_eq!(&s, signature.s(), "checking s"); } #[test] @@ -880,7 +824,7 @@ mod test { // Check that the derived sender address is the expected one. let encoded = "f86d80843b9aca00825208940b52d4d3be5d18a7ab5e4476a2f5382bbf2b38d888016345785d8a000080820a95a0d9ef1298c18c88604e3f08e14907a17dfa81b1dc6b37948abe189d8db5cb8a43a06fc7040a71d71d3cb74bd05ead7046b10668ad255da60391c017eea31555f156".to_string(); - let transaction = EthereumTransactionCommon::from_rlp(encoded).unwrap(); + let transaction = EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); let address = transaction.caller().unwrap(); assert_eq!(expected_address, address); @@ -906,25 +850,28 @@ mod test { // than secp256k1n/2 are now considered invalid let transaction = basic_eip155_transaction(); - + let signature = transaction.signature.unwrap(); // flip s - let s: H256 = transaction.s; - let s1: [u8; 32] = s.into(); + let s: &H256 = signature.s(); + let s1: [u8; 32] = (*s).into(); let mut scalar = Scalar([0; 8]); let _ = scalar.set_b32(&s1); let flipped_scalar = scalar.neg(); let flipped_s = H256::from_slice(&flipped_scalar.b32()); // flip v - let flipped_v = if transaction.v == U256::from(37) { - U256::from(38) + let flipped_v = if signature.v() == U256::from(37) { + 38 } else { - U256::from(37) + 37 }; let flipped_transaction = EthereumTransactionCommon { - s: flipped_s, - v: flipped_v, + signature: Some(TxSignature::new_unsafe( + flipped_v, + *signature.r(), + flipped_s, + )), ..transaction }; @@ -932,7 +879,9 @@ mod test { // and the caller should be the same, if EIP2 is not implemented // but with EIP2 s should be too big, and the transaction should be rejected assert_eq!( - Err(SigError::ECDSAError(libsecp256k1::Error::InvalidSignature)), + Err(SigError::TxSigError(TxSigError::ECDSAError( + libsecp256k1::Error::InvalidSignature + ))), flipped_transaction.caller() ) } @@ -952,13 +901,15 @@ mod test { to: address_from_str("ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"), value: U256::from(760460536160301065u64), data, - v: U256::from(37), - r: string_to_h256_unsafe( - "c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45a", - ), - s: string_to_h256_unsafe( - "5721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06", - ), + signature: Some(TxSignature::new_unsafe( + 37, + string_to_h256_unsafe( + "c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45a", + ), + string_to_h256_unsafe( + "5721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06", + ), + )), }; // check @@ -984,9 +935,7 @@ mod test { to: address_from_str("ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"), value: U256::from(760460536160301065u64), data, - v: U256::one(), - r: H256::zero(), - s: H256::zero(), + signature: None, }; // check @@ -1017,17 +966,17 @@ mod test { to: address_from_str("ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"), value: U256::from(760460536160301065u64), data, - v: U256::zero(), - r: H256::zero(), - s: H256::zero(), + signature: None, }; // act - let signed = transaction + let signature = transaction .sign_transaction( "dcdff53b4f013dbcdc717f89fe3bf4d8b10512aae282b48e01d7530470382701" .to_string(), ) + .unwrap() + .signature .unwrap(); // assert @@ -1039,9 +988,9 @@ mod test { "5721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06", ); - assert_eq!(v, signed.v, "checking v"); - assert_eq!(r, signed.r, "checking r"); - assert_eq!(s, signed.s, "checking s"); + assert_eq!(v, signature.v(), "checking v"); + assert_eq!(&r, signature.r(), "checking r"); + assert_eq!(&s, signature.s(), "checking s"); } #[test] @@ -1072,23 +1021,19 @@ mod test { // but equal to 27/28 as in "old" (before https://eips.ethereum.org/EIPS/eip-155) // six fields encoding let malformed_tx = "f86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae52c1ef3300f44df0bcfd1341c232ed6134672b16e35699ae3f5fe2493379a023d23d2955a239dd6f61c4e8b2678d174356ff424eac53da53e17706c43ef871".to_string(); - let e = EthereumTransactionCommon::from_rlp(malformed_tx); - assert_eq!( - e.err(), - Some(DecoderError::Custom( - "v has to be greater than 36 for a signed EIP-155 transaction", - )) - ) + let e = EthereumTransactionCommon::from_rlp_hex(malformed_tx); + assert!(e.is_err()); } #[test] fn test_rlp_decode_encode_with_valid_chain_id() { let wellformed_tx = "f86a8302ae2a7b82f618948e998a00253cb1747679ac25e69a8d870b52d8898802c68af0bb140000802da0cd2d976eb691dc16a397462c828975f0b836e1b448ecb8f00d9765cf5032cecca066247d13fc2b65fd70a2931b5897fff4b3079e9587e69ac8a0036c99eb5ea927".to_string(); - let e = EthereumTransactionCommon::from_rlp(wellformed_tx.clone()).unwrap(); - let encoded = e.rlp_bytes(); + let e = EthereumTransactionCommon::from_rlp_hex(wellformed_tx.clone()).unwrap(); + let encoded = e.to_rlp_bytes(); assert_eq!(hex::encode(encoded), wellformed_tx); } + #[test] fn test_decoding_not_eip_155_fails_gracefully() { // decoding of a transaction that is not eip 155, ie v = 28 / 27 @@ -1121,45 +1066,36 @@ mod test { #[test] fn test_signature_unsigned_fails_gracefully() { - // most data is not relevant here, the point is to test failure mode of signature verification - let transaction = EthereumTransactionCommon { - v: U256::one(), // parity is not consistent with a signed transaction - ..basic_eip155_transaction() - }; + let transaction = basic_eip155_transaction_unsigned(); // check signature fails gracefully - assert!(transaction.signature().is_err(), "testing invalid parity"); - } - - #[test] - fn test_signature_invalid_signature_fails_gracefully() { - // most data is not relevant here, the point is to test failure mode of signature verification - let transaction = EthereumTransactionCommon { - v: U256::from(38), // parity is consistent with a signed transaction - r: H256::zero(), // signature value is wrong - ..basic_eip155_transaction() - }; - - // sanity check assert!( - transaction.signature().is_ok(), - "testing signature is well formed" + transaction.signature().is_err(), + "testing signature for unsigned fails" ); + } - // check caller fails gracefully - assert!( - transaction.caller().is_err(), - "testing caller fails, because signature is useless" - ); + #[test] + fn test_impossible_create_invalid_sig() { + let basic = basic_eip155_transaction(); + let signature = basic.signature.unwrap(); + assert!(TxSignature::new(U256::from(38), H256::zero(), *signature.s()).is_err()); + assert!(TxSignature::new(U256::from(38), *signature.r(), H256::zero()).is_err()); } #[test] fn test_signature_invalid_parity_fails_gracefully() { + let basic = basic_eip155_transaction(); + let signature = basic.signature.unwrap(); // most data is not relevant here, the point is to test failure mode of signature verification let transaction = EthereumTransactionCommon { - v: U256::from(150), // parity is not consistent with chain_id + signature: Some(TxSignature::new_unsafe( + 150, + signature.r().to_owned(), + signature.s().to_owned(), + )), chain_id: U256::one(), - ..basic_eip155_transaction() + ..basic }; // check signature fails gracefully diff --git a/src/kernel_evm/ethereum/src/tx_signature.rs b/src/kernel_evm/ethereum/src/tx_signature.rs new file mode 100644 index 000000000000..5db6fd496402 --- /dev/null +++ b/src/kernel_evm/ethereum/src/tx_signature.rs @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: 2022-2023 TriliTech +// +// SPDX-License-Identifier: MIT + +use libsecp256k1::{curve::Scalar, RecoveryId, Signature}; +use primitive_types::{H256, U256}; +use rlp::{DecoderError, Encodable, RlpIterator}; +use thiserror::Error; + +use crate::rlp_helpers::{decode_field, decode_field_h256, next}; + +/// Represents a **valid** Ethereum signature +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct TxSignature { + v: U256, + r: H256, + s: H256, +} + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum ParityError { + #[error("Couldn't reconstruct V from chain_id: {0}")] + ChainId(U256), + + #[error("Couldn't reconstruct parity from V: {0}")] + V(U256), +} + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum TxSigError { + #[error("Error manipulating ECDSA key: {0}")] + ECDSAError(libsecp256k1::Error), + + #[error("Error recomputing parity of signature: {0}")] + Parity(ParityError), + + #[error("Invalid R and S: {0} {1}")] + InvalidRS(H256, H256), +} + +impl From for TxSigError { + fn from(e: libsecp256k1::Error) -> Self { + Self::ECDSAError(e) + } +} + +impl TxSignature { + pub fn v(&self) -> U256 { + self.v + } + + pub fn r(&self) -> &H256 { + &self.r + } + + pub fn s(&self) -> &H256 { + &self.s + } + + pub fn new(v: U256, r: H256, s: H256) -> Result { + if r != H256::zero() && s != H256::zero() { + Ok(TxSignature { v, r, s }) + } else { + Err(TxSigError::InvalidRS(r, s)) + } + } + + /// Computes the parity for a transaction in the Ethereum "legacy" format, + /// ie a transaction that encodes the `chain_id` in the value `v`, this + /// should not be used for EIP-1559 or EIP-2930 transaction for example. + /// The boolean correspond to parity `0` or `1`. + fn legacy_compute_parity(&self, chain_id: U256) -> Result { + let err = TxSigError::Parity(ParityError::V(self.v)); + let chain_id_encoding = chain_id + .checked_mul(U256::from(2)) + .ok_or_else(|| err.clone())? + .checked_add(U256::from(35)) + .ok_or_else(|| err.clone())?; + let parity = self.v.checked_sub(chain_id_encoding); + match parity { + Some(p) if p < U256::from(2) => Ok(p == U256::one()), + _ => Err(err), + } + } + + /// Validate that signatures conforms EIP-2 + /// and that is possible + pub fn signature( + &self, + chain_id: U256, + ) -> Result<(Signature, RecoveryId), TxSigError> { + // copy r to Scalar + let r: H256 = self.r.to_owned(); + let r1: [u8; 32] = r.into(); + let mut r = Scalar([0; 8]); + let _ = r.set_b32(&r1); + // copy s to Scalar + let s: H256 = self.s.to_owned(); + let s1: [u8; 32] = s.into(); + let mut s = Scalar([0; 8]); + let _ = s.set_b32(&s1); + if s.is_high() { + // if s > secp256k1n / 2 the signature is invalid + // cf EIP2 (part 2) https://eips.ethereum.org/EIPS/eip-2 + Err(TxSigError::ECDSAError( + libsecp256k1::Error::InvalidSignature, + )) + } else { + // recompute parity from v and chain_id + let parity = self.legacy_compute_parity(chain_id)?; + let ri = RecoveryId::parse(parity as u8)?; + Ok((Signature { r, s }, ri)) + } + } + + /// compute v from parity and chain_id + fn compute_v(chain_id: U256, parity: u8) -> Option { + if chain_id == U256::zero() { + // we don't support transactions with unpresented chain_id + None + } else { + let chain_id_encoding = chain_id + .checked_mul(U256::from(2))? + .checked_add(U256::from(35))?; + U256::from(parity).checked_add(chain_id_encoding) + } + } + + pub fn reconstruct_from_legacy( + sig: Signature, + recovery_id: RecoveryId, + chain_id: U256, + ) -> Result { + let parity: u8 = recovery_id.into(); + let v = Self::compute_v(chain_id, parity) + .ok_or(TxSigError::Parity(ParityError::ChainId(chain_id)))?; + + let (r, s) = (H256::from(sig.r.b32()), H256::from(sig.s.b32())); + Ok(TxSignature { v, r, s }) + } + + #[cfg(test)] + pub fn new_unsafe(v: u64, r: H256, s: H256) -> TxSignature { + TxSignature::new(U256::from(v), r, s).expect("Signature data should be valid") + } +} + +impl Encodable for TxSignature { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.append(&self.v); + stream.append(&self.r); + stream.append(&self.s); + } +} + +// Encode None as (0, 0, 0) +// This encoding is used for transaction object, +// which expects to have a signature. +// However, for deposits there are no signature so we have to mock it. +pub fn rlp_append_opt(sig: &Option, stream: &mut rlp::RlpStream) { + match sig { + Some(sig) => Encodable::rlp_append(sig, stream), + None => { + stream.append(&U256::zero()); + stream.append(&H256::zero()); + stream.append(&H256::zero()); + } + } +} + +// Decode (0, 0, 0) as None +// See comment for rlp_append_opt above. +pub fn rlp_decode_opt( + it: &mut RlpIterator<'_, '_>, +) -> Result, DecoderError> { + let v: U256 = decode_field(&next(it)?, "v")?; + let r: H256 = decode_field_h256(&next(it)?, "r")?; + let s: H256 = decode_field_h256(&next(it)?, "s")?; + if r == H256::zero() && s == H256::zero() && v == U256::zero() { + Ok(None) + } else { + let sig = TxSignature::new(v, r, s) + .map_err(|_| DecoderError::Custom("Invalid signature"))?; + Ok(Some(sig)) + } +} diff --git a/src/kernel_evm/evm_execution/src/lib.rs b/src/kernel_evm/evm_execution/src/lib.rs index e97e7ad9ff23..408bcf1aaaae 100644 --- a/src/kernel_evm/evm_execution/src/lib.rs +++ b/src/kernel_evm/evm_execution/src/lib.rs @@ -702,7 +702,7 @@ mod test { .unwrap(); let encoded = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83".to_string(); - let transaction = EthereumTransactionCommon::from_rlp(encoded).unwrap(); + let transaction = EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); let address = transaction.caller().unwrap(); let expected_address_string: [u8; 20] = hex::decode("9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F") @@ -721,7 +721,8 @@ mod test { .to_string(); let encoded = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83".to_string(); - let expected_transaction = EthereumTransactionCommon::from_rlp(encoded).unwrap(); + let expected_transaction = + EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); let transaction = expected_transaction.sign_transaction(string_sk).unwrap(); assert_eq!(expected_transaction, transaction) diff --git a/src/kernel_evm/kernel/src/apply.rs b/src/kernel_evm/kernel/src/apply.rs index f8c8209a87ff..485a87283cb8 100644 --- a/src/kernel_evm/kernel/src/apply.rs +++ b/src/kernel_evm/kernel/src/apply.rs @@ -9,10 +9,11 @@ use evm_execution::account_storage::{account_path, EthereumAccountStorage}; use evm_execution::handler::ExecutionOutcome; use evm_execution::precompiles::PrecompileBTreeMap; use evm_execution::run_transaction; -use primitive_types::{H160, H256, U256}; +use primitive_types::{H160, U256}; use tezos_ethereum::block::BlockConstants; use tezos_ethereum::transaction::TransactionHash; use tezos_ethereum::tx_common::EthereumTransactionCommon; +use tezos_ethereum::tx_signature::TxSignature; use tezos_evm_logging::{log, Level::*}; use tezos_smart_rollup_host::runtime::Runtime; @@ -62,24 +63,10 @@ impl Transaction { } } - fn v(&self) -> U256 { + fn signature(&self) -> Option { match &self.content { - TransactionContent::Deposit(_) => U256::zero(), - TransactionContent::Ethereum(transaction) => transaction.v, - } - } - - fn r(&self) -> H256 { - match &self.content { - TransactionContent::Deposit(_t) => H256::zero(), - TransactionContent::Ethereum(transaction) => transaction.r, - } - } - - fn s(&self) -> H256 { - match &self.content { - TransactionContent::Deposit(_) => H256::zero(), - TransactionContent::Ethereum(transaction) => transaction.s, + TransactionContent::Deposit(_) => None, + TransactionContent::Ethereum(transaction) => transaction.signature.clone(), } } } @@ -102,9 +89,7 @@ pub struct TransactionObjectInfo { pub to: Option, pub index: u32, pub value: U256, - pub v: U256, - pub r: H256, - pub s: H256, + pub signature: Option, } #[inline(always)] @@ -141,9 +126,7 @@ fn make_object_info( to: transaction.to(), index, value: transaction.value(), - v: transaction.v(), - r: transaction.r(), - s: transaction.s(), + signature: transaction.signature(), } } diff --git a/src/kernel_evm/kernel/src/block.rs b/src/kernel_evm/kernel/src/block.rs index 498135e360e8..4cd4df0d01c7 100644 --- a/src/kernel_evm/kernel/src/block.rs +++ b/src/kernel_evm/kernel/src/block.rs @@ -192,6 +192,7 @@ mod tests { TransactionHash, TransactionStatus, TRANSACTION_HASH_SIZE, }; use tezos_ethereum::tx_common::EthereumTransactionCommon; + use tezos_ethereum::tx_signature::TxSignature; use tezos_smart_rollup_mock::MockHost; fn blueprint(transactions: Vec) -> QueueElement { @@ -260,9 +261,7 @@ mod tests { to, value, data, - v, - r, - s, + signature: Some(TxSignature::new(v, r, s).unwrap()), } } @@ -313,9 +312,7 @@ mod tests { to: None, value, data, - v: U256::one(), - r: H256::zero(), - s: H256::zero(), + signature: None, }; tx.sign_transaction(private_key.to_string()).unwrap() @@ -1047,9 +1044,7 @@ mod tests { to, value, data: vec![], - v: U256::one(), - r: H256::zero(), - s: H256::zero(), + signature: None, }; // corresponding caller's address is 0xaf1276cbb260bb13deddb4209ae99ae6e497f446 diff --git a/src/kernel_evm/kernel/src/block_in_progress.rs b/src/kernel_evm/kernel/src/block_in_progress.rs index edd1b7e03507..5f5ed803887f 100644 --- a/src/kernel_evm/kernel/src/block_in_progress.rs +++ b/src/kernel_evm/kernel/src/block_in_progress.rs @@ -307,9 +307,7 @@ impl BlockInProgress { to: object_info.to, index: object_info.index, value: object_info.value, - v: object_info.v, - r: object_info.r, - s: object_info.s, + signature: object_info.signature, } } } @@ -322,9 +320,14 @@ mod tests { use primitive_types::{H160, H256, U256}; use rlp::{Decodable, Encodable, Rlp}; use tezos_ethereum::{ - signatures::EthereumTransactionCommon, transaction::TRANSACTION_HASH_SIZE, + transaction::TRANSACTION_HASH_SIZE, tx_common::EthereumTransactionCommon, + tx_signature::TxSignature, }; + fn new_sig_unsafe(v: u64, r: H256, s: H256) -> TxSignature { + TxSignature::new(U256::from(v), r, s).unwrap() + } + fn dummy_etc(i: u8) -> EthereumTransactionCommon { EthereumTransactionCommon { chain_id: U256::from(i), @@ -334,9 +337,11 @@ mod tests { to: None, value: U256::from(i), data: Vec::new(), - r: H256::from([i; 32]), - s: H256::from([i; 32]), - v: U256::from(i), + signature: Some(new_sig_unsafe( + (36 + i * 2).into(), // need to be consistent with chain_id + H256::from([i; 32]), + H256::from([i; 32]), + )), } } @@ -373,7 +378,7 @@ mod tests { }; let encoded = bip.rlp_bytes(); - let expected = "f9014d2af8e2f86fa00101010101010101010101010101010101010101010101010101010101010101f84c01f84901010180018001a00101010101010101010101010101010101010101010101010101010101010101a00101010101010101010101010101010101010101010101010101010101010101f86fa00808080808080808080808080808080808080808080808080808080808080808f84c01f84908080880088008a00808080808080808080808080808080808080808080808080808080808080808a00808080808080808080808080808080808080808080808080808080808080808f842a00202020202020202020202020202020202020202020202020202020202020202a00909090909090909090909090909090909090909090909090909090909090909030405a00606060606060606060606060606060606060606060606060606060606060606"; + let expected = "f9014d2af8e2f86fa00101010101010101010101010101010101010101010101010101010101010101f84c01f84901010180018026a00101010101010101010101010101010101010101010101010101010101010101a00101010101010101010101010101010101010101010101010101010101010101f86fa00808080808080808080808080808080808080808080808080808080808080808f84c01f84908080880088034a00808080808080808080808080808080808080808080808080808080808080808a00808080808080808080808080808080808080808080808080808080808080808f842a00202020202020202020202020202020202020202020202020202020202020202a00909090909090909090909090909090909090909090909090909090909090909030405a00606060606060606060606060606060606060606060606060606060606060606"; assert_eq!(hex::encode(encoded), expected); let bytes = hex::decode(expected).expect("Should be valid hex string"); @@ -433,7 +438,7 @@ mod tests { }; let encoded = bip.rlp_bytes(); - let expected = "f901192af8aef86fa00101010101010101010101010101010101010101010101010101010101010101f84c01f84901010180018001a00101010101010101010101010101010101010101010101010101010101010101a00101010101010101010101010101010101010101010101010101010101010101f83ba00808080808080808080808080808080808080808080808080808080808080808d902d70808940808080808080808080808080808080808080808f842a00202020202020202020202020202020202020202020202020202020202020202a00909090909090909090909090909090909090909090909090909090909090909030405a00606060606060606060606060606060606060606060606060606060606060606"; + let expected = "f901192af8aef86fa00101010101010101010101010101010101010101010101010101010101010101f84c01f84901010180018026a00101010101010101010101010101010101010101010101010101010101010101a00101010101010101010101010101010101010101010101010101010101010101f83ba00808080808080808080808080808080808080808080808080808080808080808d902d70808940808080808080808080808080808080808080808f842a00202020202020202020202020202020202020202020202020202020202020202a00909090909090909090909090909090909090909090909090909090909090909030405a00606060606060606060606060606060606060606060606060606060606060606"; assert_eq!(hex::encode(encoded), expected); let bytes = hex::decode(expected).expect("Should be valid hex string"); diff --git a/src/kernel_evm/kernel/src/blueprint.rs b/src/kernel_evm/kernel/src/blueprint.rs index 829bea9679b4..c9cf0487bfae 100644 --- a/src/kernel_evm/kernel/src/blueprint.rs +++ b/src/kernel_evm/kernel/src/blueprint.rs @@ -77,7 +77,7 @@ pub fn fetch( mod tests { use super::*; use crate::inbox::TransactionContent::Ethereum; - use primitive_types::{H160, H256, U256}; + use primitive_types::{H160, U256}; use tezos_ethereum::{ transaction::TRANSACTION_HASH_SIZE, tx_common::EthereumTransactionCommon, }; @@ -99,9 +99,7 @@ mod tests { to: address_from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea"), value: U256::from(500000000u64), data: vec![], - v: U256::from(0), - r: H256::from_low_u64_be(0), - s: H256::from_low_u64_be(0), + signature: None, }; let valid_transaction = Transaction { -- GitLab From b1a1465fb98cd871856b789c3492406248e73417 Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Fri, 4 Aug 2023 14:56:41 +0100 Subject: [PATCH 6/7] EVM: adjust naming in transaction module --- src/kernel_evm/ethereum/src/tx_common.rs | 124 ++++++++++---------- src/kernel_evm/ethereum/src/tx_signature.rs | 19 ++- src/kernel_evm/evm_execution/src/lib.rs | 23 ++-- src/kernel_evm/kernel/src/inbox.rs | 8 +- 4 files changed, 89 insertions(+), 85 deletions(-) diff --git a/src/kernel_evm/ethereum/src/tx_common.rs b/src/kernel_evm/ethereum/src/tx_common.rs index 48beb29ffd15..e6dcc526b869 100644 --- a/src/kernel_evm/ethereum/src/tx_common.rs +++ b/src/kernel_evm/ethereum/src/tx_common.rs @@ -11,8 +11,7 @@ use std::array::TryFromSliceError; -use hex::FromHexError; -use libsecp256k1::{recover, sign, Message, PublicKey, RecoveryId, SecretKey, Signature}; +use libsecp256k1::{recover, Message, RecoveryId, Signature}; use primitive_types::{H160, H256, U256}; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use sha3::{Digest, Keccak256}; @@ -28,18 +27,12 @@ use crate::{ #[derive(Error, Debug, PartialEq)] pub enum SigError { - #[error("Error reading a hex string: {0}")] - HexError(#[from] FromHexError), - #[error("Error decoding RLP encoded byte array: {0}")] DecoderError(#[from] DecoderError), #[error("Error extracting a slice")] SlicingError, - #[error("Error manipulating ECDSA key: {0}")] - ECDSAError(libsecp256k1::Error), - #[error("Signature error: {0}")] TxSigError(TxSigError), @@ -59,25 +52,6 @@ impl From for SigError { } } -impl From for SigError { - fn from(e: libsecp256k1::Error) -> Self { - Self::ECDSAError(e) - } -} - -/// produces address from a secret key -pub fn string_to_sk_and_address(s: String) -> Result<(SecretKey, H160), SigError> { - let mut data: [u8; 32] = [0u8; 32]; - hex::decode_to_slice(s, &mut data)?; - let sk = SecretKey::parse(&data)?; - let pk = PublicKey::from_secret_key(&sk); - let serialised = &pk.serialize()[1..]; - let kec = Keccak256::digest(serialised); - let mut value: [u8; 20] = [0u8; 20]; - value.copy_from_slice(&kec[12..]); - Ok((sk, value.into())) -} - /// Data common for all kind of Ethereum transactions /// (transfers, contract creation and contract invocation). /// All transaction versions (Legacy, EIP-2930 and EIP-1559) @@ -124,7 +98,7 @@ impl EthereumTransactionCommon { ..self.clone() }; - let bytes = to_sign.to_rlp_bytes(); + let bytes = to_sign.to_bytes(); let hash: [u8; 32] = Keccak256::digest(bytes).into(); Message::parse(&hash) } @@ -147,7 +121,7 @@ impl EthereumTransactionCommon { pub fn caller(&self) -> Result { let mes = self.message(); let (sig, ri) = self.signature()?; - let pk = recover(&mes, &sig, &ri)?; + let pk = recover(&mes, &sig, &ri).map_err(TxSigError::ECDSAError)?; let serialised = &pk.serialize()[1..]; let kec = Keccak256::digest(serialised); let value: [u8; 20] = kec.as_slice()[12..].try_into()?; @@ -158,11 +132,8 @@ impl EthereumTransactionCommon { ///produce a signed EthereumTransactionCommon. If the initial one was signed /// you should get the same thing. pub fn sign_transaction(&self, string_sk: String) -> Result { - let hex: &[u8] = &hex::decode(string_sk)?; - let sk = SecretKey::parse_slice(hex)?; let mes = self.message(); - let (sig, ri) = sign(&mes, &sk); - let signature = TxSignature::reconstruct_from_legacy(sig, ri, self.chain_id)?; + let signature = TxSignature::sign_legacy(&mes, string_sk, self.chain_id)?; Ok(EthereumTransactionCommon { signature: Some(signature), @@ -170,22 +141,29 @@ impl EthereumTransactionCommon { }) } - /// Unserialize bytes as a RLP encoded legacy transaction. - pub fn from_rlp_bytes( - bytes: &[u8], - ) -> Result { + // Unserialize Ethereum tx of arbitrary version from raw bytes. + // This is a separate method of the tx type + // but not rlp::Decodable instance because after legacy + // version a tx encoding, strictly speaking, is not RLP list anymore, + // rather opaque sequence of bytes. + pub fn from_bytes(bytes: &[u8]) -> Result { let decoder = Rlp::new(bytes); EthereumTransactionCommon::decode(&decoder) } /// Unserialize an hex string as a RLP encoded legacy transaction. - pub fn from_rlp_hex(e: String) -> Result { + pub fn from_hex(e: String) -> Result { let tx = hex::decode(e).or(Err(DecoderError::Custom("Couldn't parse hex value")))?; - Self::from_rlp_bytes(&tx) + Self::from_bytes(&tx) } - pub fn to_rlp_bytes(self) -> Vec { + // Serialize Ethereum tx of arbitrary version to raw bytes. + // This is a separate method of the tx type + // but not rlp::Encodable instance because after legacy + // version a tx encoding, strictly speaking, is not RLP list anymore, + // rather opaque sequence of bytes. + pub fn to_bytes(self) -> Vec { self.rlp_bytes().into() } } @@ -193,7 +171,7 @@ impl EthereumTransactionCommon { impl From for EthereumTransactionCommon { /// Decode a transaction in hex format. Unsafe, to be used only in tests : panics when fails fn from(e: String) -> Self { - EthereumTransactionCommon::from_rlp_hex(e).unwrap() + EthereumTransactionCommon::from_hex(e).unwrap() } } @@ -201,7 +179,7 @@ impl TryFrom<&[u8]> for EthereumTransactionCommon { type Error = DecoderError; fn try_from(bytes: &[u8]) -> Result { - Self::from_rlp_bytes(bytes) + Self::from_bytes(bytes) } } @@ -289,10 +267,29 @@ impl Encodable for EthereumTransactionCommon { #[allow(clippy::from_over_into)] impl Into> for EthereumTransactionCommon { fn into(self) -> Vec { - self.to_rlp_bytes() + self.to_bytes() } } +// Produces address from a secret key +// Used in tests only +pub fn string_to_sk_and_address_unsafe( + s: String, +) -> (libsecp256k1::SecretKey, primitive_types::H160) { + use libsecp256k1::PublicKey; + use libsecp256k1::SecretKey; + + let mut data: [u8; 32] = [0u8; 32]; + hex::decode_to_slice(s, &mut data).unwrap(); + let sk = SecretKey::parse(&data).unwrap(); + let pk = PublicKey::from_secret_key(&sk); + let serialised = &pk.serialize()[1..]; + let kec = Keccak256::digest(serialised); + let mut value: [u8; 20] = [0u8; 20]; + value.copy_from_slice(&kec[12..]); + (sk, value.into()) +} + // cargo test ethereum::signatures::test --features testing #[cfg(test)] mod test { @@ -402,11 +399,10 @@ mod test { #[test] fn test_caller_classic() { // setup - let (_sk, address_from_sk) = string_to_sk_and_address( + let (_sk, address_from_sk) = string_to_sk_and_address_unsafe( "4646464646464646464646464646464646464646464646464646464646464646" .to_string(), - ) - .unwrap(); + ); let encoded = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83".to_string(); @@ -414,7 +410,7 @@ mod test { address_from_str("9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F").unwrap(); // act - let transaction = EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); + let transaction = EthereumTransactionCommon::from_hex(encoded).unwrap(); let address = transaction.caller().unwrap(); // assert @@ -430,7 +426,7 @@ mod test { // act let tx = hex::decode(signing_data).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); assert!(decoded.is_ok(), "testing the decoding went ok"); // assert @@ -445,7 +441,7 @@ mod test { // act let tx = hex::decode(signed_tx).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // assert assert!(decoded.is_ok(), "testing the decoding went ok"); @@ -458,7 +454,7 @@ mod test { let signing_data = "ec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080"; // act - let encoded = expected_transaction.to_rlp_bytes(); + let encoded = expected_transaction.to_bytes(); // assert assert_eq!(signing_data, hex::encode(encoded)); @@ -507,7 +503,7 @@ mod test { let expected_encoded = "f8572e8506c50218ba8304312280843b9aca0082ffff26a0e9637495be4c216a833ef390b1f6798917c8a102ab165c5085cced7ca1f2eb3aa057854e7044a8fee7bccb6a2c32c4229dd9cbacad74350789e0ce75bf40b6f713"; // act - let encoded = transaction.to_rlp_bytes(); + let encoded = transaction.to_bytes(); // assert assert_eq!(expected_encoded, hex::encode(encoded)); @@ -521,7 +517,7 @@ mod test { // act let tx = hex::decode(signed_tx).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // assert assert!(decoded.is_ok()); @@ -535,7 +531,7 @@ mod test { let signed_tx = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"; // act - let encoded = expected_transaction.to_rlp_bytes(); + let encoded = expected_transaction.to_bytes(); // assert assert_eq!(signed_tx, hex::encode(encoded)); @@ -571,7 +567,7 @@ mod test { // act let tx = hex::decode(signed_data).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // assert assert_eq!(Ok(expected_transaction), decoded) @@ -585,7 +581,7 @@ mod test { // act let tx = hex::decode(signed_data).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // assert assert!(decoded.is_ok(), "testing the decoding went ok"); @@ -629,7 +625,7 @@ mod test { // act let signed_data = "f903732e8506c50218ba8304312294ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b880a8db2d41b89b009b903043593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064023c1700000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a8db2d41b89b009000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002ab0c205a56c1e000000000000000000000000000000000000000000000000000000a8db2d41b89b00900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009eb6299e4bb6669e42cb295a254c8492f67ae2c600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000025a0c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45aa05721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06"; let tx = hex::decode(signed_data).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // assert assert_eq!(Ok(expected_transaction), decoded); @@ -670,7 +666,7 @@ mod test { let signed_data = "f903732e8506c50218ba8304312294ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b880a8db2d41b89b009b903043593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064023c1700000000000000000000000000000000000000000000000000000000000000030b090c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a8db2d41b89b009000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002ab0c205a56c1e000000000000000000000000000000000000000000000000000000a8db2d41b89b00900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009eb6299e4bb6669e42cb295a254c8492f67ae2c600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000025a0c78be9ab81c622c08f7098eefc250935365fb794dfd94aec0fea16c32adec45aa05721614264d8490c6866f110c1594151bbcc4fac43758adae644db6bc3314d06"; // act - let encoded = expected_transaction.to_rlp_bytes(); + let encoded = expected_transaction.to_bytes(); // assert assert_eq!(signed_data, hex::encode(encoded)); @@ -714,7 +710,7 @@ mod test { // act let tx = hex::decode(signed_data).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // assert assert_eq!(Ok(expected_transaction), decoded); @@ -816,7 +812,7 @@ mod test { fn test_caller_classic_with_chain_id() { let sk = "9bfc9fbe6296c8fef8eb8d6ce2ed5f772a011898c6cabe32d35e7c3e419efb1b" .to_string(); - let (_sk, address) = string_to_sk_and_address(sk.clone()).unwrap(); + let (_sk, address) = string_to_sk_and_address_unsafe(sk.clone()); // Check that the derived address is the expected one. let expected_address = address_from_str("6471A723296395CF1Dcc568941AFFd7A390f94CE").unwrap(); @@ -824,7 +820,7 @@ mod test { // Check that the derived sender address is the expected one. let encoded = "f86d80843b9aca00825208940b52d4d3be5d18a7ab5e4476a2f5382bbf2b38d888016345785d8a000080820a95a0d9ef1298c18c88604e3f08e14907a17dfa81b1dc6b37948abe189d8db5cb8a43a06fc7040a71d71d3cb74bd05ead7046b10668ad255da60391c017eea31555f156".to_string(); - let transaction = EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); + let transaction = EthereumTransactionCommon::from_hex(encoded).unwrap(); let address = transaction.caller().unwrap(); assert_eq!(expected_address, address); @@ -1021,7 +1017,7 @@ mod test { // but equal to 27/28 as in "old" (before https://eips.ethereum.org/EIPS/eip-155) // six fields encoding let malformed_tx = "f86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae52c1ef3300f44df0bcfd1341c232ed6134672b16e35699ae3f5fe2493379a023d23d2955a239dd6f61c4e8b2678d174356ff424eac53da53e17706c43ef871".to_string(); - let e = EthereumTransactionCommon::from_rlp_hex(malformed_tx); + let e = EthereumTransactionCommon::from_hex(malformed_tx); assert!(e.is_err()); } @@ -1029,8 +1025,8 @@ mod test { fn test_rlp_decode_encode_with_valid_chain_id() { let wellformed_tx = "f86a8302ae2a7b82f618948e998a00253cb1747679ac25e69a8d870b52d8898802c68af0bb140000802da0cd2d976eb691dc16a397462c828975f0b836e1b448ecb8f00d9765cf5032cecca066247d13fc2b65fd70a2931b5897fff4b3079e9587e69ac8a0036c99eb5ea927".to_string(); - let e = EthereumTransactionCommon::from_rlp_hex(wellformed_tx.clone()).unwrap(); - let encoded = e.to_rlp_bytes(); + let e = EthereumTransactionCommon::from_hex(wellformed_tx.clone()).unwrap(); + let encoded = e.to_bytes(); assert_eq!(hex::encode(encoded), wellformed_tx); } @@ -1053,7 +1049,7 @@ mod test { let signed_tx = "f901cc8086010000000000830250008080b90178608060405234801561001057600080fd5b50602a600081905550610150806100286000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea26469706673582212204d6c1853cec27824f5dbf8bcd0994714258d22fc0e0dc8a2460d87c70e3e57a564736f6c634300081200331ca06d851632958801b6919ba534b4b1feb1bdfaabd0d42890bce200a11ac735d58da0219b058d7169d7a4839c5cdd555b0820b545797365287a81ba409419912de7b1"; // act let tx = hex::decode(signed_tx).unwrap(); - let decoded = EthereumTransactionCommon::from_rlp_bytes(&tx); + let decoded = EthereumTransactionCommon::from_bytes(&tx); // sanity check assert_eq!( diff --git a/src/kernel_evm/ethereum/src/tx_signature.rs b/src/kernel_evm/ethereum/src/tx_signature.rs index 5db6fd496402..305320626875 100644 --- a/src/kernel_evm/ethereum/src/tx_signature.rs +++ b/src/kernel_evm/ethereum/src/tx_signature.rs @@ -2,7 +2,8 @@ // // SPDX-License-Identifier: MIT -use libsecp256k1::{curve::Scalar, RecoveryId, Signature}; +use hex::FromHexError; +use libsecp256k1::{curve::Scalar, sign, Message, RecoveryId, SecretKey, Signature}; use primitive_types::{H256, U256}; use rlp::{DecoderError, Encodable, RlpIterator}; use thiserror::Error; @@ -28,6 +29,9 @@ pub enum ParityError { #[derive(Error, Debug, PartialEq, Clone)] pub enum TxSigError { + #[error("Error reading a hex string: {0}")] + HexError(#[from] FromHexError), + #[error("Error manipulating ECDSA key: {0}")] ECDSAError(libsecp256k1::Error), @@ -126,11 +130,18 @@ impl TxSignature { } } - pub fn reconstruct_from_legacy( - sig: Signature, - recovery_id: RecoveryId, + /// This function creates a signature for a legacy tx. + /// Legacy tx has a tricky approach for signature creation, where + /// `v` field of a signature carry information about `chain_id`. + /// For more information see https://eips.ethereum.org/EIPS/eip-155 + pub fn sign_legacy( + msg: &Message, + string_sk: String, chain_id: U256, ) -> Result { + let hex: &[u8] = &hex::decode(string_sk)?; + let sk = SecretKey::parse_slice(hex)?; + let (sig, recovery_id) = sign(msg, &sk); let parity: u8 = recovery_id.into(); let v = Self::compute_v(chain_id, parity) .ok_or(TxSigError::Parity(ParityError::ChainId(chain_id)))?; diff --git a/src/kernel_evm/evm_execution/src/lib.rs b/src/kernel_evm/evm_execution/src/lib.rs index 408bcf1aaaae..99f53cb0cc43 100644 --- a/src/kernel_evm/evm_execution/src/lib.rs +++ b/src/kernel_evm/evm_execution/src/lib.rs @@ -643,11 +643,10 @@ mod test { #[test] //this is based on https://eips.ethereum.org/EIPS/eip-155 fn test_signatures() { - let (sk, _address) = tezos_ethereum::tx_common::string_to_sk_and_address( + let (sk, _address) = tezos_ethereum::tx_common::string_to_sk_and_address_unsafe( "4646464646464646464646464646464646464646464646464646464646464646" .to_string(), - ) - .unwrap(); + ); let m: [u8; 32] = hex::decode( "daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53", ) @@ -685,8 +684,7 @@ mod test { ]; test_list.iter().fold((), |_, (s, ea)| { let (_, a) = - tezos_ethereum::tx_common::string_to_sk_and_address(s.to_string()) - .unwrap(); + tezos_ethereum::tx_common::string_to_sk_and_address_unsafe(s.to_string()); let value: [u8; 20] = hex::decode(ea).unwrap().try_into().unwrap(); let ea = value.into(); assert_eq!(a, ea); @@ -695,14 +693,14 @@ mod test { #[test] fn test_caller_classic() { - let (_sk, address_from_sk) = tezos_ethereum::tx_common::string_to_sk_and_address( - "4646464646464646464646464646464646464646464646464646464646464646" - .to_string(), - ) - .unwrap(); + let (_sk, address_from_sk) = + tezos_ethereum::tx_common::string_to_sk_and_address_unsafe( + "4646464646464646464646464646464646464646464646464646464646464646" + .to_string(), + ); let encoded = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83".to_string(); - let transaction = EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); + let transaction = EthereumTransactionCommon::from_hex(encoded).unwrap(); let address = transaction.caller().unwrap(); let expected_address_string: [u8; 20] = hex::decode("9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F") @@ -721,8 +719,7 @@ mod test { .to_string(); let encoded = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83".to_string(); - let expected_transaction = - EthereumTransactionCommon::from_rlp_hex(encoded).unwrap(); + let expected_transaction = EthereumTransactionCommon::from_hex(encoded).unwrap(); let transaction = expected_transaction.sign_transaction(string_sk).unwrap(); assert_eq!(expected_transaction, transaction) diff --git a/src/kernel_evm/kernel/src/inbox.rs b/src/kernel_evm/kernel/src/inbox.rs index 5ef1404b3db9..1ba8fb7392d0 100644 --- a/src/kernel_evm/kernel/src/inbox.rs +++ b/src/kernel_evm/kernel/src/inbox.rs @@ -218,7 +218,7 @@ fn handle_transaction_chunk( // if `data` was the missing chunk. if let Some(data) = crate::storage::store_transaction_chunk(host, &tx_hash, i, data)? { - if let Ok(tx) = EthereumTransactionCommon::from_rlp_bytes(&data) { + if let Ok(tx) = EthereumTransactionCommon::from_bytes(&data) { let content = TransactionContent::Ethereum(tx); return Ok(Some(Transaction { tx_hash, content })); } @@ -437,7 +437,7 @@ mod tests { fn large_transaction() -> (Vec, EthereumTransactionCommon) { let data: Vec = hex::decode(["f917e180843b9aca0082520894b53dc01974176e5dff2298c5a94343c2585e3c548a021dfe1f5c5363780000b91770".to_string(), "a".repeat(12_000), "820a96a07fd9567a72223bbc8f70bd2b42011339b61044d16b5a2233534db8ca01f3e57aa03ea489c4bb2b2b52f3c1a18966881114767654c9ab61d46b1fbff78a498043c2".to_string()].join("")).unwrap(); - let tx = EthereumTransactionCommon::from_rlp_bytes(&data).unwrap(); + let tx = EthereumTransactionCommon::from_bytes(&data).unwrap(); (data, tx) } @@ -446,7 +446,7 @@ mod tests { let mut host = MockHost::default(); let tx = - EthereumTransactionCommon::from_rlp_bytes(&hex::decode("f86d80843b9aca00825208940b52d4d3be5d18a7ab5e4476a2f5382bbf2b38d888016345785d8a000080820a95a0d9ef1298c18c88604e3f08e14907a17dfa81b1dc6b37948abe189d8db5cb8a43a06fc7040a71d71d3cb74bd05ead7046b10668ad255da60391c017eea31555f156").unwrap()).unwrap(); + EthereumTransactionCommon::from_bytes(&hex::decode("f86d80843b9aca00825208940b52d4d3be5d18a7ab5e4476a2f5382bbf2b38d888016345785d8a000080820a95a0d9ef1298c18c88604e3f08e14907a17dfa81b1dc6b37948abe189d8db5cb8a43a06fc7040a71d71d3cb74bd05ead7046b10668ad255da60391c017eea31555f156").unwrap()).unwrap(); let input = Input::SimpleTransaction(Box::new(Transaction { tx_hash: ZERO_TX_HASH, content: Ethereum(tx.clone()), @@ -698,7 +698,7 @@ mod tests { let mut host = MockHost::with_address(&address); let tx = - EthereumTransactionCommon::from_rlp_bytes(&hex::decode("f86d80843b9aca00825208940b52d4d3be5d18a7ab5\ + EthereumTransactionCommon::from_bytes(&hex::decode("f86d80843b9aca00825208940b52d4d3be5d18a7ab5\ e4476a2f5382bbf2b38d888016345785d8a000080820a95a0d9ef1298c18c88604e3f08e14907a17dfa81b1dc6b37948abe189d8db5cb8a43a06\ fc7040a71d71d3cb74bd05ead7046b10668ad255da60391c017eea31555f156").unwrap()).unwrap(); -- GitLab From 7f5a49b80eb2d97d3ecb3dba412b65cf5c89a797 Mon Sep 17 00:00:00 2001 From: Valentin Chaboche Date: Wed, 16 Aug 2023 09:10:45 +0200 Subject: [PATCH 7/7] EVM: mention transation's refactor in changelog --- src/kernel_evm/CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/kernel_evm/CHANGES.md b/src/kernel_evm/CHANGES.md index ab9919405a95..d6012eb12fe9 100644 --- a/src/kernel_evm/CHANGES.md +++ b/src/kernel_evm/CHANGES.md @@ -50,3 +50,5 @@ - Upgrade to Kernel SDK 0.2.1 (!9417, !9526) - Porting benchmark framework to the EVM Kernel. (!9529, !9524, !9545) - Reorganized dependencies at the workspace level. (!9522) +- Refactor `TransactionCommon` and transactions' signatures modules. (!9590) + -- GitLab