Skip to content

Commit

Permalink
feat(core): adds a get proof endpoint in zks namespace (#455)
Browse files Browse the repository at this point in the history
## What ❔

This PR adds a new JSON-RPC endpoint `zks_getProof` that allows users to
request proofs
of data in the tree that can later be verified against storarage roots
at a given point
in batch history.

---------

Co-authored-by: Roman Brodetski <[email protected]>
  • Loading branch information
montekki and RomanBrodetski authored Nov 14, 2023
1 parent 38384e7 commit f4313a4
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 10 deletions.
7 changes: 7 additions & 0 deletions core/lib/config/src/configs/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ pub struct Web3JsonRpcConfig {
/// The value is per active connection.
/// Note: For HTTP, rate limiting is expected to be configured on the infra level.
pub websocket_requests_per_minute_limit: Option<u32>,
/// Tree API url, currently used to proxy `getProof` calls to the tree
pub tree_api_url: Option<String>,
}

impl Web3JsonRpcConfig {
Expand Down Expand Up @@ -123,6 +125,7 @@ impl Web3JsonRpcConfig {
max_batch_request_size: Default::default(),
max_response_body_size_mb: Default::default(),
websocket_requests_per_minute_limit: Default::default(),
tree_api_url: None,
}
}

Expand Down Expand Up @@ -205,6 +208,10 @@ impl Web3JsonRpcConfig {
// The default limit is chosen to be reasonably permissive.
self.websocket_requests_per_minute_limit.unwrap_or(6000)
}

pub fn tree_api_url(&self) -> Option<String> {
self.tree_api_url.clone()
}
}

#[derive(Debug, Deserialize, Clone, PartialEq)]
Expand Down
1 change: 1 addition & 0 deletions core/lib/env_config/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ mod tests {
max_batch_request_size: Some(200),
max_response_body_size_mb: Some(10),
websocket_requests_per_minute_limit: Some(10),
tree_api_url: None,
},
contract_verification: ContractVerificationApiConfig {
port: 3070,
Expand Down
16 changes: 16 additions & 0 deletions core/lib/types/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,3 +684,19 @@ pub struct L1BatchDetails {
#[serde(flatten)]
pub base: BlockDetailsBase,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StorageProof {
pub key: H256,
pub proof: Vec<H256>,
pub value: H256,
pub index: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Proof {
pub address: Address,
pub storage_proof: Vec<StorageProof>,
}
2 changes: 2 additions & 0 deletions core/lib/web3_decl/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ pub enum Web3Error {
InvalidFilterBlockHash,
#[error("Query returned more than {0} results. Try smaller range of blocks")]
TooManyLogs(usize),
#[error("Tree API is not available")]
TreeApiUnavailable,
}
3 changes: 1 addition & 2 deletions core/lib/zksync_core/src/api_server/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,13 @@ impl TreeApiClient for AsyncTreeReader {

/// [`TreeApiClient`] implementation requesting data from a Merkle tree API server.
#[derive(Debug, Clone)]
pub(crate) struct TreeApiHttpClient {
pub struct TreeApiHttpClient {
inner: reqwest::Client,
info_url: String,
proofs_url: String,
}

impl TreeApiHttpClient {
#[cfg(test)] // temporary measure until `TreeApiClient` is required by other components
pub fn new(url_base: &str) -> Self {
Self {
inner: reqwest::Client::new(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn into_jsrpc_error(err: Web3Error) -> Error {
Web3Error::SubmitTransactionError(_, _) | Web3Error::SerializationError(_) => 3.into(),
Web3Error::PubSubTimeout => 4.into(),
Web3Error::RequestTimeout => 5.into(),
Web3Error::TreeApiUnavailable => 6.into(),
},
message: match err {
Web3Error::SubmitTransactionError(_, _) => err.to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use jsonrpc_derive::rpc;
// Workspace uses
use zksync_types::{
api::{
BlockDetails, BridgeAddresses, L1BatchDetails, L2ToL1LogProof, ProtocolVersion,
BlockDetails, BridgeAddresses, L1BatchDetails, L2ToL1LogProof, Proof, ProtocolVersion,
TransactionDetails,
},
fee::Fee,
Expand Down Expand Up @@ -111,6 +111,14 @@ pub trait ZksNamespaceT {

#[rpc(name = "zks_getLogsWithVirtualBlocks")]
fn get_logs_with_virtual_blocks(&self, filter: Filter) -> BoxFuture<Result<Vec<Log>>>;

#[rpc(name = "zks_getProof")]
fn get_proof(
&self,
address: Address,
keys: Vec<H256>,
l1_batch_number: L1BatchNumber,
) -> BoxFuture<Result<Proof>>;
}

impl<G: L1GasPriceProvider + Send + Sync + 'static> ZksNamespaceT for ZksNamespace<G> {
Expand Down Expand Up @@ -308,4 +316,19 @@ impl<G: L1GasPriceProvider + Send + Sync + 'static> ZksNamespaceT for ZksNamespa
.map_err(into_jsrpc_error)
})
}

fn get_proof(
&self,
address: Address,
keys: Vec<H256>,
l1_batch_number: L1BatchNumber,
) -> BoxFuture<Result<Proof>> {
let self_ = self.clone();
Box::pin(async move {
self_
.get_proofs_impl(address, keys.clone(), l1_batch_number)
.await
.map_err(into_jsrpc_error)
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub fn into_jsrpc_error(err: Web3Error) -> ErrorObjectOwned {
Web3Error::SubmitTransactionError(_, _) | Web3Error::SerializationError(_) => 3,
Web3Error::PubSubTimeout => 4,
Web3Error::RequestTimeout => 5,
Web3Error::TreeApiUnavailable => 6,
},
match err {
Web3Error::SubmitTransactionError(ref message, _) => message.clone(),
Expand Down
12 changes: 11 additions & 1 deletion core/lib/zksync_core/src/api_server/web3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use zksync_web3_decl::{

use crate::{
api_server::{
execution_sandbox::VmConcurrencyBarrier, tx_sender::TxSender,
execution_sandbox::VmConcurrencyBarrier, tree::TreeApiHttpClient, tx_sender::TxSender,
web3::backend_jsonrpc::batch_limiter_middleware::RateLimitMetadata,
},
l1_gas_price::L1GasPriceProvider,
Expand Down Expand Up @@ -136,6 +136,7 @@ pub struct ApiBuilder<G> {
polling_interval: Option<Duration>,
namespaces: Option<Vec<Namespace>>,
logs_translator_enabled: bool,
tree_api_url: Option<String>,
}

impl<G> ApiBuilder<G> {
Expand All @@ -159,6 +160,7 @@ impl<G> ApiBuilder<G> {
namespaces: None,
config,
logs_translator_enabled: false,
tree_api_url: None,
}
}

Expand Down Expand Up @@ -255,6 +257,11 @@ impl<G> ApiBuilder<G> {
self.logs_translator_enabled = true;
self
}

pub fn with_tree_api(mut self, tree_api_url: Option<String>) -> Self {
self.tree_api_url = tree_api_url;
self
}
}

impl<G: 'static + Send + Sync + L1GasPriceProvider> ApiBuilder<G> {
Expand All @@ -280,6 +287,9 @@ impl<G: 'static + Send + Sync + L1GasPriceProvider> ApiBuilder<G> {
api_config: self.config,
last_sealed_miniblock,
logs_translator_enabled: self.logs_translator_enabled,
tree_api: self
.tree_api_url
.map(|url| TreeApiHttpClient::new(url.as_str())),
}
}

Expand Down
52 changes: 46 additions & 6 deletions core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@ use zksync_dal::StorageProcessor;
use zksync_mini_merkle_tree::MiniMerkleTree;
use zksync_types::{
api::{
BlockDetails, BridgeAddresses, GetLogsFilter, L1BatchDetails, L2ToL1LogProof,
ProtocolVersion, TransactionDetails,
BlockDetails, BridgeAddresses, GetLogsFilter, L1BatchDetails, L2ToL1LogProof, Proof,
ProtocolVersion, StorageProof, TransactionDetails,
},
fee::Fee,
l1::L1Tx,
l2::L2Tx,
l2_to_l1_log::L2ToL1Log,
tokens::ETHEREUM_ADDRESS,
transaction_request::CallRequest,
L1BatchNumber, MiniblockNumber, Transaction, L1_MESSENGER_ADDRESS, L2_ETH_TOKEN_ADDRESS,
MAX_GAS_PER_PUBDATA_BYTE, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, U256, U64,
AccountTreeId, L1BatchNumber, MiniblockNumber, StorageKey, Transaction, L1_MESSENGER_ADDRESS,
L2_ETH_TOKEN_ADDRESS, MAX_GAS_PER_PUBDATA_BYTE, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, U256,
U64,
};
use zksync_utils::{address_to_h256, ratio_to_big_decimal_normalized};
use zksync_web3_decl::{
error::Web3Error,
types::{Address, Filter, Log, Token, H256},
};

use crate::api_server::web3::{
backend_jsonrpc::error::internal_error, metrics::API_METRICS, RpcState,
use crate::api_server::{
tree::TreeApiClient,
web3::{backend_jsonrpc::error::internal_error, metrics::API_METRICS, RpcState},
};
use crate::l1_gas_price::L1GasPriceProvider;

Expand Down Expand Up @@ -620,4 +622,42 @@ impl<G: L1GasPriceProvider> ZksNamespace<G> {
) -> Result<Vec<Log>, Web3Error> {
self.state.translate_get_logs(filter).await
}

#[tracing::instrument(skip_all)]
pub async fn get_proofs_impl(
&self,
address: Address,
keys: Vec<H256>,
l1_batch_number: L1BatchNumber,
) -> Result<Proof, Web3Error> {
const METHOD_NAME: &str = "get_proofs";

let hashed_keys = keys
.iter()
.map(|key| StorageKey::new(AccountTreeId::new(address), *key).hashed_key_u256())
.collect();

let storage_proof = self
.state
.tree_api
.as_ref()
.ok_or(Web3Error::TreeApiUnavailable)?
.get_proofs(l1_batch_number, hashed_keys)
.await
.map_err(|err| internal_error(METHOD_NAME, err))?
.into_iter()
.zip(keys)
.map(|(proof, key)| StorageProof {
key,
proof: proof.merkle_path,
value: proof.value,
index: proof.index,
})
.collect();

Ok(Proof {
address,
storage_proof,
})
}
}
3 changes: 3 additions & 0 deletions core/lib/zksync_core/src/api_server/web3/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use super::metrics::API_METRICS;
use crate::{
api_server::{
execution_sandbox::BlockArgs,
tree::TreeApiHttpClient,
tx_sender::TxSender,
web3::{
backend_jsonrpc::error::internal_error, namespaces::eth::EVENT_TOPIC_NUMBER_LIMIT,
Expand Down Expand Up @@ -167,6 +168,7 @@ impl SealedMiniblockNumber {
pub struct RpcState<E> {
pub installed_filters: Arc<RwLock<Filters>>,
pub connection_pool: ConnectionPool,
pub tree_api: Option<TreeApiHttpClient>,
pub tx_sender: TxSender<E>,
pub sync_state: Option<SyncState>,
pub(super) api_config: InternalApiConfig,
Expand All @@ -185,6 +187,7 @@ impl<E> Clone for RpcState<E> {
installed_filters: self.installed_filters.clone(),
connection_pool: self.connection_pool.clone(),
tx_sender: self.tx_sender.clone(),
tree_api: self.tree_api.clone(),
sync_state: self.sync_state.clone(),
api_config: self.api_config.clone(),
last_sealed_miniblock: self.last_sealed_miniblock.clone(),
Expand Down
1 change: 1 addition & 0 deletions core/lib/zksync_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,7 @@ async fn run_ws_api<G: L1GasPriceProvider + Send + Sync + 'static>(
)
.with_polling_interval(api_config.web3_json_rpc.pubsub_interval())
.with_threads(api_config.web3_json_rpc.ws_server_threads())
.with_tree_api(api_config.web3_json_rpc.tree_api_url())
.with_tx_sender(tx_sender, vm_barrier)
.enable_api_namespaces(Namespace::NON_DEBUG.to_vec());

Expand Down

0 comments on commit f4313a4

Please sign in to comment.