syntax = "proto3";

package sf.substreams.v1;

option go_package = "github.com/streamingfast/substreams/pb/sf/substreams/v1;pbsubstreams";

import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "sf/substreams/v1/modules.proto";
import "sf/substreams/v1/clock.proto";

service Stream {
  rpc Blocks(Request) returns (stream Response);
}

message Request {
  int64 start_block_num = 1;
  string start_cursor = 2;
  uint64 stop_block_num = 3;
  repeated ForkStep fork_steps = 4;
  string irreversibility_condition = 5;

  // Substreams has two mode when executing your module(s) either development mode or production
  // mode. Development and production modes impact the execution of Substreams, important aspects
  // of execution include:
  // * The time required to reach the first byte.
  // * The speed that large ranges get executed.
  // * The module logs and outputs sent back to the client.
  //
  // By default, the engine runs in developer mode, with richer and deeper output. Differences
  // between production and development modes include:
  // * Forward parallel execution is enabled in production mode and disabled in development mode
  // * The time required to reach the first byte in development mode is faster than in production mode.
  //
  // Specific attributes of development mode include:
  // * The client will receive all of the executed module's logs.
  // * It's possible to request specific store snapshots in the execution tree (via `debug_initial_store_snapshot_for_modules`).
  // * Multiple module's output is possible.
  //
  // With production mode`, however, you trade off functionality for high speed enabling forward
  // parallel execution of module ahead of time.
  bool production_mode = 9;

  Modules modules = 6;
  repeated string output_modules = 7;

  // Available only in developer mode
  repeated string debug_initial_store_snapshot_for_modules = 8;

  string output_module = 10;
}

message Response {
  oneof message {
    SessionInit session = 5; // Always sent first
    ModulesProgress progress = 1; // Progress of data preparation, before sending in the stream of `data` events.

    // Available only in developer mode, and only if `debug_initial_store_snapshot_for_modules` is set.
    InitialSnapshotData debug_snapshot_data = 2;
    // Available only in developer mode, and only if `debug_initial_store_snapshot_for_modules` is set.
    InitialSnapshotComplete debug_snapshot_complete = 3;

    BlockScopedData data = 4;
  }
}

enum ForkStep {
  STEP_UNKNOWN = 0;
  // Block is new head block of the chain, that is linear with the previous block
  STEP_NEW = 1;
  // Block is now forked and should be undone, it's not the head block of the chain anymore
  STEP_UNDO = 2;
  // Removed, was STEP_REDO
  reserved 3;
  // Block is now irreversible and can be committed to (finality is chain specific, see chain documentation for more details)
  STEP_IRREVERSIBLE = 4;
  // Removed, was STEP_STALLED
  reserved 5;
}

message SessionInit  {
  string trace_id = 1;
}

message InitialSnapshotComplete {
  string cursor = 1;
}

message InitialSnapshotData {
  string module_name = 1;
  StoreDeltas deltas = 2;
  uint64 sent_keys = 4;
  uint64 total_keys = 3;
}

message BlockScopedData {
  repeated ModuleOutput outputs = 1;
  Clock clock = 3;
  ForkStep step = 6;
  string cursor = 10;
}

message ModuleOutput {
  string name = 1;

  oneof data {
    google.protobuf.Any map_output = 2;

    // StoreDeltas are produced for store modules in development mode.
    // It is not possible to retrieve store models in production, with parallelization
    // enabled. If you need the deltas directly, write a pass through mapper module
    // that will get them down to you.
    StoreDeltas debug_store_deltas = 3;
  }
  repeated string debug_logs = 4;
  // LogsTruncated is a flag that tells you if you received all the logs or if they
  // were truncated because you logged too much (fixed limit currently is set to 128 KiB).
  bool debug_logs_truncated = 5;

  bool cached = 6;
}

// think about:
// message ModuleOutput { ...
//   ModuleOutputDebug debug_info = 6;
// ...}
//message ModuleOutputDebug {
//  StoreDeltas store_deltas = 3;
//  repeated string logs = 4;
//  // LogsTruncated is a flag that tells you if you received all the logs or if they
//  // were truncated because you logged too much (fixed limit currently is set to 128 KiB).
//  bool logs_truncated = 5;
//}

message ModulesProgress {
  repeated ModuleProgress modules = 1;
}

message ModuleProgress {
  string name = 1;

  oneof type {
    ProcessedRange processed_ranges = 2;
    InitialState initial_state = 3;
    ProcessedBytes processed_bytes = 4;
    Failed failed = 5;
  }

  message ProcessedRange {
    repeated BlockRange processed_ranges = 1;
  }
  message InitialState {
    uint64 available_up_to_block = 2;
  }
  message ProcessedBytes {
    uint64 total_bytes_read = 1;
    uint64 total_bytes_written = 2;
    uint64 bytes_read_delta = 3;
    uint64 bytes_written_delta = 4;
    uint64 nano_seconds_delta = 5;
  }
  message Failed {
    string reason = 1;
    repeated string logs = 2;
    // FailureLogsTruncated is a flag that tells you if you received all the logs or if they
    // were truncated because you logged too much (fixed limit currently is set to 128 KiB).
    bool logs_truncated = 3;
  }
}

message BlockRange {
  uint64 start_block = 2;
  uint64 end_block = 3;
}

message StoreDeltas {
  repeated StoreDelta deltas = 1;
}

message StoreDelta {
  enum Operation {
    UNSET = 0;
    CREATE = 1;
    UPDATE = 2;
    DELETE = 3;
  }
  Operation operation = 1;
  uint64 ordinal = 2;
  string key = 3;
  bytes old_value = 4;
  bytes new_value = 5;
}

message Output {
  uint64 block_num = 1;
  string block_id = 2;
  google.protobuf.Timestamp timestamp = 4;
  google.protobuf.Any value = 10;
}