// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

syntax = "proto3";

package pw.transfer;

option java_multiple_files = true;
option java_package = "dev.pigweed.pw_transfer";

// The transfer RPC service is used to send data between the client and server.
service Transfer {
  // Transfer data from the server to the client; a "download" from the client's
  // perspective.
  rpc Read(stream Chunk) returns (stream Chunk);

  // Transfer data from the client to the server; an "upload" from the client's
  // perspective.
  rpc Write(stream Chunk) returns (stream Chunk);

  // Query the status of a resource. Can be used for partially completed
  // transfers
  rpc GetResourceStatus(ResourceStatusRequest) returns (ResourceStatus);
}

// Represents a chunk of data sent by the transfer service. Includes fields for
// configuring the transfer parameters.
//
// Notation: (Read|Write) (→|←)
//   X → Means client sending data to the server.
//   X ← Means server sending data to the client.
message Chunk {
  // Represents the source or destination of the data. May be ephemeral or
  // stable depending on the implementation. Sent in every request to identify
  // the transfer target.
  //
  // LEGACY FIELD ONLY. Split into resource_id and session_id in transfer v2.
  //
  //  Read → ID of transfer
  //  Read ← ID of transfer
  // Write → ID of transfer
  // Write ← ID of transfer
  uint32 transfer_id = 1;

  // Used by the receiver to indicate how many bytes it can accept. The
  // transmitter sends this much data, divided into chunks no larger than
  // max_chunk_size_bytes. The receiver then starts another window by sending
  // request_bytes again with a new offset.
  //
  //  Read → The client requests this many bytes to be sent.
  //  Read ← N/A
  // Write → N/A
  // Write ← The server requests this many bytes to be sent.
  optional uint32 pending_bytes = 2;

  // Maximum size of an individual chunk. The transmitter may send smaller
  // chunks if required.
  //
  //  Read → Set maximum size for subsequent chunks.
  //  Read ← N/A
  // Write → N/A
  // Write ← Set maximum size for subsequent chunks.
  optional uint32 max_chunk_size_bytes = 3;

  // Minimum required delay between chunks. The transmitter may delay longer if
  // desired.
  //
  //  Read → Set minimum delay for subsequent chunks.
  //  Read ← N/A
  // Write → N/A
  // Write ← Set minimum delay for subsequent chunks.
  optional uint32 min_delay_microseconds = 4;

  // On writes, the offset of the data. On reads, the offset at which to read.
  //
  //  Read → Read data starting at this offset.
  //  Read ← Offset of the data.
  // Write → Offset of the data.
  // Write ← Write data starting at this offset.
  uint64 offset = 5;

  // The data that was read or the data to write.
  //
  //  Read → N/A
  //  Read ← Data read
  // Write → Data to write
  // Write ← N/A
  bytes data = 6;

  // Estimated bytes remaining to read/write. Optional except for the last data
  // chunk, for which remaining_bytes must be set to 0.
  //
  // The sender can set remaining_bytes at the beginning of a read/write so that
  // the receiver can track progress or cancel the transaction if the value is
  // too large.
  //
  //  Read → N/A
  //  Read ← Remaining bytes to read, excluding any data in this chunk. Set to
  //         0 for the last chunk.
  // Write → Remaining bytes to write, excluding any data in is chunk. Set to
  //         0 for the last chunk.
  // Write ← N/A
  optional uint64 remaining_bytes = 7;

  // Pigweed status code indicating the completion of a transfer. This is only
  // present in the final packet sent by either the transmitter or receiver.
  //
  // The possible status codes and their meanings are listed below:
  //
  //   OK: Transfer completed successfully.
  //   DATA_LOSS: Transfer data could not be read/written (e.g. corruption).
  //   INVALID_ARGUMENT: Received malformed chunk.
  //   NOT_FOUND: The requested resource ID is not registered (read/write).
  //   OUT_OF_RANGE: The requested offset is larger than the data (read/write).
  //   RESOURCE_EXHAUSTED: Concurrent transfer limit reached.
  //   UNIMPLEMENTED: Resource ID does not support requested operation (e.g.
  //       trying to write to a read-only transfer).
  //
  //  Read → Transfer complete.
  //  Read ← Transfer complete.
  // Write → Transfer complete.
  // Write ← Transfer complete.
  optional uint32 status = 8;

  // The offset up to which the transmitter can send data before waiting for the
  // receiver to acknowledge.
  //
  //  Read → Offset up to which the server can send without blocking.
  //  Read ← N/A
  // Write → N/A
  // Write ← Offset up to which the client can send without blocking.
  //
  // TODO(frolv): This will replace the pending_bytes field. Once all uses of
  // transfer are migrated, that field should be removed.
  uint32 window_end_offset = 9;

  enum Type {
    // Chunk containing transfer data.
    DATA = 0;

    // First chunk of a transfer (only sent by the client).
    START = 1;

    // Transfer parameters indicating that the transmitter should retransmit
    // from the specified offset.
    PARAMETERS_RETRANSMIT = 2;

    // Transfer parameters telling the transmitter to continue sending up to
    // index `offset + pending_bytes` of data. If the transmitter is already
    // beyond `offset`, it does not have to rewind.
    PARAMETERS_CONTINUE = 3;

    // Sender of the chunk is terminating the transfer.
    COMPLETION = 4;

    // Acknowledge the completion of a transfer. Currently unused.
    // TODO(konkers): Implement this behavior.
    COMPLETION_ACK = 5;

    // Acknowledges a transfer start request, accepting the session ID for the
    // transfer and optionally negotiating the protocol version. Sent from
    // server to client.
    START_ACK = 6;

    // Confirmation of a START_ACK's negotiated parameters, sent by the client
    // to the server. Initiates the data transfer proper.
    START_ACK_CONFIRMATION = 7;
  };

  // The type of this chunk. This field should only be processed when present.
  // TODO(frolv): Update all users of pw_transfer and remove the optional
  // semantics from this field.
  //
  //  Read → Chunk type (start/parameters).
  //  Read ← Chunk type (data).
  // Write → Chunk type (data).
  // Write ← Chunk type (start/parameters).
  optional Type type = 10;

  // Unique identifier for the source or destination of transfer data. May be
  // stable or ephemeral depending on the implementation. Only sent during the
  // initial handshake phase of a version 2 or higher transfer.
  //
  //  Read → ID of transferable resource
  //  Read ← ID of transferable resource
  // Write → ID of transferable resource
  // Write ← ID of transferable resource
  optional uint32 resource_id = 11;

  // Unique identifier for a specific transfer session. Chosen by the transfer
  // client during the initial handshake phase, and persists for the remainder
  // of that transfer operation.
  //
  //  Read → ID of transfer session
  //  Read ← ID of transfer session
  // Write → ID of transfer session
  // Write ← ID of transfer session
  optional uint32 session_id = 12;

  // The protocol version to use for this transfer. Only sent during the initial
  // handshake phase of a version 2 or higher transfer to negotiate a common
  // protocol version between the client and server.
  //
  //  Read → Desired (START) or configured (START_ACK_CONFIRMATION) version.
  //  Read ← Configured protocol version (START_ACK).
  // Write → Desired (START) or configured (START_ACK_CONFIRMATION) version.
  // Write ← Configured protocol version (START_ACK).
  optional uint32 protocol_version = 13;

  // Unique identifier for a specific transfer session. Chosen by the transfer
  // client during the initial handshake phase. This field is used to request a
  // session during the handshake, after which the regular session_id field is
  // used.
  //
  //  Read → Requested ID of transfer session
  //  Read ← N/A
  // Write → Requested ID of transfer session
  // Write ← N/A
  optional uint32 desired_session_id = 14;

  // The initial offset to start the transfer from. Can be used for read or
  // write transfers. Set by the client during start handshake.
  // Needs to be accepted by the resource transfer handler in order for the
  // non-zero offset transfer to start from the initial_offset.
  //
  //  Read → Requested initial offset for the session
  //  Read ← Confirmed (matches) or denied (zero) initial offset
  // Write → Requested initial offset for the session
  // Write ← Confirmed (matches) or denied (zero) initial offset
  uint64 initial_offset = 15;
}

// Request for GetResourceStatus, indicating the resource to get status from.
message ResourceStatusRequest {
  uint32 resource_id = 1;
}

// Response for GetResourceStatus
message ResourceStatus {
  // Resource id, matching request
  uint32 resource_id = 1;

  // Status of the resource, returns Unimplemented by default.
  uint32 status = 2;
  // The offset that can be written to (other than 0).
  uint64 writeable_offset = 3;
  // The offset that can be read from (other than 0).
  uint64 readable_offset = 4;
  // The checksum at the given write offset.
  optional uint64 write_checksum = 5;
  // The checksum at the given read offset.
  optional uint64 read_checksum = 6;
}
