diff --git a/Cargo.lock b/Cargo.lock index dbb6be2642..9c06203059 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8218,6 +8218,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "katana-grpc" +version = "1.0.0" +dependencies = [ + "tonic 0.11.0", + "tonic-build 0.11.0", + "tonic-build 0.12.3", +] + [[package]] name = "katana-node" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index c6c6bc8659..b22e0bf8c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "crates/katana/controller", "crates/katana/core", "crates/katana/executor", + "crates/katana/grpc", "crates/katana/node", "crates/katana/node-bindings", "crates/katana/pipeline", @@ -179,8 +180,8 @@ jsonrpsee = { version = "0.16.2", default-features = false } lazy_static = "1.4.0" log = "0.4.21" metrics = "0.23.0" -num-traits = { version = "0.2", default-features = false } num-bigint = "0.4.3" +num-traits = { version = "0.2", default-features = false } once_cell = "1.0" parking_lot = "0.12.1" postcard = { version = "1.0.10", features = [ "use-std" ], default-features = false } diff --git a/crates/katana/grpc/Cargo.toml b/crates/katana/grpc/Cargo.toml new file mode 100644 index 0000000000..97de5ad6f5 --- /dev/null +++ b/crates/katana/grpc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +edition.workspace = true +license-file.workspace = true +name = "katana-grpc" +repository.workspace = true +version.workspace = true + +[dependencies] +tonic.workspace = true + +[dev-dependencies] + +[build-dependencies] +tonic-build.workspace = true +wasm-tonic-build.workspace = true diff --git a/crates/katana/grpc/build.rs b/crates/katana/grpc/build.rs new file mode 100644 index 0000000000..361736f8b9 --- /dev/null +++ b/crates/katana/grpc/build.rs @@ -0,0 +1,18 @@ +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + let out_dir = + PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR environment variable not set")); + let feature_client = std::env::var("CARGO_FEATURE_CLIENT"); + let feature_server = std::env::var("CARGO_FEATURE_SERVER"); + + tonic_build::configure() + .build_server(feature_server.is_ok()) + .build_client(feature_client.is_ok()) + .file_descriptor_set_path(out_dir.join("starknet_descriptor.bin")) + .compile(&["proto/starknet.proto"], &["proto"])?; + + println!("cargo:rerun-if-changed=proto"); + + Ok(()) +} diff --git a/crates/katana/grpc/proto/starknet.proto b/crates/katana/grpc/proto/starknet.proto new file mode 100644 index 0000000000..12e3265110 --- /dev/null +++ b/crates/katana/grpc/proto/starknet.proto @@ -0,0 +1,268 @@ +syntax = "proto3"; + +package starknet; + +import "types.proto"; + +// The Starknet service provides methods for interacting with Starknet. +service Starknet { + // Returns the version of the Starknet JSON-RPC specification being used + rpc SpecVersion(SpecVersionRequest) returns (SpecVersionResponse); + + // Get block information with transaction hashes given the block id + rpc GetBlockWithTxHashes(GetBlockRequest) returns (GetBlockWithTxHashesResponse); + + // Get block information with full transactions given the block id + rpc GetBlockWithTxs(GetBlockRequest) returns (GetBlockWithTxsResponse); + + // Get block information with full transactions and receipts given the block id + rpc GetBlockWithReceipts(GetBlockRequest) returns (GetBlockWithReceiptsResponse); + + // Get the information about the result of executing the requested block + rpc GetStateUpdate(GetBlockRequest) returns (GetStateUpdateResponse); + + // Get the value of the storage at the given address and key + rpc GetStorageAt(GetStorageAtRequest) returns (GetStorageAtResponse); + + // Gets the transaction status + rpc GetTransactionStatus(GetTransactionStatusRequest) returns (GetTransactionStatusResponse); + + // Get the details and status of a submitted transaction + rpc GetTransactionByHash(GetTransactionByHashRequest) returns (GetTransactionByHashResponse); + + // Get the details of a transaction by a given block id and index + rpc GetTransactionByBlockIdAndIndex(GetTransactionByBlockIdAndIndexRequest) returns (GetTransactionByBlockIdAndIndexResponse); + + // Get the transaction receipt by the transaction hash + rpc GetTransactionReceipt(GetTransactionReceiptRequest) returns (GetTransactionReceiptResponse); + + // Get the contract class definition in the given block associated with the given hash + rpc GetClass(GetClassRequest) returns (GetClassResponse); + + // Get the contract class hash in the given block for the contract deployed at the given address + rpc GetClassHashAt(GetClassHashAtRequest) returns (GetClassHashAtResponse); + + // Get the contract class definition in the given block at the given address + rpc GetClassAt(GetClassAtRequest) returns (GetClassAtResponse); + + // Get the number of transactions in a block given a block id + rpc GetBlockTransactionCount(GetBlockRequest) returns (GetBlockTransactionCountResponse); + + // Call a starknet function without creating a Starknet transaction + rpc Call(CallRequest) returns (CallResponse); + + // Estimate the fee for Starknet transactions + rpc EstimateFee(EstimateFeeRequest) returns (EstimateFeeResponse); + + // Estimate the L2 fee of a message sent on L1 + rpc EstimateMessageFee(EstimateMessageFeeRequest) returns (EstimateFeeResponse); + + // Get the most recent accepted block number + rpc BlockNumber(BlockNumberRequest) returns (BlockNumberResponse); + + // Get the most recent accepted block hash and number + rpc BlockHashAndNumber(BlockHashAndNumberRequest) returns (BlockHashAndNumberResponse); + + // Return the currently configured Starknet chain id + rpc ChainId(ChainIdRequest) returns (ChainIdResponse); + + // Returns an object about the sync status, or false if the node is not synching + rpc Syncing(SyncingRequest) returns (SyncingResponse); + + // Returns all events matching the given filter + rpc GetEvents(GetEventsRequest) returns (GetEventsResponse); + + // Get the nonce associated with the given address in the given block + rpc GetNonce(GetNonceRequest) returns (GetNonceResponse); +} + +message SpecVersionRequest {} + +message SpecVersionResponse { + string version = 1; +} + +message GetBlockRequest { + types.BlockID block_id = 1; +} + +message GetBlockWithTxHashesResponse { + oneof result { + types.BlockWithTxHashes block = 1; + types.PendingBlockWithTxHashes pending_block = 2; + } +} + +message GetBlockWithTxsResponse { + oneof result { + types.BlockWithTxs block = 1; + types.PendingBlockWithTxs pending_block = 2; + } +} + +message GetBlockWithReceiptsResponse { + oneof result { + types.BlockWithReceipts block = 1; + types.PendingBlockWithReceipts pending_block = 2; + } +} + +message GetStateUpdateResponse { + oneof result { + types.StateUpdate state_update = 1; + types.PendingStateUpdate pending_state_update = 2; + } +} + +message GetStorageAtRequest { + types.BlockID block_id = 1; + types.Felt contract_address = 2; + types.Felt key = 3; +} + +message GetStorageAtResponse { + types.Felt value = 1; +} + +message GetTransactionStatusRequest { + types.Felt transaction_hash = 1; +} + +message GetTransactionStatusResponse { + string finality_status = 1; + string execution_status = 2; +} + +message GetTransactionByHashRequest { + types.Felt transaction_hash = 1; +} + +message GetTransactionByHashResponse { + types.Transaction transaction = 1; +} + +message GetTransactionByBlockIdAndIndexRequest { + types.BlockID block_id = 1; + uint64 index = 2; +} + +message GetTransactionByBlockIdAndIndexResponse { + types.Transaction transaction = 1; +} + +message GetTransactionReceiptRequest { + types.Felt transaction_hash = 1; +} + +message GetTransactionReceiptResponse { + types.TransactionReceipt receipt = 1; +} + +message GetClassRequest { + types.BlockID block_id = 1; + types.Felt class_hash = 2; +} + +message GetClassResponse { + oneof result { + types.DeprecatedContractClass deprecated_contract_class = 1; + types.ContractClass contract_class = 2; + } +} + +message GetClassHashAtRequest { + types.BlockID block_id = 1; + types.Felt contract_address = 2; +} + +message GetClassHashAtResponse { + types.Felt class_hash = 1; +} + +message GetClassAtRequest { + types.BlockID block_id = 1; + types.Felt contract_address = 2; +} + +message GetClassAtResponse { + oneof result { + types.DeprecatedContractClass deprecated_contract_class = 1; + types.ContractClass contract_class = 2; + } +} + +message GetBlockTransactionCountResponse { + uint64 count = 1; +} + +message CallRequest { + types.FunctionCall request = 1; + types.BlockID block_id = 2; +} + +message CallResponse { + repeated types.Felt result = 1; +} + +message EstimateFeeRequest { + repeated types.Transaction transactions = 1; + repeated string simulation_flags = 2; + types.BlockID block_id = 3; +} + +message EstimateFeeResponse { + repeated types.FeeEstimate estimates = 1; +} + +message EstimateMessageFeeRequest { + types.MessageFromL1 message = 1; + types.BlockID block_id = 2; +} + +message BlockNumberRequest {} + +message BlockNumberResponse { + uint64 block_number = 1; +} + +message BlockHashAndNumberRequest {} + +message BlockHashAndNumberResponse { + types.Felt block_hash = 1; + uint64 block_number = 2; +} + +message ChainIdRequest {} + +message ChainIdResponse { + string chain_id = 1; +} + +message SyncingRequest {} + +message SyncingResponse { + oneof result { + bool not_syncing = 1; + types.SyncStatus status = 2; + } +} + +message GetEventsRequest { + types.EventFilter filter = 1; + uint32 chunk_size = 2; + string continuation_token = 3; +} + +message GetEventsResponse { + repeated types.EmittedEvent events = 1; + string continuation_token = 2; +} + +message GetNonceRequest { + types.BlockID block_id = 1; + types.Felt contract_address = 2; +} + +message GetNonceResponse { + types.Felt nonce = 1; +} diff --git a/crates/katana/grpc/proto/types.proto b/crates/katana/grpc/proto/types.proto new file mode 100644 index 0000000000..ef07757c92 --- /dev/null +++ b/crates/katana/grpc/proto/types.proto @@ -0,0 +1,366 @@ +syntax = "proto3"; + +package types; + +message Felt { + bytes value = 1; +} + +message BlockID { + oneof identifier { + uint64 number = 1 [json_name = "block_number"]; + Felt hash = 2 [json_name = "block_hash"]; + string tag = 3 [json_name = "block_tag"]; + } +} + +enum SimulationFlag { + SKIP_FEE_CHARGE = 0; + SKIP_EXECUTE = 1; + SKIP_VALIDATE = 2; +} + +message Transaction { + oneof transaction { + InvokeTxnV1 invoke_v1 = 1; + InvokeTxnV3 invoke_v3 = 2; + DeclareTxnV1 declare_v1 = 3; + DeclareTxnV2 declare_v2 = 4; + DeclareTxnV3 declare_v3 = 5; + DeployAccountTxn deploy_account = 6; + DeployAccountTxnV3 deploy_account_v3 = 7; + } +} + +message InvokeTxnV1 { + Felt max_fee = 1 [json_name = "max_fee"]; + string version = 2 [json_name = "version"]; + repeated Felt signature = 3 [json_name = "signature"]; + Felt nonce = 4 [json_name = "nonce"]; + string type = 5 [json_name = "type"]; + Felt sender_address = 6 [json_name = "sender_address"]; + repeated Felt calldata = 7 [json_name = "calldata"]; +} + +message InvokeTxnV3 { + string type = 1 [json_name = "type"]; + Felt sender_address = 2 [json_name = "sender_address"]; + repeated Felt calldata = 3 [json_name = "calldata"]; + string version = 4 [json_name = "version"]; + repeated Felt signature = 5 [json_name = "signature"]; + Felt nonce = 6 [json_name = "nonce"]; + ResourceBoundsMapping resource_bounds = 7 [json_name = "resource_bounds"]; + Felt tip = 8 [json_name = "tip"]; + repeated Felt paymaster_data = 9 [json_name = "paymaster_data"]; + repeated Felt account_deployment_data = 10 [json_name = "account_deployment_data"]; + string nonce_data_availability_mode = 11 [json_name = "nonce_data_availability_mode"]; + string fee_data_availability_mode = 12 [json_name = "fee_data_availability_mode"]; +} + +message DeclareTxnV1 { + Felt max_fee = 1 [json_name = "max_fee"]; + string version = 2 [json_name = "version"]; + repeated Felt signature = 3 [json_name = "signature"]; + Felt nonce = 4 [json_name = "nonce"]; + string type = 5 [json_name = "type"]; + Felt class_hash = 6 [json_name = "class_hash"]; + Felt sender_address = 7 [json_name = "sender_address"]; +} + +message DeclareTxnV2 { + string type = 1 [json_name = "type"]; + Felt sender_address = 2 [json_name = "sender_address"]; + Felt compiled_class_hash = 3 [json_name = "compiled_class_hash"]; + Felt max_fee = 4 [json_name = "max_fee"]; + string version = 5 [json_name = "version"]; + repeated Felt signature = 6 [json_name = "signature"]; + Felt nonce = 7 [json_name = "nonce"]; + bytes class = 8 [json_name = "contract_class"]; +} + +message DeclareTxnV3 { + string type = 1 [json_name = "type"]; + Felt sender_address = 2 [json_name = "sender_address"]; + Felt compiled_class_hash = 3 [json_name = "compiled_class_hash"]; + string version = 4 [json_name = "version"]; + repeated Felt signature = 5 [json_name = "signature"]; + Felt nonce = 6 [json_name = "nonce"]; + Felt class_hash = 7 [json_name = "class_hash"]; + ResourceBoundsMapping resource_bounds = 8 [json_name = "resource_bounds"]; + Felt tip = 9 [json_name = "tip"]; + repeated Felt paymaster_data = 10 [json_name = "paymaster_data"]; + repeated Felt account_deployment_data = 11 [json_name = "account_deployment_data"]; + string nonce_data_availability_mode = 12 [json_name = "nonce_data_availability_mode"]; + string fee_data_availability_mode = 13 [json_name = "fee_data_availability_mode"]; +} + +message DeployAccountTxn { + Felt max_fee = 1 [json_name = "max_fee"]; + string version = 2 [json_name = "version"]; + repeated Felt signature = 3 [json_name = "signature"]; + Felt nonce = 4 [json_name = "nonce"]; + string type = 5 [json_name = "type"]; + Felt class_hash = 6 [json_name = "class_hash"]; + Felt contract_address_salt = 7 [json_name = "contract_address_salt"]; + repeated Felt constructor_calldata = 8 [json_name = "constructor_calldata"]; +} + +message DeployAccountTxnV3 { + string type = 1 [json_name = "type"]; + string version = 2 [json_name = "version"]; + repeated Felt signature = 3 [json_name = "signature"]; + Felt nonce = 4 [json_name = "nonce"]; + Felt contract_address_salt = 5 [json_name = "contract_address_salt"]; + repeated Felt constructor_calldata = 6 [json_name = "constructor_calldata"]; + Felt class_hash = 7 [json_name = "class_hash"]; + ResourceBoundsMapping resource_bounds = 8 [json_name = "resource_bounds"]; + Felt tip = 9 [json_name = "tip"]; + repeated Felt paymaster_data = 10 [json_name = "paymaster_data"]; + string nonce_data_availability_mode = 11 [json_name = "nonce_data_availability_mode"]; + string fee_data_availability_mode = 12 [json_name = "fee_data_availability_mode"]; +} + +message ResourceBoundsMapping { + ResourceBounds l1_gas = 1 [json_name = "L1_GAS"]; + ResourceBounds l2_gas = 2 [json_name = "L2_GAS"]; +} + +message ResourceBounds { + Felt max_amount = 1 [json_name = "max_amount"]; + Felt max_price_per_unit = 2 [json_name = "max_price_per_unit"]; +} + +message FeeEstimate { + Felt gas_consumed = 1 [json_name = "gas_consumed"]; + Felt gas_price = 2 [json_name = "gas_price"]; + Felt data_gas_consumed = 3 [json_name = "data_gas_consumed"]; + Felt data_gas_price = 4 [json_name = "data_gas_price"]; + Felt overall_fee = 5 [json_name = "overall_fee"]; + string unit = 6 [json_name = "unit"]; +} + +message BlockWithTxHashes { + string status = 1; + BlockHeader header = 2; + repeated Felt transactions = 3; +} + +message BlockWithTxs { + string status = 1; + BlockHeader header = 2; + repeated Transaction transactions = 3; +} + +message BlockWithReceipts { + string status = 1; + BlockHeader header = 2; + repeated TransactionWithReceipt transactions = 3; +} + +message PendingBlockWithTxHashes { + BlockHeader header = 1; + repeated Felt transactions = 2; +} + +message PendingBlockWithTxs { + BlockHeader header = 1; + repeated Transaction transactions = 2; +} + +message PendingBlockWithReceipts { + BlockHeader header = 1; + repeated TransactionWithReceipt transactions = 2; +} + +message StateUpdate { + Felt block_hash = 1; + Felt old_root = 2; + Felt new_root = 3; + StateDiff state_diff = 4; +} + +message PendingStateUpdate { + Felt old_root = 1; + StateDiff state_diff = 2; +} + +message StateDiff { + repeated StorageDiff storage_diffs = 1; + repeated Felt deprecated_declared_classes = 2; + repeated DeclaredClass declared_classes = 3; + repeated DeployedContract deployed_contracts = 4; + repeated ReplacedClass replaced_classes = 5; + repeated Nonce nonces = 6; +} + +message StorageDiff { + Felt address = 1; + repeated StorageEntry storage_entries = 2; +} + +message StorageEntry { + Felt key = 1; + Felt value = 2; +} + +message DeclaredClass { + Felt class_hash = 1; + Felt compiled_class_hash = 2; +} + +message DeployedContract { + Felt address = 1; + Felt class_hash = 2; +} + +message ReplacedClass { + Felt contract_address = 1; + Felt class_hash = 2; +} + +message Nonce { + Felt contract_address = 1; + Felt nonce = 2; +} + +message BlockHeader { + Felt block_hash = 1; + Felt parent_hash = 2; + uint64 block_number = 3; + Felt new_root = 4; + uint64 timestamp = 5; + Felt sequencer_address = 6; + ResourcePrice l1_gas_price = 7; + ResourcePrice l1_data_gas_price = 8; + string l1_da_mode = 9; + string starknet_version = 10; +} + +message ResourcePrice { + Felt price_in_wei = 1; + Felt price_in_fri = 2; +} + +message TransactionWithReceipt { + Transaction transaction = 1; + TransactionReceipt receipt = 2; +} + +message TransactionReceipt { + string type = 1; + Felt transaction_hash = 2; + FeePayment actual_fee = 3; + string finality_status = 4; + repeated MessageToL1 messages_sent = 5; + repeated Event events = 6; + ExecutionResources execution_resources = 7; + string execution_status = 8; + string revert_reason = 9; +} + +message FeePayment { + Felt amount = 1; + string unit = 2; +} + +message MessageToL1 { + Felt from_address = 1; + Felt to_address = 2; + repeated Felt payload = 3; +} + +message Event { + Felt from_address = 1; + repeated Felt keys = 2; + repeated Felt data = 3; +} + +message ExecutionResources { + uint64 steps = 1; + uint64 memory_holes = 2; + uint64 range_check_builtin_applications = 3; + uint64 pedersen_builtin_applications = 4; + uint64 poseidon_builtin_applications = 5; + uint64 ec_op_builtin_applications = 6; + uint64 ecdsa_builtin_applications = 7; + uint64 bitwise_builtin_applications = 8; + uint64 keccak_builtin_applications = 9; + uint64 segment_arena_builtin = 10; + DataAvailability data_availability = 11; +} + +message DataAvailability { + uint64 l1_gas = 1; + uint64 l1_data_gas = 2; +} + +message FunctionCall { + Felt contract_address = 1; + Felt entry_point_selector = 2; + repeated Felt calldata = 3; +} + +message MessageFromL1 { + string from_address = 1; + Felt to_address = 2; + Felt entry_point_selector = 3; + repeated Felt payload = 4; +} + +message EmittedEvent { + Event event = 1; + Felt block_hash = 2; + uint64 block_number = 3; + Felt transaction_hash = 4; +} + +message EventFilter { + BlockID from_block = 1; + BlockID to_block = 2; + Felt address = 3; + repeated Felt keys = 4; +} + +message SyncStatus { + Felt starting_block_hash = 1; + uint64 starting_block_num = 2; + Felt current_block_hash = 3; + uint64 current_block_num = 4; + Felt highest_block_hash = 5; + uint64 highest_block_num = 6; +} + +message ContractClass { + repeated Felt sierra_program = 1; + string contract_class_version = 2; + EntryPointsByType entry_points_by_type = 3; + string abi = 4; +} + +message DeprecatedContractClass { + string program = 1; + DeprecatedEntryPointsByType entry_points_by_type = 2; + string abi = 3; +} + +message EntryPointsByType { + repeated SierraEntryPoint constructor = 1; + repeated SierraEntryPoint external = 2; + repeated SierraEntryPoint l1_handler = 3; +} + +message DeprecatedEntryPointsByType { + repeated DeprecatedCairoEntryPoint constructor = 1; + repeated DeprecatedCairoEntryPoint external = 2; + repeated DeprecatedCairoEntryPoint l1_handler = 3; +} + +message SierraEntryPoint { + Felt selector = 1; + uint64 function_idx = 2; +} + +message DeprecatedCairoEntryPoint { + string offset = 1; + Felt selector = 2; +} diff --git a/crates/katana/grpc/src/lib.rs b/crates/katana/grpc/src/lib.rs new file mode 100644 index 0000000000..d0dd5a0e60 --- /dev/null +++ b/crates/katana/grpc/src/lib.rs @@ -0,0 +1 @@ +//! gRPC implementations.