Skip to content

Commit

Permalink
Handle empty bytecode case in get_code (#954)
Browse files Browse the repository at this point in the history
* Treat empty bytecode case in get_code

* clean up

* clean up

* clean up

* fix bytecode call

* update
  • Loading branch information
tcoratger authored Apr 11, 2024
1 parent 3191864 commit a6b92e0
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 20 deletions.
38 changes: 29 additions & 9 deletions src/eth_provider/provider.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::eth_provider::starknet::kakarot_core::account_contract::BytecodeOutput;
use alloy_rlp::Decodable as _;
use async_trait::async_trait;
use auto_impl::auto_impl;
use cainome::cairo_serde::CairoArrayLegacy;
use cainome::cairo_serde::CairoSerde;
use eyre::Result;
use itertools::Itertools;
use mongodb::bson::doc;
Expand All @@ -16,7 +18,8 @@ use reth_rpc_types::{
};
use reth_rpc_types::{SyncInfo, SyncStatus};
use reth_rpc_types_compat::transaction::from_recovered;
use starknet::core::types::SyncStatusType;
use starknet::core::types::{FunctionCall, SyncStatusType};
use starknet::core::utils::get_selector_from_name;
use starknet::core::utils::get_storage_var_address;
use starknet_crypto::FieldElement;

Expand Down Expand Up @@ -343,18 +346,36 @@ where
}

async fn get_code(&self, address: Address, block_id: Option<BlockId>) -> EthProviderResult<Bytes> {
let starknet_block_id = self.to_starknet_block_id(block_id).await?;

let address = starknet_address(address);
let account_contract = AccountContractReader::new(address, &self.starknet_provider);
let bytecode = account_contract.bytecode().block_id(starknet_block_id).call().await;
// TODO: temporary fix to handle empty bytecode until
// https://github.com/cartridge-gg/cainome/issues/24 is solved
let bytecode = self
.starknet_provider
.call(
FunctionCall {
contract_address: starknet_address(address),
entry_point_selector: get_selector_from_name("bytecode").unwrap(),
calldata: Default::default(),
},
self.to_starknet_block_id(block_id).await?,
)
.await
.map_err(cainome::cairo_serde::Error::Provider);

if contract_not_found(&bytecode) || entrypoint_not_found(&bytecode) {
return Ok(Bytes::default());
}

let bytecode = bytecode.map_err(KakarotError::from)?.bytecode.0;
Ok(Bytes::from(try_from_u8_iterator::<_, Vec<u8>>(bytecode)))
let bytecode_raw = bytecode.map_err(KakarotError::from)?;

// If the bytecode is empty, return an empty Bytes
if bytecode_raw.len() == 1 && bytecode_raw[0] == FieldElement::ZERO {
return Ok(Bytes::default());
}

// Deserialize the bytecode
Ok(Bytes::from(try_from_u8_iterator::<_, Vec<u8>>(
BytecodeOutput::cairo_deserialize(&bytecode_raw, 0).map_err(KakarotError::from)?.bytecode.0,
)))
}

async fn get_logs(&self, filter: Filter) -> EthProviderResult<FilterChanges> {
Expand Down Expand Up @@ -862,7 +883,6 @@ where
use crate::eth_provider::constant::{DEPLOY_WALLET, DEPLOY_WALLET_NONCE};
use starknet::accounts::{Call, Execution};
use starknet::core::types::BlockTag;
use starknet::core::utils::get_selector_from_name;

let signer_starknet_address = starknet_address(signer);
let account_contract = AccountContractReader::new(signer_starknet_address, &self.starknet_provider);
Expand Down
34 changes: 26 additions & 8 deletions src/test_utils/eoa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::Arc;
use async_trait::async_trait;
use ethers::abi::Tokenize;
use ethers::signers::{LocalWallet, Signer};
use ethers_solc::artifacts::CompactContractBytecode;
use reth_primitives::{
sign_message, Address, Bytes, Transaction, TransactionKind, TransactionSigned, TxEip1559, B256, U256,
};
Expand Down Expand Up @@ -83,26 +84,43 @@ impl<P: Provider + Send + Sync> KakarotEOA<P> {

pub async fn deploy_evm_contract<T: Tokenize>(
&self,
contract_name: &str,
contract_name: Option<&str>,
constructor_args: T,
) -> Result<KakarotEvmContract, eyre::Error> {
let nonce = self.nonce().await?;
let nonce: u64 = nonce.try_into()?;
let chain_id = self.eth_provider.chain_id().await?.unwrap_or_default();

let bytecode = <KakarotEvmContract as EvmContract>::load_contract_bytecode(contract_name)?;
// Empty bytecode if contract_name is None
let bytecode = if let Some(name) = contract_name {
<KakarotEvmContract as EvmContract>::load_contract_bytecode(name)?
} else {
CompactContractBytecode::default()
};

let expected_address = {
let expected_eth_address = self.evm_address().expect("Failed to get EVM address").create(nonce);
FieldElement::from_byte_slice_be(expected_eth_address.as_slice())
.expect("Failed to convert address to field element")
};

let tx = <KakarotEvmContract as EvmContract>::prepare_create_transaction(
&bytecode,
constructor_args,
nonce,
chain_id.try_into()?,
)?;
let tx = if contract_name.is_none() {
Transaction::Eip1559(TxEip1559 {
chain_id: chain_id.try_into()?,
nonce,
gas_limit: TX_GAS_LIMIT,
to: TransactionKind::Create,
value: U256::ZERO,
..Default::default()
})
} else {
<KakarotEvmContract as EvmContract>::prepare_create_transaction(
&bytecode,
constructor_args,
nonce,
chain_id.try_into()?,
)?
};
let tx_signed = self.sign_transaction(tx)?;
let tx_hash = self.send_transaction(tx_signed).await?;
let tx_hash: Felt252Wrapper = tx_hash.try_into().expect("Tx Hash should fit into Felt252Wrapper");
Expand Down
13 changes: 11 additions & 2 deletions src/test_utils/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@ use {
#[awt]
pub async fn counter(#[future] katana: Katana) -> (Katana, KakarotEvmContract) {
let eoa = katana.eoa();
let contract = eoa.deploy_evm_contract("Counter", ()).await.expect("Failed to deploy Counter contract");
let contract = eoa.deploy_evm_contract(Some("Counter"), ()).await.expect("Failed to deploy Counter contract");
(katana, contract)
}

#[cfg(any(test, feature = "arbitrary", feature = "testing"))]
#[fixture]
#[awt]
pub async fn contract_empty(#[future] katana: Katana) -> (Katana, KakarotEvmContract) {
let eoa = katana.eoa();
let contract = eoa.deploy_evm_contract(None, ()).await.expect("Failed to deploy empty contract");
(katana, contract)
}

Expand All @@ -22,7 +31,7 @@ pub async fn erc20(#[future] katana: Katana) -> (Katana, KakarotEvmContract) {
let eoa = katana.eoa();
let contract = eoa
.deploy_evm_contract(
"ERC20",
Some("ERC20"),
(
Token::String("Test".into()), // name
Token::String("TT".into()), // symbol
Expand Down
21 changes: 20 additions & 1 deletion tests/eth_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use kakarot_rpc::eth_provider::provider::EthereumProvider;
use kakarot_rpc::models::felt::Felt252Wrapper;
use kakarot_rpc::test_utils::eoa::Eoa as _;
use kakarot_rpc::test_utils::evm_contract::EvmContract;
use kakarot_rpc::test_utils::fixtures::{counter, katana, setup};
use kakarot_rpc::test_utils::fixtures::{contract_empty, counter, katana, setup};
use kakarot_rpc::test_utils::mongo::{BLOCK_HASH, BLOCK_NUMBER};
use kakarot_rpc::test_utils::{evm_contract::KakarotEvmContract, katana::Katana};
use reth_primitives::serde_helper::{JsonStorageKey, U64HexOrNumber};
Expand Down Expand Up @@ -218,6 +218,25 @@ async fn test_get_code(#[future] counter: (Katana, KakarotEvmContract), _setup:
assert_eq!(bytecode, Bytes::from(expected));
}

#[rstest]
#[awt]
#[tokio::test(flavor = "multi_thread")]
async fn test_get_code_empty(#[future] contract_empty: (Katana, KakarotEvmContract), _setup: ()) {
// Given
let katana: Katana = contract_empty.0;

let counter = contract_empty.1;
let eth_provider = katana.eth_provider();
let counter_address: Felt252Wrapper = counter.evm_address.into();
let counter_address = counter_address.try_into().expect("Failed to convert EVM address");

// When
let bytecode = eth_provider.get_code(counter_address, None).await.unwrap();

// Then
assert_eq!(bytecode, Bytes::default());
}

#[rstest]
#[awt]
#[tokio::test(flavor = "multi_thread")]
Expand Down

0 comments on commit a6b92e0

Please sign in to comment.