// Specification of the store RPC.
//
// This provided access to the blockchain data to the other nodes.
syntax = "proto3";
package rpc_store;

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

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

// Store API for the RPC component
service Rpc {
    // Returns the status info.
    rpc Status(google.protobuf.Empty) returns (StoreStatus) {}

    // Returns a nullifier proof for each of the requested nullifiers.
    rpc CheckNullifiers(NullifierList) returns (CheckNullifiersResponse) {}

    // Returns the latest state of an account with the specified ID.
    rpc GetAccountDetails(account.AccountId) returns (account.AccountDetails) {}

    // Returns the latest state proof of the specified account.
    rpc GetAccountProof(AccountProofRequest) returns (AccountProofResponse) {}

    // 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(shared.BlockHeaderByNumberRequest) returns (shared.BlockHeaderByNumberResponse) {}

    // Returns a list of committed 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 (shared.MaybeNoteScript) {}

    // 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 info which can be used by the requester to sync up to the tip of chain for the notes they are interested in.
    //
    // requester 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 requester to sync up to the latest state of the chain
    // for the objects (accounts, notes, nullifiers) the requester 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. requester is expected to repeat these requests
    // in a loop until `response.block_header.block_num == response.chain_tip`, at which point
    // the requester is fully synchronized with the chain.
    //
    // Each request also returns info about new notes, nullifiers 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 and nullifiers filters contain only high
    // part of hashes. Thus, returned data contains excessive notes and nullifiers, requester can make
    // additional filtering of that data on its side.
    rpc SyncState(SyncStateRequest) returns (SyncStateResponse) {}

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

    // Returns storage map updates for specified account and storage slots within a block range.
    rpc SyncStorageMaps(SyncStorageMapsRequest) returns (SyncStorageMapsResponse) {}

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

// 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 ACCOUNT PROOF
// ================================================================================================

// Returns the latest state proof of the specified account.
message AccountProofRequest {
    // 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 index (`[0..255]`).
            uint32 slot_index = 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_index`.
                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;

    // Block at which we'd like to get this data. If present, must be close to the chain tip.
    // If not present, data from the latest block will be returned.
    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 AccountProofResponse {

    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 AccountProofResponse
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 AccountProofResponse
message AccountStorageDetails {
    message AccountStorageMapDetails {
        // Wrapper for repeated storage map entries
        message MapEntries {
            // Definition of individual storage entries.
            message StorageMapEntry {
                primitives.Digest key = 1;
                primitives.Digest value = 2;
            }

            repeated StorageMapEntry entries = 1;
        }
        // slot index of the storage map
        uint32 slot_index = 1;

        // A flag that is set to `true` if the number of to-be-returned entries in the
        // storage map would exceed a threshold. This indicates to the user that `SyncStorageMaps`
        // endpoint should be used to get all storage map data.
        bool too_many_entries = 2;

        // By default we provide all storage entries.
        MapEntries entries = 3;
    }

    // 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 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 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 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 SyncStorageMapsRequest {
    // 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 SyncStorageMapsResponse {
    // 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;

    // Slot index ([0..255]).
    uint32 slot_index = 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 transaction_records = 2;
}

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

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