Skip to content

Commit

Permalink
feat: impl zks_getBlockDetails (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
grw-ms authored Oct 17, 2023
1 parent d802216 commit b13fa9d
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 8 deletions.
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

0 comments on commit b13fa9d

Please sign in to comment.