// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.

syntax = "proto3";
option go_package = "storj.io/common/pb";

package orders;

import "gogo.proto";
import "google/protobuf/timestamp.proto";
import "node.proto";

// PieceAction is an enumeration of all possible executed actions on storage node.
enum PieceAction {
    INVALID = 0;
    PUT = 1;
    GET = 2;
    GET_AUDIT = 3;
    GET_REPAIR = 4;
    PUT_REPAIR = 5;
    DELETE = 6;
    PUT_GRACEFUL_EXIT = 7;
}

// PieceHashAlgorithm defines how the hashes of the pieces are calculated.
enum PieceHashAlgorithm {
    SHA256 = 0;
    BLAKE3 = 1;
}

// OrderLimit is provided by satellite to execute specific action on storage node within some limits.
message OrderLimit {
    // unique serial to avoid replay attacks
    bytes serial_number = 1 [(gogoproto.customtype) = "SerialNumber", (gogoproto.nullable) = false];
    // satellite who issued this order limit allowing orderer to do the specified action
    bytes satellite_id = 2 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
    // uplink who requested or whom behalf the order limit to do an action
    bytes deprecated_uplink_id = 3 [(gogoproto.customtype) = "NodeID"];
    // public key that will be used to sign orders and piece hash
    bytes uplink_public_key = 13 [(gogoproto.customtype) = "PiecePublicKey", (gogoproto.nullable) = false];
    // storage node who can re claimthe order limit specified by serial
    bytes storage_node_id = 4 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];

    // piece which is allowed to be touched
    bytes piece_id = 5 [(gogoproto.customtype) = "PieceID", (gogoproto.nullable) = false];
    // limit in bytes how much can be changed
    int64  limit = 6;
    PieceAction action = 7;

    google.protobuf.Timestamp piece_expiration = 8 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
    google.protobuf.Timestamp order_expiration = 9 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
    google.protobuf.Timestamp order_creation = 12 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];

    bytes encrypted_metadata_key_id = 14;
    bytes encrypted_metadata = 15; // This refers to satellite/internalpb.OrderLimitMetadata

    bytes satellite_signature = 10;

    // this allows a storage node to find a satellite and handshake with it to get its key.
    node.NodeAddress deprecated_satellite_address = 11;
}

// OrderLimitSigning provides OrderLimit signing serialization.
//
// It is never used for sending across the network, it is
// used in signing to ensure that nullable=false fields get handled properly.
// Its purpose is to solidify the format of how we serialize for
// signing, to handle some backwards compatibility considerations.
message OrderLimitSigning {
    // unique serial to avoid replay attacks
    bytes serial_number = 1 [(gogoproto.customtype) = "SerialNumber", (gogoproto.nullable) = false];
    // satellite who issued this order limit allowing orderer to do the specified action
    bytes satellite_id = 2 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];
    // uplink who requested or whom behalf the order limit to do an action
    bytes deprecated_uplink_id = 3 [(gogoproto.customtype) = "NodeID"];
    // public key that will be used to sign orders and piece hash
    bytes uplink_public_key = 13 [(gogoproto.customtype) = "PiecePublicKey"];
    // storage node who can re claimthe order limit specified by serial
    bytes storage_node_id = 4 [(gogoproto.customtype) = "NodeID", (gogoproto.nullable) = false];

    // piece which is allowed to be touched
    bytes piece_id = 5 [(gogoproto.customtype) = "PieceID", (gogoproto.nullable) = false];
    // limit in bytes how much can be changed
    int64  limit = 6;
    PieceAction action = 7;

    google.protobuf.Timestamp piece_expiration = 8 [(gogoproto.stdtime) = true];
    google.protobuf.Timestamp order_expiration = 9 [(gogoproto.stdtime) = true];
    google.protobuf.Timestamp order_creation = 12 [(gogoproto.stdtime) = true];

    bytes encrypted_metadata_key_id = 14;
    bytes encrypted_metadata = 15;

    bytes satellite_signature = 10;

    // this allows a storage node to find a satellite and handshake with it to get its key.
    node.NodeAddress deprecated_satellite_address = 11;
}

// Order is a one step of fullfilling Amount number of bytes from an OrderLimit with SerialNumber.
message Order {
    // serial of the order limit that was signed
    bytes serial_number = 1 [(gogoproto.customtype) = "SerialNumber", (gogoproto.nullable) = false];
    // amount to be signed for
    int64 amount = 2;
    // signature
    bytes uplink_signature = 3;
}

// OrderSigning provides Order signing format.
//
// It is never used for sending across the network, it is
// used in signing to ensure that nullable=false fields get handled properly.
// Its purpose is to solidify the format of how we serialize for
// signing, to handle some backwards compatibility considerations.
message OrderSigning {
    // serial of the order limit that was signed
    bytes serial_number = 1 [(gogoproto.customtype) = "SerialNumber", (gogoproto.nullable) = false];
    // amount to be signed for
    int64 amount = 2;
    // signature
    bytes uplink_signature = 3;
}

message PieceHash {
    // piece id
    bytes piece_id = 1 [(gogoproto.customtype) = "PieceID", (gogoproto.nullable) = false];
    // hash of the piece that was/is uploaded
    bytes hash = 2;
    // size of uploaded piece
    int64 piece_size = 4;
    // timestamp when upload occurred
    google.protobuf.Timestamp timestamp = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
    // signature either satellite or storage node
    bytes signature = 3;
    // hash algorithm used
    PieceHashAlgorithm hash_algorithm = 6;
}

// PieceHashSigning provides piece hash signing format.
//
// It is never used for sending across the network, it is
// used in signing to ensure that nullable=false fields get handled properly.
// Its purpose is to solidify the format of how we serialize for
// signing, to handle some backwards compatibility considerations.
message PieceHashSigning {
    // piece id
    bytes piece_id = 1 [(gogoproto.customtype) = "PieceID", (gogoproto.nullable) = false];
    // hash of the piece that was/is uploaded
    bytes hash = 2;
    // size of uploaded piece
    int64 piece_size = 4;
    // timestamp when upload occurred
    google.protobuf.Timestamp timestamp = 5 [(gogoproto.stdtime) = true];
    // signature either satellite or storage node
    bytes signature = 3;
    // hash algorithm used
    PieceHashAlgorithm hash_algorithm = 6;
}

service Orders {
    rpc SettlementWithWindow(stream SettlementRequest) returns (SettlementWithWindowResponse) {}
}

// Expected order of messages from storagenode:
//   go repeated
//      SettlementRequest -> (async)
//   go repeated
//      <- SettlementResponse
message SettlementRequest {
    OrderLimit limit = 1;
    Order      order = 2;
}

// SettlementWithWindowResponse returns the status and the amounts
// settled summed by piece action.
// Accepted status means that the orders were successfully processed
// (or they were previously processed)
// Rejected status means that the orders were previously processed and
// the summed settled amounts don't match
message SettlementWithWindowResponse {
    enum Status {
        ACCEPTED = 0;
        REJECTED = 1;
    }
    Status status = 1;

    map<int32, int64> action_settled = 2;
}
