From 0167fed49ebfd1391e882ca2fe1387e2b3b8d1dd Mon Sep 17 00:00:00 2001 From: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:23:24 +0100 Subject: [PATCH] Support watching custom scripts (#71) * Add external script repo * Add external script repo impls * Remove leftover * Support (un)watching external scripts * Prevent send notifications for unsubscribed scripts/acounts * Fixes --- .../gen/go/ocean/v1/notification.pb.go | 622 +++++++++++++----- .../gen/go/ocean/v1/notification_grpc.pb.go | 84 +++ api-spec/protobuf/ocean/v1/account.proto | 2 +- api-spec/protobuf/ocean/v1/notification.proto | 30 +- internal/app-config/config.go | 3 +- internal/core/application/account_service.go | 23 - .../core/application/notification_service.go | 184 +++++- .../application/notification_service_test.go | 4 +- .../core/domain/external_script_repository.go | 38 ++ internal/core/ports/repo_manager.go | 14 +- .../blockchain-scanner/electrum/tcp_client.go | 17 +- .../blockchain-scanner/electrum/types.go | 4 + .../blockchain-scanner/electrum/ws_client.go | 17 +- .../storage/db/badger/repo_manager.go | 42 +- .../storage/db/badger/script_repository.go | 158 +++++ .../storage/db/inmemory/repo_manager.go | 31 + .../storage/db/inmemory/script_repository.go | 137 ++++ .../20240103141047_add_script_table.down.sql | 1 + .../20240103141047_add_script_table.up.sql | 5 + .../storage/db/postgres/repo_manager.go | 31 + .../storage/db/postgres/script_repository.go | 118 ++++ .../db/postgres/sqlc/queries/copyfrom.go | 2 +- .../storage/db/postgres/sqlc/queries/db.go | 2 +- .../db/postgres/sqlc/queries/models.go | 9 +- .../db/postgres/sqlc/queries/query.sql.go | 80 ++- .../storage/db/postgres/sqlc/query.sql | 16 + .../storage/db/test/script_repository_test.go | 97 +++ .../interfaces/grpc/handler/notification.go | 35 + internal/interfaces/grpc/handler/util.go | 25 + internal/interfaces/grpc/service.go | 6 +- 30 files changed, 1601 insertions(+), 236 deletions(-) create mode 100644 internal/core/domain/external_script_repository.go create mode 100644 internal/infrastructure/storage/db/badger/script_repository.go create mode 100644 internal/infrastructure/storage/db/inmemory/script_repository.go create mode 100644 internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.down.sql create mode 100644 internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.up.sql create mode 100644 internal/infrastructure/storage/db/postgres/script_repository.go create mode 100644 internal/infrastructure/storage/db/test/script_repository_test.go diff --git a/api-spec/protobuf/gen/go/ocean/v1/notification.pb.go b/api-spec/protobuf/gen/go/ocean/v1/notification.pb.go index 4cc293a..cf5cfb4 100644 --- a/api-spec/protobuf/gen/go/ocean/v1/notification.pb.go +++ b/api-spec/protobuf/gen/go/ocean/v1/notification.pb.go @@ -20,6 +20,195 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type WatchExternalScriptRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The script to watch. + Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + // Optional: the private blinding key in case the script locks confidential utxos to unblind. + BlindingKey string `protobuf:"bytes,2,opt,name=blinding_key,json=blindingKey,proto3" json:"blinding_key,omitempty"` +} + +func (x *WatchExternalScriptRequest) Reset() { + *x = WatchExternalScriptRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ocean_v1_notification_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WatchExternalScriptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchExternalScriptRequest) ProtoMessage() {} + +func (x *WatchExternalScriptRequest) ProtoReflect() protoreflect.Message { + mi := &file_ocean_v1_notification_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 WatchExternalScriptRequest.ProtoReflect.Descriptor instead. +func (*WatchExternalScriptRequest) Descriptor() ([]byte, []int) { + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{0} +} + +func (x *WatchExternalScriptRequest) GetScript() string { + if x != nil { + return x.Script + } + return "" +} + +func (x *WatchExternalScriptRequest) GetBlindingKey() string { + if x != nil { + return x.BlindingKey + } + return "" +} + +type WatchExternalScriptResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` +} + +func (x *WatchExternalScriptResponse) Reset() { + *x = WatchExternalScriptResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ocean_v1_notification_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WatchExternalScriptResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchExternalScriptResponse) ProtoMessage() {} + +func (x *WatchExternalScriptResponse) ProtoReflect() protoreflect.Message { + mi := &file_ocean_v1_notification_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 WatchExternalScriptResponse.ProtoReflect.Descriptor instead. +func (*WatchExternalScriptResponse) Descriptor() ([]byte, []int) { + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{1} +} + +func (x *WatchExternalScriptResponse) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +type UnwatchExternalScriptRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` +} + +func (x *UnwatchExternalScriptRequest) Reset() { + *x = UnwatchExternalScriptRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ocean_v1_notification_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnwatchExternalScriptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnwatchExternalScriptRequest) ProtoMessage() {} + +func (x *UnwatchExternalScriptRequest) ProtoReflect() protoreflect.Message { + mi := &file_ocean_v1_notification_proto_msgTypes[2] + 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 UnwatchExternalScriptRequest.ProtoReflect.Descriptor instead. +func (*UnwatchExternalScriptRequest) Descriptor() ([]byte, []int) { + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{2} +} + +func (x *UnwatchExternalScriptRequest) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +type UnwatchExternalScriptResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UnwatchExternalScriptResponse) Reset() { + *x = UnwatchExternalScriptResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ocean_v1_notification_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnwatchExternalScriptResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnwatchExternalScriptResponse) ProtoMessage() {} + +func (x *UnwatchExternalScriptResponse) ProtoReflect() protoreflect.Message { + mi := &file_ocean_v1_notification_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 UnwatchExternalScriptResponse.ProtoReflect.Descriptor instead. +func (*UnwatchExternalScriptResponse) Descriptor() ([]byte, []int) { + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{3} +} + type TransactionNotificationsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -29,7 +218,7 @@ type TransactionNotificationsRequest struct { func (x *TransactionNotificationsRequest) Reset() { *x = TransactionNotificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[0] + mi := &file_ocean_v1_notification_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -42,7 +231,7 @@ func (x *TransactionNotificationsRequest) String() string { func (*TransactionNotificationsRequest) ProtoMessage() {} func (x *TransactionNotificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[0] + mi := &file_ocean_v1_notification_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -55,7 +244,7 @@ func (x *TransactionNotificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TransactionNotificationsRequest.ProtoReflect.Descriptor instead. func (*TransactionNotificationsRequest) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{0} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{4} } type TransactionNotificationsResponse struct { @@ -78,7 +267,7 @@ type TransactionNotificationsResponse struct { func (x *TransactionNotificationsResponse) Reset() { *x = TransactionNotificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[1] + mi := &file_ocean_v1_notification_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -91,7 +280,7 @@ func (x *TransactionNotificationsResponse) String() string { func (*TransactionNotificationsResponse) ProtoMessage() {} func (x *TransactionNotificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[1] + mi := &file_ocean_v1_notification_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -104,7 +293,7 @@ func (x *TransactionNotificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TransactionNotificationsResponse.ProtoReflect.Descriptor instead. func (*TransactionNotificationsResponse) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{1} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{5} } func (x *TransactionNotificationsResponse) GetEventType() TxEventType { @@ -151,7 +340,7 @@ type UtxosNotificationsRequest struct { func (x *UtxosNotificationsRequest) Reset() { *x = UtxosNotificationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[2] + mi := &file_ocean_v1_notification_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -164,7 +353,7 @@ func (x *UtxosNotificationsRequest) String() string { func (*UtxosNotificationsRequest) ProtoMessage() {} func (x *UtxosNotificationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[2] + mi := &file_ocean_v1_notification_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -177,7 +366,7 @@ func (x *UtxosNotificationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UtxosNotificationsRequest.ProtoReflect.Descriptor instead. func (*UtxosNotificationsRequest) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{2} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{6} } type UtxosNotificationsResponse struct { @@ -194,7 +383,7 @@ type UtxosNotificationsResponse struct { func (x *UtxosNotificationsResponse) Reset() { *x = UtxosNotificationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[3] + mi := &file_ocean_v1_notification_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -207,7 +396,7 @@ func (x *UtxosNotificationsResponse) String() string { func (*UtxosNotificationsResponse) ProtoMessage() {} func (x *UtxosNotificationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[3] + mi := &file_ocean_v1_notification_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -220,7 +409,7 @@ func (x *UtxosNotificationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UtxosNotificationsResponse.ProtoReflect.Descriptor instead. func (*UtxosNotificationsResponse) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{3} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{7} } func (x *UtxosNotificationsResponse) GetEventType() UtxoEventType { @@ -254,7 +443,7 @@ type AddWebhookRequest struct { func (x *AddWebhookRequest) Reset() { *x = AddWebhookRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[4] + mi := &file_ocean_v1_notification_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -267,7 +456,7 @@ func (x *AddWebhookRequest) String() string { func (*AddWebhookRequest) ProtoMessage() {} func (x *AddWebhookRequest) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[4] + mi := &file_ocean_v1_notification_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -280,7 +469,7 @@ func (x *AddWebhookRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWebhookRequest.ProtoReflect.Descriptor instead. func (*AddWebhookRequest) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{4} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{8} } func (x *AddWebhookRequest) GetEndpoint() string { @@ -316,7 +505,7 @@ type AddWebhookResponse struct { func (x *AddWebhookResponse) Reset() { *x = AddWebhookResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[5] + mi := &file_ocean_v1_notification_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -329,7 +518,7 @@ func (x *AddWebhookResponse) String() string { func (*AddWebhookResponse) ProtoMessage() {} func (x *AddWebhookResponse) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[5] + mi := &file_ocean_v1_notification_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -342,7 +531,7 @@ func (x *AddWebhookResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddWebhookResponse.ProtoReflect.Descriptor instead. func (*AddWebhookResponse) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{5} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{9} } func (x *AddWebhookResponse) GetId() string { @@ -364,7 +553,7 @@ type RemoveWebhookRequest struct { func (x *RemoveWebhookRequest) Reset() { *x = RemoveWebhookRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[6] + mi := &file_ocean_v1_notification_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -377,7 +566,7 @@ func (x *RemoveWebhookRequest) String() string { func (*RemoveWebhookRequest) ProtoMessage() {} func (x *RemoveWebhookRequest) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[6] + mi := &file_ocean_v1_notification_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -390,7 +579,7 @@ func (x *RemoveWebhookRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWebhookRequest.ProtoReflect.Descriptor instead. func (*RemoveWebhookRequest) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{6} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{10} } func (x *RemoveWebhookRequest) GetId() string { @@ -409,7 +598,7 @@ type RemoveWebhookResponse struct { func (x *RemoveWebhookResponse) Reset() { *x = RemoveWebhookResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[7] + mi := &file_ocean_v1_notification_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -422,7 +611,7 @@ func (x *RemoveWebhookResponse) String() string { func (*RemoveWebhookResponse) ProtoMessage() {} func (x *RemoveWebhookResponse) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[7] + mi := &file_ocean_v1_notification_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -435,7 +624,7 @@ func (x *RemoveWebhookResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveWebhookResponse.ProtoReflect.Descriptor instead. func (*RemoveWebhookResponse) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{7} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{11} } type ListWebhooksRequest struct { @@ -450,7 +639,7 @@ type ListWebhooksRequest struct { func (x *ListWebhooksRequest) Reset() { *x = ListWebhooksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[8] + mi := &file_ocean_v1_notification_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -463,7 +652,7 @@ func (x *ListWebhooksRequest) String() string { func (*ListWebhooksRequest) ProtoMessage() {} func (x *ListWebhooksRequest) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[8] + mi := &file_ocean_v1_notification_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -476,7 +665,7 @@ func (x *ListWebhooksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWebhooksRequest.ProtoReflect.Descriptor instead. func (*ListWebhooksRequest) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{8} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{12} } func (x *ListWebhooksRequest) GetEventType() WebhookEventType { @@ -498,7 +687,7 @@ type ListWebhooksResponse struct { func (x *ListWebhooksResponse) Reset() { *x = ListWebhooksResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[9] + mi := &file_ocean_v1_notification_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -511,7 +700,7 @@ func (x *ListWebhooksResponse) String() string { func (*ListWebhooksResponse) ProtoMessage() {} func (x *ListWebhooksResponse) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[9] + mi := &file_ocean_v1_notification_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -524,7 +713,7 @@ func (x *ListWebhooksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWebhooksResponse.ProtoReflect.Descriptor instead. func (*ListWebhooksResponse) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{9} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{13} } func (x *ListWebhooksResponse) GetWebhookInfo() []*WebhookInfo { @@ -550,7 +739,7 @@ type WebhookInfo struct { func (x *WebhookInfo) Reset() { *x = WebhookInfo{} if protoimpl.UnsafeEnabled { - mi := &file_ocean_v1_notification_proto_msgTypes[10] + mi := &file_ocean_v1_notification_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -563,7 +752,7 @@ func (x *WebhookInfo) String() string { func (*WebhookInfo) ProtoMessage() {} func (x *WebhookInfo) ProtoReflect() protoreflect.Message { - mi := &file_ocean_v1_notification_proto_msgTypes[10] + mi := &file_ocean_v1_notification_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -576,7 +765,7 @@ func (x *WebhookInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use WebhookInfo.ProtoReflect.Descriptor instead. func (*WebhookInfo) Descriptor() ([]byte, []int) { - return file_ocean_v1_notification_proto_rawDescGZIP(), []int{10} + return file_ocean_v1_notification_proto_rawDescGZIP(), []int{14} } func (x *WebhookInfo) GetId() string { @@ -606,105 +795,132 @@ var file_ocean_v1_notification_proto_rawDesc = []byte{ 0x0a, 0x1b, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x14, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2f, 0x76, - 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x21, 0x0a, - 0x1f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0xe4, 0x01, 0x0a, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6f, 0x63, 0x65, 0x61, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x78, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x68, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x74, 0x78, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x0d, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x55, 0x74, 0x78, 0x6f, 0x73, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x7a, 0x0a, 0x1a, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x75, 0x74, - 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6f, 0x63, 0x65, 0x61, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x52, 0x05, 0x75, 0x74, 0x78, 0x6f, 0x73, - 0x22, 0x82, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x24, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, 0x68, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x26, 0x0a, 0x14, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, - 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x0a, 0x13, - 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x50, - 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, - 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, - 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x49, 0x6e, 0x66, 0x6f, - 0x22, 0x58, 0x0a, 0x0b, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, - 0x73, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x69, 0x73, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x64, 0x32, 0xdd, 0x03, 0x0a, 0x13, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, - 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x57, 0x0a, + 0x1a, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x22, 0x33, 0x0a, 0x1b, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x34, 0x0a, 0x1c, 0x55, + 0x6e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x22, 0x1f, 0x0a, 0x1d, 0x55, 0x6e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, 0x63, 0x65, 0x61, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x61, 0x0a, 0x12, 0x55, 0x74, 0x78, 0x6f, 0x73, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, - 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x74, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, + 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x78, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x68, 0x65, 0x78, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x78, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x78, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, + 0x3b, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0c, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x1b, 0x0a, 0x19, + 0x55, 0x74, 0x78, 0x6f, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7a, 0x0a, 0x1a, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6f, 0x63, + 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x24, 0x0a, 0x05, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x52, 0x05, + 0x75, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, + 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6f, 0x63, + 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x24, 0x0a, 0x12, 0x41, 0x64, + 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x22, 0x26, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x50, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6f, + 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x22, 0x50, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, + 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x77, + 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, + 0x68, 0x6f, 0x6f, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x58, 0x0a, 0x0b, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x64, 0x32, + 0xa5, 0x05, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x62, 0x0a, 0x13, 0x57, 0x61, 0x74, 0x63, 0x68, + 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x24, + 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x15, 0x55, + 0x6e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x12, 0x26, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x6e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, + 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x29, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6f, + 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x61, 0x0a, 0x12, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x49, 0x0a, 0x0a, 0x41, 0x64, - 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, + 0x12, 0x23, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x74, 0x78, 0x6f, + 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x47, 0x0a, + 0x0a, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x6f, 0x63, + 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x64, 0x64, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, - 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x1e, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, - 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x6f, 0x63, 0x65, 0x61, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x1e, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, + 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xaa, 0x01, 0x0a, 0x0c, 0x63, - 0x6f, 0x6d, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x11, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x75, 0x6c, - 0x70, 0x65, 0x6d, 0x76, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2f, 0x6f, 0x63, 0x65, 0x61, - 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2f, 0x76, 0x31, - 0x3b, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, - 0x08, 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x08, 0x4f, 0x63, 0x65, 0x61, - 0x6e, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x14, 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x5c, 0x56, 0x31, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x4f, 0x63, - 0x65, 0x61, 0x6e, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xaa, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, + 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x11, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x75, 0x6c, 0x70, 0x65, 0x6d, + 0x76, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2f, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2f, 0x61, + 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x65, 0x61, 0x6e, 0x2f, 0x76, 0x31, 0x3b, 0x6f, 0x63, + 0x65, 0x61, 0x6e, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x4f, 0x63, + 0x65, 0x61, 0x6e, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x08, 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x14, 0x4f, 0x63, 0x65, 0x61, 0x6e, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x4f, 0x63, 0x65, 0x61, 0x6e, + 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -719,45 +935,53 @@ func file_ocean_v1_notification_proto_rawDescGZIP() []byte { return file_ocean_v1_notification_proto_rawDescData } -var file_ocean_v1_notification_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_ocean_v1_notification_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_ocean_v1_notification_proto_goTypes = []interface{}{ - (*TransactionNotificationsRequest)(nil), // 0: ocean.v1.TransactionNotificationsRequest - (*TransactionNotificationsResponse)(nil), // 1: ocean.v1.TransactionNotificationsResponse - (*UtxosNotificationsRequest)(nil), // 2: ocean.v1.UtxosNotificationsRequest - (*UtxosNotificationsResponse)(nil), // 3: ocean.v1.UtxosNotificationsResponse - (*AddWebhookRequest)(nil), // 4: ocean.v1.AddWebhookRequest - (*AddWebhookResponse)(nil), // 5: ocean.v1.AddWebhookResponse - (*RemoveWebhookRequest)(nil), // 6: ocean.v1.RemoveWebhookRequest - (*RemoveWebhookResponse)(nil), // 7: ocean.v1.RemoveWebhookResponse - (*ListWebhooksRequest)(nil), // 8: ocean.v1.ListWebhooksRequest - (*ListWebhooksResponse)(nil), // 9: ocean.v1.ListWebhooksResponse - (*WebhookInfo)(nil), // 10: ocean.v1.WebhookInfo - (TxEventType)(0), // 11: ocean.v1.TxEventType - (*BlockDetails)(nil), // 12: ocean.v1.BlockDetails - (UtxoEventType)(0), // 13: ocean.v1.UtxoEventType - (*Utxo)(nil), // 14: ocean.v1.Utxo - (WebhookEventType)(0), // 15: ocean.v1.WebhookEventType + (*WatchExternalScriptRequest)(nil), // 0: ocean.v1.WatchExternalScriptRequest + (*WatchExternalScriptResponse)(nil), // 1: ocean.v1.WatchExternalScriptResponse + (*UnwatchExternalScriptRequest)(nil), // 2: ocean.v1.UnwatchExternalScriptRequest + (*UnwatchExternalScriptResponse)(nil), // 3: ocean.v1.UnwatchExternalScriptResponse + (*TransactionNotificationsRequest)(nil), // 4: ocean.v1.TransactionNotificationsRequest + (*TransactionNotificationsResponse)(nil), // 5: ocean.v1.TransactionNotificationsResponse + (*UtxosNotificationsRequest)(nil), // 6: ocean.v1.UtxosNotificationsRequest + (*UtxosNotificationsResponse)(nil), // 7: ocean.v1.UtxosNotificationsResponse + (*AddWebhookRequest)(nil), // 8: ocean.v1.AddWebhookRequest + (*AddWebhookResponse)(nil), // 9: ocean.v1.AddWebhookResponse + (*RemoveWebhookRequest)(nil), // 10: ocean.v1.RemoveWebhookRequest + (*RemoveWebhookResponse)(nil), // 11: ocean.v1.RemoveWebhookResponse + (*ListWebhooksRequest)(nil), // 12: ocean.v1.ListWebhooksRequest + (*ListWebhooksResponse)(nil), // 13: ocean.v1.ListWebhooksResponse + (*WebhookInfo)(nil), // 14: ocean.v1.WebhookInfo + (TxEventType)(0), // 15: ocean.v1.TxEventType + (*BlockDetails)(nil), // 16: ocean.v1.BlockDetails + (UtxoEventType)(0), // 17: ocean.v1.UtxoEventType + (*Utxo)(nil), // 18: ocean.v1.Utxo + (WebhookEventType)(0), // 19: ocean.v1.WebhookEventType } var file_ocean_v1_notification_proto_depIdxs = []int32{ - 11, // 0: ocean.v1.TransactionNotificationsResponse.event_type:type_name -> ocean.v1.TxEventType - 12, // 1: ocean.v1.TransactionNotificationsResponse.block_details:type_name -> ocean.v1.BlockDetails - 13, // 2: ocean.v1.UtxosNotificationsResponse.event_type:type_name -> ocean.v1.UtxoEventType - 14, // 3: ocean.v1.UtxosNotificationsResponse.utxos:type_name -> ocean.v1.Utxo - 15, // 4: ocean.v1.AddWebhookRequest.event_type:type_name -> ocean.v1.WebhookEventType - 15, // 5: ocean.v1.ListWebhooksRequest.event_type:type_name -> ocean.v1.WebhookEventType - 10, // 6: ocean.v1.ListWebhooksResponse.webhook_info:type_name -> ocean.v1.WebhookInfo - 0, // 7: ocean.v1.NotificationService.TransactionNotifications:input_type -> ocean.v1.TransactionNotificationsRequest - 2, // 8: ocean.v1.NotificationService.UtxosNotifications:input_type -> ocean.v1.UtxosNotificationsRequest - 4, // 9: ocean.v1.NotificationService.AddWebhook:input_type -> ocean.v1.AddWebhookRequest - 6, // 10: ocean.v1.NotificationService.RemoveWebhook:input_type -> ocean.v1.RemoveWebhookRequest - 8, // 11: ocean.v1.NotificationService.ListWebhooks:input_type -> ocean.v1.ListWebhooksRequest - 1, // 12: ocean.v1.NotificationService.TransactionNotifications:output_type -> ocean.v1.TransactionNotificationsResponse - 3, // 13: ocean.v1.NotificationService.UtxosNotifications:output_type -> ocean.v1.UtxosNotificationsResponse - 5, // 14: ocean.v1.NotificationService.AddWebhook:output_type -> ocean.v1.AddWebhookResponse - 7, // 15: ocean.v1.NotificationService.RemoveWebhook:output_type -> ocean.v1.RemoveWebhookResponse - 9, // 16: ocean.v1.NotificationService.ListWebhooks:output_type -> ocean.v1.ListWebhooksResponse - 12, // [12:17] is the sub-list for method output_type - 7, // [7:12] is the sub-list for method input_type + 15, // 0: ocean.v1.TransactionNotificationsResponse.event_type:type_name -> ocean.v1.TxEventType + 16, // 1: ocean.v1.TransactionNotificationsResponse.block_details:type_name -> ocean.v1.BlockDetails + 17, // 2: ocean.v1.UtxosNotificationsResponse.event_type:type_name -> ocean.v1.UtxoEventType + 18, // 3: ocean.v1.UtxosNotificationsResponse.utxos:type_name -> ocean.v1.Utxo + 19, // 4: ocean.v1.AddWebhookRequest.event_type:type_name -> ocean.v1.WebhookEventType + 19, // 5: ocean.v1.ListWebhooksRequest.event_type:type_name -> ocean.v1.WebhookEventType + 14, // 6: ocean.v1.ListWebhooksResponse.webhook_info:type_name -> ocean.v1.WebhookInfo + 0, // 7: ocean.v1.NotificationService.WatchExternalScript:input_type -> ocean.v1.WatchExternalScriptRequest + 2, // 8: ocean.v1.NotificationService.UnwatchExternalScript:input_type -> ocean.v1.UnwatchExternalScriptRequest + 4, // 9: ocean.v1.NotificationService.TransactionNotifications:input_type -> ocean.v1.TransactionNotificationsRequest + 6, // 10: ocean.v1.NotificationService.UtxosNotifications:input_type -> ocean.v1.UtxosNotificationsRequest + 8, // 11: ocean.v1.NotificationService.AddWebhook:input_type -> ocean.v1.AddWebhookRequest + 10, // 12: ocean.v1.NotificationService.RemoveWebhook:input_type -> ocean.v1.RemoveWebhookRequest + 12, // 13: ocean.v1.NotificationService.ListWebhooks:input_type -> ocean.v1.ListWebhooksRequest + 1, // 14: ocean.v1.NotificationService.WatchExternalScript:output_type -> ocean.v1.WatchExternalScriptResponse + 3, // 15: ocean.v1.NotificationService.UnwatchExternalScript:output_type -> ocean.v1.UnwatchExternalScriptResponse + 5, // 16: ocean.v1.NotificationService.TransactionNotifications:output_type -> ocean.v1.TransactionNotificationsResponse + 7, // 17: ocean.v1.NotificationService.UtxosNotifications:output_type -> ocean.v1.UtxosNotificationsResponse + 9, // 18: ocean.v1.NotificationService.AddWebhook:output_type -> ocean.v1.AddWebhookResponse + 11, // 19: ocean.v1.NotificationService.RemoveWebhook:output_type -> ocean.v1.RemoveWebhookResponse + 13, // 20: ocean.v1.NotificationService.ListWebhooks:output_type -> ocean.v1.ListWebhooksResponse + 14, // [14:21] is the sub-list for method output_type + 7, // [7:14] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name @@ -771,7 +995,7 @@ func file_ocean_v1_notification_proto_init() { file_ocean_v1_types_proto_init() if !protoimpl.UnsafeEnabled { file_ocean_v1_notification_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TransactionNotificationsRequest); i { + switch v := v.(*WatchExternalScriptRequest); i { case 0: return &v.state case 1: @@ -783,7 +1007,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TransactionNotificationsResponse); i { + switch v := v.(*WatchExternalScriptResponse); i { case 0: return &v.state case 1: @@ -795,7 +1019,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UtxosNotificationsRequest); i { + switch v := v.(*UnwatchExternalScriptRequest); i { case 0: return &v.state case 1: @@ -807,7 +1031,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UtxosNotificationsResponse); i { + switch v := v.(*UnwatchExternalScriptResponse); i { case 0: return &v.state case 1: @@ -819,7 +1043,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddWebhookRequest); i { + switch v := v.(*TransactionNotificationsRequest); i { case 0: return &v.state case 1: @@ -831,7 +1055,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddWebhookResponse); i { + switch v := v.(*TransactionNotificationsResponse); i { case 0: return &v.state case 1: @@ -843,7 +1067,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveWebhookRequest); i { + switch v := v.(*UtxosNotificationsRequest); i { case 0: return &v.state case 1: @@ -855,7 +1079,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveWebhookResponse); i { + switch v := v.(*UtxosNotificationsResponse); i { case 0: return &v.state case 1: @@ -867,7 +1091,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListWebhooksRequest); i { + switch v := v.(*AddWebhookRequest); i { case 0: return &v.state case 1: @@ -879,7 +1103,7 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListWebhooksResponse); i { + switch v := v.(*AddWebhookResponse); i { case 0: return &v.state case 1: @@ -891,6 +1115,54 @@ func file_ocean_v1_notification_proto_init() { } } file_ocean_v1_notification_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveWebhookRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ocean_v1_notification_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveWebhookResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ocean_v1_notification_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListWebhooksRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ocean_v1_notification_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListWebhooksResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ocean_v1_notification_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WebhookInfo); i { case 0: return &v.state @@ -909,7 +1181,7 @@ func file_ocean_v1_notification_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ocean_v1_notification_proto_rawDesc, NumEnums: 0, - NumMessages: 11, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/api-spec/protobuf/gen/go/ocean/v1/notification_grpc.pb.go b/api-spec/protobuf/gen/go/ocean/v1/notification_grpc.pb.go index ada3c3b..a8bf4cb 100644 --- a/api-spec/protobuf/gen/go/ocean/v1/notification_grpc.pb.go +++ b/api-spec/protobuf/gen/go/ocean/v1/notification_grpc.pb.go @@ -18,6 +18,14 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type NotificationServiceClient interface { + // WatchExternalScript allows to get notified about utxos/txs related to the given + // external script, ie. not derived from a wallet account. + // The service answers with the label assigned to the given script. + // The label is used as identifier of the utxos/txs received from the streams. + WatchExternalScript(ctx context.Context, in *WatchExternalScriptRequest, opts ...grpc.CallOption) (*WatchExternalScriptResponse, error) + // UnwatchExternalScript allows to stop watching for the script identified with + // the given label. + UnwatchExternalScript(ctx context.Context, in *UnwatchExternalScriptRequest, opts ...grpc.CallOption) (*UnwatchExternalScriptResponse, error) // Notifies about events related to wallet transactions. TransactionNotifications(ctx context.Context, in *TransactionNotificationsRequest, opts ...grpc.CallOption) (NotificationService_TransactionNotificationsClient, error) // Notifies about events realted to wallet utxos. @@ -38,6 +46,24 @@ func NewNotificationServiceClient(cc grpc.ClientConnInterface) NotificationServi return ¬ificationServiceClient{cc} } +func (c *notificationServiceClient) WatchExternalScript(ctx context.Context, in *WatchExternalScriptRequest, opts ...grpc.CallOption) (*WatchExternalScriptResponse, error) { + out := new(WatchExternalScriptResponse) + err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/WatchExternalScript", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *notificationServiceClient) UnwatchExternalScript(ctx context.Context, in *UnwatchExternalScriptRequest, opts ...grpc.CallOption) (*UnwatchExternalScriptResponse, error) { + out := new(UnwatchExternalScriptResponse) + err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/UnwatchExternalScript", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *notificationServiceClient) TransactionNotifications(ctx context.Context, in *TransactionNotificationsRequest, opts ...grpc.CallOption) (NotificationService_TransactionNotificationsClient, error) { stream, err := c.cc.NewStream(ctx, &NotificationService_ServiceDesc.Streams[0], "/ocean.v1.NotificationService/TransactionNotifications", opts...) if err != nil { @@ -133,6 +159,14 @@ func (c *notificationServiceClient) ListWebhooks(ctx context.Context, in *ListWe // All implementations should embed UnimplementedNotificationServiceServer // for forward compatibility type NotificationServiceServer interface { + // WatchExternalScript allows to get notified about utxos/txs related to the given + // external script, ie. not derived from a wallet account. + // The service answers with the label assigned to the given script. + // The label is used as identifier of the utxos/txs received from the streams. + WatchExternalScript(context.Context, *WatchExternalScriptRequest) (*WatchExternalScriptResponse, error) + // UnwatchExternalScript allows to stop watching for the script identified with + // the given label. + UnwatchExternalScript(context.Context, *UnwatchExternalScriptRequest) (*UnwatchExternalScriptResponse, error) // Notifies about events related to wallet transactions. TransactionNotifications(*TransactionNotificationsRequest, NotificationService_TransactionNotificationsServer) error // Notifies about events realted to wallet utxos. @@ -149,6 +183,12 @@ type NotificationServiceServer interface { type UnimplementedNotificationServiceServer struct { } +func (UnimplementedNotificationServiceServer) WatchExternalScript(context.Context, *WatchExternalScriptRequest) (*WatchExternalScriptResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WatchExternalScript not implemented") +} +func (UnimplementedNotificationServiceServer) UnwatchExternalScript(context.Context, *UnwatchExternalScriptRequest) (*UnwatchExternalScriptResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnwatchExternalScript not implemented") +} func (UnimplementedNotificationServiceServer) TransactionNotifications(*TransactionNotificationsRequest, NotificationService_TransactionNotificationsServer) error { return status.Errorf(codes.Unimplemented, "method TransactionNotifications not implemented") } @@ -176,6 +216,42 @@ func RegisterNotificationServiceServer(s grpc.ServiceRegistrar, srv Notification s.RegisterService(&NotificationService_ServiceDesc, srv) } +func _NotificationService_WatchExternalScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchExternalScriptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationServiceServer).WatchExternalScript(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocean.v1.NotificationService/WatchExternalScript", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationServiceServer).WatchExternalScript(ctx, req.(*WatchExternalScriptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NotificationService_UnwatchExternalScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnwatchExternalScriptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationServiceServer).UnwatchExternalScript(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocean.v1.NotificationService/UnwatchExternalScript", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationServiceServer).UnwatchExternalScript(ctx, req.(*UnwatchExternalScriptRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _NotificationService_TransactionNotifications_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(TransactionNotificationsRequest) if err := stream.RecvMsg(m); err != nil { @@ -279,6 +355,14 @@ var NotificationService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "ocean.v1.NotificationService", HandlerType: (*NotificationServiceServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "WatchExternalScript", + Handler: _NotificationService_WatchExternalScript_Handler, + }, + { + MethodName: "UnwatchExternalScript", + Handler: _NotificationService_UnwatchExternalScript_Handler, + }, { MethodName: "AddWebhook", Handler: _NotificationService_AddWebhook_Handler, diff --git a/api-spec/protobuf/ocean/v1/account.proto b/api-spec/protobuf/ocean/v1/account.proto index e43eeb1..47b5d99 100644 --- a/api-spec/protobuf/ocean/v1/account.proto +++ b/api-spec/protobuf/ocean/v1/account.proto @@ -156,4 +156,4 @@ message DeleteAccountRequest{ // Account namespace or label. string account_name = 1; } -message DeleteAccountResponse{} \ No newline at end of file +message DeleteAccountResponse{} diff --git a/api-spec/protobuf/ocean/v1/notification.proto b/api-spec/protobuf/ocean/v1/notification.proto index e363ac0..c923bcf 100644 --- a/api-spec/protobuf/ocean/v1/notification.proto +++ b/api-spec/protobuf/ocean/v1/notification.proto @@ -10,6 +10,15 @@ import "ocean/v1/types.proto"; // server-side stream or by subscribing webhooks invoked whenever an event // occurs. service NotificationService { + // WatchExternalScript allows to get notified about utxos/txs related to the given + // external script, ie. not derived from a wallet account. + // The service answers with the label assigned to the given script. + // The label is used as identifier of the utxos/txs received from the streams. + rpc WatchExternalScript(WatchExternalScriptRequest) returns(WatchExternalScriptResponse); + // UnwatchExternalScript allows to stop watching for the script identified with + // the given label. + rpc UnwatchExternalScript(UnwatchExternalScriptRequest) returns(UnwatchExternalScriptResponse); + //**************// // STREAMS // //**************// @@ -24,14 +33,29 @@ service NotificationService { //***************// // Adds a webhook registered for some kind of event. - rpc AddWebhook(AddWebhookRequest) returns(AddWebhookResponse){} + rpc AddWebhook(AddWebhookRequest) returns(AddWebhookResponse); // Removes some previously added webhook. - rpc RemoveWebhook(RemoveWebhookRequest) returns(RemoveWebhookResponse){} + rpc RemoveWebhook(RemoveWebhookRequest) returns(RemoveWebhookResponse); // Returns registered webhooks. - rpc ListWebhooks(ListWebhooksRequest) returns(ListWebhooksResponse){} + rpc ListWebhooks(ListWebhooksRequest) returns(ListWebhooksResponse); +} + +message WatchExternalScriptRequest { + // The script to watch. + string script = 1; + // Optional: the private blinding key in case the script locks confidential utxos to unblind. + string blinding_key = 2; +} +message WatchExternalScriptResponse { + string label = 1; +} + +message UnwatchExternalScriptRequest { + string label = 1; } +message UnwatchExternalScriptResponse {} message TransactionNotificationsRequest{} message TransactionNotificationsResponse{ diff --git a/internal/app-config/config.go b/internal/app-config/config.go index b15c3d0..eabbd92 100644 --- a/internal/app-config/config.go +++ b/internal/app-config/config.go @@ -267,7 +267,8 @@ func (c *AppConfig) notificationService() *application.NotificationService { } rm, _ := c.repoManager() - c.notifySvc = application.NewNotificationService(rm) + bcs, _ := c.bcScanner() + c.notifySvc = application.NewNotificationService(rm, bcs) return c.notifySvc } diff --git a/internal/core/application/account_service.go b/internal/core/application/account_service.go index 027b5f7..a621dc0 100644 --- a/internal/core/application/account_service.go +++ b/internal/core/application/account_service.go @@ -269,29 +269,6 @@ func (as *AccountService) registerHandlerForWalletEvents() { as.bcScanner.StopWatchForAccount(event.AccountName) }, ) - // Start watching for when utxos are spent as soon as they are added to the storage. - as.repoManager.RegisterHandlerForUtxoEvent( - domain.UtxoAdded, func(event domain.UtxoEvent) { - accountName := event.Utxos[0].AccountName - as.bcScanner.WatchForUtxos(accountName, event.Utxos) - }, - ) - - // In background, make sure to watch for all utxos to get notified when they are spent. - go func() { - utxos, err := as.repoManager.UtxoRepository().GetAllUtxos( - context.Background(), - ) - if err != nil { - as.warn(err, "account service: error while getting all utxos") - return - } - for _, u := range utxos { - if !u.IsSpent() { - as.bcScanner.WatchForUtxos(u.AccountName, []domain.UtxoInfo{u.Info()}) - } - } - }() } func (as *AccountService) listenToUtxoChannel( diff --git a/internal/core/application/notification_service.go b/internal/core/application/notification_service.go index f9d9825..103c843 100644 --- a/internal/core/application/notification_service.go +++ b/internal/core/application/notification_service.go @@ -2,7 +2,13 @@ package application import ( "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "sync" + "github.com/btcsuite/btcd/chaincfg/chainhash" + log "github.com/sirupsen/logrus" "github.com/vulpemventures/ocean/internal/core/domain" "github.com/vulpemventures/ocean/internal/core/ports" ) @@ -13,22 +19,192 @@ import ( // status of the internal wallet. type NotificationService struct { repoManager ports.RepoManager + bcScanner ports.BlockchainScanner + chUtxos chan domain.UtxoEvent + chTxs chan domain.TransactionEvent + utxoLock *sync.Mutex + txLock *sync.Mutex + + log func(format string, a ...interface{}) } func NewNotificationService( - repoManager ports.RepoManager, + repoManager ports.RepoManager, bcScanner ports.BlockchainScanner, ) *NotificationService { - return &NotificationService{repoManager} + logFn := func(format string, a ...interface{}) { + format = fmt.Sprintf("notification service: %s", format) + log.Debugf(format, a...) + } + chUtxos := make(chan domain.UtxoEvent) + chTxs := make(chan domain.TransactionEvent) + utxoLock := &sync.Mutex{} + txLock := &sync.Mutex{} + + svc := &NotificationService{ + repoManager, bcScanner, chUtxos, chTxs, utxoLock, txLock, logFn, + } + svc.registerHandlersForExternalScripts() + go svc.listenToInternalTxs() + go svc.listenToInternalUtxos() + + return svc } func (ns *NotificationService) GetTxChannel( ctx context.Context, ) (chan domain.TransactionEvent, error) { - return ns.repoManager.TransactionRepository().GetEventChannel(), nil + return ns.chTxs, nil } func (ns *NotificationService) GetUtxoChannel( ctx context.Context, ) (chan domain.UtxoEvent, error) { - return ns.repoManager.UtxoRepository().GetEventChannel(), nil + return ns.chUtxos, nil +} + +func (ns *NotificationService) WatchScript( + ctx context.Context, scriptHex, blindingKey string, +) (string, error) { + script, err := hex.DecodeString(scriptHex) + if err != nil { + return "", fmt.Errorf("invalid script: must be in hex format") + } + var key []byte + if len(blindingKey) > 0 { + buf, err := hex.DecodeString(blindingKey) + if err != nil { + return "", err + } + copy(key, buf) + } + + buf := sha256.Sum256(script) + hash, _ := chainhash.NewHash(buf[:]) + label := hash.String() + externalScript := domain.AddressInfo{ + Account: label, + Script: scriptHex, + BlindingKey: key, + } + + done, err := ns.repoManager.ExternalScriptRepository().AddScript(ctx, externalScript) + if err != nil { + return "", err + } + if done { + ns.log("added external script with label %s", label) + } + + return label, nil +} + +func (ns *NotificationService) StopWatchingScript( + ctx context.Context, label string, +) error { + done, err := ns.repoManager.ExternalScriptRepository().DeleteScript(ctx, label) + if err != nil { + return err + } + if done { + ns.log("removed script with label %s", label) + } + return nil +} + +func (ns *NotificationService) listenToInternalTxs() { + chTxs := ns.repoManager.TransactionRepository().GetEventChannel() + for event := range chTxs { + go ns.publishTx(event) + } +} + +func (ns *NotificationService) listenToInternalUtxos() { + chUtxos := ns.repoManager.UtxoRepository().GetEventChannel() + for event := range chUtxos { + go ns.publishUtxo(event) + } +} + +func (ns *NotificationService) publishUtxo(event domain.UtxoEvent) { + ns.utxoLock.Lock() + defer ns.utxoLock.Unlock() + + ns.chUtxos <- event +} + +func (ns *NotificationService) publishTx(event domain.TransactionEvent) { + ns.utxoLock.Lock() + defer ns.utxoLock.Unlock() + + ns.chTxs <- event +} + +func (ns *NotificationService) registerHandlersForExternalScripts() { + // Start watching external scripts as soon as they are persisted. + ns.repoManager.RegisterHandlerForExternalScriptEvent( + domain.ExternalScriptAdded, func(event domain.ExternalScriptEvent) { + ns.bcScanner.WatchForAccount(event.Info.Account, 0, []domain.AddressInfo{event.Info}) + chUtxos := ns.bcScanner.GetUtxoChannel(event.Info.Account) + chTxs := ns.bcScanner.GetTxChannel(event.Info.Account) + go ns.listenToUtxoChannel(event.Info.Account, chUtxos) + go ns.listenToTxChannel(event.Info.Account, chTxs) + }, + ) + + // Stop watching external scripts as soon as they are removed. + ns.repoManager.RegisterHandlerForExternalScriptEvent( + domain.ExternalScriptDeleted, func(event domain.ExternalScriptEvent) { + ns.bcScanner.StopWatchForAccount(event.Info.Account) + }, + ) + + // Start watching all registered external scripts at startup. + scripts, _ := ns.repoManager.ExternalScriptRepository().GetAllScripts( + context.Background(), + ) + for _, script := range scripts { + ns.bcScanner.WatchForAccount(script.Account, 0, []domain.AddressInfo{script}) + chUtxos := ns.bcScanner.GetUtxoChannel(script.Account) + chTxs := ns.bcScanner.GetTxChannel(script.Account) + go ns.listenToUtxoChannel(script.Account, chUtxos) + go ns.listenToTxChannel(script.Account, chTxs) + } +} + +func (ns *NotificationService) listenToUtxoChannel( + scriptHash string, chUtxos chan []*domain.Utxo, +) { + ns.log("start listening to utxo channel for script %s", scriptHash) + + for utxos := range chUtxos { + eventType := domain.UtxoAdded + if utxos[0].IsSpent() { + eventType = domain.UtxoSpent + } + utxoInfo := make([]domain.UtxoInfo, 0, len(utxos)) + for _, u := range utxos { + utxoInfo = append(utxoInfo, u.Info()) + } + go ns.publishUtxo(domain.UtxoEvent{ + EventType: eventType, + Utxos: utxoInfo, + }) + } +} + +func (ns *NotificationService) listenToTxChannel( + scriptHash string, chTxs chan *domain.Transaction, +) { + ns.log("start listening to tx channel for script %s", scriptHash) + + for tx := range chTxs { + eventType := domain.TransactionConfirmed + if !tx.IsConfirmed() { + eventType = domain.TransactionAdded + } + go ns.publishTx(domain.TransactionEvent{ + EventType: eventType, + Transaction: tx, + }) + } } diff --git a/internal/core/application/notification_service_test.go b/internal/core/application/notification_service_test.go index 9b29b77..370c0d7 100644 --- a/internal/core/application/notification_service_test.go +++ b/internal/core/application/notification_service_test.go @@ -31,7 +31,7 @@ func testGetUtxoChannel(t *testing.T) { require.NoError(t, err) require.NotNil(t, repoManager) - svc := application.NewNotificationService(repoManager) + svc := application.NewNotificationService(repoManager, nil) chEvents, err := svc.GetUtxoChannel(ctx) require.NoError(t, err) @@ -66,7 +66,7 @@ func testGetTxChannel(t *testing.T) { require.NoError(t, err) require.NotNil(t, repoManager) - svc := application.NewNotificationService(repoManager) + svc := application.NewNotificationService(repoManager, nil) chEvents, err := svc.GetTxChannel(ctx) require.NoError(t, err) diff --git a/internal/core/domain/external_script_repository.go b/internal/core/domain/external_script_repository.go new file mode 100644 index 0000000..7f78a69 --- /dev/null +++ b/internal/core/domain/external_script_repository.go @@ -0,0 +1,38 @@ +package domain + +import "context" + +const ( + ExternalScriptAdded ExternalScriptEventType = iota + ExternalScriptDeleted +) + +var ( + externalScriptTypeString = map[ExternalScriptEventType]string{ + ExternalScriptAdded: "ExternalScriptAdded", + ExternalScriptDeleted: "ExternalScriptDeleted", + } +) + +type ExternalScriptEventType int + +func (t ExternalScriptEventType) String() string { + return externalScriptTypeString[t] +} + +// ExternalScriptEvent holds info about an event occured within the repository. +type ExternalScriptEvent struct { + EventType ExternalScriptEventType + Info AddressInfo +} + +// ExternalScriptRepository is the abstraction for any kind of database intended to +// persist external scripts as AddressInfo. +type ExternalScriptRepository interface { + // AddScripts persists the given external script by preventing duplicates. + AddScript(ctx context.Context, script AddressInfo) (bool, error) + // GetAllScripts returns all the persisted external scripts. + GetAllScripts(ctx context.Context) ([]AddressInfo, error) + // DeleteScript removes an external script idenitified by its hash from the store. + DeleteScript(ctx context.Context, scriptHash string) (bool, error) +} diff --git a/internal/core/ports/repo_manager.go b/internal/core/ports/repo_manager.go index 6429d6c..909e51e 100644 --- a/internal/core/ports/repo_manager.go +++ b/internal/core/ports/repo_manager.go @@ -7,16 +7,19 @@ import ( type WalletEventHandler func(event domain.WalletEvent) type UtxoEventHandler func(event domain.UtxoEvent) type TxEventHandler func(event domain.TransactionEvent) +type ScriptEventHandler func(event domain.ExternalScriptEvent) // RepoManager is the abstraction for any kind of service intended to manage // domain repositories implementations of the same concrete type. type RepoManager interface { - // WalletRepository returns the concrete implentation as domain interface. + // WalletRepository returns the wallet repository. WalletRepository() domain.WalletRepository - // UtxoRepository returns the concrete implentation as domain interface. + // UtxoRepository returns the utxo repository. UtxoRepository() domain.UtxoRepository - // TransactionRepository returns the concrete implentation as domain interface. + // TransactionRepository returns the tx repository. TransactionRepository() domain.TransactionRepository + // ExternalScriptRepository returns the external scripts repository. + ExternalScriptRepository() domain.ExternalScriptRepository // RegisterHandlerForWalletEvent registers an handler function, executed // whenever the given event type occurs. @@ -33,6 +36,11 @@ type RepoManager interface { RegisterHandlerForTxEvent( eventType domain.TransactionEventType, handler TxEventHandler, ) + // RegisterHandlerForExternalScriptEvent registers an handler function, + // executed whenever the given event type occurs. + RegisterHandlerForExternalScriptEvent( + eventType domain.ExternalScriptEventType, handler ScriptEventHandler, + ) // Reset brings all the repos to their initial state by deleting any persisted data. Reset() diff --git a/internal/infrastructure/blockchain-scanner/electrum/tcp_client.go b/internal/infrastructure/blockchain-scanner/electrum/tcp_client.go index 7cd615d..6f88571 100644 --- a/internal/infrastructure/blockchain-scanner/electrum/tcp_client.go +++ b/internal/infrastructure/blockchain-scanner/electrum/tcp_client.go @@ -97,8 +97,10 @@ func (c *tcpClient) listen() { case "blockchain.scripthash.subscribe": scriptHash := resp.Params.([]interface{})[0].(string) account := c.chHandler.getAccountByScriptHash(scriptHash) - report := accountReport{account, scriptHash} - c.reportHandlers[account].sendReport(report) + if len(account) > 0 { + report := accountReport{account, scriptHash} + c.reportHandlers[account].sendReport(report) + } continue case "blockchain.headers.subscribe": buf, _ := json.Marshal(resp.Params.([]interface{})[0]) @@ -208,10 +210,15 @@ func (c *tcpClient) subscribeForAccount( for _, info := range addresses { scriptHashes = append(scriptHashes, calcScriptHash(info.Script)) - c.log( - "start watching address %s for account %s", - info.DerivationPath, accountName, + str := fmt.Sprintf( + "start watching address %s for account %s", info.DerivationPath, accountName, ) + if len(info.DerivationPath) <= 0 { + str = fmt.Sprintf( + "start watching script %s with label %s", info.Script, info.Account, + ) + } + c.log(str) } if err := c.subscribeForScripts(accountName, scriptHashes); err != nil { diff --git a/internal/infrastructure/blockchain-scanner/electrum/types.go b/internal/infrastructure/blockchain-scanner/electrum/types.go index 5328cc4..8f9af5e 100644 --- a/internal/infrastructure/blockchain-scanner/electrum/types.go +++ b/internal/infrastructure/blockchain-scanner/electrum/types.go @@ -180,6 +180,10 @@ func (h *chHandler) clearAccount(account string) { close(chReports) } delete(h.chReportsByAccount, account) + scripts := h.scriptsByAccount[account] + for _, script := range scripts { + delete(h.accountByScript, script) + } delete(h.scriptsByAccount, account) } diff --git a/internal/infrastructure/blockchain-scanner/electrum/ws_client.go b/internal/infrastructure/blockchain-scanner/electrum/ws_client.go index 7d4dabf..88b7730 100644 --- a/internal/infrastructure/blockchain-scanner/electrum/ws_client.go +++ b/internal/infrastructure/blockchain-scanner/electrum/ws_client.go @@ -103,8 +103,10 @@ func (c *wsClient) listen() { case "blockchain.scripthash.subscribe": scriptHash := resp.Params.([]interface{})[0].(string) account := c.chHandler.getAccountByScriptHash(scriptHash) - report := accountReport{account, scriptHash} - c.reportHandlers[account].sendReport(report) + if len(account) > 0 { + report := accountReport{account, scriptHash} + c.reportHandlers[account].sendReport(report) + } continue case "blockchain.headers.subscribe": buf, _ := json.Marshal(resp.Params.([]interface{})[0]) @@ -180,10 +182,15 @@ func (c *wsClient) subscribeForAccount( for _, info := range addresses { scriptHashes = append(scriptHashes, calcScriptHash(info.Script)) - c.log( - "start watching address %s for account %s", - info.DerivationPath, accountName, + str := fmt.Sprintf( + "start watching address %s for account %s", info.DerivationPath, accountName, ) + if len(info.DerivationPath) <= 0 { + str = fmt.Sprintf( + "start watching script %s with label %s", info.Script, info.Account, + ) + } + c.log(str) } if err := c.subscribeForScripts(accountName, scriptHashes); err != nil { diff --git a/internal/infrastructure/storage/db/badger/repo_manager.go b/internal/infrastructure/storage/db/badger/repo_manager.go index b25c2a8..97b7cf4 100644 --- a/internal/infrastructure/storage/db/badger/repo_manager.go +++ b/internal/infrastructure/storage/db/badger/repo_manager.go @@ -20,10 +20,12 @@ type repoManager struct { utxoRepository *utxoRepository walletRepository *walletRepository txRepository *transactionRepository + scriptRepository *scriptRepository walletEventHandlers *handlerMap utxoEventHandlers *handlerMap txEventHandlers *handlerMap + scriptEventHandlers *handlerMap } // NewRepoManager is the factory for creating a new badger implementation @@ -32,11 +34,12 @@ type repoManager struct { // is provided - to be used only for testing purposes), and opening and closing // the connection to them. func NewRepoManager(baseDbDir string, logger badger.Logger) (ports.RepoManager, error) { - var walletdbDir, utxoDir, txDir string + var walletdbDir, utxoDir, txDir, scriptDir string if len(baseDbDir) > 0 { walletdbDir = filepath.Join(baseDbDir, "wallet") utxoDir = filepath.Join(baseDbDir, "utxos") txDir = filepath.Join(baseDbDir, "txs") + scriptDir = filepath.Join(baseDbDir, "scripts") } walletDb, err := createDb(walletdbDir, logger) @@ -53,23 +56,31 @@ func NewRepoManager(baseDbDir string, logger badger.Logger) (ports.RepoManager, if err != nil { return nil, fmt.Errorf("opening tx db: %w", err) } + scriptDb, err := createDb(scriptDir, logger) + if err != nil { + return nil, fmt.Errorf("opening external scripts db: %w", err) + } utxoRepo := newUtxoRepository(utxoDb) walletRepo := newWalletRepository(walletDb) txRepo := newTransactionRepository(txDb) + scriptRepo := newExternalScriptRepository(scriptDb) rm := &repoManager{ utxoRepository: utxoRepo, walletRepository: walletRepo, txRepository: txRepo, - walletEventHandlers: newHandlerMap(), //make(map[domain.WalletEventType]ports.WalletEventHandler), - utxoEventHandlers: newHandlerMap(), // make(map[domain.UtxoEventType]ports.UtxoEventHandler), - txEventHandlers: newHandlerMap(), // make(map[domain.TransactionEventType]ports.TxEventHandler), + scriptRepository: scriptRepo, + walletEventHandlers: newHandlerMap(), + utxoEventHandlers: newHandlerMap(), + txEventHandlers: newHandlerMap(), + scriptEventHandlers: newHandlerMap(), } go rm.listenToWalletEvents() go rm.listenToUtxoEvents() go rm.listenToTxEvents() + go rm.listenToScriptEvents() return rm, nil } @@ -86,6 +97,10 @@ func (d *repoManager) TransactionRepository() domain.TransactionRepository { return d.txRepository } +func (d *repoManager) ExternalScriptRepository() domain.ExternalScriptRepository { + return d.scriptRepository +} + func (rm *repoManager) RegisterHandlerForWalletEvent( eventType domain.WalletEventType, handler ports.WalletEventHandler, ) { @@ -104,16 +119,24 @@ func (rm *repoManager) RegisterHandlerForTxEvent( rm.txEventHandlers.set(int(eventType), handler) } +func (rm *repoManager) RegisterHandlerForExternalScriptEvent( + eventType domain.ExternalScriptEventType, handler ports.ScriptEventHandler, +) { + rm.scriptEventHandlers.set(int(eventType), handler) +} + func (d *repoManager) Reset() { d.walletRepository.reset() d.utxoRepository.reset() d.txRepository.reset() + d.scriptRepository.reset() } func (d *repoManager) Close() { d.walletRepository.close() d.utxoRepository.close() d.txRepository.close() + d.scriptRepository.close() } func (rm *repoManager) listenToWalletEvents() { @@ -149,6 +172,17 @@ func (rm *repoManager) listenToTxEvents() { } } +func (rm *repoManager) listenToScriptEvents() { + for event := range rm.scriptRepository.chEvents { + if handlers, ok := rm.scriptEventHandlers.get(int(event.EventType)); ok { + for i := range handlers { + handler := handlers[i] + go handler.(ports.ScriptEventHandler)(event) + } + } + } +} + func createDb(dbDir string, logger badger.Logger) (*badgerhold.Store, error) { isInMemory := len(dbDir) <= 0 diff --git a/internal/infrastructure/storage/db/badger/script_repository.go b/internal/infrastructure/storage/db/badger/script_repository.go new file mode 100644 index 0000000..5416957 --- /dev/null +++ b/internal/infrastructure/storage/db/badger/script_repository.go @@ -0,0 +1,158 @@ +package dbbadger + +import ( + "context" + "fmt" + "sync" + + "github.com/dgraph-io/badger/v3" + log "github.com/sirupsen/logrus" + "github.com/timshannon/badgerhold/v4" + "github.com/vulpemventures/ocean/internal/core/domain" +) + +type scriptRepository struct { + store *badgerhold.Store + chEvents chan domain.ExternalScriptEvent + lock *sync.Mutex + + log func(format string, a ...interface{}) +} + +func NewExternalScriptRepository( + store *badgerhold.Store, +) domain.ExternalScriptRepository { + return newExternalScriptRepository(store) +} + +func newExternalScriptRepository( + store *badgerhold.Store, +) *scriptRepository { + chEvents := make(chan domain.ExternalScriptEvent) + lock := &sync.Mutex{} + logFn := func(format string, a ...interface{}) { + format = fmt.Sprintf("script repository: %s", format) + log.Debugf(format, a...) + } + return &scriptRepository{store, chEvents, lock, logFn} +} + +func (r *scriptRepository) AddScript( + ctx context.Context, info domain.AddressInfo, +) (bool, error) { + done, err := r.addScript(ctx, info) + if err != nil { + return false, err + } + + if done { + go r.publishEvent(domain.ExternalScriptEvent{ + EventType: domain.ExternalScriptAdded, + Info: info, + }) + } + + return done, nil +} + +func (r *scriptRepository) GetAllScripts( + ctx context.Context, +) ([]domain.AddressInfo, error) { + query := &badgerhold.Query{} + return r.findScripts(ctx, query) +} + +func (r *scriptRepository) DeleteScript( + ctx context.Context, scriptHash string, +) (bool, error) { + done, err := r.deleteScript(ctx, scriptHash) + if err != nil { + return false, err + } + if done { + go r.publishEvent(domain.ExternalScriptEvent{ + EventType: domain.ExternalScriptDeleted, + Info: domain.AddressInfo{ + Account: scriptHash, + }, + }) + } + + return done, nil +} + +func (r *scriptRepository) addScript( + ctx context.Context, info domain.AddressInfo, +) (bool, error) { + var err error + if ctx.Value("tx") != nil { + t := ctx.Value("tx").(*badger.Txn) + err = r.store.TxInsert(t, info.Account, info) + } else { + err = r.store.Insert(info.Account, info) + } + + if err != nil { + if err == badgerhold.ErrKeyExists { + return false, nil + } + return false, err + } + + return true, nil +} + +func (r *scriptRepository) findScripts( + ctx context.Context, query *badgerhold.Query, +) ([]domain.AddressInfo, error) { + var list []domain.AddressInfo + var err error + if ctx.Value("tx") != nil { + tx := ctx.Value("tx").(*badger.Txn) + err = r.store.TxFind(tx, &list, query) + } else { + err = r.store.Find(&list, query) + } + if err != nil { + if err == badgerhold.ErrNotFound { + return nil, nil + } + return nil, err + } + return list, nil +} + +func (r *scriptRepository) deleteScript( + ctx context.Context, scriptHash string, +) (bool, error) { + var err error + if ctx.Value("tx") != nil { + tx := ctx.Value("tx").(*badger.Txn) + err = r.store.TxDelete(tx, scriptHash, domain.AddressInfo{}) + } else { + err = r.store.Delete(scriptHash, domain.AddressInfo{}) + } + if err != nil { + if err == badgerhold.ErrNotFound { + return false, nil + } + return false, err + } + return true, nil +} + +func (r *scriptRepository) publishEvent(event domain.ExternalScriptEvent) { + r.lock.Lock() + defer r.lock.Unlock() + + r.log("publish event %s", event.EventType) + r.chEvents <- event +} + +func (r *scriptRepository) reset() { + r.store.Badger().DropAll() +} + +func (r *scriptRepository) close() { + r.store.Close() +} diff --git a/internal/infrastructure/storage/db/inmemory/repo_manager.go b/internal/infrastructure/storage/db/inmemory/repo_manager.go index 3093ea7..26a6eec 100644 --- a/internal/infrastructure/storage/db/inmemory/repo_manager.go +++ b/internal/infrastructure/storage/db/inmemory/repo_manager.go @@ -12,29 +12,35 @@ type repoManager struct { utxoRepository *utxoRepository walletRepository *walletRepository txRepository *txRepository + scriptRepository *scriptRepository walletEventHandlers *handlerMap utxoEventHandlers *handlerMap txEventHandlers *handlerMap + scriptEventHandlers *handlerMap } func NewRepoManager() ports.RepoManager { utxoRepo := newUtxoRepository() walletRepo := newWalletRepository() txRepo := newTransactionRepository() + scriptRepo := newExternalScriptRepository() rm := &repoManager{ utxoRepository: utxoRepo, walletRepository: walletRepo, txRepository: txRepo, + scriptRepository: scriptRepo, walletEventHandlers: newHandlerMap(), utxoEventHandlers: newHandlerMap(), txEventHandlers: newHandlerMap(), + scriptEventHandlers: newHandlerMap(), } go rm.listenToWalletEvents() go rm.listenToUtxoEvents() go rm.listenToTxEvents() + go rm.listenToScriptEvents() return rm } @@ -51,6 +57,10 @@ func (rm *repoManager) TransactionRepository() domain.TransactionRepository { return rm.txRepository } +func (rm *repoManager) ExternalScriptRepository() domain.ExternalScriptRepository { + return rm.scriptRepository +} + func (rm *repoManager) RegisterHandlerForWalletEvent( eventType domain.WalletEventType, handler ports.WalletEventHandler, ) { @@ -69,10 +79,17 @@ func (rm *repoManager) RegisterHandlerForTxEvent( rm.txEventHandlers.set(int(eventType), handler) } +func (rm *repoManager) RegisterHandlerForExternalScriptEvent( + eventType domain.ExternalScriptEventType, handler ports.ScriptEventHandler, +) { + rm.scriptEventHandlers.set(int(eventType), handler) +} + func (rm *repoManager) Reset() { rm.walletRepository.reset() rm.utxoRepository.reset() rm.txRepository.reset() + rm.scriptRepository.reset() } func (rm *repoManager) listenToWalletEvents() { @@ -114,10 +131,24 @@ func (rm *repoManager) listenToTxEvents() { } } +func (rm *repoManager) listenToScriptEvents() { + for event := range rm.scriptRepository.chEvents { + time.Sleep(time.Millisecond) + + if handlers, ok := rm.scriptEventHandlers.get(int(event.EventType)); ok { + for i := range handlers { + handler := handlers[i] + go handler.(ports.ScriptEventHandler)(event) + } + } + } +} + func (rm *repoManager) Close() { rm.walletRepository.close() rm.utxoRepository.close() rm.txRepository.close() + rm.scriptRepository.close() } // handlerMap is a util type to prevent race conditions when registering diff --git a/internal/infrastructure/storage/db/inmemory/script_repository.go b/internal/infrastructure/storage/db/inmemory/script_repository.go new file mode 100644 index 0000000..5c3f01f --- /dev/null +++ b/internal/infrastructure/storage/db/inmemory/script_repository.go @@ -0,0 +1,137 @@ +package inmemory + +import ( + "context" + "sync" + + "github.com/vulpemventures/ocean/internal/core/domain" +) + +type scriptInmemoryStore struct { + scripts map[string]domain.AddressInfo + lock *sync.RWMutex +} + +type scriptRepository struct { + store *scriptInmemoryStore + chEvents chan domain.ExternalScriptEvent + chLock *sync.Mutex +} + +func NewExternalScriptRepository() domain.ExternalScriptRepository { + return newExternalScriptRepository() +} + +func newExternalScriptRepository() *scriptRepository { + return &scriptRepository{ + store: &scriptInmemoryStore{ + scripts: make(map[string]domain.AddressInfo), + lock: &sync.RWMutex{}, + }, + chEvents: make(chan domain.ExternalScriptEvent), + chLock: &sync.Mutex{}, + } +} + +func (r *scriptRepository) AddScript( + ctx context.Context, info domain.AddressInfo, +) (bool, error) { + r.store.lock.Lock() + defer r.store.lock.Unlock() + + done, err := r.addScript(ctx, info) + if err != nil { + return false, err + } + + if done { + go r.publishEvent(domain.ExternalScriptEvent{ + EventType: domain.ExternalScriptAdded, + Info: info, + }) + } + + return done, nil +} + +func (r *scriptRepository) GetAllScripts( + ctx context.Context, +) ([]domain.AddressInfo, error) { + r.store.lock.RLock() + defer r.store.lock.RUnlock() + + return r.getScripts(ctx) +} + +func (r *scriptRepository) DeleteScript( + ctx context.Context, scriptHash string, +) (bool, error) { + r.store.lock.Lock() + defer r.store.lock.Unlock() + + done, err := r.deleteScript(ctx, scriptHash) + if err != nil { + return false, err + } + + if done { + go r.publishEvent(domain.ExternalScriptEvent{ + EventType: domain.ExternalScriptAdded, + Info: domain.AddressInfo{ + Account: scriptHash, + }, + }) + } + + return done, nil +} + +func (r *scriptRepository) addScript( + _ context.Context, info domain.AddressInfo, +) (bool, error) { + if _, ok := r.store.scripts[info.Account]; ok { + return false, nil + } + + r.store.scripts[info.Account] = info + + return true, nil +} + +func (r *scriptRepository) getScripts( + _ context.Context, +) ([]domain.AddressInfo, error) { + scripts := make([]domain.AddressInfo, 0, len(r.store.scripts)) + for _, info := range r.store.scripts { + scripts = append(scripts, info) + } + return scripts, nil +} + +func (r *scriptRepository) deleteScript( + _ context.Context, scriptHash string, +) (bool, error) { + if _, ok := r.store.scripts[scriptHash]; !ok { + return false, nil + } + + delete(r.store.scripts, scriptHash) + + return true, nil +} + +func (r *scriptRepository) publishEvent(event domain.ExternalScriptEvent) { + r.chLock.Lock() + defer r.chLock.Unlock() + + r.chEvents <- event +} + +func (r *scriptRepository) reset() { + r.store.lock.Lock() + defer r.store.lock.Unlock() + + r.store.scripts = make(map[string]domain.AddressInfo) +} + +func (r *scriptRepository) close() {} diff --git a/internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.down.sql b/internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.down.sql new file mode 100644 index 0000000..1c139c2 --- /dev/null +++ b/internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS external_script; \ No newline at end of file diff --git a/internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.up.sql b/internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.up.sql new file mode 100644 index 0000000..bd74ef2 --- /dev/null +++ b/internal/infrastructure/storage/db/postgres/migration/20240103141047_add_script_table.up.sql @@ -0,0 +1,5 @@ +CREATE TABLE external_script ( + account varchar(32) NOT NULL PRIMARY KEY, + script varchar(255) NOT NULL, + blinding_key bytea +); \ No newline at end of file diff --git a/internal/infrastructure/storage/db/postgres/repo_manager.go b/internal/infrastructure/storage/db/postgres/repo_manager.go index 6ce7cef..7850f6b 100644 --- a/internal/infrastructure/storage/db/postgres/repo_manager.go +++ b/internal/infrastructure/storage/db/postgres/repo_manager.go @@ -28,10 +28,12 @@ type repoManager struct { utxoRepository *utxoRepositoryPg walletRepository *walletRepositoryPg txRepository *txRepositoryPg + scriptRepository *scriptRepositoryPg walletEventHandlers *handlerMap utxoEventHandlers *handlerMap txEventHandlers *handlerMap + scriptEventHandlers *handlerMap } func NewRepoManager(dbConfig DbConfig) (ports.RepoManager, error) { @@ -49,20 +51,24 @@ func NewRepoManager(dbConfig DbConfig) (ports.RepoManager, error) { utxoRepository := newUtxoRepositoryPgImpl(pgxPool) walletRepository := newWalletRepositoryPgImpl(pgxPool) txRepository := newTxRepositoryPgImpl(pgxPool) + scriptRepository := newExternalScriptRepositoryPgImpl(pgxPool) rm := &repoManager{ pgxPool: pgxPool, utxoRepository: utxoRepository, walletRepository: walletRepository, txRepository: txRepository, + scriptRepository: scriptRepository, walletEventHandlers: newHandlerMap(), utxoEventHandlers: newHandlerMap(), txEventHandlers: newHandlerMap(), + scriptEventHandlers: newHandlerMap(), } go rm.listenToWalletEvents() go rm.listenToUtxoEvents() go rm.listenToTxEvents() + go rm.listenToScriptEvents() return rm, nil } @@ -88,6 +94,10 @@ func (rm *repoManager) TransactionRepository() domain.TransactionRepository { return rm.txRepository } +func (rm *repoManager) ExternalScriptRepository() domain.ExternalScriptRepository { + return rm.scriptRepository +} + func (rm *repoManager) RegisterHandlerForWalletEvent( eventType domain.WalletEventType, handler ports.WalletEventHandler, ) { @@ -106,6 +116,12 @@ func (rm *repoManager) RegisterHandlerForTxEvent( rm.txEventHandlers.set(int(eventType), handler) } +func (rm *repoManager) RegisterHandlerForExternalScriptEvent( + eventType domain.ExternalScriptEventType, handler ports.ScriptEventHandler, +) { + rm.scriptEventHandlers.set(int(eventType), handler) +} + func (rm *repoManager) listenToWalletEvents() { for event := range rm.walletRepository.chEvents { time.Sleep(time.Millisecond) @@ -145,6 +161,19 @@ func (rm *repoManager) listenToTxEvents() { } } +func (rm *repoManager) listenToScriptEvents() { + for event := range rm.scriptRepository.chEvents { + time.Sleep(time.Millisecond) + + if handlers, ok := rm.scriptEventHandlers.get(int(event.EventType)); ok { + for i := range handlers { + handler := handlers[i] + go handler.(ports.ScriptEventHandler)(event) + } + } + } +} + func (rm *repoManager) Reset() { ctx := context.Background() conn, err := rm.pgxPool.Acquire(ctx) @@ -164,6 +193,7 @@ func (rm *repoManager) Reset() { rm.walletRepository.reset(querier, ctx) rm.utxoRepository.reset(querier, ctx) rm.txRepository.reset(querier, ctx) + rm.scriptRepository.reset(querier, ctx) tx.Commit(ctx) } @@ -172,6 +202,7 @@ func (rm *repoManager) Close() { rm.utxoRepository.close() rm.txRepository.close() rm.walletRepository.close() + rm.scriptRepository.close() rm.pgxPool.Close() } diff --git a/internal/infrastructure/storage/db/postgres/script_repository.go b/internal/infrastructure/storage/db/postgres/script_repository.go new file mode 100644 index 0000000..5f068c4 --- /dev/null +++ b/internal/infrastructure/storage/db/postgres/script_repository.go @@ -0,0 +1,118 @@ +package postgresdb + +import ( + "context" + "sync" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/vulpemventures/ocean/internal/core/domain" + "github.com/vulpemventures/ocean/internal/infrastructure/storage/db/postgres/sqlc/queries" +) + +type scriptRepositoryPg struct { + pgxPool *pgxpool.Pool + querier *queries.Queries + chLock *sync.Mutex + chEvents chan domain.ExternalScriptEvent +} + +func NewExternalScriptRepositoryPgImpl(pgxPool *pgxpool.Pool) domain.ExternalScriptRepository { + return newExternalScriptRepositoryPgImpl(pgxPool) +} + +func newExternalScriptRepositoryPgImpl(pgxPool *pgxpool.Pool) *scriptRepositoryPg { + return &scriptRepositoryPg{ + pgxPool: pgxPool, + querier: queries.New(pgxPool), + chLock: &sync.Mutex{}, + chEvents: make(chan domain.ExternalScriptEvent), + } +} + +func (r *scriptRepositoryPg) AddScript( + ctx context.Context, info domain.AddressInfo, +) (bool, error) { + if err := r.querier.InsertScript(ctx, queries.InsertScriptParams{ + Account: info.Account, + Script: info.Script, + BlindingKey: info.BlindingKey, + }); err != nil { + if pqErr, ok := err.(*pgconn.PgError); pqErr != nil && ok && pqErr.Code == uniqueViolation { + return false, nil + } else { + return false, err + } + } + + go r.publishEvent(domain.ExternalScriptEvent{ + EventType: domain.ExternalScriptAdded, + Info: info, + }) + + return true, nil +} + +func (r *scriptRepositoryPg) GetAllScripts( + ctx context.Context, +) ([]domain.AddressInfo, error) { + return r.getScripts(ctx) +} + +func (r *scriptRepositoryPg) DeleteScript( + ctx context.Context, scriptHash string, +) (bool, error) { + _, err := r.querier.GetScript(ctx, scriptHash) + if err != nil { + if err.Error() == pgxNoRows { + return false, nil + } + return false, err + } + + if err := r.querier.DeleteScript(ctx, scriptHash); err != nil { + return false, err + } + + go r.publishEvent(domain.ExternalScriptEvent{ + EventType: domain.ExternalScriptDeleted, + Info: domain.AddressInfo{Account: scriptHash}, + }) + + return true, nil +} + +func (r *scriptRepositoryPg) publishEvent(event domain.ExternalScriptEvent) { + r.chLock.Lock() + defer r.chLock.Unlock() + + r.chEvents <- event +} + +func (r *scriptRepositoryPg) close() {} + +func (r *scriptRepositoryPg) getScripts( + ctx context.Context, +) ([]domain.AddressInfo, error) { + rows, err := r.querier.GetAllScripts(ctx) + if err != nil { + return nil, err + } + + scripts := make([]domain.AddressInfo, 0, len(rows)) + for _, r := range rows { + scripts = append(scripts, domain.AddressInfo{ + Account: r.Account, + Script: r.Script, + BlindingKey: r.BlindingKey, + }) + } + + return scripts, nil +} + +func (r *scriptRepositoryPg) reset( + querier *queries.Queries, ctx context.Context, +) { + querier.ResetScripts(ctx) +} diff --git a/internal/infrastructure/storage/db/postgres/sqlc/queries/copyfrom.go b/internal/infrastructure/storage/db/postgres/sqlc/queries/copyfrom.go index 8bde823..983d969 100644 --- a/internal/infrastructure/storage/db/postgres/sqlc/queries/copyfrom.go +++ b/internal/infrastructure/storage/db/postgres/sqlc/queries/copyfrom.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 // source: copyfrom.go package queries diff --git a/internal/infrastructure/storage/db/postgres/sqlc/queries/db.go b/internal/infrastructure/storage/db/postgres/sqlc/queries/db.go index 6694141..e5683cf 100644 --- a/internal/infrastructure/storage/db/postgres/sqlc/queries/db.go +++ b/internal/infrastructure/storage/db/postgres/sqlc/queries/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 package queries diff --git a/internal/infrastructure/storage/db/postgres/sqlc/queries/models.go b/internal/infrastructure/storage/db/postgres/sqlc/queries/models.go index dc93ebe..ee58bd6 100644 --- a/internal/infrastructure/storage/db/postgres/sqlc/queries/models.go +++ b/internal/infrastructure/storage/db/postgres/sqlc/queries/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 package queries @@ -17,6 +17,7 @@ type Account struct { NextExternalIndex int32 NextInternalIndex int32 FkWalletID string + Unconf sql.NullBool } type AccountScriptInfo struct { @@ -25,6 +26,12 @@ type AccountScriptInfo struct { FkAccountName string } +type ExternalScript struct { + Account string + Script string + BlindingKey []byte +} + type Transaction struct { TxID string TxHex string diff --git a/internal/infrastructure/storage/db/postgres/sqlc/queries/query.sql.go b/internal/infrastructure/storage/db/postgres/sqlc/queries/query.sql.go index 77cd4b9..c742d5a 100644 --- a/internal/infrastructure/storage/db/postgres/sqlc/queries/query.sql.go +++ b/internal/infrastructure/storage/db/postgres/sqlc/queries/query.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 // source: query.sql package queries @@ -28,6 +28,15 @@ func (q *Queries) DeleteAccountScripts(ctx context.Context, fkAccountName string return err } +const deleteScript = `-- name: DeleteScript :exec +DELETE FROM external_script WHERE account = $1 +` + +func (q *Queries) DeleteScript(ctx context.Context, account string) error { + _, err := q.db.Exec(ctx, deleteScript, account) + return err +} + const deleteTransactionInputAccounts = `-- name: DeleteTransactionInputAccounts :exec DELETE FROM tx_input_account WHERE fk_tx_id=$1 ` @@ -56,7 +65,7 @@ func (q *Queries) DeleteUtxosForAccountName(ctx context.Context, accountName str } const getAccount = `-- name: GetAccount :one -SELECT namespace, index, label, xpub, derivation_path, next_external_index, next_internal_index, fk_wallet_id FROM account WHERE namespace = $1 OR label = $1 +SELECT namespace, index, label, xpub, derivation_path, next_external_index, next_internal_index, fk_wallet_id, unconf FROM account WHERE namespace = $1 OR label = $1 ` func (q *Queries) GetAccount(ctx context.Context, namespace string) (Account, error) { @@ -71,10 +80,35 @@ func (q *Queries) GetAccount(ctx context.Context, namespace string) (Account, er &i.NextExternalIndex, &i.NextInternalIndex, &i.FkWalletID, + &i.Unconf, ) return i, err } +const getAllScripts = `-- name: GetAllScripts :many +SELECT account, script, blinding_key FROM external_script +` + +func (q *Queries) GetAllScripts(ctx context.Context) ([]ExternalScript, error) { + rows, err := q.db.Query(ctx, getAllScripts) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ExternalScript + for rows.Next() { + var i ExternalScript + if err := rows.Scan(&i.Account, &i.Script, &i.BlindingKey); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getAllUtxos = `-- name: GetAllUtxos :many SELECT u.id, tx_id, vout, value, asset, value_commitment, asset_commitment, value_blinder, asset_blinder, script, nonce, range_proof, surjection_proof, account_name, lock_timestamp, lock_expiry_timestamp, us.id, block_height, block_time, block_hash, status, fk_utxo_id FROM utxo u left join utxo_status us on u.id = us.fk_utxo_id ` @@ -147,6 +181,17 @@ func (q *Queries) GetAllUtxos(ctx context.Context) ([]GetAllUtxosRow, error) { return items, nil } +const getScript = `-- name: GetScript :one +SELECT account, script, blinding_key FROM external_script WHERE account = $1 +` + +func (q *Queries) GetScript(ctx context.Context, account string) (ExternalScript, error) { + row := q.db.QueryRow(ctx, getScript, account) + var i ExternalScript + err := row.Scan(&i.Account, &i.Script, &i.BlindingKey) + return i, err +} + const getTransaction = `-- name: GetTransaction :many SELECT tx_id, tx_hex, block_hash, block_height, id, account_name, fk_tx_id FROM transaction t left join tx_input_account tia on t.tx_id = tia.fk_tx_id WHERE tx_id=$1 ` @@ -450,7 +495,7 @@ func (q *Queries) GetWalletAccountsAndScripts(ctx context.Context, id string) ([ const insertAccount = `-- name: InsertAccount :one INSERT INTO account(namespace,label,index,xpub,derivation_path,next_external_index,next_internal_index,fk_wallet_id) -VALUES($1,$2,$3,$4,$5,$6,$7,$8) RETURNING namespace, index, label, xpub, derivation_path, next_external_index, next_internal_index, fk_wallet_id +VALUES($1,$2,$3,$4,$5,$6,$7,$8) RETURNING namespace, index, label, xpub, derivation_path, next_external_index, next_internal_index, fk_wallet_id, unconf ` type InsertAccountParams struct { @@ -485,6 +530,7 @@ func (q *Queries) InsertAccount(ctx context.Context, arg InsertAccountParams) (A &i.NextExternalIndex, &i.NextInternalIndex, &i.FkWalletID, + &i.Unconf, ) return i, err } @@ -495,6 +541,22 @@ type InsertAccountScriptsParams struct { FkAccountName string } +const insertScript = `-- name: InsertScript :exec +INSERT INTO external_script(account,script,blinding_key) VALUES($1,$2,$3) +` + +type InsertScriptParams struct { + Account string + Script string + BlindingKey []byte +} + +// EXTERNAL SCRIPT +func (q *Queries) InsertScript(ctx context.Context, arg InsertScriptParams) error { + _, err := q.db.Exec(ctx, insertScript, arg.Account, arg.Script, arg.BlindingKey) + return err +} + const insertTransaction = `-- name: InsertTransaction :one INSERT INTO transaction(tx_id,tx_hex,block_hash,block_height) VALUES($1,$2,$3,$4) RETURNING tx_id, tx_hex, block_hash, block_height @@ -678,6 +740,15 @@ func (q *Queries) InsertWallet(ctx context.Context, arg InsertWalletParams) (Wal return i, err } +const resetScripts = `-- name: ResetScripts :exec +DELETE FROM external_script +` + +func (q *Queries) ResetScripts(ctx context.Context) error { + _, err := q.db.Exec(ctx, resetScripts) + return err +} + const resetTransactions = `-- name: ResetTransactions :exec DELETE FROM transaction ` @@ -706,7 +777,7 @@ func (q *Queries) ResetWallet(ctx context.Context) error { } const updateAccount = `-- name: UpdateAccount :one -UPDATE account SET next_external_index = $1, next_internal_index = $2, label = $3 WHERE namespace = $4 RETURNING namespace, index, label, xpub, derivation_path, next_external_index, next_internal_index, fk_wallet_id +UPDATE account SET next_external_index = $1, next_internal_index = $2, label = $3 WHERE namespace = $4 RETURNING namespace, index, label, xpub, derivation_path, next_external_index, next_internal_index, fk_wallet_id, unconf ` type UpdateAccountParams struct { @@ -733,6 +804,7 @@ func (q *Queries) UpdateAccount(ctx context.Context, arg UpdateAccountParams) (A &i.NextExternalIndex, &i.NextInternalIndex, &i.FkWalletID, + &i.Unconf, ) return i, err } diff --git a/internal/infrastructure/storage/db/postgres/sqlc/query.sql b/internal/infrastructure/storage/db/postgres/sqlc/query.sql index 34c1b07..1ad156f 100644 --- a/internal/infrastructure/storage/db/postgres/sqlc/query.sql +++ b/internal/infrastructure/storage/db/postgres/sqlc/query.sql @@ -81,6 +81,19 @@ DELETE FROM tx_input_account WHERE fk_tx_id=$1; -- name: GetTransaction :many SELECT * FROM transaction t left join tx_input_account tia on t.tx_id = tia.fk_tx_id WHERE tx_id=$1; +/* EXTERNAL SCRIPT */ +-- name: InsertScript :exec +INSERT INTO external_script(account,script,blinding_key) VALUES($1,$2,$3); + +-- name: GetAllScripts :many +SELECT * FROM external_script; + +-- name: GetScript :one +SELECT * FROM external_script WHERE account = $1; + +-- name: DeleteScript :exec +DELETE FROM external_script WHERE account = $1; + -- name: ResetUtxos :exec DELETE FROM utxo; @@ -89,3 +102,6 @@ DELETE FROM wallet; -- name: ResetTransactions :exec DELETE FROM transaction; + +-- name: ResetScripts :exec +DELETE FROM external_script; diff --git a/internal/infrastructure/storage/db/test/script_repository_test.go b/internal/infrastructure/storage/db/test/script_repository_test.go new file mode 100644 index 0000000..d516975 --- /dev/null +++ b/internal/infrastructure/storage/db/test/script_repository_test.go @@ -0,0 +1,97 @@ +package db_test + +import ( + "encoding/hex" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/vulpemventures/ocean/internal/core/domain" + "github.com/vulpemventures/ocean/internal/core/ports" + dbbadger "github.com/vulpemventures/ocean/internal/infrastructure/storage/db/badger" + "github.com/vulpemventures/ocean/internal/infrastructure/storage/db/inmemory" +) + +func TestExternalScriptRepository(t *testing.T) { + repositories, err := newExternalScriptRepositories( + func(repoType string) ports.ScriptEventHandler { + return func(event domain.ExternalScriptEvent) { + fmt.Printf("received event from %s repo: %+v\n", repoType, event) + } + }, + ) + require.NoError(t, err) + + for name, repo := range repositories { + t.Run(name, func(t *testing.T) { + testExternalScriptRepository(t, repo) + }) + } +} + +func testExternalScriptRepository(t *testing.T, repo domain.ExternalScriptRepository) { + accountName := "test1" + newScript := domain.AddressInfo{ + Account: accountName, + Script: hex.EncodeToString(randomScript()), + BlindingKey: randomBytes(32), + } + + t.Run("add_script", func(t *testing.T) { + done, err := repo.AddScript(ctx, newScript) + require.NoError(t, err) + require.True(t, done) + + done, err = repo.AddScript(ctx, newScript) + require.NoError(t, err) + require.False(t, done) + }) + + t.Run("get_all_scripts", func(t *testing.T) { + scripts, err := repo.GetAllScripts(ctx) + require.NoError(t, err) + require.Len(t, scripts, 1) + }) + + t.Run("delete_script", func(t *testing.T) { + done, err := repo.DeleteScript(ctx, newScript.Account) + require.NoError(t, err) + require.True(t, done) + + done, err = repo.DeleteScript(ctx, newScript.Account) + require.NoError(t, err) + require.False(t, done) + + scripts, err := repo.GetAllScripts(ctx) + require.NoError(t, err) + require.Empty(t, scripts) + }) +} + +func newExternalScriptRepositories( + handlerFactory func(repoType string) ports.ScriptEventHandler, +) (map[string]domain.ExternalScriptRepository, error) { + inmemoryRepoManager := inmemory.NewRepoManager() + badgerRepoManager, err := dbbadger.NewRepoManager("", nil) + if err != nil { + return nil, err + } + + handlers := []ports.ScriptEventHandler{ + handlerFactory("badger"), handlerFactory("inmemory"), + } + + repoManagers := []ports.RepoManager{badgerRepoManager, inmemoryRepoManager, pgRepoManager} + + for i, handler := range handlers { + repoManager := repoManagers[i] + repoManager.RegisterHandlerForExternalScriptEvent(domain.ExternalScriptAdded, handler) + repoManager.RegisterHandlerForExternalScriptEvent(domain.ExternalScriptDeleted, handler) + } + + return map[string]domain.ExternalScriptRepository{ + "inmemory": inmemoryRepoManager.ExternalScriptRepository(), + "badger": badgerRepoManager.ExternalScriptRepository(), + "postgres": pgRepoManager.ExternalScriptRepository(), + }, nil +} diff --git a/internal/interfaces/grpc/handler/notification.go b/internal/interfaces/grpc/handler/notification.go index 0abbd3c..4274334 100644 --- a/internal/interfaces/grpc/handler/notification.go +++ b/internal/interfaces/grpc/handler/notification.go @@ -6,6 +6,8 @@ import ( pb "github.com/vulpemventures/ocean/api-spec/protobuf/gen/go/ocean/v1" "github.com/vulpemventures/ocean/internal/core/application" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) var ErrStreamConnectionClosed = fmt.Errorf("connection closed on by server") @@ -83,6 +85,39 @@ func (n notification) UtxosNotifications( } } +func (n notification) WatchExternalScript( + ctx context.Context, req *pb.WatchExternalScriptRequest, +) (*pb.WatchExternalScriptResponse, error) { + script, err := parseScript(req.GetScript()) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + blindingKey, err := parsePrvkey(req.GetBlindingKey()) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + label, err := n.appSvc.WatchScript(ctx, script, blindingKey) + if err != nil { + return nil, err + } + return &pb.WatchExternalScriptResponse{ + Label: label, + }, nil +} + +func (n notification) UnwatchExternalScript( + ctx context.Context, req *pb.UnwatchExternalScriptRequest, +) (*pb.UnwatchExternalScriptResponse, error) { + label, err := parseAccountName(req.GetLabel()) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + if err := n.appSvc.StopWatchingScript(ctx, label); err != nil { + return nil, err + } + return &pb.UnwatchExternalScriptResponse{}, nil +} + func (n notification) AddWebhook( ctx context.Context, req *pb.AddWebhookRequest, ) (*pb.AddWebhookResponse, error) { diff --git a/internal/interfaces/grpc/handler/util.go b/internal/interfaces/grpc/handler/util.go index 08820b5..0548318 100644 --- a/internal/interfaces/grpc/handler/util.go +++ b/internal/interfaces/grpc/handler/util.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "fmt" + "github.com/btcsuite/btcd/txscript" "github.com/vulpemventures/go-elements/address" "github.com/vulpemventures/go-elements/elementsutil" pb "github.com/vulpemventures/ocean/api-spec/protobuf/gen/go/ocean/v1" @@ -291,3 +292,27 @@ func parseRootPath(p string) (string, error) { } return p, nil } + +func parseScript(script string) (string, error) { + if len(script) <= 0 { + return "", fmt.Errorf("missing script") + } + buf, err := hex.DecodeString(script) + if err != nil { + return "", fmt.Errorf("invalid script: must be in hex format") + } + if _, err := txscript.ParsePkScript(buf); err != nil { + return "", fmt.Errorf("invalid script: %s", err) + } + return script, nil +} + +func parsePrvkey(key string) (string, error) { + if len(key) <= 0 { + return "", nil + } + if _, err := hex.DecodeString(key); err != nil { + return "", fmt.Errorf("invalid key: must be in hex format") + } + return key, nil +} diff --git a/internal/interfaces/grpc/service.go b/internal/interfaces/grpc/service.go index 72ab66f..5889066 100644 --- a/internal/interfaces/grpc/service.go +++ b/internal/interfaces/grpc/service.go @@ -53,14 +53,14 @@ func NewService(config ServiceConfig, appConfig *appconfig.AppConfig) (*service, } func (s *service) Start() error { + s.appConfig.BlockchainScanner().Start() + s.log("started blockchain scanner") + srv, err := s.start() if err != nil { return err } - s.appConfig.BlockchainScanner().Start() - s.log("started blockchain scanner") - s.log("start listening on %s", s.config.address()) s.grpcServer = srv