diff --git a/cmd/main.go b/cmd/main.go index 6f773440..96981094 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,7 +10,7 @@ import ( contracts "github.com/primevprotocol/contracts-abi/config" mevcommit "github.com/primevprotocol/mev-commit" - ks "github.com/primevprotocol/mev-commit/pkg/keysigner" + ks "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner" "github.com/primevprotocol/mev-commit/pkg/node" "github.com/primevprotocol/mev-commit/pkg/util" "github.com/urfave/cli/v2" @@ -192,6 +192,13 @@ var ( Value: contracts.TestnetContracts.PreconfCommitmentStore, }) + optionBlockTrackerAddr = altsrc.NewStringFlag(&cli.StringFlag{ + Name: "block-tracker-contract", + Usage: "address of the block tracker contract", + EnvVars: []string{"MEV_COMMIT_BLOCK_TRACKER_ADDR"}, + Value: contracts.TestnetContracts.BlockTracker, + }) + optionSettlementRPCEndpoint = altsrc.NewStringFlag(&cli.StringFlag{ Name: "settlement-rpc-endpoint", Usage: "rpc endpoint of the settlement layer", @@ -199,6 +206,13 @@ var ( Value: "http://localhost:8545", }) + optionSettlementWSRPCEndpoint = altsrc.NewStringFlag(&cli.StringFlag{ + Name: "settlement-ws-rpc-endpoint", + Usage: "ws rpc endpoint of the settlement layer", + EnvVars: []string{"MEV_COMMIT_SETTLEMENT_WS_RPC_ENDPOINT"}, + Value: "ws://localhost:8546", + }) + optionNATAddr = altsrc.NewStringFlag(&cli.StringFlag{ Name: "nat-addr", Usage: "external address of the node", @@ -246,7 +260,9 @@ func main() { optionBidderRegistryAddr, optionProviderRegistryAddr, optionPreconfStoreAddr, + optionBlockTrackerAddr, optionSettlementRPCEndpoint, + optionSettlementWSRPCEndpoint, optionNATAddr, optionNATPort, optionServerTLSCert, @@ -330,7 +346,9 @@ func launchNodeWithConfig(c *cli.Context) error { PreconfContract: c.String(optionPreconfStoreAddr.Name), ProviderRegistryContract: c.String(optionProviderRegistryAddr.Name), BidderRegistryContract: c.String(optionBidderRegistryAddr.Name), + BlockTrackerContract: c.String(optionBlockTrackerAddr.Name), RPCEndpoint: c.String(optionSettlementRPCEndpoint.Name), + WSRPCEndpoint: c.String(optionSettlementWSRPCEndpoint.Name), NatAddr: natAddr, TLSCertificateFile: crtFile, TLSPrivateKeyFile: keyFile, diff --git a/gen/go/bidderapi/v1/bidderapi.pb.go b/gen/go/bidderapi/v1/bidderapi.pb.go index b4cc9573..fb283ba9 100644 --- a/gen/go/bidderapi/v1/bidderapi.pb.go +++ b/gen/go/bidderapi/v1/bidderapi.pb.go @@ -12,6 +12,7 @@ import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" reflect "reflect" sync "sync" ) @@ -23,16 +24,18 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type PrepayRequest struct { +type DepositRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Amount string `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + Amount string `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + WindowNumber *wrapperspb.UInt64Value `protobuf:"bytes,2,opt,name=windowNumber,proto3" json:"windowNumber,omitempty"` + BlockNumber *wrapperspb.UInt64Value `protobuf:"bytes,3,opt,name=blockNumber,proto3" json:"blockNumber,omitempty"` } -func (x *PrepayRequest) Reset() { - *x = PrepayRequest{} +func (x *DepositRequest) Reset() { + *x = DepositRequest{} if protoimpl.UnsafeEnabled { mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -40,13 +43,13 @@ func (x *PrepayRequest) Reset() { } } -func (x *PrepayRequest) String() string { +func (x *DepositRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PrepayRequest) ProtoMessage() {} +func (*DepositRequest) ProtoMessage() {} -func (x *PrepayRequest) ProtoReflect() protoreflect.Message { +func (x *DepositRequest) ProtoReflect() protoreflect.Message { mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -58,28 +61,43 @@ func (x *PrepayRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PrepayRequest.ProtoReflect.Descriptor instead. -func (*PrepayRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use DepositRequest.ProtoReflect.Descriptor instead. +func (*DepositRequest) Descriptor() ([]byte, []int) { return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{0} } -func (x *PrepayRequest) GetAmount() string { +func (x *DepositRequest) GetAmount() string { if x != nil { return x.Amount } return "" } -type PrepayResponse struct { +func (x *DepositRequest) GetWindowNumber() *wrapperspb.UInt64Value { + if x != nil { + return x.WindowNumber + } + return nil +} + +func (x *DepositRequest) GetBlockNumber() *wrapperspb.UInt64Value { + if x != nil { + return x.BlockNumber + } + return nil +} + +type DepositResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Amount string `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + Amount string `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + WindowNumber *wrapperspb.UInt64Value `protobuf:"bytes,2,opt,name=windowNumber,proto3" json:"windowNumber,omitempty"` } -func (x *PrepayResponse) Reset() { - *x = PrepayResponse{} +func (x *DepositResponse) Reset() { + *x = DepositResponse{} if protoimpl.UnsafeEnabled { mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -87,13 +105,13 @@ func (x *PrepayResponse) Reset() { } } -func (x *PrepayResponse) String() string { +func (x *DepositResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PrepayResponse) ProtoMessage() {} +func (*DepositResponse) ProtoMessage() {} -func (x *PrepayResponse) ProtoReflect() protoreflect.Message { +func (x *DepositResponse) ProtoReflect() protoreflect.Message { mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -105,18 +123,25 @@ func (x *PrepayResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PrepayResponse.ProtoReflect.Descriptor instead. -func (*PrepayResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use DepositResponse.ProtoReflect.Descriptor instead. +func (*DepositResponse) Descriptor() ([]byte, []int) { return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{1} } -func (x *PrepayResponse) GetAmount() string { +func (x *DepositResponse) GetAmount() string { if x != nil { return x.Amount } return "" } +func (x *DepositResponse) GetWindowNumber() *wrapperspb.UInt64Value { + if x != nil { + return x.WindowNumber + } + return nil +} + type EmptyMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -155,6 +180,53 @@ func (*EmptyMessage) Descriptor() ([]byte, []int) { return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{2} } +type GetDepositRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WindowNumber *wrapperspb.UInt64Value `protobuf:"bytes,1,opt,name=windowNumber,proto3" json:"windowNumber,omitempty"` +} + +func (x *GetDepositRequest) Reset() { + *x = GetDepositRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetDepositRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDepositRequest) ProtoMessage() {} + +func (x *GetDepositRequest) ProtoReflect() protoreflect.Message { + mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetDepositRequest.ProtoReflect.Descriptor instead. +func (*GetDepositRequest) Descriptor() ([]byte, []int) { + return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{3} +} + +func (x *GetDepositRequest) GetWindowNumber() *wrapperspb.UInt64Value { + if x != nil { + return x.WindowNumber + } + return nil +} + type Bid struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -170,7 +242,7 @@ type Bid struct { func (x *Bid) Reset() { *x = Bid{} if protoimpl.UnsafeEnabled { - mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[3] + mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -183,7 +255,7 @@ func (x *Bid) String() string { func (*Bid) ProtoMessage() {} func (x *Bid) ProtoReflect() protoreflect.Message { - mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[3] + mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -196,7 +268,7 @@ func (x *Bid) ProtoReflect() protoreflect.Message { // Deprecated: Use Bid.ProtoReflect.Descriptor instead. func (*Bid) Descriptor() ([]byte, []int) { - return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{3} + return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{4} } func (x *Bid) GetTxHashes() []string { @@ -254,7 +326,7 @@ type Commitment struct { func (x *Commitment) Reset() { *x = Commitment{} if protoimpl.UnsafeEnabled { - mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[4] + mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -267,7 +339,7 @@ func (x *Commitment) String() string { func (*Commitment) ProtoMessage() {} func (x *Commitment) ProtoReflect() protoreflect.Message { - mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[4] + mi := &file_bidderapi_v1_bidderapi_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -280,7 +352,7 @@ func (x *Commitment) ProtoReflect() protoreflect.Message { // Deprecated: Use Commitment.ProtoReflect.Descriptor instead. func (*Commitment) Descriptor() ([]byte, []int) { - return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{4} + return file_bidderapi_v1_bidderapi_proto_rawDescGZIP(), []int{5} } func (x *Commitment) GetTxHashes() []string { @@ -365,254 +437,314 @@ var file_bidderapi_v1_bidderapi_proto_rawDesc = []byte{ 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x02, 0x0a, 0x0d, 0x50, 0x72, 0x65, 0x70, - 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xa5, 0x01, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8c, 0x01, 0x92, 0x41, 0x2e, - 0x32, 0x23, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x45, 0x54, 0x48, 0x20, - 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x65, 0x70, 0x61, 0x69, 0x64, 0x20, 0x69, 0x6e, - 0x20, 0x77, 0x65, 0x69, 0x2e, 0x8a, 0x01, 0x06, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0xba, 0x48, - 0x58, 0xba, 0x01, 0x55, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x2a, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x30, - 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x3a, 0x7a, 0x92, 0x41, 0x77, 0x0a, 0x51, 0x2a, 0x0e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, - 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x36, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, 0x69, 0x64, 0x73, 0x20, 0x74, 0x6f, - 0x20, 0x62, 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x65, 0x69, 0x2e, - 0xd2, 0x01, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0x22, 0x7b, 0x22, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x20, 0x7d, 0x22, 0x9e, 0x01, - 0x0a, 0x0e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x74, 0x92, 0x41, 0x71, 0x0a, 0x4b, 0x2a, - 0x0f, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x32, 0x38, 0x47, 0x65, 0x74, 0x20, 0x70, 0x72, 0x65, 0x70, 0x61, 0x69, 0x64, 0x20, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, 0x69, 0x64, 0x64, - 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, - 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x32, 0x22, 0x7b, 0x22, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x20, 0x7d, 0x22, 0x0e, - 0x0a, 0x0c, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa2, - 0x0b, 0x0a, 0x03, 0x42, 0x69, 0x64, 0x12, 0xa3, 0x02, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x85, 0x02, 0x92, 0x41, 0x78, - 0x32, 0x64, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, - 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, - 0x68, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, - 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x8a, 0x01, 0x0f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, - 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0xba, 0x48, 0x86, 0x01, 0xba, 0x01, 0x82, 0x01, - 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x36, 0x74, 0x78, 0x5f, - 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, - 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x68, - 0x65, 0x73, 0x2e, 0x1a, 0x3d, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x2c, - 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, - 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0x24, 0x27, 0x29, 0x29, - 0x20, 0x26, 0x26, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, - 0x20, 0x30, 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0xed, 0x01, 0x0a, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0xd4, 0x01, - 0x92, 0x41, 0x76, 0x32, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x45, - 0x54, 0x48, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, - 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, - 0x20, 0x70, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, - 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x8a, 0x01, 0x06, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0xba, 0x48, 0x58, 0xba, 0x01, 0x55, 0x0a, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x2a, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, - 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, - 0x20, 0x3e, 0x20, 0x30, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0xb9, 0x01, 0x0a, - 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x42, 0x95, 0x01, 0x92, 0x41, 0x47, 0x32, 0x45, 0x4d, 0x61, 0x78, 0x20, 0x62, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdc, 0x06, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x94, 0x01, 0x0a, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x7c, 0x92, 0x41, 0x30, + 0x32, 0x25, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x45, 0x54, 0x48, 0x20, + 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, 0x20, + 0x69, 0x6e, 0x20, 0x77, 0x65, 0x69, 0x2e, 0x8a, 0x01, 0x06, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, + 0xba, 0x48, 0x46, 0xba, 0x01, 0x43, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, + 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, + 0x18, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, + 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x27, 0x29, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x8f, 0x02, 0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, + 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0xcc, 0x01, 0x92, 0x41, 0x62, 0x32, 0x60, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x69, 0x6e, + 0x67, 0x20, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x2e, 0xba, 0x48, + 0x64, 0xba, 0x01, 0x61, 0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x12, 0x35, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x69, 0x66, 0x20, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2e, 0x1a, 0x1a, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x3e, 0x20, 0x30, 0x29, 0x52, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x91, 0x02, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, + 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0xd0, 0x01, 0x92, 0x41, 0x68, 0x32, 0x66, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x69, 0x6e, + 0x67, 0x20, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x73, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x20, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, + 0x74, 0x65, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, + 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x2e, 0xba, 0x48, 0x62, 0xba, 0x01, 0x5f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x34, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, + 0x20, 0x69, 0x66, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2e, 0x1a, 0x1a, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x8c, 0x01, 0x92, 0x41, 0x88, 0x01, 0x0a, 0x4f, + 0x2a, 0x0f, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x32, 0x33, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, + 0x69, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, + 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x69, + 0x6e, 0x20, 0x77, 0x65, 0x69, 0x2e, 0xd2, 0x01, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x32, + 0x35, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x22, 0x2c, 0x20, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x22, 0x3a, 0x20, 0x31, 0x20, 0x7d, 0x22, 0xee, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, + 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x80, 0x01, 0x92, 0x41, 0x7d, 0x0a, 0x42, 0x2a, 0x10, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x2e, + 0x47, 0x65, 0x74, 0x20, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, + 0x64, 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x32, 0x37, + 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, + 0x2c, 0x20, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, + 0x3a, 0x20, 0x22, 0x31, 0x22, 0x20, 0x7d, 0x22, 0x0e, 0x0a, 0x0c, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x90, 0x02, + 0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x42, 0xcd, 0x01, 0x92, 0x41, 0x63, 0x32, 0x61, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x2e, 0xba, 0x48, 0x64, 0xba, 0x01, + 0x61, 0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x35, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x69, 0x66, 0x20, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x65, 0x64, 0x2e, 0x1a, 0x1a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x20, + 0x30, 0x29, 0x52, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x22, 0xa2, 0x0b, 0x0a, 0x03, 0x42, 0x69, 0x64, 0x12, 0xa3, 0x02, 0x0a, 0x09, 0x74, 0x78, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x85, 0x02, 0x92, + 0x41, 0x78, 0x32, 0x64, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x8a, 0x01, 0x0f, 0x5b, 0x61, 0x2d, 0x66, 0x41, + 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0xba, 0x48, 0x86, 0x01, 0xba, 0x01, + 0x82, 0x01, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x36, 0x74, + 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, + 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x2e, 0x1a, 0x3d, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, + 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, + 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0x24, 0x27, + 0x29, 0x29, 0x20, 0x26, 0x26, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, + 0x20, 0x3e, 0x20, 0x30, 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0xed, + 0x01, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0xd4, 0x01, 0x92, 0x41, 0x76, 0x32, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x45, 0x54, 0x48, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, + 0x64, 0x64, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x74, 0x6f, 0x20, 0x70, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x8a, 0x01, 0x06, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0xba, 0x48, 0x58, 0xba, 0x01, + 0x55, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x2a, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x5d, + 0x2b, 0x24, 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0xb9, + 0x01, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x95, 0x01, 0x92, 0x41, 0x47, 0x32, 0x45, 0x4d, 0x61, 0x78, + 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, + 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, + 0x6e, 0x2e, 0xba, 0x48, 0x48, 0xba, 0x01, 0x45, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x25, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, + 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x0b, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0xc2, 0x01, 0x0a, 0x15, 0x64, + 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x8d, 0x01, 0x92, 0x41, 0x2d, + 0x32, 0x2b, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, + 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x73, 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x5a, + 0xba, 0x01, 0x57, 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x64, 0x65, 0x63, 0x61, + 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x13, 0x64, 0x65, 0x63, 0x61, + 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0xb8, 0x01, 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x87, 0x01, + 0x92, 0x41, 0x2b, 0x32, 0x29, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, + 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, + 0x65, 0x6e, 0x64, 0x73, 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, + 0x56, 0xba, 0x01, 0x53, 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2c, 0x64, 0x65, 0x63, 0x61, 0x79, + 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x11, 0x64, 0x65, 0x63, 0x61, 0x79, 0x45, 0x6e, + 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0xc8, 0x02, 0x92, 0x41, 0xc4, + 0x02, 0x0a, 0x71, 0x2a, 0x0b, 0x42, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x32, 0x40, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x62, 0x69, 0x64, 0x64, + 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, + 0x72, 0x20, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6e, 0x6f, 0x64, + 0x65, 0x2e, 0xd2, 0x01, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0xd2, 0x01, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0xd2, 0x01, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x32, 0xce, 0x01, 0x7b, 0x22, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x22, 0x66, 0x65, 0x34, 0x63, 0x62, 0x34, 0x37, 0x64, 0x62, 0x33, + 0x36, 0x33, 0x30, 0x35, 0x35, 0x31, 0x62, 0x65, 0x65, 0x64, 0x66, 0x62, 0x64, 0x30, 0x32, 0x61, + 0x37, 0x31, 0x65, 0x63, 0x63, 0x36, 0x39, 0x66, 0x64, 0x35, 0x39, 0x37, 0x35, 0x38, 0x65, 0x32, + 0x62, 0x61, 0x36, 0x39, 0x39, 0x36, 0x30, 0x36, 0x65, 0x32, 0x64, 0x35, 0x63, 0x37, 0x34, 0x32, + 0x38, 0x34, 0x66, 0x66, 0x61, 0x37, 0x22, 0x2c, 0x20, 0x22, 0x37, 0x31, 0x63, 0x31, 0x33, 0x34, + 0x38, 0x66, 0x32, 0x64, 0x37, 0x66, 0x66, 0x37, 0x65, 0x38, 0x31, 0x34, 0x66, 0x39, 0x63, 0x33, + 0x36, 0x31, 0x37, 0x39, 0x38, 0x33, 0x37, 0x30, 0x33, 0x34, 0x33, 0x35, 0x65, 0x61, 0x37, 0x34, + 0x34, 0x36, 0x64, 0x65, 0x34, 0x32, 0x30, 0x61, 0x65, 0x61, 0x63, 0x34, 0x38, 0x38, 0x62, 0x66, + 0x31, 0x64, 0x65, 0x33, 0x35, 0x37, 0x33, 0x37, 0x65, 0x38, 0x22, 0x5d, 0x2c, 0x20, 0x22, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x7d, 0x22, 0xf7, 0x09, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x95, 0x01, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x78, 0x92, 0x41, 0x75, 0x32, 0x61, 0x48, + 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, + 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, + 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x8a, 0x01, 0x0f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, + 0x34, 0x7d, 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x8f, 0x01, 0x0a, + 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x70, 0x92, 0x41, 0x6d, 0x32, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, + 0x66, 0x20, 0x45, 0x54, 0x48, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, + 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x67, 0x72, 0x65, 0x65, 0x64, + 0x20, 0x74, 0x6f, 0x20, 0x70, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0x52, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x6d, + 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x42, 0x4a, 0x92, 0x41, 0x47, 0x32, 0x45, 0x4d, 0x61, 0x78, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x2e, - 0xba, 0x48, 0x48, 0xba, 0x01, 0x45, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x12, 0x25, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, - 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x0b, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0xc2, 0x01, 0x0a, 0x15, 0x64, 0x65, 0x63, + 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x7b, 0x0a, + 0x13, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x5f, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4b, 0x92, 0x41, 0x48, 0x32, + 0x46, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x64, 0x42, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x7d, 0x0a, 0x16, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x47, 0x92, 0x41, 0x44, 0x32, + 0x42, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x62, + 0x69, 0x64, 0x2e, 0x52, 0x14, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x42, 0x69, 0x64, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x62, 0x0a, 0x11, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x35, 0x92, 0x41, 0x32, 0x32, 0x30, 0x48, 0x65, 0x78, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, + 0x66, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x10, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x9e, 0x01, + 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x6b, 0x92, 0x41, + 0x68, 0x32, 0x66, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x88, + 0x01, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x5d, 0x92, 0x41, 0x5a, 0x32, 0x58, + 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, + 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x64, 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x8d, 0x01, 0x92, 0x41, 0x2d, 0x32, 0x2b, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, - 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x73, 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x5a, 0xba, 0x01, - 0x57, 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, - 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xb8, 0x01, - 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x87, 0x01, 0x92, 0x41, + 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x42, 0x30, 0x92, 0x41, 0x2d, 0x32, 0x2b, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, + 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x13, 0x64, 0x65, 0x63, 0x61, + 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x5e, 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x42, 0x2e, 0x92, 0x41, 0x2b, 0x32, 0x29, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x65, 0x6e, - 0x64, 0x73, 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x56, 0xba, - 0x01, 0x53, 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2c, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, - 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, - 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, - 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x11, 0x64, 0x65, 0x63, 0x61, 0x79, 0x45, 0x6e, 0x64, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0xc8, 0x02, 0x92, 0x41, 0xc4, 0x02, 0x0a, - 0x71, 0x2a, 0x0b, 0x42, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x40, - 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, - 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, - 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0xd2, 0x01, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0xd2, 0x01, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0xd2, 0x01, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x32, 0xce, 0x01, 0x7b, 0x22, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, - 0x3a, 0x20, 0x5b, 0x22, 0x66, 0x65, 0x34, 0x63, 0x62, 0x34, 0x37, 0x64, 0x62, 0x33, 0x36, 0x33, - 0x30, 0x35, 0x35, 0x31, 0x62, 0x65, 0x65, 0x64, 0x66, 0x62, 0x64, 0x30, 0x32, 0x61, 0x37, 0x31, - 0x65, 0x63, 0x63, 0x36, 0x39, 0x66, 0x64, 0x35, 0x39, 0x37, 0x35, 0x38, 0x65, 0x32, 0x62, 0x61, - 0x36, 0x39, 0x39, 0x36, 0x30, 0x36, 0x65, 0x32, 0x64, 0x35, 0x63, 0x37, 0x34, 0x32, 0x38, 0x34, - 0x66, 0x66, 0x61, 0x37, 0x22, 0x2c, 0x20, 0x22, 0x37, 0x31, 0x63, 0x31, 0x33, 0x34, 0x38, 0x66, - 0x32, 0x64, 0x37, 0x66, 0x66, 0x37, 0x65, 0x38, 0x31, 0x34, 0x66, 0x39, 0x63, 0x33, 0x36, 0x31, - 0x37, 0x39, 0x38, 0x33, 0x37, 0x30, 0x33, 0x34, 0x33, 0x35, 0x65, 0x61, 0x37, 0x34, 0x34, 0x36, - 0x64, 0x65, 0x34, 0x32, 0x30, 0x61, 0x65, 0x61, 0x63, 0x34, 0x38, 0x38, 0x62, 0x66, 0x31, 0x64, - 0x65, 0x33, 0x35, 0x37, 0x33, 0x37, 0x65, 0x38, 0x22, 0x5d, 0x2c, 0x20, 0x22, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x34, - 0x35, 0x36, 0x7d, 0x22, 0xf7, 0x09, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x95, 0x01, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x78, 0x92, 0x41, 0x75, 0x32, 0x61, 0x48, 0x65, 0x78, - 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x6f, 0x66, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, - 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x8a, 0x01, - 0x0f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, - 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x0a, 0x62, - 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x70, 0x92, 0x41, 0x6d, 0x32, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, - 0x45, 0x54, 0x48, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, - 0x64, 0x65, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x67, 0x72, 0x65, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x70, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x52, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x6d, 0x0a, 0x0c, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x42, 0x4a, 0x92, 0x41, 0x47, 0x32, 0x45, 0x4d, 0x61, 0x78, 0x20, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x2e, 0x52, 0x0b, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x7b, 0x0a, 0x13, 0x72, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x5f, 0x64, 0x69, 0x67, 0x65, - 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4b, 0x92, 0x41, 0x48, 0x32, 0x46, 0x48, - 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, - 0x64, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x42, - 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x7d, 0x0a, 0x16, 0x72, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x47, 0x92, 0x41, 0x44, 0x32, 0x42, 0x48, - 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, - 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, - 0x61, 0x74, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x62, 0x69, 0x64, - 0x2e, 0x52, 0x14, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x42, 0x69, 0x64, 0x53, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x62, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x35, 0x92, 0x41, 0x32, 0x32, 0x30, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, - 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x9e, 0x01, 0x0a, 0x14, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x6b, 0x92, 0x41, 0x68, 0x32, - 0x66, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, - 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x72, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, - 0x65, 0x6e, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x88, 0x01, 0x0a, - 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x5d, 0x92, 0x41, 0x5a, 0x32, 0x58, 0x48, 0x65, - 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, - 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x64, 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, 0x79, - 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x42, 0x30, 0x92, 0x41, 0x2d, 0x32, 0x2b, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x64, - 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x5e, 0x0a, - 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x42, 0x2e, 0x92, 0x41, 0x2b, 0x32, - 0x29, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, - 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x65, 0x6e, 0x64, 0x73, - 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x11, 0x64, 0x65, 0x63, 0x61, - 0x79, 0x45, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x32, 0xae, 0x03, - 0x0a, 0x06, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x12, 0x53, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, - 0x42, 0x69, 0x64, 0x12, 0x11, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x1a, 0x18, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, - 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, - 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x62, 0x69, 0x64, 0x30, 0x01, 0x12, 0x70, 0x0a, - 0x0f, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x1b, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, - 0x70, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1c, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, - 0x70, 0x72, 0x65, 0x70, 0x61, 0x79, 0x2f, 0x7b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x12, - 0x6a, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x1a, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x62, 0x69, - 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x71, 0x0a, 0x0f, 0x47, - 0x65, 0x74, 0x4d, 0x69, 0x6e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1a, - 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x62, 0x69, 0x64, - 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, - 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x74, - 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x63, 0x65, 0x42, 0xb6, - 0x02, 0x92, 0x41, 0x7a, 0x12, 0x78, 0x0a, 0x0a, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x41, - 0x50, 0x49, 0x2a, 0x5d, 0x0a, 0x1b, 0x42, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x20, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x31, 0x2e, - 0x31, 0x12, 0x3e, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x76, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, - 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, - 0x45, 0x32, 0x0b, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x0a, 0x10, - 0x63, 0x6f, 0x6d, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x42, 0x0e, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, - 0x72, 0x69, 0x6d, 0x65, 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, - 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, - 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x62, 0x69, 0x64, - 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, - 0x0c, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, - 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x42, - 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, - 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x73, 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x11, 0x64, 0x65, + 0x63, 0x61, 0x79, 0x45, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x32, + 0xa8, 0x03, 0x0a, 0x06, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x12, 0x53, 0x0a, 0x07, 0x53, 0x65, + 0x6e, 0x64, 0x42, 0x69, 0x64, 0x12, 0x11, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x1a, 0x18, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, + 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, + 0x6e, 0x74, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, + 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x62, 0x69, 0x64, 0x30, 0x01, 0x12, + 0x6b, 0x0a, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x1c, 0x2e, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, + 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, + 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x2f, 0x7b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x12, 0x6c, 0x0a, 0x0a, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x1f, 0x2e, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x62, 0x69, + 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x67, + 0x65, 0x74, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x6e, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x4d, 0x69, 0x6e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x1a, 0x2e, 0x62, 0x69, + 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1d, 0x2e, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x74, 0x5f, 0x6d, + 0x69, 0x6e, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0xb6, 0x02, 0x92, 0x41, 0x7a, + 0x12, 0x78, 0x0a, 0x0a, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x41, 0x50, 0x49, 0x2a, 0x5d, + 0x0a, 0x1b, 0x42, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x31, 0x2e, 0x31, 0x12, 0x3e, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x62, 0x6c, 0x6f, 0x62, + 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x0b, 0x31, + 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, + 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x42, 0x69, + 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x44, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, + 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x62, 0x69, 0x64, 0x64, + 0x65, 0x72, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, + 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x42, 0x69, 0x64, + 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x42, 0x69, 0x64, 0x64, + 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x42, 0x69, 0x64, 0x64, 0x65, + 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x42, 0x69, 0x64, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -627,28 +759,34 @@ func file_bidderapi_v1_bidderapi_proto_rawDescGZIP() []byte { return file_bidderapi_v1_bidderapi_proto_rawDescData } -var file_bidderapi_v1_bidderapi_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_bidderapi_v1_bidderapi_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_bidderapi_v1_bidderapi_proto_goTypes = []interface{}{ - (*PrepayRequest)(nil), // 0: bidderapi.v1.PrepayRequest - (*PrepayResponse)(nil), // 1: bidderapi.v1.PrepayResponse - (*EmptyMessage)(nil), // 2: bidderapi.v1.EmptyMessage - (*Bid)(nil), // 3: bidderapi.v1.Bid - (*Commitment)(nil), // 4: bidderapi.v1.Commitment + (*DepositRequest)(nil), // 0: bidderapi.v1.DepositRequest + (*DepositResponse)(nil), // 1: bidderapi.v1.DepositResponse + (*EmptyMessage)(nil), // 2: bidderapi.v1.EmptyMessage + (*GetDepositRequest)(nil), // 3: bidderapi.v1.GetDepositRequest + (*Bid)(nil), // 4: bidderapi.v1.Bid + (*Commitment)(nil), // 5: bidderapi.v1.Commitment + (*wrapperspb.UInt64Value)(nil), // 6: google.protobuf.UInt64Value } var file_bidderapi_v1_bidderapi_proto_depIdxs = []int32{ - 3, // 0: bidderapi.v1.Bidder.SendBid:input_type -> bidderapi.v1.Bid - 0, // 1: bidderapi.v1.Bidder.PrepayAllowance:input_type -> bidderapi.v1.PrepayRequest - 2, // 2: bidderapi.v1.Bidder.GetAllowance:input_type -> bidderapi.v1.EmptyMessage - 2, // 3: bidderapi.v1.Bidder.GetMinAllowance:input_type -> bidderapi.v1.EmptyMessage - 4, // 4: bidderapi.v1.Bidder.SendBid:output_type -> bidderapi.v1.Commitment - 1, // 5: bidderapi.v1.Bidder.PrepayAllowance:output_type -> bidderapi.v1.PrepayResponse - 1, // 6: bidderapi.v1.Bidder.GetAllowance:output_type -> bidderapi.v1.PrepayResponse - 1, // 7: bidderapi.v1.Bidder.GetMinAllowance:output_type -> bidderapi.v1.PrepayResponse - 4, // [4:8] is the sub-list for method output_type - 0, // [0:4] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 6, // 0: bidderapi.v1.DepositRequest.windowNumber:type_name -> google.protobuf.UInt64Value + 6, // 1: bidderapi.v1.DepositRequest.blockNumber:type_name -> google.protobuf.UInt64Value + 6, // 2: bidderapi.v1.DepositResponse.windowNumber:type_name -> google.protobuf.UInt64Value + 6, // 3: bidderapi.v1.GetDepositRequest.windowNumber:type_name -> google.protobuf.UInt64Value + 4, // 4: bidderapi.v1.Bidder.SendBid:input_type -> bidderapi.v1.Bid + 0, // 5: bidderapi.v1.Bidder.Deposit:input_type -> bidderapi.v1.DepositRequest + 3, // 6: bidderapi.v1.Bidder.GetDeposit:input_type -> bidderapi.v1.GetDepositRequest + 2, // 7: bidderapi.v1.Bidder.GetMinDeposit:input_type -> bidderapi.v1.EmptyMessage + 5, // 8: bidderapi.v1.Bidder.SendBid:output_type -> bidderapi.v1.Commitment + 1, // 9: bidderapi.v1.Bidder.Deposit:output_type -> bidderapi.v1.DepositResponse + 1, // 10: bidderapi.v1.Bidder.GetDeposit:output_type -> bidderapi.v1.DepositResponse + 1, // 11: bidderapi.v1.Bidder.GetMinDeposit:output_type -> bidderapi.v1.DepositResponse + 8, // [8:12] is the sub-list for method output_type + 4, // [4:8] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_bidderapi_v1_bidderapi_proto_init() } @@ -658,7 +796,7 @@ func file_bidderapi_v1_bidderapi_proto_init() { } if !protoimpl.UnsafeEnabled { file_bidderapi_v1_bidderapi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PrepayRequest); i { + switch v := v.(*DepositRequest); i { case 0: return &v.state case 1: @@ -670,7 +808,7 @@ func file_bidderapi_v1_bidderapi_proto_init() { } } file_bidderapi_v1_bidderapi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PrepayResponse); i { + switch v := v.(*DepositResponse); i { case 0: return &v.state case 1: @@ -694,7 +832,7 @@ func file_bidderapi_v1_bidderapi_proto_init() { } } file_bidderapi_v1_bidderapi_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bid); i { + switch v := v.(*GetDepositRequest); i { case 0: return &v.state case 1: @@ -706,6 +844,18 @@ func file_bidderapi_v1_bidderapi_proto_init() { } } file_bidderapi_v1_bidderapi_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Bid); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bidderapi_v1_bidderapi_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Commitment); i { case 0: return &v.state @@ -724,7 +874,7 @@ func file_bidderapi_v1_bidderapi_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_bidderapi_v1_bidderapi_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/go/bidderapi/v1/bidderapi.pb.gw.go b/gen/go/bidderapi/v1/bidderapi.pb.gw.go index 910b8362..c9b33cd2 100644 --- a/gen/go/bidderapi/v1/bidderapi.pb.gw.go +++ b/gen/go/bidderapi/v1/bidderapi.pb.gw.go @@ -52,8 +52,12 @@ func request_Bidder_SendBid_0(ctx context.Context, marshaler runtime.Marshaler, } -func request_Bidder_PrepayAllowance_0(ctx context.Context, marshaler runtime.Marshaler, client BidderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PrepayRequest +var ( + filter_Bidder_Deposit_0 = &utilities.DoubleArray{Encoding: map[string]int{"amount": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Bidder_Deposit_0(ctx context.Context, marshaler runtime.Marshaler, client BidderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DepositRequest var metadata runtime.ServerMetadata var ( @@ -73,13 +77,20 @@ func request_Bidder_PrepayAllowance_0(ctx context.Context, marshaler runtime.Mar return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "amount", err) } - msg, err := client.PrepayAllowance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Bidder_Deposit_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Deposit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Bidder_PrepayAllowance_0(ctx context.Context, marshaler runtime.Marshaler, server BidderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PrepayRequest +func local_request_Bidder_Deposit_0(ctx context.Context, marshaler runtime.Marshaler, server BidderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DepositRequest var metadata runtime.ServerMetadata var ( @@ -99,43 +110,68 @@ func local_request_Bidder_PrepayAllowance_0(ctx context.Context, marshaler runti return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "amount", err) } - msg, err := server.PrepayAllowance(ctx, &protoReq) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Bidder_Deposit_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Deposit(ctx, &protoReq) return msg, metadata, err } -func request_Bidder_GetAllowance_0(ctx context.Context, marshaler runtime.Marshaler, client BidderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq EmptyMessage +var ( + filter_Bidder_GetDeposit_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Bidder_GetDeposit_0(ctx context.Context, marshaler runtime.Marshaler, client BidderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetDepositRequest var metadata runtime.ServerMetadata - msg, err := client.GetAllowance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Bidder_GetDeposit_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetDeposit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Bidder_GetAllowance_0(ctx context.Context, marshaler runtime.Marshaler, server BidderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq EmptyMessage +func local_request_Bidder_GetDeposit_0(ctx context.Context, marshaler runtime.Marshaler, server BidderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetDepositRequest var metadata runtime.ServerMetadata - msg, err := server.GetAllowance(ctx, &protoReq) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Bidder_GetDeposit_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetDeposit(ctx, &protoReq) return msg, metadata, err } -func request_Bidder_GetMinAllowance_0(ctx context.Context, marshaler runtime.Marshaler, client BidderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func request_Bidder_GetMinDeposit_0(ctx context.Context, marshaler runtime.Marshaler, client BidderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq EmptyMessage var metadata runtime.ServerMetadata - msg, err := client.GetMinAllowance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.GetMinDeposit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Bidder_GetMinAllowance_0(ctx context.Context, marshaler runtime.Marshaler, server BidderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { +func local_request_Bidder_GetMinDeposit_0(ctx context.Context, marshaler runtime.Marshaler, server BidderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq EmptyMessage var metadata runtime.ServerMetadata - msg, err := server.GetMinAllowance(ctx, &protoReq) + msg, err := server.GetMinDeposit(ctx, &protoReq) return msg, metadata, err } @@ -153,7 +189,7 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser return }) - mux.Handle("POST", pattern_Bidder_PrepayAllowance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_Bidder_Deposit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -161,12 +197,12 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/bidderapi.v1.Bidder/PrepayAllowance", runtime.WithHTTPPathPattern("/v1/bidder/prepay/{amount}")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/bidderapi.v1.Bidder/Deposit", runtime.WithHTTPPathPattern("/v1/bidder/deposit/{amount}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Bidder_PrepayAllowance_0(annotatedContext, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Bidder_Deposit_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { @@ -174,11 +210,11 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser return } - forward_Bidder_PrepayAllowance_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Bidder_Deposit_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Bidder_GetAllowance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Bidder_GetDeposit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -186,12 +222,12 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetAllowance", runtime.WithHTTPPathPattern("/v1/bidder/get_allowance")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetDeposit", runtime.WithHTTPPathPattern("/v1/bidder/get_deposit")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Bidder_GetAllowance_0(annotatedContext, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Bidder_GetDeposit_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { @@ -199,11 +235,11 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser return } - forward_Bidder_GetAllowance_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Bidder_GetDeposit_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Bidder_GetMinAllowance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Bidder_GetMinDeposit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -211,12 +247,12 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetMinAllowance", runtime.WithHTTPPathPattern("/v1/bidder/get_min_allowance")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetMinDeposit", runtime.WithHTTPPathPattern("/v1/bidder/get_min_deposit")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Bidder_GetMinAllowance_0(annotatedContext, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Bidder_GetMinDeposit_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { @@ -224,7 +260,7 @@ func RegisterBidderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser return } - forward_Bidder_GetMinAllowance_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Bidder_GetMinDeposit_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -291,69 +327,69 @@ func RegisterBidderHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli }) - mux.Handle("POST", pattern_Bidder_PrepayAllowance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_Bidder_Deposit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/bidderapi.v1.Bidder/PrepayAllowance", runtime.WithHTTPPathPattern("/v1/bidder/prepay/{amount}")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/bidderapi.v1.Bidder/Deposit", runtime.WithHTTPPathPattern("/v1/bidder/deposit/{amount}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Bidder_PrepayAllowance_0(annotatedContext, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Bidder_Deposit_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_Bidder_PrepayAllowance_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Bidder_Deposit_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Bidder_GetAllowance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Bidder_GetDeposit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetAllowance", runtime.WithHTTPPathPattern("/v1/bidder/get_allowance")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetDeposit", runtime.WithHTTPPathPattern("/v1/bidder/get_deposit")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Bidder_GetAllowance_0(annotatedContext, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Bidder_GetDeposit_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_Bidder_GetAllowance_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Bidder_GetDeposit_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Bidder_GetMinAllowance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Bidder_GetMinDeposit_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetMinAllowance", runtime.WithHTTPPathPattern("/v1/bidder/get_min_allowance")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/bidderapi.v1.Bidder/GetMinDeposit", runtime.WithHTTPPathPattern("/v1/bidder/get_min_deposit")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Bidder_GetMinAllowance_0(annotatedContext, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Bidder_GetMinDeposit_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_Bidder_GetMinAllowance_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Bidder_GetMinDeposit_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -363,19 +399,19 @@ func RegisterBidderHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli var ( pattern_Bidder_SendBid_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "bidder", "bid"}, "")) - pattern_Bidder_PrepayAllowance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "bidder", "prepay", "amount"}, "")) + pattern_Bidder_Deposit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "bidder", "deposit", "amount"}, "")) - pattern_Bidder_GetAllowance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "bidder", "get_allowance"}, "")) + pattern_Bidder_GetDeposit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "bidder", "get_deposit"}, "")) - pattern_Bidder_GetMinAllowance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "bidder", "get_min_allowance"}, "")) + pattern_Bidder_GetMinDeposit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "bidder", "get_min_deposit"}, "")) ) var ( forward_Bidder_SendBid_0 = runtime.ForwardResponseStream - forward_Bidder_PrepayAllowance_0 = runtime.ForwardResponseMessage + forward_Bidder_Deposit_0 = runtime.ForwardResponseMessage - forward_Bidder_GetAllowance_0 = runtime.ForwardResponseMessage + forward_Bidder_GetDeposit_0 = runtime.ForwardResponseMessage - forward_Bidder_GetMinAllowance_0 = runtime.ForwardResponseMessage + forward_Bidder_GetMinDeposit_0 = runtime.ForwardResponseMessage ) diff --git a/gen/go/bidderapi/v1/bidderapi_grpc.pb.go b/gen/go/bidderapi/v1/bidderapi_grpc.pb.go index fed53db5..e6972471 100644 --- a/gen/go/bidderapi/v1/bidderapi_grpc.pb.go +++ b/gen/go/bidderapi/v1/bidderapi_grpc.pb.go @@ -19,10 +19,10 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - Bidder_SendBid_FullMethodName = "/bidderapi.v1.Bidder/SendBid" - Bidder_PrepayAllowance_FullMethodName = "/bidderapi.v1.Bidder/PrepayAllowance" - Bidder_GetAllowance_FullMethodName = "/bidderapi.v1.Bidder/GetAllowance" - Bidder_GetMinAllowance_FullMethodName = "/bidderapi.v1.Bidder/GetMinAllowance" + Bidder_SendBid_FullMethodName = "/bidderapi.v1.Bidder/SendBid" + Bidder_Deposit_FullMethodName = "/bidderapi.v1.Bidder/Deposit" + Bidder_GetDeposit_FullMethodName = "/bidderapi.v1.Bidder/GetDeposit" + Bidder_GetMinDeposit_FullMethodName = "/bidderapi.v1.Bidder/GetMinDeposit" ) // BidderClient is the client API for Bidder service. @@ -33,18 +33,18 @@ type BidderClient interface { // // Send a bid to the bidder mev-commit node. SendBid(ctx context.Context, in *Bid, opts ...grpc.CallOption) (Bidder_SendBidClient, error) - // PrepayAllowance + // Deposit // - // PrepayAllowance is called by the bidder node to add prepaid allowance in the bidder registry. - PrepayAllowance(ctx context.Context, in *PrepayRequest, opts ...grpc.CallOption) (*PrepayResponse, error) - // GetAllowance + // Deposit is called by the bidder node to add deposit in the bidder registry. + Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*DepositResponse, error) + // GetDeposit // - // GetAllowance is called by the bidder to get its allowance in the bidder registry. - GetAllowance(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*PrepayResponse, error) - // GetMinAllowance + // GetDeposit is called by the bidder to get its deposit in the bidder registry. + GetDeposit(ctx context.Context, in *GetDepositRequest, opts ...grpc.CallOption) (*DepositResponse, error) + // GetMinDeposit // - // GetMinAllowance is called by the bidder to get the minimum allowance required in the bidder registry to make bids. - GetMinAllowance(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*PrepayResponse, error) + // GetMinDeposit is called by the bidder to get the minimum deposit required in the bidder registry to make bids. + GetMinDeposit(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*DepositResponse, error) } type bidderClient struct { @@ -87,27 +87,27 @@ func (x *bidderSendBidClient) Recv() (*Commitment, error) { return m, nil } -func (c *bidderClient) PrepayAllowance(ctx context.Context, in *PrepayRequest, opts ...grpc.CallOption) (*PrepayResponse, error) { - out := new(PrepayResponse) - err := c.cc.Invoke(ctx, Bidder_PrepayAllowance_FullMethodName, in, out, opts...) +func (c *bidderClient) Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*DepositResponse, error) { + out := new(DepositResponse) + err := c.cc.Invoke(ctx, Bidder_Deposit_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *bidderClient) GetAllowance(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*PrepayResponse, error) { - out := new(PrepayResponse) - err := c.cc.Invoke(ctx, Bidder_GetAllowance_FullMethodName, in, out, opts...) +func (c *bidderClient) GetDeposit(ctx context.Context, in *GetDepositRequest, opts ...grpc.CallOption) (*DepositResponse, error) { + out := new(DepositResponse) + err := c.cc.Invoke(ctx, Bidder_GetDeposit_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *bidderClient) GetMinAllowance(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*PrepayResponse, error) { - out := new(PrepayResponse) - err := c.cc.Invoke(ctx, Bidder_GetMinAllowance_FullMethodName, in, out, opts...) +func (c *bidderClient) GetMinDeposit(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*DepositResponse, error) { + out := new(DepositResponse) + err := c.cc.Invoke(ctx, Bidder_GetMinDeposit_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -122,18 +122,18 @@ type BidderServer interface { // // Send a bid to the bidder mev-commit node. SendBid(*Bid, Bidder_SendBidServer) error - // PrepayAllowance + // Deposit // - // PrepayAllowance is called by the bidder node to add prepaid allowance in the bidder registry. - PrepayAllowance(context.Context, *PrepayRequest) (*PrepayResponse, error) - // GetAllowance + // Deposit is called by the bidder node to add deposit in the bidder registry. + Deposit(context.Context, *DepositRequest) (*DepositResponse, error) + // GetDeposit // - // GetAllowance is called by the bidder to get its allowance in the bidder registry. - GetAllowance(context.Context, *EmptyMessage) (*PrepayResponse, error) - // GetMinAllowance + // GetDeposit is called by the bidder to get its deposit in the bidder registry. + GetDeposit(context.Context, *GetDepositRequest) (*DepositResponse, error) + // GetMinDeposit // - // GetMinAllowance is called by the bidder to get the minimum allowance required in the bidder registry to make bids. - GetMinAllowance(context.Context, *EmptyMessage) (*PrepayResponse, error) + // GetMinDeposit is called by the bidder to get the minimum deposit required in the bidder registry to make bids. + GetMinDeposit(context.Context, *EmptyMessage) (*DepositResponse, error) mustEmbedUnimplementedBidderServer() } @@ -144,14 +144,14 @@ type UnimplementedBidderServer struct { func (UnimplementedBidderServer) SendBid(*Bid, Bidder_SendBidServer) error { return status.Errorf(codes.Unimplemented, "method SendBid not implemented") } -func (UnimplementedBidderServer) PrepayAllowance(context.Context, *PrepayRequest) (*PrepayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method PrepayAllowance not implemented") +func (UnimplementedBidderServer) Deposit(context.Context, *DepositRequest) (*DepositResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") } -func (UnimplementedBidderServer) GetAllowance(context.Context, *EmptyMessage) (*PrepayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetAllowance not implemented") +func (UnimplementedBidderServer) GetDeposit(context.Context, *GetDepositRequest) (*DepositResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDeposit not implemented") } -func (UnimplementedBidderServer) GetMinAllowance(context.Context, *EmptyMessage) (*PrepayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetMinAllowance not implemented") +func (UnimplementedBidderServer) GetMinDeposit(context.Context, *EmptyMessage) (*DepositResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMinDeposit not implemented") } func (UnimplementedBidderServer) mustEmbedUnimplementedBidderServer() {} @@ -187,56 +187,56 @@ func (x *bidderSendBidServer) Send(m *Commitment) error { return x.ServerStream.SendMsg(m) } -func _Bidder_PrepayAllowance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PrepayRequest) +func _Bidder_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DepositRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(BidderServer).PrepayAllowance(ctx, in) + return srv.(BidderServer).Deposit(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Bidder_PrepayAllowance_FullMethodName, + FullMethod: Bidder_Deposit_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BidderServer).PrepayAllowance(ctx, req.(*PrepayRequest)) + return srv.(BidderServer).Deposit(ctx, req.(*DepositRequest)) } return interceptor(ctx, in, info, handler) } -func _Bidder_GetAllowance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EmptyMessage) +func _Bidder_GetDeposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetDepositRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(BidderServer).GetAllowance(ctx, in) + return srv.(BidderServer).GetDeposit(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Bidder_GetAllowance_FullMethodName, + FullMethod: Bidder_GetDeposit_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BidderServer).GetAllowance(ctx, req.(*EmptyMessage)) + return srv.(BidderServer).GetDeposit(ctx, req.(*GetDepositRequest)) } return interceptor(ctx, in, info, handler) } -func _Bidder_GetMinAllowance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Bidder_GetMinDeposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EmptyMessage) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(BidderServer).GetMinAllowance(ctx, in) + return srv.(BidderServer).GetMinDeposit(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Bidder_GetMinAllowance_FullMethodName, + FullMethod: Bidder_GetMinDeposit_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BidderServer).GetMinAllowance(ctx, req.(*EmptyMessage)) + return srv.(BidderServer).GetMinDeposit(ctx, req.(*EmptyMessage)) } return interceptor(ctx, in, info, handler) } @@ -249,16 +249,16 @@ var Bidder_ServiceDesc = grpc.ServiceDesc{ HandlerType: (*BidderServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "PrepayAllowance", - Handler: _Bidder_PrepayAllowance_Handler, + MethodName: "Deposit", + Handler: _Bidder_Deposit_Handler, }, { - MethodName: "GetAllowance", - Handler: _Bidder_GetAllowance_Handler, + MethodName: "GetDeposit", + Handler: _Bidder_GetDeposit_Handler, }, { - MethodName: "GetMinAllowance", - Handler: _Bidder_GetMinAllowance_Handler, + MethodName: "GetMinDeposit", + Handler: _Bidder_GetMinDeposit_Handler, }, }, Streams: []grpc.StreamDesc{ diff --git a/gen/go/handshake/v1/handshake.pb.go b/gen/go/handshake/v1/handshake.pb.go index 4ac8750e..bf49b1ef 100644 --- a/gen/go/handshake/v1/handshake.pb.go +++ b/gen/go/handshake/v1/handshake.pb.go @@ -20,20 +20,77 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Defines a message for SerializedKeys equivalent to the Go struct +type SerializedKeys struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PKEPublicKey []byte `protobuf:"bytes,1,opt,name=PKEPublicKey,proto3" json:"PKEPublicKey,omitempty"` // Maps Go's []byte to proto's bytes type + NIKEPublicKey []byte `protobuf:"bytes,2,opt,name=NIKEPublicKey,proto3" json:"NIKEPublicKey,omitempty"` // Maps Go's []byte to proto's bytes type +} + +func (x *SerializedKeys) Reset() { + *x = SerializedKeys{} + if protoimpl.UnsafeEnabled { + mi := &file_handshake_v1_handshake_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SerializedKeys) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SerializedKeys) ProtoMessage() {} + +func (x *SerializedKeys) ProtoReflect() protoreflect.Message { + mi := &file_handshake_v1_handshake_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SerializedKeys.ProtoReflect.Descriptor instead. +func (*SerializedKeys) Descriptor() ([]byte, []int) { + return file_handshake_v1_handshake_proto_rawDescGZIP(), []int{0} +} + +func (x *SerializedKeys) GetPKEPublicKey() []byte { + if x != nil { + return x.PKEPublicKey + } + return nil +} + +func (x *SerializedKeys) GetNIKEPublicKey() []byte { + if x != nil { + return x.NIKEPublicKey + } + return nil +} + type HandshakeReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PeerType string `protobuf:"bytes,1,opt,name=peer_type,json=peerType,proto3" json:"peer_type,omitempty"` - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` - Sig []byte `protobuf:"bytes,3,opt,name=sig,proto3" json:"sig,omitempty"` + PeerType string `protobuf:"bytes,1,opt,name=peer_type,json=peerType,proto3" json:"peer_type,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + Sig []byte `protobuf:"bytes,3,opt,name=sig,proto3" json:"sig,omitempty"` + Keys *SerializedKeys `protobuf:"bytes,4,opt,name=keys,proto3" json:"keys,omitempty"` // References another message type for the pointer to SerializedKeys } func (x *HandshakeReq) Reset() { *x = HandshakeReq{} if protoimpl.UnsafeEnabled { - mi := &file_handshake_v1_handshake_proto_msgTypes[0] + mi := &file_handshake_v1_handshake_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -46,7 +103,7 @@ func (x *HandshakeReq) String() string { func (*HandshakeReq) ProtoMessage() {} func (x *HandshakeReq) ProtoReflect() protoreflect.Message { - mi := &file_handshake_v1_handshake_proto_msgTypes[0] + mi := &file_handshake_v1_handshake_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -59,7 +116,7 @@ func (x *HandshakeReq) ProtoReflect() protoreflect.Message { // Deprecated: Use HandshakeReq.ProtoReflect.Descriptor instead. func (*HandshakeReq) Descriptor() ([]byte, []int) { - return file_handshake_v1_handshake_proto_rawDescGZIP(), []int{0} + return file_handshake_v1_handshake_proto_rawDescGZIP(), []int{1} } func (x *HandshakeReq) GetPeerType() string { @@ -83,6 +140,13 @@ func (x *HandshakeReq) GetSig() []byte { return nil } +func (x *HandshakeReq) GetKeys() *SerializedKeys { + if x != nil { + return x.Keys + } + return nil +} + type HandshakeResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -95,7 +159,7 @@ type HandshakeResp struct { func (x *HandshakeResp) Reset() { *x = HandshakeResp{} if protoimpl.UnsafeEnabled { - mi := &file_handshake_v1_handshake_proto_msgTypes[1] + mi := &file_handshake_v1_handshake_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -108,7 +172,7 @@ func (x *HandshakeResp) String() string { func (*HandshakeResp) ProtoMessage() {} func (x *HandshakeResp) ProtoReflect() protoreflect.Message { - mi := &file_handshake_v1_handshake_proto_msgTypes[1] + mi := &file_handshake_v1_handshake_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -121,7 +185,7 @@ func (x *HandshakeResp) ProtoReflect() protoreflect.Message { // Deprecated: Use HandshakeResp.ProtoReflect.Descriptor instead. func (*HandshakeResp) Descriptor() ([]byte, []int) { - return file_handshake_v1_handshake_proto_rawDescGZIP(), []int{1} + return file_handshake_v1_handshake_proto_rawDescGZIP(), []int{2} } func (x *HandshakeResp) GetObservedAddress() []byte { @@ -143,30 +207,39 @@ var File_handshake_v1_handshake_proto protoreflect.FileDescriptor var file_handshake_v1_handshake_proto_rawDesc = []byte{ 0x0a, 0x1c, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, - 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x53, 0x0a, 0x0c, - 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x12, 0x1b, 0x0a, 0x09, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x65, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, - 0x67, 0x22, 0x57, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6f, 0x62, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x65, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x42, 0xb9, 0x01, 0x0a, 0x10, 0x63, - 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x76, 0x31, 0x42, - 0x0e, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, - 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, - 0x69, 0x6d, 0x65, 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, - 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x68, - 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x68, 0x61, 0x6e, 0x64, - 0x73, 0x68, 0x61, 0x6b, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x58, 0x58, 0xaa, 0x02, 0x0c, - 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x48, - 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x48, 0x61, - 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, - 0x6b, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x5a, 0x0a, 0x0e, + 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x22, + 0x0a, 0x0c, 0x50, 0x4b, 0x45, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x50, 0x4b, 0x45, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x4e, 0x49, 0x4b, 0x45, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x4e, 0x49, 0x4b, 0x45, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x85, 0x01, 0x0a, 0x0c, 0x48, 0x61, 0x6e, + 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x65, 0x65, + 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x65, + 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x10, 0x0a, 0x03, + 0x73, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x12, 0x30, + 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x68, + 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, + 0x22, 0x57, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6f, 0x62, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, + 0x70, 0x65, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x65, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x42, 0xb9, 0x01, 0x0a, 0x10, 0x63, 0x6f, + 0x6d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, + 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, + 0x6d, 0x65, 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, 0x2d, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x68, 0x61, + 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x68, 0x61, 0x6e, 0x64, 0x73, + 0x68, 0x61, 0x6b, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x48, + 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x48, 0x61, + 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x48, 0x61, 0x6e, + 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, + 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -181,17 +254,19 @@ func file_handshake_v1_handshake_proto_rawDescGZIP() []byte { return file_handshake_v1_handshake_proto_rawDescData } -var file_handshake_v1_handshake_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_handshake_v1_handshake_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_handshake_v1_handshake_proto_goTypes = []interface{}{ - (*HandshakeReq)(nil), // 0: handshake.v1.HandshakeReq - (*HandshakeResp)(nil), // 1: handshake.v1.HandshakeResp + (*SerializedKeys)(nil), // 0: handshake.v1.SerializedKeys + (*HandshakeReq)(nil), // 1: handshake.v1.HandshakeReq + (*HandshakeResp)(nil), // 2: handshake.v1.HandshakeResp } var file_handshake_v1_handshake_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: handshake.v1.HandshakeReq.keys:type_name -> handshake.v1.SerializedKeys + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_handshake_v1_handshake_proto_init() } @@ -201,7 +276,7 @@ func file_handshake_v1_handshake_proto_init() { } if !protoimpl.UnsafeEnabled { file_handshake_v1_handshake_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HandshakeReq); i { + switch v := v.(*SerializedKeys); i { case 0: return &v.state case 1: @@ -213,6 +288,18 @@ func file_handshake_v1_handshake_proto_init() { } } file_handshake_v1_handshake_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HandshakeReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_handshake_v1_handshake_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HandshakeResp); i { case 0: return &v.state @@ -231,7 +318,7 @@ func file_handshake_v1_handshake_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_handshake_v1_handshake_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/go/keyexchange/v1/keyexchange.pb.go b/gen/go/keyexchange/v1/keyexchange.pb.go new file mode 100644 index 00000000..b82a8cec --- /dev/null +++ b/gen/go/keyexchange/v1/keyexchange.pb.go @@ -0,0 +1,240 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: keyexchange/v1/keyexchange.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Represents a message containing encrypted keys. +type EncryptedKeysMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EncryptedKeys [][]byte `protobuf:"bytes,1,rep,name=EncryptedKeys,proto3" json:"EncryptedKeys,omitempty"` + TimestampMessage []byte `protobuf:"bytes,2,opt,name=TimestampMessage,proto3" json:"TimestampMessage,omitempty"` +} + +func (x *EncryptedKeysMessage) Reset() { + *x = EncryptedKeysMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_keyexchange_v1_keyexchange_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EncryptedKeysMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EncryptedKeysMessage) ProtoMessage() {} + +func (x *EncryptedKeysMessage) ProtoReflect() protoreflect.Message { + mi := &file_keyexchange_v1_keyexchange_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EncryptedKeysMessage.ProtoReflect.Descriptor instead. +func (*EncryptedKeysMessage) Descriptor() ([]byte, []int) { + return file_keyexchange_v1_keyexchange_proto_rawDescGZIP(), []int{0} +} + +func (x *EncryptedKeysMessage) GetEncryptedKeys() [][]byte { + if x != nil { + return x.EncryptedKeys + } + return nil +} + +func (x *EncryptedKeysMessage) GetTimestampMessage() []byte { + if x != nil { + return x.TimestampMessage + } + return nil +} + +// Wraps a message and its signature. +type EKMWithSignature struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message []byte `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=Signature,proto3" json:"Signature,omitempty"` +} + +func (x *EKMWithSignature) Reset() { + *x = EKMWithSignature{} + if protoimpl.UnsafeEnabled { + mi := &file_keyexchange_v1_keyexchange_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EKMWithSignature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EKMWithSignature) ProtoMessage() {} + +func (x *EKMWithSignature) ProtoReflect() protoreflect.Message { + mi := &file_keyexchange_v1_keyexchange_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EKMWithSignature.ProtoReflect.Descriptor instead. +func (*EKMWithSignature) Descriptor() ([]byte, []int) { + return file_keyexchange_v1_keyexchange_proto_rawDescGZIP(), []int{1} +} + +func (x *EKMWithSignature) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *EKMWithSignature) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +var File_keyexchange_v1_keyexchange_proto protoreflect.FileDescriptor + +var file_keyexchange_v1_keyexchange_proto_rawDesc = []byte{ + 0x0a, 0x20, 0x6b, 0x65, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2f, 0x76, 0x31, + 0x2f, 0x6b, 0x65, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0b, 0x6b, 0x65, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, + 0x68, 0x0a, 0x14, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0d, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x2a, 0x0a, + 0x10, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x45, 0x4b, 0x4d, + 0x57, 0x69, 0x74, 0x68, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0xab, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x6b, 0x65, + 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x10, 0x4b, 0x65, 0x79, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x76, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x6b, 0x65, 0x79, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4b, 0x58, 0x58, 0xaa, + 0x02, 0x0b, 0x4b, 0x65, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0xca, 0x02, 0x0b, + 0x4b, 0x65, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0xe2, 0x02, 0x17, 0x4b, 0x65, + 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0b, 0x4b, 0x65, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_keyexchange_v1_keyexchange_proto_rawDescOnce sync.Once + file_keyexchange_v1_keyexchange_proto_rawDescData = file_keyexchange_v1_keyexchange_proto_rawDesc +) + +func file_keyexchange_v1_keyexchange_proto_rawDescGZIP() []byte { + file_keyexchange_v1_keyexchange_proto_rawDescOnce.Do(func() { + file_keyexchange_v1_keyexchange_proto_rawDescData = protoimpl.X.CompressGZIP(file_keyexchange_v1_keyexchange_proto_rawDescData) + }) + return file_keyexchange_v1_keyexchange_proto_rawDescData +} + +var file_keyexchange_v1_keyexchange_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_keyexchange_v1_keyexchange_proto_goTypes = []interface{}{ + (*EncryptedKeysMessage)(nil), // 0: keyexchange.EncryptedKeysMessage + (*EKMWithSignature)(nil), // 1: keyexchange.EKMWithSignature +} +var file_keyexchange_v1_keyexchange_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_keyexchange_v1_keyexchange_proto_init() } +func file_keyexchange_v1_keyexchange_proto_init() { + if File_keyexchange_v1_keyexchange_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_keyexchange_v1_keyexchange_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptedKeysMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_keyexchange_v1_keyexchange_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EKMWithSignature); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_keyexchange_v1_keyexchange_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_keyexchange_v1_keyexchange_proto_goTypes, + DependencyIndexes: file_keyexchange_v1_keyexchange_proto_depIdxs, + MessageInfos: file_keyexchange_v1_keyexchange_proto_msgTypes, + }.Build() + File_keyexchange_v1_keyexchange_proto = out.File + file_keyexchange_v1_keyexchange_proto_rawDesc = nil + file_keyexchange_v1_keyexchange_proto_goTypes = nil + file_keyexchange_v1_keyexchange_proto_depIdxs = nil +} diff --git a/gen/go/preconfirmation/v1/preconfirmation.pb.go b/gen/go/preconfirmation/v1/preconfirmation.pb.go index 2fc47e87..464c1418 100644 --- a/gen/go/preconfirmation/v1/preconfirmation.pb.go +++ b/gen/go/preconfirmation/v1/preconfirmation.pb.go @@ -32,6 +32,7 @@ type Bid struct { Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` DecayStartTimestamp int64 `protobuf:"varint,6,opt,name=decay_start_timestamp,json=decayStartTimestamp,proto3" json:"decay_start_timestamp,omitempty"` DecayEndTimestamp int64 `protobuf:"varint,7,opt,name=decay_end_timestamp,json=decayEndTimestamp,proto3" json:"decay_end_timestamp,omitempty"` + NikePublicKey []byte `protobuf:"bytes,8,opt,name=nike_public_key,json=nikePublicKey,proto3" json:"nike_public_key,omitempty"` } func (x *Bid) Reset() { @@ -115,6 +116,60 @@ func (x *Bid) GetDecayEndTimestamp() int64 { return 0 } +func (x *Bid) GetNikePublicKey() []byte { + if x != nil { + return x.NikePublicKey + } + return nil +} + +type EncryptedBid struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ciphertext []byte `protobuf:"bytes,1,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` +} + +func (x *EncryptedBid) Reset() { + *x = EncryptedBid{} + if protoimpl.UnsafeEnabled { + mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EncryptedBid) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EncryptedBid) ProtoMessage() {} + +func (x *EncryptedBid) ProtoReflect() protoreflect.Message { + mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EncryptedBid.ProtoReflect.Descriptor instead. +func (*EncryptedBid) Descriptor() ([]byte, []int) { + return file_preconfirmation_v1_preconfirmation_proto_rawDescGZIP(), []int{1} +} + +func (x *EncryptedBid) GetCiphertext() []byte { + if x != nil { + return x.Ciphertext + } + return nil +} + type PreConfirmation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -124,12 +179,13 @@ type PreConfirmation struct { Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` ProviderAddress []byte `protobuf:"bytes,4,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty"` + SharedSecret []byte `protobuf:"bytes,5,opt,name=shared_secret,json=sharedSecret,proto3" json:"shared_secret,omitempty"` } func (x *PreConfirmation) Reset() { *x = PreConfirmation{} if protoimpl.UnsafeEnabled { - mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[1] + mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -142,7 +198,7 @@ func (x *PreConfirmation) String() string { func (*PreConfirmation) ProtoMessage() {} func (x *PreConfirmation) ProtoReflect() protoreflect.Message { - mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[1] + mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -155,7 +211,7 @@ func (x *PreConfirmation) ProtoReflect() protoreflect.Message { // Deprecated: Use PreConfirmation.ProtoReflect.Descriptor instead. func (*PreConfirmation) Descriptor() ([]byte, []int) { - return file_preconfirmation_v1_preconfirmation_proto_rawDescGZIP(), []int{1} + return file_preconfirmation_v1_preconfirmation_proto_rawDescGZIP(), []int{2} } func (x *PreConfirmation) GetBid() *Bid { @@ -186,14 +242,84 @@ func (x *PreConfirmation) GetProviderAddress() []byte { return nil } +func (x *PreConfirmation) GetSharedSecret() []byte { + if x != nil { + return x.SharedSecret + } + return nil +} + +type EncryptedPreConfirmation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Commitment []byte `protobuf:"bytes,1,opt,name=commitment,proto3" json:"commitment,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + CommitmentIndex []byte `protobuf:"bytes,3,opt,name=commitmentIndex,proto3" json:"commitmentIndex,omitempty"` +} + +func (x *EncryptedPreConfirmation) Reset() { + *x = EncryptedPreConfirmation{} + if protoimpl.UnsafeEnabled { + mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EncryptedPreConfirmation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EncryptedPreConfirmation) ProtoMessage() {} + +func (x *EncryptedPreConfirmation) ProtoReflect() protoreflect.Message { + mi := &file_preconfirmation_v1_preconfirmation_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EncryptedPreConfirmation.ProtoReflect.Descriptor instead. +func (*EncryptedPreConfirmation) Descriptor() ([]byte, []int) { + return file_preconfirmation_v1_preconfirmation_proto_rawDescGZIP(), []int{3} +} + +func (x *EncryptedPreConfirmation) GetCommitment() []byte { + if x != nil { + return x.Commitment + } + return nil +} + +func (x *EncryptedPreConfirmation) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *EncryptedPreConfirmation) GetCommitmentIndex() []byte { + if x != nil { + return x.CommitmentIndex + } + return nil +} + var File_preconfirmation_v1_preconfirmation_proto protoreflect.FileDescriptor var file_preconfirmation_v1_preconfirmation_proto_rawDesc = []byte{ 0x0a, 0x28, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x70, 0x72, 0x65, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x22, 0xfa, - 0x01, 0x0a, 0x03, 0x42, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x22, 0xa2, + 0x02, 0x0a, 0x03, 0x42, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, @@ -208,32 +334,49 @@ var file_preconfirmation_v1_preconfirmation_proto_rawDesc = []byte{ 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x64, 0x65, 0x63, 0x61, 0x79, 0x45, - 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9d, 0x01, 0x0a, 0x0f, - 0x50, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x29, 0x0a, 0x03, 0x62, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, - 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x69, 0x64, 0x52, 0x03, 0x62, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, - 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, - 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0xe9, 0x01, 0x0a, 0x16, - 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x14, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x50, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, - 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x65, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x3b, 0x70, - 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x31, - 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x12, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x50, 0x72, - 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, - 0xe2, 0x02, 0x1e, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0xea, 0x02, 0x13, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x0f, 0x6e, + 0x69, 0x6b, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6e, 0x69, 0x6b, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x22, 0x2e, 0x0a, 0x0c, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, + 0x42, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x03, 0x62, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x52, 0x03, 0x62, + 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x64, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x82, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, + 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0xe9, 0x01, + 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x14, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, + 0x6d, 0x65, 0x76, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x65, 0x76, 0x2d, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, + 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, + 0x3b, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x12, 0x50, 0x72, 0x65, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, + 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5c, + 0x56, 0x31, 0xe2, 0x02, 0x1e, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x13, 0x50, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -248,10 +391,12 @@ func file_preconfirmation_v1_preconfirmation_proto_rawDescGZIP() []byte { return file_preconfirmation_v1_preconfirmation_proto_rawDescData } -var file_preconfirmation_v1_preconfirmation_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_preconfirmation_v1_preconfirmation_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_preconfirmation_v1_preconfirmation_proto_goTypes = []interface{}{ - (*Bid)(nil), // 0: preconfirmation.v1.Bid - (*PreConfirmation)(nil), // 1: preconfirmation.v1.PreConfirmation + (*Bid)(nil), // 0: preconfirmation.v1.Bid + (*EncryptedBid)(nil), // 1: preconfirmation.v1.EncryptedBid + (*PreConfirmation)(nil), // 2: preconfirmation.v1.PreConfirmation + (*EncryptedPreConfirmation)(nil), // 3: preconfirmation.v1.EncryptedPreConfirmation } var file_preconfirmation_v1_preconfirmation_proto_depIdxs = []int32{ 0, // 0: preconfirmation.v1.PreConfirmation.bid:type_name -> preconfirmation.v1.Bid @@ -281,6 +426,18 @@ func file_preconfirmation_v1_preconfirmation_proto_init() { } } file_preconfirmation_v1_preconfirmation_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptedBid); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_preconfirmation_v1_preconfirmation_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PreConfirmation); i { case 0: return &v.state @@ -292,6 +449,18 @@ func file_preconfirmation_v1_preconfirmation_proto_init() { return nil } } + file_preconfirmation_v1_preconfirmation_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptedPreConfirmation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -299,7 +468,7 @@ func file_preconfirmation_v1_preconfirmation_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_preconfirmation_v1_preconfirmation_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/openapi/bidderapi/v1/bidderapi.swagger.yaml b/gen/openapi/bidderapi/v1/bidderapi.swagger.yaml index 5ad93c7f..a58adcec 100644 --- a/gen/openapi/bidderapi/v1/bidderapi.swagger.yaml +++ b/gen/openapi/bidderapi/v1/bidderapi.swagger.yaml @@ -37,54 +37,73 @@ paths: required: true schema: $ref: '#/definitions/bidderapiv1Bid' - /v1/bidder/get_allowance: - get: - summary: GetAllowance - description: GetAllowance is called by the bidder to get its allowance in the bidder registry. - operationId: Bidder_GetAllowance + /v1/bidder/deposit/{amount}: + post: + summary: Deposit + description: Deposit is called by the bidder node to add deposit in the bidder registry. + operationId: Bidder_Deposit responses: "200": description: A successful response. schema: - $ref: '#/definitions/v1PrepayResponse' + $ref: '#/definitions/v1DepositResponse' default: description: An unexpected error response. schema: $ref: '#/definitions/googlerpcStatus' - /v1/bidder/get_min_allowance: + parameters: + - name: amount + description: Amount of ETH to be deposited in wei. + in: path + required: true + type: string + - name: windowNumber + description: Optional window number for querying deposit. If not specified, the current block number is used. + in: query + required: false + type: string + format: uint64 + - name: blockNumber + description: Optional block number for querying deposit. If specified, calculate window based on this block number. + in: query + required: false + type: string + format: uint64 + /v1/bidder/get_deposit: get: - summary: GetMinAllowance - description: GetMinAllowance is called by the bidder to get the minimum allowance required in the bidder registry to make bids. - operationId: Bidder_GetMinAllowance + summary: GetDeposit + description: GetDeposit is called by the bidder to get its deposit in the bidder registry. + operationId: Bidder_GetDeposit responses: "200": description: A successful response. schema: - $ref: '#/definitions/v1PrepayResponse' + $ref: '#/definitions/v1DepositResponse' default: description: An unexpected error response. schema: $ref: '#/definitions/googlerpcStatus' - /v1/bidder/prepay/{amount}: - post: - summary: PrepayAllowance - description: PrepayAllowance is called by the bidder node to add prepaid allowance in the bidder registry. - operationId: Bidder_PrepayAllowance + parameters: + - name: windowNumber + description: Optional window number for querying deposits. If not specified, the current block number is used. + in: query + required: false + type: string + format: uint64 + /v1/bidder/get_min_deposit: + get: + summary: GetMinDeposit + description: GetMinDeposit is called by the bidder to get the minimum deposit required in the bidder registry to make bids. + operationId: Bidder_GetMinDeposit responses: "200": description: A successful response. schema: - $ref: '#/definitions/v1PrepayResponse' + $ref: '#/definitions/v1DepositResponse' default: description: An unexpected error response. schema: $ref: '#/definitions/googlerpcStatus' - parameters: - - name: amount - description: Amount of ETH to be prepaid in wei. - in: path - required: true - type: string definitions: bidderapiv1Bid: type: object @@ -181,12 +200,16 @@ definitions: type: string format: int64 description: Timestamp at which the bid ends decaying. - v1PrepayResponse: + v1DepositResponse: type: object example: amount: "1000000000000000000" + windowNumber: "1" properties: amount: type: string - description: Get prepaid allowance for bidder in the bidder registry. - title: Prepay response + windowNumber: + type: string + format: uint64 + description: Get deposit for bidder in the bidder registry. + title: Deposit response diff --git a/go.mod b/go.mod index 50b4e811..ecb039e7 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/libp2p/go-msgio v0.3.0 github.com/multiformats/go-multiaddr v0.12.2 github.com/multiformats/go-multiaddr-dns v0.3.1 - github.com/primevprotocol/contracts-abi v0.2.3 + github.com/primevprotocol/contracts-abi v0.2.4-0.20240423171419-a959d3727058 github.com/prometheus/client_golang v1.18.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.27.1 diff --git a/go.sum b/go.sum index 5d064133..f9f6c1e1 100644 --- a/go.sum +++ b/go.sum @@ -334,8 +334,24 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/primevprotocol/contracts-abi v0.2.0 h1:fzADMI4pVpLVOagZ6K1dOeibYDc89TNyuwymSKzU9ls= +github.com/primevprotocol/contracts-abi v0.2.0/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= github.com/primevprotocol/contracts-abi v0.2.3 h1:4jZGl0hrEdP1yensIrs4Sh2DNU6EG+BBNtKvrH5liR0= github.com/primevprotocol/contracts-abi v0.2.3/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240319201845-0e7b67b8e539 h1:gEge1UMJF88weugg6nUvUVbMu8byxASqHbpSKUs8ElE= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240319201845-0e7b67b8e539/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240328210357-00b3c4b870a6 h1:tyEa3/qAkvc21mQg/diqNP5iVAmc4acF+1P/5/sM3Ag= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240328210357-00b3c4b870a6/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240401131709-dcd3b451314a h1:bSmtNx7BXLtnKiOP4Ku8xpKIumScyBl96bb5hcBvvV8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240401131709-dcd3b451314a/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240410153636-21ff37a788ad h1:Gvv4EwVerh4gpBBT4uU7gMHhuSGwFnXDZUQxsxWQ0ls= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240410153636-21ff37a788ad/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240418181518-36932433f9a8 h1:XGHghC8zN/3neOpP7TWndrtgcLI1Vu2vU0PQFCQSjrM= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240418181518-36932433f9a8/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240419134844-6dd1a8c7bf60 h1:FEYkczFrI/CwTZRm0wARuWshpn185BEw4uFAKxlqQBg= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240419134844-6dd1a8c7bf60/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240423171419-a959d3727058 h1:IG9XeHGs8/S9tW/4dfLosOa6NL0LGODp+FvWVIWNIBE= +github.com/primevprotocol/contracts-abi v0.2.4-0.20240423171419-a959d3727058/go.mod h1:dE2KkvEqC+itvPa3SCrqQfvH5Hfnfn6omNRwWDTdIp8= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= diff --git a/integrationtest/bidder/main.go b/integrationtest/bidder/main.go index 622e78e5..af158508 100644 --- a/integrationtest/bidder/main.go +++ b/integrationtest/bidder/main.go @@ -188,7 +188,7 @@ func main() { defer ticker.Stop() for { - err = checkOrPrepay(bidderClient, logger) + err = checkOrDeposit(bidderClient, logger) if err != nil { logger.Error("failed to check or stake", "error", err) } @@ -213,52 +213,52 @@ func main() { wg.Wait() } -func checkOrPrepay( +func checkOrDeposit( bidderClient pb.BidderClient, logger *slog.Logger, ) error { - allowance, err := bidderClient.GetAllowance(context.Background(), &pb.EmptyMessage{}) + deposit, err := bidderClient.GetDeposit(context.Background(), &pb.GetDepositRequest{}) if err != nil { - logger.Error("failed to get allowance", "error", err) + logger.Error("failed to get deposit", "error", err) return err } - logger.Info("prepaid allowance", "amount", allowance.Amount) + logger.Info("deposited", "amount", deposit.Amount) - minAllowance, err := bidderClient.GetMinAllowance(context.Background(), &pb.EmptyMessage{}) + minDeposit, err := bidderClient.GetMinDeposit(context.Background(), &pb.EmptyMessage{}) if err != nil { - logger.Error("failed to get min allowance", "error", err) + logger.Error("failed to get min deposit", "error", err) return err } - allowanceAmt, set := big.NewInt(0).SetString(allowance.Amount, 10) + depositAmt, set := big.NewInt(0).SetString(deposit.Amount, 10) if !set { - logger.Error("failed to parse allowance amount") - return errors.New("failed to parse allowance amount") + logger.Error("failed to parse deposit amount") + return errors.New("failed to parse deposit amount") } - minAllowanceAmt, set := big.NewInt(0).SetString(minAllowance.Amount, 10) + minDepositAmt, set := big.NewInt(0).SetString(minDeposit.Amount, 10) if !set { - logger.Error("failed to parse min allowance amount") - return errors.New("failed to parse min allowance amount") + logger.Error("failed to parse min deposit amount") + return errors.New("failed to parse min deposit amount") } - if allowanceAmt.Cmp(minAllowanceAmt) > 0 { + if depositAmt.Cmp(minDepositAmt) > 0 { logger.Error("bidder already has balance") return nil } - topup := big.NewInt(0).Mul(minAllowanceAmt, big.NewInt(10)) + topup := big.NewInt(0).Mul(minDepositAmt, big.NewInt(10)) - _, err = bidderClient.PrepayAllowance(context.Background(), &pb.PrepayRequest{ + _, err = bidderClient.Deposit(context.Background(), &pb.DepositRequest{ Amount: topup.String(), }) if err != nil { - logger.Error("failed to prepay allowance", "error", err) + logger.Error("failed to deposit", "error", err) return err } - logger.Info("prepaid allowance", "amount", topup.String()) + logger.Info("deposit", "amount", topup.String()) return nil } diff --git a/integrationtest/real-bidder/main.go b/integrationtest/real-bidder/main.go index 227b69c2..02f7b1b9 100644 --- a/integrationtest/real-bidder/main.go +++ b/integrationtest/real-bidder/main.go @@ -82,7 +82,7 @@ func main() { )) registry := prometheus.NewRegistry() - registry.MustRegister(receivedPreconfs, sentBids) + registry.MustRegister(receivedPreconfs, sentBids, sendBidDuration) router := http.NewServeMux() router.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) @@ -124,11 +124,11 @@ func main() { wg.Add(1) go func() { - ticker := time.NewTicker(1 * time.Hour) + ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() for { - err = checkOrPrepay(bidderClient, logger) + err = checkOrDeposit(bidderClient, logger) if err != nil { logger.Error("failed to check or stake", "err", err) } @@ -136,25 +136,71 @@ func main() { } }() + type blockWithTxns struct { + blockNum int64 + txns []string + } + + newBlockChan := make(chan blockWithTxns, 1) + wg.Add(1) go func(logger *slog.Logger) { defer wg.Done() + + currentBlkNum := int64(0) for { block, blkNum, err := RetreivedBlock(rpcClient) if err != nil || len(block) == 0 { logger.Error("failed to get block", "err", err) - } else { - throtle := time.Duration(12000*time.Millisecond) / time.Duration(len(block)) - logger.Info("thortling set", "throtle", throtle.String()) - bundle := 1 - for j := 0; j < len(block); j += bundle { - bundle := rand.Intn(10) - err = sendBid(bidderClient, logger, rpcClient, block[j:j+bundle], int64(blkNum), (time.Now().UnixMilli())-10000, (time.Now().UnixMilli())) - if err != nil { - logger.Error("failed to send bid", "err", err) - } - time.Sleep(throtle) - } + } + + if currentBlkNum == blkNum { + time.Sleep(1 * time.Second) + continue + } + + currentBlkNum = blkNum + newBlockChan <- blockWithTxns{ + blockNum: blkNum, + txns: block, + } + } + }(logger) + + wg.Add(1) + go func(logger *slog.Logger) { + defer wg.Done() + ticker := time.NewTicker(200 * time.Millisecond) + currentBlock := blockWithTxns{} + for { + select { + case block := <-newBlockChan: + currentBlock = block + case <-ticker.C: + } + + if len(currentBlock.txns) == 0 { + continue + } + + bundleLen := rand.Intn(10) + bundleStart := rand.Intn(len(currentBlock.txns)) + bundleEnd := bundleStart + bundleLen + if bundleEnd > len(currentBlock.txns) { + bundleEnd = len(currentBlock.txns) - 1 + } + + err = sendBid( + bidderClient, + logger, + rpcClient, + currentBlock.txns[bundleStart:bundleEnd], + currentBlock.blockNum, + (time.Now().UnixMilli())-10000, + (time.Now().UnixMilli())+10000, + ) + if err != nil { + logger.Error("failed to send bid", "err", err) } } }(logger) @@ -181,52 +227,52 @@ func RetreivedBlock(rpcClient *ethclient.Client) ([]string, int64, error) { return blockTxns, int64(blkNum), nil } -func checkOrPrepay( +func checkOrDeposit( bidderClient pb.BidderClient, logger *slog.Logger, ) error { - allowance, err := bidderClient.GetAllowance(context.Background(), &pb.EmptyMessage{}) + deposit, err := bidderClient.GetDeposit(context.Background(), &pb.GetDepositRequest{}) if err != nil { - logger.Error("failed to get allowance", "err", err) + logger.Error("failed to get deposit", "err", err) return err } - logger.Info("prepaid allowance", "amount", allowance.Amount) + logger.Info("deposit", "amount", deposit.Amount) - minAllowance, err := bidderClient.GetMinAllowance(context.Background(), &pb.EmptyMessage{}) + minDeposit, err := bidderClient.GetMinDeposit(context.Background(), &pb.EmptyMessage{}) if err != nil { - logger.Error("failed to get min allowance", "err", err) + logger.Error("failed to get min deposit", "err", err) return err } - allowanceAmt, set := big.NewInt(0).SetString(allowance.Amount, 10) + depositAmt, set := big.NewInt(0).SetString(deposit.Amount, 10) if !set { - logger.Error("failed to parse allowance amount") - return errors.New("failed to parse allowance amount") + logger.Error("failed to parse deposit amount") + return errors.New("failed to parse deposit amount") } - minAllowanceAmt, set := big.NewInt(0).SetString(minAllowance.Amount, 10) + minDepositAmt, set := big.NewInt(0).SetString(minDeposit.Amount, 10) if !set { - logger.Error("failed to parse min allowance amount") - return errors.New("failed to parse min allowance amount") + logger.Error("failed to parse min deposit amount") + return errors.New("failed to parse min deposit amount") } - if allowanceAmt.Cmp(minAllowanceAmt) > 0 { + if depositAmt.Cmp(minDepositAmt) > 0 { logger.Error("bidder already has balance") return nil } - topup := big.NewInt(0).Mul(minAllowanceAmt, big.NewInt(10)) + topup := big.NewInt(0).Mul(minDepositAmt, big.NewInt(10)) - _, err = bidderClient.PrepayAllowance(context.Background(), &pb.PrepayRequest{ + _, err = bidderClient.Deposit(context.Background(), &pb.DepositRequest{ Amount: topup.String(), }) if err != nil { - logger.Error("failed to prepay allowance", "err", err) + logger.Error("failed to deposit", "err", err) return err } - logger.Info("prepaid allowance", "amount", topup.String()) + logger.Info("deposit", "amount", topup.String()) return nil } diff --git a/messages/handshake/v1/handshake.proto b/messages/handshake/v1/handshake.proto index 3eabd107..5bcd666f 100644 --- a/messages/handshake/v1/handshake.proto +++ b/messages/handshake/v1/handshake.proto @@ -2,10 +2,17 @@ syntax = "proto3"; package handshake.v1; +// Defines a message for SerializedKeys equivalent to the Go struct +message SerializedKeys { + bytes PKEPublicKey = 1; // Maps Go's []byte to proto's bytes type + bytes NIKEPublicKey = 2; // Maps Go's []byte to proto's bytes type +} + message HandshakeReq { string peer_type = 1; string token = 2; bytes sig = 3; + SerializedKeys keys = 4; // References another message type for the pointer to SerializedKeys }; message HandshakeResp { diff --git a/messages/keyexchange/v1/keyexchange.proto b/messages/keyexchange/v1/keyexchange.proto new file mode 100644 index 00000000..b0445dff --- /dev/null +++ b/messages/keyexchange/v1/keyexchange.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package keyexchange; + +// Represents a message containing encrypted keys. +message EncryptedKeysMessage { + repeated bytes EncryptedKeys = 1; + bytes TimestampMessage = 2; +} + +// Wraps a message and its signature. +message EKMWithSignature { + bytes Message = 1; + bytes Signature = 2; +} diff --git a/messages/preconfirmation/v1/preconfirmation.proto b/messages/preconfirmation/v1/preconfirmation.proto index 4c82f050..5a6e0fc8 100644 --- a/messages/preconfirmation/v1/preconfirmation.proto +++ b/messages/preconfirmation/v1/preconfirmation.proto @@ -10,11 +10,23 @@ message Bid { bytes signature = 5; int64 decay_start_timestamp = 6; int64 decay_end_timestamp = 7; + bytes nike_public_key = 8; }; +message EncryptedBid { + bytes ciphertext = 1; +} + message PreConfirmation { Bid bid = 1; bytes digest = 2; bytes signature = 3; bytes provider_address = 4; + bytes shared_secret = 5; }; + +message EncryptedPreConfirmation { + bytes commitment = 1; + bytes signature = 2; + bytes commitmentIndex = 3; +} \ No newline at end of file diff --git a/pkg/contracts/bidder_registry/bidder_registry.go b/pkg/contracts/bidder_registry/bidder_registry.go index 8913550c..c938d3e0 100644 --- a/pkg/contracts/bidder_registry/bidder_registry.go +++ b/pkg/contracts/bidder_registry/bidder_registry.go @@ -22,17 +22,20 @@ var bidderRegistryABI = func() abi.ABI { } type Interface interface { - // PrepayAllowance registers a bidder with the bidder_registry contract. - PrepayAllowance(ctx context.Context, amount *big.Int) error - // GetAllowance returns the stake of a bidder. - GetAllowance(ctx context.Context, address common.Address) (*big.Int, error) - // GetMinAllowance returns the minimum stake required to register as a bidder. - GetMinAllowance(ctx context.Context) (*big.Int, error) + // DepositForSpecificWindow registers a bidder with the bidder_registry contract for a specific window. + DepositForSpecificWindow(ctx context.Context, amount, window *big.Int) error + // GetDeposit returns the stake of a bidder. + GetDeposit(ctx context.Context, address common.Address, window *big.Int) (*big.Int, error) + // GetMinDeposit returns the minimum stake required to register as a bidder. + GetMinDeposit(ctx context.Context) (*big.Int, error) // CheckBidderRegistred returns true if bidder is registered - CheckBidderAllowance(ctx context.Context, address common.Address) bool + CheckBidderDeposit(ctx context.Context, address common.Address, window, blocksPerWindow *big.Int) bool + // WithdrawDeposit withdraws the stake of a bidder. + WithdrawDeposit(ctx context.Context, window *big.Int) error } type bidderRegistryContract struct { + owner common.Address bidderRegistryABI abi.ABI bidderRegistryContractAddr common.Address client evmclient.Interface @@ -40,11 +43,13 @@ type bidderRegistryContract struct { } func New( + owner common.Address, bidderRegistryContractAddr common.Address, client evmclient.Interface, logger *slog.Logger, ) Interface { return &bidderRegistryContract{ + owner: owner, bidderRegistryABI: bidderRegistryABI(), bidderRegistryContractAddr: bidderRegistryContractAddr, client: client, @@ -52,8 +57,8 @@ func New( } } -func (r *bidderRegistryContract) PrepayAllowance(ctx context.Context, amount *big.Int) error { - callData, err := r.bidderRegistryABI.Pack("prepay") +func (r *bidderRegistryContract) DepositForSpecificWindow(ctx context.Context, amount, window *big.Int) error { + callData, err := r.bidderRegistryABI.Pack("depositForSpecificWindow", window) if err != nil { r.logger.Error("error packing call data", "error", err) return err @@ -75,23 +80,42 @@ func (r *bidderRegistryContract) PrepayAllowance(ctx context.Context, amount *bi if receipt.Status != types.ReceiptStatusSuccessful { r.logger.Error( - "prepay failed for bidder registry", + "deposit failed for bidder registry", "txnHash", txnHash, "receipt", receipt, ) return err } - r.logger.Info("prepay successful for bidder registry", "txnHash", txnHash) + var bidderRegistered struct { + Bidder common.Address + DepositedAmount *big.Int + WindowNumber *big.Int + } + for _, log := range receipt.Logs { + if len(log.Topics) > 1 { + bidderRegistered.Bidder = common.HexToAddress(log.Topics[1].Hex()) + } + + err := r.bidderRegistryABI.UnpackIntoInterface(&bidderRegistered, "BidderRegistered", log.Data) + if err != nil { + r.logger.Debug("failed to unpack event", "err", err) + continue + } + r.logger.Info("bidder registered", "address", bidderRegistered.Bidder, "depositedAmount", bidderRegistered.DepositedAmount.String(), "windowNumber", bidderRegistered.WindowNumber.Int64()) + } + + r.logger.Info("deposit successful for bidder registry", "txnHash", txnHash, "bidder", bidderRegistered.Bidder) return nil } -func (r *bidderRegistryContract) GetAllowance( +func (r *bidderRegistryContract) GetDeposit( ctx context.Context, address common.Address, + window *big.Int, ) (*big.Int, error) { - callData, err := r.bidderRegistryABI.Pack("getAllowance", address) + callData, err := r.bidderRegistryABI.Pack("getDeposit", address, window) if err != nil { r.logger.Error("error packing call data", "error", err) return nil, err @@ -105,7 +129,7 @@ func (r *bidderRegistryContract) GetAllowance( return nil, err } - results, err := r.bidderRegistryABI.Unpack("getAllowance", result) + results, err := r.bidderRegistryABI.Unpack("getDeposit", result) if err != nil { r.logger.Error("error unpacking result", "error", err) return nil, err @@ -114,8 +138,8 @@ func (r *bidderRegistryContract) GetAllowance( return abi.ConvertType(results[0], new(big.Int)).(*big.Int), nil } -func (r *bidderRegistryContract) GetMinAllowance(ctx context.Context) (*big.Int, error) { - callData, err := r.bidderRegistryABI.Pack("minAllowance") +func (r *bidderRegistryContract) GetMinDeposit(ctx context.Context) (*big.Int, error) { + callData, err := r.bidderRegistryABI.Pack("minDeposit") if err != nil { r.logger.Error("error packing call data", "error", err) return nil, err @@ -129,7 +153,7 @@ func (r *bidderRegistryContract) GetMinAllowance(ctx context.Context) (*big.Int, return nil, err } - results, err := r.bidderRegistryABI.Unpack("minAllowance", result) + results, err := r.bidderRegistryABI.Unpack("minDeposit", result) if err != nil { r.logger.Error("error unpacking result", "error", err) return nil, err @@ -138,22 +162,82 @@ func (r *bidderRegistryContract) GetMinAllowance(ctx context.Context) (*big.Int, return abi.ConvertType(results[0], new(big.Int)).(*big.Int), nil } -func (r *bidderRegistryContract) CheckBidderAllowance( +func (r *bidderRegistryContract) WithdrawDeposit(ctx context.Context, window *big.Int) error { + callData, err := r.bidderRegistryABI.Pack("withdrawBidderAmountFromWindow", r.owner, window) + if err != nil { + r.logger.Error("error packing call data", "error", err) + return err + } + + txnHash, err := r.client.Send(ctx, &evmclient.TxRequest{ + To: &r.bidderRegistryContractAddr, + CallData: callData, + }) + if err != nil { + return err + } + + receipt, err := r.client.WaitForReceipt(ctx, txnHash) + if err != nil { + return err + } + + if receipt.Status != types.ReceiptStatusSuccessful { + r.logger.Error( + "withdraw failed for bidder registry", + "txnHash", txnHash, + "receipt", receipt, + ) + return err + } + + var bidderWithdrawn struct { + Bidder common.Address + Amount *big.Int + Window *big.Int + } + + for _, log := range receipt.Logs { + if len(log.Topics) > 1 { + bidderWithdrawn.Bidder = common.HexToAddress(log.Topics[1].Hex()) + } + + err := r.bidderRegistryABI.UnpackIntoInterface(&bidderWithdrawn, "BidderWithdrawn", log.Data) + if err != nil { + r.logger.Debug("Failed to unpack event", "err", err) + continue + } + r.logger.Info("bidder withdrawn", "address", bidderWithdrawn.Bidder, "withdrawn", bidderWithdrawn.Amount.Uint64(), "windowNumber", bidderWithdrawn.Window.Int64()) + } + + r.logger.Info("withdraw successful for bidder registry", "txnHash", txnHash, "bidder", bidderWithdrawn.Bidder) + + return nil +} + +func (r *bidderRegistryContract) CheckBidderDeposit( ctx context.Context, address common.Address, + window *big.Int, + blocksPerWindow *big.Int, ) bool { - - minStake, err := r.GetMinAllowance(ctx) + minStake, err := r.GetMinDeposit(ctx) if err != nil { r.logger.Error("error getting min stake", "error", err) return false } - stake, err := r.GetAllowance(ctx, address) + stake, err := r.GetDeposit(ctx, address, window) if err != nil { r.logger.Error("error getting stake", "error", err) return false } - - return stake.Cmp(minStake) >= 0 + r.logger.Info("checking bidder deposit", + "stake", stake.Uint64(), + "blocksPerWindow", blocksPerWindow.Uint64(), + "minStake", minStake.Uint64(), + "window", window.Uint64(), + "address", address.Hex(), + ) + return (stake.Div(stake, blocksPerWindow)).Cmp(minStake) >= 0 } diff --git a/pkg/contracts/bidder_registry/bidder_registry_test.go b/pkg/contracts/bidder_registry/bidder_registry_test.go index 6f132938..54bb4f65 100644 --- a/pkg/contracts/bidder_registry/bidder_registry_test.go +++ b/pkg/contracts/bidder_registry/bidder_registry_test.go @@ -18,12 +18,15 @@ import ( func TestBidderRegistryContract(t *testing.T) { t.Parallel() - t.Run("PrepayAllowance", func(t *testing.T) { + owner := common.HexToAddress("abcd") + + t.Run("Deposit", func(t *testing.T) { registryContractAddr := common.HexToAddress("abcd") txHash := common.HexToHash("abcdef") amount := big.NewInt(1000000000000000000) + window := big.NewInt(1) - expCallData, err := bidder_registrycontract.BidderRegistryABI().Pack("prepay") + expCallData, err := bidder_registrycontract.BidderRegistryABI().Pack("depositForSpecificWindow", window) if err != nil { t.Fatal(err) } @@ -66,23 +69,23 @@ func TestBidderRegistryContract(t *testing.T) { ) registryContract := bidder_registrycontract.New( + owner, registryContractAddr, mockClient, util.NewTestLogger(os.Stdout), ) - - err = registryContract.PrepayAllowance(context.Background(), amount) + err = registryContract.DepositForSpecificWindow(context.Background(), amount, big.NewInt(1)) if err != nil { t.Fatal(err) } }) - t.Run("GetAllowance", func(t *testing.T) { + t.Run("GetDeposit", func(t *testing.T) { registryContractAddr := common.HexToAddress("abcd") amount := big.NewInt(1000000000000000000) address := common.HexToAddress("abcdef") - - expCallData, err := bidder_registrycontract.BidderRegistryABI().Pack("getAllowance", address) + window := big.NewInt(1) + expCallData, err := bidder_registrycontract.BidderRegistryABI().Pack("getDeposit", address, window) if err != nil { t.Fatal(err) } @@ -106,12 +109,12 @@ func TestBidderRegistryContract(t *testing.T) { ) registryContract := bidder_registrycontract.New( + owner, registryContractAddr, mockClient, util.NewTestLogger(os.Stdout), ) - - stakeAmt, err := registryContract.GetAllowance(context.Background(), address) + stakeAmt, err := registryContract.GetDeposit(context.Background(), address, window) if err != nil { t.Fatal(err) } @@ -125,7 +128,7 @@ func TestBidderRegistryContract(t *testing.T) { registryContractAddr := common.HexToAddress("abcd") amount := big.NewInt(1000000000000000000) - expCallData, err := bidder_registrycontract.BidderRegistryABI().Pack("minAllowance") + expCallData, err := bidder_registrycontract.BidderRegistryABI().Pack("minDeposit") if err != nil { t.Fatal(err) } @@ -149,12 +152,13 @@ func TestBidderRegistryContract(t *testing.T) { ) registryContract := bidder_registrycontract.New( + owner, registryContractAddr, mockClient, util.NewTestLogger(os.Stdout), ) - stakeAmt, err := registryContract.GetMinAllowance(context.Background()) + stakeAmt, err := registryContract.GetMinDeposit(context.Background()) if err != nil { t.Fatal(err) } @@ -164,14 +168,18 @@ func TestBidderRegistryContract(t *testing.T) { } }) - t.Run("CheckBidderAllowance", func(t *testing.T) { + t.Run("CheckBidderDeposit", func(t *testing.T) { registryContractAddr := common.HexToAddress("abcd") - amount := big.NewInt(1000000000000000000) + blocksPerWindow := big.NewInt(64) + amount := new(big.Int).Mul(big.NewInt(1000000000000000000), blocksPerWindow) address := common.HexToAddress("abcdef") + callCount := 0 + mockClient := mockevmclient.New( mockevmclient.WithCallFunc( func(ctx context.Context, req *evmclient.TxRequest) ([]byte, error) { + callCount++ if req.To.Cmp(registryContractAddr) != 0 { t.Fatalf( "expected to address to be %s, got %s", @@ -179,18 +187,24 @@ func TestBidderRegistryContract(t *testing.T) { ) } + if callCount == 1 { + return new(big.Int).Div(amount, blocksPerWindow).FillBytes(make([]byte, 32)), nil + } + return amount.FillBytes(make([]byte, 32)), nil }, ), ) registryContract := bidder_registrycontract.New( + owner, registryContractAddr, mockClient, util.NewTestLogger(os.Stdout), ) - isRegistered := registryContract.CheckBidderAllowance(context.Background(), address) + window := big.NewInt(1) + isRegistered := registryContract.CheckBidderDeposit(context.Background(), address, window, blocksPerWindow) if !isRegistered { t.Fatal("expected bidder to be registered") } diff --git a/pkg/contracts/block_tracker/block_tracker.go b/pkg/contracts/block_tracker/block_tracker.go new file mode 100644 index 00000000..0d38d633 --- /dev/null +++ b/pkg/contracts/block_tracker/block_tracker.go @@ -0,0 +1,118 @@ +package blocktrackercontract + +import ( + "context" + "fmt" + "log/slog" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + blocktracker "github.com/primevprotocol/contracts-abi/clients/BlockTracker" + "github.com/primevprotocol/mev-commit/pkg/evmclient" +) + +var blockTrackerABI = func() abi.ABI { + abi, err := abi.JSON(strings.NewReader(blocktracker.BlocktrackerMetaData.ABI)) + if err != nil { + panic(err) + } + return abi +}() + +type Interface interface { + // GetBlocksPerWindow returns the number of blocks per window. + GetBlocksPerWindow(ctx context.Context) (uint64, error) + // GetCurrentWindow returns the current window number. + GetCurrentWindow(ctx context.Context) (uint64, error) +} + +type blockTrackerContract struct { + blockTrackerABI abi.ABI + blockTrackerContractAddr common.Address + client evmclient.Interface + wsClient evmclient.Interface + logger *slog.Logger +} + +type NewL1BlockEvent struct { + BlockNumber *big.Int + Winner common.Address + Window *big.Int +} + +func New( + blockTrackerContractAddr common.Address, + client evmclient.Interface, + wsClient evmclient.Interface, + logger *slog.Logger, +) Interface { + return &blockTrackerContract{ + blockTrackerABI: blockTrackerABI, + blockTrackerContractAddr: blockTrackerContractAddr, + client: client, + wsClient: wsClient, + logger: logger, + } +} + +// GetBlocksPerWindow returns the number of blocks per window. +func (btc *blockTrackerContract) GetBlocksPerWindow(ctx context.Context) (uint64, error) { + callData, err := btc.blockTrackerABI.Pack("getBlocksPerWindow") + if err != nil { + btc.logger.Error("error packing call data for getBlocksPerWindow", "error", err) + return 0, err + } + + result, err := btc.client.Call(ctx, &evmclient.TxRequest{ + To: &btc.blockTrackerContractAddr, + CallData: callData, + }) + if err != nil { + return 0, err + } + + results, err := btc.blockTrackerABI.Unpack("getBlocksPerWindow", result) + if err != nil { + btc.logger.Error("error unpacking result for getBlocksPerWindow", "error", err) + return 0, err + } + + blocksPerWindow, ok := results[0].(*big.Int) + if !ok { + return 0, fmt.Errorf("invalid result type") + } + + return blocksPerWindow.Uint64(), nil +} + +// GetCurrentWindow returns the current window number. +func (btc *blockTrackerContract) GetCurrentWindow(ctx context.Context) (uint64, error) { + callData, err := btc.blockTrackerABI.Pack("getCurrentWindow") + if err != nil { + btc.logger.Error("error packing call data for getCurrentWindow", "error", err) + return 0, err + } + + result, err := btc.client.Call(ctx, &evmclient.TxRequest{ + To: &btc.blockTrackerContractAddr, + CallData: callData, + }) + if err != nil { + return 0, err + } + + results, err := btc.blockTrackerABI.Unpack("getCurrentWindow", result) + if err != nil { + btc.logger.Error("error unpacking result for getCurrentWindow", "error", err) + return 0, err + } + + currentWindow, ok := results[0].(*big.Int) + if !ok { + return 0, fmt.Errorf("invalid result type") + } + + return currentWindow.Uint64(), nil +} diff --git a/pkg/contracts/preconf/preconf.go b/pkg/contracts/preconf/preconf.go index c8639b07..42d7d5b6 100644 --- a/pkg/contracts/preconf/preconf.go +++ b/pkg/contracts/preconf/preconf.go @@ -2,6 +2,7 @@ package preconfcontract import ( "context" + "fmt" "log/slog" "math/big" "strings" @@ -24,16 +25,23 @@ var preconfABI = func() abi.ABI { var defaultWaitTimeout = 10 * time.Second type Interface interface { - StoreCommitment( + StoreEncryptedCommitment( ctx context.Context, - bid *big.Int, - blockNumber uint64, - txHash string, - decayStartTimeStamp uint64, - decayEndTimeStamp uint64, + commitmentDigest []byte, + commitmentSignature []byte, + ) (common.Hash, error) + OpenCommitment( + ctx context.Context, + encryptedCommitmentIndex []byte, + bid string, + blockNumber int64, + txnHash string, + decayStartTimeStamp int64, + decayEndTimeStamp int64, bidSignature []byte, commitmentSignature []byte, - ) error + sharedSecretKey []byte, + ) (common.Hash, error) } type preconfContract struct { @@ -56,41 +64,79 @@ func New( } } -func (p *preconfContract) StoreCommitment( +func (p *preconfContract) StoreEncryptedCommitment( + ctx context.Context, + commitmentDigest []byte, + commitmentSignature []byte, +) (common.Hash, error) { + callData, err := p.preconfABI.Pack( + "storeEncryptedCommitment", + [32]byte(commitmentDigest), + commitmentSignature, + ) + if err != nil { + p.logger.Error("preconf contract storeEncryptedCommitment pack error", "err", err) + return common.Hash{}, err + } + + txnHash, err := p.client.Send(ctx, &evmclient.TxRequest{ + To: &p.preconfContractAddr, + CallData: callData, + }) + if err != nil { + return common.Hash{}, err + } + + return txnHash, nil +} + +func (p *preconfContract) OpenCommitment( ctx context.Context, - bid *big.Int, - blockNumber uint64, - txHash string, - deacyStartTimeStamp uint64, - decayEndTimeStamp uint64, + encryptedCommitmentIndex []byte, + bid string, + blockNumber int64, + txnHash string, + decayStartTimeStamp int64, + decayEndTimeStamp int64, bidSignature []byte, commitmentSignature []byte, -) error { + sharedSecretKey []byte, +) (common.Hash, error) { + bidAmt, ok := new(big.Int).SetString(bid, 10) + if !ok { + p.logger.Error("Error converting bid to big.Int", "bid", bid) + return common.Hash{}, fmt.Errorf("error converting bid to big.Int, bid: %s", bid) + } + + var eciBytes [32]byte + copy(eciBytes[:], encryptedCommitmentIndex) callData, err := p.preconfABI.Pack( - "storeCommitment", - uint64(bid.Int64()), - blockNumber, - txHash, - deacyStartTimeStamp, - decayEndTimeStamp, + "openCommitment", + eciBytes, + bidAmt.Uint64(), + big.NewInt(blockNumber).Uint64(), + txnHash, + big.NewInt(decayStartTimeStamp).Uint64(), + big.NewInt(decayEndTimeStamp).Uint64(), bidSignature, commitmentSignature, + sharedSecretKey, ) if err != nil { - p.logger.Error("preconf contract storeCommitment pack error", "err", err) - return err + p.logger.Error("Error packing call data for openCommitment", "error", err) + return common.Hash{}, err } - txnHash, err := p.client.Send(ctx, &evmclient.TxRequest{ + txHash, err := p.client.Send(ctx, &evmclient.TxRequest{ To: &p.preconfContractAddr, CallData: callData, }) if err != nil { - return err + return common.Hash{}, err } - p.logger.Info("preconf contract storeCommitment successful", "txnHash", txnHash) + p.logger.Info("preconf contract openCommitment successful", "txHash", txHash.String()) - return nil + return txHash, nil } diff --git a/pkg/contracts/preconf/preconf_test.go b/pkg/contracts/preconf/preconf_test.go index 8b43d733..0d3f8571 100644 --- a/pkg/contracts/preconf/preconf_test.go +++ b/pkg/contracts/preconf/preconf_test.go @@ -3,7 +3,6 @@ package preconfcontract_test import ( "bytes" "context" - "math/big" "os" "testing" @@ -18,25 +17,16 @@ import ( func TestPreconfContract(t *testing.T) { t.Parallel() - t.Run("StoreCommitment", func(t *testing.T) { + t.Run("StoreEncryptedCommitment", func(t *testing.T) { preConfContract := common.HexToAddress("abcd") txHash := common.HexToHash("abcdef") - bid := big.NewInt(1000000000000000000) - blockNum := uint64(100) - bidSig := []byte("abcdef") - commitment := []byte("abcdef") - decayStart := uint64(1710095453035) - decayEnd := uint64(1710095454035) + commitment := [32]byte([]byte("abcdefabcdefabcdefabcdefabcdefaa")) + commitmentSignature := []byte("abcdef") expCallData, err := preconfcontract.PreConfABI().Pack( - "storeCommitment", - uint64(bid.Int64()), - blockNum, - txHash.String(), - decayStart, - decayEnd, - bidSig, + "storeEncryptedCommitment", commitment, + commitmentSignature, ) if err != nil { @@ -76,15 +66,10 @@ func TestPreconfContract(t *testing.T) { util.NewTestLogger(os.Stdout), ) - err = preConfContractClient.StoreCommitment( + _, err = preConfContractClient.StoreEncryptedCommitment( context.Background(), - bid, - blockNum, - txHash.String(), - decayStart, - decayEnd, - bidSig, - commitment, + commitment[:], + commitmentSignature, ) if err != nil { t.Fatal(err) diff --git a/pkg/contracts/provider_registry/registry.go b/pkg/contracts/provider_registry/registry.go index 9c37ecdf..b81563a6 100644 --- a/pkg/contracts/provider_registry/registry.go +++ b/pkg/contracts/provider_registry/registry.go @@ -126,6 +126,7 @@ func (r *registryContract) GetMinStake(ctx context.Context) (*big.Int, error) { CallData: callData, }) if err != nil { + r.logger.Error("error calling contract", "error", err) return nil, err } diff --git a/pkg/depositmanager/deposit.go b/pkg/depositmanager/deposit.go new file mode 100644 index 00000000..a218c9ca --- /dev/null +++ b/pkg/depositmanager/deposit.go @@ -0,0 +1,189 @@ +package depositmanager + +import ( + "context" + "fmt" + "log/slog" + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + bidderregistry "github.com/primevprotocol/contracts-abi/clients/BidderRegistry" + blocktracker "github.com/primevprotocol/contracts-abi/clients/BlockTracker" + blocktrackercontract "github.com/primevprotocol/mev-commit/pkg/contracts/block_tracker" + preconfcontract "github.com/primevprotocol/mev-commit/pkg/contracts/preconf" + "github.com/primevprotocol/mev-commit/pkg/events" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type BidderRegistry interface { + CheckBidderDeposit(context.Context, common.Address, *big.Int, *big.Int) bool + GetMinDeposit(ctx context.Context) (*big.Int, error) +} + +type Store interface { + GetBalance(bidder common.Address, windowNumber *big.Int) (*big.Int, error) + SetBalance(bidder common.Address, windowNumber *big.Int, balance *big.Int) error + DeductAndCheckBalanceForBlock(bidder common.Address, defaultAmount, bidAmount *big.Int, blockNumber int64) (*big.Int, error) + RefundBalanceForBlock(bidder common.Address, amount *big.Int, blockNumber int64) error +} + +type DepositManager struct { + bidderRegistry BidderRegistry + blockTracker blocktrackercontract.Interface + commitmentDA preconfcontract.Interface + store Store + evtMgr events.EventManager + blocksPerWindow atomic.Uint64 // todo: move to the store + minDeposit atomic.Int64 // todo: move to the store + currentWindow atomic.Int64 // todo: move to the store + logger *slog.Logger +} + +func NewDepositManager( + br BidderRegistry, + blockTracker blocktrackercontract.Interface, + commitmentDA preconfcontract.Interface, + store Store, + evtMgr events.EventManager, + logger *slog.Logger, +) *DepositManager { + return &DepositManager{ + bidderRegistry: br, + blockTracker: blockTracker, + commitmentDA: commitmentDA, + store: store, + evtMgr: evtMgr, + logger: logger, + } +} + +func (a *DepositManager) Start(ctx context.Context) <-chan struct{} { + doneChan := make(chan struct{}) + + eg, egCtx := errgroup.WithContext(ctx) + + eg.Go(func() error { + ev1 := events.NewEventHandler( + "NewWindow", + func(window *blocktracker.BlocktrackerNewWindow) error { + a.currentWindow.Store(window.Window.Int64()) + return nil + }, + ) + + sub1, err := a.evtMgr.Subscribe(ev1) + if err != nil { + return fmt.Errorf("failed to subscribe to NewWindow event: %w", err) + } + defer sub1.Unsubscribe() + + ev2 := events.NewEventHandler( + "BidderRegistered", + func(bidderReg *bidderregistry.BidderregistryBidderRegistered) error { + // todo: do we need to check if commiter is connected to this bidder? + return a.store.SetBalance(bidderReg.Bidder, bidderReg.WindowNumber, bidderReg.DepositedAmount) + }, + ) + + sub2, err := a.evtMgr.Subscribe(ev2) + if err != nil { + return fmt.Errorf("failed to subscribe to BidderRegistered event: %w", err) + } + defer sub2.Unsubscribe() + + select { + case <-egCtx.Done(): + return nil + case err := <-sub1.Err(): + return fmt.Errorf("error in NewWindow event subscription: %w", err) + case err := <-sub2.Err(): + return fmt.Errorf("error in BidderRegistered event subscription: %w", err) + } + }) + + go func() { + defer close(doneChan) + if err := eg.Wait(); err != nil { + a.logger.Error("error in DepositManager", "error", err) + } + }() + + return doneChan +} + +func (a *DepositManager) CheckAndDeductDeposit(ctx context.Context, address common.Address, bidAmountStr string, blockNumber int64) (*big.Int, error) { + if a.blocksPerWindow.Load() == 0 { + blocksPerWindow, err := a.blockTracker.GetBlocksPerWindow(ctx) + if err != nil { + a.logger.Error("getting blocks per window", "error", err) + return nil, status.Errorf(codes.Internal, "failed to get blocks per window: %v", err) + } + a.blocksPerWindow.Store(blocksPerWindow) + } + + if a.minDeposit.Load() == 0 { + minDeposit, err := a.bidderRegistry.GetMinDeposit(ctx) + if err != nil { + a.logger.Error("getting min deposit", "error", err) + return nil, status.Errorf(codes.Internal, "failed to get min deposit: %v", err) + + } + a.minDeposit.Store(minDeposit.Int64()) + } + + bidAmount, ok := new(big.Int).SetString(bidAmountStr, 10) + if !ok { + a.logger.Error("parsing bid amount", "amount", bidAmountStr) + return nil, status.Errorf(codes.InvalidArgument, "failed to parse bid amount") + } + + // adding 2 to the current window, bcs oracle is 2 windows behind + windowToCheck := big.NewInt(a.currentWindow.Load() + 2) + + balance, err := a.store.GetBalance(address, windowToCheck) + if err != nil { + a.logger.Error("getting balance", "error", err) + return nil, status.Errorf(codes.Internal, "failed to get balance: %v", err) + } + + if balance == nil { + a.logger.Error("bidder balance not found", "address", address.Hex(), "window", windowToCheck) + return nil, status.Errorf(codes.FailedPrecondition, "balance not found") + } + + a.logger.Info("checking bidder deposit", + "stake", balance.Uint64(), + "blocksPerWindow", a.blocksPerWindow.Load(), + "minStake", a.minDeposit.Load(), + "window", windowToCheck.Uint64(), + "address", address.Hex(), + ) + + blocksPerWindow := new(big.Int).SetUint64(a.blocksPerWindow.Load()) + minDeposit := big.NewInt(a.minDeposit.Load()) + + // todo: make sense to do division only once, when bidder deposit funds, + // not everytime, when checking deposit + effectiveStake := new(big.Int).Div(new(big.Int).Set(balance), blocksPerWindow) + + isEnoughDeposit := effectiveStake.Cmp(minDeposit) >= 0 + + if !isEnoughDeposit { + a.logger.Error("bidder does not have enough deposit", "ethAddress", address) + return nil, status.Errorf(codes.FailedPrecondition, "bidder do not have enough deposit") + } + + deductedBalance, err := a.store.DeductAndCheckBalanceForBlock(address, effectiveStake, bidAmount, blockNumber) + if err != nil { + a.logger.Error("deducting balance", "error", err) + return nil, status.Errorf(codes.FailedPrecondition, "failed to deduct balance: %v", err) + } + return deductedBalance, nil +} + +func (a *DepositManager) RefundDeposit(address common.Address, deductedAmount *big.Int, blockNumber int64) error { + return a.store.RefundBalanceForBlock(address, deductedAmount, blockNumber) +} diff --git a/pkg/events/events.go b/pkg/events/events.go new file mode 100644 index 00000000..67d9b9ac --- /dev/null +++ b/pkg/events/events.go @@ -0,0 +1,289 @@ +package events + +import ( + "bytes" + "context" + "fmt" + "log/slog" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// EVMClient is an interface for interacting with an Ethereum client for event subscription. +type EVMClient interface { + BlockNumber(ctx context.Context) (uint64, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) +} + +// ProgressStore is an interface for storing the last block number processed by the event listener. +type ProgressStore interface { + LastBlock() (uint64, error) + SetLastBlock(block uint64) error +} + +// EventManager is an interface for subscribing to events. This interface is a stand-in for +// the generic event handlers that are used to subscribe to events. +type EventHandler interface { + EventName() string + Handle(types.Log) error + SetTopicAndContract(topic common.Hash, contract *abi.ABI) + Topic() common.Hash +} + +// eventHandler is a generic implementation of EventHandler for type-safe event handling. +type eventHandler[T any] struct { + handler func(*T) error + name string + topicID common.Hash + contract *abi.ABI +} + +// NewEventHandler creates a new EventHandler for the given event name from the known contracts. +// The handler function is called when an event is received. The handler function should +// return an error if the event is a fatal error, otherwise it should return nil. The event +// handler should be used to subscribe to events using the EventManager interface. +func NewEventHandler[T any](name string, handler func(*T) error) EventHandler { + return &eventHandler[T]{ + handler: handler, + name: name, + } +} + +func (h *eventHandler[T]) EventName() string { + return h.name +} + +func (h *eventHandler[T]) SetTopicAndContract(topic common.Hash, contract *abi.ABI) { + h.topicID = topic + h.contract = contract +} + +func (h *eventHandler[T]) Handle(log types.Log) error { + if h.contract == nil { + return fmt.Errorf("contract not set") + } + + if !bytes.Equal(log.Topics[0].Bytes(), h.topicID.Bytes()) { + return nil + } + + obj := new(T) + + if len(log.Data) > 0 { + err := h.contract.UnpackIntoInterface(obj, h.name, log.Data) + if err != nil { + return err + } + } + + var indexed abi.Arguments + for _, arg := range h.contract.Events[h.name].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + + if len(indexed) > 0 { + err := abi.ParseTopics(obj, indexed, log.Topics[1:]) + if err != nil { + return err + } + } + + return h.handler(obj) +} + +func (h *eventHandler[T]) Topic() common.Hash { + return h.topicID +} + +type EventManager interface { + Subscribe(event EventHandler) (Subscription, error) +} + +type Subscription interface { + Unsubscribe() + Err() <-chan error +} + +type Listener struct { + logger *slog.Logger + evmClient EVMClient + progressStore ProgressStore + subMu sync.RWMutex + subscribers map[common.Hash][]*subscription + contracts map[common.Address]*abi.ABI +} + +func NewListener( + logger *slog.Logger, + evmClient EVMClient, + progressStore ProgressStore, + contracts map[common.Address]*abi.ABI, +) *Listener { + return &Listener{ + logger: logger, + evmClient: evmClient, + progressStore: progressStore, + subscribers: make(map[common.Hash][]*subscription), + contracts: contracts, + } +} + +type subscription struct { + event EventHandler + unsub func() + errCh chan error +} + +func (s *subscription) Unsubscribe() { + s.unsub() +} + +func (s *subscription) Err() <-chan error { + return s.errCh +} + +func (l *Listener) Subscribe(event EventHandler) (Subscription, error) { + found := false + for _, c := range l.contracts { + for _, e := range c.Events { + if e.Name == event.EventName() { + event.SetTopicAndContract(e.ID, c) + found = true + break + } + } + } + + if !found { + return nil, fmt.Errorf("event not found") + } + + l.subMu.Lock() + defer l.subMu.Unlock() + + sub := &subscription{ + event: event, + errCh: make(chan error), + unsub: func() { l.unsubscribe(event) }, + } + + l.subscribers[event.Topic()] = append(l.subscribers[event.Topic()], sub) + + return sub, nil +} + +func (l *Listener) unsubscribe(event EventHandler) { + l.subMu.Lock() + defer l.subMu.Unlock() + + events := l.subscribers[event.Topic()] + for i, e := range events { + if e.event == event { + events = append(events[:i], events[i+1:]...) + break + } + } + + l.subscribers[event.Topic()] = events +} + +func (l *Listener) publishLogEvent(ctx context.Context, log types.Log) { + l.subMu.RLock() + defer l.subMu.RUnlock() + + wg := sync.WaitGroup{} + events := l.subscribers[log.Topics[0]] + for _, event := range events { + ev := event + wg.Add(1) + go func() { + defer wg.Done() + + if err := ev.event.Handle(log); err != nil { + l.logger.Error("failed to handle log", "error", err) + select { + case ev.errCh <- err: + case <-ctx.Done(): + } + } + }() + } + + wg.Wait() +} + +func (l *Listener) Start(ctx context.Context) <-chan struct{} { + doneChan := make(chan struct{}) + + if len(l.contracts) == 0 { + close(doneChan) + return doneChan + } + + go func() { + defer close(doneChan) + + lastBlock, err := l.progressStore.LastBlock() + if err != nil { + l.logger.Error("failed to get last block", "error", err) + return + } + + contracts := make([]common.Address, 0, len(l.contracts)) + for addr := range l.contracts { + contracts = append(contracts, addr) + } + + ticker := time.NewTicker(500 * time.Millisecond) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + blockNumber, err := l.evmClient.BlockNumber(ctx) + if err != nil { + l.logger.Error("failed to get block number", "error", err) + return + } + + if blockNumber > lastBlock { + q := ethereum.FilterQuery{ + FromBlock: big.NewInt(int64(lastBlock + 1)), + ToBlock: big.NewInt(int64(blockNumber)), + Addresses: contracts, + } + + logs, err := l.evmClient.FilterLogs(ctx, q) + if err != nil { + l.logger.Error("failed to filter logs", "error", err) + return + } + + for _, logMsg := range logs { + // process log + l.publishLogEvent(ctx, logMsg) + } + + if err := l.progressStore.SetLastBlock(blockNumber); err != nil { + l.logger.Error("failed to set last block", "error", err) + return + } + l.logger.Debug("processed logs", "from", lastBlock+1, "to", blockNumber, "count", len(logs)) + lastBlock = blockNumber + } + } + } + }() + + return doneChan +} diff --git a/pkg/events/events_test.go b/pkg/events/events_test.go new file mode 100644 index 00000000..1fd28062 --- /dev/null +++ b/pkg/events/events_test.go @@ -0,0 +1,286 @@ +package events_test + +import ( + "context" + "fmt" + "io" + "log/slog" + "math/big" + "strings" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + bidderregistry "github.com/primevprotocol/contracts-abi/clients/BidderRegistry" + "github.com/primevprotocol/mev-commit/pkg/events" +) + +func TestEventHandler(t *testing.T) { + t.Parallel() + + b := bidderregistry.BidderregistryBidderRegistered{ + Bidder: common.HexToAddress("0xabcd"), + DepositedAmount: big.NewInt(1000), + WindowNumber: big.NewInt(99), + } + + evtHdlr := events.NewEventHandler( + "BidderRegistered", + func(ev *bidderregistry.BidderregistryBidderRegistered) error { + if ev.Bidder.Hex() != b.Bidder.Hex() { + return fmt.Errorf("expected bidder %s, got %s", b.Bidder.Hex(), ev.Bidder.Hex()) + } + if ev.DepositedAmount.Cmp(b.DepositedAmount) != 0 { + return fmt.Errorf("expected deposited amount %d, got %d", b.DepositedAmount, ev.DepositedAmount) + } + if ev.WindowNumber.Cmp(b.WindowNumber) != 0 { + return fmt.Errorf("expected window number %d, got %d", b.WindowNumber, ev.WindowNumber) + } + return nil + }, + ) + + bidderABI, err := abi.JSON(strings.NewReader(bidderregistry.BidderregistryABI)) + if err != nil { + t.Fatal(err) + } + + event := bidderABI.Events["BidderRegistered"] + + evtHdlr.SetTopicAndContract(event.ID, &bidderABI) + + if evtHdlr.Topic().Cmp(event.ID) != 0 { + t.Fatalf("expected topic %s, got %s", event.ID, evtHdlr.Topic()) + } + + if evtHdlr.EventName() != "BidderRegistered" { + t.Fatalf("expected event name BidderRegistered, got %s", evtHdlr.EventName()) + } + + buf, err := event.Inputs.NonIndexed().Pack( + b.DepositedAmount, + b.WindowNumber, + ) + if err != nil { + t.Fatal(err) + } + + bidder := common.HexToHash(b.Bidder.Hex()) + + // Creating a Log object + testLog := types.Log{ + Topics: []common.Hash{ + event.ID, // The first topic is the hash of the event signature + bidder, // The next topics are the indexed event parameters + }, + Data: buf, + } + + if err := evtHdlr.Handle(testLog); err != nil { + t.Fatal(err) + } +} + +func TestEventManager(t *testing.T) { + t.Parallel() + + bidders := []bidderregistry.BidderregistryBidderRegistered{ + { + Bidder: common.HexToAddress("0xabcd"), + DepositedAmount: big.NewInt(1000), + WindowNumber: big.NewInt(99), + }, + { + Bidder: common.HexToAddress("0xcdef"), + DepositedAmount: big.NewInt(2000), + WindowNumber: big.NewInt(100), + }, + } + + count := 0 + + handlerTriggered1 := make(chan struct{}) + handlerTriggered2 := make(chan struct{}) + + evtHdlr := events.NewEventHandler( + "BidderRegistered", + func(ev *bidderregistry.BidderregistryBidderRegistered) error { + if count >= len(bidders) { + return fmt.Errorf("unexpected event") + } + if ev.Bidder.Hex() != bidders[count].Bidder.Hex() { + return fmt.Errorf("expected bidder %s, got %s", bidders[count].Bidder.Hex(), ev.Bidder.Hex()) + } + if ev.DepositedAmount.Cmp(bidders[count].DepositedAmount) != 0 { + return fmt.Errorf("expected deposited amount %d, got %d", bidders[count].DepositedAmount, ev.DepositedAmount) + } + if ev.WindowNumber.Cmp(bidders[count].WindowNumber) != 0 { + return fmt.Errorf("expected window number %d, got %d", bidders[count].WindowNumber, ev.WindowNumber) + } + count++ + if count == 1 { + close(handlerTriggered1) + } else { + close(handlerTriggered2) + } + return nil + }, + ) + + bidderABI, err := abi.JSON(strings.NewReader(bidderregistry.BidderregistryABI)) + if err != nil { + t.Fatal(err) + } + + data1, err := bidderABI.Events["BidderRegistered"].Inputs.NonIndexed().Pack( + bidders[0].DepositedAmount, + bidders[0].WindowNumber, + ) + if err != nil { + t.Fatal(err) + } + + data2, err := bidderABI.Events["BidderRegistered"].Inputs.NonIndexed().Pack( + bidders[1].DepositedAmount, + bidders[1].WindowNumber, + ) + if err != nil { + t.Fatal(err) + } + + evmClient := &testEVMClient{ + logs: []types.Log{ + { + Topics: []common.Hash{ + bidderABI.Events["BidderRegistered"].ID, + common.HexToHash(bidders[0].Bidder.Hex()), + }, + Data: data1, + BlockNumber: 1, + }, + { + Topics: []common.Hash{ + bidderABI.Events["BidderRegistered"].ID, + common.HexToHash(bidders[1].Bidder.Hex()), + }, + Data: data2, + BlockNumber: 2, + }, + }, + } + + store := &testStore{} + + contracts := map[common.Address]*abi.ABI{ + common.HexToAddress("0xabcd"): &bidderABI, + } + + evtMgr := events.NewListener( + slog.New(slog.NewTextHandler(io.Discard, nil)), + evmClient, + store, + contracts, + ) + + ctx, cancel := context.WithCancel(context.Background()) + done := evtMgr.Start(ctx) + + sub, err := evtMgr.Subscribe(evtHdlr) + if err != nil { + t.Fatal(err) + } + + defer sub.Unsubscribe() + + evmClient.SetBlockNumber(1) + + select { + case <-handlerTriggered1: + case <-time.After(5 * time.Second): + t.Fatal("timed out waiting for handler to be triggered") + } + + evmClient.SetBlockNumber(2) + select { + case <-handlerTriggered2: + case <-time.After(5 * time.Second): + t.Fatal("timed out waiting for handler to be triggered") + } + + start := time.Now() + for { + if b, err := store.LastBlock(); err != nil { + t.Fatal(err) + } else if b == 2 { + break + } + if time.Since(start) > 5*time.Second { + t.Fatal("timed out waiting for block number to be updated") + } + time.Sleep(100 * time.Millisecond) + } + + cancel() + <-done +} + +type testEVMClient struct { + mu sync.Mutex + blockNum uint64 + logs []types.Log +} + +func (t *testEVMClient) SetBlockNumber(blockNum uint64) { + t.mu.Lock() + defer t.mu.Unlock() + + t.blockNum = blockNum +} + +func (t *testEVMClient) BlockNumber(context.Context) (uint64, error) { + t.mu.Lock() + defer t.mu.Unlock() + + return t.blockNum, nil +} + +func (t *testEVMClient) FilterLogs( + ctx context.Context, + q ethereum.FilterQuery, +) ([]types.Log, error) { + t.mu.Lock() + defer t.mu.Unlock() + + logs := make([]types.Log, 0, len(t.logs)) + for _, log := range t.logs { + if log.BlockNumber >= q.FromBlock.Uint64() && log.BlockNumber <= q.ToBlock.Uint64() { + logs = append(logs, log) + } + } + + return logs, nil +} + +type testStore struct { + mu sync.Mutex + blockNumber uint64 +} + +func (t *testStore) LastBlock() (uint64, error) { + t.mu.Lock() + defer t.mu.Unlock() + + return t.blockNumber, nil +} + +func (t *testStore) SetLastBlock(blockNumber uint64) error { + t.mu.Lock() + defer t.mu.Unlock() + + t.blockNumber = blockNumber + return nil +} diff --git a/pkg/evmclient/evm.go b/pkg/evmclient/evm.go index 65b7499b..1f12128c 100644 --- a/pkg/evmclient/evm.go +++ b/pkg/evmclient/evm.go @@ -72,6 +72,8 @@ type EVM interface { NetworkID(ctx context.Context) (*big.Int, error) // BlockNumber returns the most recent block number BlockNumber(ctx context.Context) (uint64, error) + // BlockByNumber returns the block identified by number. + BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) // PendingNonceAt retrieves the current pending nonce associated with an account. PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) // NonceAt retrieves the current nonce associated with an account. @@ -101,6 +103,10 @@ type EVM interface { // mined yet. Note that the transaction may not be part of the canonical chain even if // it's not pending. TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) + // SubscribeFilterLogs creates a new subscription to filter logs. It returns a subscription + SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + // FilterLogs executes a filter query to return the logs that satisfy the specified + FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) } type Batcher interface { @@ -137,4 +143,4 @@ func WrapEthClient(client *ethclient.Client) EVM { return &evm{ Client: client, } -} +} \ No newline at end of file diff --git a/pkg/evmclient/evmclient.go b/pkg/evmclient/evmclient.go index 6c341eb5..71815946 100644 --- a/pkg/evmclient/evmclient.go +++ b/pkg/evmclient/evmclient.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/primevprotocol/mev-commit/pkg/keysigner" + "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner" "github.com/prometheus/client_golang/prometheus" ) @@ -43,6 +43,10 @@ type Interface interface { WaitForReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) Call(ctx context.Context, tx *TxRequest) ([]byte, error) CancelTx(ctx context.Context, txHash common.Hash) (common.Hash, error) + SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + BlockByNumber(ctx context.Context, blockNumber *big.Int) (*types.Block, error) + BlockNumber(ctx context.Context) (uint64, error) + FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) } type EvmClient struct { @@ -383,6 +387,22 @@ func (c *EvmClient) CancelTx(ctx context.Context, txnHash common.Hash) (common.H return signedTx.Hash(), nil } +func (c *EvmClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, logsCh chan<- types.Log) (ethereum.Subscription, error) { + return c.ethClient.SubscribeFilterLogs(ctx, query, logsCh) +} + +func (c *EvmClient) BlockByNumber(ctx context.Context, blockNumber *big.Int) (*types.Block, error) { + return c.ethClient.BlockByNumber(ctx, blockNumber) +} + +func (c *EvmClient) BlockNumber(ctx context.Context) (uint64, error) { + return c.ethClient.BlockNumber(ctx) +} + +func (c *EvmClient) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { + return c.ethClient.FilterLogs(ctx, query) +} + type TxnInfo struct { Hash string Nonce uint64 diff --git a/pkg/evmclient/evmclient_test.go b/pkg/evmclient/evmclient_test.go index 71b98917..7e3569ab 100644 --- a/pkg/evmclient/evmclient_test.go +++ b/pkg/evmclient/evmclient_test.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/primevprotocol/mev-commit/pkg/evmclient" "github.com/primevprotocol/mev-commit/pkg/evmclient/mockevm" - mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keysigner/mock" + mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner/mock" "github.com/primevprotocol/mev-commit/pkg/util" ) diff --git a/pkg/evmclient/mock/mock.go b/pkg/evmclient/mock/mock.go index 62d7bfe8..c365260e 100644 --- a/pkg/evmclient/mock/mock.go +++ b/pkg/evmclient/mock/mock.go @@ -3,7 +3,9 @@ package mockevmclient import ( "context" "errors" + "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/primevprotocol/mev-commit/pkg/evmclient" @@ -51,11 +53,54 @@ func WithCancelFunc( } } +func WithSubscribeFilterLogs( + f func(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error), +) Option { + return func(m *mockEvmClient) { + m.SubscribeFilterLogsFunc = f + } +} + +func WithBlockByNumber( + f func(ctx context.Context, number *big.Int) (*types.Block, error), +) Option { + return func(m *mockEvmClient) { + m.BlockByNumberFunc = f + } +} + +func WithBlockNumber( + f func(ctx context.Context) (uint64, error), +) Option { + return func(m *mockEvmClient) { + m.BlockNumberFunc = f + } +} + +func WithFilterLogs( + f func(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error), +) Option { + return func(m *mockEvmClient) { + m.FilterLogsFunc = f + } +} + type mockEvmClient struct { - SendFunc func(ctx context.Context, req *evmclient.TxRequest) (common.Hash, error) - WaitForReceiptFunc func(ctx context.Context, txnHash common.Hash) (*types.Receipt, error) - CallFunc func(ctx context.Context, req *evmclient.TxRequest) ([]byte, error) - CancelFunc func(ctx context.Context, txHash common.Hash) (common.Hash, error) + SendFunc func(ctx context.Context, req *evmclient.TxRequest) (common.Hash, error) + WaitForReceiptFunc func(ctx context.Context, txnHash common.Hash) (*types.Receipt, error) + CallFunc func(ctx context.Context, req *evmclient.TxRequest) ([]byte, error) + CancelFunc func(ctx context.Context, txHash common.Hash) (common.Hash, error) + SubscribeFilterLogsFunc func(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + BlockByNumberFunc func(ctx context.Context, number *big.Int) (*types.Block, error) + BlockNumberFunc func(ctx context.Context) (uint64, error) + FilterLogsFunc func(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) +} + +func (m *mockEvmClient) BlockNumber(ctx context.Context) (uint64, error) { + if m.BlockNumberFunc == nil { + return 0, errors.New("not implemented") + } + return m.BlockNumberFunc(ctx) } func (m *mockEvmClient) Send( @@ -91,3 +136,31 @@ func (m *mockEvmClient) CancelTx(ctx context.Context, txHash common.Hash) (commo } return m.CancelFunc(ctx, txHash) } + +func (m *mockEvmClient) SubscribeFilterLogs( + ctx context.Context, + query ethereum.FilterQuery, + ch chan<- types.Log, +) (ethereum.Subscription, error) { + if m.SubscribeFilterLogsFunc == nil { + return nil, errors.New("not implemented") + } + return m.SubscribeFilterLogsFunc(ctx, query, ch) +} + +func (m *mockEvmClient) BlockByNumber( + ctx context.Context, + number *big.Int, +) (*types.Block, error) { + if m.BlockByNumberFunc == nil { + return nil, errors.New("not implemented") + } + return m.BlockByNumber(ctx, number) +} + +func (m *mockEvmClient) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { + if m.FilterLogsFunc == nil { + return nil, errors.New("not implemented") + } + return m.FilterLogsFunc(ctx, query) +} diff --git a/pkg/evmclient/mockevm/mockevm.go b/pkg/evmclient/mockevm/mockevm.go index 1a07e1a1..dc65fd57 100644 --- a/pkg/evmclient/mockevm/mockevm.go +++ b/pkg/evmclient/mockevm/mockevm.go @@ -16,6 +16,7 @@ type mockEvm struct { networkID *big.Int batcherFunc func() evmclient.Batcher blockNumFunc func(ctx context.Context) (uint64, error) + blockByNumberFunc func(ctx context.Context, blockNumber *big.Int) (*types.Block, error) pendingNonceAtFunc func(ctx context.Context, account common.Address) (uint64, error) nonceAtFunc func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) suggestGasPriceFunc func(ctx context.Context) (*big.Int, error) @@ -25,6 +26,8 @@ type mockEvm struct { callContractFunc func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) transactionReceiptFunc func(ctx context.Context, txHash common.Hash) (*types.Receipt, error) transactionByHasFunc func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) + subscribeLogsFunc func(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + filterLogsFunc func(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) } type Option func(*mockEvm) @@ -49,6 +52,12 @@ func WithBlockNumFunc(blockNumFunc func(ctx context.Context) (uint64, error)) Op } } +func WithBlockByNumberFunc(blockByNumberFunc func(ctx context.Context, blockNumber *big.Int) (*types.Block, error)) Option { + return func(m *mockEvm) { + m.blockByNumberFunc = blockByNumberFunc + } +} + func WithPendingNonceAtFunc(pendingNonceAtFunc func(ctx context.Context, account common.Address) (uint64, error)) Option { return func(m *mockEvm) { m.pendingNonceAtFunc = pendingNonceAtFunc @@ -103,6 +112,18 @@ func WithTransactionByHashFunc(transactionByHashFunc func(ctx context.Context, t } } +func WithSubscribeLogsFunc(subscribeLogsFunc func(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)) Option { + return func(m *mockEvm) { + m.subscribeLogsFunc = subscribeLogsFunc + } +} + +func WithFilterLogs(filterLogsFunc func(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)) Option { + return func(m *mockEvm) { + m.filterLogsFunc = filterLogsFunc + } +} + func NewMockEvm(networkID uint64, opts ...Option) *mockEvm { m := &mockEvm{} for _, opt := range opts { @@ -132,6 +153,13 @@ func (m *mockEvm) BlockNumber(ctx context.Context) (uint64, error) { return 0, ErrNotImplemented } +func (m *mockEvm) BlockByNumber(ctx context.Context, blockNumber *big.Int) (*types.Block, error) { + if m.blockByNumberFunc != nil { + return m.blockByNumberFunc(ctx, blockNumber) + } + return nil, ErrNotImplemented +} + func (m *mockEvm) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { if m.pendingNonceAtFunc != nil { return m.pendingNonceAtFunc(ctx, account) @@ -204,3 +232,24 @@ func (m *mockEvm) TransactionByHash( } return nil, false, ErrNotImplemented } + +func (m *mockEvm) SubscribeFilterLogs( + ctx context.Context, + query ethereum.FilterQuery, + ch chan<- types.Log, +) (ethereum.Subscription, error) { + if m.subscribeLogsFunc != nil { + return m.subscribeLogsFunc(ctx, query, ch) + } + return nil, ErrNotImplemented +} + +func (m *mockEvm) FilterLogs( + ctx context.Context, + query ethereum.FilterQuery, +) ([]types.Log, error) { + if m.filterLogsFunc != nil { + return m.filterLogsFunc(ctx, query) + } + return nil, ErrNotImplemented +} \ No newline at end of file diff --git a/pkg/keyexchange/keyexchange.go b/pkg/keyexchange/keyexchange.go new file mode 100644 index 00000000..fce96c17 --- /dev/null +++ b/pkg/keyexchange/keyexchange.go @@ -0,0 +1,289 @@ +package keyexchange + +import ( + "bytes" + "context" + "crypto/rand" + "errors" + "fmt" + "log/slog" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto/ecies" + keyexchangepb "github.com/primevprotocol/mev-commit/gen/go/keyexchange/v1" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + "github.com/primevprotocol/mev-commit/pkg/p2p" + "github.com/primevprotocol/mev-commit/pkg/signer" + "github.com/primevprotocol/mev-commit/pkg/topology" + "google.golang.org/protobuf/proto" +) + +func New( + topo Topology, + streamer p2p.Streamer, + keyKeeper keykeeper.KeyKeeper, + logger *slog.Logger, + signer signer.Signer, +) *KeyExchange { + return &KeyExchange{ + topo: topo, + streamer: streamer, + keyKeeper: keyKeeper, + logger: logger, + signer: signer, + } +} + +func (ke *KeyExchange) timestampMessageStream() p2p.StreamDesc { + return p2p.StreamDesc{ + Name: ProtocolName, + Version: ProtocolVersion, + Handler: ke.handleTimestampMessage, + } +} + +func (ke *KeyExchange) Streams() []p2p.StreamDesc { + return []p2p.StreamDesc{ke.timestampMessageStream()} +} + +func (ke *KeyExchange) SendTimestampMessage() error { + providers, err := ke.getProviders() + if err != nil { + ke.logger.Error("getting providers", "error", err) + return ErrNoProvidersAvailable + } + + encryptedKeys, timestampMessage, err := ke.prepareMessages(providers) + if err != nil { + return err + } + + if err := ke.distributeMessages(providers, encryptedKeys, timestampMessage); err != nil { + return err + } + + return nil +} + +func (ke *KeyExchange) getProviders() ([]p2p.Peer, error) { + providers := ke.topo.GetPeers(topology.Query{Type: p2p.PeerTypeProvider}) + if len(providers) == 0 { + return nil, ErrNoProvidersAvailable + } + return providers, nil +} + +func (ke *KeyExchange) prepareMessages(providers []p2p.Peer) ([][]byte, []byte, error) { + bidderKK, ok := ke.keyKeeper.(*keykeeper.BidderKeyKeeper) + if !ok { + return nil, nil, fmt.Errorf("keyKeeper is not of type BidderKeyKeeper") + } + + var encryptedKeys [][]byte + for _, provider := range providers { + encryptedKey, err := ecies.Encrypt(rand.Reader, provider.Keys.PKEPublicKey, bidderKK.AESKey, nil, nil) + if err != nil { + return nil, nil, fmt.Errorf("error encrypting key for provider %s: %w", provider.EthAddress, err) + } + encryptedKeys = append(encryptedKeys, encryptedKey) + } + + timestampMessage := fmt.Sprintf("mev-commit bidder %s setup %d", bidderKK.KeySigner.GetAddress(), time.Now().Unix()) + encryptedTimestampMessage, err := keykeeper.EncryptWithAESGCM(bidderKK.AESKey, []byte(timestampMessage)) + if err != nil { + return nil, nil, fmt.Errorf("error encrypting timestamp message: %w", err) + } + + return encryptedKeys, encryptedTimestampMessage, nil +} + +func (ke *KeyExchange) distributeMessages(providers []p2p.Peer, encryptedKeys [][]byte, timestampMessage []byte) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ekmWithSignature, err := ke.createSignedMessage(encryptedKeys, timestampMessage) + if err != nil { + return fmt.Errorf("error creating signed message: %w", err) + } + + var wg sync.WaitGroup + errorsChan := make(chan error, len(providers)) + + for _, provider := range providers { + wg.Add(1) + go func(provider p2p.Peer) { + defer wg.Done() + if err := ke.sendMessageToProvider(ctx, provider, ekmWithSignature); err != nil { + errorsChan <- err + ke.logger.Error("error sending message to provider", "provider", provider.EthAddress, "error", err) + } + }(provider) + } + + wg.Wait() + close(errorsChan) + + if len(errorsChan) > 0 { + return fmt.Errorf("errors occurred while distributing messages") + } + + return nil +} + +func (ke *KeyExchange) createSignedMessage(encryptedKeys [][]byte, timestampMessage []byte) (*keyexchangepb.EKMWithSignature, error) { + message := keyexchangepb.EncryptedKeysMessage{ + EncryptedKeys: encryptedKeys, + TimestampMessage: timestampMessage, + } + + messageBytes, err := proto.Marshal(&message) + if err != nil { + return nil, fmt.Errorf("failed to marshal message: %w", err) + } + + hashedMessage := hashData(messageBytes) + + bidderKK := ke.keyKeeper.(*keykeeper.BidderKeyKeeper) + + signature, err := bidderKK.KeySigner.SignHash(hashedMessage.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to sign message: %w", err) + } + + ekmWithSignature := &keyexchangepb.EKMWithSignature{ + Message: messageBytes, + Signature: signature, + } + + return ekmWithSignature, nil +} + +func (ke *KeyExchange) sendMessageToProvider(ctx context.Context, provider p2p.Peer, ekmWithSignature *keyexchangepb.EKMWithSignature) error { + stream, err := ke.streamer.NewStream( + ctx, + provider, + nil, + ke.timestampMessageStream(), + ) + if err != nil { + return fmt.Errorf("failed to create new stream to provider %s: %w", provider.EthAddress, err) + } + defer stream.Close() + + err = stream.WriteMsg(ctx, ekmWithSignature) + if err != nil { + _ = stream.Reset() + return fmt.Errorf("failed to send message to provider %s: %w", provider.EthAddress, err) + } + + return nil +} + +func (ke *KeyExchange) handleTimestampMessage(ctx context.Context, peer p2p.Peer, stream p2p.Stream) error { + ekmWithSignature, err := ke.readAndVerifyMessage(ctx, peer, stream) + if err != nil { + return fmt.Errorf("read and verify message failed: %w", err) + } + + message, aesKey, err := ke.decryptMessage(ekmWithSignature) + if err != nil { + return fmt.Errorf("decrypt message failed: %w", err) + } + + if err := ke.validateAndProcessTimestamp(message); err != nil { + return fmt.Errorf("validate and process timestamp failed: %w", err) + } + + ke.keyKeeper.(*keykeeper.ProviderKeyKeeper).SetAESKey(peer.EthAddress, aesKey) + + ke.logger.Info("successfully processed timestamp message", "peer", peer.EthAddress, "key", aesKey) + + return nil +} + +func (ke *KeyExchange) readAndVerifyMessage(ctx context.Context, peer p2p.Peer, stream p2p.Stream) (*keyexchangepb.EKMWithSignature, error) { + if peer.Type != p2p.PeerTypeBidder { + return nil, ErrInvalidBidderTypeForMessage + } + + ekmWithSignature := new(keyexchangepb.EKMWithSignature) + + err := stream.ReadMsg(ctx, ekmWithSignature) + if err != nil { + return nil, err + } + + err = ke.verifySignature(peer, ekmWithSignature) + if err != nil { + return nil, fmt.Errorf("verification failed: %w", err) + } + + return ekmWithSignature, nil +} + +func (ke *KeyExchange) verifySignature(peer p2p.Peer, ekm *keyexchangepb.EKMWithSignature) error { + verified, ethAddress, err := ke.signer.Verify(ekm.Signature, ekm.Message) + if err != nil { + return errors.Join(err, ErrSignatureVerificationFailed) + } + + if !verified { + return ErrSignatureVerificationFailed + } + + if !bytes.Equal(peer.EthAddress.Bytes(), ethAddress.Bytes()) { + return ErrObservedAddressMismatch + } + + return nil +} + +func (ke *KeyExchange) decryptMessage(ekmWithSignature *keyexchangepb.EKMWithSignature) ([]byte, []byte, error) { + var ( + aesKey []byte + decrypted bool + err error + message keyexchangepb.EncryptedKeysMessage + ) + + err = proto.Unmarshal(ekmWithSignature.Message, &message) + if err != nil { + return nil, nil, fmt.Errorf("failed to unmarshal message: %w", err) + } + + providerKK := ke.keyKeeper.(*keykeeper.ProviderKeyKeeper) + + for i := 0; i < len(message.EncryptedKeys); i++ { + aesKey, err = providerKK.DecryptWithECIES(message.EncryptedKeys[i]) + if err == nil { + decrypted = true + break // Successfully decrypted AES key, stop trying further keys + } + } + + if !decrypted { + return nil, nil, fmt.Errorf("none of the AES keys could be decrypted") + } + + encryptedMessage := message.TimestampMessage + decryptedMessage, err := keykeeper.DecryptWithAESGCM(aesKey, encryptedMessage) + if err != nil { + return nil, nil, fmt.Errorf("failed to decrypt message: %w", err) + } + + return decryptedMessage, aesKey, nil +} + +func (ke *KeyExchange) validateAndProcessTimestamp(message []byte) error { + _, timestamp, err := parseTimestampMessage(string(message)) + if err != nil { + return fmt.Errorf("failed to parse message: %w", err) + } + + if !isTimestampRecent(timestamp) { + return fmt.Errorf("the timestamp is more than 1 minute old") + } + + return nil +} diff --git a/pkg/keyexchange/keyexchange_test.go b/pkg/keyexchange/keyexchange_test.go new file mode 100644 index 00000000..82cba0d5 --- /dev/null +++ b/pkg/keyexchange/keyexchange_test.go @@ -0,0 +1,106 @@ +package keyexchange_test + +import ( + "bytes" + "io" + "os" + "testing" + "time" + + "log/slog" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/primevprotocol/mev-commit/pkg/keyexchange" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner/mock" + "github.com/primevprotocol/mev-commit/pkg/p2p" + p2ptest "github.com/primevprotocol/mev-commit/pkg/p2p/testing" + "github.com/primevprotocol/mev-commit/pkg/signer" + "github.com/primevprotocol/mev-commit/pkg/topology" +) + +type testTopology struct { + peers []p2p.Peer +} + +func (tt *testTopology) GetPeers(q topology.Query) []p2p.Peer { + return tt.peers +} + +func newTestLogger(t *testing.T, w io.Writer) *slog.Logger { + t.Helper() + + testLogger := slog.NewTextHandler(w, &slog.HandlerOptions{ + Level: slog.LevelDebug, + }) + return slog.New(testLogger) +} + +func TestKeyExchange_SendAndHandleTimestampMessage(t *testing.T) { + t.Parallel() + + privKey, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + address := crypto.PubkeyToAddress(privKey.PublicKey) + ks := mockkeysigner.NewMockKeySigner(privKey, address) + bidderKK, err := keykeeper.NewBidderKeyKeeper(ks) + if err != nil { + t.Fatalf("Failed to create BidderKeyKeeper: %v", err) + } + + providerKK, err := keykeeper.NewProviderKeyKeeper(ks) + if err != nil { + t.Fatalf("Failed to create ProviderKeyKeeper: %v", err) + } + + bidderPeer := p2p.Peer{ + EthAddress: bidderKK.KeySigner.GetAddress(), + Type: p2p.PeerTypeBidder, + } + + providerPeer := p2p.Peer{ + EthAddress: providerKK.KeySigner.GetAddress(), + Type: p2p.PeerTypeProvider, + Keys: &p2p.Keys{PKEPublicKey: providerKK.GetECIESPublicKey(), NIKEPublicKey: providerKK.GetNIKEPublicKey()}, + } + topo1 := &testTopology{peers: []p2p.Peer{providerPeer}} + topo2 := &testTopology{peers: []p2p.Peer{bidderPeer}} + + logger := newTestLogger(t, os.Stdout) + + signer := signer.New() + svc1 := p2ptest.New( + &bidderPeer, + ) + + svc2 := p2ptest.New( + &providerPeer, + ) + + ke1 := keyexchange.New(topo1, svc1, bidderKK, logger, signer) + ke2 := keyexchange.New(topo2, svc2, providerKK, logger, signer) + + svc1.SetPeerHandler(bidderPeer, ke2.Streams()[0]) + + err = ke1.SendTimestampMessage() + if err != nil { + t.Fatalf("SendTimestampMessage failed: %v", err) + } + + start := time.Now() + for { + if time.Since(start) > 5*time.Second { + t.Fatal("timed out") + } + aesKey, exists := providerKK.GetAESKey(bidderPeer.EthAddress) + if exists { + if !bytes.Equal(bidderKK.AESKey, aesKey) { + t.Fatal("AES keys are not equal") + } + break + } + time.Sleep(100 * time.Millisecond) + } +} diff --git a/pkg/keyexchange/models.go b/pkg/keyexchange/models.go new file mode 100644 index 00000000..ff41c9f0 --- /dev/null +++ b/pkg/keyexchange/models.go @@ -0,0 +1,40 @@ +package keyexchange + +import ( + "errors" + "log/slog" + + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + "github.com/primevprotocol/mev-commit/pkg/p2p" + "github.com/primevprotocol/mev-commit/pkg/signer" + "github.com/primevprotocol/mev-commit/pkg/topology" +) + +// Protocol constants. +const ( + ProtocolName = "keyexchange" + ProtocolHandlerName = "timestampMessage" + ProtocolVersion = "1.0.0" +) + +// Error declarations. +var ( + ErrSignatureVerificationFailed = errors.New("signature verification failed") + ErrObservedAddressMismatch = errors.New("observed address mismatch") + ErrInvalidBidderTypeForMessage = errors.New("invalid bidder type for message") + ErrNoProvidersAvailable = errors.New("no providers available") +) + +// KeyExchange manages the key exchange process. +type KeyExchange struct { + keyKeeper keykeeper.KeyKeeper + topo Topology + streamer p2p.Streamer + signer signer.Signer + logger *slog.Logger +} + +// Topology interface to get peers. +type Topology interface { + GetPeers(topology.Query) []p2p.Peer +} diff --git a/pkg/keyexchange/util.go b/pkg/keyexchange/util.go new file mode 100644 index 00000000..2631061f --- /dev/null +++ b/pkg/keyexchange/util.go @@ -0,0 +1,37 @@ +package keyexchange + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func hashData(data []byte) common.Hash { + hash := crypto.Keccak256Hash(data) + return hash +} + +func parseTimestampMessage(msg string) (string, int64, error) { + parts := strings.Fields(msg) + if len(parts) != 5 || parts[0] != "mev-commit" || parts[1] != "bidder" || parts[3] != "setup" { + return "", 0, fmt.Errorf("message format is incorrect") + } + address := parts[2] + timestamp, err := strconv.ParseInt(parts[4], 10, 64) + if err != nil { + return "", 0, fmt.Errorf("invalid timestamp") + } + + return address, timestamp, nil +} + +func isTimestampRecent(timestamp int64) bool { + currentTime := time.Now().Unix() + difference := currentTime - timestamp + + return difference <= 60 +} diff --git a/pkg/keykeeper/aes.go b/pkg/keykeeper/aes.go new file mode 100644 index 00000000..337ad28e --- /dev/null +++ b/pkg/keykeeper/aes.go @@ -0,0 +1,50 @@ +package keykeeper + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" +) + +func generateAESKey() ([]byte, error) { + aesKey := make([]byte, 32) // AES-256 + _, err := rand.Read(aesKey) + if err != nil { + return nil, err + } + return aesKey, nil +} + +func EncryptWithAESGCM(aesKey, plaintext []byte) ([]byte, error) { + block, err := aes.NewCipher(aesKey) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + nonce := make([]byte, aesgcm.NonceSize()) + if _, err := rand.Read(nonce); err != nil { + return nil, err + } + ciphertext := aesgcm.Seal(nonce, nonce, plaintext, nil) + return ciphertext, nil +} + +func DecryptWithAESGCM(aesKey, ciphertext []byte) ([]byte, error) { + block, err := aes.NewCipher(aesKey) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + nonce := ciphertext[:aesgcm.NonceSize()] + plaintext, err := aesgcm.Open(nil, nonce, ciphertext[aesgcm.NonceSize():], nil) + if err != nil { + return nil, err + } + return plaintext, nil +} diff --git a/pkg/keykeeper/ecies.go b/pkg/keykeeper/ecies.go new file mode 100644 index 00000000..74b67dba --- /dev/null +++ b/pkg/keykeeper/ecies.go @@ -0,0 +1,25 @@ +package keykeeper + +import ( + "crypto/elliptic" + "errors" + + "github.com/ethereum/go-ethereum/crypto/ecies" +) + +func SerializePublicKey(pub *ecies.PublicKey) []byte { + return elliptic.MarshalCompressed(elliptic.P256(), pub.X, pub.Y) +} + +func DeserializePublicKey(data []byte) (*ecies.PublicKey, error) { + x, y := elliptic.UnmarshalCompressed(elliptic.P256(), data) + if x == nil { + return nil, errors.New("invalid public key") + } + return &ecies.PublicKey{ + X: x, + Y: y, + Curve: elliptic.P256(), + Params: ecies.ECIES_AES128_SHA256, + }, nil +} diff --git a/pkg/keykeeper/keykeeper.go b/pkg/keykeeper/keykeeper.go new file mode 100644 index 00000000..396bbdf3 --- /dev/null +++ b/pkg/keykeeper/keykeeper.go @@ -0,0 +1,115 @@ +package keykeeper + +import ( + "crypto/ecdh" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner" +) + +// NewBaseKeyKeeper creates a new BaseKeyKeeper. +func NewBaseKeyKeeper(keysigner keysigner.KeySigner) *BaseKeyKeeper { + return &BaseKeyKeeper{KeySigner: keysigner} +} + +func (bkk *BaseKeyKeeper) SignHash(data []byte) ([]byte, error) { + return bkk.KeySigner.SignHash(data) +} + +func (bkk *BaseKeyKeeper) GetAddress() common.Address { + return bkk.KeySigner.GetAddress() +} + +func (bkk *BaseKeyKeeper) GetPrivateKey() (*ecdsa.PrivateKey, error) { + return bkk.KeySigner.GetPrivateKey() +} + +func (bkk *BaseKeyKeeper) ZeroPrivateKey(key *ecdsa.PrivateKey) { + bkk.KeySigner.ZeroPrivateKey(key) +} + +func NewBidderKeyKeeper(keysigner keysigner.KeySigner) (*BidderKeyKeeper, error) { + aesKey, err := generateAESKey() + if err != nil { + return nil, err + } + + bidHashesToNIKE := make(map[string]*ecdh.PrivateKey) + + return &BidderKeyKeeper{ + BaseKeyKeeper: NewBaseKeyKeeper(keysigner), + AESKey: aesKey, + BidHashesToNIKE: bidHashesToNIKE, + }, nil +} + +func (bkk *BidderKeyKeeper) GenerateNIKEKeys(bidHash []byte) (*ecdh.PublicKey, error) { + nikePrivateKey, err := ecdh.P256().GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + nikePublicKey := nikePrivateKey.PublicKey() + bkk.BidHashesToNIKE[hex.EncodeToString(bidHash)] = nikePrivateKey + return nikePublicKey, nil +} + +func NewProviderKeyKeeper(keysigner keysigner.KeySigner) (*ProviderKeyKeeper, error) { + biddersAESKeys := make(map[common.Address][]byte) + + encryptionPrivateKey, err := ecies.GenerateKey(rand.Reader, elliptic.P256(), nil) + if err != nil { + return nil, err + } + + nikePrivateKey, err := ecdh.P256().GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + + return &ProviderKeyKeeper{ + BaseKeyKeeper: NewBaseKeyKeeper(keysigner), + BiddersAESKeys: biddersAESKeys, + bidderAESKeysMutex: &sync.RWMutex{}, + keys: ProviderKeys{ + EncryptionPrivateKey: encryptionPrivateKey, + EncryptionPublicKey: &encryptionPrivateKey.PublicKey, + NIKEPrivateKey: nikePrivateKey, + NIKEPublicKey: nikePrivateKey.PublicKey(), + }, + }, nil +} + +func (pkk *ProviderKeyKeeper) GetNIKEPublicKey() *ecdh.PublicKey { + return pkk.keys.NIKEPublicKey +} + +func (pkk *ProviderKeyKeeper) GetECIESPublicKey() *ecies.PublicKey { + return pkk.keys.EncryptionPublicKey +} + +func (pkk *ProviderKeyKeeper) DecryptWithECIES(message []byte) ([]byte, error) { + return pkk.keys.EncryptionPrivateKey.Decrypt(message, nil, nil) +} + +func (pkk *ProviderKeyKeeper) GetNIKEPrivateKey() *ecdh.PrivateKey { + return pkk.keys.NIKEPrivateKey +} + +func (pkk *ProviderKeyKeeper) SetAESKey(bidder common.Address, key []byte) { + pkk.bidderAESKeysMutex.Lock() + defer pkk.bidderAESKeysMutex.Unlock() + pkk.BiddersAESKeys[bidder] = key +} + +func (pkk *ProviderKeyKeeper) GetAESKey(bidder common.Address) ([]byte, bool) { + pkk.bidderAESKeysMutex.RLock() + defer pkk.bidderAESKeysMutex.RUnlock() + key, exists := pkk.BiddersAESKeys[bidder] + return key, exists +} diff --git a/pkg/keysigner/keysigner.go b/pkg/keykeeper/keysigner/keysigner.go similarity index 100% rename from pkg/keysigner/keysigner.go rename to pkg/keykeeper/keysigner/keysigner.go diff --git a/pkg/keysigner/keystoresigner.go b/pkg/keykeeper/keysigner/keystoresigner.go similarity index 100% rename from pkg/keysigner/keystoresigner.go rename to pkg/keykeeper/keysigner/keystoresigner.go diff --git a/pkg/keysigner/mock/mock.go b/pkg/keykeeper/keysigner/mock/mock.go similarity index 100% rename from pkg/keysigner/mock/mock.go rename to pkg/keykeeper/keysigner/mock/mock.go diff --git a/pkg/keysigner/privatekeysigner.go b/pkg/keykeeper/keysigner/privatekeysigner.go similarity index 100% rename from pkg/keysigner/privatekeysigner.go rename to pkg/keykeeper/keysigner/privatekeysigner.go diff --git a/pkg/keykeeper/models.go b/pkg/keykeeper/models.go new file mode 100644 index 00000000..286f5dc0 --- /dev/null +++ b/pkg/keykeeper/models.go @@ -0,0 +1,42 @@ +package keykeeper + +import ( + "crypto/ecdh" + "crypto/ecdsa" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner" +) + +type KeyKeeper interface { + SignHash(data []byte) ([]byte, error) + GetAddress() common.Address + GetPrivateKey() (*ecdsa.PrivateKey, error) + ZeroPrivateKey(key *ecdsa.PrivateKey) +} + +type BaseKeyKeeper struct { + KeySigner keysigner.KeySigner +} + +type ProviderKeys struct { + EncryptionPrivateKey *ecies.PrivateKey + EncryptionPublicKey *ecies.PublicKey + NIKEPrivateKey *ecdh.PrivateKey + NIKEPublicKey *ecdh.PublicKey +} + +type ProviderKeyKeeper struct { + *BaseKeyKeeper + keys ProviderKeys + bidderAESKeysMutex *sync.RWMutex + BiddersAESKeys map[common.Address][]byte +} + +type BidderKeyKeeper struct { + *BaseKeyKeeper + AESKey []byte + BidHashesToNIKE map[string]*ecdh.PrivateKey +} diff --git a/pkg/node/node.go b/pkg/node/node.go index 98008786..ed8203df 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -10,29 +10,41 @@ import ( "math/big" "net" "net/http" + "strings" "time" "github.com/bufbuild/protovalidate-go" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + bidderregistry "github.com/primevprotocol/contracts-abi/clients/BidderRegistry" + blocktracker "github.com/primevprotocol/contracts-abi/clients/BlockTracker" + preconf "github.com/primevprotocol/contracts-abi/clients/PreConfCommitmentStore" bidderapiv1 "github.com/primevprotocol/mev-commit/gen/go/bidderapi/v1" preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" providerapiv1 "github.com/primevprotocol/mev-commit/gen/go/providerapi/v1" + "github.com/primevprotocol/mev-commit/pkg/depositmanager" "github.com/primevprotocol/mev-commit/pkg/apiserver" bidder_registrycontract "github.com/primevprotocol/mev-commit/pkg/contracts/bidder_registry" + blocktrackercontract "github.com/primevprotocol/mev-commit/pkg/contracts/block_tracker" preconfcontract "github.com/primevprotocol/mev-commit/pkg/contracts/preconf" provider_registrycontract "github.com/primevprotocol/mev-commit/pkg/contracts/provider_registry" "github.com/primevprotocol/mev-commit/pkg/debugapi" "github.com/primevprotocol/mev-commit/pkg/discovery" + "github.com/primevprotocol/mev-commit/pkg/events" "github.com/primevprotocol/mev-commit/pkg/evmclient" - "github.com/primevprotocol/mev-commit/pkg/keysigner" + "github.com/primevprotocol/mev-commit/pkg/keyexchange" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner" "github.com/primevprotocol/mev-commit/pkg/p2p" "github.com/primevprotocol/mev-commit/pkg/p2p/libp2p" "github.com/primevprotocol/mev-commit/pkg/preconfirmation" bidderapi "github.com/primevprotocol/mev-commit/pkg/rpc/bidder" providerapi "github.com/primevprotocol/mev-commit/pkg/rpc/provider" - "github.com/primevprotocol/mev-commit/pkg/signer/preconfsigner" + "github.com/primevprotocol/mev-commit/pkg/signer" + "github.com/primevprotocol/mev-commit/pkg/signer/preconfencryptor" + "github.com/primevprotocol/mev-commit/pkg/store" "github.com/primevprotocol/mev-commit/pkg/topology" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" @@ -56,16 +68,19 @@ type Options struct { RPCAddr string Bootnodes []string PreconfContract string + BlockTrackerContract string ProviderRegistryContract string BidderRegistryContract string RPCEndpoint string + WSRPCEndpoint string NatAddr string TLSCertificateFile string TLSPrivateKeyFile string } type Node struct { - closers []io.Closer + waitClose func() + closers []io.Closer } func NewNode(opts *Options) (*Node, error) { @@ -78,6 +93,7 @@ func NewNode(opts *Options) (*Node, error) { contractRPC, err := ethclient.Dial(opts.RPCEndpoint) if err != nil { + opts.Logger.Error("failed to connect to rpc", "error", err) return nil, err } evmClient, err := evmclient.New( @@ -86,15 +102,32 @@ func NewNode(opts *Options) (*Node, error) { opts.Logger.With("component", "evmclient"), ) if err != nil { + opts.Logger.Error("failed to create evm client", "error", err) return nil, err } nd.closers = append(nd.closers, evmClient) - srv.MetricsRegistry().MustRegister(evmClient.Metrics()...) + wsRPC, err := ethclient.Dial(opts.WSRPCEndpoint) + if err != nil { + opts.Logger.Error("failed to connect to ws rpc", "error", err) + return nil, err + } + wsEvmClient, err := evmclient.New( + opts.KeySigner, + evmclient.WrapEthClient(wsRPC), + opts.Logger.With("component", "wsevmclient"), + ) + if err != nil { + opts.Logger.Error("failed to create ws evm client", "error", err) + return nil, err + } + nd.closers = append(nd.closers, wsEvmClient) + bidderRegistryContractAddr := common.HexToAddress(opts.BidderRegistryContract) bidderRegistry := bidder_registrycontract.New( + opts.KeySigner.GetAddress(), bidderRegistryContractAddr, evmClient, opts.Logger.With("component", "bidderregistry"), @@ -108,8 +141,25 @@ func NewNode(opts *Options) (*Node, error) { opts.Logger.With("component", "providerregistry"), ) + var keyKeeper keykeeper.KeyKeeper + switch opts.PeerType { + case p2p.PeerTypeProvider.String(): + keyKeeper, err = keykeeper.NewProviderKeyKeeper(opts.KeySigner) + if err != nil { + opts.Logger.Error("failed to create provider key keeper", "error", err) + return nil, errors.Join(err, nd.Close()) + } + case p2p.PeerTypeBidder.String(): + keyKeeper, err = keykeeper.NewBidderKeyKeeper(opts.KeySigner) + if err != nil { + opts.Logger.Error("failed to create bidder key keeper", "error", err) + return nil, errors.Join(err, nd.Close()) + } + default: + keyKeeper = keykeeper.NewBaseKeyKeeper(opts.KeySigner) + } p2pSvc, err := libp2p.New(&libp2p.Options{ - KeySigner: opts.KeySigner, + KeyKeeper: keyKeeper, Secret: opts.Secret, PeerType: peerType, Register: providerRegistry, @@ -121,6 +171,7 @@ func NewNode(opts *Options) (*Node, error) { NatAddr: opts.NatAddr, }) if err != nil { + opts.Logger.Error("failed to create p2p service", "error", err) return nil, err } nd.closers = append(nd.closers, p2pSvc) @@ -141,9 +192,37 @@ func NewNode(opts *Options) (*Node, error) { debugapi.RegisterAPI(srv, topo, p2pSvc, opts.Logger.With("component", "debugapi")) + ctx, cancel := context.WithCancel(context.Background()) + + var preconfProtoClosed <-chan struct{} + st, err := store.NewStore() + if err != nil { + opts.Logger.Error("failed to create store", "error", err) + cancel() + return nil, err + } + + contracts, err := getContractABIs(opts) + if err != nil { + opts.Logger.Error("failed to get contract ABIs", "error", err) + cancel() + return nil, err + } + + evtMgr := events.NewListener( + opts.Logger.With("component", "events"), + evmClient, + st, + contracts, + ) + + evtMgrDone := evtMgr.Start(ctx) + if opts.PeerType != p2p.PeerTypeBootnode.String() { lis, err := net.Listen("tcp", opts.RPCAddr) if err != nil { + opts.Logger.Error("failed to listen", "error", err) + cancel() return nil, errors.Join(err, nd.Close()) } @@ -154,22 +233,50 @@ func NewNode(opts *Options) (*Node, error) { opts.TLSPrivateKeyFile, ) if err != nil { + opts.Logger.Error("failed to load TLS credentials", "error", err) + cancel() return nil, fmt.Errorf("unable to load TLS credentials: %w", err) } } grpcServer := grpc.NewServer(grpc.Creds(tlsCredentials)) - preconfSigner := preconfsigner.NewSigner(opts.KeySigner) + preconfEncryptor := preconfencryptor.NewEncryptor(keyKeeper) validator, err := protovalidate.New() if err != nil { + opts.Logger.Error("failed to create proto validator", "error", err) + cancel() return nil, errors.Join(err, nd.Close()) } var ( - bidProcessor preconfirmation.BidProcessor = noOpBidProcessor{} - commitmentDA preconfcontract.Interface = noOpCommitmentDA{} + bidProcessor preconfirmation.BidProcessor = noOpBidProcessor{} + depositMgr preconfirmation.DepositManager = noOpDepositManager{} + ) + + blockTrackerAddr := common.HexToAddress(opts.BlockTrackerContract) + + blockTracker := blocktrackercontract.New( + blockTrackerAddr, + evmClient, + wsEvmClient, + opts.Logger.With("component", "blocktrackercontract"), ) + preconfContractAddr := common.HexToAddress(opts.PreconfContract) + commitmentDA := preconfcontract.New( + preconfContractAddr, + evmClient, + opts.Logger.With("component", "preconfcontract"), + ) + opts.Logger.Info("registered preconf contract") + + store, err := store.NewStore() + if err != nil { + opts.Logger.Error("failed to create store", "error", err) + cancel() + return nil, err + } + switch opts.PeerType { case p2p.PeerTypeProvider.String(): providerAPI := providerapi.NewService( @@ -182,48 +289,87 @@ func NewNode(opts *Options) (*Node, error) { providerapiv1.RegisterProviderServer(grpcServer, providerAPI) bidProcessor = providerAPI srv.RegisterMetricsCollectors(providerAPI.Metrics()...) - - preconfContractAddr := common.HexToAddress(opts.PreconfContract) - - commitmentDA = preconfcontract.New( - preconfContractAddr, - evmClient, - opts.Logger.With("component", "preconfcontract"), + depositMgr = depositmanager.NewDepositManager(bidderRegistry, + blockTracker, + commitmentDA, + store, + evtMgr, + opts.Logger.With("component", "depositmanager"), ) - + depositMgr.Start(ctx) preconfProto := preconfirmation.New( + keyKeeper.GetAddress(), topo, p2pSvc, - preconfSigner, - bidderRegistry, + preconfEncryptor, + depositMgr, bidProcessor, commitmentDA, + blockTracker, + evtMgr, + store, opts.Logger.With("component", "preconfirmation_protocol"), ) + + preconfProtoClosed = preconfProto.Start(ctx) + // Only register handler for provider p2pSvc.AddStreamHandlers(preconfProto.Streams()...) + keyexchange := keyexchange.New( + topo, + p2pSvc, + keyKeeper, + opts.Logger.With("component", "keyexchange_protocol"), + signer.New(), + ) + p2pSvc.AddStreamHandlers(keyexchange.Streams()...) srv.RegisterMetricsCollectors(preconfProto.Metrics()...) case p2p.PeerTypeBidder.String(): preconfProto := preconfirmation.New( + keyKeeper.GetAddress(), topo, p2pSvc, - preconfSigner, - bidderRegistry, + preconfEncryptor, + depositMgr, bidProcessor, commitmentDA, + blockTracker, + evtMgr, + store, opts.Logger.With("component", "preconfirmation_protocol"), ) + + preconfProtoClosed = preconfProto.Start(ctx) + srv.RegisterMetricsCollectors(preconfProto.Metrics()...) bidderAPI := bidderapi.NewService( preconfProto, opts.KeySigner.GetAddress(), bidderRegistry, + blockTracker, validator, opts.Logger.With("component", "bidderapi"), ) bidderapiv1.RegisterBidderServer(grpcServer, bidderAPI) + + keyexchange := keyexchange.New( + topo, + p2pSvc, + keyKeeper, + opts.Logger.With("component", "keyexchange_protocol"), + signer.New(), + ) + topo.SubscribePeer(func(p p2p.Peer) { + if p.Type == p2p.PeerTypeProvider { + err = keyexchange.SendTimestampMessage() + if err != nil { + opts.Logger.Error("failed to send timestamp message", "error", err) + } + } + }) + srv.RegisterMetricsCollectors(bidderAPI.Metrics()...) } @@ -278,23 +424,27 @@ func NewNode(opts *Options) (*Node, error) { break } if grpcConn == nil { + cancel() return nil, errors.New("dialing of grpc server failed") } + handlerCtx, handlerCancel := context.WithTimeout(context.Background(), 3*time.Second) + defer handlerCancel() + gatewayMux := runtime.NewServeMux() - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() switch opts.PeerType { case p2p.PeerTypeProvider.String(): - err := providerapiv1.RegisterProviderHandler(ctx, gatewayMux, grpcConn) + err := providerapiv1.RegisterProviderHandler(handlerCtx, gatewayMux, grpcConn) if err != nil { opts.Logger.Error("failed to register provider handler", "err", err) + cancel() return nil, errors.Join(err, nd.Close()) } case p2p.PeerTypeBidder.String(): - err := bidderapiv1.RegisterBidderHandler(ctx, gatewayMux, grpcConn) + err := bidderapiv1.RegisterBidderHandler(handlerCtx, gatewayMux, grpcConn) if err != nil { opts.Logger.Error("failed to register bidder handler", "err", err) + cancel() return nil, errors.Join(err, nd.Close()) } } @@ -313,6 +463,7 @@ func NewNode(opts *Options) (*Node, error) { }, ), ) + opts.Logger.Info("grpc server connected and handlers are started", "state", grpcConn.GetState()) } server := &http.Server{ @@ -340,10 +491,57 @@ func NewNode(opts *Options) (*Node, error) { }() nd.closers = append(nd.closers, server) + nd.waitClose = func() { + cancel() + + closeChan := make(chan struct{}) + go func() { + defer close(closeChan) + + <-evtMgrDone + <-preconfProtoClosed + }() + + <-closeChan + } + return nd, nil } +func getContractABIs(opts *Options) (map[common.Address]*abi.ABI, error) { + abis := make(map[common.Address]*abi.ABI) + + btABI, err := abi.JSON(strings.NewReader(blocktracker.BlocktrackerABI)) + if err != nil { + return nil, err + } + abis[common.HexToAddress(opts.BlockTrackerContract)] = &btABI + + pcABI, err := abi.JSON(strings.NewReader(preconf.PreconfcommitmentstoreABI)) + if err != nil { + return nil, err + } + abis[common.HexToAddress(opts.PreconfContract)] = &pcABI + + brABI, err := abi.JSON(strings.NewReader(bidderregistry.BidderregistryABI)) + if err != nil { + return nil, err + } + abis[common.HexToAddress(opts.BidderRegistryContract)] = &brABI + + return abis, nil +} + func (n *Node) Close() error { + workersClosed := make(chan struct{}) + go func() { + defer close(workersClosed) + + if n.waitClose != nil { + n.waitClose() + } + }() + var err error for _, c := range n.closers { err = errors.Join(err, c.Close()) @@ -366,21 +564,16 @@ func (noOpBidProcessor) ProcessBid( return statusC, nil } -type noOpCommitmentDA struct{} +type noOpDepositManager struct{} -func (noOpCommitmentDA) StoreCommitment( - _ context.Context, - _ *big.Int, - _ uint64, - _ string, - _ uint64, - _ uint64, - _ []byte, - _ []byte, -) error { +func (noOpDepositManager) Start(_ context.Context) <-chan struct{} { return nil } -func (noOpCommitmentDA) Close() error { +func (noOpDepositManager) CheckAndDeductDeposit(_ context.Context, _ common.Address, _ string, _ int64) (*big.Int, error) { + return big.NewInt(0), nil +} + +func (noOpDepositManager) RefundDeposit(_ common.Address, _ *big.Int, _ int64) error { return nil } diff --git a/pkg/p2p/libp2p/internal/handshake/handshake.go b/pkg/p2p/libp2p/internal/handshake/handshake.go index 9e3dd77c..8ebaa601 100644 --- a/pkg/p2p/libp2p/internal/handshake/handshake.go +++ b/pkg/p2p/libp2p/internal/handshake/handshake.go @@ -3,6 +3,7 @@ package handshake import ( "bytes" "context" + "crypto/ecdh" "errors" "fmt" @@ -11,7 +12,7 @@ import ( "github.com/libp2p/go-libp2p/core" "github.com/libp2p/go-libp2p/core/protocol" handshakepb "github.com/primevprotocol/mev-commit/gen/go/handshake/v1" - "github.com/primevprotocol/mev-commit/pkg/keysigner" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" "github.com/primevprotocol/mev-commit/pkg/p2p" "github.com/primevprotocol/mev-commit/pkg/signer" ) @@ -34,7 +35,7 @@ type ProviderRegistry interface { // Handshake is the handshake protocol type Service struct { - ks keysigner.KeySigner + kk keykeeper.KeyKeeper peerType p2p.PeerType passcode string signer signer.Signer @@ -44,7 +45,7 @@ type Service struct { } func New( - ks keysigner.KeySigner, + kk keykeeper.KeyKeeper, peerType p2p.PeerType, passcode string, signer signer.Signer, @@ -52,7 +53,7 @@ func New( getEthAddress func(core.PeerID) (common.Address, error), ) (*Service, error) { s := &Service{ - ks: ks, + kk: kk, peerType: peerType, passcode: passcode, signer: signer, @@ -72,17 +73,6 @@ func ProtocolID() protocol.ID { return protocol.ID(fmt.Sprintf("/%s/%s", ProtocolName, ProtocolVersion)) } -type HandshakeReq struct { - PeerType string - Token string - Sig []byte -} - -type HandshakeResp struct { - ObservedAddress common.Address - PeerType string -} - func (h *Service) verifyReq( req *handshakepb.HandshakeReq, peerID core.PeerID, @@ -119,7 +109,7 @@ func (h *Service) verifyReq( func (h *Service) createSignature() ([]byte, error) { unsignedData := []byte(h.peerType.String() + h.passcode) hash := crypto.Keccak256Hash(unsignedData) - sig, err := h.ks.SignHash(hash.Bytes()) + sig, err := h.kk.SignHash(hash.Bytes()) if err != nil { return nil, err } @@ -139,12 +129,22 @@ func (h *Service) setHandshakeReq() error { Sig: sig, } + if h.peerType == p2p.PeerTypeProvider { + providerKK := h.kk.(*keykeeper.ProviderKeyKeeper) + ppk := keykeeper.SerializePublicKey(providerKK.GetECIESPublicKey()) + npk := providerKK.GetNIKEPublicKey().Bytes() + req.Keys = &handshakepb.SerializedKeys{ + PKEPublicKey: ppk, + NIKEPublicKey: npk, + } + } + h.handshakeReq = req return nil } func (h *Service) verifyResp(resp *handshakepb.HandshakeResp) error { - if !bytes.Equal(resp.ObservedAddress, h.ks.GetAddress().Bytes()) { + if !bytes.Equal(resp.ObservedAddress, h.kk.GetAddress().Bytes()) { return errors.New("observed address mismatch") } @@ -160,7 +160,6 @@ func (h *Service) Handle( stream p2p.Stream, peerID core.PeerID, ) (*p2p.Peer, error) { - req := new(handshakepb.HandshakeReq) err := stream.ReadMsg(ctx, req) if err != nil { @@ -196,10 +195,26 @@ func (h *Service) Handle( return nil, err } - return &p2p.Peer{ + p := &p2p.Peer{ EthAddress: ethAddress, Type: p2p.FromString(req.PeerType), - }, nil + } + + if req.PeerType == p2p.PeerTypeProvider.String() { + ppk, err := keykeeper.DeserializePublicKey(req.Keys.PKEPublicKey) + if err != nil { + return &p2p.Peer{}, err + } + npk, err := ecdh.P256().NewPublicKey(req.Keys.NIKEPublicKey) + if err != nil { + return &p2p.Peer{}, err + } + p.Keys = &p2p.Keys{ + PKEPublicKey: ppk, + NIKEPublicKey: npk, + } + } + return p, nil } func (h *Service) Handshake( @@ -207,7 +222,6 @@ func (h *Service) Handshake( peerID core.PeerID, stream p2p.Stream, ) (*p2p.Peer, error) { - if err := stream.WriteMsg(ctx, h.handshakeReq); err != nil { return nil, err } @@ -241,8 +255,25 @@ func (h *Service) Handshake( return nil, err } - return &p2p.Peer{ + p := &p2p.Peer{ EthAddress: ethAddress, Type: p2p.FromString(ack.PeerType), - }, nil + } + + if ack.PeerType == p2p.PeerTypeProvider.String() { + ppk, err := keykeeper.DeserializePublicKey(ack.Keys.PKEPublicKey) + if err != nil { + return &p2p.Peer{}, err + } + npk, err := ecdh.P256().NewPublicKey(ack.Keys.NIKEPublicKey) + if err != nil { + return &p2p.Peer{}, err + } + p.Keys = &p2p.Keys{ + PKEPublicKey: ppk, + NIKEPublicKey: npk, + } + } + + return p, nil } diff --git a/pkg/p2p/libp2p/internal/handshake/handshake_test.go b/pkg/p2p/libp2p/internal/handshake/handshake_test.go index f3768096..36ce06f7 100644 --- a/pkg/p2p/libp2p/internal/handshake/handshake_test.go +++ b/pkg/p2p/libp2p/internal/handshake/handshake_test.go @@ -9,7 +9,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/libp2p/go-libp2p/core" - mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keysigner/mock" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner/mock" "github.com/primevprotocol/mev-commit/pkg/p2p" "github.com/primevprotocol/mev-commit/pkg/p2p/libp2p/internal/handshake" p2ptest "github.com/primevprotocol/mev-commit/pkg/p2p/testing" @@ -46,7 +47,10 @@ func TestHandshake(t *testing.T) { } address1 := common.HexToAddress("0x1") ks1 := mockkeysigner.NewMockKeySigner(privKey1, address1) - + kk1, err := keykeeper.NewProviderKeyKeeper(ks1) + if err != nil { + t.Fatal(err) + } privKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) @@ -54,9 +58,12 @@ func TestHandshake(t *testing.T) { address2 := common.HexToAddress("0x2") ks2 := mockkeysigner.NewMockKeySigner(privKey2, address2) - + kk2, err := keykeeper.NewProviderKeyKeeper(ks2) + if err != nil { + t.Fatal(err) + } hs1, err := handshake.New( - ks1, + kk1, p2p.PeerTypeProvider, "test", &testSigner{address: address2}, @@ -70,7 +77,7 @@ func TestHandshake(t *testing.T) { } hs2, err := handshake.New( - ks2, + kk2, p2p.PeerTypeProvider, "test", &testSigner{address: address1}, @@ -105,6 +112,14 @@ func TestHandshake(t *testing.T) { t.Errorf("expected peer type %s, got %s", p2p.PeerTypeProvider, p.Type) return } + if !p.Keys.NIKEPublicKey.Equal(kk2.GetNIKEPublicKey()) { + t.Errorf("expected nike pk %s, got %s", p.Keys.NIKEPublicKey.Bytes(), kk2.GetNIKEPublicKey().Bytes()) + return + } + if !p.Keys.PKEPublicKey.ExportECDSA().Equal(kk2.GetECIESPublicKey().ExportECDSA()) { + t.Error("expected pke pk is not equal to present") + return + } }() p, err := hs2.Handshake(context.Background(), core.PeerID("test1"), out) @@ -117,6 +132,12 @@ func TestHandshake(t *testing.T) { if p.Type != p2p.PeerTypeProvider { t.Fatalf("expected peer type %s, got %s", p2p.PeerTypeProvider, p.Type) } + if !p.Keys.NIKEPublicKey.Equal(kk1.GetNIKEPublicKey()) { + t.Fatalf("expected nike pk %s, got %s", p.Keys.NIKEPublicKey.Bytes(), kk1.GetNIKEPublicKey().Bytes()) + } + if !p.Keys.PKEPublicKey.ExportECDSA().Equal(kk1.GetECIESPublicKey().ExportECDSA()) { + t.Fatalf("expected pke pk is not equal to present") + } <-done }) } diff --git a/pkg/p2p/libp2p/libp2p.go b/pkg/p2p/libp2p/libp2p.go index 198f7304..7e9b5556 100644 --- a/pkg/p2p/libp2p/libp2p.go +++ b/pkg/p2p/libp2p/libp2p.go @@ -13,7 +13,7 @@ import ( "github.com/Masterminds/semver/v3" ma "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" - "github.com/primevprotocol/mev-commit/pkg/keysigner" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" "github.com/primevprotocol/mev-commit/pkg/util" "google.golang.org/grpc/status" @@ -58,7 +58,7 @@ type ProviderRegistry interface { } type Options struct { - KeySigner keysigner.KeySigner + KeyKeeper keykeeper.KeyKeeper Secret string PeerType p2p.PeerType Register handshake.ProviderRegistry @@ -71,11 +71,11 @@ type Options struct { } func New(opts *Options) (*Service, error) { - privKey, err := opts.KeySigner.GetPrivateKey() + privKey, err := opts.KeyKeeper.GetPrivateKey() if err != nil { return nil, fmt.Errorf("failed to get priv key: %w", err) } - defer opts.KeySigner.ZeroPrivateKey(privKey) + defer opts.KeyKeeper.ZeroPrivateKey(privKey) padded32BytePrivKey := util.PadKeyTo32Bytes(privKey.D) libp2pKey, err := libp2pcrypto.UnmarshalSecp256k1PrivateKey(padded32BytePrivKey) @@ -163,7 +163,7 @@ func New(opts *Options) (*Service, error) { } hsSvc, err := handshake.New( - opts.KeySigner, + opts.KeyKeeper, opts.PeerType, opts.Secret, signer.New(), diff --git a/pkg/p2p/libp2p/libp2p_test.go b/pkg/p2p/libp2p/libp2p_test.go index e5d3e079..18642b29 100644 --- a/pkg/p2p/libp2p/libp2p_test.go +++ b/pkg/p2p/libp2p/libp2p_test.go @@ -13,7 +13,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/libp2p/go-libp2p/core/peer" - mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keysigner/mock" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner/mock" "github.com/primevprotocol/mev-commit/pkg/p2p" "github.com/primevprotocol/mev-commit/pkg/p2p/libp2p" "github.com/stretchr/testify/assert" @@ -49,8 +50,12 @@ func newTestService(t *testing.T) *libp2p.Service { } address := crypto.PubkeyToAddress(privKey.PublicKey) ks := mockkeysigner.NewMockKeySigner(privKey, address) + pkk, err := keykeeper.NewProviderKeyKeeper(ks) + if err != nil { + t.Fatal(err) + } svc, err := libp2p.New(&libp2p.Options{ - KeySigner: ks, + KeyKeeper: pkk, Secret: "test", ListenPort: 0, ListenAddr: "0.0.0.0", @@ -250,7 +255,7 @@ func TestBootstrap(t *testing.T) { ks := mockkeysigner.NewMockKeySigner(privKey, address) bnOpts := testDefaultOptions - bnOpts.KeySigner = ks + bnOpts.KeyKeeper = keykeeper.NewBaseKeyKeeper(ks) bnOpts.PeerType = p2p.PeerTypeBootnode bootnode, err := libp2p.New(&bnOpts) @@ -271,8 +276,10 @@ func TestBootstrap(t *testing.T) { n1Opts := testDefaultOptions n1Opts.BootstrapAddrs = []string{bootnode.AddrString()} - n1Opts.KeySigner = ks - + n1Opts.KeyKeeper, err = keykeeper.NewProviderKeyKeeper(ks) + if err != nil { + t.Fatal(err) + } p1, err := libp2p.New(&n1Opts) if err != nil { t.Fatal(err) diff --git a/pkg/p2p/p2p.go b/pkg/p2p/p2p.go index af4c3d7b..770cf695 100644 --- a/pkg/p2p/p2p.go +++ b/pkg/p2p/p2p.go @@ -2,10 +2,15 @@ package p2p import ( "context" + "crypto/ecdh" + "encoding/base64" + "encoding/json" "errors" "io" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" @@ -54,9 +59,64 @@ var ( ErrNoAddresses = errors.New("no addresses") ) +type Keys struct { + PKEPublicKey *ecies.PublicKey + NIKEPublicKey *ecdh.PublicKey +} + +type jsonKeys struct { + PKEPublicKey string `json:"pkePublicKey"` + NIKEPublicKey string `json:"nikePublicKey"` +} + +func (k *Keys) MarshalJSON() ([]byte, error) { + ppk := keykeeper.SerializePublicKey(k.PKEPublicKey) + pkePublicKeyB64 := base64.StdEncoding.EncodeToString(ppk) + + npk := k.NIKEPublicKey.Bytes() + nikePublicKeyB64 := base64.StdEncoding.EncodeToString(npk) + + return json.Marshal(jsonKeys{ + PKEPublicKey: pkePublicKeyB64, + NIKEPublicKey: nikePublicKeyB64, + }) +} + +func (k *Keys) UnmarshalJSON(data []byte) error { + var jk jsonKeys + if err := json.Unmarshal(data, &jk); err != nil { + return err + } + + pkePublicKeyBytes, err := base64.StdEncoding.DecodeString(jk.PKEPublicKey) + if err != nil { + return err + } + + pkePublicKey, err := keykeeper.DeserializePublicKey(pkePublicKeyBytes) + if err != nil { + return err + } + + nikePublicKeyBytes, err := base64.StdEncoding.DecodeString(jk.NIKEPublicKey) + if err != nil { + return err + } + nikePublicKey, err := ecdh.P256().NewPublicKey(nikePublicKeyBytes) + if err != nil { + return err + } + + k.PKEPublicKey = pkePublicKey + k.NIKEPublicKey = nikePublicKey + + return nil +} + type Peer struct { EthAddress common.Address Type PeerType + Keys *Keys } type PeerInfo struct { diff --git a/pkg/preconfirmation/preconfirmation.go b/pkg/preconfirmation/preconfirmation.go index c68d1a8b..bbd25ac8 100644 --- a/pkg/preconfirmation/preconfirmation.go +++ b/pkg/preconfirmation/preconfirmation.go @@ -3,34 +3,47 @@ package preconfirmation import ( "context" "errors" + "fmt" "log/slog" "math/big" "sync" "time" "github.com/ethereum/go-ethereum/common" + blocktracker "github.com/primevprotocol/contracts-abi/clients/BlockTracker" + preconfcommstore "github.com/primevprotocol/contracts-abi/clients/PreConfCommitmentStore" preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" providerapiv1 "github.com/primevprotocol/mev-commit/gen/go/providerapi/v1" + blocktrackercontract "github.com/primevprotocol/mev-commit/pkg/contracts/block_tracker" preconfcontract "github.com/primevprotocol/mev-commit/pkg/contracts/preconf" + "github.com/primevprotocol/mev-commit/pkg/events" "github.com/primevprotocol/mev-commit/pkg/p2p" - signer "github.com/primevprotocol/mev-commit/pkg/signer/preconfsigner" + encryptor "github.com/primevprotocol/mev-commit/pkg/signer/preconfencryptor" + "github.com/primevprotocol/mev-commit/pkg/store" "github.com/primevprotocol/mev-commit/pkg/topology" + "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) const ( ProtocolName = "preconfirmation" - ProtocolVersion = "2.0.0" + ProtocolVersion = "3.0.0" ) type Preconfirmation struct { - signer signer.Signer + owner common.Address + encryptor encryptor.Encryptor topo Topology streamer p2p.Streamer - us BidderStore + depositMgr DepositManager processer BidProcessor commitmentDA preconfcontract.Interface + blockTracker blocktrackercontract.Interface + evtMgr events.EventManager + ecds EncrDecrCommitmentStore + newL1Blocks chan *blocktracker.BlocktrackerNewL1Block + enryptedCmts chan *preconfcommstore.PreconfcommitmentstoreEncryptedCommitmentStored logger *slog.Logger metrics *metrics } @@ -39,36 +52,56 @@ type Topology interface { GetPeers(topology.Query) []p2p.Peer } -type BidderStore interface { - CheckBidderAllowance(context.Context, common.Address) bool -} - type BidProcessor interface { ProcessBid(context.Context, *preconfpb.Bid) (chan providerapiv1.BidResponse_Status, error) } +type EncrDecrCommitmentStore interface { + GetCommitmentsByBlockNumber(blockNum int64) ([]*store.EncryptedPreConfirmationWithDecrypted, error) + GetCommitmentByHash(commitmentHash string) (*store.EncryptedPreConfirmationWithDecrypted, error) + AddCommitment(commitment *store.EncryptedPreConfirmationWithDecrypted) + DeleteCommitmentByBlockNumber(blockNum int64) error + SetCommitmentIndexByCommitmentDigest(commitmentDigest, commitmentIndex [32]byte) error +} + +type DepositManager interface { + Start(ctx context.Context) <-chan struct{} + CheckAndDeductDeposit(ctx context.Context, ethAddress common.Address, bidAmount string, blockNumber int64) (*big.Int, error) + RefundDeposit(ethAddress common.Address, amount *big.Int, blockNumber int64) error +} + func New( + owner common.Address, topo Topology, streamer p2p.Streamer, - signer signer.Signer, - us BidderStore, + encryptor encryptor.Encryptor, + depositMgr DepositManager, processor BidProcessor, commitmentDA preconfcontract.Interface, + blockTracker blocktrackercontract.Interface, + evtMgr events.EventManager, + edcs EncrDecrCommitmentStore, logger *slog.Logger, ) *Preconfirmation { return &Preconfirmation{ + owner: owner, topo: topo, streamer: streamer, - signer: signer, - us: us, + encryptor: encryptor, + depositMgr: depositMgr, processer: processor, commitmentDA: commitmentDA, + blockTracker: blockTracker, + evtMgr: evtMgr, + ecds: edcs, + newL1Blocks: make(chan *blocktracker.BlocktrackerNewL1Block), + enryptedCmts: make(chan *preconfcommstore.PreconfcommitmentstoreEncryptedCommitmentStored), logger: logger, metrics: newMetrics(), } } -func (p *Preconfirmation) preconfStream() p2p.StreamDesc { +func (p *Preconfirmation) bidStream() p2p.StreamDesc { return p2p.StreamDesc{ Name: ProtocolName, Version: ProtocolVersion, @@ -77,7 +110,85 @@ func (p *Preconfirmation) preconfStream() p2p.StreamDesc { } func (p *Preconfirmation) Streams() []p2p.StreamDesc { - return []p2p.StreamDesc{p.preconfStream()} + return []p2p.StreamDesc{p.bidStream()} +} + +func (p *Preconfirmation) Start(ctx context.Context) <-chan struct{} { + doneChan := make(chan struct{}) + + eg, egCtx := errgroup.WithContext(ctx) + + eg.Go(func() error { + ev1 := events.NewEventHandler( + "NewL1Block", + func(newL1Block *blocktracker.BlocktrackerNewL1Block) error { + select { + case <-egCtx.Done(): + return nil + case p.newL1Blocks <- newL1Block: + return nil + } + }, + ) + + sub1, err := p.evtMgr.Subscribe(ev1) + if err != nil { + return fmt.Errorf("failed to subscribe to NewL1Block event: %w", err) + } + defer sub1.Unsubscribe() + + ev2 := events.NewEventHandler( + "EncryptedCommitmentStored", + func(ec *preconfcommstore.PreconfcommitmentstoreEncryptedCommitmentStored) error { + select { + case <-egCtx.Done(): + return nil + case p.enryptedCmts <- ec: + return nil + } + }, + ) + sub2, err := p.evtMgr.Subscribe(ev2) + if err != nil { + return fmt.Errorf("failed to subscribe to EncryptedCommitmentStored event: %w", err) + } + defer sub2.Unsubscribe() + + select { + case <-egCtx.Done(): + return nil + case err := <-sub1.Err(): + return fmt.Errorf("NewL1Block subscription error: %w", err) + case err := <-sub2.Err(): + return fmt.Errorf("EncryptedCommitmentStored subscription error: %w", err) + } + }) + + eg.Go(func() error { + for { + select { + case <-egCtx.Done(): + return nil + case newL1Block := <-p.newL1Blocks: + if err := p.handleNewL1Block(egCtx, newL1Block); err != nil { + return err + } + case ec := <-p.enryptedCmts: + if err := p.handleEncryptedCommitmentStored(egCtx, ec); err != nil { + return err + } + } + } + }) + + go func() { + defer close(doneChan) + if err := eg.Wait(); err != nil { + p.logger.Error("failed to start preconfirmation", "error", err) + } + }() + + return doneChan } // SendBid is meant to be called by the bidder to construct and send bids to the provider. @@ -92,12 +203,14 @@ func (p *Preconfirmation) SendBid( decayStartTimestamp int64, decayEndTimestamp int64, ) (chan *preconfpb.PreConfirmation, error) { - signedBid, err := p.signer.ConstructSignedBid(txHash, bidAmt, blockNumber, decayStartTimestamp, decayEndTimestamp) + startTime := time.Now() + bid, encryptedBid, err := p.encryptor.ConstructEncryptedBid(txHash, bidAmt, blockNumber, decayStartTimestamp, decayEndTimestamp) if err != nil { - p.logger.Error("constructing signed bid", "error", err, "txHash", txHash) + p.logger.Error("constructing encrypted bid", "error", err, "txHash", txHash) return nil, err } - p.logger.Info("constructed signed bid", "signedBid", signedBid) + duration := time.Since(startTime) + p.logger.Info("constructed encrypted bid", "encryptedBid", encryptedBid, "duration", duration) providers := p.topo.GetPeers(topology.Query{Type: p2p.PeerTypeProvider}) if len(providers) == 0 { @@ -120,16 +233,16 @@ func (p *Preconfirmation) SendBid( ctx, provider, nil, - p.preconfStream(), + p.bidStream(), ) if err != nil { logger.Error("creating stream", "error", err) return } - logger.Info("sending signed bid", "signedBid", signedBid) + logger.Info("sending encrypted bid", "encryptedBid", encryptedBid) - err = providerStream.WriteMsg(ctx, signedBid) + err = providerStream.WriteMsg(ctx, encryptedBid) if err != nil { _ = providerStream.Reset() logger.Error("writing message", "error", err) @@ -137,8 +250,8 @@ func (p *Preconfirmation) SendBid( } p.metrics.SentBidsCount.Inc() - preConfirmation := new(preconfpb.PreConfirmation) - err = providerStream.ReadMsg(ctx, preConfirmation) + encryptedPreConfirmation := new(preconfpb.EncryptedPreConfirmation) + err = providerStream.ReadMsg(ctx, encryptedPreConfirmation) if err != nil { _ = providerStream.Reset() logger.Error("reading message", "error", err) @@ -148,13 +261,31 @@ func (p *Preconfirmation) SendBid( _ = providerStream.Close() // Process preConfirmation as a bidder - providerAddress, err := p.signer.VerifyPreConfirmation(preConfirmation) + verifyStartTime := time.Now() + sharedSecretKey, providerAddress, err := p.encryptor.VerifyEncryptedPreConfirmation(provider.Keys.NIKEPublicKey, bid.Digest, encryptedPreConfirmation) if err != nil { logger.Error("verifying provider signature", "error", err) return } + verifyDuration := time.Since(verifyStartTime) + logger.Info("verified encrypted preconfirmation", "duration", verifyDuration) + + preConfirmation := &preconfpb.PreConfirmation{ + Bid: bid, + SharedSecret: sharedSecretKey, + Digest: encryptedPreConfirmation.Commitment, + Signature: encryptedPreConfirmation.Signature, + } + preConfirmation.ProviderAddress = make([]byte, len(providerAddress)) copy(preConfirmation.ProviderAddress, providerAddress[:]) + + encryptedAndDecryptedPreconfirmation := &store.EncryptedPreConfirmationWithDecrypted{ + EncryptedPreConfirmation: encryptedPreConfirmation, + PreConfirmation: preConfirmation, + } + + p.ecds.AddCommitment(encryptedAndDecryptedPreconfirmation) logger.Info("received preconfirmation", "preConfirmation", preConfirmation) p.metrics.ReceivedPreconfsCount.Inc() @@ -188,26 +319,39 @@ func (p *Preconfirmation) handleBid( return ErrInvalidBidderTypeForBid } - bid := new(preconfpb.Bid) - err := stream.ReadMsg(ctx, bid) + encryptedBid := new(preconfpb.EncryptedBid) + err := stream.ReadMsg(ctx, encryptedBid) if err != nil { return err } - p.logger.Info("received bid", "bid", bid) - - ethAddress, err := p.signer.VerifyBid(bid) + p.logger.Info("received bid", "encryptedBid", encryptedBid) + bid, err := p.encryptor.DecryptBidData(peer.EthAddress, encryptedBid) + if err != nil { + return err + } + ethAddress, err := p.encryptor.VerifyBid(bid) if err != nil { - p.logger.Error("verifying bid", "error", err) - return status.Errorf(codes.InvalidArgument, "invalid bid: %v", err) + return err } - if !p.us.CheckBidderAllowance(ctx, *ethAddress) { - p.logger.Error("bidder does not have enough allowance", "ethAddress", ethAddress) - return status.Errorf(codes.FailedPrecondition, "bidder not allowed") + deductedAmount, err := p.depositMgr.CheckAndDeductDeposit(ctx, *ethAddress, bid.BidAmount, bid.BlockNumber) + if err != nil { + p.logger.Error("checking deposit", "error", err) + return err } - bidAmt, _ := new(big.Int).SetString(bid.BidAmount, 10) + // Setup defer for possible refund + successful := false + defer func() { + if !successful { + // Refund the deducted amount if the bid process did not succeed + refundErr := p.depositMgr.RefundDeposit(*ethAddress, deductedAmount, bid.BlockNumber) + if refundErr != nil { + p.logger.Error("refunding deposit", "error", refundErr) + } + } + }() // try to enqueue for 5 seconds ctx, cancel := context.WithTimeout(ctx, 5*time.Second) @@ -225,28 +369,80 @@ func (p *Preconfirmation) handleBid( case providerapiv1.BidResponse_STATUS_REJECTED: return status.Errorf(codes.Internal, "bid rejected") case providerapiv1.BidResponse_STATUS_ACCEPTED: - preConfirmation, err := p.signer.ConstructPreConfirmation(bid) + preConfirmation, encryptedPreConfirmation, err := p.encryptor.ConstructEncryptedPreConfirmation(bid) if err != nil { - return status.Errorf(codes.Internal, "failed to construct preconfirmation: %v", err) + return status.Errorf(codes.Internal, "failed to constuct encrypted preconfirmation: %v", err) } - p.logger.Info("sending preconfirmation", "preConfirmation", preConfirmation) - err = p.commitmentDA.StoreCommitment( + p.logger.Info("sending preconfirmation", "preConfirmation", encryptedPreConfirmation) + _, err = p.commitmentDA.StoreEncryptedCommitment( ctx, - bidAmt, - uint64(preConfirmation.Bid.BlockNumber), - preConfirmation.Bid.TxHash, - uint64(preConfirmation.Bid.DecayStartTimestamp), - uint64(preConfirmation.Bid.DecayEndTimestamp), - preConfirmation.Bid.Signature, - preConfirmation.Signature, + encryptedPreConfirmation.Commitment, + encryptedPreConfirmation.Signature, ) if err != nil { p.logger.Error("storing commitment", "error", err) - return status.Errorf(codes.Internal, "failed to store commitment: %v", err) + return status.Errorf(codes.Internal, "failed to store commitments: %v", err) + } + + encryptedAndDecryptedPreconfirmation := &store.EncryptedPreConfirmationWithDecrypted{ + EncryptedPreConfirmation: encryptedPreConfirmation, + PreConfirmation: preConfirmation, } - return stream.WriteMsg(ctx, preConfirmation) + + p.ecds.AddCommitment(encryptedAndDecryptedPreconfirmation) + + // If we reach here, the bid was successful + successful = true + + return stream.WriteMsg(ctx, encryptedPreConfirmation) } } + return nil +} +func (p *Preconfirmation) handleNewL1Block(ctx context.Context, newL1Block *blocktracker.BlocktrackerNewL1Block) error { + p.logger.Info("New L1 Block event received", "blockNumber", newL1Block.BlockNumber, "winner", newL1Block.Winner, "window", newL1Block.Window) + commitments, err := p.ecds.GetCommitmentsByBlockNumber(newL1Block.BlockNumber.Int64()) + if err != nil { + p.logger.Error("failed to get commitments by block number", "error", err) + return err + } + for _, commitment := range commitments { + if common.BytesToAddress(commitment.ProviderAddress) != newL1Block.Winner { + p.logger.Info("provider address does not match the winner", "providerAddress", commitment.ProviderAddress, "winner", newL1Block.Winner) + continue + } + startTime := time.Now() + txHash, err := p.commitmentDA.OpenCommitment( + ctx, + commitment.EncryptedPreConfirmation.CommitmentIndex, + commitment.PreConfirmation.Bid.BidAmount, + commitment.PreConfirmation.Bid.BlockNumber, + commitment.PreConfirmation.Bid.TxHash, + commitment.PreConfirmation.Bid.DecayStartTimestamp, + commitment.PreConfirmation.Bid.DecayEndTimestamp, + commitment.PreConfirmation.Bid.Signature, + commitment.PreConfirmation.Signature, + commitment.PreConfirmation.SharedSecret, + ) + if err != nil { + // todo: retry mechanism? + p.logger.Error("failed to open commitment", "error", err) + continue + } + duration := time.Since(startTime) + p.logger.Info("opened commitment", "txHash", txHash, "duration", duration) + } + + err = p.ecds.DeleteCommitmentByBlockNumber(newL1Block.BlockNumber.Int64()) + if err != nil { + p.logger.Error("failed to delete commitments by block number", "error", err) + return err + } return nil } + +func (p *Preconfirmation) handleEncryptedCommitmentStored(ctx context.Context, ec *preconfcommstore.PreconfcommitmentstoreEncryptedCommitmentStored) error { + p.logger.Info("Encrypted Commitment Stored event received", "commitmentDigest", ec.CommitmentDigest, "commitmentIndex", ec.CommitmentIndex) + return p.ecds.SetCommitmentIndexByCommitmentDigest(ec.CommitmentDigest, ec.CommitmentIndex) +} diff --git a/pkg/preconfirmation/preconfirmation_test.go b/pkg/preconfirmation/preconfirmation_test.go index 81a77740..f6182bfa 100644 --- a/pkg/preconfirmation/preconfirmation_test.go +++ b/pkg/preconfirmation/preconfirmation_test.go @@ -2,19 +2,29 @@ package preconfirmation_test import ( "context" + "crypto/ecdh" + "crypto/elliptic" + "crypto/rand" + "errors" "io" "log/slog" "math/big" "os" + "strings" "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/ecies" + blocktracker "github.com/primevprotocol/contracts-abi/clients/BlockTracker" preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" providerapiv1 "github.com/primevprotocol/mev-commit/gen/go/providerapi/v1" + "github.com/primevprotocol/mev-commit/pkg/events" "github.com/primevprotocol/mev-commit/pkg/p2p" p2ptest "github.com/primevprotocol/mev-commit/pkg/p2p/testing" "github.com/primevprotocol/mev-commit/pkg/preconfirmation" + "github.com/primevprotocol/mev-commit/pkg/store" "github.com/primevprotocol/mev-commit/pkg/topology" ) @@ -26,33 +36,39 @@ func (t *testTopo) GetPeers(q topology.Query) []p2p.Peer { return []p2p.Peer{t.peer} } -type testBidderStore struct{} +type testEncryptor struct { + bidHash []byte + encryptedBid *preconfpb.EncryptedBid + bid *preconfpb.Bid + encryptedPreConfirmation *preconfpb.EncryptedPreConfirmation + preConfirmation *preconfpb.PreConfirmation + sharedSecretKey []byte + bidSigner common.Address + preConfirmationSigner common.Address +} -func (t *testBidderStore) CheckBidderAllowance(_ context.Context, _ common.Address) bool { - return true +func (t *testEncryptor) ConstructEncryptedBid(_ string, _ string, _ int64, _ int64, _ int64) (*preconfpb.Bid, *preconfpb.EncryptedBid, error) { + return t.bid, t.encryptedBid, nil } -type testSigner struct { - bid *preconfpb.Bid - preConfirmation *preconfpb.PreConfirmation - bidSigner common.Address - preConfirmationSigner common.Address +func (t *testEncryptor) ConstructEncryptedPreConfirmation(_ *preconfpb.Bid) (*preconfpb.PreConfirmation, *preconfpb.EncryptedPreConfirmation, error) { + return t.preConfirmation, t.encryptedPreConfirmation, nil } -func (t *testSigner) ConstructSignedBid(_ string, _ string, _ int64, _ int64, _ int64) (*preconfpb.Bid, error) { - return t.bid, nil +func (t *testEncryptor) VerifyBid(_ *preconfpb.Bid) (*common.Address, error) { + return &t.bidSigner, nil } -func (t *testSigner) ConstructPreConfirmation(_ *preconfpb.Bid) (*preconfpb.PreConfirmation, error) { - return t.preConfirmation, nil +func (t *testEncryptor) VerifyPreConfirmation(_ *preconfpb.PreConfirmation) (*common.Address, error) { + return &t.preConfirmationSigner, nil } -func (t *testSigner) VerifyBid(_ *preconfpb.Bid) (*common.Address, error) { - return &t.bidSigner, nil +func (t *testEncryptor) DecryptBidData(_ common.Address, _ *preconfpb.EncryptedBid) (*preconfpb.Bid, error) { + return t.bid, nil } -func (t *testSigner) VerifyPreConfirmation(_ *preconfpb.PreConfirmation) (*common.Address, error) { - return &t.preConfirmationSigner, nil +func (t *testEncryptor) VerifyEncryptedPreConfirmation(*ecdh.PublicKey, []byte, *preconfpb.EncryptedPreConfirmation) ([]byte, *common.Address, error) { + return t.sharedSecretKey, &t.preConfirmationSigner, nil } type testProcessor struct { @@ -61,8 +77,7 @@ type testProcessor struct { func (t *testProcessor) ProcessBid( _ context.Context, - _ *preconfpb.Bid, -) (chan providerapiv1.BidResponse_Status, error) { + _ *preconfpb.Bid) (chan providerapiv1.BidResponse_Status, error) { statusC := make(chan providerapiv1.BidResponse_Status, 1) statusC <- t.status return statusC, nil @@ -70,23 +85,77 @@ func (t *testProcessor) ProcessBid( type testCommitmentDA struct{} -func (t *testCommitmentDA) StoreCommitment( +func (t *testCommitmentDA) StoreEncryptedCommitment( _ context.Context, - _ *big.Int, - _ uint64, + _ []byte, + _ []byte, +) (common.Hash, error) { + return common.Hash{}, nil +} + +func (t *testCommitmentDA) OpenCommitment( + _ context.Context, + _ []byte, + _ string, + _ int64, _ string, - _ uint64, - _ uint64, + _ int64, + _ int64, _ []byte, _ []byte, -) error { - return nil + _ []byte, +) (common.Hash, error) { + return common.Hash{}, nil } func (t *testCommitmentDA) Close() error { return nil } +type testBlockTrackerContract struct { + blockNumberToWinner map[uint64]common.Address + lastBlockNumber uint64 + blocksPerWindow uint64 +} + +// GetCurrentWindow returns the current window number. +func (btc *testBlockTrackerContract) GetCurrentWindow(ctx context.Context) (uint64, error) { + return btc.lastBlockNumber / btc.blocksPerWindow, nil +} + +// GetBlocksPerWindow returns the number of blocks per window. +func (btc *testBlockTrackerContract) GetBlocksPerWindow(ctx context.Context) (uint64, error) { + return btc.blocksPerWindow, nil +} + +type testEventManager struct { + btABI *abi.ABI + handler events.EventHandler + handlerSub chan struct{} + sub *testSub +} + +type testSub struct { + errC chan error +} + +func (t *testSub) Unsubscribe() {} + +func (t *testSub) Err() <-chan error { + return t.errC +} + +func (t *testEventManager) Subscribe(evt events.EventHandler) (events.Subscription, error) { + if evt.EventName() != "NewL1Block" { + return nil, errors.New("invalid event") + } + evt.SetTopicAndContract(t.btABI.Events["NewL1Block"].ID, t.btABI) + t.handler = evt + close(t.handlerSub) + + return t.sub, nil +} + func newTestLogger(t *testing.T, w io.Writer) *slog.Logger { t.Helper() @@ -96,6 +165,20 @@ func newTestLogger(t *testing.T, w io.Writer) *slog.Logger { return slog.New(testLogger) } +type testDepositManager struct{} + +func (t *testDepositManager) Start(ctx context.Context) <-chan struct{} { + return nil +} + +func (t *testDepositManager) CheckAndDeductDeposit(ctx context.Context, address common.Address, bidAmountStr string, blockNumber int64) (*big.Int, error) { + return big.NewInt(0), nil +} + +func (t *testDepositManager) RefundDeposit(address common.Address, deductedAmount *big.Int, blockNumber int64) error { + return nil +} + func TestPreconfBidSubmission(t *testing.T) { t.Parallel() @@ -104,9 +187,24 @@ func TestPreconfBidSubmission(t *testing.T) { EthAddress: common.HexToAddress("0x1"), Type: p2p.PeerTypeBidder, } + + encryptionPrivateKey, err := ecies.GenerateKey(rand.Reader, elliptic.P256(), nil) + if err != nil { + t.Fatal(err) + } + + nikePrivateKey, err := ecdh.P256().GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + server := p2p.Peer{ EthAddress: common.HexToAddress("0x2"), Type: p2p.PeerTypeProvider, + Keys: &p2p.Keys{ + PKEPublicKey: &encryptionPrivateKey.PublicKey, + NIKEPublicKey: nikePrivateKey.PublicKey(), + }, } bid := &preconfpb.Bid{ @@ -119,35 +217,63 @@ func TestPreconfBidSubmission(t *testing.T) { Signature: []byte("test"), } + encryptedBid := &preconfpb.EncryptedBid{ + Ciphertext: []byte("test"), + } + preConfirmation := &preconfpb.PreConfirmation{ Bid: bid, Digest: []byte("test"), Signature: []byte("test"), } + encryptedPreConfirmation := &preconfpb.EncryptedPreConfirmation{ + Commitment: []byte("test"), + Signature: []byte("test"), + } svc := p2ptest.New( &client, ) topo := &testTopo{server} - us := &testBidderStore{} proc := &testProcessor{ status: providerapiv1.BidResponse_STATUS_ACCEPTED, } - signer := &testSigner{ - bid: bid, - preConfirmation: preConfirmation, - bidSigner: common.HexToAddress("0x1"), - preConfirmationSigner: common.HexToAddress("0x2"), + signer := &testEncryptor{ + bidHash: bid.Digest, + encryptedBid: encryptedBid, + bid: bid, + preConfirmation: preConfirmation, + encryptedPreConfirmation: encryptedPreConfirmation, + bidSigner: common.HexToAddress("0x1"), + preConfirmationSigner: common.HexToAddress("0x2"), } + btABI, err := abi.JSON(strings.NewReader(blocktracker.BlocktrackerABI)) + if err != nil { + t.Fatal(err) + } + eventManager := &testEventManager{ + btABI: &btABI, + sub: &testSub{errC: make(chan error)}, + handlerSub: make(chan struct{}), + } + store, err := store.NewStore() + if err != nil { + t.Fatal(err) + } + depositMgr := &testDepositManager{} p := preconfirmation.New( + client.EthAddress, topo, svc, signer, - us, + depositMgr, proc, &testCommitmentDA{}, + &testBlockTrackerContract{blockNumberToWinner: make(map[uint64]common.Address), blocksPerWindow: 64}, + eventManager, + store, newTestLogger(t, os.Stdout), ) diff --git a/pkg/rpc/bidder/service.go b/pkg/rpc/bidder/service.go index 9642d88d..a8f0d9eb 100644 --- a/pkg/rpc/bidder/service.go +++ b/pkg/rpc/bidder/service.go @@ -3,6 +3,7 @@ package bidderapi import ( "context" "encoding/hex" + "fmt" "log/slog" "math/big" "strings" @@ -13,34 +14,41 @@ import ( bidderapiv1 "github.com/primevprotocol/mev-commit/gen/go/bidderapi/v1" preconfirmationv1 "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" registrycontract "github.com/primevprotocol/mev-commit/pkg/contracts/bidder_registry" + blocktrackercontract "github.com/primevprotocol/mev-commit/pkg/contracts/block_tracker" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/wrapperspb" ) type Service struct { bidderapiv1.UnimplementedBidderServer - sender PreconfSender - owner common.Address - registryContract registrycontract.Interface - logger *slog.Logger - metrics *metrics - validator *protovalidate.Validator + sender PreconfSender + owner common.Address + registryContract registrycontract.Interface + blockTrackerContract blocktrackercontract.Interface + logger *slog.Logger + metrics *metrics + validator *protovalidate.Validator + depositedWindows map[*big.Int]struct{} } func NewService( sender PreconfSender, owner common.Address, registryContract registrycontract.Interface, + blockTrackerContract blocktrackercontract.Interface, validator *protovalidate.Validator, logger *slog.Logger, ) *Service { return &Service{ - sender: sender, - owner: owner, - registryContract: registryContract, - logger: logger, - metrics: newMetrics(), - validator: validator, + sender: sender, + owner: owner, + registryContract: registryContract, + blockTrackerContract: blockTrackerContract, + logger: logger, + metrics: newMetrics(), + validator: validator, + depositedWindows: make(map[*big.Int]struct{}), } } @@ -103,53 +111,111 @@ func (s *Service) SendBid( return nil } -func (s *Service) PrepayAllowance( +func (s *Service) Deposit( ctx context.Context, - stake *bidderapiv1.PrepayRequest, -) (*bidderapiv1.PrepayResponse, error) { - err := s.validator.Validate(stake) + r *bidderapiv1.DepositRequest, +) (*bidderapiv1.DepositResponse, error) { + err := s.validator.Validate(r) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "validating prepay request: %v", err) + return nil, status.Errorf(codes.InvalidArgument, "validating deposit request: %v", err) } - amount, success := big.NewInt(0).SetString(stake.Amount, 10) + currentWindow, err := s.blockTrackerContract.GetCurrentWindow(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "getting current window: %v", err) + } + + windowToDeposit, err := s.calculateWindowToDeposit(ctx, r, currentWindow) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "calculating window to deposit: %v", err) + } + if _, ok := s.depositedWindows[windowToDeposit]; ok { + return nil, status.Errorf(codes.FailedPrecondition, "deposited already for window %d", windowToDeposit.Int64()) + } + + for window := range s.depositedWindows { + if window.Cmp(new(big.Int).SetUint64(currentWindow)) < 0 { + err := s.registryContract.WithdrawDeposit(ctx, window) + if err != nil { + return nil, status.Errorf(codes.Internal, "withdrawing deposit: %v", err) + } + s.logger.Info("withdrew deposit", "window", window) + delete(s.depositedWindows, window) + } + } + + amount, success := big.NewInt(0).SetString(r.Amount, 10) if !success { - return nil, status.Errorf(codes.InvalidArgument, "parsing amount: %v", stake.Amount) + return nil, status.Errorf(codes.InvalidArgument, "parsing amount: %v", r.Amount) } - err = s.registryContract.PrepayAllowance(ctx, amount) + err = s.registryContract.DepositForSpecificWindow(ctx, amount, windowToDeposit) if err != nil { - return nil, status.Errorf(codes.Internal, "prepaying allowance: %v", err) + return nil, status.Errorf(codes.Internal, "deposit: %v", err) } - stakeAmount, err := s.registryContract.GetAllowance(ctx, s.owner) + stakeAmount, err := s.registryContract.GetDeposit(ctx, s.owner, windowToDeposit) if err != nil { - return nil, status.Errorf(codes.Internal, "getting allowance: %v", err) + return nil, status.Errorf(codes.Internal, "getting deposit: %v", err) } - return &bidderapiv1.PrepayResponse{Amount: stakeAmount.String()}, nil + s.logger.Info("deposit successful", "amount", stakeAmount.String(), "window", windowToDeposit) + s.depositedWindows[windowToDeposit] = struct{}{} + + return &bidderapiv1.DepositResponse{Amount: stakeAmount.String(), WindowNumber: wrapperspb.UInt64(windowToDeposit.Uint64())}, nil } -func (s *Service) GetAllowance( +func (s *Service) calculateWindowToDeposit(ctx context.Context, r *bidderapiv1.DepositRequest, currentWindow uint64) (*big.Int, error) { + if r.WindowNumber != nil { + // Directly use the specified window number if available. + return new(big.Int).SetUint64(r.WindowNumber.Value), nil + } else if r.BlockNumber != nil { + // Calculate the window based on the block number. + blocksPerWindow, err := s.blockTrackerContract.GetBlocksPerWindow(ctx) + if err != nil { + return nil, fmt.Errorf("getting window for block: %w", err) + } + return new(big.Int).SetUint64((r.BlockNumber.Value-1)/blocksPerWindow + 1), nil + } + // Default to two windows ahead of the current window if no specific block or window is given. + // This is for the case where the oracle works 2 windows behind the current window. + return new(big.Int).SetUint64(currentWindow + 2), nil +} + +func (s *Service) GetDeposit( ctx context.Context, - _ *bidderapiv1.EmptyMessage, -) (*bidderapiv1.PrepayResponse, error) { - stakeAmount, err := s.registryContract.GetAllowance(ctx, s.owner) + r *bidderapiv1.GetDepositRequest, +) (*bidderapiv1.DepositResponse, error) { + var ( + window uint64 + err error + ) + if r.WindowNumber == nil { + window, err = s.blockTrackerContract.GetCurrentWindow(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "getting current window: %v", err) + } + // as oracle working 2 windows behind the current window, we add + 2 here + window += 2 + } else { + window = r.WindowNumber.Value + } + stakeAmount, err := s.registryContract.GetDeposit(ctx, s.owner, new(big.Int).SetUint64(window)) if err != nil { - return nil, status.Errorf(codes.Internal, "getting allowance: %v", err) + return nil, status.Errorf(codes.Internal, "getting deposit: %v", err) } - return &bidderapiv1.PrepayResponse{Amount: stakeAmount.String()}, nil + return &bidderapiv1.DepositResponse{Amount: stakeAmount.String()}, nil } -func (s *Service) GetMinAllowance( +func (s *Service) GetMinDeposit( ctx context.Context, _ *bidderapiv1.EmptyMessage, -) (*bidderapiv1.PrepayResponse, error) { - stakeAmount, err := s.registryContract.GetMinAllowance(ctx) +) (*bidderapiv1.DepositResponse, error) { + stakeAmount, err := s.registryContract.GetMinDeposit(ctx) if err != nil { - return nil, status.Errorf(codes.Internal, "getting min allowance: %v", err) + return nil, status.Errorf(codes.Internal, "getting min deposit: %v", err) } - return &bidderapiv1.PrepayResponse{Amount: stakeAmount.String()}, nil + return &bidderapiv1.DepositResponse{Amount: stakeAmount.String()}, nil } diff --git a/pkg/rpc/bidder/service_test.go b/pkg/rpc/bidder/service_test.go index 9fed9da1..a2975f72 100644 --- a/pkg/rpc/bidder/service_test.go +++ b/pkg/rpc/bidder/service_test.go @@ -20,6 +20,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" + "google.golang.org/protobuf/types/known/wrapperspb" ) const ( @@ -75,25 +76,45 @@ func (s *testSender) SendBid( } type testRegistryContract struct { - allowance *big.Int - minAllowance *big.Int + deposit *big.Int + minDeposit *big.Int } -func (t *testRegistryContract) PrepayAllowance(ctx context.Context, amount *big.Int) error { - t.allowance = amount +func (t *testRegistryContract) DepositForSpecificWindow(ctx context.Context, amount, window *big.Int) error { + t.deposit = amount return nil } -func (t *testRegistryContract) GetAllowance(ctx context.Context, address common.Address) (*big.Int, error) { - return t.allowance, nil +func (t *testRegistryContract) GetDeposit(ctx context.Context, address common.Address, window *big.Int) (*big.Int, error) { + return t.deposit, nil } -func (t *testRegistryContract) GetMinAllowance(ctx context.Context) (*big.Int, error) { - return t.minAllowance, nil +func (t *testRegistryContract) GetMinDeposit(ctx context.Context) (*big.Int, error) { + return t.minDeposit, nil } -func (t *testRegistryContract) CheckBidderAllowance(ctx context.Context, address common.Address) bool { - return t.allowance.Cmp(t.minAllowance) > 0 +func (t *testRegistryContract) CheckBidderDeposit(ctx context.Context, address common.Address, window, numberOfRounds *big.Int) bool { + return t.deposit.Cmp(t.minDeposit) > 0 +} + +func (t *testRegistryContract) WithdrawDeposit(ctx context.Context, window *big.Int) error { + return nil +} + +type testBlockTrackerContract struct { + blockNumberToWinner map[uint64]common.Address + lastBlockNumber uint64 + blocksPerWindow uint64 +} + +// GetCurrentWindow returns the current window number. +func (btc *testBlockTrackerContract) GetCurrentWindow(ctx context.Context) (uint64, error) { + return btc.lastBlockNumber / btc.blocksPerWindow, nil +} + +// GetBlocksPerWindow returns the number of blocks per window. +func (btc *testBlockTrackerContract) GetBlocksPerWindow(ctx context.Context) (uint64, error) { + return btc.blocksPerWindow, nil } func startServer(t *testing.T) bidderapiv1.BidderClient { @@ -106,13 +127,14 @@ func startServer(t *testing.T) bidderapiv1.BidderClient { } owner := common.HexToAddress("0x00001") - registryContract := &testRegistryContract{minAllowance: big.NewInt(100000000000000000)} + registryContract := &testRegistryContract{minDeposit: big.NewInt(100000000000000000)} sender := &testSender{noOfPreconfs: 2} - + blockTrackerContract := &testBlockTrackerContract{blocksPerWindow: 64, blockNumberToWinner: make(map[uint64]common.Address)} srvImpl := bidderapi.NewService( sender, owner, registryContract, + blockTrackerContract, validator, logger, ) @@ -146,68 +168,68 @@ func startServer(t *testing.T) bidderapiv1.BidderClient { return client } -func TestAllowanceHandling(t *testing.T) { +func TestDepositHandling(t *testing.T) { t.Parallel() client := startServer(t) - t.Run("prepay", func(t *testing.T) { + t.Run("deposit", func(t *testing.T) { type testCase struct { amount string err string } for _, tc := range []testCase{ - { - amount: "", - err: "amount must be a valid integer", - }, - { - amount: "0000000000000000000", - err: "amount must be a valid integer", - }, - { - amount: "asdf", - err: "amount must be a valid integer", - }, + // { + // amount: "", + // err: "amount must be a valid integer", + // }, + // { + // amount: "0000000000000000000", + // err: "amount must be a valid integer", + // }, + // { + // amount: "asdf", + // err: "amount must be a valid integer", + // }, { amount: "1000000000000000000", err: "", }, } { - allowance, err := client.PrepayAllowance(context.Background(), &bidderapiv1.PrepayRequest{Amount: tc.amount}) + deposit, err := client.Deposit(context.Background(), &bidderapiv1.DepositRequest{Amount: tc.amount}) if tc.err != "" { if err == nil || !strings.Contains(err.Error(), tc.err) { - t.Fatalf("expected error prepaying allowance") + t.Fatalf("expected error depositing") } } else { if err != nil { - t.Fatalf("error prepaying allowance: %v", err) + t.Fatalf("error depositing: %v", err) } - if allowance.Amount != tc.amount { - t.Fatalf("expected amount to be %v, got %v", tc.amount, allowance.Amount) + if deposit.Amount != tc.amount { + t.Fatalf("expected amount to be %v, got %v", tc.amount, deposit.Amount) } } } }) - t.Run("get allowance", func(t *testing.T) { - allowance, err := client.GetAllowance(context.Background(), &bidderapiv1.EmptyMessage{}) + t.Run("get deposit", func(t *testing.T) { + deposit, err := client.GetDeposit(context.Background(), &bidderapiv1.GetDepositRequest{WindowNumber: wrapperspb.UInt64(1)}) if err != nil { - t.Fatalf("error getting allowance: %v", err) + t.Fatalf("error getting deposit: %v", err) } - if allowance.Amount != "1000000000000000000" { - t.Fatalf("expected amount to be 1000000000000000000, got %v", allowance.Amount) + if deposit.Amount != "1000000000000000000" { + t.Fatalf("expected amount to be 1000000000000000000, got %v", deposit.Amount) } }) - t.Run("get min allowance", func(t *testing.T) { - allowance, err := client.GetMinAllowance(context.Background(), &bidderapiv1.EmptyMessage{}) + t.Run("get min deposit", func(t *testing.T) { + deposit, err := client.GetMinDeposit(context.Background(), &bidderapiv1.EmptyMessage{}) if err != nil { - t.Fatalf("error getting min allowance: %v", err) + t.Fatalf("error getting min deposit: %v", err) } - if allowance.Amount != "100000000000000000" { - t.Fatalf("expected amount to be 100000000000000000, got %v", allowance.Amount) + if deposit.Amount != "100000000000000000" { + t.Fatalf("expected amount to be 100000000000000000, got %v", deposit.Amount) } }) } diff --git a/pkg/signer/preconfsigner/signer.go b/pkg/signer/preconfencryptor/encryptor.go similarity index 59% rename from pkg/signer/preconfsigner/signer.go rename to pkg/signer/preconfencryptor/encryptor.go index a5e0b2f2..e88a27a9 100644 --- a/pkg/signer/preconfsigner/signer.go +++ b/pkg/signer/preconfencryptor/encryptor.go @@ -1,8 +1,10 @@ -package preconfsigner +package preconfencryptor import ( "bytes" + "crypto/ecdh" "encoding/hex" + "encoding/json" "errors" "math/big" @@ -10,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" - "github.com/primevprotocol/mev-commit/pkg/keysigner" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" ) var ( @@ -19,34 +21,39 @@ var ( ErrInvalidSignature = errors.New("signature is not valid") ErrInvalidHash = errors.New("bidhash doesn't match bid payload") ErrAlreadySignedPreConfirmation = errors.New("preConfirmation is already hashed or signed") + ErrInvalidCommitment = errors.New("commitment is incorrect") ) -type Signer interface { - ConstructSignedBid(string, string, int64, int64, int64) (*preconfpb.Bid, error) - ConstructPreConfirmation(*preconfpb.Bid) (*preconfpb.PreConfirmation, error) +type Encryptor interface { + ConstructEncryptedBid(string, string, int64, int64, int64) (*preconfpb.Bid, *preconfpb.EncryptedBid, error) + ConstructEncryptedPreConfirmation(*preconfpb.Bid) (*preconfpb.PreConfirmation, *preconfpb.EncryptedPreConfirmation, error) VerifyBid(*preconfpb.Bid) (*common.Address, error) - VerifyPreConfirmation(*preconfpb.PreConfirmation) (*common.Address, error) + VerifyEncryptedPreConfirmation(providerNikePK *ecdh.PublicKey, bidHash []byte, c *preconfpb.EncryptedPreConfirmation) ([]byte, *common.Address, error) + DecryptBidData(common.Address, *preconfpb.EncryptedBid) (*preconfpb.Bid, error) } -type privateKeySigner struct { - keySigner keysigner.KeySigner +type encryptor struct { + keyKeeper keykeeper.KeyKeeper + bidHashesToBid map[string]*preconfpb.Bid } -func NewSigner(keySigner keysigner.KeySigner) *privateKeySigner { - return &privateKeySigner{ - keySigner: keySigner, +func NewEncryptor(keyKeeper keykeeper.KeyKeeper) *encryptor { + bidHashesToBid := make(map[string]*preconfpb.Bid) + return &encryptor{ + keyKeeper: keyKeeper, + bidHashesToBid: bidHashesToBid, } } -func (p *privateKeySigner) ConstructSignedBid( +func (e *encryptor) ConstructEncryptedBid( txHash string, bidAmt string, blockNumber int64, decayStartTimeStamp int64, decayEndTimeStamp int64, -) (*preconfpb.Bid, error) { +) (*preconfpb.Bid, *preconfpb.EncryptedBid, error) { if txHash == "" || bidAmt == "" || blockNumber == 0 { - return nil, errors.New("missing required fields") + return nil, nil, errors.New("missing required fields") } bid := &preconfpb.Bid{ @@ -59,42 +66,75 @@ func (p *privateKeySigner) ConstructSignedBid( bidHash, err := GetBidHash(bid) if err != nil { - return nil, err + return nil, nil, err } - sig, err := p.keySigner.SignHash(bidHash) + // todo: probably sign all data including nike public key + sig, err := e.keyKeeper.SignHash(bidHash) if err != nil { - return nil, err + return nil, nil, err } if sig[64] == 0 || sig[64] == 1 { sig[64] += 27 // Transform V from 0/1 to 27/28 } + bidderKK := e.keyKeeper.(*keykeeper.BidderKeyKeeper) + nikePublicKey, err := bidderKK.GenerateNIKEKeys(bidHash) + if err != nil { + return nil, nil, err + } + + bid.NikePublicKey = nikePublicKey.Bytes() bid.Digest = bidHash bid.Signature = sig - return bid, nil + bidDataBytes, err := json.Marshal(bid) + if err != nil { + return nil, nil, err + } + + e.bidHashesToBid[hex.EncodeToString(bidHash)] = bid + + encryptedBidData, err := keykeeper.EncryptWithAESGCM(bidderKK.AESKey, bidDataBytes) + if err != nil { + return nil, nil, err + } + + return bid, &preconfpb.EncryptedBid{Ciphertext: encryptedBidData}, nil } -func (p *privateKeySigner) ConstructPreConfirmation(bid *preconfpb.Bid) (*preconfpb.PreConfirmation, error) { - _, err := p.VerifyBid(bid) +func (e *encryptor) ConstructEncryptedPreConfirmation(bid *preconfpb.Bid) (*preconfpb.PreConfirmation, *preconfpb.EncryptedPreConfirmation, error) { + _, err := e.VerifyBid(bid) if err != nil { - return nil, err + return nil, nil, err + } + + bidDataPublicKey, err := ecdh.Curve.NewPublicKey(ecdh.P256(), bid.NikePublicKey) + if err != nil { + return nil, nil, err + } + + providerKK := e.keyKeeper.(*keykeeper.ProviderKeyKeeper) + sharedSecredProviderSk, err := providerKK.GetNIKEPrivateKey().ECDH(bidDataPublicKey) + if err != nil { + return nil, nil, err } preConfirmation := &preconfpb.PreConfirmation{ - Bid: bid, + Bid: bid, + SharedSecret: sharedSecredProviderSk, + ProviderAddress: providerKK.GetAddress().Bytes(), } preConfirmationHash, err := GetPreConfirmationHash(preConfirmation) if err != nil { - return nil, err + return nil, nil, err } - sig, err := p.keySigner.SignHash(preConfirmationHash) + sig, err := e.keyKeeper.SignHash(preConfirmationHash) if err != nil { - return nil, err + return nil, nil, err } if sig[64] == 0 || sig[64] == 1 { @@ -104,10 +144,13 @@ func (p *privateKeySigner) ConstructPreConfirmation(bid *preconfpb.Bid) (*precon preConfirmation.Digest = preConfirmationHash preConfirmation.Signature = sig - return preConfirmation, nil + return preConfirmation, &preconfpb.EncryptedPreConfirmation{ + Commitment: preConfirmationHash, + Signature: sig, + }, nil } -func (p *privateKeySigner) VerifyBid(bid *preconfpb.Bid) (*common.Address, error) { +func (e *encryptor) VerifyBid(bid *preconfpb.Bid) (*common.Address, error) { if bid.Digest == nil || bid.Signature == nil { return nil, ErrMissingHashSignature } @@ -124,24 +167,59 @@ func (p *privateKeySigner) VerifyBid(bid *preconfpb.Bid) (*common.Address, error ) } +func (e *encryptor) DecryptBidData(bidderAddress common.Address, bid *preconfpb.EncryptedBid) (*preconfpb.Bid, error) { + pkk := e.keyKeeper.(*keykeeper.ProviderKeyKeeper) + aesKey, exists := pkk.GetAESKey(bidderAddress) + if !exists { + return nil, errors.New("no AES key found for bidder") + } + decryptedBytes, err := keykeeper.DecryptWithAESGCM(aesKey, bid.Ciphertext) + if err != nil { + return nil, err + } + + var bidData preconfpb.Bid + if err := json.Unmarshal(decryptedBytes, &bidData); err != nil { + return nil, err + } + + return &bidData, nil +} + // VerifyPreConfirmation verifies the preconfirmation message, and returns the address of the provider // that signed the preconfirmation. -func (p *privateKeySigner) VerifyPreConfirmation(c *preconfpb.PreConfirmation) (*common.Address, error) { - if c.Digest == nil || c.Signature == nil { - return nil, ErrMissingHashSignature +func (e *encryptor) VerifyEncryptedPreConfirmation(providerNikePK *ecdh.PublicKey, bidHash []byte, c *preconfpb.EncryptedPreConfirmation) ([]byte, *common.Address, error) { + if c.Signature == nil { + return nil, nil, ErrMissingHashSignature } - _, err := p.VerifyBid(c.Bid) + bidHashStr := hex.EncodeToString(bidHash) + bid := e.bidHashesToBid[bidHashStr] + + bidderKK := e.keyKeeper.(*keykeeper.BidderKeyKeeper) + sharedSecredBidderSk, err := bidderKK.BidHashesToNIKE[bidHashStr].ECDH(providerNikePK) if err != nil { - return nil, err + return nil, nil, err + } + + preConfirmation := &preconfpb.PreConfirmation{ + Bid: bid, + Digest: bidHash, + Signature: c.Signature, + SharedSecret: sharedSecredBidderSk, } - preConfirmationHash, err := GetPreConfirmationHash(c) + preConfirmationHash, err := GetPreConfirmationHash(preConfirmation) if err != nil { - return nil, err + return nil, nil, err + } + + address, err := eipVerify(preConfirmationHash, c.Commitment, c.Signature) + if err != nil { + return nil, nil, err } - return eipVerify(preConfirmationHash, c.Digest, c.Signature) + return sharedSecredBidderSk, address, nil } func eipVerify( @@ -242,13 +320,14 @@ func GetPreConfirmationHash(c *preconfpb.PreConfirmation) ([]byte, error) { // EIP712_MESSAGE_TYPEHASH eip712MessageTypeHash := crypto.Keccak256Hash( - []byte("PreConfCommitment(string txnHash,uint64 bid,uint64 blockNumber,uint64 decayStartTimeStamp,uint64 decayEndTimeStamp,string bidHash,string signature)"), + []byte("PreConfCommitment(string txnHash,uint64 bid,uint64 blockNumber,uint64 decayStartTimeStamp,uint64 decayEndTimeStamp,bytes32 bidHash,string signature,string sharedSecretKey)"), ) // Convert the txnHash to a byte array and hash it txnHashHash := crypto.Keccak256Hash([]byte(c.Bid.TxHash)) bidDigestHash := crypto.Keccak256Hash([]byte(hex.EncodeToString(c.Bid.Digest))) bidSigHash := crypto.Keccak256Hash([]byte(hex.EncodeToString(c.Bid.Signature))) + sharedSecretHash := crypto.Keccak256Hash([]byte(hex.EncodeToString(c.SharedSecret))) // Encode values similar to Solidity's abi.encode data := append(eip712MessageTypeHash.Bytes(), txnHashHash.Bytes()...) @@ -258,6 +337,7 @@ func GetPreConfirmationHash(c *preconfpb.PreConfirmation) ([]byte, error) { data = append(data, math.U256Bytes(big.NewInt(c.Bid.DecayEndTimestamp))...) data = append(data, bidDigestHash.Bytes()...) data = append(data, bidSigHash.Bytes()...) + data = append(data, sharedSecretHash.Bytes()...) dataHash := crypto.Keccak256Hash(data) rawData := append([]byte("\x19\x01"), append(domainSeparatorBid.Bytes(), dataHash.Bytes()...)...) diff --git a/pkg/signer/preconfencryptor/encryptor_test.go b/pkg/signer/preconfencryptor/encryptor_test.go new file mode 100644 index 00000000..5a33cad1 --- /dev/null +++ b/pkg/signer/preconfencryptor/encryptor_test.go @@ -0,0 +1,260 @@ +package preconfencryptor_test + +import ( + "encoding/hex" + "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" + preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" + "github.com/primevprotocol/mev-commit/pkg/keykeeper" + mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keykeeper/keysigner/mock" + "github.com/primevprotocol/mev-commit/pkg/signer/preconfencryptor" + "github.com/stretchr/testify/assert" +) + +func TestBids(t *testing.T) { + t.Parallel() + + t.Run("bid", func(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + + address := crypto.PubkeyToAddress(key.PublicKey) + keySigner := mockkeysigner.NewMockKeySigner(key, address) + keyKeeper, err := keykeeper.NewBidderKeyKeeper(keySigner) + if err != nil { + t.Fatal(err) + } + encryptor := preconfencryptor.NewEncryptor(keyKeeper) + + start := time.Now().UnixMilli() + end := start + 100000 + _, encryptedBid, err := encryptor.ConstructEncryptedBid("0xkartik", "10", 2, start, end) + if err != nil { + t.Fatal(err) + } + + providerKeyKeeper, err := keykeeper.NewProviderKeyKeeper(keySigner) + if err != nil { + t.Fatal(err) + } + providerKeyKeeper.SetAESKey(address, keyKeeper.AESKey) + encryptorProvider := preconfencryptor.NewEncryptor(providerKeyKeeper) + bid, err := encryptorProvider.DecryptBidData(address, encryptedBid) + if err != nil { + t.Fatal(err) + } + + bidAddress, err := encryptor.VerifyBid(bid) + if err != nil { + t.Fatal(err) + } + + originatorAddress, pubkey, err := encryptor.BidOriginator(bid) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, address, *originatorAddress) + assert.Equal(t, address, *bidAddress) + assert.Equal(t, key.PublicKey, *pubkey) + }) + t.Run("preConfirmation", func(t *testing.T) { + bidderKey, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + + keySigner := mockkeysigner.NewMockKeySigner(bidderKey, crypto.PubkeyToAddress(bidderKey.PublicKey)) + bidderKeyKeeper, err := keykeeper.NewBidderKeyKeeper(keySigner) + if err != nil { + t.Fatal(err) + } + bidderEncryptor := preconfencryptor.NewEncryptor(bidderKeyKeeper) + providerKey, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + + bidderAddress := crypto.PubkeyToAddress(bidderKey.PublicKey) + keySigner = mockkeysigner.NewMockKeySigner(providerKey, crypto.PubkeyToAddress(providerKey.PublicKey)) + providerKeyKeeper, err := keykeeper.NewProviderKeyKeeper(keySigner) + if err != nil { + t.Fatal(err) + } + + providerKeyKeeper.SetAESKey(bidderAddress, bidderKeyKeeper.AESKey) + providerEncryptor := preconfencryptor.NewEncryptor(providerKeyKeeper) + start := time.Now().UnixMilli() + end := start + 100000 + + bid, encryptedBid, err := bidderEncryptor.ConstructEncryptedBid("0xkartik", "10", 2, start, end) + if err != nil { + t.Fatal(err) + } + + decryptedBid, err := providerEncryptor.DecryptBidData(bidderAddress, encryptedBid) + if err != nil { + t.Fatal(err) + } + _, encryptedPreConfirmation, err := providerEncryptor.ConstructEncryptedPreConfirmation(decryptedBid) + if err != nil { + t.Fail() + } + + _, address, err := bidderEncryptor.VerifyEncryptedPreConfirmation(providerKeyKeeper.GetNIKEPublicKey(), bid.Digest, encryptedPreConfirmation) + if err != nil { + t.Fail() + } + + assert.Equal(t, crypto.PubkeyToAddress(providerKey.PublicKey), *address) + }) +} + +func TestHashing(t *testing.T) { + t.Parallel() + + t.Run("bid", func(t *testing.T) { + bid := &preconfpb.Bid{ + TxHash: "0xkartik", + BidAmount: "2", + BlockNumber: 2, + DecayStartTimestamp: 10, + DecayEndTimestamp: 20, + } + + hash, err := preconfencryptor.GetBidHash(bid) + if err != nil { + t.Fatal(err) + } + + hashStr := hex.EncodeToString(hash) + // This hash is sourced from the solidity contract to ensure interoperability + expHash := "a0327970258c49b922969af74d60299a648c50f69a2d98d6ab43f32f64ac2100" + if hashStr != expHash { + t.Fatalf("hash mismatch: %s != %s", hashStr, expHash) + } + }) + + t.Run("preConfirmation", func(t *testing.T) { + bidHash := "a0327970258c49b922969af74d60299a648c50f69a2d98d6ab43f32f64ac2100" + bidSignature := "876c1216c232828be9fabb14981c8788cebdf6ed66e563c4a2ccc82a577d052543207aeeb158a32d8977736797ae250c63ef69a82cd85b727da21e20d030fb311b" + + bidHashBytes, err := hex.DecodeString(bidHash) + if err != nil { + t.Fatal(err) + } + bidSigBytes, err := hex.DecodeString(bidSignature) + if err != nil { + t.Fatal(err) + } + + bid := &preconfpb.Bid{ + TxHash: "0xkartik", + BidAmount: "2", + BlockNumber: 2, + DecayStartTimestamp: 10, + DecayEndTimestamp: 20, + Digest: bidHashBytes, + Signature: bidSigBytes, + } + + sharedSecretBytes := []byte("0xsecret") + + preConfirmation := &preconfpb.PreConfirmation{ + Bid: bid, + SharedSecret: sharedSecretBytes, + } + + hash, err := preconfencryptor.GetPreConfirmationHash(preConfirmation) + if err != nil { + t.Fatal(err) + } + + hashStr := hex.EncodeToString(hash) + expHash := "65618f8f9e46b8f0790c621ca2989cfe4c949594a4a3a81261baa682e8883840" + if hashStr != expHash { + t.Fatalf("hash mismatch: %s != %s", hashStr, expHash) + } + }) +} + +// todo: mock key encryptor +// func TestSignature(t *testing.T) { +// t.Parallel() +// // alice keys 0x328809Bc894f92807417D2dAD6b7C998c1aFdac6 +// pkey, err := crypto.HexToECDSA("9C0257114EB9399A2985F8E75DAD7600C5D89FE3824FFA99EC1C3EB8BF3B0501") +// if err != nil { +// t.Fatal(err) +// } +// keySigner := mockkeysigner.NewMockKeySigner(pkey, crypto.PubkeyToAddress(pkey.PublicKey)) +// bidderKK, err := keykeeper.NewBidderKeyKeeper(keySigner) +// if err != nil { +// t.Fatal(err) +// } +// bidder := preconfencryptor.NewEncryptor(bidderKK) + +// bid, _, err := bidder.ConstructEncryptedBid("0xkartik", "2", 2, 10, 20) +// if err != nil { +// t.Fatal(err) +// } + +// // bob keys 0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e +// providerKey, err := crypto.HexToECDSA("38E47A7B719DCE63662AEAF43440326F551B8A7EE198CEE35CB5D517F2D296A2") +// if err != nil { +// t.Fatal(err) +// } +// keySigner = mockkeysigner.NewMockKeySigner(providerKey, crypto.PubkeyToAddress(providerKey.PublicKey)) +// providerKK, err := keykeeper.NewProviderKeyKeeper(keySigner) +// if err != nil { +// t.Fatal(err) +// } +// provider := preconfencryptor.NewEncryptor(providerKK) +// preconf, err := provider.ConstructEncryptedPreConfirmation(bid) +// if err != nil { +// t.Fatal(err) +// } + +// // expCommitmentDigest := "1a85d596184bc14bcc974fcc276430f5295e24d8a6e09cda91a2fd2f72257d29" +// // expCommitmentSig := "9a7a3b1a0cbbc50e4bea5991f0005db26cd5d20f80595df433b837039b20a4e62078edb2390085d1a01aad16fddb9eea0b2f641433c946c9ab1708db7d5065c91b" +// // if hex.EncodeToString(preconf.Commitment) != expCommitmentDigest { +// // t.Fatalf("digest mismatch: %s != %s", hex.EncodeToString(preconf.Commitment), expCommitmentDigest) +// // } +// // if hex.EncodeToString(preconf.Signature) != expCommitmentSig { +// // t.Fatalf("signature mismatch: %s != %s", hex.EncodeToString(preconf.Signature), expCommitmentSig) +// } +// } + +func TestVerify(t *testing.T) { + t.Parallel() + + bidSig := "8af22e36247e14ba05d3a5a3cc62eee708cfd9ce293c0aebcbe7f89229f6db56638af8427806247d9abb295f681c1a2f2bb127f3bf80799f80d62b252cce04d91c" + bidHash := "2574b1ab8a90e173528ddee748be8e8e696b1f0cf687f75966550f5e9ef408b0" + + bidHashBytes, err := hex.DecodeString(bidHash) + if err != nil { + t.Fatal(err) + } + + bidSigBytes, err := hex.DecodeString(bidSig) + if err != nil { + t.Fatal(err) + } + + // Adjust the last byte if it's 27 or 28 + if bidSigBytes[64] >= 27 && bidSigBytes[64] <= 28 { + bidSigBytes[64] -= 27 + } + + owner, err := preconfencryptor.EIPVerify(bidHashBytes, bidHashBytes, bidSigBytes) + if err != nil { + t.Fatal(err) + } + + expOwner := "0x8339F9E3d7B2693aD8955Aa5EC59D56669A84d60" + if owner.Hex() != expOwner { + t.Fatalf("owner mismatch: %s != %s", owner.Hex(), expOwner) + } +} diff --git a/pkg/signer/preconfencryptor/export_test.go b/pkg/signer/preconfencryptor/export_test.go new file mode 100644 index 00000000..44e999fd --- /dev/null +++ b/pkg/signer/preconfencryptor/export_test.go @@ -0,0 +1,58 @@ +package preconfencryptor + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" +) + +var EIPVerify = eipVerify + +func (e *encryptor) BidOriginator(bid *preconfpb.Bid) (*common.Address, *ecdsa.PublicKey, error) { + _, err := e.VerifyBid(bid) + if err != nil { + return nil, nil, err + } + + sig := make([]byte, len(bid.Signature)) + copy(sig, bid.Signature) + if sig[64] >= 27 && sig[64] <= 28 { + sig[64] -= 27 + } + + pubkey, err := crypto.SigToPub(bid.Digest, sig) + if err != nil { + return nil, nil, err + } + + address := crypto.PubkeyToAddress(*pubkey) + + return &address, pubkey, nil +} + +// todo: come up with better test with passing all the data +// currently this is not used +// func (p *encryptor) PreConfirmationOriginator( +// c *PreConfirmation, +// ) (*common.Address, *ecdsa.PublicKey, error) { +// _, err := p.VerifyEncryptedPreConfirmation(c) +// if err != nil { +// return nil, nil, err +// } + +// sig := make([]byte, len(c.Signature)) +// copy(sig, c.Signature) +// if sig[64] >= 27 && sig[64] <= 28 { +// sig[64] -= 27 +// } +// pubkey, err := crypto.SigToPub(c.Digest, sig) +// if err != nil { +// return nil, nil, err +// } + +// address := crypto.PubkeyToAddress(*pubkey) + +// return &address, pubkey, nil +// } diff --git a/pkg/signer/preconfsigner/export_test.go b/pkg/signer/preconfsigner/export_test.go deleted file mode 100644 index 5492a797..00000000 --- a/pkg/signer/preconfsigner/export_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package preconfsigner - -import ( - "crypto/ecdsa" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" -) - -var EIPVerify = eipVerify - -func (p *privateKeySigner) BidOriginator(bid *preconfpb.Bid) (*common.Address, *ecdsa.PublicKey, error) { - _, err := p.VerifyBid(bid) - if err != nil { - return nil, nil, err - } - - sig := make([]byte, len(bid.Signature)) - copy(sig, bid.Signature) - if sig[64] >= 27 && sig[64] <= 28 { - sig[64] -= 27 - } - - pubkey, err := crypto.SigToPub(bid.Digest, sig) - if err != nil { - return nil, nil, err - } - - address := crypto.PubkeyToAddress(*pubkey) - - return &address, pubkey, nil -} - -func (p *privateKeySigner) PreConfirmationOriginator( - c *preconfpb.PreConfirmation, -) (*common.Address, *ecdsa.PublicKey, error) { - _, err := p.VerifyPreConfirmation(c) - if err != nil { - return nil, nil, err - } - - sig := make([]byte, len(c.Signature)) - copy(sig, c.Signature) - if sig[64] >= 27 && sig[64] <= 28 { - sig[64] -= 27 - } - pubkey, err := crypto.SigToPub(c.Digest, sig) - if err != nil { - return nil, nil, err - } - - address := crypto.PubkeyToAddress(*pubkey) - - return &address, pubkey, nil -} diff --git a/pkg/signer/preconfsigner/signer_test.go b/pkg/signer/preconfsigner/signer_test.go deleted file mode 100644 index 79efb4d1..00000000 --- a/pkg/signer/preconfsigner/signer_test.go +++ /dev/null @@ -1,225 +0,0 @@ -package preconfsigner_test - -import ( - "encoding/hex" - "testing" - "time" - - "github.com/ethereum/go-ethereum/crypto" - preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" - mockkeysigner "github.com/primevprotocol/mev-commit/pkg/keysigner/mock" - "github.com/primevprotocol/mev-commit/pkg/signer/preconfsigner" - "github.com/stretchr/testify/assert" -) - -func TestBids(t *testing.T) { - t.Parallel() - - t.Run("bid", func(t *testing.T) { - key, err := crypto.GenerateKey() - if err != nil { - t.Fatal(err) - } - - keySigner := mockkeysigner.NewMockKeySigner(key, crypto.PubkeyToAddress(key.PublicKey)) - signer := preconfsigner.NewSigner(keySigner) - - start := time.Now().UnixMilli() - end := start + 100000 - bid, err := signer.ConstructSignedBid("0xkartik", "10", 2, start, end) - if err != nil { - t.Fatal(err) - } - - address, err := signer.VerifyBid(bid) - if err != nil { - t.Fatal(err) - } - - expectedAddress := crypto.PubkeyToAddress(key.PublicKey) - - originatorAddress, pubkey, err := signer.BidOriginator(bid) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, expectedAddress, *originatorAddress) - assert.Equal(t, expectedAddress, *address) - assert.Equal(t, key.PublicKey, *pubkey) - }) - t.Run("preConfirmation", func(t *testing.T) { - bidderKey, err := crypto.GenerateKey() - if err != nil { - t.Fatal(err) - } - - keySigner := mockkeysigner.NewMockKeySigner(bidderKey, crypto.PubkeyToAddress(bidderKey.PublicKey)) - - bidderSigner := preconfsigner.NewSigner(keySigner) - providerKey, err := crypto.GenerateKey() - if err != nil { - t.Fatal(err) - } - - keySigner = mockkeysigner.NewMockKeySigner(providerKey, crypto.PubkeyToAddress(providerKey.PublicKey)) - providerSigner := preconfsigner.NewSigner(keySigner) - - bid, err := bidderSigner.ConstructSignedBid("0xkartik", "10", 2, 1, 2) - if err != nil { - t.Fatal(err) - } - - preConfirmation, err := providerSigner.ConstructPreConfirmation(bid) - if err != nil { - t.Fail() - } - - address, err := bidderSigner.VerifyPreConfirmation(preConfirmation) - if err != nil { - t.Fail() - } - - assert.Equal(t, crypto.PubkeyToAddress(providerKey.PublicKey), *address) - }) -} - -func TestHashing(t *testing.T) { - t.Parallel() - - t.Run("bid", func(t *testing.T) { - bid := &preconfpb.Bid{ - TxHash: "0xkartik", - BidAmount: "200", - BlockNumber: 3000, - DecayStartTimestamp: 10, - DecayEndTimestamp: 30, - } - - hash, err := preconfsigner.GetBidHash(bid) - if err != nil { - t.Fatal(err) - } - - hashStr := hex.EncodeToString(hash) - // This hash is sourced from the solidity contract to ensure interoperability - expHash := "a837b0c680d4b9b11011ac6225670498d845e65f1dc340b00694d74a6ca0a049" - if hashStr != expHash { - t.Fatalf("hash mismatch: %s != %s", hashStr, expHash) - } - }) - - t.Run("preConfirmation", func(t *testing.T) { - bidHash := "a0327970258c49b922969af74d60299a648c50f69a2d98d6ab43f32f64ac2100" - bidSignature := "876c1216c232828be9fabb14981c8788cebdf6ed66e563c4a2ccc82a577d052543207aeeb158a32d8977736797ae250c63ef69a82cd85b727da21e20d030fb311b" - - bidHashBytes, err := hex.DecodeString(bidHash) - if err != nil { - t.Fatal(err) - } - bidSigBytes, err := hex.DecodeString(bidSignature) - if err != nil { - t.Fatal(err) - } - - bid := &preconfpb.Bid{ - TxHash: "0xkartik", - BidAmount: "2", - BlockNumber: 2, - DecayStartTimestamp: 10, - DecayEndTimestamp: 20, - Digest: bidHashBytes, - Signature: bidSigBytes, - } - - preConfirmation := &preconfpb.PreConfirmation{ - Bid: bid, - } - - hash, err := preconfsigner.GetPreConfirmationHash(preConfirmation) - if err != nil { - t.Fatal(err) - } - - hashStr := hex.EncodeToString(hash) - expHash := "54c118e537dd7cf63b5388a5fc8322f0286a978265d0338b108a8ca9d155dccc" - if hashStr != expHash { - t.Fatalf("hash mismatch: %s != %s", hashStr, expHash) - } - }) -} - -func TestSignature(t *testing.T) { - t.Parallel() - // alice keys 0x328809Bc894f92807417D2dAD6b7C998c1aFdac6 - pkey, err := crypto.HexToECDSA("9C0257114EB9399A2985F8E75DAD7600C5D89FE3824FFA99EC1C3EB8BF3B0501") - if err != nil { - t.Fatal(err) - } - keySigner := mockkeysigner.NewMockKeySigner(pkey, crypto.PubkeyToAddress(pkey.PublicKey)) - bidder := preconfsigner.NewSigner(keySigner) - - bid, err := bidder.ConstructSignedBid("0xkartik", "2", 2, 10, 20) - if err != nil { - t.Fatal(err) - } - - // bob keys 0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e - providerKey, err := crypto.HexToECDSA("38E47A7B719DCE63662AEAF43440326F551B8A7EE198CEE35CB5D517F2D296A2") - if err != nil { - t.Fatal(err) - } - keySigner = mockkeysigner.NewMockKeySigner(providerKey, crypto.PubkeyToAddress(providerKey.PublicKey)) - provider := preconfsigner.NewSigner(keySigner) - preconf, err := provider.ConstructPreConfirmation(bid) - if err != nil { - t.Fatal(err) - } - - expBidDigest := "a0327970258c49b922969af74d60299a648c50f69a2d98d6ab43f32f64ac2100" - expBidSig := "876c1216c232828be9fabb14981c8788cebdf6ed66e563c4a2ccc82a577d052543207aeeb158a32d8977736797ae250c63ef69a82cd85b727da21e20d030fb311b" - expCommitmentDigest := "54c118e537dd7cf63b5388a5fc8322f0286a978265d0338b108a8ca9d155dccc" - expCommitmentSig := "ec0f11f77a9e96bb9c2345f031a5d12dca8d01de8a2e957cf635be14802f9ad01c6183688f0c2672639e90cc2dce0662d9bea3337306ca7d4b56dd80326aaa231b" - if hex.EncodeToString(preconf.Bid.Digest) != expBidDigest { - t.Fatalf("digest mismatch: %s != %s", hex.EncodeToString(preconf.Bid.Digest), expBidDigest) - } - if hex.EncodeToString(preconf.Bid.Signature) != expBidSig { - t.Fatalf("signature mismatch: %s != %s", hex.EncodeToString(preconf.Bid.Signature), expBidSig) - } - if hex.EncodeToString(preconf.Digest) != expCommitmentDigest { - t.Fatalf("digest mismatch: %s != %s", hex.EncodeToString(preconf.Digest), expCommitmentDigest) - } - if hex.EncodeToString(preconf.Signature) != expCommitmentSig { - t.Fatalf("signature mismatch: %s != %s", hex.EncodeToString(preconf.Signature), expCommitmentSig) - } -} - -func TestVerify(t *testing.T) { - t.Parallel() - - bidSig := "8af22e36247e14ba05d3a5a3cc62eee708cfd9ce293c0aebcbe7f89229f6db56638af8427806247d9abb295f681c1a2f2bb127f3bf80799f80d62b252cce04d91c" - bidHash := "2574b1ab8a90e173528ddee748be8e8e696b1f0cf687f75966550f5e9ef408b0" - - bidHashBytes, err := hex.DecodeString(bidHash) - if err != nil { - t.Fatal(err) - } - - bidSigBytes, err := hex.DecodeString(bidSig) - if err != nil { - t.Fatal(err) - } - - // Adjust the last byte if it's 27 or 28 - if bidSigBytes[64] >= 27 && bidSigBytes[64] <= 28 { - bidSigBytes[64] -= 27 - } - - owner, err := preconfsigner.EIPVerify(bidHashBytes, bidHashBytes, bidSigBytes) - if err != nil { - t.Fatal(err) - } - - expOwner := "0x8339F9E3d7B2693aD8955Aa5EC59D56669A84d60" - if owner.Hex() != expOwner { - t.Fatalf("owner mismatch: %s != %s", owner.Hex(), expOwner) - } -} diff --git a/pkg/store/store.go b/pkg/store/store.go new file mode 100644 index 00000000..32fcc231 --- /dev/null +++ b/pkg/store/store.go @@ -0,0 +1,215 @@ +package store + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + lru "github.com/hashicorp/golang-lru/v2" + preconfpb "github.com/primevprotocol/mev-commit/gen/go/preconfirmation/v1" +) + +type Store struct { + *BlockStore + *CommitmentsStore + *BidderBalancesStore +} + +type BlockStore struct { + data map[string]uint64 + mu sync.RWMutex +} + +type CommitmentsStore struct { + commitmentsByBlockNumber map[int64][]*EncryptedPreConfirmationWithDecrypted + commitmentsByCommitmentHash map[string]*EncryptedPreConfirmationWithDecrypted + commitmentByBlockNumberMu sync.RWMutex + commitmentsByCommitmentHashMu sync.RWMutex +} + +type EncryptedPreConfirmationWithDecrypted struct { + *preconfpb.EncryptedPreConfirmation + *preconfpb.PreConfirmation +} + +func NewStore() (*Store, error) { + balancesByBlockCache, err := lru.New[string, *big.Int](1024) + if err != nil { + return nil, fmt.Errorf("failed to create balancesByBlockCache: %w", err) + } + return &Store{ + BlockStore: &BlockStore{ + data: make(map[string]uint64), + }, + CommitmentsStore: &CommitmentsStore{ + commitmentsByBlockNumber: make(map[int64][]*EncryptedPreConfirmationWithDecrypted), + commitmentsByCommitmentHash: make(map[string]*EncryptedPreConfirmationWithDecrypted), + }, + BidderBalancesStore: &BidderBalancesStore{ + balances: make(map[string]*big.Int), + balancesByBlock: balancesByBlockCache, + }, + }, nil +} + +func (bs *BlockStore) LastBlock() (uint64, error) { + bs.mu.RLock() + defer bs.mu.RUnlock() + + if value, exists := bs.data["last_block"]; exists { + return value, nil + } + return 0, nil +} + +func (bs *BlockStore) SetLastBlock(blockNum uint64) error { + bs.mu.Lock() + defer bs.mu.Unlock() + + bs.data["last_block"] = blockNum + return nil +} + +func (cs *CommitmentsStore) addCommitmentByBlockNumber(blockNum int64, commitment *EncryptedPreConfirmationWithDecrypted) { + cs.commitmentByBlockNumberMu.Lock() + defer cs.commitmentByBlockNumberMu.Unlock() + + cs.commitmentsByBlockNumber[blockNum] = append(cs.commitmentsByBlockNumber[blockNum], commitment) +} + +func (cs *CommitmentsStore) addCommitmentByHash(hash string, commitment *EncryptedPreConfirmationWithDecrypted) { + cs.commitmentsByCommitmentHashMu.Lock() + defer cs.commitmentsByCommitmentHashMu.Unlock() + + cs.commitmentsByCommitmentHash[hash] = commitment +} + +func (cs *CommitmentsStore) AddCommitment(commitment *EncryptedPreConfirmationWithDecrypted) { + cs.addCommitmentByBlockNumber(commitment.Bid.BlockNumber, commitment) + cs.addCommitmentByHash(common.Bytes2Hex(commitment.Commitment), commitment) +} + +func (cs *CommitmentsStore) GetCommitmentsByBlockNumber(blockNum int64) ([]*EncryptedPreConfirmationWithDecrypted, error) { + cs.commitmentByBlockNumberMu.RLock() + defer cs.commitmentByBlockNumberMu.RUnlock() + + if commitments, exists := cs.commitmentsByBlockNumber[blockNum]; exists { + return commitments, nil + } + return nil, nil +} + +func (cs *CommitmentsStore) GetCommitmentByHash(hash string) (*EncryptedPreConfirmationWithDecrypted, error) { + cs.commitmentsByCommitmentHashMu.RLock() + defer cs.commitmentsByCommitmentHashMu.RUnlock() + + if commitment, exists := cs.commitmentsByCommitmentHash[hash]; exists { + return commitment, nil + } + return nil, nil +} + +func (cs *CommitmentsStore) DeleteCommitmentByBlockNumber(blockNum int64) error { + cs.commitmentByBlockNumberMu.Lock() + defer cs.commitmentByBlockNumberMu.Unlock() + + for _, v := range cs.commitmentsByBlockNumber[blockNum] { + err := cs.deleteCommitmentByHash(common.Bytes2Hex(v.Commitment)) + if err != nil { + return err + } + } + delete(cs.commitmentsByBlockNumber, blockNum) + return nil +} + +func (cs *CommitmentsStore) deleteCommitmentByHash(hash string) error { + cs.commitmentsByCommitmentHashMu.Lock() + defer cs.commitmentsByCommitmentHashMu.Unlock() + + delete(cs.commitmentsByCommitmentHash, hash) + return nil +} + +func (cs *CommitmentsStore) SetCommitmentIndexByCommitmentDigest(cDigest, cIndex [32]byte) error { + // when we will have db, this will be UPDATE query, instead of inmemory update + commitment, err := cs.GetCommitmentByHash(common.Bytes2Hex(cDigest[:])) + if err != nil { + return fmt.Errorf("failed to get commitment by hash: %w", err) + } + if commitment == nil { + // commitment could be not found in case this commitment is from another bidder/provider + // so no need to return error in this case + return nil + } + commitment.EncryptedPreConfirmation.CommitmentIndex = cIndex[:] + return nil +} + +type BidderBalancesStore struct { + balances map[string]*big.Int + balancesByBlock *lru.Cache[string, *big.Int] + mu sync.RWMutex +} + +func (bbs *BidderBalancesStore) SetBalance(bidder common.Address, windowNumber, depositedAmount *big.Int) error { + bbs.mu.Lock() + defer bbs.mu.Unlock() + bssKey := getBBSKey(bidder, windowNumber) + bbs.balances[bssKey] = depositedAmount + return nil +} + +func (bbs *BidderBalancesStore) GetBalance(bidder common.Address, windowNumber *big.Int) (*big.Int, error) { + bbs.mu.RLock() + defer bbs.mu.RUnlock() + bssKey := getBBSKey(bidder, windowNumber) + if balance, exists := bbs.balances[bssKey]; exists { + return balance, nil + } + return nil, nil +} + +func getBBSKey(bidder common.Address, windowNumber *big.Int) string { + return bidder.String() + windowNumber.String() +} + +func (bbs *BidderBalancesStore) DeductAndCheckBalanceForBlock(bidder common.Address, defaultAmount, bidAmount *big.Int, blockNumber int64) (*big.Int, error) { + key := getBBSforBlockKey(bidder, blockNumber) + if currentBalance, ok := bbs.balancesByBlock.Get(key); ok { + if currentBalance.Cmp(bidAmount) >= 0 { + newBalance := new(big.Int).Sub(currentBalance, bidAmount) + bbs.balancesByBlock.Add(key, newBalance) + return newBalance, nil + } + return nil, fmt.Errorf("insufficient funds") + } + + // If no balance found, set balance to defaultAmount - bidAmount + if defaultAmount.Cmp(bidAmount) >= 0 { + newBalance := new(big.Int).Sub(defaultAmount, bidAmount) + bbs.balancesByBlock.Add(key, newBalance) + return newBalance, nil + } + return nil, fmt.Errorf("default amount is less than bid amount, cannot deduct") +} + + +func (bbs *BidderBalancesStore) RefundBalanceForBlock(bidder common.Address, amount *big.Int, blockNumber int64) error { + key := getBBSforBlockKey(bidder, blockNumber) + if currentBalance, ok := bbs.balancesByBlock.Get(key); ok { + // If a balance exists, simply add the amount back + updatedBalance := new(big.Int).Add(currentBalance, amount) + bbs.balancesByBlock.Add(key, updatedBalance) + return nil + } + + // If no balance found (which should be unusual for a refund), initialize to the refund amount + bbs.balancesByBlock.Add(key, amount) + return nil +} + +func getBBSforBlockKey(bidder common.Address, blockNumber int64) string { + return bidder.String() + fmt.Sprint(blockNumber) +} diff --git a/pkg/topology/topology.go b/pkg/topology/topology.go index 4c329c2d..4a3f6ebf 100644 --- a/pkg/topology/topology.go +++ b/pkg/topology/topology.go @@ -25,6 +25,11 @@ type Topology struct { addressbook p2p.Addressbook announcer Announcer metrics *metrics + subs []func(p2p.Peer) +} + +func (t *Topology) SubscribePeer(handler func(p2p.Peer)) { + t.subs = append(t.subs, handler) } func New(a p2p.Addressbook, logger *slog.Logger) *Topology { @@ -127,6 +132,9 @@ func (t *Topology) Disconnected(p p2p.Peer) { func (t *Topology) AddPeers(peers ...p2p.Peer) { for _, p := range peers { t.add(p) + for _, sub := range t.subs { + sub(p) + } } } diff --git a/rpc/bidderapi/v1/bidderapi.proto b/rpc/bidderapi/v1/bidderapi.proto index 09a7cd3c..6d2e4da0 100644 --- a/rpc/bidderapi/v1/bidderapi.proto +++ b/rpc/bidderapi/v1/bidderapi.proto @@ -5,6 +5,7 @@ package bidderapi.v1; import "protoc-gen-openapiv2/options/annotations.proto"; import "google/api/annotations.proto"; import "buf/validate/validate.proto"; +import "google/protobuf/wrappers.proto"; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { @@ -27,58 +28,88 @@ service Bidder { body: "*" }; } - // PrepayAllowance + // Deposit // - // PrepayAllowance is called by the bidder node to add prepaid allowance in the bidder registry. - rpc PrepayAllowance(PrepayRequest) returns (PrepayResponse) { - option (google.api.http) = {post: "/v1/bidder/prepay/{amount}"}; + // Deposit is called by the bidder node to add deposit in the bidder registry. + rpc Deposit(DepositRequest) returns (DepositResponse) { + option (google.api.http) = {post: "/v1/bidder/deposit/{amount}"}; } - // GetAllowance + // GetDeposit // - // GetAllowance is called by the bidder to get its allowance in the bidder registry. - rpc GetAllowance(EmptyMessage) returns (PrepayResponse) { - option (google.api.http) = {get: "/v1/bidder/get_allowance"}; + // GetDeposit is called by the bidder to get its deposit in the bidder registry. + rpc GetDeposit(GetDepositRequest) returns (DepositResponse) { + option (google.api.http) = { + get: "/v1/bidder/get_deposit" + }; } - // GetMinAllowance + // GetMinDeposit // - // GetMinAllowance is called by the bidder to get the minimum allowance required in the bidder registry to make bids. - rpc GetMinAllowance(EmptyMessage) returns (PrepayResponse) { - option (google.api.http) = {get: "/v1/bidder/get_min_allowance"}; + // GetMinDeposit is called by the bidder to get the minimum deposit required in the bidder registry to make bids. + rpc GetMinDeposit(EmptyMessage) returns (DepositResponse) { + option (google.api.http) = {get: "/v1/bidder/get_min_deposit"}; } } -message PrepayRequest { +message DepositRequest { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { json_schema: { - title: "Prepay request" - description: "Prepayment for bids to be issued by the bidder in wei." + title: "Deposit request" + description: "Deposit for bids to be issued by the bidder in wei." required: ["amount"] } - example: "{\"amount\": \"1000000000000000000\" }" + example: "{\"amount\": \"1000000000000000000\", \"windowNumber\": 1 }" }; string amount = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "Amount of ETH to be prepaid in wei." + description: "Amount of ETH to be deposited in wei." pattern: "[0-9]+" }, (buf.validate.field).cel = { id: "amount", message: "amount must be a valid integer.", - expression: "this.matches('^[0-9]+$') && uint(this) > 0" - }]; + expression: "this.matches('^[0-9]+$')" + }]; + google.protobuf.UInt64Value windowNumber = 2 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Optional window number for querying deposit. If not specified, the current block number is used." + }, (buf.validate.field).cel = { + id: "windowNumber", + message: "windowNumber must be a positive integer if specified.", + expression: "this == null || (this > 0)" + }]; + google.protobuf.UInt64Value blockNumber = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Optional block number for querying deposit. If specified, calculate window based on this block number." + }, (buf.validate.field).cel = { + id: "blockNumber", + message: "blockNumber must be a positive integer if specified.", + expression: "this == null || (this > 0)" + }]; }; -message PrepayResponse { +message DepositResponse { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { json_schema: { - title: "Prepay response" - description: "Get prepaid allowance for bidder in the bidder registry." + title: "Deposit response" + description: "Get deposit for bidder in the bidder registry." } - example: "{\"amount\": \"1000000000000000000\" }" + example: "{\"amount\": \"1000000000000000000\", \"windowNumber\": \"1\" }" }; string amount = 1; + google.protobuf.UInt64Value windowNumber = 2; }; message EmptyMessage {}; +message GetDepositRequest { + google.protobuf.UInt64Value windowNumber = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Optional window number for querying deposits. If not specified, the current block number is used." + }, (buf.validate.field).cel = { + id: "windowNumber", + message: "windowNumber must be a positive integer if specified.", + expression: "this == null || (this > 0)" + }]; +} + message Bid { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { json_schema: {