Skip to content
Draft
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
45 changes: 43 additions & 2 deletions lib/rpc/src/zks_impl.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::ReadRpcStorage;
use crate::log_proof_utils::{batch_tree_proof, chain_proof_vector, get_chain_log_proof};
use crate::result::ToRpcResult;
use alloy::primitives::{Address, B256, BlockNumber, TxHash, U64, U256, keccak256};
use alloy::primitives::{Address, B256, BlockNumber, Bytes, TxHash, U64, U256, keccak256};
use alloy::providers::{DynProvider, Provider};
use alloy::rpc::types::Index;
use anyhow::Context;
Expand All @@ -22,7 +22,9 @@ use zksync_os_rpc_api::{
},
zks::ZksApiServer,
};
use zksync_os_storage_api::{PersistedBatch, RepositoryError, StateError, read_multichain_root};
use zksync_os_storage_api::{
PersistedBatch, RepositoryError, StateError, ViewState, read_multichain_root,
};
use zksync_os_types::L2_TO_L1_TREE_SIZE;

const LOG_PROOF_SUPPORTED_METADATA_VERSION: u8 = 1;
Expand Down Expand Up @@ -415,6 +417,35 @@ impl<RpcStorage: ReadRpcStorage> ZksNamespace<RpcStorage> {
l1_verification_data,
}))
}

/// Fetch the 124-byte `AccountProperties::encoding()` preimage for
/// `address` at the end of L1 batch `batch_number`.
///
/// See [`ZksApi::get_account_preimage`] for rationale — selective-
/// disclosure tooling needs the full encoded preimage to verify
/// individual `AccountProperties` fields against the blake2s hash
/// stored in the state tree at the account-properties slot.
///
/// Implementation note: this thinly wraps the existing
/// `ViewState::get_account` path that `eth_getBalance`,
/// `eth_getCode`, etc. already rely on internally — it just hands
/// the raw encoding bytes back to the caller instead of extracting
/// a single field.
async fn get_account_preimage_impl(
&self,
address: Address,
batch_number: u64,
) -> ZksResult<Option<Bytes>> {
let Some(batch) = self.storage.batch().get_batch_by_number(batch_number)? else {
return Ok(None);
};
let last_block_number = batch.last_block_number();
let mut state_view = self.storage.state_view_at(last_block_number)?;
let Some(props) = state_view.get_account(address) else {
return Ok(None);
};
Ok(Some(Bytes::copy_from_slice(&props.encoding())))
}
}

#[async_trait]
Expand Down Expand Up @@ -465,6 +496,16 @@ impl<RpcStorage: ReadRpcStorage> ZksApiServer for ZksNamespace<RpcStorage> {
.await
.to_rpc_result()
}

async fn get_account_preimage(
&self,
account: Address,
batch_number: u64,
) -> RpcResult<Option<Bytes>> {
self.get_account_preimage_impl(account, batch_number)
.await
.to_rpc_result()
}
}

/// `zks` namespace result type.
Expand Down
37 changes: 36 additions & 1 deletion lib/rpc_api/src/zks.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::types::{BatchStorageProof, BlockMetadata, L2ToL1LogProof, LogProofTarget};
use alloy::primitives::{Address, B256, TxHash};
use alloy::primitives::{Address, B256, Bytes, TxHash};
use alloy::rpc::types::Index;
use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc;
Expand Down Expand Up @@ -42,4 +42,39 @@ pub trait ZksApi {
keys: Vec<B256>,
batch_number: u64,
) -> RpcResult<Option<BatchStorageProof>>;

/// Returns the 124-byte `AccountProperties::encoding()` preimage for
/// the given account at the end of the given L1 batch.
///
/// Each `AccountProperties` struct lives in the state tree as
/// `blake2s(AccountProperties::encoding())` at a slot keyed by the
/// account address under `ACCOUNT_PROPERTIES_STORAGE_ADDRESS`. The
/// Merkle proof for that slot — returned by [`Self::get_proof`] —
/// covers the hash, but not the preimage itself. This method exposes
/// the preimage bytes so that clients can reconstruct the full
/// `AccountProperties` struct and check individual fields (balance,
/// nonce, observable bytecode hash, versioning data, etc.) against
/// a zks_getProof-verified tree value.
///
/// The canonical user of this RPC is selective-disclosure tooling
/// that needs to prove claims like
/// `balance_of(l1_commitment, address, balance)` or
/// `observable_bytecode_hash(l1_commitment, address, hash)` inside
/// a zk circuit. Such tooling must hash the preimage bytes inside
/// the circuit and compare against the Merkle-proof-verified slot
/// value; partial reconstruction from `eth_getBalance` /
/// `eth_getCode` / `eth_getTransactionCount` is not sufficient
/// because `versioning_data`, `bytecode_hash`, and `artifacts_len`
/// are internal fields not otherwise exposed over the JSON-RPC
/// surface.
///
/// Returns `None` when the account does not exist at the queried
/// batch (i.e., the account has never been touched), matching the
/// semantics of a non-existing-slot `zks_getProof` result.
#[method(name = "getAccountPreimage")]
async fn get_account_preimage(
&self,
account: Address,
batch_number: u64,
) -> RpcResult<Option<Bytes>>;
}