syntax = "proto3";

import "google/protobuf/empty.proto";
import "types/types.proto";

package sentry;

option go_package = "./sentry;sentryproto";

enum MessageId {
  // ======= eth 65 protocol ===========

  STATUS_65 = 0;
  GET_BLOCK_HEADERS_65 = 1;
  BLOCK_HEADERS_65 = 2;
  BLOCK_HASHES_65 = 3;
  GET_BLOCK_BODIES_65 = 4;
  BLOCK_BODIES_65 = 5;
  GET_NODE_DATA_65 = 6;
  NODE_DATA_65 = 7;
  GET_RECEIPTS_65 = 8;
  RECEIPTS_65 = 9;
  NEW_BLOCK_HASHES_65 = 10;
  NEW_BLOCK_65 = 11;
  TRANSACTIONS_65 = 12;
  NEW_POOLED_TRANSACTION_HASHES_65 = 13;
  GET_POOLED_TRANSACTIONS_65 = 14;
  POOLED_TRANSACTIONS_65 = 15;


  // ======= eth 66 protocol ===========

  // eth64 announcement messages (no id)
  STATUS_66 = 17;
  NEW_BLOCK_HASHES_66 = 18;
  NEW_BLOCK_66 = 19;
  TRANSACTIONS_66 = 20;

  // eth65 announcement messages (no id)
  NEW_POOLED_TRANSACTION_HASHES_66 = 21;

  // eth66 messages with request-id
  GET_BLOCK_HEADERS_66 = 22;
  GET_BLOCK_BODIES_66 = 23;
  GET_NODE_DATA_66 = 24;
  GET_RECEIPTS_66 = 25;
  GET_POOLED_TRANSACTIONS_66 = 26;
  BLOCK_HEADERS_66 = 27;
  BLOCK_BODIES_66 = 28;
  NODE_DATA_66 = 29;
  RECEIPTS_66 = 30;
  POOLED_TRANSACTIONS_66 = 31;

  // ======= eth 67 protocol ===========
  // Version 67 removed the GetNodeData and NodeData messages.

  // ======= eth 68 protocol ===========
  NEW_POOLED_TRANSACTION_HASHES_68 = 32;

  // ======= wit protocol ===========
  GET_BLOCK_WITNESS_W0 = 33;
  BLOCK_WITNESS_W0 = 34;
  NEW_WITNESS_W0 = 35;
  NEW_WITNESS_HASHES_W0 = 36;

  // ======= eth 69 protocol ===========
  STATUS_69 = 37;
  GET_RECEIPTS_69 = 38;
  BLOCK_RANGE_UPDATE_69 = 39;

  // ======= eth 70 protocol ===========
  GET_RECEIPTS_70 = 40;
  RECEIPTS_70 = 41;

  // ======= eth 71 protocol (EIP-8159) ===========
  GET_BLOCK_ACCESS_LISTS_71 = 42;
  BLOCK_ACCESS_LISTS_71 = 43;
}

message OutboundMessageData {
  MessageId id = 1;
  bytes data = 2;
}

message SendMessageByMinBlockRequest {
  OutboundMessageData data = 1;
  uint64 min_block = 2;
  uint64 max_peers = 3;
}

message SendMessageByIdRequest {
  OutboundMessageData data = 1;
  types.H512 peer_id = 2;
}

message SendMessageToRandomPeersRequest {
  OutboundMessageData data = 1;
  uint64 max_peers = 2;
}

message SentPeers {repeated types.H512 peers = 1;}

enum PenaltyKind {Kick = 0;}

message PenalizePeerRequest {
  types.H512 peer_id = 1;
  PenaltyKind penalty = 2;
}

message SetPeerLatestBlockRequest {
  types.H512 peer_id = 1;
  uint64 latest_block_height = 2;
}

message SetPeerMinimumBlockRequest {
  types.H512 peer_id = 1;
  uint64 min_block_height = 2;
}

message SetPeerBlockRangeRequest {
  types.H512 peer_id = 1;
  uint64 min_block_height = 2;
  uint64 latest_block_height = 3;
}

message AddPeerRequest {
  string url = 1;
}

message RemovePeerRequest {
  string url = 1;
}

message InboundMessage {
  MessageId id = 1;
  bytes data = 2;
  types.H512 peer_id = 3;
}

message Forks {
  types.H256 genesis = 1;
  repeated uint64 height_forks = 2;
  repeated uint64 time_forks = 3;
}

message StatusData {
  uint64 network_id = 1;
  types.H256 total_difficulty = 2;
  types.H256 best_hash = 3;
  Forks fork_data = 4;
  uint64 max_block_height = 5;
  uint64 max_block_time = 6;
  uint64 minimum_block_height = 7;
}

enum Protocol {
  ETH65 = 0;
  ETH66 = 1;
  ETH67 = 2;
  ETH68 = 3;
  ETH69 = 4;
  ETH70 = 6;
  ETH71 = 7;
  WIT0 = 5;
}

message SetStatusReply {}

message HandShakeReply {
  Protocol protocol = 1;
  repeated Protocol side_protocols = 2;
}

message MessagesRequest {
  repeated MessageId ids = 1;
}

message PeersReply {
  repeated types.PeerInfo peers = 1;
}

message PeerCountRequest {}

message PeerCountPerProtocol {
  Protocol protocol = 1;
  uint64 count = 2;
} 

message PeerCountReply {
  uint64 count = 1;
  repeated PeerCountPerProtocol counts_per_protocol = 2;
}

message PeerByIdRequest {types.H512 peer_id = 1;}

message PeerByIdReply {optional types.PeerInfo peer = 1;}

message PeerEventsRequest {}

message PeerEvent {
  enum PeerEventId {
    // Happens after after a successful sub-protocol handshake.
    Connect = 0;
    Disconnect = 1;
  }
  types.H512 peer_id = 1;
  PeerEventId event_id = 2;
}

message AddPeerReply {
  bool success = 1;
}

message RemovePeerReply {
  bool success = 1;
}

service Sentry {
  // SetStatus - force new ETH client state of sentry - network_id, max_block, etc...
  rpc SetStatus(StatusData) returns (SetStatusReply);

  rpc PenalizePeer(PenalizePeerRequest) returns (google.protobuf.Empty);
  rpc SetPeerLatestBlock(SetPeerLatestBlockRequest) returns (google.protobuf.Empty);
  rpc SetPeerMinimumBlock(SetPeerMinimumBlockRequest) returns (google.protobuf.Empty);
  rpc SetPeerBlockRange(SetPeerBlockRangeRequest) returns (google.protobuf.Empty);

  // HandShake - pre-requirement for all Send* methods - returns list of ETH protocol versions,
  // without knowledge of protocol - impossible encode correct P2P message
  rpc HandShake(google.protobuf.Empty) returns (HandShakeReply);
  rpc SendMessageByMinBlock(SendMessageByMinBlockRequest) returns (SentPeers);
  rpc SendMessageById(SendMessageByIdRequest) returns (SentPeers);
  rpc SendMessageToRandomPeers(SendMessageToRandomPeersRequest)
      returns (SentPeers);
  rpc SendMessageToAll(OutboundMessageData) returns (SentPeers);

  // Subscribe to receive messages.
  // Calling multiple times with a different set of ids starts separate streams.
  // It is possible to subscribe to the same set if ids more than once.
  rpc Messages(MessagesRequest) returns (stream InboundMessage);

  rpc Peers(google.protobuf.Empty) returns (PeersReply);
  rpc PeerCount(PeerCountRequest) returns (PeerCountReply);
  rpc PeerById(PeerByIdRequest) returns (PeerByIdReply);
  // Subscribe to notifications about connected or lost peers.
  rpc PeerEvents(PeerEventsRequest) returns (stream PeerEvent);

  rpc AddPeer(AddPeerRequest) returns (AddPeerReply);
  rpc RemovePeer(RemovePeerRequest) returns (RemovePeerReply);

  rpc AddTrustedPeer(AddPeerRequest) returns (AddPeerReply);
  rpc RemoveTrustedPeer(RemovePeerRequest) returns (RemovePeerReply);

  // NodeInfo returns a collection of metadata known about the host.
  rpc NodeInfo(google.protobuf.Empty) returns(types.NodeInfoReply);
}
