// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.

syntax = "proto3";
import "google/api/annotations.proto";

package qrl;

////////////////////////////
////////////////////////////
////////////////////////////
//////     API       ///////
////////////////////////////
////////////////////////////
////////////////////////////

// This service describes the Public API used by clients (wallet/cli/etc)
service PublicAPI
{
    rpc GetNodeState (GetNodeStateReq) returns (GetNodeStateResp) {
      option (google.api.http) = {
        get: "/node-state"
      };
    };

    rpc GetKnownPeers (GetKnownPeersReq) returns (GetKnownPeersResp) {
      option (google.api.http) = {
        get: "/known-peers"
      };
    };

    rpc GetPeersStat (GetPeersStatReq) returns (GetPeersStatResp) {
      option (google.api.http) = {
        get: "/peers-stat"
      };
    };

    rpc GetStats (GetStatsReq) returns (GetStatsResp) {
      option (google.api.http) = {
        get: "/stats"
      };
    };

    rpc GetAddressState (GetAddressStateReq) returns (GetAddressStateResp) {
      option (google.api.http) = {
        get: "/address-state"
      };
    };

    rpc GetOptimizedAddressState (GetAddressStateReq) returns (GetOptimizedAddressStateResp) {
      option (google.api.http) = {
        get: "/optimized-address-state"
      };
    };

    rpc GetMultiSigAddressState (GetMultiSigAddressStateReq) returns (GetMultiSigAddressStateResp) {
      option (google.api.http) = {
        get: "/multi-sig-address-state"
      };
    };

    rpc IsSlave (IsSlaveReq) returns (IsSlaveResp) {
      option (google.api.http) = {
        get: "/is-slave"
      };
    };

    rpc GetObject(GetObjectReq) returns (GetObjectResp) {
      option (google.api.http) = {
        get: "/object"
      };
    };

    rpc GetLatestData(GetLatestDataReq) returns (GetLatestDataResp) {
      option (google.api.http) = {
        get: "/latest-data"
      };
    };

    rpc PushTransaction (PushTransactionReq) returns (PushTransactionResp) {
      option (google.api.http) = {
        post: "/push-transaction"
      };
    };

    rpc TransferCoins (TransferCoinsReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/transfer-coins"
      };
    };

    rpc ParseAddress (ParseAddressReq) returns (ParseAddressResp) {
      option (google.api.http) = {
        get: "/parse-address"
      };
    };

    rpc GetChainStats (GetChainStatsReq) returns (GetChainStatsResp) {
      option (google.api.http) = {
        get: "/chain-stats"
      };
    };

    rpc GetStakingStats (GetStakingStatsReq) returns (GetStakingStatsResp) {
      option (google.api.http) = {
        get: "/staking-stats"
      };
    };

    rpc GetStakerList (GetStakerListReq) returns (GetStakerListResp) {
      option (google.api.http) = {
        get: "/staker-list"
      };
    };

    rpc GetAddressFromPK (GetAddressFromPKReq) returns (GetAddressFromPKResp) {
      option (google.api.http) = {
        get: "/address-from-pk"
      };
    };

    rpc GetMultiSigCreateTxn (MultiSigCreateTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/multi-sig-create-txn"
      };
    };

    rpc GetMultiSigSpendTxn (MultiSigSpendTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/multi-sig-spend-txn"
      };
    };

    rpc GetMultiSigVoteTxn (MultiSigVoteTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/multi-sig-vote-txn"
      };
    };

    rpc GetMessageTxn (MessageTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/message-txn"
      };
    };

    rpc GetTokenTxn (TokenTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/token-txn"
      };
    };

    rpc GetTransferTokenTxn (TransferTokenTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/transfer-token-txn"
      };
    };

    rpc GetSlaveTxn (SlaveTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/slave-txn"
      };
    };

    rpc GetLatticeTxn (LatticeTxnReq) returns (TransferCoinsResp) {
      option (google.api.http) = {
        post: "/lattice-txn"
      };
    };

    rpc GetTransaction(GetTransactionReq) returns (GetTransactionResp) {
      option (google.api.http) = {
        get: "/transaction"
      };
    };

    rpc GetMiniTransactionsByAddress(GetMiniTransactionsByAddressReq) returns (GetMiniTransactionsByAddressResp) {
      option (google.api.http) = {
        get: "/mini-transaction-by-address"
      };
    };

    rpc GetTransactionsByAddress(GetTransactionsByAddressReq) returns (GetTransactionsByAddressResp) {
      option (google.api.http) = {
        get: "/transactions-by-address"
      };
    };

    rpc GetTokensByAddress(GetTransactionsByAddressReq) returns (GetTokensByAddressResp) {
      option (google.api.http) = {
        get: "/tokens-by-address"
      };
    };

    rpc GetSlavesByAddress(GetTransactionsByAddressReq) returns (GetSlavesByAddressResp) {
      option (google.api.http) = {
        get: "/slaves-by-address"
      };
    };

    rpc GetLatticePKsByAddress(GetTransactionsByAddressReq) returns (GetLatticePKsByAddressResp) {
      option (google.api.http) = {
        get: "/lattice-pks-by-address"
      };
    };

    rpc GetMultiSigAddressesByAddress(GetTransactionsByAddressReq) returns (GetMultiSigAddressesByAddressResp) {
      option (google.api.http) = {
        get: "/multi-sig-addresses-by-address"
      };
    };

    rpc GetMultiSigSpendTxsByAddress(GetMultiSigSpendTxsByAddressReq) returns (GetMultiSigSpendTxsByAddressResp) {
      option (google.api.http) = {
        get: "/multi-sig-spend-txs-by-address"
      };
    };

    rpc GetVoteStats(GetVoteStatsReq) returns (GetVoteStatsResp) {
      option (google.api.http) = {
        get: "/vote-stats"
      };
    };

    rpc GetInboxMessagesByAddress(GetTransactionsByAddressReq) returns (GetInboxMessagesByAddressResp) {
      option (google.api.http) = {
        get: "/inbox-messages-by-address"
      };
    };

    rpc GetBalance(GetBalanceReq) returns (GetBalanceResp) {
      option (google.api.http) = {
        get: "/balance"
      };
    };

    rpc GetTotalBalance(GetTotalBalanceReq) returns (GetTotalBalanceResp) {
      option (google.api.http) = {
        get: "/total-balance"
      };
    };

    rpc GetOTS(GetOTSReq) returns (GetOTSResp) {
      option (google.api.http) = {
        get: "/ots"
      };
    };

    rpc GetHeight(GetHeightReq) returns (GetHeightResp) {
      option (google.api.http) = {
        get: "/height"
      };
    };

    rpc GetBlock(GetBlockReq) returns (GetBlockResp) {
      option (google.api.http) = {
        get: "/block"
      };
    };

    rpc GetBlockByNumber(GetBlockByNumberReq) returns (GetBlockByNumberResp) {
      option (google.api.http) = {
        get: "/block-by-number"
      };
    };
}

// This is a place holder for testing/instrumentation APIs
service AdminAPI
{
}

////////////////////////////
////////////////////////////
////////////////////////////
//    Request/Response    //
////////////////////////////
////////////////////////////
////////////////////////////

/**
 * Empty message definition
*/
message Empty       { }

/**
 * Represents a query to get node state
*/
message GetNodeStateReq { }

/**
 * Represents the reply message to node state query
*/
message GetNodeStateResp {
    NodeInfo info = 1;
}

/**
 * Represents a query to get known peers
*/
message GetKnownPeersReq { }

/**
 * Represents the reply message to known peers query
*/
message GetKnownPeersResp {
    NodeInfo node_info = 1;             // NodeInfo object containing node state information
    repeated Peer known_peers = 2;      // List of Peer objects containing peer nodes detailed information
}

/**
 * Represents a query to get connected peers stat
*/
message GetPeersStatReq { }

/**
 * Represents the reply message to peers stat query
*/
message GetPeersStatResp {
    repeated PeerStat peers_stat = 1;    // PeerState object contains peer_ip, port and peer state information
}

/**
 * Represents the query for get chain size
*/

message GetChainStatsReq { }

/**
 * Represents the reply message for get chain stats
*/

message GetChainStatsResp {
    uint64 state_size = 1;                    // whole state folder size in bytes
    string state_size_mb = 2;                 // megabytes
    string state_size_gb = 3;                 // gigabytes
}

/**
 * Represents the query for staking statistics
*/
message GetStakingStatsReq { }

/**
 * Represents the reply message for staking statistics
*/
message GetStakingStatsResp {
    uint32 total_stakers = 1;                 // Total number of stakers
    uint32 active_stakers = 2;                // Number of active stakers
    uint64 total_staked_amount = 3;           // Total staked amount in quarks
    bool staking_enabled = 4;                 // Whether staking is enabled
    bool donor_wallet_loaded = 5;             // Whether donor wallet is loaded
    uint64 min_stake_amount = 6;              // Minimum stake amount in quarks
    uint32 reward_interval = 7;               // Reward interval in blocks
    uint64 reward_amount = 8;                 // Reward amount per distribution in quarks
}

/**
 * Represents the query for staker list
*/
message GetStakerListReq { }

/**
 * Represents information about a single staker
*/
message StakerInfo {
    bytes address = 1;                        // Staker address in bytes
    uint64 balance = 2;                       // Current balance in quarks
    bool is_active = 3;                       // Whether the staker is active
    uint64 nonce = 4;                         // Current nonce
}

/**
 * Represents the reply message for staker list
*/
message GetStakerListResp {
    repeated StakerInfo stakers = 1;          // List of stakers
    uint32 total_count = 2;                   // Total number of stakers
    uint64 total_staked = 3;                  // Total staked amount in quarks
}

/**
 * Represents a query to get statistics about node
*/
message GetStatsReq {
    bool include_timeseries = 1;            // Boolean to define if block timeseries should be included in reply or not
}

/**
 * Represents the reply message to get statistics about node
*/
message GetStatsResp {
    NodeInfo node_info = 1;                 // NodeInfo object containing node state information
    uint64 epoch = 2;                       // Current epoch
    uint64 uptime_network = 3;              // Indicates uptime in seconds

    uint64 block_last_reward = 4;           // Block reward
    uint64 block_time_mean = 5;             // Blocktime average
    uint64 block_time_sd = 6;               // Blocktime standrad deviation

    uint64 coins_total_supply = 7;          // Total coins supply
    uint64 coins_emitted = 8;               // Total coins emitted

    repeated BlockDataPoint block_timeseries = 9;
}

message GetAddressFromPKReq {
    bytes pk = 1;
}

message GetAddressFromPKResp {
    bytes address = 1;
}

/**
 * BlockDataPoint message definition
*/
message BlockDataPoint
{
    uint64 number = 1;                      // Block number
    string difficulty = 2;                  // Block difficulty
    uint64 timestamp = 3;                   // Block timestamp
    uint64 time_last = 4;
    uint64 time_movavg = 5;
    float hash_power = 6;                   // Hash power

    bytes header_hash = 7;                  // Block header hash
    bytes header_hash_prev = 8;             // Previous block's header hash
}

message GetAddressStateReq {
    bytes address = 1;
    bool exclude_ots_bitfield = 2;
    bool exclude_transaction_hashes = 3; 
}

message GetAddressStateResp {
    AddressState state = 1;
}

message GetOptimizedAddressStateResp {
    OptimizedAddressState state = 1;
}

message GetMultiSigAddressStateReq {
    bytes address = 1;
}

message GetMultiSigAddressStateResp {
    MultiSigAddressState state = 1;
}

message IsSlaveReq {
    bytes master_address = 1;
    bytes slave_pk = 2;
}

message IsSlaveResp {
    bool result = 1;
}

message ParseAddressReq { 
    bytes address = 1;
}

message ParseAddressResp {
    bool is_valid = 1;
    AddressDescriptor desc = 2;
}


message GetObjectReq {  bytes query = 1;    }
message GetObjectResp {
    bool found = 1;
    oneof result {
        OptimizedAddressState address_state = 2;
        TransactionExtended transaction = 3;
        BlockExtended block_extended = 4;
    }
}

message GetLatestDataReq {
    enum Filter {
        ALL = 0;
        BLOCKHEADERS = 1;
        TRANSACTIONS = 2;
        TRANSACTIONS_UNCONFIRMED = 3;
    }
    Filter filter = 1;
    uint32 offset = 2;                      // Offset in the result list (works backwards in this case)
    uint32 quantity = 3;                    // Number of items to retrive. Capped at 100
}

message GetLatestDataResp {
    repeated BlockHeaderExtended blockheaders = 1;
    repeated TransactionExtended transactions = 2;
    repeated TransactionExtended transactions_unconfirmed = 3;
}

message TransferCoinsReq {
    bytes master_addr = 1;                 // Transaction source address
    repeated bytes addresses_to = 2;       // Transaction destination address
    repeated uint64 amounts = 3;           // Amount. It should be expressed in Shor
    bytes message_data = 4;                // Message Data. Optional field to send messages
    uint64 fee = 5;                        // Fee. It should be expressed in Shor
    bytes xmss_pk = 6;                     // XMSS Public key
}

message TransferCoinsResp {
    TransactionExtended extended_transaction_unsigned = 1;
}

message PushTransactionReq {    Transaction transaction_signed = 1;     }
message PushTransactionResp {
    enum ResponseCode {
        UNKNOWN = 0;
        ERROR = 1;
        VALIDATION_FAILED = 2;
        SUBMITTED = 3;
    }

    ResponseCode error_code = 1;
    string error_description = 2;
    bytes tx_hash = 3;
}

message MultiSigCreateTxnReq {
    bytes master_addr = 1;

    repeated bytes signatories = 2;
    repeated uint32 weights = 3;
    uint32 threshold = 4;

    uint64 fee = 5;
    bytes xmss_pk = 6;
}

message MultiSigSpendTxnReq {
    bytes master_addr = 1;

    bytes multi_sig_address = 2;
    repeated bytes addrs_to = 3;
    repeated uint64 amounts = 4;
    uint64 expiry_block_number = 5;

    uint64 fee = 6;
    bytes xmss_pk = 7;
}

message MultiSigVoteTxnReq {
    bytes master_addr = 1;

    bytes shared_key = 2;
    bool unvote = 3;

    uint64 fee = 4;
    bytes xmss_pk = 5;
}

message MessageTxnReq {
    bytes master_addr = 1;
    bytes message = 2;
    bytes addr_to = 3;
    uint64 fee = 4;
    bytes xmss_pk = 5;
}

message TokenTxnReq {
    bytes master_addr = 1;
    bytes symbol = 2;
    bytes name = 3;
    bytes owner = 4;
    uint64 decimals = 5;
    repeated AddressAmount initial_balances = 6;
    uint64 fee = 7;
    bytes xmss_pk = 8;
}

message TransferTokenTxnReq {
    bytes master_addr = 1;
    repeated bytes addresses_to = 2;
    bytes token_txhash = 3;
    repeated uint64 amounts = 4;
    uint64 fee = 5;
    bytes xmss_pk = 6;
}

message SlaveTxnReq {
    bytes master_addr = 1;
    repeated bytes slave_pks = 2;
    repeated uint32 access_types = 3;
    uint64 fee = 4;
    bytes xmss_pk = 5;
}

message LatticeTxnReq {
    bytes master_addr = 1;
    bytes pk1 = 2;
    bytes pk2 = 3;
    bytes pk3 = 4;
    uint64 fee = 5;
    bytes xmss_pk = 6;
}

message MiniTransaction {
    string transaction_hash = 1;
    bool out = 2;
    uint64 amount = 3;
}

message GetTransactionReq {
    bytes tx_hash = 1;
}

message GetTransactionResp {
    Transaction tx = 1;
    uint64 confirmations = 2;
    uint64 block_number = 3;
    bytes block_header_hash = 4;
    uint64 timestamp = 5;
    bytes addr_from = 6;
}

message GetMiniTransactionsByAddressReq {
    bytes address = 1;
    uint64 item_per_page = 2;
    uint64 page_number = 3;
}

message GetMiniTransactionsByAddressResp {
    repeated MiniTransaction mini_transactions = 1;
    uint64 balance = 2;
}

message GetTransactionsByAddressReq {
    bytes address = 1;
    uint64 item_per_page = 2;
    uint64 page_number = 3;
}

message GetTransactionsByAddressResp {
    repeated GetTransactionResp transactions_detail = 1;
}

message GetMultiSigSpendTxsByAddressReq {
    enum FilterType {
        NONE = 0;
        EXECUTED_ONLY = 1;
        NON_EXECUTED = 2;
        EXPIRED = 3;
        NON_EXPIRED = 4;
        NON_EXECUTED_EXPIRED = 5;
        NON_EXECUTED_NON_EXPIRED = 6;
    }
    bytes address = 1;
    uint64 item_per_page = 2;
    uint64 page_number = 3;
    FilterType filter_type = 4;
}

message GetMultiSigSpendTxsByAddressResp {
    repeated GetTransactionResp transactions_detail = 1;
}

message GetVoteStatsReq {
    bytes multi_sig_spend_tx_hash = 1;
}

message GetVoteStatsResp {
    VoteStats vote_stats = 1;
}

message GetInboxMessagesByAddressResp {
    repeated GetTransactionResp transactions_detail = 1;
}

message InboxMessage {
    bytes addr_from = 1;
    uint64 timestamp = 2;
    bytes message = 3;
    bytes tx_hash = 4;
    uint64 block_number = 5;
}

message TokenDetail {
    bytes token_txhash = 1;
    bytes name = 2;
    bytes symbol = 3;
    uint64 balance = 4;
}

message GetTokensByAddressResp {
    repeated TokenDetail tokens_detail = 1;
}

message SlaveDetail {
    bytes slave_address = 1;
    uint64 access_type = 2;
}

message GetSlavesByAddressResp {
    repeated SlaveDetail slaves_detail = 1;
}

message LatticePKsDetail {
    bytes pk1 = 1;
    bytes pk2 = 2;
    bytes pk3 = 3;
    bytes tx_hash = 4;
}

message GetLatticePKsByAddressResp {
    repeated LatticePKsDetail lattice_pks_detail = 1;
}

message MultiSigDetail {
    bytes address = 1;
    uint64 balance = 2;
}

message GetMultiSigAddressesByAddressResp {
    repeated MultiSigDetail multi_sig_detail = 1;
}

message GetBalanceReq {
    bytes address = 1;
}

message GetBalanceResp {
    uint64 balance = 1;
}

message GetTotalBalanceReq {
    repeated bytes addresses = 1;
}

message GetTotalBalanceResp {
    uint64 balance = 1;
}

message GetOTSReq {
    bytes address = 1;
    uint64 page_from = 2;
    uint64 page_count = 3;
    uint64 unused_ots_index_from = 4;
}

message OTSBitfieldByPage {
    repeated bytes ots_bitfield = 1;
    uint64 page_number = 2;
}

message GetOTSResp {
    repeated OTSBitfieldByPage ots_bitfield_by_page = 1;
    uint64 next_unused_ots_index = 2;
    bool unused_ots_index_found = 3;
}

message GetHeightReq {
}

message GetHeightResp {
    uint64 height = 1;
}

message GetBlockReq {
    bytes header_hash = 1;
}

message GetBlockResp {
    Block block = 1;
}

message GetBlockByNumberReq {
    uint64 block_number = 1;
}

message GetBlockByNumberResp {
    Block block = 1;
}

////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////

message GetLocalAddressesReq { }
message GetLocalAddressesResp {
    repeated bytes addresses = 1;
}

////////////////////////////
////////////////////////////
////////////////////////////
//         Content        //
////////////////////////////
////////////////////////////
////////////////////////////

message NodeInfo
{
    enum State {
        UNKNOWN = 0;
        UNSYNCED = 1;
        SYNCING = 2;
        SYNCED = 3;
        FORKED = 4;
    }

    string version = 1;
    State  state = 2;
    uint32 num_connections = 3;
    uint32 num_known_peers = 4;
    uint64 uptime = 5;               // Uptime in seconds
    uint64 block_height = 6;
    bytes  block_last_hash = 7;
    string network_id = 8;
}

message AddressDescriptor {                 // 3 byte scheme, 0-3 bits = hf, 4-7 = sig scheme, 8-11 = params (inc h), 12-15 addr fmt, 16-23 params2
    string hash_function = 1;
    string signature_scheme = 2;
    uint32 tree_height = 3;
    uint32 signatures = 4;
    string address_format = 5;
}

message StoredPeers {
    repeated Peer peers = 1;
}

message Peer {
    string ip = 1;
}

message AddressState {
    bytes address = 1;
    uint64 balance = 2;
    uint64 nonce = 3;                           // FIXME: Discuss. 32 or 64 bits?
    repeated bytes transaction_hashes = 5;
    map<string, uint64> tokens = 6;
    repeated LatticePK latticePK_list = 7;
    map<string, uint32> slave_pks_access_type = 8;
    repeated bytes falcon_pk_list = 10;         // Store list of used Falcon public keys
}

message OptimizedAddressState {
    bytes address = 1;
    uint64 balance = 2;
    uint64 nonce = 3;                           // FIXME: Discuss. 32 or 64 bits?
    uint64 used_falcon_pk_count = 5;  // Keep track of number of Falcon public keys that have been used
    uint64 transaction_hash_count = 6;
    uint64 tokens_count = 7;
    uint64 slaves_count = 8;
    uint64 lattice_pk_count = 9;
    uint64 multi_sig_address_count = 10;
    uint64 multi_sig_spend_count = 11;
    uint64 inbox_message_count = 12;
    uint64 falcon_pk_count = 13;  // Count of Falcon public keys used

    repeated bytes foundation_multi_sig_spend_txn_hash = 17;
    repeated bytes foundation_multi_sig_vote_txn_hash = 18;
    repeated bytes unvotes = 19;

    repeated Transaction proposal_vote_stats = 20;
}

message MultiSigAddressState {
    bytes address = 1;
    bytes creation_tx_hash = 2;
    uint64 nonce = 3;
    uint64 balance = 4;
    repeated bytes signatories = 5;
    repeated uint32 weights = 6;
    uint32 threshold = 7;
    uint64 transaction_hash_count = 8;
    uint64 multi_sig_spend_count = 9;
    // TODO: To be implemented
    uint64 multi_sig_address_count = 10;

    repeated bytes foundation_multi_sig_spend_txn_hash = 11;
    repeated bytes foundation_multi_sig_vote_txn_hash = 12;
    repeated bytes unvotes = 13;

    repeated Transaction proposal_vote_stats = 14;
}

message MultiSigAddressesList {
    repeated bytes hashes = 1;
}

message DataList {
    repeated bytes values = 1;
}

message Bitfield {
    repeated bytes bitfields = 1;
}

message TransactionHashList {
    repeated bytes hashes = 1;
}

message LatticePK {
    bytes kyber_pk = 1;
    bytes dilithium_pk = 2;
}

message AddressAmount {
    bytes address = 1;
    uint64 amount = 2;
}

message BlockHeader {
    // Header
    bytes hash_header = 1;

    uint64 block_number = 2;
    uint64 timestamp_seconds = 3;
    bytes hash_header_prev = 4;
    uint64 reward_block = 5;
    uint64 reward_fee = 6;
    bytes merkle_root = 7;

    uint32 mining_nonce = 8;
    uint64 extra_nonce = 9;
}

message BlockHeaderExtended {
    BlockHeader header = 1;
    TransactionCount transaction_count = 2;
}

message TransactionCount {
    map<uint32, uint32> count = 1;
}

message TransactionExtended {
    BlockHeader header = 1;
    Transaction tx = 2;
    bytes addr_from = 3;
    uint64 size = 4;
    uint64 timestamp_seconds = 5;
}

message BlockExtended {
    BlockHeader header = 1;
    repeated TransactionExtended extended_transactions = 2;

    // This is only applicable to genesis blocks
    repeated GenesisBalance genesis_balance = 3;
    uint64 size = 4;
}

message Block {
    BlockHeader header = 1;
    repeated Transaction transactions = 2;

    // This is only applicable to genesis blocks
    repeated GenesisBalance genesis_balance = 3;
}

message GenesisBalance
{
    bytes address = 1;                     // Address is string only here to increase visibility
    uint64 balance = 2;
}

message BlockMetaDataList {
    repeated BlockMetaData block_number_hashes = 1;
}

message Transaction {
    bytes master_addr = 1;
    uint64 fee = 2;
    bytes public_key = 3;
    bytes signature = 4;
    uint64 nonce = 5;
    bytes transaction_hash = 6;

    oneof transactionType {
        Transfer transfer = 7;
        CoinBase coinbase = 8;
        LatticePublicKey latticePK = 9;
        Message message = 10;
        Token token = 11;
        TransferToken transfer_token = 12;
        Slave slave = 13;

        MultiSigCreate multi_sig_create = 14;
        MultiSigSpend multi_sig_spend = 15;
        MultiSigVote multi_sig_vote = 16;

        ProposalCreate proposal_create = 17;
        ProposalVote proposal_vote = 18;
    }

    //////////
    message Transfer {
        repeated bytes addrs_to = 1;
        repeated uint64 amounts = 2;
        bytes message_data = 3;
    }

    message CoinBase {
        bytes addr_to = 1;
        uint64 amount = 2;
    }

    message LatticePublicKey {
        bytes pk1 = 1;  // kyber_pk
        bytes pk2 = 2;  // dilithium_pk
        bytes pk3 = 3;  // ecdsa_pk
    }

    message Message {
        bytes message_hash = 1;
        bytes addr_to = 2;
    }

    message Token {
        bytes symbol = 1;
        bytes name = 2;
        bytes owner = 3;
        uint64 decimals = 4;
        repeated AddressAmount initial_balances = 5;
    }

    message TransferToken {
        bytes token_txhash = 1;
        repeated bytes addrs_to = 2;
        repeated uint64 amounts = 3;
    }

    message Slave {
        repeated bytes slave_pks = 1;
        repeated uint32 access_types = 2;
    }

    message MultiSigCreate {
        repeated bytes signatories = 1;
        repeated uint32 weights = 2;
        uint32 threshold = 3;
    }

    message MultiSigSpend {
        bytes multi_sig_address = 1;
        repeated bytes addrs_to = 2;
        repeated uint64 amounts = 3;
        uint64 expiry_block_number = 4;
    }

    message MultiSigVote {
        bytes shared_key = 1;
        bool unvote = 2;

        bytes prev_tx_hash = 3;  // To be used internally by State
    }

    message ProposalCreate {
        uint64 expiry_block_number = 1;
        string description = 2;

        oneof proposalType {
            QIP qip = 3;
            Config config = 4;
            Other other = 5;
        }

        message QIP {
            string qip_link = 1;
        }

        message Config {
            repeated bytes changes_bitfield = 1;

            uint64 reorg_limit = 2;
            uint64 max_coin_supply = 3;
            uint64 complete_emission_time_span_in_years = 4;

            uint64 mining_nonce_offset = 5;
            uint64 extra_nonce_offset = 6;
            uint64 mining_blob_size_in_bytes = 7;
            uint64 block_timing_in_seconds = 8;

            uint64 number_of_blocks_analyze = 9;
            uint64 block_size_multiplier = 10;  // Support upto 2 decimal places
            uint64 block_min_size_limit_in_bytes = 11;

            uint64 transaction_multi_output_limit = 12;

            uint64 message_max_length = 13;

            uint64 token_symbol_max_length = 14;
            uint64 token_name_max_length = 15;

            uint64 lattice_pk1_max_length = 16;
            uint64 lattice_pk2_max_length = 17;
            uint64 lattice_pk3_max_length = 18;

            uint64 foundation_multi_sig_address_threshold_percentage = 19;

            uint64 proposal_threshold_per = 20;
            repeated string proposal_default_options = 21;
            uint64 description_max_length = 22;
            uint64 options_max_number = 23;
            uint64 option_max_text_length = 24;
            uint64 proposal_config_activation_delay = 25;

            uint64 N_measurement = 26;
            uint64 kp = 27;
        }

        message Other {
            repeated string options = 1;
        }
    }

    message ProposalVote {
        bytes shared_key = 1;
        uint32 option = 2;
    }
}

message VoteStats {
    bytes multi_sig_address = 1;
    bytes shared_key = 2;
    repeated bytes signatories = 3;
    repeated bytes tx_hashes = 4;
    repeated bool unvotes = 5;
    uint64 expiry_block_number = 6;
    uint64 total_weight = 7;
    bool executed = 8;
}

message ProposalVoteStats {
    bytes addr_from = 1;
    bytes shared_key = 2;
    string proposal_type = 3;
    repeated uint64 weight_by_option = 4;
    uint64 expiry_block_number = 5;
    bool executed = 6;

    uint64 number_of_tx_hashes = 7;  // Keep track of number of pages for vote txn hash
}

message ProposalRecord {
    uint64 number_of_tx_hashes = 1;
}

message TokenList {
    repeated bytes token_txhash = 1;
}

message TokenBalance {
    uint64 balance = 1;
    uint64 decimals = 2;
    bytes tx_hash = 3; // Tx hash responsible for the creation of this data
    bool delete = 4;  // For internal use only
}

message SlaveMetadata {
    uint64 access_type = 1;
    bytes tx_hash = 2;
    bool delete = 3;
}

message LatticePKMetadata {
    bool enabled = 1;
    bytes tx_hash = 2;
    bool delete = 3;
}

message TokenMetadata {
    bytes token_txhash = 1;
    repeated bytes transfer_token_tx_hashes = 2;
}

message EncryptedEphemeralMessage {
    bytes msg_id = 1;                       // b'NEW' or PRF
    uint64 ttl = 2;                         // Expiry Timestamp in seconds
    uint64 ttr = 3;                         // Time to relay
    message Channel {
        bytes enc_aes256_symkey = 4;        // aes256_symkey encrypted by kyber
    }
    Channel channel = 5;
    uint64 nonce = 6;                       // nonce
    bytes payload = 7;                      // JSON content, encrypted by aes256_symkey
}

message AddressList {
    repeated bytes addresses = 1;
}

message BlockHeightData {
    uint64 block_number = 1;
    bytes block_headerhash = 2;
    bytes cumulative_difficulty = 3;
}

message BlockMetaData {
    bytes block_difficulty = 1;
    bytes cumulative_difficulty = 2;
    repeated bytes child_headerhashes = 3;
    repeated bytes last_N_headerhashes = 4;     // Keeps last N headerhashes, for measurement of timestamp difference
}

message BlockNumberMapping {
    bytes headerhash = 1;
    bytes prev_headerhash = 2;
}

message PeerStat {
    bytes peer_ip = 1;
    uint32 port = 2;
    NodeChainState node_chain_state = 3;
}

message NodeChainState {
    uint64 block_number = 1;
    bytes header_hash = 2;
    bytes cumulative_difficulty = 3;
    string version = 4;
    uint64 timestamp = 5;
}

message NodeHeaderHash {
    uint64 block_number = 1;
    repeated bytes headerhashes = 2;
}

message P2PAcknowledgement {
    uint32 bytes_processed = 1;
}

message PeerInfo {
    bytes peer_ip = 1;
    uint32 port = 2;
    uint32 banned_timestamp = 3;
    uint32 credibility = 4;
    repeated uint32 last_connections_timestamp = 5;
}

message Peers {
    repeated PeerInfo peer_info_list = 1;
}

message DevConfig {
    bytes prev_state_key = 1;
    bytes current_state_key = 2;
    bytes activation_header_hash = 3;
    uint64 activation_block_number = 4;

    Chain chain = 5;
    Block block = 6;
    Transaction transaction = 7;
    POW pow = 8;

    message Chain {
        uint64 reorg_limit = 1;
        uint64 max_coin_supply = 2;
        uint64 complete_emission_time_span_in_years = 3;
    }

    message Block {
        uint64 mining_nonce_offset = 1;
        uint64 extra_nonce_offset = 2;
        uint64 mining_blob_size_in_bytes = 3;
        uint64 block_timing_in_seconds = 4;
        BlockSizeController block_size_controller = 5;

        message BlockSizeController {
            uint64 number_of_blocks_analyze = 1;
            uint64 size_multiplier = 2;  // Support upto 2 decimal places
            uint64 block_min_size_limit_in_bytes = 3;
        }
    }

    message Transaction {
        uint64 multi_output_limit = 1;
        Message message = 2;
        Slave slave = 3;
        Token token = 4;
        Lattice lattice = 5;
        FoundationMultiSig foundation_multi_sig = 6;
        Proposal proposal = 7;

        message Message {
            uint64 max_length = 1;
        }

        message Slave {
            uint64 slave_pk_max_length = 2;
        }

        message Token {
            uint64 symbol_max_length = 1;
            uint64 name_max_length = 2;
        }

        message Lattice {
            uint64 pk1_max_length = 1;
            uint64 pk2_max_length = 2;
            uint64 pk3_max_length = 3;
        }

        message FoundationMultiSig {
            uint64 threshold_percentage = 1;  // Support upto 2 decimal places
        }

        message Proposal {
            uint64 threshold_per = 1;  // Support upto 2 decimal places
            repeated string default_options = 2;  // Convention: All strings must be in capital letters

            uint64 description_max_length = 3;
            uint64 options_max_number = 4;
            uint64 option_max_text_length = 5;
            uint64 proposal_config_activation_delay = 6;
        }
    }

    message POW {
        uint64 N_measurement = 1;
        uint64 kp = 2;
    }
}