// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .

syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option java_package = "cash.z.wallet.sdk.internal.rpc";
option go_package = ".;walletrpc";
option swift_prefix = "";
import "service.proto";

message DarksideMetaState {
    int32 saplingActivation = 1;
    string branchID = 2;
    string chainName = 3;
}

// A block is a hex-encoded string.
message DarksideBlock {
    string block = 1;
}

// DarksideBlocksURL is typically something like:
// https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt
message DarksideBlocksURL {
    string url = 1;
}

// DarksideTransactionsURL refers to an HTTP source that contains a list
// of hex-encoded transactions, one per line, that are to be associated
// with the given height (fake-mined into the block at that height)
message DarksideTransactionsURL {
    int64 height = 1;
    string url = 2;
}

message DarksideHeight {
    int64 height = 1;
}

message DarksideEmptyBlocks {
    int64 height = 1;
    int32 nonce = 2;
    int32 count = 3;
}

// Darksidewalletd maintains two staging areas, blocks and transactions. The
// Stage*() gRPCs add items to the staging area; ApplyStaged() "applies" everything
// in the staging area to the working (operational) state that the mock zcashd
// serves; transactions are placed into their corresponding blocks (by height).
service DarksideStreamer {
    // Reset reverts all darksidewalletd state (active block range, latest height,
    // staged blocks and transactions) and lightwalletd state (cache) to empty,
    // the same as the initial state. This occurs synchronously and instantaneously;
    // no reorg happens in lightwalletd. This is good to do before each independent
    // test so that no state leaks from one test to another.
    // Also sets (some of) the values returned by GetLightdInfo(). The Sapling
    // activation height specified here must be where the block range starts.
    rpc Reset(DarksideMetaState) returns (Empty) {}

    // StageBlocksStream accepts a list of blocks and saves them into the blocks
    // staging area until ApplyStaged() is called; there is no immediate effect on
    // the mock zcashd. Blocks are hex-encoded. Order is important, see ApplyStaged.
    rpc StageBlocksStream(stream DarksideBlock) returns (Empty) {}

    // StageBlocks is the same as StageBlocksStream() except the blocks are fetched
    // from the given URL. Blocks are one per line, hex-encoded (not JSON).
    rpc StageBlocks(DarksideBlocksURL) returns (Empty) {}

    // StageBlocksCreate is like the previous two, except it creates 'count'
    // empty blocks at consecutive heights starting at height 'height'. The
    // 'nonce' is part of the header, so it contributes to the block hash; this
    // lets you create identical blocks (same transactions and height), but with
    // different hashes.
    rpc StageBlocksCreate(DarksideEmptyBlocks) returns (Empty) {}

    // StageTransactionsStream stores the given transaction-height pairs in the
    // staging area until ApplyStaged() is called. Note that these transactions
    // are not returned by the production GetTransaction() gRPC until they
    // appear in a "mined" block (contained in the active blockchain presented
    // by the mock zcashd).
    rpc StageTransactionsStream(stream RawTransaction) returns (Empty) {}

    // StageTransactions is the same except the transactions are fetched from
    // the given url. They are all staged into the block at the given height.
    // Staging transactions to different heights requires multiple calls.
    rpc StageTransactions(DarksideTransactionsURL) returns (Empty) {}

    // ApplyStaged iterates the list of blocks that were staged by the
    // StageBlocks*() gRPCs, in the order they were staged, and "merges" each
    // into the active, working blocks list that the mock zcashd is presenting
    // to lightwalletd. Even as each block is applied, the active list can't
    // have gaps; if the active block range is 1000-1006, and the staged block
    // range is 1003-1004, the resulting range is 1000-1004, with 1000-1002
    // unchanged, blocks 1003-1004 from the new range, and 1005-1006 dropped.
    //
    // After merging all blocks, ApplyStaged() appends staged transactions (in
    // the order received) into each one's corresponding (by height) block
    // The staging area is then cleared.
    //
    // The argument specifies the latest block height that mock zcashd reports
    // (i.e. what's returned by GetLatestBlock). Note that ApplyStaged() can
    // also be used to simply advance the latest block height presented by mock
    // zcashd. That is, there doesn't need to be anything in the staging area.
    rpc ApplyStaged(DarksideHeight) returns (Empty) {}

    // Calls to the production gRPC SendTransaction() store the transaction in
    // a separate area (not the staging area); this method returns all transactions
    // in this separate area, which is then cleared. The height returned
    // with each transaction is -1 (invalid) since these transactions haven't
    // been mined yet. The intention is that the transactions returned here can
    // then, for example, be given to StageTransactions() to get them "mined"
    // into a specified block on the next ApplyStaged().
    rpc GetIncomingTransactions(Empty) returns (stream RawTransaction) {}

    // Clear the incoming transaction pool.
    rpc ClearIncomingTransactions(Empty) returns (Empty) {}
}