Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: impl zks_getBlockDetails #182

Merged
Merged
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
25 changes: 24 additions & 1 deletion SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ The `status` options are:
| [`ZKS`](#zks-namespace) | [`zks_estimateFee`](#zks_estimateFee) | `SUPPORTED` | Gets the Fee estimation data for a given Request |
| `ZKS` | `zks_estimateGasL1ToL2` | `NOT IMPLEMENTED` | Estimate of the gas required for a L1 to L2 transaction |
| `ZKS` | `zks_getAllAccountBalances` | `NOT IMPLEMENTED` | Returns all balances for confirmed tokens given by an account address |
| `ZKS` | `zks_getBlockDetails` | `NOT IMPLEMENTED` | Returns additional zkSync-specific information about the L2 block |
| [`ZKS`](#zks-namespace) | [`zks_getBlockDetails`](#zks_getblockdetails) | `SUPPORTED` | Returns additional zkSync-specific information about the L2 block |
| `ZKS` | `zks_getBridgeContracts` | `NOT IMPLEMENTED` | Returns L1/L2 addresses of default bridges |
| `ZKS` | `zks_getBytecodeByHash` | `NOT IMPLEMENTED` | Returns bytecode of a transaction given by its hash |
| `ZKS` | `zks_getConfirmedTokens` | `NOT IMPLEMENTED` | Returns [address, symbol, name, and decimal] information of all tokens within a range of ids given by parameters `from` and `limit` |
Expand Down Expand Up @@ -1742,3 +1742,26 @@ curl --request POST \
--header 'content-type: application/json' \
--data '{"jsonrpc": "2.0","id": "1","method": "zks_getTransactionDetails","params": ["0xa5d62a85561295ed58f8daad4e9442691e6da4301a859f364d28a02917d6e04d"]}'
```

### `zks_getBlockDetails`

[source](src/zks.rs)

Returns additional zkSync-specific information about the L2 block.

#### Arguments

+ `block: u32` - The number of the block

#### Status

`SUPPORTED`

#### Example

```bash
curl --request POST \
--url http://localhost:8011/ \
--header 'content-type: application/json' \
--data '{"jsonrpc": "2.0", "id": 1, "method": "zks_getBlockDetails", "params": [ 140599 ]}'
```
15 changes: 15 additions & 0 deletions e2e-tests/test/zks-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,18 @@ describe("zks_getTransactionDetails", function () {
expect(details["initiatorAddress"].toLowerCase()).to.equal(wallet.address.toLowerCase());
});
});

describe("zks_getBlockDetails", function () {
it("Should return block details for locally-produced blocks", async function () {
const wallet = new Wallet(RichAccounts[0].PrivateKey);
const deployer = new Deployer(hre, wallet);

const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
await greeter.setGreeting("Luke Skywalker");

const latestBlock = await provider.getBlock("latest");
const details = await provider.send("zks_getBlockDetails", [latestBlock.number]);

expect(details["timestamp"]).to.equal(latestBlock.timestamp);
});
});
6 changes: 5 additions & 1 deletion src/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use zksync_basic_types::{Address, L1BatchNumber, L2ChainId, MiniblockNumber, H25

use zksync_types::{
api::{
Block, BlockIdVariant, BlockNumber, Transaction, TransactionDetails, TransactionVariant,
Block, BlockDetails, BlockIdVariant, BlockNumber, Transaction, TransactionDetails,
TransactionVariant,
},
l2::L2Tx,
ProtocolVersionId, StorageKey,
Expand Down Expand Up @@ -231,6 +232,9 @@ pub trait ForkSource {
full_transactions: bool,
) -> eyre::Result<Option<Block<TransactionVariant>>>;

/// Returns the block details for a given miniblock number.
fn get_block_details(&self, miniblock: MiniblockNumber) -> eyre::Result<Option<BlockDetails>>;

/// Returns the transaction count for a given block hash.
fn get_block_transaction_count_by_hash(&self, block_hash: H256) -> eyre::Result<Option<U256>>;

Expand Down
63 changes: 63 additions & 0 deletions src/http_fork_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,16 @@ impl ForkSource for HttpForkSource {
})
.wrap_err("fork http client failed")
}

/// Returns details of a block, given miniblock number
fn get_block_details(
&self,
miniblock: zksync_basic_types::MiniblockNumber,
) -> eyre::Result<Option<zksync_types::api::BlockDetails>> {
let client = self.create_client();
block_on(async move { client.get_block_details(miniblock).await })
.wrap_err("fork http client failed")
}
}

#[cfg(test)]
Expand Down Expand Up @@ -553,4 +563,57 @@ mod tests {
Address::from_str("0x63ab285cd87a189f345fed7dd4e33780393e01f0").unwrap()
);
}

#[test]
fn test_get_block_details() {
let miniblock = MiniblockNumber::from(16474138);
let mock_server = testing::MockServer::run();
mock_server.expect(
serde_json::json!({
"jsonrpc": "2.0",
"id": 0,
"method": "zks_getBlockDetails",
"params": [
miniblock.0,
],
}),
serde_json::json!({
"jsonrpc": "2.0",
"result": {
"number": 16474138,
"l1BatchNumber": 270435,
"timestamp": 1697405098,
"l1TxCount": 0,
"l2TxCount": 1,
"rootHash": "0xd9e60f9a684fd7fc16e87ae923341a6e4af24f286e76612efdfc2d55f3f4d064",
"status": "sealed",
"commitTxHash": null,
"committedAt": null,
"proveTxHash": null,
"provenAt": null,
"executeTxHash": null,
"executedAt": null,
"l1GasPrice": 6156252068u64,
"l2FairGasPrice": 250000000u64,
"baseSystemContractsHashes": {
"bootloader": "0x0100089b8a2f2e6a20ba28f02c9e0ed0c13d702932364561a0ea61621f65f0a8",
"default_aa": "0x0100067d16a5485875b4249040bf421f53e869337fe118ec747cf40a4c777e5f"
},
"operatorAddress": "0xa9232040bf0e0aea2578a5b2243f2916dbfc0a69",
"protocolVersion": "Version15"
},
"id": 0
}),
);

let fork_source = HttpForkSource::new(mock_server.url(), CacheConfig::Memory);
let block_details = fork_source
.get_block_details(miniblock)
.expect("failed fetching transaction")
.expect("no transaction");
assert_eq!(
block_details.operator_address,
Address::from_str("0xa9232040bf0e0aea2578a5b2243f2916dbfc0a69").unwrap()
);
}
}
172 changes: 166 additions & 6 deletions src/zks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ use std::sync::{Arc, RwLock};

use bigdecimal::BigDecimal;
use futures::FutureExt;
use zksync_basic_types::{MiniblockNumber, U256};
use zksync_basic_types::{Address, L1BatchNumber, MiniblockNumber, U256};
use zksync_core::api_server::web3::backend_jsonrpc::{
error::into_jsrpc_error, namespaces::zks::ZksNamespaceT,
};
use zksync_types::{
api::{BridgeAddresses, ProtocolVersion, TransactionDetails, TransactionStatus},
api::{
BlockDetails, BlockDetailsBase, BlockStatus, BridgeAddresses, ProtocolVersion,
TransactionDetails, TransactionStatus,
},
fee::Fee,
ProtocolVersionId,
};
use zksync_web3_decl::{
error::Web3Error,
Expand All @@ -17,7 +21,7 @@ use zksync_web3_decl::{

use crate::{
fork::ForkSource,
node::{InMemoryNodeInner, TransactionResult},
node::{InMemoryNodeInner, TransactionResult, L2_GAS_PRICE},
utils::{not_implemented, utc_datetime_from_epoch_ms, IntoBoxedFuture},
};
use colored::Colorize;
Expand Down Expand Up @@ -180,12 +184,75 @@ impl<S: Send + Sync + 'static + ForkSource + std::fmt::Debug> ZksNamespaceT
not_implemented("zks_L1BatchNumber")
}

/// Get block details.
///
/// # Arguments
///
/// * `blockNumber` - `u32` miniblock number
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with an `Option<BlockDetails>` representing details of the block (if found).
fn get_block_details(
&self,
_block_number: zksync_basic_types::MiniblockNumber,
block_number: zksync_basic_types::MiniblockNumber,
) -> jsonrpc_core::BoxFuture<jsonrpc_core::Result<Option<zksync_types::api::BlockDetails>>>
{
not_implemented("zks_getBlockDetails")
let inner = self.node.clone();
Box::pin(async move {
let reader = inner
.read()
.map_err(|_err| into_jsrpc_error(Web3Error::InternalError))?;

let maybe_block = reader
.block_hashes
.get(&(block_number.0 as u64))
.and_then(|hash| reader.blocks.get(hash))
.map(|block| BlockDetails {
number: MiniblockNumber(block.number.as_u32()),
l1_batch_number: L1BatchNumber(
block.l1_batch_number.unwrap_or_default().as_u32(),
),
base: BlockDetailsBase {
timestamp: block.timestamp.as_u64(),
l1_tx_count: 1,
l2_tx_count: block.transactions.len(),
root_hash: Some(block.hash),
status: BlockStatus::Verified,
commit_tx_hash: None,
committed_at: None,
prove_tx_hash: None,
proven_at: None,
execute_tx_hash: None,
executed_at: None,
l1_gas_price: 0,
l2_fair_gas_price: L2_GAS_PRICE,
base_system_contracts_hashes: reader
.system_contracts
.baseline_contracts
.hashes(),
},
operator_address: Address::zero(),
protocol_version: Some(ProtocolVersionId::latest()),
})
.or_else(|| {
reader
.fork_storage
.inner
.read()
.expect("failed reading fork storage")
.fork
.as_ref()
.and_then(|fork| {
fork.fork_source
.get_block_details(block_number)
.ok()
.flatten()
})
});

Ok(maybe_block)
})
}

fn get_miniblock_range(
Expand Down Expand Up @@ -320,7 +387,7 @@ mod tests {

use super::*;
use zksync_basic_types::{Address, H256};
use zksync_types::api::TransactionReceipt;
use zksync_types::api::{Block, TransactionReceipt, TransactionVariant};
use zksync_types::transaction_request::CallRequest;

#[tokio::test]
Expand Down Expand Up @@ -503,4 +570,97 @@ mod tests {
assert!(matches!(result.status, TransactionStatus::Included));
assert_eq!(result.fee, U256::from(127_720_500_000_000u64));
}

#[tokio::test]
async fn test_get_block_details_local() {
// Arrange
let node = InMemoryNode::<HttpForkSource>::default();
let namespace = ZkMockNamespaceImpl::new(node.get_inner());
let inner = node.get_inner();
{
let mut writer = inner.write().unwrap();
let block = Block::<TransactionVariant>::default();
writer.blocks.insert(H256::repeat_byte(0x1), block);
writer.block_hashes.insert(0, H256::repeat_byte(0x1));
}
// Act
let result = namespace
.get_block_details(MiniblockNumber(0))
.await
.expect("get block details")
.expect("block details");

// Assert
assert!(matches!(result.number, MiniblockNumber(0)));
assert_eq!(result.l1_batch_number, L1BatchNumber(0));
assert_eq!(result.base.timestamp, 0);
}

#[tokio::test]
async fn test_get_block_details_fork() {
let mock_server = MockServer::run_with_config(ForkBlockConfig {
number: 10,
transaction_count: 0,
hash: H256::repeat_byte(0xab),
});
let miniblock = MiniblockNumber::from(16474138);
mock_server.expect(
serde_json::json!({
"jsonrpc": "2.0",
"id": 0,
"method": "zks_getBlockDetails",
"params": [
miniblock.0,
],
}),
serde_json::json!({
"jsonrpc": "2.0",
"result": {
"number": 16474138,
"l1BatchNumber": 270435,
"timestamp": 1697405098,
"l1TxCount": 0,
"l2TxCount": 1,
"rootHash": "0xd9e60f9a684fd7fc16e87ae923341a6e4af24f286e76612efdfc2d55f3f4d064",
"status": "sealed",
"commitTxHash": null,
"committedAt": null,
"proveTxHash": null,
"provenAt": null,
"executeTxHash": null,
"executedAt": null,
"l1GasPrice": 6156252068u64,
"l2FairGasPrice": 250000000u64,
"baseSystemContractsHashes": {
"bootloader": "0x0100089b8a2f2e6a20ba28f02c9e0ed0c13d702932364561a0ea61621f65f0a8",
"default_aa": "0x0100067d16a5485875b4249040bf421f53e869337fe118ec747cf40a4c777e5f"
},
"operatorAddress": "0xa9232040bf0e0aea2578a5b2243f2916dbfc0a69",
"protocolVersion": "Version15"
},
"id": 0
}),
);

let node = InMemoryNode::<HttpForkSource>::new(
Some(ForkDetails::from_network(&mock_server.url(), None, CacheConfig::None).await),
crate::node::ShowCalls::None,
ShowStorageLogs::None,
ShowVMDetails::None,
ShowGasDetails::None,
false,
&system_contracts::Options::BuiltIn,
);

let namespace = ZkMockNamespaceImpl::new(node.get_inner());
let result = namespace
.get_block_details(miniblock)
.await
.expect("get block details")
.expect("block details");

assert!(matches!(result.number, MiniblockNumber(16474138)));
assert_eq!(result.l1_batch_number, L1BatchNumber(270435));
assert_eq!(result.base.timestamp, 1697405098);
}
}
11 changes: 11 additions & 0 deletions test_endpoints.http
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,17 @@ content-type: application/json
POST http://localhost:8011
content-type: application/json

{
"jsonrpc": "2.0",
"id": "1",
"method": "zks_getBlockDetails",
"params": [16474138]
}

###
POST http://localhost:8011
content-type: application/json

{
"jsonrpc": "2.0",
"id": "1",
Expand Down