Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion monad-eth-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::{fmt::Debug, ops::Deref};
use ::serde::Serialize;
use alloy_consensus::{transaction::Recovered, Header, TxEnvelope};
use alloy_eips::eip7702::RecoveredAuthorization;
use alloy_primitives::{Address, B256};
use alloy_primitives::{Address, FixedBytes, B256};
use alloy_rlp::{
Decodable, Encodable, RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper,
};
Expand All @@ -44,6 +44,7 @@ pub struct EthAccount {
pub nonce: Nonce,
pub balance: Balance,
pub code_hash: Option<B256>,
pub inline_code: Option<FixedBytes<23>>,
pub is_delegated: bool,
Comment on lines 46 to 48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest consolidating these three fields to a single Vec<u8> field but provide helper methods like has_code(), is_inline_delegated() and get_code_hash() (similar to the Account in execution).
The only con of this is this will require computing code hash on demand for delegated accounts, which adds a keccak operation. However, this should only impact eth_getAccount in RPC iiuc, and delegated accounts are expected to be a small portion, thus the impact is minimal.

}

Expand Down
12 changes: 8 additions & 4 deletions monad-rpc/src/handlers/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,11 +689,15 @@ async fn include_code_output<T: Triedb>(
.get_account(block_key, contract_addr.0.into())
.await
.map_err(JsonRpcError::internal_error)?;
let code = triedb_env
.get_code(block_key, account.code_hash)
.await
.map_err(JsonRpcError::internal_error)?;

let code = if account.inline_code.is_some() {
format!("0x{:x}", account.inline_code.unwrap())
} else {
triedb_env
.get_code_db(block_key, account.code_hash)
.await
.map_err(JsonRpcError::internal_error)?
};
let decoded_code = hex::decode(&code)
.map_err(|_| JsonRpcError::internal_error("could not decode code".to_string()))?;
frame.output = decoded_code.into();
Expand Down
12 changes: 8 additions & 4 deletions monad-rpc/src/handlers/eth/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,14 @@ pub async fn monad_eth_getCode<T: Triedb>(
.await
.map_err(JsonRpcError::internal_error)?;

let code = triedb_env
.get_code(block_key, account.code_hash)
.await
.map_err(JsonRpcError::internal_error)?;
let code = if account.inline_code.is_some() {
format!("0x{:x}", account.inline_code.unwrap())
} else {
triedb_env
.get_code_db(block_key, account.code_hash)
.await
.map_err(JsonRpcError::internal_error)?
};

match triedb_env
.get_state_availability(block_key)
Expand Down
1 change: 1 addition & 0 deletions monad-state-backend/src/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ where
nonce: *nonce,
balance: self.max_account_balance,
code_hash: None,
inline_code: None,
is_delegated: false,
})
})
Expand Down
1 change: 1 addition & 0 deletions monad-state-backend/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ where
balance: self.balances.get(address).cloned().unwrap_or_default(),
nonce: self.nonces.get(address).cloned().unwrap_or_default(),
code_hash: None,
inline_code: None,
is_delegated: false,
})
})
Expand Down
32 changes: 29 additions & 3 deletions monad-triedb-utils/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

use alloy_primitives::{B256, U256};
use alloy_primitives::{keccak256, FixedBytes, B256, U256};
use alloy_rlp::Decodable;
use monad_eth_types::EthAccount;
use tracing::warn;
Expand Down Expand Up @@ -47,23 +47,49 @@ pub fn rlp_decode_account(account_rlp: Vec<u8>) -> Option<EthAccount> {
return None;
};

let is_delegated = false;
let mut is_delegated = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i feel like having this is_delegated field here is error prone. it's defaulted to false for legacy accounts and only updated in get_accounts_async, whereas for new accounts it's the actual value here. I think they should be updated at the same place

let mut inline_code = None;
let code_hash = if buf.is_empty() {
None
} else {
} else if buf.len() == 33 {
match <[u8; 32]>::decode(&mut buf) {
Ok(x) => Some(x),
Err(e) => {
warn!("rlp code_hash decode failed: {:?}", e);
return None;
}
}
} else if buf.len() != 24 {
Comment on lines +54 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the significance of length 33 and 24?

warn!(
"The stored code_hash length is not expected {:?}",
buf.len()
);
return None;
} else {
match <[u8; 23]>::decode(&mut buf) {
Ok(x) => {
is_delegated = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't seem right -- is_delegated should only be true if it's prefixed with 0xef0100 right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the decoded code field is inline, they are always 23 bytes and represents delegated code based on how execution stores it, but yeah we should add an assertion checking the prefix here.

inline_code = Some(x);
match keccak256(x).as_slice().try_into() {
Ok(hash) => Some(hash),
Err(e) => {
warn!("error extracting code_hash {:?}", e);
return None;
}
}
}
Err(e) => {
warn!("rlp delegated code_hash decode failed: {:?}", e);
return None;
}
}
};

Some(EthAccount {
nonce,
balance,
code_hash: code_hash.map(B256::from),
inline_code: inline_code.map(FixedBytes::<23>::from),
is_delegated,
})
}
Expand Down
42 changes: 24 additions & 18 deletions monad-triedb-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ impl TriedbReader {
let completed_counter = Arc::new(AtomicUsize::new(0));
let mut num_accounts = 0;
let eth_account_receivers = eth_addresses.map(|eth_address| {
trace!(?eth_address, block_id = ?seq_num.0, "reading account code_hash");
num_accounts += 1;
let (triedb_key, key_len_nibbles) =
create_triedb_key(version, KeyInput::Address(eth_address.as_ref()));
Expand All @@ -177,29 +178,34 @@ impl TriedbReader {
match res {
Some(mut eth_account) => {
trace!(?eth_account, block_id = ?seq_num.0, "account code_hash");
match eth_account.code_hash {
Some(code_hash) => {
// Request code
let (triedb_key, key_len_nibbles) =
create_triedb_key(version, KeyInput::CodeHash(&code_hash));
let res = self.handle.read(&triedb_key, key_len_nibbles, seq_num.0);
trace!(?res, block_id = ?seq_num.0, ?eth_account, "account code_data");
match res {
Some(data) => {
if data.len() >= 3 {
let delegation_code = &data[0..3];
eth_account.is_delegated =
delegation_code == [0xef, 0x01, 0x00];
if eth_account.is_delegated {
trace!(?eth_account, block_id = ?seq_num.0, "is_delegated == true");
if eth_account.inline_code.is_none() { // legacy accounts have hash_code set but no inline_code.
match eth_account.code_hash {
Some(code_hash) => {
// Request code
let (triedb_key, key_len_nibbles) =
create_triedb_key(version, KeyInput::CodeHash(&code_hash));
let res = self.handle.read(&triedb_key, key_len_nibbles, seq_num.0);
trace!(?res, block_id = ?seq_num.0, ?eth_account, "account code_data");
match res {
Some(data) => {
if data.len() >= 3 {
let delegation_code = &data[0..3];
eth_account.is_delegated =
delegation_code == [0xef, 0x01, 0x00];
if eth_account.is_delegated {
trace!(?eth_account, block_id = ?seq_num.0, "is_delegated == true");
}
}
Some(eth_account)
}
Some(eth_account)
None => Some(eth_account),
}
None => Some(eth_account),
}
None => Some(eth_account),
}
None => Some(eth_account),
}
else {
Some(eth_account)
}
}
None => None,
Expand Down
2 changes: 1 addition & 1 deletion monad-triedb-utils/src/mock_triedb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl Triedb for MockTriedb {
ready(Ok("0x0".to_string()))
}

fn get_code(
fn get_code_db(
&self,
_block_key: BlockKey,
_code_hash: EthCodeHash,
Expand Down
21 changes: 13 additions & 8 deletions monad-triedb-utils/src/triedb_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use futures::{channel::oneshot, FutureExt};
use monad_triedb::{TraverseEntry, TriedbHandle};
use monad_types::{BlockId, Hash, SeqNum};
use serde::{Deserialize, Serialize};
use tracing::{error, warn};
use tracing::{debug, error, warn};

use crate::{
decode::{
Expand Down Expand Up @@ -96,6 +96,7 @@ pub struct Account {
pub nonce: u64,
pub balance: U256,
pub code_hash: [u8; 32],
pub inline_code: Option<FixedBytes<23>>,
}

#[derive(Debug, Clone, Default)]
Expand Down Expand Up @@ -497,7 +498,7 @@ pub trait Triedb: Debug {
addr: EthAddress,
at: EthStorageKey,
) -> impl std::future::Future<Output = Result<String, String>> + Send;
fn get_code(
fn get_code_db(
&self,
key: BlockKey,
code_hash: EthCodeHash,
Expand Down Expand Up @@ -958,11 +959,15 @@ impl Triedb for TriedbEnv {
})
.await?
{
Some(account) => Ok(Account {
nonce: account.nonce,
balance: account.balance,
code_hash: account.code_hash.map_or([0u8; 32], |bytes| bytes.0),
}),
Some(account) => {
debug!(?addr, "code_hash decoded account");
Ok(Account {
nonce: account.nonce,
balance: account.balance,
code_hash: account.code_hash.map_or([0u8; 32], |bytes| bytes.0),
inline_code: account.inline_code,
})
}
None => Ok(Account::default()),
}
}
Expand All @@ -989,7 +994,7 @@ impl Triedb for TriedbEnv {
}

#[tracing::instrument(level = "debug")]
async fn get_code(
async fn get_code_db(
&self,
block_key: BlockKey,
code_hash: EthCodeHash,
Expand Down
Loading