Skip to content

Commit

Permalink
Merge pull request #1203 from matter-labs/alekseysidorov-919-stable-a…
Browse files Browse the repository at this point in the history
…pi-operations

Operations endpoint for the REST API
  • Loading branch information
popzxc authored Dec 11, 2020
2 parents f7a4c42 + e7e6dba commit 528e113
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 15 deletions.
10 changes: 5 additions & 5 deletions core/bin/zksync_api/src/api_server/rest/v1/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,14 @@ mod tests {
blocks.into_iter().map(From::from).collect()
};

assert_eq!(client.block_by_id(1).await?.unwrap(), blocks[4]);
assert_eq!(client.blocks_range(Pagination::Last, 5).await?, blocks);
assert_eq!(client.block_by_id(1).await?.unwrap(), blocks[7]);
assert_eq!(client.blocks_range(Pagination::Last, 10).await?, blocks);
assert_eq!(
client.blocks_range(Pagination::Before(2), 5).await?,
&blocks[4..5]
&blocks[7..8]
);
assert_eq!(
client.blocks_range(Pagination::After(4), 5).await?,
client.blocks_range(Pagination::After(7), 5).await?,
&blocks[0..1]
);

Expand All @@ -328,7 +328,7 @@ mod tests {
transactions.into_iter().map(From::from).collect()
};
assert_eq!(client.block_transactions(1).await?, expected_txs);
assert_eq!(client.block_transactions(2).await?, vec![]);
assert_eq!(client.block_transactions(6).await?, vec![]);

server.stop().await;
Ok(())
Expand Down
1 change: 1 addition & 0 deletions core/bin/zksync_api/src/api_server/rest/v1/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pub use super::{
blocks::{BlockInfo, TransactionInfo},
config::Contracts,
operations::PriorityOpReceipt,
tokens::TokenPriceKind,
transactions::{SumbitErrorCode, TxReceipt},
};
Expand Down
2 changes: 2 additions & 0 deletions core/bin/zksync_api/src/api_server/rest/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod blocks;
pub mod client;
mod config;
mod error;
mod operations;
mod search;
#[cfg(test)]
mod test_utils;
Expand All @@ -46,6 +47,7 @@ pub(crate) fn api_scope(
tx_sender.pool.clone(),
))
.service(transactions::api_scope(tx_sender.clone()))
.service(operations::api_scope(tx_sender.pool.clone()))
.service(search::api_scope(tx_sender.pool.clone()))
.service(tokens::api_scope(
tx_sender.tokens,
Expand Down
161 changes: 161 additions & 0 deletions core/bin/zksync_api/src/api_server/rest/v1/operations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! Operations part of API implementation.
// Built-in uses

// External uses
use actix_web::{
web::{self, Json},
Scope,
};
use serde::{Deserialize, Serialize};

// Workspace uses
use zksync_storage::{ConnectionPool, QueryResult};
use zksync_types::BlockNumber;

// Local uses
use super::{
blocks::BlockInfo,
client::{Client, ClientError},
transactions::TxReceipt,
Error as ApiError, JsonResult,
};

/// Shared data between `api/v1/operations` endpoints.
#[derive(Debug, Clone)]
struct ApiOperationsData {
pool: ConnectionPool,
}

impl ApiOperationsData {
pub fn new(pool: ConnectionPool) -> Self {
Self { pool }
}

pub async fn priority_op(&self, serial_id: u64) -> QueryResult<Option<PriorityOpReceipt>> {
let mut storage = self.pool.access_storage().await?;

let executed_op = storage
.chain()
.operations_schema()
.get_executed_priority_operation(serial_id as u32)
.await?;

let executed_op = if let Some(executed_op) = executed_op {
executed_op
} else {
return Ok(None);
};

let blocks = storage
.chain()
.block_schema()
.load_block_range(executed_op.block_number as BlockNumber, 1)
.await?;

let block_info = blocks.into_iter().next().map(BlockInfo::from);

let status = match block_info {
None => TxReceipt::Pending,
Some(info) if info.verify_tx_hash.is_some() => TxReceipt::Verified {
block: info.block_number,
},
Some(info) if info.commit_tx_hash.is_some() => TxReceipt::Committed {
block: info.block_number,
},
Some(_) => TxReceipt::Executed,
};

Ok(Some(PriorityOpReceipt {
status,
index: executed_op.block_index as u64,
}))
}
}

// Data transfer objects.

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PriorityOpReceipt {
#[serde(flatten)]
pub status: TxReceipt,
pub index: u64,
}

// Client implementation

/// Operations API part.
impl Client {
/// Gets priority operation receipt.
pub async fn priority_op(
&self,
serial_id: u64,
) -> Result<Option<PriorityOpReceipt>, ClientError> {
self.get(&format!("operations/priority_op/{}", serial_id))
.send()
.await
}
}

// Server implementation

async fn priority_op(
data: web::Data<ApiOperationsData>,
web::Path(serial_id): web::Path<u64>,
) -> JsonResult<Option<PriorityOpReceipt>> {
let receipt = data
.priority_op(serial_id)
.await
.map_err(ApiError::internal)?;

Ok(Json(receipt))
}

pub fn api_scope(pool: ConnectionPool) -> Scope {
let data = ApiOperationsData::new(pool);

web::scope("operations")
.data(data)
.route("priority_op/{id}", web::get().to(priority_op))
}

#[cfg(test)]
mod tests {
use super::{
super::test_utils::{TestServerConfig, COMMITTED_OP_SERIAL_ID, VERIFIED_OP_SERIAL_ID},
*,
};

#[actix_rt::test]
async fn test_operations_scope() -> anyhow::Result<()> {
let cfg = TestServerConfig::default();
cfg.fill_database().await?;

let (client, server) = cfg.start_server(|cfg| api_scope(cfg.pool.clone()));

let requests = vec![
(
VERIFIED_OP_SERIAL_ID,
Some(PriorityOpReceipt {
index: 2,
status: TxReceipt::Verified { block: 2 },
}),
),
(
COMMITTED_OP_SERIAL_ID,
Some(PriorityOpReceipt {
index: 1,
status: TxReceipt::Committed { block: 4 },
}),
),
];

for (serial_id, expected_op) in requests {
assert_eq!(client.priority_op(serial_id).await?, expected_op);
}

server.stop().await;
Ok(())
}
}
68 changes: 59 additions & 9 deletions core/bin/zksync_api/src/api_server/rest/v1/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ use tokio::sync::Mutex;
// Workspace uses
use zksync_config::{ApiServerOptions, ConfigurationOptions};
use zksync_crypto::rand::{SeedableRng, XorShiftRng};
use zksync_storage::test_data::{
dummy_ethereum_tx_hash, gen_acc_random_updates, gen_unique_operation,
gen_unique_operation_with_txs, BLOCK_SIZE_CHUNKS,
};
use zksync_storage::ConnectionPool;
use zksync_storage::{
chain::operations::records::NewExecutedPriorityOperation,
test_data::{
dummy_ethereum_tx_hash, gen_acc_random_updates, gen_unique_operation,
gen_unique_operation_with_txs, BLOCK_SIZE_CHUNKS,
},
};
use zksync_test_account::ZkSyncAccount;
use zksync_types::{ethereum::OperationType, helpers::apply_updates, AccountMap, Action};
use zksync_types::{
ethereum::OperationType, helpers::apply_updates, AccountMap, Action, BlockNumber, H256,
};
use zksync_types::{
operations::{ChangePubKeyOp, TransferToNewOp},
Address, ExecutedOperations, ExecutedTx, Token, ZkSyncOp, ZkSyncTx,
Expand All @@ -26,6 +31,15 @@ use zksync_types::{
// Local uses
use super::client::Client;

/// Serial ID of the verified priority operation.
pub const VERIFIED_OP_SERIAL_ID: u64 = 10;
/// Serial ID of the committed priority operation.
pub const COMMITTED_OP_SERIAL_ID: u64 = 243;
/// Number of committed blocks.
pub const COMMITTED_BLOCKS_COUNT: BlockNumber = 8;
/// Number of verified blocks.
pub const VERIFIED_BLOCKS_COUNT: BlockNumber = 3;

#[derive(Debug, Clone)]
pub struct TestServerConfig {
pub env_options: ConfigurationOptions,
Expand Down Expand Up @@ -179,11 +193,9 @@ impl TestServerConfig {
.await?;

let mut accounts = AccountMap::default();
let n_committed = 5;
let n_verified = n_committed - 2;

// Create and apply several blocks to work with.
for block_number in 1..=n_committed {
for block_number in 1..=COMMITTED_BLOCKS_COUNT {
let updates = (0..3)
.map(|_| gen_acc_random_updates(&mut rng))
.flatten()
Expand Down Expand Up @@ -242,7 +254,7 @@ impl TestServerConfig {
.await?;

// Add verification for the block if required.
if block_number <= n_verified {
if block_number <= VERIFIED_BLOCKS_COUNT {
storage
.prover_schema()
.store_proof(block_number, &Default::default())
Expand Down Expand Up @@ -282,6 +294,44 @@ impl TestServerConfig {
}
}

// Store priority operations for some tests.
let ops = vec![
// Verified priority operation.
NewExecutedPriorityOperation {
block_number: 2,
block_index: 2,
operation: Default::default(),
from_account: Default::default(),
to_account: Default::default(),
priority_op_serialid: VERIFIED_OP_SERIAL_ID as i64,
deadline_block: 100,
eth_hash: H256::default().as_bytes().to_vec(),
eth_block: 10,
created_at: chrono::Utc::now(),
},
// Committed priority operation.
NewExecutedPriorityOperation {
block_number: VERIFIED_BLOCKS_COUNT as i64 + 1,
block_index: 1,
operation: Default::default(),
from_account: Default::default(),
to_account: Default::default(),
priority_op_serialid: COMMITTED_OP_SERIAL_ID as i64,
deadline_block: 200,
eth_hash: H256::default().as_bytes().to_vec(),
eth_block: 14,
created_at: chrono::Utc::now(),
},
];

for op in ops {
storage
.chain()
.operations_schema()
.store_executed_priority_op(op)
.await?;
}

storage.commit().await?;
// Storage has been inited, so we can safely drop this guard.
drop(inited_guard);
Expand Down
7 changes: 6 additions & 1 deletion core/lib/storage/src/chain/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,12 @@ impl<'a, 'c> OperationsSchema<'a, 'c> {
Ok(())
}

pub(crate) async fn store_executed_priority_op(
/// Stores executed priority operation in database.
///
/// This method is made public to fill the database for tests, do not use it for
/// any other purposes.
#[doc = "hidden"]
pub async fn store_executed_priority_op(
&mut self,
operation: NewExecutedPriorityOperation,
) -> QueryResult<()> {
Expand Down

0 comments on commit 528e113

Please sign in to comment.