/*
 * Electron Cash - a lightweight Bitcoin Cash client
 * CashFusion - an advanced coin anonymizer
 *
 * Copyright (C) 2020 Mark B. Lundeberg
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
syntax = "proto2";

package fusion;

// Some primitives

message InputComponent {
    required bytes prev_txid = 1; // in 'reverse' order, just like in tx
    required uint32 prev_index = 2;
    required bytes pubkey = 3;
    required uint64 amount = 4;
    }

message OutputComponent {
    required bytes scriptpubkey = 1;
    required uint64 amount = 2;
    }

message BlankComponent {
    }

message Component {
    required bytes salt_commitment = 1; // 32 bytes
    oneof component {
        InputComponent input = 2;
        OutputComponent output = 3;
        BlankComponent blank = 4;
        }
    }

message InitialCommitment {
    required bytes salted_component_hash = 1; // 32 byte hash
    required bytes amount_commitment = 2; // uncompressed point
    required bytes communication_key = 3; // compressed point
    }

message Proof {
    // During blame phase, messages of this form are encrypted and sent
    // to a different player. It is already known which commitment this
    // should apply to, so we only need to point at the component.
    required fixed32 component_idx = 1;
    required bytes salt = 2; // 32 bytes
    required bytes pedersen_nonce = 3; // 32 bytes
}



// Primary communication message types (and flow)

// Setup phase

message ClientHello { // from client
    required bytes version = 1;
    optional bytes genesis_hash = 2; // 32 byte hash (bitcoind little-endian memory order)
}

message ServerHello { // from server
    repeated uint64 tiers = 1;
    required uint32 num_components = 2;
    required uint64 component_feerate = 4; // sats/kB
    required uint64 min_excess_fee = 5; // sats
    required uint64 max_excess_fee = 6; // sats

    optional string donation_address = 15; // BCH Address "bitcoincash:qpx..."
}

message JoinPools { // from client
    message PoolTag {
        // These tags can be used to client to stop the server from including
        // the client too many times in the same fusion. Thus, the client can
        // connect many times without fear of fusing with themselves.
        required bytes id = 1; // allowed up to 20 bytes
        required uint32 limit = 2; // between 1 and 5 inclusive
        optional bool no_ip = 3; // whether to do an IP-less tag -- this will collide with all other users, make sure it's random so you can't get DoSed.
    }
    repeated uint64 tiers = 1;
    repeated PoolTag tags = 2; // at most five tags.
}

message TierStatusUpdate { // from server
    message TierStatus {
        // in future, we will want server to indicate 'remaining time' and mask number of players.
        // note: if player is in queue then a status will be ommitted.
        optional uint32 players = 1;
        optional uint32 min_players = 2; // minimum required to start (may have delay to allow extra)
        optional uint32 max_players = 3; // maximum allowed (immediate start)
        optional uint32 time_remaining = 4;
    }
    map<uint64, TierStatus> statuses = 1;
}

message FusionBegin { // from server
    required uint64 tier = 1;
    required bytes covert_domain = 2;
    required uint32 covert_port = 3;
    optional bool covert_ssl = 4;
    required fixed64 server_time = 5; // server unix time when sending this message; can't be too far off from recipient's clock.
}


// Fusion round (repeatable multiple times per connection)

message StartRound { // from server
    required bytes round_pubkey = 1;
    repeated bytes blind_nonce_points = 2;
    required fixed64 server_time = 5; // server unix time when sending this message; can't be too far off from recipient's clock.
    }

// Phase 3
message PlayerCommit { // from client
    repeated bytes initial_commitments = 1; // serialized InitialCommitment messages; server will repeat them later, verbatim.
    required uint64 excess_fee = 2;
    required bytes pedersen_total_nonce = 3; // 32 bytes
    required bytes random_number_commitment = 4; // 32 bytes
    repeated bytes blind_sig_requests = 5; // 32 byte scalars
    }

// Phase 4
message BlindSigResponses { // from server
    repeated bytes scalars = 1; // 32 byte scalars
}

message AllCommitments {
    // All the commitments from all players. At ~140 bytes per commitment and hundreds of commitments, this can be quite large, so it gets sent in its own message during the covert phase.
    repeated bytes initial_commitments = 1;
    }

//Phase 5
message CovertComponent { // from covert client
    // The round key is used to identify the pool if needed
    optional bytes round_pubkey = 1;
    required bytes signature = 2;
    required bytes component = 3; // bytes so that it can be signed and hashed verbatim
    }

//Phase 6
message ShareCovertComponents { // from server
    // This is a large message! 168 bytes per initial commitment, ~112 bytes per input component.
    // Can easily reach 100 kB or more.
    repeated bytes components = 4;
    optional bool skip_signatures = 5; // if the server already sees a problem in submitted components
    optional bytes session_hash = 6; // the server's calculation of session hash, so clients can crosscheck.
}

// Phase 7A
message CovertTransactionSignature { // from covert client
    // The round key is used to identify the pool if needed
    optional bytes round_pubkey = 1;
    required uint32 which_input = 2;
    required bytes txsignature = 3;
    }

// Phase 8
message FusionResult { // from server
    required bool ok = 1;
    repeated bytes txsignatures = 2; // if ok
    repeated uint32 bad_components = 3; // if not ok
    }

// Phase 9
message MyProofsList { // from client
    repeated bytes encrypted_proofs = 1;
    required bytes random_number = 2;  // the number we committed to, back in phase 3
    }

message TheirProofsList { // from server
    message RelayedProof {
        required bytes encrypted_proof = 1;
        required uint32 src_commitment_idx = 2; // which of the commitments is being proven (index in full list)
        required uint32 dst_key_idx = 3;  // which of the recipient's keys will unlock the encryption (index in player list)
        }
    repeated RelayedProof proofs = 1;
    }

// Phase 10
message Blames { // from client
    message BlameProof {
        required uint32 which_proof = 1;
        oneof decrypter {
            bytes session_key = 2; // 32 byte, preferred if the proof decryption works at all
            bytes privkey = 3; // 32 byte scalar
            }

        // Some errors can only be discovered by checking the blockchain,
        // Namely, if an input UTXO is missing/spent/unconfirmed/different
        // scriptpubkey/different amount, than indicated.
        optional bool need_lookup_blockchain = 4;

        // The client can indicate why it thinks the blame is deserved. In
        // case the server finds no issue, this string might help for debugging.
        optional string blame_reason = 5;
        }
    repeated BlameProof blames = 1;
    }

// Final message of the round
message RestartRound {
}

// Fatal error from server, likely we did something wrong (it will disconnect us, but the message may help debugging).
message Error {
    optional string message = 1;
}

// Simple ping, as a keepalive.
message Ping {
}

// Simple acknowledgement, nothing more to say.
message OK {
}

// Primary communication channel types

message ClientMessage {
    oneof msg {
        ClientHello clienthello = 1;
        JoinPools joinpools = 2;
        PlayerCommit playercommit = 3;
        MyProofsList myproofslist = 5;
        Blames blames = 6;
        }
    }

message ServerMessage {
    oneof msg {
        ServerHello serverhello = 1;
        TierStatusUpdate tierstatusupdate = 2;
        FusionBegin fusionbegin = 3;
        StartRound startround = 4;
        BlindSigResponses blindsigresponses = 5;
        AllCommitments allcommitments = 6;
        ShareCovertComponents sharecovertcomponents = 7;
        FusionResult fusionresult = 8;
        TheirProofsList theirproofslist = 9;

        RestartRound restartround = 14;
        Error error = 15;
        }
    }

message CovertMessage { // client -> server, covertly
    oneof msg {
        CovertComponent component = 1;
        CovertTransactionSignature signature = 2;
        Ping ping = 3;
        }
    }

message CovertResponse { // server -> a covert client
    oneof msg {
        OK ok = 1;
        Error error = 15;
    }
}
