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_getRawBlockTransactions #185

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
27 changes: 25 additions & 2 deletions SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ The `status` options are:
| `ZKS` | `zks_getL2ToL1LogProof` | `NOT IMPLEMENTED` | Given a transaction hash, and an index of the L2 to L1 log produced within the transaction, it returns the proof for the corresponding L2 to L1 log |
| `ZKS` | `zks_getL2ToL1MsgProof` | `NOT IMPLEMENTED` | Given a block, a sender, a message, and an optional message log index in the block containing the L1->L2 message, it returns the proof for the message sent via the L1Messenger system contract |
| `ZKS` | `zks_getMainContract` | `NOT IMPLEMENTED` | Returns the address of the zkSync Era contract |
| `ZKS` | `zks_getRawBlockTransactions` | `NOT IMPLEMENTED` | Returns data of transactions in a block |
| [`ZKS`](#zks-namespace) | [`zks_getRawBlockTransactions`](#zks_getrawblocktransactions) | `SUPPORTED` | Returns data of transactions in a block |
| `ZKS` | `zks_getTestnetPaymaster` | `NOT IMPLEMENTED` | Returns the address of the testnet paymaster |
| [`ZKS`](#zks-namespace) | [`zks_getTokenPrice`](#zks_getTokenPrice) | `SUPPORTED` | Gets the USD price of a token <br />_(`ETH` is hard-coded to `1_500`, while some others are `1`)_ |
| [`ZKS`](#zks-namespace) | [`zks_getTransactionDetails`](#zks_gettransactiondetails) | `SUPPORTED` | Returns data from a specific transaction given by the transaction hash |
Expand Down Expand Up @@ -1791,4 +1791,27 @@ curl --request POST \
"id": "2",
"method": "zks_getBridgeContracts"
}'
```
```

### `zks_getRawBlockTransactions`

[source](src/zks.rs)

Returns data of transactions in a 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_getRawBlockTransactions", "params": [ 140599 ]}'
```
16 changes: 16 additions & 0 deletions e2e-tests/test/zks-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,19 @@ describe("zks_getBytecodeByHash", function () {
expect(ethers.utils.hexlify(bytecode)).to.equal(artifact.deployedBytecode);
});
});

describe("zks_getRawBlockTransactions", function () {
it("Should return transactions 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"]);
const receipt = await greeter.setGreeting("Luke Skywalker");

const latestBlock = await provider.getBlock("latest");
const txns = await provider.send("zks_getRawBlockTransactions", [latestBlock.number - 1]);

expect(txns.length).to.equal(1);
expect(txns[0]["execute"]["calldata"]).to.equal(receipt.data);
});
});
201 changes: 195 additions & 6 deletions src/zks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ use bigdecimal::BigDecimal;
use futures::FutureExt;
use zksync_basic_types::{Address, L1BatchNumber, MiniblockNumber, U256};
use zksync_core::api_server::web3::backend_jsonrpc::{
error::into_jsrpc_error, namespaces::zks::ZksNamespaceT,
error::{internal_error, into_jsrpc_error},
namespaces::zks::ZksNamespaceT,
};
use zksync_state::ReadStorage;
use zksync_types::{
api::{
BlockDetails, BlockDetailsBase, BlockStatus, BridgeAddresses, ProtocolVersion,
TransactionDetails, TransactionStatus,
TransactionDetails, TransactionStatus, TransactionVariant,
},
fee::Fee,
ProtocolVersionId,
ExecuteTransactionCommon, ProtocolVersionId, Transaction,
};
use zksync_web3_decl::{
error::Web3Error,
Expand Down Expand Up @@ -69,11 +70,74 @@ impl<S: Send + Sync + 'static + ForkSource + std::fmt::Debug> ZksNamespaceT
}
}

/// Returns data of transactions in a block.
///
/// # Arguments
///
/// * `block` - Block number
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `Vec` of `Transaction`s representing the transactions in the block.
fn get_raw_block_transactions(
&self,
_block_number: MiniblockNumber,
block_number: MiniblockNumber,
) -> jsonrpc_core::BoxFuture<jsonrpc_core::Result<Vec<zksync_types::Transaction>>> {
not_implemented("zks_getRawBlockTransactions")
let inner = self.node.clone();
Box::pin(async move {
let reader = inner
.read()
.map_err(|_err| into_jsrpc_error(Web3Error::InternalError))?;

let maybe_transactions = reader
.block_hashes
.get(&(block_number.0 as u64))
.and_then(|hash| reader.blocks.get(hash))
.map(|block| {
block
.transactions
.iter()
.map(|tx| match tx {
TransactionVariant::Full(tx) => &tx.hash,
TransactionVariant::Hash(hash) => hash,
})
.flat_map(|tx_hash| {
reader.tx_results.get(tx_hash).map(
|TransactionResult { info, .. }| Transaction {
common_data: ExecuteTransactionCommon::L2(
info.tx.common_data.clone(),
),
execute: info.tx.execute.clone(),
received_timestamp_ms: info.tx.received_timestamp_ms,
raw_bytes: info.tx.raw_bytes.clone(),
},
)
})
.collect()
});

let transactions = match maybe_transactions {
Some(txns) => Ok(txns),
None => {
let fork_storage_read = reader
.fork_storage
.inner
.read()
.expect("failed reading fork storage");

match fork_storage_read.fork.as_ref() {
Some(fork) => fork
.fork_source
.get_raw_block_transactions(block_number)
.map_err(|e| internal_error("get_raw_block_transactions", e)),
None => Ok(vec![]),
}
}
}
.map_err(into_jsrpc_error)?;

Ok(transactions)
})
}

fn estimate_gas_l1_to_l2(
Expand Down Expand Up @@ -442,7 +506,7 @@ mod tests {

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

#[tokio::test]
Expand Down Expand Up @@ -865,4 +929,129 @@ mod tests {
// Assert
assert_eq!(input_bytecode, actual);
}

#[tokio::test]
async fn test_get_raw_block_transactions_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 mut block = Block::<TransactionVariant>::default();
let txn = api::Transaction::default();
writer.tx_results.insert(
txn.hash,
TransactionResult {
info: testing::default_tx_execution_info(),
receipt: TransactionReceipt {
logs: vec![],
gas_used: Some(U256::from(10_000)),
effective_gas_price: Some(U256::from(1_000_000_000)),
..Default::default()
},
debug: testing::default_tx_debug_info(),
},
);
block.transactions.push(TransactionVariant::Full(txn));
writer.blocks.insert(H256::repeat_byte(0x1), block);
writer.block_hashes.insert(0, H256::repeat_byte(0x1));
}

// Act
let txns = namespace
.get_raw_block_transactions(MiniblockNumber(0))
.await
.expect("get transaction details");

// Assert
assert_eq!(txns.len(), 1);
}

#[tokio::test]
async fn test_get_raw_block_transactions_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_getRawBlockTransactions",
"params": [miniblock.0]
}),
serde_json::json!({
"jsonrpc": "2.0",
"result": [
{
"common_data": {
"L2": {
"nonce": 86,
"fee": {
"gas_limit": "0xcc626",
"max_fee_per_gas": "0x141dd760",
"max_priority_fee_per_gas": "0x0",
"gas_per_pubdata_limit": "0x4e20"
},
"initiatorAddress": "0x840bd73f903ba7dbb501be8326fe521dadcae1a5",
"signature": [
135,
163,
2,
78,
118,
14,
209
],
"transactionType": "EIP1559Transaction",
"input": {
"hash": "0xc1f625f55d186ad0b439054adfe3317ae703c5f588f4fa1896215e8810a141e0",
"data": [
2,
249,
1,
110,
130
]
},
"paymasterParams": {
"paymaster": "0x0000000000000000000000000000000000000000",
"paymasterInput": []
}
}
},
"execute": {
"contractAddress": "0xbe7d1fd1f6748bbdefc4fbacafbb11c6fc506d1d",
"calldata": "0x38ed173900000000000000000000000000000000000000000000000000000000002c34cc00000000000000000000000000000000000000000000000000000000002c9a2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000840bd73f903ba7dbb501be8326fe521dadcae1a500000000000000000000000000000000000000000000000000000000652c5d1900000000000000000000000000000000000000000000000000000000000000020000000000000000000000008e86e46278518efc1c5ced245cba2c7e3ef115570000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf4",
"value": "0x0",
"factoryDeps": null
},
"received_timestamp_ms": 1697405097873u64,
"raw_bytes": "0x02f9016e820144568084141dd760830cc62694be7d1fd1f6748bbdefc4fbacafbb11c6fc506d1d80b9010438ed173900000000000000000000000000000000000000000000000000000000002c34cc00000000000000000000000000000000000000000000000000000000002c9a2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000840bd73f903ba7dbb501be8326fe521dadcae1a500000000000000000000000000000000000000000000000000000000652c5d1900000000000000000000000000000000000000000000000000000000000000020000000000000000000000008e86e46278518efc1c5ced245cba2c7e3ef115570000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf4c080a087a3024e760ed14134ef541608bf308e083c899a89dba3c02bf3040f07c8b91b9fc3a7eeb6b3b8b36bb03ea4352415e7815dda4954f4898d255bd7660736285e"
}
],
"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 txns = namespace
.get_raw_block_transactions(miniblock)
.await
.expect("get transaction details");
assert_eq!(txns.len(), 1);
}
}
11 changes: 11 additions & 0 deletions test_endpoints.http
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,17 @@ content-type: application/json
POST http://localhost:8011
content-type: application/json

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

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

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