diff --git a/README.md b/README.md index d263a31..45e6447 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ This repo contains and tracks the specification of P2P protocol for StarkNet nod The main entry point is the [here](./p2p/starknet-p2p.md). -For a guide on how to implement the protocols, see [here](./p2p/proto/protocols.md) +For a guide on the various protocols of Starknet and how to implement them, see [here](./p2p/proto/protocols.md) diff --git a/p2p/proto/class.proto b/p2p/proto/class.proto index bc1a936..334eec0 100644 --- a/p2p/proto/class.proto +++ b/p2p/proto/class.proto @@ -4,15 +4,16 @@ import "p2p/proto/common.proto"; message EntryPoint { Felt252 selector = 1; - Felt252 offset = 2; + uint64 offset = 2; } message Cairo0Class { - bytes abi = 1; + string abi = 1; repeated EntryPoint externals = 2; repeated EntryPoint l1_handlers = 3; repeated EntryPoint constructors = 4; - bytes program = 5; + // Compressed in base64 representation. + string program = 5; } message SierraEntryPoint { @@ -27,24 +28,18 @@ message Cairo1EntryPoints { } message Cairo1Class { - bytes abi = 1; + string abi = 1; Cairo1EntryPoints entry_points = 2; repeated Felt252 program = 3; string contract_class_version = 4; - bytes compiled = 5; } -// is it better to separate the definition from the hashes? (will need to repeate the hashes -// for the definitions stream) -// or, make the definitions optional? maybe it is enough to know only that a class exists, not its definition -// which may be fetched lazily later. message Class { oneof class { Cairo0Class cairo0 = 1; Cairo1Class cairo1 = 2; } uint32 domain = 3; - Hash class_hash = 4; } message ClassesRequest { diff --git a/p2p/proto/common.proto b/p2p/proto/common.proto index 476840c..e32d029 100644 --- a/p2p/proto/common.proto +++ b/p2p/proto/common.proto @@ -20,25 +20,40 @@ message PeerID { bytes id = 1; } +message Uint128 { + uint64 low = 1; + uint64 high = 2; +} + message ConsensusSignature { Felt252 r = 1; Felt252 s = 2; } -message Merkle { - uint32 n_leaves = 1; // needed to know the height, so as to how many nodes to expect in a proof. +message Patricia { + uint64 n_leaves = 1; // needed to know the height, so as to how many nodes to expect in a proof. // and also when receiving all leaves, how many to expect - Hash root = 2; + Hash root = 2; } -message Patricia { - uint32 height = 1; - Hash root = 2; +message StateDiffCommitment { + uint64 state_diff_length = 1; + Hash root = 2; } message BlockID { uint64 number = 1; - Hash header = 2; + Hash header = 2; +} + +enum L1DataAvailabilityMode { + Calldata = 0; + Blob = 1; +} + +enum VolitionDomain { + L1 = 0; + L2 = 1; } message Iteration { @@ -48,11 +63,11 @@ message Iteration { } oneof start { uint64 block_number = 1; - Hash header = 2; + Hash header = 2; } Direction direction = 3; - uint64 limit = 4; - uint64 step = 5; // to allow interleaving from several nodes + uint64 limit = 4; + uint64 step = 5; // to allow interleaving from several nodes // bool interleave = 6; // return results in any order of blocks, per block the messages should still be in the order specified } diff --git a/p2p/proto/consensus.proto b/p2p/proto/consensus.proto index 7ccaf19..24fc556 100644 --- a/p2p/proto/consensus.proto +++ b/p2p/proto/consensus.proto @@ -9,34 +9,34 @@ import "google/protobuf/timestamp.proto"; // WIP - will change message Proposal { - uint64 block_number = 2; - uint32 round = 3; - uint32 pol = 4; // proof of lock - Hash block_header_hash = 5; - google.protobuf.Timestamp timestamp = 6; - ConsensusSignature signature = 7; + uint64 block_number = 1; + uint32 round = 2; + uint32 pol = 3; // proof of lock + Hash block_header_hash = 4; + google protobuf Timestamp timestamp = 5; + ConsensusSignature signature = 6; } // A block proposal is a series of (Transactions+, StateDiff)* BlockHeader message Vote { enum Type { - UNKNOWN = 0; - Proposal = 1; - Prevote = 2; + UNKNOWN = 0; + Proposal = 1; + Prevote = 2; Precommit = 3; }; - Proposal proposal = 1; - Address validator_address = 2; - int32 validator_index = 3; // ??? - ConsensusSignature signature = 4; + Proposal proposal = 1; + Address validator_address = 2; + int32 validator_index = 3; // ??? + ConsensusSignature signature = 4; } message CreateBlock { oneof messages { Transactions transactions = 1; - StateDiff state_diff = 2; - Proposal proposal = 3; + StateDiff state_diff = 2; + Proposal proposal = 3; } } diff --git a/p2p/proto/event.proto b/p2p/proto/event.proto index 3b861ef..89e0239 100644 --- a/p2p/proto/event.proto +++ b/p2p/proto/event.proto @@ -3,9 +3,9 @@ import "p2p/proto/common.proto"; message Event { Hash transaction_hash = 1; - Felt252 from_address = 2; - repeated Felt252 keys = 3; - repeated Felt252 data = 4; + Felt252 from_address = 3; + repeated Felt252 keys = 4; + repeated Felt252 data = 5; } message EventsRequest { @@ -16,6 +16,6 @@ message EventsRequest { message EventsResponse { oneof event_message { Event event = 1; - Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its events. + Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its events. } } diff --git a/p2p/proto/header.proto b/p2p/proto/header.proto index 3122858..364f2db 100644 --- a/p2p/proto/header.proto +++ b/p2p/proto/header.proto @@ -1,38 +1,37 @@ syntax = "proto3"; import "p2p/proto/common.proto"; -import "p2p/proto/state.proto"; // Note: commitments may change to be for the previous blocks like comet/tendermint // hash of block header sent to L1 message SignedBlockHeader { - Hash block_hash = 1; // For the structure of the block hash, see https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/header/#block_hash - Hash parent_hash = 2; - uint64 number = 3; - uint64 time = 4; // Encoded in Unix time. - Address sequencer_address = 5; - Hash state_diff_commitment = 6; // The state diff commitment returned by the Starknet Feeder Gateway. - // For more info, see https://community.starknet.io/t/introducing-p2p-authentication-and-mismatch-resolution-in-v0-12-2/97993 - Patricia state = 7; // hash of contract and class patricia tries. Same as in L1. Later more trees will be included - // The following merkles can be built on the fly while sequencing/validating txs. - Merkle transactions = 8; // By order of execution. TBD: required? the client can execute (powerful machine) and match state diff - Merkle events = 9; // By order of issuance. TBD: in receipts? - Merkle receipts = 10; // By order of issuance. - string protocol_version = 11; // Starknet version - Felt252 gas_price = 12; - uint64 num_storage_diffs = 13; - uint64 num_nonce_updates = 14; - uint64 num_declared_classes = 15; // Includes both Cairo 0 and Cairo 1. - uint64 num_deployed_contracts = 16; // This includes the replaced classes too. + Hash block_hash = 1; // For the structure of the block hash, see https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/header/#block_hash + Hash parent_hash = 2; + uint64 number = 3; // This can be deduced from context. We can consider removing this field. + uint64 time = 4; // Encoded in Unix time. + Address sequencer_address = 5; + Hash state_root = 6; // Patricia root of contract and class patricia tries. Each of those tries are of height 251. Same as in L1. Later more trees will be included + StateDiffCommitment state_diff_commitment = 7; // The state diff commitment returned by the Starknet Feeder Gateway + // For more info, see https://community.starknet.io/t/introducing-p2p-authentication-and-mismatch-resolution-in-v0-12-2/97993 + // The leaves contain a hash of the transaction hash and transaction signature. + Patricia transactions = 8; // By order of execution. TBD: required? the client can execute (powerful machine) and match state diff + Patricia events = 9; // By order of issuance. TBD: in receipts? + Hash receipts = 10; // By order of issuance. This is a patricia root. No need for length because it's the same length as transactions. + string protocol_version = 11; // Starknet version + Uint128 gas_price_fri = 12; + Uint128 gas_price_wei = 13; + Uint128 data_gas_price_fri = 14; + Uint128 data_gas_price_wei = 15; + L1DataAvailabilityMode l1_data_availability_mode = 16; // for now, we assume a small consensus, so this fits in 1M. Else, these will be repeated and extracted from this message. - repeated ConsensusSignature signatures = 17; - // can be more explicit here about the signature structure as this is not part of account abstraction + repeated ConsensusSignature signatures = 17; + // can be more explicit here about the signature structure as this is not part of account abstraction } // sent to all peers (except the ones this was received from, if any). // for a fraction of peers, also send the GetBlockHeaders response (as if they asked for it for this block) message NewBlock { oneof maybe_full { - BlockID id = 1; + BlockID id = 1; BlockHeadersResponse header = 2; } } @@ -46,6 +45,6 @@ message BlockHeadersRequest { message BlockHeadersResponse { oneof header_message { SignedBlockHeader header = 1; - Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its header. + Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its header. } } diff --git a/p2p/proto/mempool.proto b/p2p/proto/mempool.proto index fc8b4dd..3392c78 100644 --- a/p2p/proto/mempool.proto +++ b/p2p/proto/mempool.proto @@ -9,7 +9,7 @@ message PooledTransactionsRequest { message Known { oneof known { - Hashes txs = 1; // for mempool of 2000 txs, this will be 64K. Can use Hash32 instead (8K)... + Hashes txs = 1; // for mempool of 2000 txs, this will be 64K. Can use Hash32 instead (8K)... uint64 marker = 2; // since last returned marker. } } @@ -20,11 +20,11 @@ message PooledTransactionsRequest // can propagate it without being pulled // nodes should track state diffs to know when txs have been included (the contract nonce increases) message PolledTransactionsResponse { - optional uint64 marker = 1; // optional, if the peer supports that. - bool baseline = 2; // means treat all data as baseline, not diff (may be if 'known' was sent but the mempool was reset/reorged + optional uint64 marker = 1; // optional, if the peer supports that. + bool baseline = 2; // means treat all data as baseline, not diff (may be if 'known' was sent but the mempool was reset/reorged oneof responses { - Transactions pending = 3; // if 'known' is given, they will be only txs added after the known - Fin fin = 4; + Transactions pending = 3; // if 'known' is given, they will be only txs added after the known + Fin fin = 4; } } diff --git a/p2p/proto/protocols.md b/p2p/proto/protocols.md index 1fb4161..32c8b6f 100644 --- a/p2p/proto/protocols.md +++ b/p2p/proto/protocols.md @@ -1,11 +1,274 @@ -## Protocols Breakdown +# Starknet Protocols + +## Terminology +`Request` - The message that is sent in order to request data from another node. + +`Response` - The message that is sent in response to a `Request` message and contains the requested data. + +`Querying peer` - The peer that sent the `Request` message. + +`Replying peer` - The peer that received the `Request` message. + +`Session` - All the messages related to responding to a `Request`. Including both the `Request` +message and all the `Response` messages. This term is used to distinguish between `Response` +messages from one `Request` to another. + +## Protocols Briefing +The following table describes the different protocols in Starknet, the name that should be used in +negotiation, and the protobuf messages related to the protocol. | Protocol | Name (for negotiation) | Request Message | Response Message | | ------------ | -------------- | -------------- | -------------- | -| Headers | /starknet/headers/1 | [BlockHeadersRequest](./block.proto) | [BlockHeadersResponse](./block.proto) | -| StateDiffs | /starknet/state_diffs/1 | [StateDiffsRequest](./state.proto) | [StateDiffsResponse](./state.proto) | -| Classes | /starknet/classes/1 | [ClassesRequest](./class.proto) | [ClassesResponse](./class.proto) | -| Transactions | /starknet/transactions/1 | [TransactionsRequest](./transaction.proto) | [TransactionsResponse](./transaction.proto) | -| Receipts | /starknet/receipts/1 | [ReceiptsRequest](./receipt.proto) | [ReceiptResponse](./receipt.proto) | -| Events | /starknet/events/1 | [EventsRequest](./event.proto) | [EventsResponse](./event.proto) | +| Headers | /starknet/headers/0.1.0-rc.0 | [BlockHeadersRequest](./header.proto) | [BlockHeadersResponse](./header.proto) | +| StateDiffs | /starknet/state_diffs/0.1.0-rc.0 | [StateDiffsRequest](./state.proto) | [StateDiffsResponse](./state.proto) | +| Classes | /starknet/classes/0.1.0-rc.0 | [ClassesRequest](./class.proto) | [ClassesResponse](./class.proto) | +| Transactions | /starknet/transactions/0.1.0-rc.0 | [TransactionsRequest](./transaction.proto) | [TransactionsResponse](./transaction.proto) | +| Events | /starknet/events/0.1.0-rc.0 | [EventsRequest](./event.proto) | [EventsResponse](./event.proto) | | Kademlia (for discovery) | /starknet/kad// | -| Identify (for discovery) | /starknet/id/ | + +In addition, nodes should also support the `Identify` protocol, who's name for negotiation is +`/ipfs/id/1.0.0` + +## Overview +In each Starknet protocol (except for Kademlia and Identify), one peer sends a single request +message and the other peer responds with multiple response messages + +The request is always a range of blocks and the responses are data related to those blocks. + +This allows the querying peer to process data (e.g calculate hashes) before it downloaded all the +data that the replying peer sent in the session. + +The data sent is always a `oneof` of messages containing data and a [Fin](#fin). + +### Partitioning data into blocks +Except for the [headers](#headers) protocol, The partition of data into blocks does not appear in +the data itself. + +This partition can be done based on the data in the header. + +For each protocol, in the header, there's a commitment on the data given by this protocol. +The commitment is made of hash and number of elements + +The commitment is included inside the block hash. Meaning that it can be validated once we +downloaded the header. + +The length field can be used to determine in each protocol when does the data of one block stops and +the data of the next block after it begins. + +### Length Prefix +Each response message is prefixed with the amount of bytes that the encoded message contains. + +The length is written as a [varint](https://protobuf.dev/programming-guides/encoding/#varints) + +The request message is not prefixed with length. The querying peer should close its writer end once +it sends the request message (it's the only message it should send in the session). The replying peer +should read bytes until it reaches EOF and thus it doesn't need to know the length before reading +the message + +### Limits +- Each message must be less than 1MB, except for the [Class](./class.proto) message which is allowed +to be up to 4MB. +- TBD limits on the amount of total messages/bytes in a single session +- TBD timeout for a session +- TBD timeout between two messages + +### Optional fields +In `proto3`, each field that's a nested message is optional, and the `optional` keyword does nothing +for that field (See this [issue](https://github.com/protocolbuffers/protobuf/issues/249)). + +We want to mark nested fields as required, so every field that is not marked as `optional` is +considered required, even if it's nested. + +If a peer sends a message with a missing required field, they are considered malicious. + +### Iteration +The request message in each protocol contains an [Iteration](./common.proto). + +The Iteration message defines an ordered range of blocks. + +The Iteration message has the following fields: +- **start**: The first block that we request, by hash or by number +- **direction**: Decides whether the blocks we send after the starting block are the blocks after it +or the blocks before it +- **limit**: The amount of blocks requested +- **step**: Given a step `i`, the responder will return every i'th block it sees until it reached `limit` blocks. + +For example. If the request has the values: +- **start**: 10 +- **direction**: Backward +- **limit**: 3 +- **step**: 3 + +Then the blocks we'll receive will be blocks no. 10, 7, 4 + +### Fin +The [Fin](./common.proto) message is an empty message that signals the end of a protocol. +After sending all the data, the replying peer sends a Fin message. +If a replying peer sends any additional messages after sending a fin message, they are considered +malicious and the connection with them should be dropped. + +### Missing Data +We currently assume that if a peer has some of the data for a block then it has all the data for +that block. + +If the replying peer doesn't have all the data for all the blocks in the request, it should send +all the data for the blocks it has according to the order of the iteration and stop and send Fin +when it encountered the first missing block (even if it's the first block). + +### Pre-0.13.2 blocks +Starting from Starknet v0.13.2, the block hash covers all the data related to the block that a peer +can download from another peer + +The fields that are not in the block hash of blocks before v0.13.2 are: +* State diff commitment and length +* Receipt commitment +* L1 data availability mode +* Gas and data gas prices +* In event commitment, the emmiting transaction + +This means that a peer cannot download those blocks from an untrusted peer, because +the replying peer can change the value of those fields and the signed block hash won't change. + +In order to solve this issue, we will add a file with all the hashes of blocks from all public +Starknet chains, where every hash is calculated with the v0.13.2 formula. + +## Protocols Breakdown +### Headers +The Headers protocol is used to download block headers and signatures. + +Its name for negotiation is `/starknet/headers/0.1.0-rc.0` + +Each single message is a fin or a [SignedBlockHeader](./header.proto) + +A header is comprised of: +* block hash +* hash of the parent block +* block number +* metadata for the block (timestamp, sequencer address, protocol version, gas prices) +* Commitments on the data of each of the other protocols. Each commitment is comprised of length and +hash. For more details see the [block hash description](https://docs.google.com/document/d/1EIlHskVJEyztS8eXRyPzd8cZwGuPcIKR5xUAIawzryk/edit) + +There's a single message for each block. That's because we assume that there will never be a header +that's bigger than the [message size limit](#limits), even when including all signatures for this +header. + +### Transactions and Receipts +The transactions protocol is used to download the transactions and receipts in a range of blocks. + +Its name for negotiation is `/starknet/transactions/0.1.0-rc.0` + +Each single message is either a fin, or a [TransactionWithReceipt](./transaction.proto) +(A pair of [Transaction](./transaction.proto) and [Receipt](./receipt.proto)) + +Each transaction represents a Starknet transaction. For more detail on the different transaction +types, their content and their hash calculation see [here](https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/). + +A receipt is comprised of +* The hash of the transaction this receipt belongs to +* The actual fee paid for this transaction and the price unit used for the payment +* The messages to l1 this transaction generated. +* The execution resources this transaction took. Comprised of Cairo resources and data availability +resources +* The revert reason as a string if this transaction was reverted. If the `revert_reason` field is +missing, it means that this transaction was successful + +The transactions and receipts need to be sent according to the transaction execution order. + +The commitment of the transactions in the header is comprised of the number of transactions and +their merkle root. + +In order to verify the transactions, the node needs to calculate their hash and compare it to the +transactions merkle root in the block header. + +The same is true for receipts. They also have a commitment in the header. + +The number can be used to delimiter between the transactions and receipts of each block. + +In order to avoid potential DDoS, the querying peer should reject the connection and declare the +replying peer malicious once it sent more transactions with receipts than the sum of transaction +amounts in the headers of the requested blocks. + +### Events +The Events protocol is used to download the events emitted in a range of blocks. + +Its name for negotiation is `/starknet/events/0.1.0-rc.0` + +Each single message is a fin or an [Event](./event.proto) + +Each event contains +* The hash of the transaction that emitted this event +* The address of the contract that emitted this event. +* The keys and data of this event. + +The commitment of the events in the header is comprised of the number of events and +their merkle root. + +In order to verify the events, the node needs to calculate their hash and compare it to the +events merkle root in the block header. + +The number can be used to delimiter between the events of each block, and the transaction index field +of each event is used to delimiter between the events of each transaction within a block. + +In order to avoid potential DDoS, the querying peer should reject the connection and declare the +replying peer malicious once it sent more events than the sum of event amounts in the headers of the +requested blocks. + +### State Diff +The state diff protocol is used to download the state diffs created by a range of blocks. +It can be used to avoid running the transactions for computing the state diff. + +Its name for negotiation is `/starknet/state_diffs/0.1.0-rc.0` + +In order to verify the state diff, the node needs to calculate its hash and compare it to the +`state_diff_commitment` field in the block header. The structure of the hash is detailed [here](https://community.starknet.io/t/introducing-p2p-authentication-and-mismatch-resolution-in-v0-12-2/97993) + +#### Message structure +Each single message is either a fin, a [ContractDiff](./state.proto) or a [DeclaredClass](./state.proto). + +A ContractDiff represents all the changes made for a given contract. This includes: +* Storage diffs - Represented by the `values` field. +* Nonce updates - Represented by the `nonce` field if present. +* Deployed contracts - Represented by the `class_hash` field if present. If it's present it means +that this contract was deployed or replaced in this block. + +A DeclaredClass represents a declared class. If the class is Cairo1 then the `compiled_class_hash` +field will be present + +There's no guarantee on what ordering will the messages have for a single block's state diff. +The only constraint is that each `ContractDiff` can't be empty, i.e one of the following fields have +value: +* `nonce` +* `class_hash` +* `values` + +#### Length +As we've seen in other protocols like [Transactions](#transactions and receipts), The node needs to know the +amount of data to expect in order to reject malicious peers trying to DDoS the node. + +In order to do that, the node needs to look at the block header at the field `state_diff_length`. +This field is equal to +`num_storage_diffs + num_nonce_updates + num_deployed_contracts + num_declared_classes` + +Then, whenever the node receives a [StateDiffResponse](./state.proto), it needs to add a number to +a counter and declare the peer malicious if the counter becomes bigger than `state_diff_length`: +* For DeclaredClass, the counter should increase by 1. +* For ContractDiff, the counter should increase by the length of the field `values`, +and by 1 if the field `class_hash` is present, and by 1 if the field `nonce` is present. + +### Classes +The classes protocol is used to download the classes declared in a range of blocks. + +Its name for negotiation is `/starknet/classes/0.1.0-rc.0` + +Each single message is a fin or a [Class](./class.proto). For more information on classes, see +[here](https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-classes/) + +You should use the [state diff protocol](#state-diff) before using this protocol. +The reason is that the class hashes and amount of declared classes per block are part of the +`state_diff_commitment` in the header, and these values are needed to +1. Validate the results of the classes protocol. +2. Delimiter between the classes of each block. +3. Reject a replying peer DDoSing us if it sent more classes than the amount of declared classes in +the block range. + +The fact that each class is a single message and each message is up to 4MB gives us a protection +from DDoS the same way other protocols gain protection with a length field. diff --git a/p2p/proto/receipt.proto b/p2p/proto/receipt.proto index bd17d4f..be03d91 100644 --- a/p2p/proto/receipt.proto +++ b/p2p/proto/receipt.proto @@ -2,9 +2,14 @@ syntax = "proto3"; import "p2p/proto/common.proto"; message MessageToL1 { - Felt252 from_address = 1; - repeated Felt252 payload = 2; - EthereumAddress to_address = 3; + Felt252 from_address = 2; + repeated Felt252 payload = 3; + EthereumAddress to_address = 4; +} + +enum PriceUnit { + Wei = 0; + Fri = 1; } message EthereumAddress { @@ -14,27 +19,29 @@ message EthereumAddress { message Receipt { message ExecutionResources { message BuiltinCounter { - uint32 bitwise = 1; - uint32 ecdsa = 2; - uint32 ec_op = 3; - uint32 pedersen = 4; + uint32 bitwise = 1; + uint32 ecdsa = 2; + uint32 ec_op = 3; + uint32 pedersen = 4; uint32 range_check = 5; - uint32 poseidon = 6; - uint32 keccak = 7; - uint32 output = 8; + uint32 poseidon = 6; + uint32 keccak = 7; + uint32 output = 8; } - BuiltinCounter builtins = 1; - uint32 steps = 2; - uint32 memory_holes = 3; + BuiltinCounter builtins = 1; + uint32 steps = 2; + uint32 memory_holes = 3; + Felt252 l1_gas = 4; + Felt252 l1_data_gas = 5; } message Common { - Hash transaction_hash = 1; - Felt252 actual_fee = 2; - repeated MessageToL1 messages_sent = 3; - ExecutionResources execution_resources = 4; - string revert_reason = 5; + Felt252 actual_fee = 2; + PriceUnit price_unit = 3; + repeated MessageToL1 messages_sent = 4; + ExecutionResources execution_resources = 5; + optional string revert_reason = 6; } @@ -43,8 +50,8 @@ message Receipt { } message L1Handler { - Common common = 1; - Hash msg_hash = 2; + Common common = 1; + Hash msg_hash = 2; } message Declare { @@ -57,31 +64,15 @@ message Receipt { } message DeployAccount { - Common common = 1; + Common common = 1; Felt252 contract_address = 2; } oneof type { - Invoke invoke = 1; - L1Handler l1_handler = 2; - Declare declare = 3; - Deploy deprecated_deploy = 4; - DeployAccount deploy_account = 5; + Invoke invoke = 1; + L1Handler l1_handler = 2; + Declare declare = 3; + Deploy deprecated_deploy = 4; + DeployAccount deploy_account = 5; } } - -message ReceiptsRequest { - Iteration iteration = 1; -} - -message Receipts { - repeated Receipt items = 2; -} - -// Responses are sent ordered by the order given in the request. -message ReceiptsResponse { - oneof receipt_message { - Receipt receipt = 1; - Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its receipts. - } -} diff --git a/p2p/proto/snapshot.proto b/p2p/proto/snapshot.proto index 3f86674..a4f1863 100644 --- a/p2p/proto/snapshot.proto +++ b/p2p/proto/snapshot.proto @@ -5,17 +5,17 @@ import "p2p/proto/state.proto"; message PatriciaNode { message Edge { - uint32 length = 1; - Felt252 path = 2; // as bits of left/right - Felt252 value = 3; + uint32 length = 1; + Felt252 path = 2; // as bits of left/right + Felt252 value = 3; } message Binary { - Felt252 left = 1; + Felt252 left = 1; Felt252 right = 2; } oneof node { - Edge edge = 1; + Edge edge = 1; Binary binary = 2; } } @@ -28,20 +28,20 @@ message PatriciaRangeProof { // leafs of the contract state tree message ContractState { Address address = 1; // the key - Hash class = 2; - Hash storage = 3; // patricia - uint64 nonce = 4; + Hash class = 2; + Hash storage = 3; // patricia + uint64 nonce = 4; } // request a range from the contract state tree that matches the given root (block) // starts at 'start' and ends no more than 'end'. // the result is (ContractRange+, PatriciaRangeProof)* message ContractRangeRequest { - uint32 domain = 1; // volition - Hash state_root = 2; - Address start = 3; - Address end = 4; - uint32 chunks_per_proof = 5; // how many ContractRange items to send before sending a proof + uint32 domain = 1; // volition + Hash state_root = 2; + Address start = 3; + Address end = 4; + uint32 chunks_per_proof = 5; // how many ContractRange items to send before sending a proof } // stream of leaves in the contracts tree @@ -50,49 +50,49 @@ message ContractRange { } message ContractRangeResponse { - optional Hash root = 1; // may not appear if Fin is sent to end the whole response + optional Hash root = 1; // may not appear if Fin is sent to end the whole response optional Hash contracts_root = 2;// may not appear if Fin is sent to end the whole response - optional Hash classes_root = 3;// may not appear if Fin is sent to end the whole response + optional Hash classes_root = 3;// may not appear if Fin is sent to end the whole response oneof responses { ContractRange range = 4; - Fin fin = 5; + Fin fin = 5; } } // duplicate of GetContractRange. Can introduce a 'type' instead. // result is (Classes+, PatriciaRangeProof)* message ClassRangeRequest { - Hash root = 1; - Hash start = 2; - Hash end = 3; + Hash root = 1; + Hash start = 2; + Hash end = 3; uint32 chunks_per_proof = 4; } message ClassRangeResponse { - optional Hash root = 1; // may not appear if Fin is sent to end the whole response + optional Hash root = 1; // may not appear if Fin is sent to end the whole response optional Hash contracts_root = 2;// may not appear if Fin is sent to end the whole response - optional Hash classes_root = 3;// may not appear if Fin is sent to end the whole response + optional Hash classes_root = 3;// may not appear if Fin is sent to end the whole response oneof responses { Classes classes = 4; - Fin fin = 5; + Fin fin = 5; } } // A position in some contract's state tree is identified by the state tree's root and the key in it message StorageLeafQuery { - Hash contract_storage_root = 1; - Felt252 key = 2; + Hash contract_storage_root = 1; + Felt252 key = 2; } message StorageRangeQuery { StorageLeafQuery start = 1; - StorageLeafQuery end = 2; + StorageLeafQuery end = 2; } // result is (ContractStorageRange+, PatriciaRangeProof)* message ContractStorageRequest { - uint32 domain = 1; // volition - Hash state_root = 2; + uint32 domain = 1; // volition + Hash state_root = 2; repeated StorageRangeQuery query = 3; } @@ -101,9 +101,9 @@ message ContractStorage { } message ContractStorageResponse { - optional Hash state_root = 1; // may not appear if Fin is sent to end the whole response + optional Hash state_root = 1; // may not appear if Fin is sent to end the whole response oneof responses { ContractStorage storage = 2; - Fin fin = 3; + Fin fin = 3; } } diff --git a/p2p/proto/state.proto b/p2p/proto/state.proto index c945628..ff157d8 100644 --- a/p2p/proto/state.proto +++ b/p2p/proto/state.proto @@ -9,12 +9,16 @@ message ContractStoredValue { } message ContractDiff { - Address address = 1; - optional Felt252 nonce = 2; // Present only if the nonce was updated - optional Hash class_hash = 3; // Present only if the contract was deployed or replaced in this block. - optional bool is_replaced = 4; // Present only if the contract was deployed or replaced, in order to determine whether the contract was deployed or replaced. - repeated ContractStoredValue values = 5; - uint32 domain = 6; // volition state domain + Address address = 1; + optional Felt252 nonce = 2; // Present only if the nonce was updated + optional Hash class_hash = 3; // Present only if the contract was deployed or replaced in this block. + repeated ContractStoredValue values = 4; + VolitionDomain domain = 5; +} + +message DeclaredClass { + Hash class_hash = 1; + optional Hash compiled_class_hash = 2; // Present only if the class is Cairo1 } message StateDiffsRequest { @@ -25,7 +29,8 @@ message StateDiffsRequest { message StateDiffsResponse { // All of the messages related to a block need to be sent before a message from the next block is sent. oneof state_diff_message { - ContractDiff contract_diff = 1; // Multiple contract diffs for the same contract may appear continuously if the diff is too large. - Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its state diff. + ContractDiff contract_diff = 1; // Multiple contract diffs for the same contract may appear continuously if the diff is too large or if it's more convenient. + DeclaredClass declared_class = 2; + Fin fin = 3; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its state diff. } } diff --git a/p2p/proto/transaction.proto b/p2p/proto/transaction.proto index 8eb3d89..3faed1f 100644 --- a/p2p/proto/transaction.proto +++ b/p2p/proto/transaction.proto @@ -1,8 +1,9 @@ syntax = "proto3"; import "p2p/proto/common.proto"; +import "p2p/proto/receipt.proto"; message ResourceLimits { - Felt252 max_amount = 1; + Felt252 max_amount = 1; Felt252 max_price_per_unit = 2; } @@ -15,128 +16,134 @@ message AccountSignature { repeated Felt252 parts = 1; } +// This is a transaction that is already accepted in a block. Once we have a mempool, we will define +// a separate message for BroadcastedTransaction. message Transaction { message DeclareV0 { - Address sender = 1; - Felt252 max_fee = 2; - AccountSignature signature = 3; - Hash class_hash = 4; + Address sender = 1; + Felt252 max_fee = 2; + AccountSignature signature = 3; + Hash class_hash = 4; } message DeclareV1 { - Address sender = 1; - Felt252 max_fee = 2; - AccountSignature signature = 3; - Hash class_hash = 4; - Felt252 nonce = 5; + Address sender = 1; + Felt252 max_fee = 2; + AccountSignature signature = 3; + Hash class_hash = 4; + Felt252 nonce = 5; } message DeclareV2 { - Address sender = 1; - Felt252 max_fee = 2; - AccountSignature signature = 3; - Hash class_hash = 4; - Felt252 nonce = 5; - Felt252 compiled_class_hash = 6; + Address sender = 1; + Felt252 max_fee = 2; + AccountSignature signature = 3; + Hash class_hash = 4; + Felt252 nonce = 5; + Hash compiled_class_hash = 6; } // see https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3 message DeclareV3 { - Address sender = 1; - AccountSignature signature = 2; - Hash class_hash = 3; - Felt252 nonce = 4; - Felt252 compiled_class_hash = 5; - ResourceBounds resource_bounds = 6; - Felt252 tip = 7; - Address paymaster_data = 8; - Address account_deployment_data = 9; - string nonce_domain = 10; // rename to nonce_data_availability_mode ? - string fee_domain = 11; // rename to fee_data_availability_mode ? + Address sender = 1; + AccountSignature signature = 2; + Hash class_hash = 3; + Felt252 nonce = 4; + Hash compiled_class_hash = 5; + ResourceBounds resource_bounds = 6; + uint64 tip = 7; + repeated Felt252 paymaster_data = 8; + repeated Felt252 account_deployment_data = 9; + VolitionDomain nonce_data_availability_mode = 10; + VolitionDomain fee_data_availability_mode = 11; } message Deploy { - Hash class_hash = 1; - Felt252 address_salt = 2; + Hash class_hash = 1; + Felt252 address_salt = 2; repeated Felt252 calldata = 3; - uint32 version = 4; + uint32 version = 4; } message DeployAccountV1 { - Felt252 max_fee = 1; - AccountSignature signature = 2; - Hash class_hash = 3; - Felt252 nonce = 4; - Felt252 address_salt = 5; - repeated Felt252 calldata = 6; + Felt252 max_fee = 1; + AccountSignature signature = 2; + Hash class_hash = 3; + Felt252 nonce = 4; + Felt252 address_salt = 5; + repeated Felt252 calldata = 6; } // see https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0 message DeployAccountV3 { - AccountSignature signature = 1; - Hash class_hash = 2; - Felt252 nonce = 3; - Felt252 address_salt = 4; - repeated Felt252 calldata = 5; - ResourceBounds resource_bounds = 6; - Felt252 tip = 7; - Address paymaster_data = 8; - string nonce_domain = 9; // rename to nonce_data_availability_mode ? - string fee_domain = 10; // rename to fee_data_availability_mode ? + AccountSignature signature = 1; + Hash class_hash = 2; + Felt252 nonce = 3; + Felt252 address_salt = 4; + repeated Felt252 calldata = 5; + ResourceBounds resource_bounds = 6; + uint64 tip = 7; + repeated Felt252 paymaster_data = 8; + VolitionDomain nonce_data_availability_mode = 9; + VolitionDomain fee_data_availability_mode = 10; } message InvokeV0 { - Felt252 max_fee = 1; - AccountSignature signature = 2; - Address address = 3; - Felt252 entry_point_selector = 4; - repeated Felt252 calldata = 5; + Felt252 max_fee = 1; + AccountSignature signature = 2; + Address address = 3; + Felt252 entry_point_selector = 4; + repeated Felt252 calldata = 5; } message InvokeV1 { - Address sender = 1; - Felt252 max_fee = 2; - AccountSignature signature = 3; - repeated Felt252 calldata = 4; - Felt252 nonce = 5; + Address sender = 1; + Felt252 max_fee = 2; + AccountSignature signature = 3; + repeated Felt252 calldata = 4; + Felt252 nonce = 5; } // see https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x41906f1c314cca5f43170ea75d3b1904196a10101190d2b12a41cc61cfd17c message InvokeV3 { - Address sender = 1; - AccountSignature signature = 2; - repeated Felt252 calldata = 3; - ResourceBounds resource_bounds = 4; - Felt252 tip = 5; - Address paymaster_data = 6; - Address account_deployment_data = 7; - string nonce_domain = 8; // rename to nonce_data_availability_mode ? - string fee_domain = 9; // rename to fee_data_availability_mode ? - Felt252 nonce = 10; + Address sender = 1; + AccountSignature signature = 2; + repeated Felt252 calldata = 3; + ResourceBounds resource_bounds = 4; + uint64 tip = 5; + repeated Felt252 paymaster_data = 6; + repeated Felt252 account_deployment_data = 7; + VolitionDomain nonce_data_availability_mode = 8; + VolitionDomain fee_data_availability_mode = 9; + Felt252 nonce = 10; } message L1HandlerV0 { - Felt252 nonce = 1; - Address address = 2; - Felt252 entry_point_selector = 3; - repeated Felt252 calldata = 4; + Felt252 nonce = 1; + Address address = 2; + Felt252 entry_point_selector = 3; + repeated Felt252 calldata = 4; } oneof txn { - DeclareV0 declare_v0 = 1; - DeclareV1 declare_v1 = 2; - DeclareV2 declare_v2 = 3; - DeclareV3 declare_v3 = 4; - Deploy deploy = 5; + DeclareV0 declare_v0 = 1; + DeclareV1 declare_v1 = 2; + DeclareV2 declare_v2 = 3; + DeclareV3 declare_v3 = 4; + Deploy deploy = 5; DeployAccountV1 deploy_account_v1 = 6; DeployAccountV3 deploy_account_v3 = 7; - InvokeV0 invoke_v0 = 8; - InvokeV1 invoke_v1 = 9; - InvokeV3 invoke_v3 = 10; - L1HandlerV0 l1_handler = 11; + InvokeV0 invoke_v0 = 8; + InvokeV1 invoke_v1 = 9; + InvokeV3 invoke_v3 = 10; + L1HandlerV0 l1_handler = 11; } - Hash transaction_hash = 12; +} + +message TransactionWithReceipt { + Transaction transaction = 1; + Receipt receipt = 2; } // TBD: can support a flag to return tx hashes only, good for standalone mempool to remove them, @@ -145,10 +152,11 @@ message TransactionsRequest { Iteration iteration = 1; } -// Responses are sent ordered by the order given in the request. +// Responses are sent ordered by the order given in the request. The order inside each block is +// according to the execution order. message TransactionsResponse { oneof transaction_message { - Transaction transaction = 1; - Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its transactions. + TransactionWithReceipt transaction_with_receipt = 1; + Fin fin = 2; // Fin is sent after the peer sent all the data or when it encountered a block that it doesn't have its transactions. } }