syntax = "proto3";

import "sdk/document.proto";
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";

package m10.sdk.transaction;

// Decoded `LedgerRequest.payload` message data for requests related to
// transactions and ledger state.
message TransactionRequestPayload {
  // Replay protection
  uint64 nonce = 1;
  // UNIX timestamp in microseconds
  uint64 timestamp = 2;

  // [optional] unique Context ID, allowing linking of multiple requests
  bytes context_id = 5;

  // Request payload data.
  TransactionData data = 6;
}

message TransactionData {
  oneof data {
    CreateTransfer transfer = 10;
    CreateLedgerAccount create_ledger_account = 11;
    SetFreezeState set_freeze_state = 12;
    m10.sdk.DocumentOperations document_operations = 16;
    InvokeAction invoke_action = 20;
    CreateTransfer initiate_transfer = 21;
    CommitTransfer commit_transfer = 22;
    SetInstrument set_instrument = 23;
    SetBalanceLimit set_balance_limit = 24;
    SetIssuanceLimit set_issuance_limit = 25;
    SetDisplayCode set_display_code = 26;
    CreateToken create_token = 100;
    RedeemToken redeem_token = 101;
    CreateLock create_lock = 102;
    ReleaseLock release_lock = 103;
    RedeemLocksForCycle redeem_locks_for_cycle = 104;
  }
}

message TransactionResponse {
  /// Unique transaction ID.
  uint64 tx_id = 1;
  /// Error information, if any.
  TransactionError error = 2;
  /// Transaction timestamp.
  fixed64 timestamp = 4;

  // Responses
  /// account ID.
  bytes account_created = 3;
  /// Committed transfer details.
  CreateTransfer transfer_committed = 5;
  OfflineToken token = 6;
}

message TransactionError {
  /// Error code.
  Code code = 1;
  /// Error message.
  string message = 2;

  enum Code {
    // Generic status codes
    UNKNOWN = 0;
    UNIMPLEMENTED = 1;
    NOT_FOUND = 2;
    ALREADY_EXISTS = 3;
    UNAUTHORIZED = 4;

    // Request is malformed
    BAD_REQUEST = 5;
    // Invalid/unsupported transaction request type
    INVALID_REQUEST_TYPE = 6;
    // Invalid account ID
    INVALID_ACCOUNT_ID = 7;
    // Invalid transfer parameters (amount, accounts, etc)
    INVALID_TRANSFER = 8;
    // The request message was too large to be processed.
    MESSAGE_TOO_LARGE = 10;
    // The request signature does not match the request payload
    INVALID_SIGNATURE = 11;

    // Signature verification failed
    VERIFICATION_FAILED = 12;

    // Replay protection was triggered, e.g. nonce reuse, bad block window
    REPLAY_PROTECTION = 20;

    // Invalid expression used
    INVALID_EXPRESSION = 21;
    INCORRECT_TYPE = 22;

    // Ledger-specific status codes
    ACCOUNT_FROZEN = 23;
    UNMODIFIED_STATE = 24;
    INSUFFICIENT_BALANCE = 25;
    BALANCE_OVERFLOW = 26;
    ACCOUNT_DEPTH_EXCEEDED = 27;
    HOLDING_LIMIT_EXCEEDED = 28;
    ISSUANCE_LIMIT_EXCEEDED = 29;

    // Action specific status codes
    INVALID_TARGET = 30;

    // Instrument specific status codes
    DISPLAY_CODE_CONFLICT = 40;
    INVALID_DISPLAY_CODE = 41;

    // Settlement lock specific status codes
    INSUFFICIENT_AVAILABLE_BALANCE = 42;
    LOCK_NOT_FOUND = 43;
    INVALID_LOCK_STATE = 44;
    DUPLICATE_LOCK_ID = 45;
  }
}

message CreateLedgerTransfer {
  /// Ledger identifier.
  string ledger_id = 1;
  /// Nonce for replay protection.
  uint64 nonce = 2;
  /// Transfer details.
  CreateTransfer transfer = 3;
}

message CreateLedgerTransfers {
  // Ledger transfers
  repeated CreateLedgerTransfer transfers = 1;
  // UTC timestamp defining the end date of the contract
  // Interpret as microseconds since the UNIX_EPOCH
  uint64 valid_until = 2;
}

message GetTransferRequest {
  /// Transaction ID of the transfer.
  uint64 tx_id = 1;
}

// Transfers are returned in descending order from max_tx_id to min_tx_id
message ListTransferRequest {
  oneof filter {
    /// Account ID to filter transfers by.
    bytes account_id = 1;
    /// Context ID to filter transfers by.
    bytes context_id = 2;
  }
  /// Maximum number of transfers to return.
  uint64 limit = 4;
  /// Whether to include transfers from child accounts.
  bool include_child_accounts = 5;
  /// Minimum transaction ID to include.
  uint64 min_tx_id = 6;
  // default = maximum possible tx id
  /// Maximum transaction ID to include.
  uint64 max_tx_id = 7;
  /// State of the transfer.
  FinalizedTransfer.TransferState state = 8;
}

message CreateTransfer {
  /// List of transfer steps.
  repeated TransferStep transfer_steps = 1;
}

message TransferStep {
  /// Source account ID.
  bytes from_account_id = 1;
  /// Destination account ID.
  bytes to_account_id = 2;
  /// Amount to transfer.
  uint64 amount = 4;
  /// Metadata associated with the transfer step.
  repeated google.protobuf.Any metadata = 7;
}

message FinalizedTransfer {
  /// Transaction ID of the finalized transfer.
  uint64 tx_id = 1;
  /// Context ID associated with the transfer.
  bytes context_id = 2;
  /// List of transfer steps.
  repeated TransferStep transfer_steps = 3;
  /// Error information, if any.
  TransactionError error = 4;
  /// Timestamp of the transfer.
  fixed64 timestamp = 5;
  /// State of the transfer.
  TransferState state = 7;
  enum TransferState {
    ACCEPTED = 0;
    REJECTED = 1;
    PENDING  = 2;
    EXPIRED  = 3;
    // Sentinel value used only as a filter in requests; not stored on finalized transfers.
    TRANSFER_STATE_UNSPECIFIED = 4;
  }
}

message FinalizedTransfers {
  /// List of finalized transfers.
  repeated FinalizedTransfer transfers = 1;
}

message Instrument {
  /// Instrument code (e.g., currency code).
  string code = 1;
  /// Number of decimal places for the instrument.
  uint32 decimal_places = 2;
  /// Description of the instrument.
  string description = 3;
  /// Unique display code for the instrument. Equals `code` if not set.
  string display_code = 4;
}

message CreateLedgerAccount {
  /// Parent account ID.
  bytes parent_id = 1;
  /// Whether this is an issuance account.
  bool issuance = 2;
  /// Whether the account is initially frozen.
  bool frozen = 3;
  /// Instrument associated with the account.
  Instrument instrument = 4;
  /// Balance limit for the account.
  uint64 balance_limit = 5;
  /// Maximum issued balance for issuance accounts. 0 = unlimited.
  uint64 issuance_limit = 6;
}

message SetFreezeState {
  /// Account ID to set freeze state for.
  bytes account_id = 1;
  /// Whether to freeze the account.
  bool frozen = 2;
}

message SetInstrument {
  /// Account ID to set the instrument for.
  bytes account_id = 1;
  /// Instrument code.
  string code = 2;
  /// Number of decimal places.
  uint32 decimal_places = 3;
  /// Instrument description.
  string description = 4;
  /// Unique display code for the instrument.
  string display_code = 5;
}

message SetBalanceLimit {
  /// Account ID to set the balance limit for.
  bytes account_id = 1;
  /// New balance limit.
  uint64 balance_limit = 2;
}

message SetIssuanceLimit {
  /// Account ID of the issuance account.
  bytes account_id = 1;
  /// New issuance limit. 0 = unlimited.
  uint64 issuance_limit = 2;
}

message SetDisplayCode {
  /// Account ID to set the display code for.
  bytes account_id = 1;
  /// New display code
  string display_code = 2;
}

message GetAccountRequest {
  /// ID of the account to retrieve.
  bytes id = 1;
}

message IndexedAccount {
  /// Account ID.
  bytes id = 1;
  /// Issuance information.
  Issuance issuance = 3;
  /// Account balance.
  uint64 balance = 4;
  /// Whether the account is frozen.
  bool frozen = 5;
  /// Instrument associated with the account.
  Instrument instrument = 6;
  /// Balance limit of the account.
  uint64 balance_limit = 7;
  /// Sum of active settlement locks on the account.
  uint64 locked_balance = 8;
  /// Spendable balance after lock reservation.
  uint64 available_balance = 9;
  /// Display Code of the account
  string display_code = 10;
  message Issuance {
    /// Issued balance of the account.
    uint64 issued_balance = 1;
    /// Number of non-leaf child accounts.
    uint64 non_leaf_children = 2;
    /// Number of leaf child accounts.
    uint64 leaf_children = 3;
    /// Maximum allowed issued balance. 0 = unlimited.
    uint64 issuance_limit = 4;
  }
}

// Invokes a registered action by sending an opaque payload to a Target
message InvokeAction {
  // Unique name of the action to invoke
  string name = 1;
  // Invoking account ID
  bytes from_account = 3;
  // Specifies the identity/identities who have permission to see & be notified
  // of the invoked action
  Target target = 4;
  // Opaque payload
  bytes payload = 5;
}

message Target {
  oneof target {
    // Account ID
    bytes account_id = 1;
    // Any Account
    google.protobuf.Empty any_account = 2;
  }
}

message Action {
  /// Transaction ID of the action.
  uint64 tx_id = 1;
  /// Name of the action.
  string name = 2;
  // [optional] unique Context ID, allowing linking of multiple requests
  bytes context_id = 3;
  /// Invoking account ID.
  bytes from_account = 4;
  /// Target acount ID of the action.
  Target target = 5;
  /// Action payload.
  bytes payload = 6;
}

message Actions {
  /// List of actions.
  repeated Action actions = 1;
}

message GetActionRequest {
  /// Transaction ID of the action to retrieve.
  uint64 tx_id = 1;
}

message ListActionsRequest {
  /// Name of the action to filter by.
  string name = 1;
  oneof filter {
    /// Account ID to filter by.
    bytes account_id = 2;
    /// Context ID to filter by.
    bytes context_id = 3;
  }
  /// Maximum number of actions to return.
  uint64 limit = 4;
  /// Minimum transaction ID to include.
  uint64 min_tx_id = 5;
  // default = maximum possible tx id
  /// Maximum transaction ID to include.
  uint64 max_tx_id = 6;
}

message CommitTransfer {
  /// Transaction ID of the pending transfer.
  uint64 pending_tx_id = 1;
  enum TransferState {
    ACCEPTED = 0;
    REJECTED = 1;
  }
  /// New state of the transfer.
  TransferState new_state = 2;
}


message Signature {
  /// Public key used for the signature.
  bytes public_key = 2;
  /// Signature data.
  bytes signature = 3;
  /// Signature algorithm.
  Algorithm algorithm = 4;
  enum Algorithm {
    P256_SHA256_ASN1 = 0;
    ED25519 = 1;
    ED25519_PH_SHA_512 = 2;
  }
}

message CreateToken {
    /// Address for the token.
    bytes address = 1;
    /// Account ID associated with the token.
    bytes account_id = 2;
    /// Value of the token.
    uint64 value = 3;
}

message OfflineToken {
    message Data {
        /// Token ID.
        uint64 id = 1;
        /// Token address.
        bytes address = 2;
        /// Account ID associated with the token.
        bytes account_id = 3;
        /// Token value.
        uint64 value = 4;
        /// Currency of the token.
        string currency = 5;
    }
    /// Token data.
    Data data = 1;
    /// Signature for the token.
    Signature signature = 2;
}

message RedeemableToken {
    message TokenInput {
        /// Input value.
        uint64 input = 1;
        /// Value associated with the input.
        uint64 value = 2;
    }
    message Data {
        /// Token ID.
        bytes id = 1;
        /// Token address.
        bytes address = 2;
        /// List of token inputs.
        repeated TokenInput inputs = 3;
        /// Token currency.
        string currency = 4;
    }
    /// Token data.
    Data data = 1;
    /// Signature for the token.
    Signature signature = 2;
}

message RedeemToken {
    /// Redeemable token.
    RedeemableToken token = 1;
    /// Account ID for redemption.
    bytes account_id = 2;
}

message CreateLock {
  // Unique identifier for this lock (client-generated, e.g. UUID bytes).
  bytes lock_id = 1;
  // The settlement cycle this lock belongs to.
  bytes cycle_id = 2;
  // The account whose balance is being locked.
  bytes account_id = 3;
  // The amount to reserve.
  uint64 amount = 4;
  // Human-readable reason. Defaults to "SETTLEMENT".
  string reason = 5;
  // Optional reference to the settlement batch/cycle document.
  bytes settlement_batch_ref = 6;
}

message ReleaseLock {
  bytes lock_id = 1;
  // Reference to the release/abort transaction or reason document.
  bytes release_ref = 2;
}

message RedeemLocksForCycle {
  // The settlement cycle being redeemed.
  bytes cycle_id = 1;
  // Reference to the confirmed Fedwire settlement transaction.
  bytes redemption_tx_ref = 3;
  // One entry per lock. All steps are applied atomically.
  repeated RedemptionStep steps = 4;
}

message RedemptionStep {
  bytes lock_id = 1;
  bytes holder_account_id = 2;
  bytes issuance_account_id = 3;
  uint64 amount = 4;
}

enum LockStatus {
  ACTIVE = 0;
  REDEEMED = 1;
  RELEASED = 2;
}

message LockDocument {
  bytes lock_id = 1;
  bytes cycle_id = 2;
  bytes account_id = 3;
  uint64 amount = 4;
  LockStatus status = 5;
  uint64 created_at = 6;
  bytes created_by = 7;
  string reason = 8;
  bytes settlement_batch_ref = 9;
  bytes redemption_tx_ref = 10;
  bytes release_tx_ref = 11;
}

enum SettlementCycleStatus {
  OPEN = 0;
  LOCKED = 1;
  CASH_SETTLED = 2;
  CLOSED = 3;
  ABORTED = 4;
}

message SettlementCycle {
  bytes cycle_id = 1;
  SettlementCycleStatus status = 2;
  uint64 cutoff_at = 3;
  uint64 settled_at = 4;
  uint64 closed_at = 5;
  bytes created_by = 6;
  bytes operator_metadata = 7;
}
