// Specification of the user facing gRPC API.
syntax = "proto3";
package rpc;

import "types/account.proto";
import "types/blockchain.proto";
import "types/note.proto";
import "types/primitives.proto";
import "types/transaction.proto";
import "google/protobuf/empty.proto";

// RPC API
// ================================================================================================

// RPC API for the RPC component
service Api {
    // Returns the status info of the node.
    rpc Status(google.protobuf.Empty) returns (RpcStatus) {}

    // Returns a Sparse Merkle Tree opening proof for each requested nullifier
    //
    // Each proof demonstrates either:
    // - **Inclusion**: Nullifier exists in the tree (note was consumed)
    // - **Non-inclusion**: Nullifier does not exist (note was not consumed)
    //
    // The `leaf` field indicates the status:
    // * `empty_leaf_index`: Non-inclusion proof (nullifier not in tree)
    // * `single` or `multiple`: Inclusion proof only if the requested nullifier appears as a key.
    //
    // Verify proofs against the nullifier tree root in the latest block header.
    rpc CheckNullifiers(NullifierList) returns (CheckNullifiersResponse) {}

    // Returns the latest details of the specified account.
    rpc GetAccount(AccountRequest) returns (AccountResponse) {}

    // Returns raw block data for the specified block number.
    rpc GetBlockByNumber(blockchain.BlockNumber) returns (blockchain.MaybeBlock) {}

    // Retrieves block header by given block number. Optionally, it also returns the MMR path
    // and current chain length to authenticate the block's inclusion.
    rpc GetBlockHeaderByNumber(BlockHeaderByNumberRequest) returns (BlockHeaderByNumberResponse) {}

    // Returns a list of notes matching the provided note IDs.
    rpc GetNotesById(note.NoteIdList) returns (note.CommittedNoteList) {}

    // Returns the script for a note by its root.
    rpc GetNoteScriptByRoot(note.NoteRoot) returns (MaybeNoteScript) {}

    // Submits proven transaction to the Miden network. Returns the node's current block height.
    rpc SubmitProvenTransaction(transaction.ProvenTransaction) returns (blockchain.BlockNumber) {}

    // Submits a proven batch of transactions to the Miden network.
    //
    // The batch may include transactions which were are:
    //
    //   - already in the mempool i.e. previously successfully submitted
    //   - will be submitted to the mempool in the future
    //   - won't be submitted to the mempool at all
    //
    // All transactions in the batch but not in the mempool must build on the current mempool
    // state following normal transaction submission rules.
    //
    // Returns the node's current block height.
    rpc SubmitProvenBatch(transaction.ProvenTransactionBatch) returns (blockchain.BlockNumber) {}

    // Returns a list of nullifiers that match the specified prefixes and are recorded in the node.
    //
    // Note that only 16-bit prefixes are supported at this time.
    rpc SyncNullifiers(SyncNullifiersRequest) returns (SyncNullifiersResponse) {}

    // Returns account vault updates for specified account within a block range.
    rpc SyncAccountVault(SyncAccountVaultRequest) returns (SyncAccountVaultResponse) {}

    // Returns info which can be used by the client to sync up to the tip of chain for the notes they are interested in.
    //
    // Client specifies the `note_tags` they are interested in, and the block height from which to search for new for
    // matching notes for. The request will then return the next block containing any note matching the provided tags.
    //
    // The response includes each note's metadata and inclusion proof.
    //
    // A basic note sync can be implemented by repeatedly requesting the previous response's block until reaching the
    // tip of the chain.
    rpc SyncNotes(SyncNotesRequest) returns (SyncNotesResponse) {}

    // Returns info which can be used by the client to sync up to the latest state of the chain
    // for the objects (accounts and notes) the client is interested in.
    //
    // This request returns the next block containing requested data. It also returns `chain_tip`
    // which is the latest block number in the chain. Client is expected to repeat these requests
    // in a loop until `response.block_header.block_num == response.chain_tip`, at which point
    // the client is fully synchronized with the chain.
    //
    // Each update response also contains info about new notes, accounts etc. created. It also returns
    // Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain
    // MMR peaks and chain MMR nodes.
    //
    // For preserving some degree of privacy, note tags contain only high
    // part of hashes. Thus, returned data contains excessive notes, client can make
    // additional filtering of that data on its side.
    rpc SyncState(SyncStateRequest) returns (SyncStateResponse) {}

    // Returns storage map updates for specified account and storage slots within a block range.
    rpc SyncAccountStorageMaps(SyncAccountStorageMapsRequest) returns (SyncAccountStorageMapsResponse) {}

    // Returns transactions records for specific accounts within a block range.
    rpc SyncTransactions(SyncTransactionsRequest) returns (SyncTransactionsResponse) {}

    // Returns the query parameter limits configured for RPC methods.
    //
    // These define the maximum number of each parameter a method will accept.
    // Exceeding the limit will result in the request being rejected and you should instead send
    // multiple smaller requests.
    rpc GetLimits(google.protobuf.Empty) returns (RpcLimits) {}
}

// RPC STATUS
// ================================================================================================

// Represents the status of the node.
message RpcStatus {
    // The rpc component's running version.
    string version = 1;

    // The genesis commitment.
    primitives.Digest genesis_commitment = 2;

    // The store status.
    StoreStatus store = 3;

    // The block producer status.
    BlockProducerStatus block_producer = 4;
}


// BLOCK PRODUCER STATUS
// ================================================================================================


// Represents the status of the block producer.
message BlockProducerStatus {
    // The block producer's running version.
    string version = 1;

    // The block producer's status.
    string status = 2;

    // The block producer's current view of the chain tip height.
    //
    // This is the height of the latest block that the block producer considers
    // to be part of the canonical chain.
    fixed32 chain_tip = 4;

    // Statistics about the mempool.
    MempoolStats mempool_stats = 3;
}

// Statistics about the mempool.
message MempoolStats {
    // Number of transactions currently in the mempool waiting to be batched.
    uint64 unbatched_transactions = 1;

    // Number of batches currently being proven.
    uint64 proposed_batches = 2;

    // Number of proven batches waiting for block inclusion.
    uint64 proven_batches = 3;
}

// STORE STATUS
// ================================================================================================

// Represents the status of the store.
message StoreStatus {
    // The store's running version.
    string version = 1;

    // The store's status.
    string status = 2;

    // Number of the latest block in the chain.
    fixed32 chain_tip = 3;
}

// GET BLOCK HEADER BY NUMBER
// ================================================================================================

// Returns the block header corresponding to the requested block number, as well as the merkle
// path and current forest which validate the block's inclusion in the chain.
//
// The Merkle path is an MMR proof for the block's leaf, based on the current chain length.
message BlockHeaderByNumberRequest {
    // The target block height, defaults to latest if not provided.
    optional uint32 block_num = 1;
    // Whether or not to return authentication data for the block header.
    optional bool include_mmr_proof = 2;
}

// Represents the result of getting a block header by block number.
message BlockHeaderByNumberResponse {
    // The requested block header.
    blockchain.BlockHeader block_header = 1;

    // Merkle path to verify the block's inclusion in the MMR at the returned `chain_length`.
    optional primitives.MerklePath mmr_path = 2;

    // Current chain length.
    optional fixed32 chain_length = 3;
}

// GET NOTE SCRIPT BY ROOT
// ================================================================================================

// Represents a note script or nothing.
message MaybeNoteScript {
    // The script for a note by its root.
    optional note.NoteScript script = 1;
}

// GET ACCOUNT PROOF
// ================================================================================================

// Defines the request for account details.
message AccountRequest {
    // Request the details for a public account.
    message AccountDetailRequest {
        // Represents a storage slot index and the associated map keys.
        message StorageMapDetailRequest {
            // Indirection required for use in `oneof {..}` block.
            message MapKeys {
                // A list of map keys associated with this storage slot.
                repeated primitives.Digest map_keys = 1;
            }
            // Storage slot name.
            string slot_name = 1;

            oneof slot_data {
                // Request to return all storage map data. If the number exceeds a threshold of 1000 entries,
                // the response will not contain them but must be requested separately.
                bool all_entries = 2;

                // A list of map keys associated with the given storage slot identified by `slot_name`.
                MapKeys map_keys = 3;
            }
        }

        // Last known code commitment to the requester. The response will include account code
        // only if its commitment is different from this value.
        //
        // If the field is ommiteed, the response will not include the account code.
        optional primitives.Digest code_commitment = 1;

        // Last known asset vault commitment to the requester. The response will include asset vault data
        // only if its commitment is different from this value. If the value is not present in the
        // request, the response will not contain one either.
        // If the number of to-be-returned asset entries exceed a threshold, they have to be requested
        // separately, which is signaled in the response message with dedicated flag.
        optional primitives.Digest asset_vault_commitment = 2;

        // Additional request per storage map.
        repeated StorageMapDetailRequest storage_maps = 3;
    }

    // ID of the account for which we want to get data
    account.AccountId account_id = 1;

    // Optional block height at which to return the proof.
    //
    // Defaults to current chain tip if unspecified.
    optional blockchain.BlockNumber block_num = 2;

    // Request for additional account details; valid only for public accounts.
    optional AccountDetailRequest details = 3;
}

// Represents the result of getting account proof.
message AccountResponse {

    message AccountDetails {
        // Account header.
        account.AccountHeader header = 1;

        // Account storage data
        AccountStorageDetails storage_details = 2;

        // Account code; empty if code commitments matched or none was requested.
        optional bytes code = 3;

        // Account asset vault data; empty if vault commitments matched or the requester
        // omitted it in the request.
        optional AccountVaultDetails vault_details = 4;
    }

    // The block number at which the account witness was created and the account details were observed.
    blockchain.BlockNumber block_num = 1;

    // Account ID, current state commitment, and SMT path.
    account.AccountWitness witness = 2;

    // Additional details for public accounts.
    optional AccountDetails details = 3;
}

// Account vault details for AccountResponse
message AccountVaultDetails {
    // A flag that is set to true if the account contains too many assets. This indicates
    // to the user that `SyncAccountVault` endpoint should be used to retrieve the
    // account's assets
    bool too_many_assets = 1;

    // When too_many_assets == false, this will contain the list of assets in the
    // account's vault
    repeated primitives.Asset assets = 2;
}

// Account storage details for AccountResponse
message AccountStorageDetails {
    message AccountStorageMapDetails {
        // Wrapper for repeated storage map entries including their proofs.
        // Used when specific keys are requested to enable client-side verification.
        message MapEntriesWithProofs {
            // Definition of individual storage entries including a proof.
            message StorageMapEntryWithProof {
                primitives.Digest key = 1;
                primitives.Digest value = 2;
                primitives.SmtOpening proof = 3;
            }

            repeated StorageMapEntryWithProof entries = 1;
        }

        // Wrapper for repeated storage map entries (without proofs).
        // Used when all entries are requested for small maps.
        message AllMapEntries {
            // Definition of individual storage entries.
            message StorageMapEntry {
                primitives.Digest key = 1;
                primitives.Digest value = 2;
            }

            repeated StorageMapEntry entries = 1;
        }

        // Storage slot name.
        string slot_name = 1;

        // True when the number of entries exceeds the response limit.
        // When set, clients should use the `SyncAccountStorageMaps` endpoint.
        bool too_many_entries = 2;

        // The map entries (with or without proofs). Empty when too_many_entries is true.
        oneof entries {
            // All storage entries without proofs (for small maps or full requests).
            AllMapEntries all_entries = 3;

            // Specific entries with their SMT proofs (for partial requests).
            MapEntriesWithProofs entries_with_proofs = 4;
        }
    }

    // Account storage header (storage slot info for up to 256 slots)
    account.AccountStorageHeader header = 1;

    // Additional data for the requested storage maps
    repeated AccountStorageMapDetails map_details = 2;
}

// CHECK NULLIFIERS
// ================================================================================================

// List of nullifiers to return proofs for.
message NullifierList {
    // List of nullifiers to return proofs for.
    repeated primitives.Digest nullifiers = 1;
}

// Represents the result of checking nullifiers.
message CheckNullifiersResponse {
    // Each requested nullifier has its corresponding nullifier proof at the same position.
    repeated primitives.SmtOpening proofs = 1;
}

// SYNC NULLIFIERS
// ================================================================================================

// Returns a list of nullifiers that match the specified prefixes and are recorded in the node.
message SyncNullifiersRequest {
    // Block number from which the nullifiers are requested (inclusive).
    BlockRange block_range = 1;

    // Number of bits used for nullifier prefix. Currently the only supported value is 16.
    uint32 prefix_len = 2;

    // List of nullifiers to check. Each nullifier is specified by its prefix with length equal
    // to `prefix_len`.
    repeated uint32 nullifiers = 3;
}

// Represents the result of syncing nullifiers.
message SyncNullifiersResponse {
    // Represents a single nullifier update.
    message NullifierUpdate {
        // Nullifier ID.
        primitives.Digest nullifier = 1;

        // Block number.
        fixed32 block_num = 2;
    }

    // Pagination information.
    PaginationInfo pagination_info = 1;

    // List of nullifiers matching the prefixes specified in the request.
    repeated NullifierUpdate nullifiers = 2;
}

// SYNC ACCOUNT VAULT
// ================================================================================================

// Account vault synchronization request.
//
// Allows requesters to sync asset values for specific public accounts within a block range.
message SyncAccountVaultRequest {
    // Block range from which to start synchronizing.
    //
    // If the `block_to` is specified, this block must be close to the chain tip (i.e., within 30 blocks),
    // otherwise an error will be returned.
    BlockRange block_range = 1;

    // Account for which we want to sync asset vault.
    account.AccountId account_id = 2;
}

message SyncAccountVaultResponse {
    // Pagination information.
    PaginationInfo pagination_info = 1;

    // List of asset updates for the account.
    //
    // Multiple updates can be returned for a single asset, and the one with a higher `block_num`
    // is expected to be retained by the caller.
    repeated AccountVaultUpdate updates = 2;
}

message AccountVaultUpdate {
    // Vault key associated with the asset.
    primitives.Digest vault_key = 1;

    // Asset value related to the vault key.
    // If not present, the asset was removed from the vault.
    optional primitives.Asset asset = 2;

    // Block number at which the above asset was updated in the account vault.
    fixed32 block_num = 3;
}

// SYNC NOTES
// ================================================================================================

// Note synchronization request.
//
// Specifies note tags that requester is interested in. The server will return the first block which
// contains a note matching `note_tags` or the chain tip.
message SyncNotesRequest {
    // Block range from which to start synchronizing.
    BlockRange block_range = 1;

    // Specifies the tags which the requester is interested in.
    repeated fixed32 note_tags = 2;
}

// Represents the result of syncing notes request.
message SyncNotesResponse {
    // Pagination information.
    PaginationInfo pagination_info = 1;

    // Block header of the block with the first note matching the specified criteria.
    blockchain.BlockHeader block_header = 2;

    // Merkle path to verify the block's inclusion in the MMR at the returned `chain_tip`.
    //
    // An MMR proof can be constructed for the leaf of index `block_header.block_num` of
    // an MMR of forest `chain_tip` with this path.
    primitives.MerklePath mmr_path = 3;

    // List of all notes together with the Merkle paths from `response.block_header.note_root`.
    repeated note.NoteSyncRecord notes = 4;
}

// SYNC STATE
// ================================================================================================

// State synchronization request.
//
// Specifies state updates the requester is interested in. The server will return the first block which
// contains a note matching `note_tags` or the chain tip. And the corresponding updates to
// `account_ids` for that block range.
message SyncStateRequest {
    // Last block known by the requester. The response will contain data starting from the next block,
    // until the first block which contains a note of matching the requested tag, or the chain tip
    // if there are no notes.
    fixed32 block_num = 1;

    // Accounts' commitment to include in the response.
    //
    // An account commitment will be included if-and-only-if it is the latest update. Meaning it is
    // possible there was an update to the account for the given range, but if it is not the latest,
    // it won't be included in the response.
    repeated account.AccountId account_ids = 2;

    // Specifies the tags which the requester is interested in.
    repeated fixed32 note_tags = 3;
}

// Represents the result of syncing state request.
message SyncStateResponse {
    // Number of the latest block in the chain.
    fixed32 chain_tip = 1;

    // Block header of the block with the first note matching the specified criteria.
    blockchain.BlockHeader block_header = 2;

    // Data needed to update the partial MMR from `request.block_num + 1` to `response.block_header.block_num`.
    primitives.MmrDelta mmr_delta = 3;

    // List of account commitments updated after `request.block_num + 1` but not after `response.block_header.block_num`.
    repeated account.AccountSummary accounts = 5;

    // List of transactions executed against requested accounts between `request.block_num + 1` and
    // `response.block_header.block_num`.
    repeated transaction.TransactionSummary transactions = 6;

    // List of all notes together with the Merkle paths from `response.block_header.note_root`.
    repeated note.NoteSyncRecord notes = 7;
}

// SYNC ACCOUNT STORAGE MAP
// ================================================================================================

// Storage map synchronization request.
//
// Allows requesters to sync storage map values for specific public accounts within a block range,
// with support for cursor-based pagination to handle large storage maps.
message SyncAccountStorageMapsRequest {
    // Block range from which to start synchronizing.
    //
    // If the `block_to` is specified, this block must be close to the chain tip (i.e., within 30 blocks),
    // otherwise an error will be returned.
    BlockRange block_range = 1;

    // Account for which we want to sync storage maps.
    account.AccountId account_id = 3;
}

message SyncAccountStorageMapsResponse {
    // Pagination information.
    PaginationInfo pagination_info = 1;

    // The list of storage map updates.
    //
    // Multiple updates can be returned for a single slot index and key combination, and the one
    // with a higher `block_num` is expected to be retained by the caller.
    repeated StorageMapUpdate updates = 2;
}

// Represents a single storage map update.
message StorageMapUpdate {
    // Block number in which the slot was updated.
    fixed32 block_num = 1;

    // Storage slot name.
    string slot_name = 2;

    // The storage map key.
    primitives.Digest key = 3;

    // The storage map value.
    primitives.Digest value = 4;
}

// BLOCK RANGE
// ================================================================================================

// Represents a block range.
message BlockRange {
    // Block number from which to start (inclusive).
    fixed32 block_from = 1;

    // Block number up to which to check (inclusive). If not specified, checks up to the latest block.
    optional fixed32 block_to = 2;
}

// PAGINATION INFO
// ================================================================================================

// Represents pagination information for chunked responses.
//
// Pagination is done using block numbers as the axis, allowing requesters to request
// data in chunks by specifying block ranges and continuing from where the previous
// response left off.
//
// To request the next chunk, the requester should use `block_num + 1` from the previous response
// as the `block_from` for the next request.
message PaginationInfo {
    // Current chain tip
    fixed32 chain_tip = 1;

    // The block number of the last check included in this response.
    //
    // For chunked responses, this may be less than `request.block_range.block_to`.
    // If it is less than request.block_range.block_to, the user is expected to make a subsequent request
    // starting from the next block to this one (ie, request.block_range.block_from = block_num + 1).
    fixed32 block_num = 2;
}

// SYNC TRANSACTIONS
// ================================================================================================

// Transactions synchronization request.
//
// Allows requesters to sync transactions for specific accounts within a block range.
message SyncTransactionsRequest {
    // Block range from which to start synchronizing.
    BlockRange block_range = 1;

    // Accounts to sync transactions for.
    repeated account.AccountId account_ids = 2;
}

// Represents the result of syncing transactions request.
message SyncTransactionsResponse {
    // Pagination information.
    PaginationInfo pagination_info = 1;

    // List of transaction records.
    repeated TransactionRecord transactions = 2;
}

// Represents a transaction record.
message TransactionRecord {
    // Block number in which the transaction was included.
    fixed32 block_num = 1;

    // A transaction header.
    transaction.TransactionHeader header = 2;
}

// RPC LIMITS
// ================================================================================================

// Represents the query parameter limits for RPC endpoints.
message RpcLimits {
    // Maps RPC endpoint names to their parameter limits.
    // Key: endpoint name (e.g., "CheckNullifiers", "SyncState")
    // Value: map of parameter names to their limit values
    map<string, EndpointLimits> endpoints = 1;
}

// Represents the parameter limits for a single endpoint.
message EndpointLimits {
    // Maps parameter names to their limit values.
    // Key: parameter name (e.g., "nullifier", "account_id")
    // Value: limit value
    map<string, uint32> parameters = 1;
}
