From 5f5fa9a3a3694a07dfe27479d28c3fa75ec3b01c Mon Sep 17 00:00:00 2001 From: Vladimir Motylenko Date: Tue, 3 Aug 2021 19:00:00 +0300 Subject: [PATCH] Feat(evm): Add feature that change structure of evm unsigned transaction. And fix big transaction deallocation. --- README.md | 6 +- evm-utils/evm-rpc/src/lib.rs | 5 +- evm-utils/evm-state/src/executor.rs | 81 +++++++-- evm-utils/evm-state/src/transactions.rs | 111 +++++++++--- evm-utils/programs/evm_loader/src/error.rs | 6 + .../programs/evm_loader/src/instructions.rs | 3 + .../programs/evm_loader/src/processor.rs | 167 ++++++++++++++++-- sdk/src/feature_set.rs | 4 + .../proto/solana.storage.evm_compatibility.rs | 2 + storage-proto/src/convert.rs | 23 +-- storage-proto/src/evm.proto | 1 + 11 files changed, 339 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index cbdcef5264..1128f72fe1 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,11 @@ $ cargo test --no-fail-fast Info about EVM integration is at our [docs](https://docs.velas.com/evm). ### Starting a local testnet -Start your own devnet locally, instructions are in the [online docs](https://docs.velas.com/cluster/bench-tps). +Start your own Development network locally, instructions are in the [online docs](https://docs.velas.com/cluster/bench-tps). ### Accessing the remote testnet and mainnet -* `testnet` - public devnet accessible via bootstrap.testnet.veladev.net. -* `mainnet` - public devnet accessible via bootstrap.velas.com. +* `testnet` - public accessible via bootstrap.testnet.veladev.net. +* `mainnet` - public accessible via bootstrap.velas.com. # Benchmarking diff --git a/evm-utils/evm-rpc/src/lib.rs b/evm-utils/evm-rpc/src/lib.rs index ed1745daba..e785bb53f9 100644 --- a/evm-utils/evm-rpc/src/lib.rs +++ b/evm-utils/evm-rpc/src/lib.rs @@ -676,6 +676,7 @@ impl RPCTransaction { ), ), }; + let caller_addr = tx.caller.to_string(); let v = chain_id * 2 + 35; ( to, @@ -687,8 +688,8 @@ impl RPCTransaction { value, nonce, v, - U256::zero(), - U256::zero(), + Hex::::from_hex(&caller_addr)?.0, + U256::from(0x1), ) } }; diff --git a/evm-utils/evm-state/src/executor.rs b/evm-utils/evm-state/src/executor.rs index bb83f5d0df..729db0a896 100644 --- a/evm-utils/evm-state/src/executor.rs +++ b/evm-utils/evm-state/src/executor.rs @@ -212,15 +212,21 @@ impl Executor { &mut self, caller: H160, tx: UnsignedTransaction, + calculate_tx_hash_with_caller: bool, precompiles: F, ) -> Result where F: FnMut(H160, &[u8], Option, &Context) -> Option, { - // TODO use u64 for chain_id - let chain_id = Some(self.config.chain_id); + let chain_id = self.config.chain_id; - let tx_hash = tx.signing_hash(chain_id); + let unsigned_tx = UnsignedTransactionWithCaller { + unsigned_tx: tx.clone(), + caller, + chain_id, + signed_compatible: calculate_tx_hash_with_caller, + }; + let tx_hash = unsigned_tx.tx_id_hash(); let result = self.transaction_execute_raw( caller, tx.nonce, @@ -229,15 +235,10 @@ impl Executor { tx.action, tx.input.clone(), tx.value, - chain_id, + Some(chain_id), tx_hash, precompiles, )?; - let unsigned_tx = UnsignedTransactionWithCaller { - unsigned_tx: tx, - caller, - chain_id, - }; self.register_tx_with_receipt(TransactionInReceipt::Unsigned(unsigned_tx), result.clone()); Ok(result) @@ -319,9 +320,7 @@ impl Executor { fn register_tx_with_receipt(&mut self, tx: TransactionInReceipt, result: ExecutionResult) { let tx_hash = match &tx { TransactionInReceipt::Signed(tx) => tx.tx_id_hash(), - TransactionInReceipt::Unsigned(tx) => { - tx.unsigned_tx.signing_hash(Some(self.config.chain_id)) - } + TransactionInReceipt::Unsigned(tx) => tx.tx_id_hash(), }; debug!( @@ -362,7 +361,13 @@ impl Executor { ); } - pub fn register_swap_tx_in_evm(&mut self, mint_address: H160, recipient: H160, amount: U256) { + pub fn register_swap_tx_in_evm( + &mut self, + mint_address: H160, + recipient: H160, + amount: U256, + signed_compatible: bool, + ) { let nonce = self.with_executor( |_, _, _, _| None, |e| { @@ -382,7 +387,8 @@ impl Executor { let unsigned_tx = UnsignedTransactionWithCaller { unsigned_tx: tx, caller: mint_address, - chain_id: Some(self.config.chain_id), + chain_id: self.config.chain_id, + signed_compatible, }; let result = ExecutionResult { tx_logs: Vec::new(), @@ -617,6 +623,7 @@ mod tests { .transaction_execute_unsinged( alice.secret.to_address(), create_tx.clone(), + false, noop_precompile ) .unwrap() @@ -627,12 +634,56 @@ mod tests { let hash = create_tx.signing_hash(Some(chain_id)); assert!(matches!( executor - .transaction_execute_unsinged(alice.secret.to_address(), create_tx, noop_precompile) + .transaction_execute_unsinged(alice.secret.to_address(), create_tx,false, noop_precompile) .unwrap_err(), Error::DuplicateTx { tx_hash } if tx_hash == hash )); } + #[test] + fn handle_duplicate_txs_unsigned_new_hash() { + let _logger = simple_logger::SimpleLogger::new().init(); + + let chain_id = 0xeba; + let evm_config = EvmConfig { + chain_id, + ..EvmConfig::default() + }; + let mut executor = + Executor::with_config(EvmBackend::default(), Default::default(), evm_config); + + let code = hex::decode(METACOIN_CODE).unwrap(); + + let alice = Persona::new(); + let create_tx = alice.unsigned(TransactionAction::Create, &code); + + let address = alice.address(); + assert!(matches!( + executor + .transaction_execute_unsinged(address, create_tx.clone(), true, noop_precompile) + .unwrap() + .exit_reason, + ExitReason::Succeed(ExitSucceed::Returned) + )); + + let create_tx_with_caller = UnsignedTransactionWithCaller { + unsigned_tx: create_tx.clone(), + caller: address, + chain_id, + signed_compatible: true, + }; + let hash = create_tx_with_caller.tx_id_hash(); + println!("tx = {:?}", create_tx_with_caller); + println!("hash = {}", hash); + + assert!(matches!( + executor + .transaction_execute_unsinged(address, create_tx, true, noop_precompile) + .unwrap_err(), + Error::DuplicateTx { tx_hash } if tx_hash == hash + )); + } + #[test] fn it_execute_only_txs_with_correct_chain_id() { let _logger = simple_logger::SimpleLogger::new().init(); diff --git a/evm-utils/evm-state/src/transactions.rs b/evm-utils/evm-state/src/transactions.rs index ed3bfd234f..435851d927 100644 --- a/evm-utils/evm-state/src/transactions.rs +++ b/evm-utils/evm-state/src/transactions.rs @@ -17,6 +17,8 @@ pub use secp256k1::{PublicKey, SecretKey, SECP256K1}; pub type Address = H160; pub type Gas = U256; +const UNSIGNED_TX_MARKER: u8 = 0x1; + /// Etherium transaction. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct Transaction { @@ -364,8 +366,16 @@ impl Decodable for TransactionInReceipt { fn decode(rlp: &Rlp<'_>) -> Result { let items = rlp.item_count()?; Ok(match items { - 8 => TransactionInReceipt::Unsigned(UnsignedTransactionWithCaller::decode(rlp)?), - 9 => TransactionInReceipt::Signed(Transaction::decode(rlp)?), + 8 => TransactionInReceipt::Unsigned(UnsignedTransactionWithCaller::decode(rlp, false)?), + 9 => { + if rlp.val_at::(8) == Ok(0x1u8) { + TransactionInReceipt::Unsigned(UnsignedTransactionWithCaller::decode( + rlp, true, + )?) + } else { + TransactionInReceipt::Signed(Transaction::decode(rlp)?) + } + } _ => return Err(DecoderError::RlpInvalidLength), }) } @@ -375,39 +385,81 @@ impl Decodable for TransactionInReceipt { pub struct UnsignedTransactionWithCaller { pub unsigned_tx: UnsignedTransaction, pub caller: H160, - pub chain_id: Option, + pub chain_id: u64, + // with signed_compatible, transaction serialization differ, and start to be compatible with signed tx, the only difference is that s is always empty. + pub signed_compatible: bool, } impl Encodable for UnsignedTransactionWithCaller { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(8); - s.append(&self.unsigned_tx.nonce); - s.append(&self.unsigned_tx.gas_price); - s.append(&self.unsigned_tx.gas_limit); - s.append(&self.unsigned_tx.action); - s.append(&self.unsigned_tx.value); - s.append(&self.unsigned_tx.input); - s.append(&self.caller); - s.append(&self.chain_id.unwrap_or(0)); + let chain_id = self.chain_id; + if self.signed_compatible { + s.begin_list(9); + s.append(&self.unsigned_tx.nonce); + s.append(&self.unsigned_tx.gas_price); + s.append(&self.unsigned_tx.gas_limit); + s.append(&self.unsigned_tx.action); + s.append(&self.unsigned_tx.value); + s.append(&self.unsigned_tx.input); + s.append(&chain_id); + s.append(&self.caller); + s.append(&UNSIGNED_TX_MARKER); + } else { + s.begin_list(8); + s.append(&self.unsigned_tx.nonce); + s.append(&self.unsigned_tx.gas_price); + s.append(&self.unsigned_tx.gas_limit); + s.append(&self.unsigned_tx.action); + s.append(&self.unsigned_tx.value); + s.append(&self.unsigned_tx.input); + s.append(&self.caller); + s.append(&chain_id); + } } } -impl Decodable for UnsignedTransactionWithCaller { - fn decode(rlp: &Rlp<'_>) -> Result { - let chain_id = rlp.val_at(7)?; - let chain_id = if chain_id == 0 { None } else { Some(chain_id) }; - Ok(Self { - unsigned_tx: UnsignedTransaction { - nonce: rlp.val_at(0)?, - gas_price: rlp.val_at(1)?, - gas_limit: rlp.val_at(2)?, - action: rlp.val_at(3)?, - value: rlp.val_at(4)?, - input: rlp.val_at(5)?, - }, - caller: rlp.val_at(6)?, - chain_id, - }) +impl UnsignedTransactionWithCaller { + pub fn tx_id_hash(&self) -> H256 { + // old transaction hash was calculated with different rlp structure, use signing_hash to be compatible + if !self.signed_compatible { + return self.unsigned_tx.signing_hash(Some(self.chain_id)); + } + let mut stream = RlpStream::new(); + self.rlp_append(&mut stream); + H256::from_slice(Keccak256::digest(&stream.as_raw()).as_slice()) + } + fn decode(rlp: &Rlp<'_>, signed_compatible: bool) -> Result { + if signed_compatible { + let chain_id = rlp.val_at(6)?; + Ok(Self { + unsigned_tx: UnsignedTransaction { + nonce: rlp.val_at(0)?, + gas_price: rlp.val_at(1)?, + gas_limit: rlp.val_at(2)?, + action: rlp.val_at(3)?, + value: rlp.val_at(4)?, + input: rlp.val_at(5)?, + }, + caller: rlp.val_at(7)?, + chain_id, + signed_compatible, + }) + } else { + let chain_id = rlp.val_at(7)?; + Ok(Self { + unsigned_tx: UnsignedTransaction { + nonce: rlp.val_at(0)?, + gas_price: rlp.val_at(1)?, + gas_limit: rlp.val_at(2)?, + action: rlp.val_at(3)?, + value: rlp.val_at(4)?, + input: rlp.val_at(5)?, + }, + caller: rlp.val_at(6)?, + chain_id, + signed_compatible, + }) + } } } @@ -577,7 +629,8 @@ mod test { let unsigned = UnsignedTransactionWithCaller { unsigned_tx, caller: H160::repeat_byte(3), - chain_id: None, + chain_id: 0x0, + signed_compatible: false, }; let bytes = rlp::encode(&unsigned); let unsigned_deserialized: TransactionInReceipt = diff --git a/evm-utils/programs/evm_loader/src/error.rs b/evm-utils/programs/evm_loader/src/error.rs index 04701d5ff0..85fc54e38f 100644 --- a/evm-utils/programs/evm_loader/src/error.rs +++ b/evm-utils/programs/evm_loader/src/error.rs @@ -30,6 +30,9 @@ pub enum EvmError { #[snafu(display("Authorized transaction EVM address should be calculated from sender address using evm_address_for_program."))] AuthorizedTransactionIncorrectAddress, + #[snafu(display("Wrong AuthorizedTx account owner.."))] + AuthorizedTransactionIncorrectOwner, + #[snafu(display("Cannot free ownership of an account that EVM didn't own."))] FreeNotEvmAccount, @@ -50,6 +53,9 @@ pub enum EvmError { #[snafu(display("EVM Transaction was reverted."))] RevertTransaction, + + #[snafu(display("This instruction is not supported yet."))] + InstructionNotSupportedYet, } impl DecodeError for EvmError { diff --git a/evm-utils/programs/evm_loader/src/instructions.rs b/evm-utils/programs/evm_loader/src/instructions.rs index 61e20dd7e8..afc6a040ee 100644 --- a/evm-utils/programs/evm_loader/src/instructions.rs +++ b/evm-utils/programs/evm_loader/src/instructions.rs @@ -14,6 +14,9 @@ pub enum EvmBigTransaction { /// Execute merged transaction, in order to do this, user should make sure that transaction is successfully writed. EvmTransactionExecute {}, + + /// Execute merged unsigned transaction, in order to do this, user should make sure that transaction is successfully writed. + EvmTransactionExecuteUnsigned { from: evm::Address }, } #[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)] diff --git a/evm-utils/programs/evm_loader/src/processor.rs b/evm-utils/programs/evm_loader/src/processor.rs index 9df2d15e31..5c8c3c028f 100644 --- a/evm-utils/programs/evm_loader/src/processor.rs +++ b/evm-utils/programs/evm_loader/src/processor.rs @@ -1,3 +1,4 @@ +use std::cell::RefMut; use std::ops::DerefMut; use super::account_structure::AccountStructure; @@ -7,6 +8,7 @@ use super::scope::*; use log::*; use evm::{gweis_to_lamports, Executor, ExitReason}; +use solana_sdk::account::Account; use solana_sdk::ic_msg; use solana_sdk::instruction::InstructionError; use solana_sdk::process_instruction::InvokeContext; @@ -43,7 +45,8 @@ impl EvmProcessor { .is_feature_active(&solana_sdk::feature_set::velas::native_swap_in_evm_history::id()); let new_error_handling = invoke_context .is_feature_active(&solana_sdk::feature_set::velas::evm_new_error_handling::id()); - + let unsigned_tx_fix = invoke_context + .is_feature_active(&solana_sdk::feature_set::velas::unsigned_tx_fix::id()); if cross_execution && !cross_execution_enabled { ic_msg!(invoke_context, "Cross-Program evm execution not enabled."); return Err(EvmError::CrossExecutionNotEnabled.into()); @@ -80,11 +83,17 @@ impl EvmProcessor { EvmInstruction::EvmTransaction { evm_tx } => { self.process_raw_tx(executor, invoke_context, accounts, evm_tx) } - EvmInstruction::EvmAuthorizedTransaction { from, unsigned_tx } => { - self.process_authorized_tx(executor, invoke_context, accounts, from, unsigned_tx) - } + EvmInstruction::EvmAuthorizedTransaction { from, unsigned_tx } => self + .process_authorized_tx( + executor, + invoke_context, + accounts, + from, + unsigned_tx, + unsigned_tx_fix, + ), EvmInstruction::EvmBigTransaction(big_tx) => { - self.process_big_tx(executor, invoke_context, accounts, big_tx) + self.process_big_tx(executor, invoke_context, accounts, big_tx, unsigned_tx_fix) } EvmInstruction::FreeOwnership {} => { self.process_free_ownership(executor, invoke_context, accounts) @@ -99,6 +108,7 @@ impl EvmProcessor { lamports, evm_address, register_swap_tx_in_evm, + unsigned_tx_fix, ), }; @@ -130,6 +140,8 @@ impl EvmProcessor { SwapInsufficient => InstructionError::InsufficientFunds, BorrowingFailed => InstructionError::AccountBorrowFailed, RevertTransaction => return Ok(()), // originally revert was not an error + // future error would be just invalid errors. + _ => InstructionError::InvalidError, } } else { error.into() @@ -173,6 +185,7 @@ impl EvmProcessor { accounts: AccountStructure, from: evm::Address, unsigned_tx: evm::UnsignedTransaction, + unsigned_tx_fix: bool, ) -> Result<(), EvmError> { // TODO: Check that it is from program? // TODO: Gas limit? @@ -211,10 +224,22 @@ impl EvmProcessor { unsigned_tx.action ); + if unsigned_tx_fix { + let program_caller = invoke_context.get_caller().map(|k| *k).unwrap_or_default(); + let program_owner = program_account + .try_account_ref() + .map_err(|_| EvmError::BorrowingFailed)? + .owner; + if program_owner != program_caller { + return Err(EvmError::AuthorizedTransactionIncorrectOwner); + } + } + let tx_gas_price = unsigned_tx.gas_price; let result = executor.transaction_execute_unsinged( from, unsigned_tx, + unsigned_tx_fix, precompiles::entrypoint(accounts, executor.support_precompile()), ); let sender = accounts.first(); @@ -259,6 +284,7 @@ impl EvmProcessor { lamports: u64, evm_address: evm::Address, register_swap_tx_in_evm: bool, + unsigned_tx_fix: bool, ) -> Result<(), EvmError> { let gweis = evm::lamports_to_gwei(lamports); let user = accounts.first().ok_or_else(|| { @@ -306,7 +332,12 @@ impl EvmProcessor { .lamports += lamports; executor.deposit(evm_address, gweis); if register_swap_tx_in_evm { - executor.register_swap_tx_in_evm(*precompiles::ETH_TO_VLX_ADDR, evm_address, gweis) + executor.register_swap_tx_in_evm( + *precompiles::ETH_TO_VLX_ADDR, + evm_address, + gweis, + unsigned_tx_fix, + ) } Ok(()) } @@ -317,10 +348,11 @@ impl EvmProcessor { invoke_context: &dyn InvokeContext, accounts: AccountStructure, big_tx: EvmBigTransaction, + unsigned_tx_fix: bool, ) -> Result<(), EvmError> { debug!("executing big_tx = {:?}", big_tx); - let storage = accounts.first().ok_or_else(|| { + let storage_account = accounts.first().ok_or_else(|| { ic_msg!( invoke_context, "EvmBigTransaction: No storage account found." @@ -328,14 +360,14 @@ impl EvmProcessor { EvmError::MissingAccount })?; - if storage.signer_key().is_none() { + if storage_account.signer_key().is_none() { ic_msg!( invoke_context, "EvmBigTransaction: Storage should sign instruction." ); return Err(EvmError::MissingRequiredSignature); } - let mut storage = storage + let mut storage = storage_account .try_account_ref_mut() .map_err(|_| EvmError::BorrowingFailed)?; @@ -403,7 +435,11 @@ impl EvmProcessor { tx, precompiles::entrypoint(accounts, executor.support_precompile()), ); + let sender = accounts.users.get(1); + if unsigned_tx_fix { + self.cleanup_storage(invoke_context, storage, &sender.unwrap_or(accounts.evm))?; + } self.handle_transaction_result( invoke_context, @@ -413,9 +449,120 @@ impl EvmProcessor { result, ) } + EvmBigTransaction::EvmTransactionExecuteUnsigned { from } => { + if !unsigned_tx_fix { + ic_msg!( + invoke_context, + "BigTransaction::EvmTransactionExecuteUnsigned: Unsigned tx fix is not activated, this instruction is not supported." + ); + return Err(EvmError::InstructionNotSupportedYet); + } + debug!("Tx chunks crc = {:#x}", tx_chunks.crc()); + + let bytes = tx_chunks.take(); + + debug!("Trying to deserialize tx chunks byte = {:?}", bytes); + let unsigned_tx: evm::UnsignedTransaction = + bincode::deserialize(&bytes).map_err(|e| { + ic_msg!( + invoke_context, + "BigTransaction::EvmTransactionExecute: Tx chunks deserialize error: {:?}", + e + ); + EvmError::DeserializationError + })?; + + debug!("Executing EVM tx = {:?}", unsigned_tx); + // TODO: Gas limit? + let program_account = accounts.users.get(1).ok_or_else(|| { + ic_msg!( + invoke_context, + "BigTransaction::EvmTransactionExecuteUnsigned: Not enough accounts, expected signer address as second account." + ); + EvmError::MissingAccount + })?; + let key = if let Some(key) = program_account.signer_key() { + key + } else { + ic_msg!( + invoke_context, + "BigTransaction::EvmTransactionExecuteUnsigned: Second account is not a signer, cannot execute transaction." + ); + return Err(EvmError::MissingRequiredSignature); + }; + let from_expected = crate::evm_address_for_program(*key); + + if from_expected != from { + ic_msg!( + invoke_context, + "BigTransaction::EvmTransactionExecuteUnsigned: From is not calculated with evm_address_for_program." + ); + return Err(EvmError::AuthorizedTransactionIncorrectAddress); + } + + ic_msg!( + invoke_context, + "BigTransaction::EvmTransactionExecuteUnsigned: Executing authorized transaction: gas_limit:{}, gas_price:{}, value:{}, action:{:?},", + unsigned_tx.gas_limit, + unsigned_tx.gas_price, + unsigned_tx.value, + unsigned_tx.action + ); + + if unsigned_tx_fix { + let program_caller = + invoke_context.get_caller().map(|k| *k).unwrap_or_default(); + let program_owner = program_account + .try_account_ref() + .map_err(|_| EvmError::BorrowingFailed)? + .owner; + if program_owner != program_caller { + return Err(EvmError::AuthorizedTransactionIncorrectOwner); + } + } + let tx_gas_price = unsigned_tx.gas_price; + let result = executor.transaction_execute_unsinged( + from, + unsigned_tx, + unsigned_tx_fix, + precompiles::entrypoint(accounts, executor.support_precompile()), + ); + + self.cleanup_storage(invoke_context, storage, &program_account)?; + self.handle_transaction_result( + invoke_context, + accounts, + Some(program_account), + tx_gas_price, + result, + ) + } } } + pub fn cleanup_storage<'a>( + &self, + invoke_context: &dyn InvokeContext, + mut storage_ref: RefMut, + user: &'a KeyedAccount<'a>, + ) -> Result<(), EvmError> { + let balance = storage_ref.lamports; + + storage_ref.lamports = 0; + + user.try_account_ref_mut() + .map_err(|_| EvmError::BorrowingFailed)? + .lamports += balance; + + ic_msg!( + invoke_context, + "Refunding storage rent fee to transaction sender fee:{:?}, sender:{}", + balance, + user.unsigned_key() + ); + Ok(()) + } + // Handle executor errors. // refund fee pub fn handle_transaction_result( @@ -1670,7 +1817,7 @@ mod test { let first_user_account = RefCell::new(solana_sdk::account::AccountSharedData { lamports: 1000, data: vec![], - owner: crate::ID, + owner: solana_sdk::system_program::id(), executable: false, rent_epoch: 0, }); diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index c4b6290579..a37840763b 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -165,6 +165,10 @@ pub mod velas { pub mod evm_new_error_handling { solana_sdk::declare_id!("9HscytNCkVfhQYuVbKGdicUzk6zGjRVtwXXbo1b6spRG"); } + + pub mod unsigned_tx_fix { + solana_sdk::declare_id!("5sUZxkYgPYW97L2pjMqhsmeCxEdu3LtxMStD3Ycrag2K"); + } } lazy_static! { /// Map of feature identifiers to user-visible description diff --git a/storage-proto/proto/solana.storage.evm_compatibility.rs b/storage-proto/proto/solana.storage.evm_compatibility.rs index aefef27160..19da2fbf0e 100644 --- a/storage-proto/proto/solana.storage.evm_compatibility.rs +++ b/storage-proto/proto/solana.storage.evm_compatibility.rs @@ -56,6 +56,8 @@ pub struct UnsignedTransactionWithCaller { pub caller: std::vec::Vec, #[prost(uint64, tag = "3")] pub chain_id: u64, + #[prost(bool, tag = "4")] + pub signed_compatible: bool, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct TransactionInReceipt { diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 1039cec8db..192ab59b90 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -1042,8 +1042,9 @@ impl From let bytes = rlp::encode(&unsigned.unsigned_tx); Self { rlp_encoded_body: bytes.to_vec(), - chain_id: unsigned.chain_id.unwrap_or_default(), + chain_id: unsigned.chain_id, caller: unsigned.caller.into_vec(), + signed_compatible: unsigned.signed_compatible, } } } @@ -1055,13 +1056,9 @@ impl TryFrom fn try_from( unsigned: generated_evm::UnsignedTransactionWithCaller, ) -> Result { - let chain_id = if unsigned.chain_id == 0 { - None - } else { - unsigned.chain_id.into() - }; Ok(Self { - chain_id, + chain_id: unsigned.chain_id, + signed_compatible: unsigned.signed_compatible, caller: convert_from_bytes(unsigned.caller)?, unsigned_tx: rlp::decode(&unsigned.rlp_encoded_body) .map_err(|_| "Failed to deserialize rlp tx body")?, @@ -1896,7 +1893,8 @@ mod test { input: b"random bytes".to_vec(), }, caller: evm_state::H160::random(), - chain_id: Some(0xdead), + chain_id: 0xdead, + signed_compatible: true, }); let tx_serialized: generated_evm::TransactionInReceipt = tx.clone().into(); assert_eq!(tx, tx_serialized.try_into().unwrap()); @@ -1911,7 +1909,8 @@ mod test { input: b"123random bytes".to_vec(), }, caller: evm_state::H160::random(), - chain_id: Some(0xde2d), + chain_id: 0xde2d, + signed_compatible: false, }); let tx_serialized: generated_evm::TransactionInReceipt = tx.clone().into(); assert_eq!(tx, tx_serialized.try_into().unwrap()); @@ -1946,7 +1945,8 @@ mod test { input: b"random bytes".to_vec(), }, caller: evm_state::H160::random(), - chain_id: Some(0xdead), + chain_id: 0xdead, + signed_compatible: true, }); let receipt = evm_state::TransactionReceipt { transaction: tx, @@ -2023,7 +2023,8 @@ mod test { input: b"random bytes".to_vec(), }, caller: evm_state::H160::random(), - chain_id: Some(0xdead), + chain_id: 0xdead, + signed_compatible: false, }); let tx2 = evm_state::TransactionInReceipt::Signed(evm_state::Transaction { diff --git a/storage-proto/src/evm.proto b/storage-proto/src/evm.proto index 410bd7fbac..aa2234cea9 100644 --- a/storage-proto/src/evm.proto +++ b/storage-proto/src/evm.proto @@ -36,6 +36,7 @@ message UnsignedTransactionWithCaller { bytes rlp_encoded_body = 1; bytes caller = 2; uint64 chain_id = 3; + bool signed_compatible = 4; } message TransactionInReceipt {