syntax = "proto3";
package band.base.oracle.v1;

option go_package = "github.com/bandprotocol/chain/v3/client/grpc/oracle/proof";

import "google/api/annotations.proto";
import "gogoproto/gogo.proto";
import "band/oracle/v1/oracle.proto";

// Service defines the gRPC querier service.
service Service {
  // Proof queries the proof for given request ID
  rpc Proof(ProofRequest) returns (ProofResponse) {
    option (google.api.http).get = "/bandchain/v1/oracle/proof/{request_id}";
  }

  // MultiProof queries multiple proofs for given list of request IDs
  rpc MultiProof(MultiProofRequest) returns (MultiProofResponse) {
    option (google.api.http).get = "/bandchain/v1/oracle/multi_proof";
  }

  // RequestCountProof queries the count proof
  rpc RequestCountProof(RequestCountProofRequest) returns (RequestCountProofResponse) {
    option (google.api.http).get = "/bandchain/v1/oracle/requests_count_proof";
  }
}

// ProofRequest is request type for the Service/Proof RPC method.
message ProofRequest {
  // RequestID is ID of an oracle request
  uint64 request_id = 1;
  // height is block height
  int64 height = 2;
}

// ProofResponse is response type for the Service/Proof RPC method.
message ProofResponse {
  // height is block height
  int64 height = 1;
  // result is the proof
  SingleProofResult result = 2 [(gogoproto.nullable) = false];
}

// MultiProofRequest is request type for the Service/MultiProof RPC method.
message MultiProofRequest {
  // request_ids is the list of request IDs
  repeated uint64 request_ids = 1;
}

// MultiProofResponse is response type for the Service/MultiProof RPC method.
message MultiProofResponse {
  int64            height = 1;
  MultiProofResult result = 2 [(gogoproto.nullable) = false];
}

// RequestCountProofRequest is request type for the Service/RequestCountProof RPC method.
message RequestCountProofRequest {}

// RequestCountProofResponse is response type for the Service/RequestCountProof RPC method.
message RequestCountProofResponse {
  int64            height = 1;
  CountProofResult result = 2 [(gogoproto.nullable) = false];
}

// SingleProofResponse is the data structure for response of single proof
message SingleProofResult {
  SingleProof proof           = 1 [(gogoproto.nullable) = false];
  bytes       evm_proof_bytes = 2 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// MultiProofResult is the data structure for response of multi proof
message MultiProofResult {
  MultiProof proof           = 1 [(gogoproto.nullable) = false];
  bytes      evm_proof_bytes = 2 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// CountProofResult is the data structure for response of count proof
message CountProofResult {
  CountProof proof           = 1 [(gogoproto.nullable) = false];
  bytes      evm_proof_bytes = 2 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// SingleProof contains block height, oracle data proof and block relay proof
message SingleProof {
  uint64          block_height      = 1;
  OracleDataProof oracle_data_proof = 2 [(gogoproto.nullable) = false];
  BlockRelayProof block_relay_proof = 3 [(gogoproto.nullable) = false];
}

// MultiProof contains block height, list of oracle data proof and block relay proof
message MultiProof {
  uint64                   block_height            = 1;
  repeated OracleDataProof oracle_data_multi_proof = 2 [(gogoproto.nullable) = false];
  BlockRelayProof          block_relay_proof       = 3 [(gogoproto.nullable) = false];
}

// CountProof contains block height, count proof and block relay proof
message CountProof {
  uint64             block_height      = 1;
  RequestsCountProof count_proof       = 2 [(gogoproto.nullable) = false];
  BlockRelayProof    block_relay_proof = 3 [(gogoproto.nullable) = false];
}

// OracleDataProof contains result, version and merkle paths
message OracleDataProof {
  band.oracle.v1.Result   result       = 1 [(gogoproto.nullable) = false];
  uint64                  version      = 2;
  repeated IAVLMerklePath merkle_paths = 3 [(gogoproto.nullable) = false];
}

// IAVLMerklePath represents a Merkle step to a leaf data node in an iAVL tree.
message IAVLMerklePath {
  bool   is_data_on_right = 1;
  uint32 subtree_height   = 2;
  uint64 subtree_size     = 3;
  uint64 subtree_version  = 4;
  bytes  sibling_hash     = 5 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// BlockRelayProof contains multi store proof, block header merkle parts, common encoded part and signatures
message BlockRelayProof {
  MultiStoreProof        multi_store_proof         = 1 [(gogoproto.nullable) = false];
  BlockHeaderMerkleParts block_header_merkle_parts = 2 [(gogoproto.nullable) = false];
  CommonEncodedVotePart  common_encoded_vote_part  = 3 [(gogoproto.nullable) = false];
  repeated TMSignature   signatures                = 4 [(gogoproto.nullable) = false];
}

// MultiStoreProof stores a compact of other Cosmos-SDK modules' storage hash in multistore to
// compute (in combination with oracle store hash) Tendermint's application state hash at a given block.
//
//                                   __________________________[AppHash]__________________________
//                                  /                                                             \
//                    ___________[N24]____________                                    ___________[N25]________
//                   /                            \                                  /                        \                        
//            _____[N20]_____                 ____[N21]______                   ____[N22]______              _[N23]_
//           /               \               /               \                 /               \            /       \
//       _[N12]_          _[N13]_          _[N14]_          _[N15]_          _[N16]_          _[N17]_     [N18]     [Q]
//      /       \        /       \        /       \        /       \        /       \        /       \    /   \
//    [N0]     [N1]    [N2]     [N3]    [N4]     [N5]    [N6]     [N7]    [N8]     [N9]   [N10]    [N11] [O] [P]
//   /   \    /   \    /  \    /   \   /   \    /   \   /   \    /   \   /   \    /   \   /   \    /   \
//  [0] [1]  [2] [3] [4] [5]  [6] [7] [8] [9]  [A] [B] [C] [D]  [E] [F] [G] [H]  [I] [J] [K] [L]  [M] [N]
//
// [0] - acc (auth) [1] - authz         [2] - bandtss       [3] - bank        [4] - capability [5] - consensus
// [6] - crisis     [7] - distribution  [8] - evidence      [9] - feeds       [A] - feegrant   [B] - feeibc
// [C] - globalfee  [D] - gov           [E] - ibc           [F] - icahost     [G] - mint       [H] - oracle
// [I] - params     [J] - restake       [K] - rollingseed   [L] - slashing    [M] - staking    [N] - transfer
// [O] - tss        [P] - tunnel        [Q] - upgrade
//
// Notice that NOT all leaves of the Merkle tree are needed in order to compute the Merkle
// root hash, since we only want to validate the correctness of [H] In fact, only
// [G], [N9], [N17], [N23], and [N24] are needed in order to compute [AppHash].
message MultiStoreProof {
  bytes oracle_iavl_state_hash = 1 [
    (gogoproto.customname) = "OracleIAVLStateHash",
    (gogoproto.casttype)   = "github.com/cometbft/cometbft/libs/bytes.HexBytes"
  ];
  bytes mint_store_merkle_hash = 2 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes params_to_restake_stores_merkle_hash = 3
      [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes rollingseed_to_transfer_stores_merkle_hash = 4
      [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes tss_to_upgrade_stores_merkle_hash = 5 [
    (gogoproto.customname) = "TSSToUpgradeStoresMerkleHash",
    (gogoproto.casttype)   = "github.com/cometbft/cometbft/libs/bytes.HexBytes"
  ];
  bytes auth_to_icahost_stores_merkle_hash = 6
      [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// BlockHeaderMerkleParts stores a group of hashes using for computing Tendermint's block
// header hash from app hash, and height.
//
// In Tendermint, a block header hash is the Merkle hash of a binary tree with 14 leaf nodes.
// Each node encodes a data piece of the blockchain. The notable data leaves are: [A] app_hash,
// [2] height. All data pieces are combined into one 32-byte hash to be signed
// by block validators. The structure of the Merkle tree is shown below.
//
//                                   [BlockHeader]
//                                /                \
//                   [3A]                                    [3B]
//                 /      \                                /      \
//         [2A]                [2B]                [2C]                [2D]
//        /    \              /    \              /    \              /    \
//    [1A]      [1B]      [1C]      [1D]      [1E]      [1F]        [C]    [D]
//    /  \      /  \      /  \      /  \      /  \      /  \
//  [0]  [1]  [2]  [3]  [4]  [5]  [6]  [7]  [8]  [9]  [A]  [B]
//
//  [0] - version               [1] - chain_id            [2] - height        [3] - time
//  [4] - last_block_id         [5] - last_commit_hash    [6] - data_hash     [7] - validators_hash
//  [8] - next_validators_hash  [9] - consensus_hash      [A] - app_hash      [B] - last_results_hash
//  [C] - evidence_hash         [D] - proposer_address
//
// Notice that NOT all leaves of the Merkle tree are needed in order to compute the Merkle
// root hash, since we only want to validate the correctness of [2], [3], and [A]. In fact, only
// [1A], [2B], [1E], [B], and [2D] are needed in order to compute [BlockHeader].
message BlockHeaderMerkleParts {
  bytes  version_and_chain_id_hash = 1 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  uint64 height                    = 2;
  uint64 time_second               = 3;
  uint32 time_nano_second          = 4;
  bytes  last_block_id_and_other   = 5 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes  next_validator_hash_and_consensus_hash = 6
      [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes last_results_hash          = 7 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes evidence_and_proposer_hash = 8 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// CommonEncodedVotePart represents the common part of encoded vote
message CommonEncodedVotePart {
  bytes signed_data_prefix = 1 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes signed_data_suffix = 2 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// TMSignature contains all details of validator signature for performing signer recovery for ECDSA
// secp256k1 signature. Note that this struct is written specifically for signature signed on
// Tendermint's precommit data, which includes the block hash and some additional information prepended
// and appended to the block hash. The prepended part (prefix) and the appended part (suffix) are
// different for each signer (including signature size, machine clock, validator index, etc).
message TMSignature {
  bytes  r                 = 1 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  bytes  s                 = 2 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
  uint32 v                 = 3;
  bytes  encoded_timestamp = 4 [(gogoproto.casttype) = "github.com/cometbft/cometbft/libs/bytes.HexBytes"];
}

// RequestsCountProof contains count, version and merkle paths
message RequestsCountProof {
  uint64                  count        = 1;
  uint64                  version      = 2;
  repeated IAVLMerklePath merkle_paths = 3 [(gogoproto.nullable) = false];
}
