From 46179e2e8949f964b6a4aee25713714ab2ff8625 Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Tue, 18 Jul 2023 19:57:02 +0300 Subject: [PATCH] ROLL-13 Missing Tezos facade endpoints (#31) --- Cargo.lock | 13 + tezos_node/Cargo.toml | 4 +- tezos_node/src/main.rs | 3 + tezos_node/src/rollup.rs | 14 +- tezos_node/src/rollup/facade.rs | 20 +- tezos_node/src/rollup/mock_client.rs | 51 +++- tezos_node/src/rollup/rpc_client.rs | 140 ++++++++++- tezos_node/src/services.rs | 29 ++- tezos_node/src/services/blocks.rs | 236 +++++++++++++++--- .../src/services/blocks/rfc3339_timestamp.rs | 20 ++ tezos_node/src/services/context.rs | 2 +- tezos_node/src/services/contracts.rs | 37 +++ tezos_node/src/services/helpers.rs | 128 +++++++++- tezos_proto/src/config.rs | 225 ++++++++++++++++- tezos_proto/src/context/batch.rs | 10 +- 15 files changed, 855 insertions(+), 77 deletions(-) create mode 100644 tezos_node/src/services/blocks/rfc3339_timestamp.rs diff --git a/Cargo.lock b/Cargo.lock index f28fa52..f58c1fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3288,6 +3288,7 @@ version = "0.1.0" dependencies = [ "actix-web", "async-trait", + "chrono", "clap", "derive_more", "env_logger", @@ -3307,6 +3308,7 @@ dependencies = [ "tezos-rpc", "tezos_proto", "tokio", + "tokio-stream", ] [[package]] @@ -3441,6 +3443,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.8" diff --git a/tezos_node/Cargo.toml b/tezos_node/Cargo.toml index ace69e3..261a9b7 100644 --- a/tezos_node/Cargo.toml +++ b/tezos_node/Cargo.toml @@ -34,9 +34,11 @@ tezos_core = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "d tezos_operation = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "develop", package = "tezos-operation", default-features = false, features = ["ed25519"] } tezos_michelson = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "develop", package = "tezos-michelson", default-features = false } tezos_contract = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "develop", package = "tezos-contract", default-features = false } +chrono = { version = "0.4", default-features = false } ibig = { version = "0.3", features = ["std", "num-traits"], default-features = false } +tokio = { version = "1.25.0", features = ["process", "macros"] } +tokio-stream = "0.1.14" [dev-dependencies] -tokio = { version = "1.25.0", features = ["process", "macros"] } static_init = "1.0.3" tezos_rpc = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "develop", package = "tezos-rpc", features = ["http"] } \ No newline at end of file diff --git a/tezos_node/src/main.rs b/tezos_node/src/main.rs index 2298b3a..ab0631e 100644 --- a/tezos_node/src/main.rs +++ b/tezos_node/src/main.rs @@ -38,5 +38,8 @@ async fn main() -> std::io::Result<()> { let data = Data::new(client); let host = Data::new(rpc_host); + + tezos_node::rollup::rpc_client::run_block_updater(&data); + launch_node::(data, &args.rpc_addr, args.port, host).await } diff --git a/tezos_node/src/rollup.rs b/tezos_node/src/rollup.rs index a6b0d2d..2429551 100644 --- a/tezos_node/src/rollup.rs +++ b/tezos_node/src/rollup.rs @@ -9,6 +9,10 @@ pub mod rpc_backend; pub mod rpc_client; pub mod rpc_helpers; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex}; + +use actix_web::web::Bytes; use async_trait::async_trait; use layered_store::StoreType; use serde::Serialize; @@ -26,18 +30,23 @@ use tezos_rpc::models::{ operation::Operation, version::VersionInfo, }; +use tokio::sync::mpsc::Receiver; use crate::Result; pub use block_id::BlockId; #[async_trait] -pub trait RollupClient { +pub trait RollupClient: Sync + Send { async fn initialize(&mut self) -> Result<()>; async fn store_get(&self, key: String, block_id: &BlockId) -> Result; async fn get_chain_id(&self) -> Result; async fn get_version(&self) -> Result; async fn is_chain_synced(&self) -> Result; async fn inject_batch(&self, messages: Vec>) -> Result<()>; + fn get_ttl_blocks(&self) -> Result>>>; + fn create_channel(&self) -> Result>>; + async fn broadcast_to_channels(&self, data: Bytes) -> Result<()>; + fn channels_count(&self) -> usize; async fn get_batch_head(&self, block_id: &BlockId) -> Result { let head: Head = self.store_get("/head".into(), block_id).await?; @@ -83,7 +92,7 @@ pub trait TezosFacade { async fn get_block_header(&self, block_id: &BlockId) -> Result; async fn get_block_metadata(&self, block_id: &BlockId) -> Result; async fn get_block_protocols(&self, block_id: &BlockId) -> Result; - async fn get_live_blocks(&self, block_id: &BlockId) -> Result>; + async fn get_live_blocks(&self, block_id: &BlockId) -> Result>; async fn get_contract(&self, block_id: &BlockId, address: &Address) -> Result; async fn get_contract_balance(&self, block_id: &BlockId, address: &Address) -> Result; async fn get_contract_counter( @@ -140,6 +149,7 @@ pub trait TezosFacade { async fn get_operation(&self, block_id: &BlockId, pass: i32, index: i32) -> Result; async fn get_operation_list(&self, block_id: &BlockId, pass: i32) -> Result>; async fn get_operation_list_list(&self, block_id: &BlockId) -> Result>>; + async fn get_heads_main_channel(&self) -> Result>>; } #[async_trait] diff --git a/tezos_node/src/rollup/facade.rs b/tezos_node/src/rollup/facade.rs index ee18d92..2e1de8f 100644 --- a/tezos_node/src/rollup/facade.rs +++ b/tezos_node/src/rollup/facade.rs @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: MIT +use actix_web::web::Bytes; use async_trait::async_trait; use michelson_vm::entrypoints::collect_entrypoints; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use tezos_core::types::encoded::{ Address, BlockHash, ContractAddress, Encoded, ImplicitAddress, OperationHash, PublicKey, ScriptExprHash, @@ -17,6 +18,7 @@ use tezos_rpc::models::{ contract::{ContractEntrypoints, ContractInfo, ContractScript}, operation::Operation, }; +use tokio::sync::mpsc::Receiver; use crate::{ rollup::{BlockId, BlockProtocols, RollupClient, TezosFacade}, @@ -24,7 +26,7 @@ use crate::{ }; #[async_trait] -impl TezosFacade for T { +impl TezosFacade for T { async fn get_block_hash(&self, block_id: &BlockId) -> Result { match block_id { BlockId::Hash(hash) => Ok(hash.clone()), @@ -278,9 +280,15 @@ impl TezosFacade for T { }) } - async fn get_live_blocks(&self, block_id: &BlockId) -> Result> { - let receipt = self.get_batch_receipt(block_id).await?; - // TODO: ttl blocks - Ok(vec![receipt.header.predecessor]) + async fn get_live_blocks(&self, _block_id: &BlockId) -> Result> { + let live_blocks_ptr = self.get_ttl_blocks().unwrap(); + let live_blocks = live_blocks_ptr.lock().unwrap(); + // TODO: remove hashes after block_id? + //let block_hash = self.get_block_hash(block_id).await.unwrap(); + Ok(live_blocks.clone()) + } + + async fn get_heads_main_channel(&self) -> Result>> { + self.create_channel() } } diff --git a/tezos_node/src/rollup/mock_client.rs b/tezos_node/src/rollup/mock_client.rs index e33b4a8..eea560e 100644 --- a/tezos_node/src/rollup/mock_client.rs +++ b/tezos_node/src/rollup/mock_client.rs @@ -2,12 +2,14 @@ // // SPDX-License-Identifier: MIT +use actix_web::web::Bytes; use async_trait::async_trait; use layered_store::{ephemeral::EphemeralCopy, StoreType}; use log::debug; -use std::cell::RefCell; +use std::collections::VecDeque; use std::sync::Mutex; -use tezos_core::types::encoded::{ChainId, Encoded, OperationHash}; +use std::{cell::RefCell, sync::Arc}; +use tezos_core::types::encoded::{BlockHash, ChainId, Encoded, OperationHash}; use tezos_operation::operations::SignedOperation; use tezos_proto::{ batcher::apply_batch, @@ -19,6 +21,7 @@ use tezos_rpc::models::operation::Operation; use tezos_rpc::models::version::{ AdditionalInfo, CommitInfo, NetworkVersion, Version, VersionInfo, }; +use tokio::sync::mpsc::{channel, Receiver, Sender}; use crate::{ rollup::{rpc_helpers::parse_operation, BlockId, RollupClient, TezosHelpers}, @@ -30,6 +33,8 @@ const CHAIN_ID: &str = "NetXP2FfcNxFANL"; pub struct RollupMockClient { context: Mutex>, mempool: Mutex>>, + ttl_blocks: Arc>>, + channels: Arc>>>>, } macro_rules! get_mut { @@ -38,11 +43,15 @@ macro_rules! get_mut { }; } +const MAX_TTL_BLOCKS_COUNT: usize = 60; + impl Default for RollupMockClient { fn default() -> Self { Self { context: Mutex::new(RefCell::new(TezosEphemeralContext::default())), mempool: Mutex::new(RefCell::new(Vec::new())), + ttl_blocks: Arc::new(Mutex::new(VecDeque::with_capacity(MAX_TTL_BLOCKS_COUNT))), + channels: Arc::new(Mutex::new(Vec::new())), } } } @@ -114,6 +123,44 @@ impl RollupClient for RollupMockClient { async fn inject_batch(&self, _messages: Vec>) -> Result<()> { unreachable!() } + + fn create_channel(&self) -> Result>> { + const LONG_POLL_CHANNEL_SIZE: usize = 1; + let (tx, rx) = channel::>(LONG_POLL_CHANNEL_SIZE); + let mut channels = self.channels.lock().unwrap(); + channels.push(tx); + Ok(rx) + } + + fn get_ttl_blocks(&self) -> Result>>> { + Ok(Arc::clone(&self.ttl_blocks)) + } + + async fn broadcast_to_channels(&self, data: Bytes) -> Result<()> { + let mut channels = self.channels.lock().unwrap(); + let mut i = 0; + while i < channels.len() { + if channels[i].is_closed() { + channels.remove(i); + continue; + } + + let value = data.clone(); + if let Err(_) = channels[i].try_send(Ok(value)) { + channels.remove(i); + continue; + } + + i += 1; + } + + Ok(()) + } + + fn channels_count(&self) -> usize { + let channels_ptr = self.channels.lock().unwrap(); + channels_ptr.len() + } } #[async_trait] diff --git a/tezos_node/src/rollup/rpc_client.rs b/tezos_node/src/rollup/rpc_client.rs index 99cb66f..98aa3da 100644 --- a/tezos_node/src/rollup/rpc_client.rs +++ b/tezos_node/src/rollup/rpc_client.rs @@ -2,19 +2,32 @@ // // SPDX-License-Identifier: MIT +use std::{ + collections::VecDeque, + sync::{Arc, Mutex}, +}; + +use actix_web::web::{Bytes, Data}; use async_trait::async_trait; +use chrono::Utc; use layered_store::StoreType; use log::debug; use reqwest::Client; use serde::Deserialize; -use tezos_core::types::encoded::{ChainId, Encoded, SmartRollupAddress}; -use tezos_rpc::models::version::{ - AdditionalInfo, CommitInfo, NetworkVersion, Version, VersionInfo, +use tezos_core::types::encoded::{BlockHash, ChainId, Encoded, SmartRollupAddress}; +use tezos_rpc::models::{ + block::FullHeader, + version::{AdditionalInfo, CommitInfo, NetworkVersion, Version, VersionInfo}, +}; +use tokio::{ + sync::mpsc::{channel, Receiver, Sender}, + time::{sleep, Duration}, }; use crate::{ internal_error, rollup::{BlockId, RollupClient}, + services::blocks::HeaderShell, Error, Result, }; @@ -57,8 +70,12 @@ pub struct RollupRpcClient { client: Client, chain_id: Option, origination_level: Option, + ttl_blocks: Arc>>, + channels: Arc>>>>, } +const MAX_TTL_BLOCKS_COUNT: i32 = 60; + impl RollupRpcClient { pub fn new(endpoint: &str) -> Self { Self { @@ -66,6 +83,10 @@ impl RollupRpcClient { client: Client::new(), origination_level: None, chain_id: None, + ttl_blocks: Arc::new(Mutex::new(VecDeque::with_capacity( + MAX_TTL_BLOCKS_COUNT as usize, + ))), + channels: Arc::new(Mutex::new(Vec::new())), } } @@ -186,6 +207,7 @@ impl RollupClient for RollupRpcClient { "Rollup origination level: {}", self.origination_level.as_ref().unwrap() ); + Ok(()) } @@ -270,4 +292,116 @@ impl RollupClient for RollupRpcClient { }) } } + + fn create_channel(&self) -> Result>> { + const LONG_POLL_CHANNEL_SIZE: usize = 1; + let (tx, rx) = channel::>(LONG_POLL_CHANNEL_SIZE); + let mut channels = self.channels.lock().unwrap(); + channels.push(tx); + Ok(rx) + } + + fn get_ttl_blocks(&self) -> Result>>> { + Ok(Arc::clone(&self.ttl_blocks)) + } + + async fn broadcast_to_channels(&self, data: Bytes) -> Result<()> { + let mut channels = self.channels.lock().unwrap(); + let mut i = 0; + while i < channels.len() { + if channels[i].is_closed() { + channels.remove(i); + continue; + } + + let value = data.clone(); + if let Err(_) = channels[i].try_send(Ok(value)) { + channels.remove(i); + continue; + } + + i += 1; + } + + Ok(()) + } + + fn channels_count(&self) -> usize { + let channels_ptr = self.channels.lock().unwrap(); + channels_ptr.len() + } +} + +pub fn run_block_updater(client: &Data) -> () { + const BLOCK_INTERVAL_SEC: i64 = 8; + const BLOCK_DELAY_SEC: i64 = 3; + + let client = client.clone(); + tokio::spawn(async move { + // TODO: wait chain sync? + let mut curr_level = 0; + + loop { + let timestamp = Utc::now().timestamp(); + let head = client.get_batch_receipt(&BlockId::Head).await.unwrap(); + let head_timestamp = head.header.timestamp; + + if curr_level == head.header.level { + sleep(Duration::from_secs(BLOCK_DELAY_SEC as u64)).await; + continue; + } + + curr_level = std::cmp::max(curr_level, head.header.level - MAX_TTL_BLOCKS_COUNT); + + while curr_level < head.header.level { + let batch_receipt = client + .get_batch_receipt(&BlockId::Level(curr_level.try_into().unwrap())) + .await + .unwrap(); + + { + let ttl_blocks_ptr = client.get_ttl_blocks().unwrap(); + let mut ttl_blocks: std::sync::MutexGuard<'_, VecDeque> = + ttl_blocks_ptr.lock().unwrap(); + + if ttl_blocks.len() == MAX_TTL_BLOCKS_COUNT as usize { + ttl_blocks.pop_front(); + } + ttl_blocks.push_back(batch_receipt.hash.clone()); + } + + curr_level += 1; + + if client.channels_count() == 0 { + continue; + } + + let full_header: FullHeader = batch_receipt.into(); + + let header = HeaderShell { + hash: Some(full_header.hash), + level: full_header.level, + proto: full_header.proto, + predecessor: full_header.predecessor, + timestamp: full_header.timestamp, + validation_pass: full_header.validation_pass, + operations_hash: full_header.operations_hash, + fitness: full_header.fitness, + context: full_header.context, + protocol_data: Some("".to_string()), + }; + + let header_json = serde_json::to_string(&header).unwrap(); + let header_bytes = Bytes::from(header_json); + + if let Err(_) = client.broadcast_to_channels(header_bytes).await { + debug!("Error while broadcast header to long polls clients"); + } + } + + let next_block_timestamp = head_timestamp + BLOCK_INTERVAL_SEC + BLOCK_DELAY_SEC; + let waiting_interval = std::cmp::max(next_block_timestamp - timestamp, BLOCK_DELAY_SEC); + sleep(Duration::from_secs(waiting_interval as u64)).await; + } + }); } diff --git a/tezos_node/src/services.rs b/tezos_node/src/services.rs index 6ee0376..5bdb157 100644 --- a/tezos_node/src/services.rs +++ b/tezos_node/src/services.rs @@ -15,9 +15,10 @@ use crate::services::{ context::{big_map_value, big_map_value_normalized, constants, delegate, delegates}, contracts::{ contract, contract_balance, contract_counter, contract_delegate, contract_entrypoints, - contract_public_key, contract_script, contract_script_normalized, contract_storage, + contract_public_key, contract_raw_json_bytes_stub, contract_script, + contract_script_normalized, contract_storage, }, - helpers::{forge_operation, preapply_operations, run_operation}, + helpers::{forge_operation, preapply_operations, run_operation, simulate_operation}, operations::{ operation, operation_hash, operation_hash_list, operation_hash_list_list, operation_list, operation_list_list, @@ -26,6 +27,8 @@ use crate::services::{ }; use actix_web::web::{get, post, ServiceConfig}; +use self::blocks::{block_header_shell, bootstrap_info, heads_main}; + #[macro_export] macro_rules! json_response { ($value: expr) => { @@ -49,6 +52,10 @@ pub fn config(cfg: &mut "/chains/main/blocks/{block_id}/helpers/scripts/run_operation", post().to(run_operation::), ) + .route( + "/chains/main/blocks/{block_id}/helpers/scripts/simulate_operation", + post().to(simulate_operation::), + ) .route( "/chains/main/blocks/{block_id}/helpers/forge/operations", post().to(forge_operation::), @@ -65,6 +72,10 @@ pub fn config(cfg: &mut "/chains/main/blocks/{block_id}/header", get().to(block_header::), ) + .route( + "/chains/main/blocks/{block_id}/header/shell", + get().to(block_header_shell::), + ) .route( "/chains/main/blocks/{block_id}/metadata", get().to(block_metadata::), @@ -78,6 +89,8 @@ pub fn config(cfg: &mut get().to(live_blocks::), ) .route("/chains/main/blocks/{block_id}", get().to(block::)) + .route("/monitor/bootstrapped", get().to(bootstrap_info::)) + .route("/monitor/heads/main", get().to(heads_main::)) .route( "/chains/main/blocks/{block_id}/context/delegates", get().to(delegates), @@ -134,6 +147,18 @@ pub fn config(cfg: &mut "/chains/main/blocks/{block_id}/context/contracts/{contract_id}", get().to(contract::), ) + .route( + "/chains/main/blocks/{block_id}/context/raw/json/contracts/index/{contract_id}/used_bytes", + get().to(contract_raw_json_bytes_stub::), + ) + .route( + "/chains/main/blocks/{block_id}/context/raw/json/contracts/index/{contract_id}/paid_bytes", + get().to(contract_raw_json_bytes_stub::), + ) + .route( + "/chains/main/blocks/{block_id}/context/raw/json/big_maps/index/0/total_bytes", + get().to(contract_raw_json_bytes_stub::), + ) .route( "/chains/main/blocks/{block_id}/operations/{pass}/{index}", get().to(operation::), diff --git a/tezos_node/src/services/blocks.rs b/tezos_node/src/services/blocks.rs index 4455771..34415f8 100644 --- a/tezos_node/src/services/blocks.rs +++ b/tezos_node/src/services/blocks.rs @@ -3,17 +3,67 @@ // SPDX-License-Identifier: MIT use actix_web::{ + http::header, web::{Data, Path}, - Responder, Result, + HttpResponse, Responder, Result, }; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; +use tezos_core::types::encoded::{BlockHash, ContextHash, OperationListListHash}; +use tokio_stream::wrappers::ReceiverStream; -use crate::{json_response, rollup::TezosFacade}; +use crate::{ + json_response, + rollup::{BlockId, TezosFacade}, +}; + +pub mod rfc3339_timestamp; + +#[derive(Serialize, Deserialize)] +pub struct BootstrapInfo { + pub hash: BlockHash, + #[serde(with = "rfc3339_timestamp")] + pub timestamp: NaiveDateTime, +} + +#[derive(Serialize, Deserialize)] +pub struct HeaderShell { + #[serde(skip_serializing_if = "Option::is_none")] + pub hash: Option, + pub level: i32, + pub proto: u8, + pub predecessor: BlockHash, + #[serde(with = "rfc3339_timestamp")] + pub timestamp: NaiveDateTime, + pub validation_pass: u8, + pub operations_hash: OperationListListHash, + pub fitness: Vec, + pub context: ContextHash, + #[serde(skip_serializing_if = "Option::is_none")] + pub protocol_data: Option, +} + +async fn get_block_id(client: &Data, block_id: &str) -> Result { + let value = match block_id.try_into() { + Ok(block_id) => block_id, + Err(_) => { + let hash = &block_id[0..51]; + let offset_str = block_id.trim_start_matches(format!("{}{}", hash, "~").as_str()); + let offset = i32::from_str_radix(offset_str, 10).unwrap(); + let header = client.get_block_header(&hash.try_into()?).await?; + let target_level = header.level - offset; + BlockId::Level(target_level.try_into().unwrap()) + } + }; + Ok(value) +} pub async fn block_hash( client: Data, path: Path<(String,)>, ) -> Result { - let value = client.get_block_hash(&path.0.as_str().try_into()?).await?; + let block_id = get_block_id(&client, path.0.as_str()).await?; + let value = client.get_block_hash(&block_id).await?; Ok(json_response!(value)) } @@ -21,9 +71,29 @@ pub async fn block_header( client: Data, path: Path<(String,)>, ) -> Result { - let value = client - .get_block_header(&path.0.as_str().try_into()?) - .await?; + let block_id = get_block_id(&client, path.0.as_str()).await?; + let value = client.get_block_header(&block_id).await?; + Ok(json_response!(value)) +} + +pub async fn block_header_shell( + client: Data, + path: Path<(String,)>, +) -> Result { + let block_id = get_block_id(&client, path.0.as_str()).await?; + let full_header = client.get_block_header(&block_id).await?; + let value = HeaderShell { + hash: Option::None, + level: full_header.level, + proto: full_header.proto, + predecessor: full_header.predecessor, + timestamp: full_header.timestamp, + validation_pass: full_header.validation_pass, + operations_hash: full_header.operations_hash, + fitness: full_header.fitness, + context: full_header.context, + protocol_data: Option::None, + }; Ok(json_response!(value)) } @@ -63,6 +133,26 @@ pub async fn block( Ok(json_response!(value)) } +pub async fn bootstrap_info(client: Data) -> Result { + let header = client.get_block_header(&BlockId::Head).await?; + let value = BootstrapInfo { + hash: header.hash, + timestamp: header.timestamp, + }; + Ok(json_response!(value)) +} + +pub async fn heads_main(client: Data) -> Result { + let rx = client.get_heads_main_channel().await.unwrap(); + let body_stream = ReceiverStream::new(rx); + + let response = HttpResponse::Ok() + .insert_header((header::CONTENT_TYPE, "application/json")) + .streaming(body_stream); + + Ok(response) +} + #[cfg(test)] mod test { use actix_web::{test, web::Data, App}; @@ -74,7 +164,43 @@ mod test { }; use tezos_rpc::models::block::FullHeader; - use crate::{rollup::mock_client::RollupMockClient, services::config, Result}; + use crate::{ + rollup::mock_client::RollupMockClient, + services::{ + blocks::{BootstrapInfo, HeaderShell}, + config, + }, + Result, + }; + + fn get_test_batch_receipt() -> BatchReceipt { + BatchReceipt { + balance_updates: None, + chain_id: "NetXdQprcVkpaWU".try_into().unwrap(), + hash: "BKiHLREqU3JkXfzEDYAkmmfX48gBDtYhMrpA98s7Aq4SzbUAB6M" + .try_into() + .unwrap(), // head::default + protocol: "PtLimaPtLMwfNinJi9rCfDPWea8dFgTZ1MeJ9f1m2SRic6ayiwW" + .try_into() + .unwrap(), + header: BatchHeader { + predecessor: "BM4iF1PGVN74h1kvqUtY26boVKpZuJFvpQRN34JLYSkQ9G3jBnn" + .try_into() + .unwrap(), + level: 3113764, + timestamp: 1675429049, + operations_hash: "LLoZzirJsJexRFU6yznwPxSfETprh8Yd5scW1DXfFAwoGmcvQJteE" + .try_into() + .unwrap(), + payload_hash: "vh2tsz2UVT8kVJzgye4MpngkRvJJLgnNhZjUgn9oURpB1PYpyrFK" + .try_into() + .unwrap(), + context: "CoVnr5Sy57UkHt1Aqmw62KyLUYxVmKCyp45HY7MXW8sFemT3Uf6i" + .try_into() + .unwrap(), + }, + } + } #[actix_web::test] async fn test_block_hash() -> Result<()> { @@ -107,34 +233,7 @@ mod test { let client = RollupMockClient::default(); client.patch(|context| { context.set_head(Head::default()).unwrap(); - context - .set_batch_receipt(BatchReceipt { - balance_updates: None, - chain_id: "NetXdQprcVkpaWU".try_into().unwrap(), - hash: "BKiHLREqU3JkXfzEDYAkmmfX48gBDtYhMrpA98s7Aq4SzbUAB6M" - .try_into() - .unwrap(), // head::default - protocol: "PtLimaPtLMwfNinJi9rCfDPWea8dFgTZ1MeJ9f1m2SRic6ayiwW" - .try_into() - .unwrap(), - header: BatchHeader { - predecessor: "BM4iF1PGVN74h1kvqUtY26boVKpZuJFvpQRN34JLYSkQ9G3jBnn" - .try_into() - .unwrap(), - level: 3113764, - timestamp: 1675429049, - operations_hash: "LLoZzirJsJexRFU6yznwPxSfETprh8Yd5scW1DXfFAwoGmcvQJteE" - .try_into() - .unwrap(), - payload_hash: "vh2tsz2UVT8kVJzgye4MpngkRvJJLgnNhZjUgn9oURpB1PYpyrFK" - .try_into() - .unwrap(), - context: "CoVnr5Sy57UkHt1Aqmw62KyLUYxVmKCyp45HY7MXW8sFemT3Uf6i" - .try_into() - .unwrap(), - }, - }) - .unwrap(); + context.set_batch_receipt(get_test_batch_receipt()).unwrap(); Ok(()) })?; @@ -156,4 +255,71 @@ mod test { assert_eq!("NetXdQprcVkpaWU", res.chain_id.value()); Ok(()) } + + #[actix_web::test] + async fn test_block_header_shell() -> Result<()> { + let client = RollupMockClient::default(); + client.patch(|context| { + context.set_head(Head::default()).unwrap(); + context.set_batch_receipt(get_test_batch_receipt()).unwrap(); + Ok(()) + })?; + + let app = test::init_service( + App::new() + .configure(config::) + .app_data(Data::new(client)), + ) + .await; + + let req = test::TestRequest::get() + .uri("/chains/main/blocks/head/header/shell") + .to_request(); + let res: HeaderShell = test::call_and_read_body_json(&app, req).await; + + assert_eq!(3113764, res.level); + assert_eq!(0, res.proto); + assert_eq!( + "BM4iF1PGVN74h1kvqUtY26boVKpZuJFvpQRN34JLYSkQ9G3jBnn", + res.predecessor.value() + ); + assert_eq!(4, res.validation_pass); + assert_eq!( + "LLoZzirJsJexRFU6yznwPxSfETprh8Yd5scW1DXfFAwoGmcvQJteE", + res.operations_hash.value() + ); + assert_eq!( + "CoVnr5Sy57UkHt1Aqmw62KyLUYxVmKCyp45HY7MXW8sFemT3Uf6i", + res.context.value() + ); + Ok(()) + } + + #[actix_web::test] + async fn test_bootstrap_info() -> Result<()> { + let client = RollupMockClient::default(); + client.patch(|context| { + context.set_head(Head::default()).unwrap(); + context.set_batch_receipt(get_test_batch_receipt()).unwrap(); + Ok(()) + })?; + + let app = test::init_service( + App::new() + .configure(config::) + .app_data(Data::new(client)), + ) + .await; + + let req = test::TestRequest::get() + .uri("/monitor/bootstrapped") + .to_request(); + + let res: BootstrapInfo = test::call_and_read_body_json(&app, req).await; + assert_eq!( + "BKiHLREqU3JkXfzEDYAkmmfX48gBDtYhMrpA98s7Aq4SzbUAB6M", + res.hash.value() + ); + Ok(()) + } } diff --git a/tezos_node/src/services/blocks/rfc3339_timestamp.rs b/tezos_node/src/services/blocks/rfc3339_timestamp.rs new file mode 100644 index 0000000..753667c --- /dev/null +++ b/tezos_node/src/services/blocks/rfc3339_timestamp.rs @@ -0,0 +1,20 @@ +use chrono::{DateTime, NaiveDateTime, Utc}; +use serde::{Deserialize, Deserializer, Serializer}; + +pub fn serialize(date: &NaiveDateTime, serializer: S) -> Result +where + S: Serializer, +{ + let dt = DateTime::::from_utc(*date, Utc); + serializer.serialize_str(&dt.to_rfc3339()) +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + DateTime::parse_from_rfc3339(&s) + .map(|op| op.naive_utc()) + .map_err(serde::de::Error::custom) +} diff --git a/tezos_node/src/services/context.rs b/tezos_node/src/services/context.rs index 224bcc0..b67a2e3 100644 --- a/tezos_node/src/services/context.rs +++ b/tezos_node/src/services/context.rs @@ -13,7 +13,7 @@ use tezos_proto::config::Config; use crate::{json_response, rollup::TezosFacade, Error}; pub async fn constants() -> Result { - Ok(json_response!(Config::default())) + Ok(json_response!(Config::default().tezos)) } pub async fn delegates() -> Result { diff --git a/tezos_node/src/services/contracts.rs b/tezos_node/src/services/contracts.rs index 36b5d06..26c5f38 100644 --- a/tezos_node/src/services/contracts.rs +++ b/tezos_node/src/services/contracts.rs @@ -103,6 +103,14 @@ pub async fn contract( Ok(json_response!(value)) } +pub async fn contract_raw_json_bytes_stub( + _client: Data, + _path: Path<(String,)>, +) -> Result { + // temporary stub + Ok(json_response!("0")) +} + #[cfg(test)] mod test { use actix_web::{test, web::Data, App}; @@ -158,4 +166,33 @@ mod test { assert!(res.is_none()); Ok(()) } + + #[actix_web::test] + async fn test_context_raw_json_stubs() -> Result<()> { + let client = RollupMockClient::default(); + client.patch(|context| { + context.set_head(Head::default()).unwrap(); + Ok(()) + })?; + + let app = test::init_service( + App::new() + .configure(config::) + .app_data(Data::new(client)), + ) + .await; + + let endpoints = [ + "/chains/main/blocks/head/context/raw/json/contracts/index/KT1GszRPFC31pjKXuRfTU53BfFhx3vwqK3bZ/used_bytes", + "/chains/main/blocks/head/context/raw/json/contracts/index/KT1GszRPFC31pjKXuRfTU53BfFhx3vwqK3bZ/paid_bytes", + "/chains/main/blocks/head/context/raw/json/big_maps/index/0/total_bytes", + ]; + + for endpoint in endpoints { + let req = test::TestRequest::get().uri(endpoint).to_request(); + let res: String = test::call_and_read_body_json(&app, req).await; + assert_eq!("0", res); + } + Ok(()) + } } diff --git a/tezos_node/src/services/helpers.rs b/tezos_node/src/services/helpers.rs index 0cdbe06..a3c54fd 100644 --- a/tezos_node/src/services/helpers.rs +++ b/tezos_node/src/services/helpers.rs @@ -4,12 +4,12 @@ use actix_web::{ error::ErrorInternalServerError, - web::{Data, Json, Path}, + web::{Data, Json, Path, Query}, Responder, Result, }; -use serde::{Deserialize, Deserializer}; +use serde::{Deserialize, Deserializer, Serialize}; use tezos_core::types::{ - encoded::{Address, BlockHash, ImplicitAddress, PublicKey}, + encoded::{Address, BlockHash, ImplicitAddress, PublicKey, Signature}, mutez::Mutez, number::Nat, }; @@ -106,11 +106,34 @@ pub enum RequestContent { pub struct OperationRequest { pub branch: BlockHash, pub contents: Vec, + pub signature: Option, } #[derive(Deserialize, Debug, Clone)] pub struct RunOperationRequest { operation: OperationRequest, + //chain_id +} + +#[derive(Deserialize)] +pub struct SimulateOperationRequest { + operation: OperationRequest, + //blocks_before_activation: i32, + //latency: i16, + //chain_id +} + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct SimulateOperationQueryParams { + successor_level: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct OperationResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, + pub contents: Vec, } impl TryInto for RequestContent { @@ -137,7 +160,7 @@ impl TryInto for OperationRequest { Ok(SignedOperation { branch: self.branch, contents: contents?, - signature: ZERO_SIGNATURE.try_into().unwrap(), + signature: self.signature.unwrap_or(ZERO_SIGNATURE.try_into().unwrap()), }) } } @@ -156,6 +179,29 @@ pub async fn run_operation( Ok(json_response!(value)) } +pub async fn simulate_operation( + client: Data, + path: Path<(String,)>, + _query: Query, + request: Json, +) -> Result { + let value = client + .simulate_operation( + &path.0.as_str().try_into()?, + request.0.operation.try_into()?, + ) + .await?; + let response = OperationResponse { + signature: value.signature, + contents: value + .contents + .into_iter() + .map(|c| c.try_into().unwrap()) + .collect(), + }; + Ok(json_response!(response)) +} + pub async fn preapply_operations( client: Data, path: Path<(String,)>, @@ -166,7 +212,15 @@ pub async fn preapply_operations( let value = client .simulate_operation(&path.0.as_str().try_into()?, operation) .await?; - Ok(json_response!(vec![value])) + let response = OperationResponse { + signature: value.signature, + contents: value + .contents + .into_iter() + .map(|c| c.try_into().unwrap()) + .collect(), + }; + Ok(json_response!(vec![response])) } pub async fn forge_operation( @@ -195,7 +249,11 @@ mod test { use tezos_proto::context::{head::Head, TezosContext}; use tezos_rpc::models::{error::RpcError, operation::Operation}; - use crate::{rollup::mock_client::RollupMockClient, services::config, Result}; + use crate::{ + rollup::mock_client::RollupMockClient, + services::{config, helpers::OperationResponse}, + Result, + }; #[actix_web::test] async fn test_forge_operation() -> Result<()> { @@ -327,8 +385,64 @@ mod test { let res: Operation = test::call_and_read_body_json(&app, req).await; assert_eq!( res.hash.unwrap().into_string(), - "oooHiZmTVQFVe48pqX2BqnywnH6PWDKUquYoPjtVkihLRpGQHZd" + "ooDLM1HCkn7kD9yNYDaickE6yB8re3hege7WVKYhUBMg33BFsUq" ); Ok(()) } + + #[actix_web::test] + async fn test_simulate_operation() -> Result<()> { + let client = RollupMockClient::default(); + client.patch(|context| { + context.set_head(Head::default()).unwrap(); + context + .set_public_key( + "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx", + PublicKey::new(String::from( + "edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav", + )) + .unwrap(), + ) + .unwrap(); + context + .set_balance( + "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx", + Mutez::from(100000u32), + ) + .unwrap(); + context.commit().unwrap(); + Ok(()) + })?; + + let app = test::init_service( + App::new() + .configure(config::) + .app_data(Data::new(client)), + ) + .await; + + let req = test::TestRequest::post() + .uri("/chains/main/blocks/head/helpers/scripts/simulate_operation?successor_level=true") + .set_json(json!({ + "operation": { + "branch": "BMGK5hqUdRZ6PMNtHmiB4KLt8Sy9q1GNZZbGXoGaeTTwAUqhUwU", + "contents": [{ + "kind": "transaction", + "source": "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx", + "fee": "471", + "counter": "80342938", + "gas_limit": "1546", + "storage_limit": "0", + "amount": "0", + "destination": "tz1dShXbbgJ4i1L6KMWAuuNdBPk5xCM1NRrU", + }] + }, + "chain_id": "NetXRc5LThNqBfZ" + })) + .to_request(); + + let res: OperationResponse = test::call_and_read_body_json(&app, req).await; + assert_eq!(res.contents.len(), 1); + Ok(()) + } } diff --git a/tezos_proto/src/config.rs b/tezos_proto/src/config.rs index d889466..be26a69 100644 --- a/tezos_proto/src/config.rs +++ b/tezos_proto/src/config.rs @@ -9,32 +9,231 @@ pub const PROTOCOL: &str = "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK" pub const BLOCK_TIME: i64 = 8; #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Config { - pub blocks_per_cycle: i32, - pub max_operations_time_to_live: i32, - pub max_block_header_length: i32, +pub struct Ratio { + pub numerator: i32, + pub denominator: i32, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DalParametric { + pub feature_enable: bool, + pub number_of_slots: i32, + pub attestation_lag: i32, + pub attestation_threshold: i32, + //pub availability_threshold: i32, + pub blocks_per_epoch: i32, + pub redundancy_factor: i32, + pub page_size: i32, + pub slot_size: i32, + pub number_of_shards: i32, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TezosConfig { + pub proof_of_work_nonce_size: i32, + pub nonce_length: i32, + pub max_anon_ops_per_block: i32, pub max_operation_data_length: i32, - pub max_operations_list_length: i32, - pub pow_nonce: String, + pub max_proposals_per_delegate: i32, + pub max_micheline_node_count: i32, + pub max_micheline_bytes_limit: i32, + pub max_allowed_global_constants_depth: i32, + pub cache_layout_size: i32, + pub michelson_maximum_type_size: i32, + pub smart_rollup_max_wrapped_proof_binary_size: i32, + pub smart_rollup_message_size_limit: i32, + pub smart_rollup_max_number_of_messages_per_level: Nat, + pub preserved_cycles: i32, + pub blocks_per_cycle: i32, + pub blocks_per_commitment: i32, + pub nonce_revelation_threshold: i32, + pub blocks_per_stake_snapshot: i32, + pub cycles_per_voting_period: i32, pub hard_gas_limit_per_operation: Nat, - pub hard_storage_limit_per_operation: Nat, pub hard_gas_limit_per_block: Nat, + pub proof_of_work_threshold: Nat, + pub minimal_stake: Nat, + pub vdf_difficulty: Nat, + pub seed_nonce_revelation_tip: Nat, + pub origination_size: i32, + pub baking_reward_fixed_portion: Nat, + pub baking_reward_bonus_per_slot: Nat, + pub endorsing_reward_per_slot: Nat, pub cost_per_byte: Nat, + pub hard_storage_limit_per_operation: Nat, + pub quorum_min: i32, + pub quorum_max: i32, + pub min_proposal_quorum: i32, + pub liquidity_baking_subsidy: Nat, + pub liquidity_baking_toggle_ema_threshold: i32, + pub max_operations_time_to_live: i32, + pub minimal_block_delay: Nat, + pub delay_increment_per_round: Nat, + pub consensus_committee_size: i32, + pub consensus_threshold: i32, + pub minimal_participation_ratio: Ratio, + pub max_slashing_period: i32, + pub frozen_deposits_percentage: i32, + pub double_baking_punishment: Nat, + pub ratio_of_frozen_deposits_slashed_per_double_endorsement: Ratio, + //pub testnet_dictator: Nat, + //pub initial_seed: Nat, + pub cache_script_size: i32, + pub cache_stake_distribution_cycles: i32, + pub cache_sampler_state_cycles: i32, + pub tx_rollup_enable: bool, + pub tx_rollup_origination_size: i32, + pub tx_rollup_hard_size_limit_per_inbox: i32, + pub tx_rollup_hard_size_limit_per_message: i32, + pub tx_rollup_max_withdrawals_per_batch: i32, + pub tx_rollup_commitment_bond: Nat, + pub tx_rollup_finality_period: i32, + pub tx_rollup_withdraw_period: i32, + pub tx_rollup_max_inboxes_count: i32, + pub tx_rollup_max_messages_per_inbox: i32, + pub tx_rollup_max_commitments_count: i32, + pub tx_rollup_cost_per_byte_ema_factor: i32, + pub tx_rollup_max_ticket_payload_size: i32, + pub tx_rollup_rejection_max_proof_size: i32, + pub tx_rollup_sunset_level: i32, + pub dal_parametric: DalParametric, + pub smart_rollup_enable: bool, + pub smart_rollup_arith_pvm_enable: bool, + pub smart_rollup_origination_size: i32, + pub smart_rollup_challenge_window_in_blocks: i32, + pub smart_rollup_stake_amount: Nat, + pub smart_rollup_commitment_period_in_blocks: i32, + pub smart_rollup_max_lookahead_in_blocks: i32, + pub smart_rollup_max_active_outbox_levels: i32, + pub smart_rollup_max_outbox_messages_per_level: i32, + pub smart_rollup_number_of_sections_in_dissection: i32, + pub smart_rollup_timeout_period_in_blocks: i32, + pub smart_rollup_max_number_of_cemented_commitments: i32, + pub smart_rollup_max_number_of_parallel_games: i32, + pub zk_rollup_enable: bool, + pub zk_rollup_origination_size: i32, + pub zk_rollup_min_pending_to_process: i32, } -impl Config { +impl TezosConfig { pub fn default() -> Self { Self { + proof_of_work_nonce_size: 8, + nonce_length: 32, + max_anon_ops_per_block: 132, + max_operation_data_length: 86400, + max_proposals_per_delegate: 20, + max_micheline_node_count: 50000, + max_micheline_bytes_limit: 50000, + max_allowed_global_constants_depth: 10000, + cache_layout_size: 3, + michelson_maximum_type_size: 2001, + smart_rollup_max_wrapped_proof_binary_size: 30000, + smart_rollup_message_size_limit: 4096, + smart_rollup_max_number_of_messages_per_level: 1000000u64.into(), + preserved_cycles: 5, blocks_per_cycle: 8096, + blocks_per_commitment: 128, + nonce_revelation_threshold: 512, + blocks_per_stake_snapshot: 1024, + cycles_per_voting_period: 5, + hard_gas_limit_per_operation: 1040000u64.into(), + hard_gas_limit_per_block: 5200000u64.into(), + proof_of_work_threshold: 70368744177663u64.into(), + minimal_stake: 6000000000u64.into(), + vdf_difficulty: 8000000000u64.into(), + seed_nonce_revelation_tip: 125000u64.into(), + origination_size: 257, + baking_reward_fixed_portion: 5000000u64.into(), + baking_reward_bonus_per_slot: 2143u64.into(), + endorsing_reward_per_slot: 1428u64.into(), + cost_per_byte: 250u32.into(), + hard_storage_limit_per_operation: 60000u64.into(), + quorum_min: 2000, + quorum_max: 7000, + min_proposal_quorum: 500, + liquidity_baking_subsidy: 1250000u64.into(), + liquidity_baking_toggle_ema_threshold: 1000000000, max_operations_time_to_live: 240, + minimal_block_delay: 15u64.into(), + delay_increment_per_round: 8u64.into(), + consensus_committee_size: 7000, + consensus_threshold: 4667, + minimal_participation_ratio: Ratio { + numerator: 2, + denominator: 3, + }, + max_slashing_period: 2, + frozen_deposits_percentage: 10, + double_baking_punishment: 640000000u64.into(), + ratio_of_frozen_deposits_slashed_per_double_endorsement: Ratio { + numerator: 1, + denominator: 2, + }, + cache_script_size: 100000000, + cache_stake_distribution_cycles: 8, + cache_sampler_state_cycles: 8, + tx_rollup_enable: false, + tx_rollup_origination_size: 4000, + tx_rollup_hard_size_limit_per_inbox: 500000, + tx_rollup_hard_size_limit_per_message: 5000, + tx_rollup_max_withdrawals_per_batch: 15, + tx_rollup_commitment_bond: 10000000000u64.into(), + tx_rollup_finality_period: 40000, + tx_rollup_withdraw_period: 40000, + tx_rollup_max_inboxes_count: 40100, + tx_rollup_max_messages_per_inbox: 1010, + tx_rollup_max_commitments_count: 80100, + tx_rollup_cost_per_byte_ema_factor: 120, + tx_rollup_max_ticket_payload_size: 2048, + tx_rollup_rejection_max_proof_size: 30000, + tx_rollup_sunset_level: 3473409, + dal_parametric: DalParametric { + feature_enable: false, + number_of_slots: 256, + attestation_lag: 1, + attestation_threshold: 50, + //availability_threshold: 50, + blocks_per_epoch: 1, + redundancy_factor: 16, + page_size: 4096, + slot_size: 1048576, + number_of_shards: 2048, + }, + smart_rollup_enable: false, + smart_rollup_arith_pvm_enable: false, + smart_rollup_origination_size: 6314, + smart_rollup_challenge_window_in_blocks: 80640, + smart_rollup_stake_amount: 10000000000u64.into(), + smart_rollup_commitment_period_in_blocks: 60, + smart_rollup_max_lookahead_in_blocks: 172800, + smart_rollup_max_active_outbox_levels: 80640, + smart_rollup_max_outbox_messages_per_level: 100, + smart_rollup_number_of_sections_in_dissection: 32, + smart_rollup_timeout_period_in_blocks: 40320, + smart_rollup_max_number_of_cemented_commitments: 5, + smart_rollup_max_number_of_parallel_games: 32, + zk_rollup_enable: false, + zk_rollup_origination_size: 4000, + zk_rollup_min_pending_to_process: 10, + } + } +} + +pub struct Config { + pub tezos: TezosConfig, + pub max_block_header_length: i32, + pub max_operations_list_length: i32, + pub pow_nonce: String, +} + +impl Config { + pub fn default() -> Self { + Self { + tezos: TezosConfig::default(), max_block_header_length: 2048, - max_operation_data_length: 86400, max_operations_list_length: 1024, pow_nonce: "deadbeef".into(), - hard_gas_limit_per_operation: 1040000u64.into(), - hard_storage_limit_per_operation: 60000u64.into(), - hard_gas_limit_per_block: 5200000u64.into(), - cost_per_byte: 250u32.into(), } } } diff --git a/tezos_proto/src/context/batch.rs b/tezos_proto/src/context/batch.rs index 72e34ca..6951dc4 100644 --- a/tezos_proto/src/context/batch.rs +++ b/tezos_proto/src/context/batch.rs @@ -112,13 +112,13 @@ impl From for Metadata { level_info: Some(LevelInfo { level: receipt.header.level, level_position: receipt.header.level - 1, - cycle: receipt.header.level / config.blocks_per_cycle, - cycle_position: receipt.header.level % config.blocks_per_cycle, + cycle: receipt.header.level / config.tezos.blocks_per_cycle, + cycle_position: receipt.header.level % config.tezos.blocks_per_cycle, expected_commitment: false, }), // default - max_operations_ttl: config.max_operations_time_to_live, - max_operation_data_length: config.max_operation_data_length, + max_operations_ttl: config.tezos.max_operations_time_to_live, + max_operation_data_length: config.tezos.max_operation_data_length, max_operation_list_length: vec![ OperationListLength { max_size: 0, @@ -135,7 +135,7 @@ impl From for Metadata { OperationListLength { max_size: config.max_operations_list_length, max_op: Some( - config.max_operations_list_length * config.max_operation_data_length, + config.max_operations_list_length * config.tezos.max_operation_data_length, ), }, ],