diff --git a/api-spec/openapi/swagger/arkwallet/v1/bitcoin_wallet.openapi.json b/api-spec/openapi/swagger/arkwallet/v1/bitcoin_wallet.openapi.json index 3e700d08d..7ebb9c909 100644 --- a/api-spec/openapi/swagger/arkwallet/v1/bitcoin_wallet.openapi.json +++ b/api-spec/openapi/swagger/arkwallet/v1/bitcoin_wallet.openapi.json @@ -1099,6 +1099,46 @@ } } }, + "/v1/wallet/unwatch-all-scripts": { + "post": { + "tags": [ + "WalletService" + ], + "operationId": "WalletService_UnwatchAllScripts", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnwatchAllScriptsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "a successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnwatchAllScriptsResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Status" + } + } + } + } + } + } + }, "/v1/wallet/unwatch-scripts": { "post": { "tags": [ @@ -1861,6 +1901,14 @@ "title": "UnlockResponse", "type": "object" }, + "UnwatchAllScriptsRequest": { + "title": "UnwatchAllScriptsRequest", + "type": "object" + }, + "UnwatchAllScriptsResponse": { + "title": "UnwatchAllScriptsResponse", + "type": "object" + }, "UnwatchScriptsRequest": { "title": "UnwatchScriptsRequest", "type": "object", diff --git a/api-spec/protobuf/arkwallet/v1/bitcoin_wallet.proto b/api-spec/protobuf/arkwallet/v1/bitcoin_wallet.proto index bf82fcb66..5ea6a9060 100644 --- a/api-spec/protobuf/arkwallet/v1/bitcoin_wallet.proto +++ b/api-spec/protobuf/arkwallet/v1/bitcoin_wallet.proto @@ -177,6 +177,12 @@ service WalletService { body: "*" }; } + rpc UnwatchAllScripts(UnwatchAllScriptsRequest) returns (UnwatchAllScriptsResponse) { + option (meshapi.gateway.http) = { + post: "/v1/wallet/unwatch-all-scripts" + body: "*" + }; + } rpc NotificationStream(NotificationStreamRequest) returns (stream NotificationStreamResponse) { option (meshapi.gateway.http) = { get: "/v1/wallet/notifications" @@ -378,6 +384,9 @@ message UnwatchScriptsRequest { } message UnwatchScriptsResponse {} +message UnwatchAllScriptsRequest {} +message UnwatchAllScriptsResponse {} + message GetTransactionRequest { string txid = 1; } diff --git a/api-spec/protobuf/gen/ark/v1/indexer.pb.rgw.go b/api-spec/protobuf/gen/ark/v1/indexer.pb.rgw.go index 2b9e07ca5..4ddecb860 100644 --- a/api-spec/protobuf/gen/ark/v1/indexer.pb.rgw.go +++ b/api-spec/protobuf/gen/ark/v1/indexer.pb.rgw.go @@ -125,7 +125,7 @@ func request_IndexerService_GetConnectors_0(ctx context.Context, marshaler gatew var ( query_params_IndexerService_GetVtxoTree_0 = gateway.QueryParameterParseOptions{ - Filter: trie.New("batch_outpoint.vout", "batch_outpoint.txid", "txid", "vout"), + Filter: trie.New("batch_outpoint.txid", "batch_outpoint.vout", "txid", "vout"), } ) @@ -173,7 +173,7 @@ func request_IndexerService_GetVtxoTree_0(ctx context.Context, marshaler gateway var ( query_params_IndexerService_GetVtxoTreeLeaves_0 = gateway.QueryParameterParseOptions{ - Filter: trie.New("txid", "vout", "batch_outpoint.vout", "batch_outpoint.txid"), + Filter: trie.New("batch_outpoint.vout", "batch_outpoint.txid", "txid", "vout"), } ) @@ -243,7 +243,7 @@ func request_IndexerService_GetVtxos_0(ctx context.Context, marshaler gateway.Ma var ( query_params_IndexerService_GetVtxoChain_0 = gateway.QueryParameterParseOptions{ - Filter: trie.New("outpoint.txid", "outpoint.vout", "txid", "vout"), + Filter: trie.New("txid", "vout", "outpoint.txid", "outpoint.vout"), } ) diff --git a/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.go b/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.go index f769ec47e..19e61d2d1 100644 --- a/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.go +++ b/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.go @@ -2551,6 +2551,78 @@ func (*UnwatchScriptsResponse) Descriptor() ([]byte, []int) { return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{57} } +type UnwatchAllScriptsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UnwatchAllScriptsRequest) Reset() { + *x = UnwatchAllScriptsRequest{} + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UnwatchAllScriptsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnwatchAllScriptsRequest) ProtoMessage() {} + +func (x *UnwatchAllScriptsRequest) ProtoReflect() protoreflect.Message { + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[58] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnwatchAllScriptsRequest.ProtoReflect.Descriptor instead. +func (*UnwatchAllScriptsRequest) Descriptor() ([]byte, []int) { + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{58} +} + +type UnwatchAllScriptsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UnwatchAllScriptsResponse) Reset() { + *x = UnwatchAllScriptsResponse{} + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UnwatchAllScriptsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnwatchAllScriptsResponse) ProtoMessage() {} + +func (x *UnwatchAllScriptsResponse) ProtoReflect() protoreflect.Message { + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[59] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnwatchAllScriptsResponse.ProtoReflect.Descriptor instead. +func (*UnwatchAllScriptsResponse) Descriptor() ([]byte, []int) { + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{59} +} + type GetTransactionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Txid string `protobuf:"bytes,1,opt,name=txid,proto3" json:"txid,omitempty"` @@ -2560,7 +2632,7 @@ type GetTransactionRequest struct { func (x *GetTransactionRequest) Reset() { *x = GetTransactionRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[58] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2572,7 +2644,7 @@ func (x *GetTransactionRequest) String() string { func (*GetTransactionRequest) ProtoMessage() {} func (x *GetTransactionRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[58] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2585,7 +2657,7 @@ func (x *GetTransactionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTransactionRequest.ProtoReflect.Descriptor instead. func (*GetTransactionRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{58} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{60} } func (x *GetTransactionRequest) GetTxid() string { @@ -2604,7 +2676,7 @@ type GetTransactionResponse struct { func (x *GetTransactionResponse) Reset() { *x = GetTransactionResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[59] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2616,7 +2688,7 @@ func (x *GetTransactionResponse) String() string { func (*GetTransactionResponse) ProtoMessage() {} func (x *GetTransactionResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[59] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2629,7 +2701,7 @@ func (x *GetTransactionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTransactionResponse.ProtoReflect.Descriptor instead. func (*GetTransactionResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{59} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{61} } func (x *GetTransactionResponse) GetTxHex() string { @@ -2648,7 +2720,7 @@ type SignMessageRequest struct { func (x *SignMessageRequest) Reset() { *x = SignMessageRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[60] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2660,7 +2732,7 @@ func (x *SignMessageRequest) String() string { func (*SignMessageRequest) ProtoMessage() {} func (x *SignMessageRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[60] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2673,7 +2745,7 @@ func (x *SignMessageRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SignMessageRequest.ProtoReflect.Descriptor instead. func (*SignMessageRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{60} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{62} } func (x *SignMessageRequest) GetMessage() []byte { @@ -2692,7 +2764,7 @@ type SignMessageResponse struct { func (x *SignMessageResponse) Reset() { *x = SignMessageResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[61] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2704,7 +2776,7 @@ func (x *SignMessageResponse) String() string { func (*SignMessageResponse) ProtoMessage() {} func (x *SignMessageResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[61] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2717,7 +2789,7 @@ func (x *SignMessageResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SignMessageResponse.ProtoReflect.Descriptor instead. func (*SignMessageResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{61} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{63} } func (x *SignMessageResponse) GetSignature() []byte { @@ -2737,7 +2809,7 @@ type VerifyMessageSignatureRequest struct { func (x *VerifyMessageSignatureRequest) Reset() { *x = VerifyMessageSignatureRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[62] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2749,7 +2821,7 @@ func (x *VerifyMessageSignatureRequest) String() string { func (*VerifyMessageSignatureRequest) ProtoMessage() {} func (x *VerifyMessageSignatureRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[62] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2762,7 +2834,7 @@ func (x *VerifyMessageSignatureRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyMessageSignatureRequest.ProtoReflect.Descriptor instead. func (*VerifyMessageSignatureRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{62} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{64} } func (x *VerifyMessageSignatureRequest) GetMessage() []byte { @@ -2788,7 +2860,7 @@ type VerifyMessageSignatureResponse struct { func (x *VerifyMessageSignatureResponse) Reset() { *x = VerifyMessageSignatureResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[63] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2800,7 +2872,7 @@ func (x *VerifyMessageSignatureResponse) String() string { func (*VerifyMessageSignatureResponse) ProtoMessage() {} func (x *VerifyMessageSignatureResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[63] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2813,7 +2885,7 @@ func (x *VerifyMessageSignatureResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyMessageSignatureResponse.ProtoReflect.Descriptor instead. func (*VerifyMessageSignatureResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{63} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{65} } func (x *VerifyMessageSignatureResponse) GetValid() bool { @@ -2831,7 +2903,7 @@ type GetCurrentBlockTimeRequest struct { func (x *GetCurrentBlockTimeRequest) Reset() { *x = GetCurrentBlockTimeRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[64] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2843,7 +2915,7 @@ func (x *GetCurrentBlockTimeRequest) String() string { func (*GetCurrentBlockTimeRequest) ProtoMessage() {} func (x *GetCurrentBlockTimeRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[64] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2856,7 +2928,7 @@ func (x *GetCurrentBlockTimeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentBlockTimeRequest.ProtoReflect.Descriptor instead. func (*GetCurrentBlockTimeRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{64} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{66} } type GetCurrentBlockTimeResponse struct { @@ -2868,7 +2940,7 @@ type GetCurrentBlockTimeResponse struct { func (x *GetCurrentBlockTimeResponse) Reset() { *x = GetCurrentBlockTimeResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[65] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2880,7 +2952,7 @@ func (x *GetCurrentBlockTimeResponse) String() string { func (*GetCurrentBlockTimeResponse) ProtoMessage() {} func (x *GetCurrentBlockTimeResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[65] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2893,7 +2965,7 @@ func (x *GetCurrentBlockTimeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentBlockTimeResponse.ProtoReflect.Descriptor instead. func (*GetCurrentBlockTimeResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{65} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{67} } func (x *GetCurrentBlockTimeResponse) GetTimestamp() *BlockTimestamp { @@ -2912,7 +2984,7 @@ type RescanUtxosRequest struct { func (x *RescanUtxosRequest) Reset() { *x = RescanUtxosRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[66] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2924,7 +2996,7 @@ func (x *RescanUtxosRequest) String() string { func (*RescanUtxosRequest) ProtoMessage() {} func (x *RescanUtxosRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[66] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2937,7 +3009,7 @@ func (x *RescanUtxosRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RescanUtxosRequest.ProtoReflect.Descriptor instead. func (*RescanUtxosRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{66} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{68} } func (x *RescanUtxosRequest) GetOutpoints() []string { @@ -2955,7 +3027,7 @@ type RescanUtxosResponse struct { func (x *RescanUtxosResponse) Reset() { *x = RescanUtxosResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[67] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2967,7 +3039,7 @@ func (x *RescanUtxosResponse) String() string { func (*RescanUtxosResponse) ProtoMessage() {} func (x *RescanUtxosResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[67] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2980,7 +3052,7 @@ func (x *RescanUtxosResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RescanUtxosResponse.ProtoReflect.Descriptor instead. func (*RescanUtxosResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{67} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{69} } type WithdrawRequest struct { @@ -2996,7 +3068,7 @@ type WithdrawRequest struct { func (x *WithdrawRequest) Reset() { *x = WithdrawRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[68] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3008,7 +3080,7 @@ func (x *WithdrawRequest) String() string { func (*WithdrawRequest) ProtoMessage() {} func (x *WithdrawRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[68] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3021,7 +3093,7 @@ func (x *WithdrawRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WithdrawRequest.ProtoReflect.Descriptor instead. func (*WithdrawRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{68} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{70} } func (x *WithdrawRequest) GetAddress() string { @@ -3054,7 +3126,7 @@ type WithdrawResponse struct { func (x *WithdrawResponse) Reset() { *x = WithdrawResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[69] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3066,7 +3138,7 @@ func (x *WithdrawResponse) String() string { func (*WithdrawResponse) ProtoMessage() {} func (x *WithdrawResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[69] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3079,7 +3151,7 @@ func (x *WithdrawResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WithdrawResponse.ProtoReflect.Descriptor instead. func (*WithdrawResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{69} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{71} } func (x *WithdrawResponse) GetTxid() string { @@ -3098,7 +3170,7 @@ type LoadSignerKeyRequest struct { func (x *LoadSignerKeyRequest) Reset() { *x = LoadSignerKeyRequest{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[70] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3110,7 +3182,7 @@ func (x *LoadSignerKeyRequest) String() string { func (*LoadSignerKeyRequest) ProtoMessage() {} func (x *LoadSignerKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[70] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3123,7 +3195,7 @@ func (x *LoadSignerKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LoadSignerKeyRequest.ProtoReflect.Descriptor instead. func (*LoadSignerKeyRequest) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{70} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{72} } func (x *LoadSignerKeyRequest) GetPrivateKey() string { @@ -3141,7 +3213,7 @@ type LoadSignerKeyResponse struct { func (x *LoadSignerKeyResponse) Reset() { *x = LoadSignerKeyResponse{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[71] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3153,7 +3225,7 @@ func (x *LoadSignerKeyResponse) String() string { func (*LoadSignerKeyResponse) ProtoMessage() {} func (x *LoadSignerKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[71] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3166,7 +3238,7 @@ func (x *LoadSignerKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LoadSignerKeyResponse.ProtoReflect.Descriptor instead. func (*LoadSignerKeyResponse) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{71} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{73} } type TxInput struct { @@ -3181,7 +3253,7 @@ type TxInput struct { func (x *TxInput) Reset() { *x = TxInput{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[72] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3193,7 +3265,7 @@ func (x *TxInput) String() string { func (*TxInput) ProtoMessage() {} func (x *TxInput) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[72] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3206,7 +3278,7 @@ func (x *TxInput) ProtoReflect() protoreflect.Message { // Deprecated: Use TxInput.ProtoReflect.Descriptor instead. func (*TxInput) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{72} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{74} } func (x *TxInput) GetTxid() string { @@ -3247,7 +3319,7 @@ type TxOutpoint struct { func (x *TxOutpoint) Reset() { *x = TxOutpoint{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[73] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3259,7 +3331,7 @@ func (x *TxOutpoint) String() string { func (*TxOutpoint) ProtoMessage() {} func (x *TxOutpoint) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[73] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3272,7 +3344,7 @@ func (x *TxOutpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use TxOutpoint.ProtoReflect.Descriptor instead. func (*TxOutpoint) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{73} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{75} } func (x *TxOutpoint) GetTxid() string { @@ -3299,7 +3371,7 @@ type BlockTimestamp struct { func (x *BlockTimestamp) Reset() { *x = BlockTimestamp{} - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[74] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3311,7 +3383,7 @@ func (x *BlockTimestamp) String() string { func (*BlockTimestamp) ProtoMessage() {} func (x *BlockTimestamp) ProtoReflect() protoreflect.Message { - mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[74] + mi := &file_arkwallet_v1_bitcoin_wallet_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3324,7 +3396,7 @@ func (x *BlockTimestamp) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockTimestamp.ProtoReflect.Descriptor instead. func (*BlockTimestamp) Descriptor() ([]byte, []int) { - return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{74} + return file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP(), []int{76} } func (x *BlockTimestamp) GetHeight() uint32 { @@ -3461,7 +3533,9 @@ const file_arkwallet_v1_bitcoin_wallet_proto_rawDesc = "" + "\x14WatchScriptsResponse\"1\n" + "\x15UnwatchScriptsRequest\x12\x18\n" + "\ascripts\x18\x01 \x03(\tR\ascripts\"\x18\n" + - "\x16UnwatchScriptsResponse\"+\n" + + "\x16UnwatchScriptsResponse\"\x1a\n" + + "\x18UnwatchAllScriptsRequest\"\x1b\n" + + "\x19UnwatchAllScriptsResponse\"+\n" + "\x15GetTransactionRequest\x12\x12\n" + "\x04txid\x18\x01 \x01(\tR\x04txid\"/\n" + "\x16GetTransactionResponse\x12\x15\n" + @@ -3502,7 +3576,7 @@ const file_arkwallet_v1_bitcoin_wallet_proto_rawDesc = "" + "\x05index\x18\x02 \x01(\rR\x05index\"<\n" + "\x0eBlockTimestamp\x12\x16\n" + "\x06height\x18\x01 \x01(\rR\x06height\x12\x12\n" + - "\x04time\x18\x02 \x01(\x03R\x04time2\xb7!\n" + + "\x04time\x18\x02 \x01(\x03R\x04time2\xc6\"\n" + "\rWalletService\x12\\\n" + "\aGenSeed\x12\x1c.arkwallet.v1.GenSeedRequest\x1a\x1d.arkwallet.v1.GenSeedResponse\"\x14\xb2J\x11\x12\x0f/v1/wallet/seed\x12^\n" + "\x06Create\x12\x1b.arkwallet.v1.CreateRequest\x1a\x1c.arkwallet.v1.CreateResponse\"\x19\xb2J\x16B\x01*\"\x11/v1/wallet/create\x12b\n" + @@ -3535,7 +3609,8 @@ const file_arkwallet_v1_bitcoin_wallet_proto_rawDesc = "" + "\x13GetCurrentBlockTime\x12(.arkwallet.v1.GetCurrentBlockTimeRequest\x1a).arkwallet.v1.GetCurrentBlockTimeResponse\"\x1a\xb2J\x17\x12\x15/v1/wallet/block-time\x12f\n" + "\bWithdraw\x12\x1d.arkwallet.v1.WithdrawRequest\x1a\x1e.arkwallet.v1.WithdrawResponse\"\x1b\xb2J\x18B\x01*\"\x13/v1/wallet/withdraw\x12w\n" + "\fWatchScripts\x12!.arkwallet.v1.WatchScriptsRequest\x1a\".arkwallet.v1.WatchScriptsResponse\" \xb2J\x1dB\x01*\"\x18/v1/wallet/watch-scripts\x12\x7f\n" + - "\x0eUnwatchScripts\x12#.arkwallet.v1.UnwatchScriptsRequest\x1a$.arkwallet.v1.UnwatchScriptsResponse\"\"\xb2J\x1fB\x01*\"\x1a/v1/wallet/unwatch-scripts\x12\x88\x01\n" + + "\x0eUnwatchScripts\x12#.arkwallet.v1.UnwatchScriptsRequest\x1a$.arkwallet.v1.UnwatchScriptsResponse\"\"\xb2J\x1fB\x01*\"\x1a/v1/wallet/unwatch-scripts\x12\x8c\x01\n" + + "\x11UnwatchAllScripts\x12&.arkwallet.v1.UnwatchAllScriptsRequest\x1a'.arkwallet.v1.UnwatchAllScriptsResponse\"&\xb2J#B\x01*\"\x1e/v1/wallet/unwatch-all-scripts\x12\x88\x01\n" + "\x12NotificationStream\x12'.arkwallet.v1.NotificationStreamRequest\x1a(.arkwallet.v1.NotificationStreamResponse\"\x1d\xb2J\x1a\x12\x18/v1/wallet/notifications0\x01\x12w\n" + "\rLoadSignerKey\x12\".arkwallet.v1.LoadSignerKeyRequest\x1a#.arkwallet.v1.LoadSignerKeyResponse\"\x1d\xb2J\x1aB\x01*\"\x15/v1/wallet/signer-key\x12s\n" + "\vRescanUtxos\x12 .arkwallet.v1.RescanUtxosRequest\x1a!.arkwallet.v1.RescanUtxosResponse\"\x1f\xb2J\x1cB\x01*\"\x17/v1/wallet/rescan-utxosB\xab\x01\n" + @@ -3553,7 +3628,7 @@ func file_arkwallet_v1_bitcoin_wallet_proto_rawDescGZIP() []byte { return file_arkwallet_v1_bitcoin_wallet_proto_rawDescData } -var file_arkwallet_v1_bitcoin_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 75) +var file_arkwallet_v1_bitcoin_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 77) var file_arkwallet_v1_bitcoin_wallet_proto_goTypes = []any{ (*GetReadyUpdateRequest)(nil), // 0: arkwallet.v1.GetReadyUpdateRequest (*GetReadyUpdateResponse)(nil), // 1: arkwallet.v1.GetReadyUpdateResponse @@ -3613,31 +3688,33 @@ var file_arkwallet_v1_bitcoin_wallet_proto_goTypes = []any{ (*WatchScriptsResponse)(nil), // 55: arkwallet.v1.WatchScriptsResponse (*UnwatchScriptsRequest)(nil), // 56: arkwallet.v1.UnwatchScriptsRequest (*UnwatchScriptsResponse)(nil), // 57: arkwallet.v1.UnwatchScriptsResponse - (*GetTransactionRequest)(nil), // 58: arkwallet.v1.GetTransactionRequest - (*GetTransactionResponse)(nil), // 59: arkwallet.v1.GetTransactionResponse - (*SignMessageRequest)(nil), // 60: arkwallet.v1.SignMessageRequest - (*SignMessageResponse)(nil), // 61: arkwallet.v1.SignMessageResponse - (*VerifyMessageSignatureRequest)(nil), // 62: arkwallet.v1.VerifyMessageSignatureRequest - (*VerifyMessageSignatureResponse)(nil), // 63: arkwallet.v1.VerifyMessageSignatureResponse - (*GetCurrentBlockTimeRequest)(nil), // 64: arkwallet.v1.GetCurrentBlockTimeRequest - (*GetCurrentBlockTimeResponse)(nil), // 65: arkwallet.v1.GetCurrentBlockTimeResponse - (*RescanUtxosRequest)(nil), // 66: arkwallet.v1.RescanUtxosRequest - (*RescanUtxosResponse)(nil), // 67: arkwallet.v1.RescanUtxosResponse - (*WithdrawRequest)(nil), // 68: arkwallet.v1.WithdrawRequest - (*WithdrawResponse)(nil), // 69: arkwallet.v1.WithdrawResponse - (*LoadSignerKeyRequest)(nil), // 70: arkwallet.v1.LoadSignerKeyRequest - (*LoadSignerKeyResponse)(nil), // 71: arkwallet.v1.LoadSignerKeyResponse - (*TxInput)(nil), // 72: arkwallet.v1.TxInput - (*TxOutpoint)(nil), // 73: arkwallet.v1.TxOutpoint - (*BlockTimestamp)(nil), // 74: arkwallet.v1.BlockTimestamp + (*UnwatchAllScriptsRequest)(nil), // 58: arkwallet.v1.UnwatchAllScriptsRequest + (*UnwatchAllScriptsResponse)(nil), // 59: arkwallet.v1.UnwatchAllScriptsResponse + (*GetTransactionRequest)(nil), // 60: arkwallet.v1.GetTransactionRequest + (*GetTransactionResponse)(nil), // 61: arkwallet.v1.GetTransactionResponse + (*SignMessageRequest)(nil), // 62: arkwallet.v1.SignMessageRequest + (*SignMessageResponse)(nil), // 63: arkwallet.v1.SignMessageResponse + (*VerifyMessageSignatureRequest)(nil), // 64: arkwallet.v1.VerifyMessageSignatureRequest + (*VerifyMessageSignatureResponse)(nil), // 65: arkwallet.v1.VerifyMessageSignatureResponse + (*GetCurrentBlockTimeRequest)(nil), // 66: arkwallet.v1.GetCurrentBlockTimeRequest + (*GetCurrentBlockTimeResponse)(nil), // 67: arkwallet.v1.GetCurrentBlockTimeResponse + (*RescanUtxosRequest)(nil), // 68: arkwallet.v1.RescanUtxosRequest + (*RescanUtxosResponse)(nil), // 69: arkwallet.v1.RescanUtxosResponse + (*WithdrawRequest)(nil), // 70: arkwallet.v1.WithdrawRequest + (*WithdrawResponse)(nil), // 71: arkwallet.v1.WithdrawResponse + (*LoadSignerKeyRequest)(nil), // 72: arkwallet.v1.LoadSignerKeyRequest + (*LoadSignerKeyResponse)(nil), // 73: arkwallet.v1.LoadSignerKeyResponse + (*TxInput)(nil), // 74: arkwallet.v1.TxInput + (*TxOutpoint)(nil), // 75: arkwallet.v1.TxOutpoint + (*BlockTimestamp)(nil), // 76: arkwallet.v1.BlockTimestamp } var file_arkwallet_v1_bitcoin_wallet_proto_depIdxs = []int32{ 8, // 0: arkwallet.v1.NotificationStreamResponse.entries:type_name -> arkwallet.v1.VtoxsPerScript 9, // 1: arkwallet.v1.VtoxsPerScript.vtxos:type_name -> arkwallet.v1.VtxoWithKey - 72, // 2: arkwallet.v1.SelectUtxosResponse.utxos:type_name -> arkwallet.v1.TxInput - 72, // 3: arkwallet.v1.ListConnectorUtxosResponse.utxos:type_name -> arkwallet.v1.TxInput - 73, // 4: arkwallet.v1.LockConnectorUtxosRequest.utxos:type_name -> arkwallet.v1.TxOutpoint - 74, // 5: arkwallet.v1.GetCurrentBlockTimeResponse.timestamp:type_name -> arkwallet.v1.BlockTimestamp + 74, // 2: arkwallet.v1.SelectUtxosResponse.utxos:type_name -> arkwallet.v1.TxInput + 74, // 3: arkwallet.v1.ListConnectorUtxosResponse.utxos:type_name -> arkwallet.v1.TxInput + 75, // 4: arkwallet.v1.LockConnectorUtxosRequest.utxos:type_name -> arkwallet.v1.TxOutpoint + 76, // 5: arkwallet.v1.GetCurrentBlockTimeResponse.timestamp:type_name -> arkwallet.v1.BlockTimestamp 10, // 6: arkwallet.v1.WalletService.GenSeed:input_type -> arkwallet.v1.GenSeedRequest 12, // 7: arkwallet.v1.WalletService.Create:input_type -> arkwallet.v1.CreateRequest 14, // 8: arkwallet.v1.WalletService.Restore:input_type -> arkwallet.v1.RestoreRequest @@ -3662,52 +3739,54 @@ var file_arkwallet_v1_bitcoin_wallet_proto_depIdxs = []int32{ 48, // 27: arkwallet.v1.WalletService.ConnectorsAccountBalance:input_type -> arkwallet.v1.ConnectorsAccountBalanceRequest 50, // 28: arkwallet.v1.WalletService.LockConnectorUtxos:input_type -> arkwallet.v1.LockConnectorUtxosRequest 52, // 29: arkwallet.v1.WalletService.GetDustAmount:input_type -> arkwallet.v1.GetDustAmountRequest - 58, // 30: arkwallet.v1.WalletService.GetTransaction:input_type -> arkwallet.v1.GetTransactionRequest - 60, // 31: arkwallet.v1.WalletService.SignMessage:input_type -> arkwallet.v1.SignMessageRequest - 62, // 32: arkwallet.v1.WalletService.VerifyMessageSignature:input_type -> arkwallet.v1.VerifyMessageSignatureRequest - 64, // 33: arkwallet.v1.WalletService.GetCurrentBlockTime:input_type -> arkwallet.v1.GetCurrentBlockTimeRequest - 68, // 34: arkwallet.v1.WalletService.Withdraw:input_type -> arkwallet.v1.WithdrawRequest + 60, // 30: arkwallet.v1.WalletService.GetTransaction:input_type -> arkwallet.v1.GetTransactionRequest + 62, // 31: arkwallet.v1.WalletService.SignMessage:input_type -> arkwallet.v1.SignMessageRequest + 64, // 32: arkwallet.v1.WalletService.VerifyMessageSignature:input_type -> arkwallet.v1.VerifyMessageSignatureRequest + 66, // 33: arkwallet.v1.WalletService.GetCurrentBlockTime:input_type -> arkwallet.v1.GetCurrentBlockTimeRequest + 70, // 34: arkwallet.v1.WalletService.Withdraw:input_type -> arkwallet.v1.WithdrawRequest 54, // 35: arkwallet.v1.WalletService.WatchScripts:input_type -> arkwallet.v1.WatchScriptsRequest 56, // 36: arkwallet.v1.WalletService.UnwatchScripts:input_type -> arkwallet.v1.UnwatchScriptsRequest - 6, // 37: arkwallet.v1.WalletService.NotificationStream:input_type -> arkwallet.v1.NotificationStreamRequest - 70, // 38: arkwallet.v1.WalletService.LoadSignerKey:input_type -> arkwallet.v1.LoadSignerKeyRequest - 66, // 39: arkwallet.v1.WalletService.RescanUtxos:input_type -> arkwallet.v1.RescanUtxosRequest - 11, // 40: arkwallet.v1.WalletService.GenSeed:output_type -> arkwallet.v1.GenSeedResponse - 13, // 41: arkwallet.v1.WalletService.Create:output_type -> arkwallet.v1.CreateResponse - 15, // 42: arkwallet.v1.WalletService.Restore:output_type -> arkwallet.v1.RestoreResponse - 17, // 43: arkwallet.v1.WalletService.Unlock:output_type -> arkwallet.v1.UnlockResponse - 19, // 44: arkwallet.v1.WalletService.Lock:output_type -> arkwallet.v1.LockResponse - 21, // 45: arkwallet.v1.WalletService.Status:output_type -> arkwallet.v1.StatusResponse - 23, // 46: arkwallet.v1.WalletService.GetNetwork:output_type -> arkwallet.v1.GetNetworkResponse - 25, // 47: arkwallet.v1.WalletService.GetForfeitPubkey:output_type -> arkwallet.v1.GetForfeitPubkeyResponse - 27, // 48: arkwallet.v1.WalletService.DeriveConnectorAddress:output_type -> arkwallet.v1.DeriveConnectorAddressResponse - 29, // 49: arkwallet.v1.WalletService.DeriveAddresses:output_type -> arkwallet.v1.DeriveAddressesResponse - 31, // 50: arkwallet.v1.WalletService.SignTransaction:output_type -> arkwallet.v1.SignTransactionResponse - 33, // 51: arkwallet.v1.WalletService.SignTransactionTapscript:output_type -> arkwallet.v1.SignTransactionTapscriptResponse - 35, // 52: arkwallet.v1.WalletService.SelectUtxos:output_type -> arkwallet.v1.SelectUtxosResponse - 37, // 53: arkwallet.v1.WalletService.BroadcastTransaction:output_type -> arkwallet.v1.BroadcastTransactionResponse - 1, // 54: arkwallet.v1.WalletService.GetReadyUpdate:output_type -> arkwallet.v1.GetReadyUpdateResponse - 3, // 55: arkwallet.v1.WalletService.IsTransactionConfirmed:output_type -> arkwallet.v1.IsTransactionConfirmedResponse - 5, // 56: arkwallet.v1.WalletService.GetOutpointStatus:output_type -> arkwallet.v1.GetOutpointStatusResponse - 41, // 57: arkwallet.v1.WalletService.EstimateFees:output_type -> arkwallet.v1.EstimateFeesResponse - 43, // 58: arkwallet.v1.WalletService.FeeRate:output_type -> arkwallet.v1.FeeRateResponse - 45, // 59: arkwallet.v1.WalletService.ListConnectorUtxos:output_type -> arkwallet.v1.ListConnectorUtxosResponse - 47, // 60: arkwallet.v1.WalletService.MainAccountBalance:output_type -> arkwallet.v1.MainAccountBalanceResponse - 49, // 61: arkwallet.v1.WalletService.ConnectorsAccountBalance:output_type -> arkwallet.v1.ConnectorsAccountBalanceResponse - 51, // 62: arkwallet.v1.WalletService.LockConnectorUtxos:output_type -> arkwallet.v1.LockConnectorUtxosResponse - 53, // 63: arkwallet.v1.WalletService.GetDustAmount:output_type -> arkwallet.v1.GetDustAmountResponse - 59, // 64: arkwallet.v1.WalletService.GetTransaction:output_type -> arkwallet.v1.GetTransactionResponse - 61, // 65: arkwallet.v1.WalletService.SignMessage:output_type -> arkwallet.v1.SignMessageResponse - 63, // 66: arkwallet.v1.WalletService.VerifyMessageSignature:output_type -> arkwallet.v1.VerifyMessageSignatureResponse - 65, // 67: arkwallet.v1.WalletService.GetCurrentBlockTime:output_type -> arkwallet.v1.GetCurrentBlockTimeResponse - 69, // 68: arkwallet.v1.WalletService.Withdraw:output_type -> arkwallet.v1.WithdrawResponse - 55, // 69: arkwallet.v1.WalletService.WatchScripts:output_type -> arkwallet.v1.WatchScriptsResponse - 57, // 70: arkwallet.v1.WalletService.UnwatchScripts:output_type -> arkwallet.v1.UnwatchScriptsResponse - 7, // 71: arkwallet.v1.WalletService.NotificationStream:output_type -> arkwallet.v1.NotificationStreamResponse - 71, // 72: arkwallet.v1.WalletService.LoadSignerKey:output_type -> arkwallet.v1.LoadSignerKeyResponse - 67, // 73: arkwallet.v1.WalletService.RescanUtxos:output_type -> arkwallet.v1.RescanUtxosResponse - 40, // [40:74] is the sub-list for method output_type - 6, // [6:40] is the sub-list for method input_type + 58, // 37: arkwallet.v1.WalletService.UnwatchAllScripts:input_type -> arkwallet.v1.UnwatchAllScriptsRequest + 6, // 38: arkwallet.v1.WalletService.NotificationStream:input_type -> arkwallet.v1.NotificationStreamRequest + 72, // 39: arkwallet.v1.WalletService.LoadSignerKey:input_type -> arkwallet.v1.LoadSignerKeyRequest + 68, // 40: arkwallet.v1.WalletService.RescanUtxos:input_type -> arkwallet.v1.RescanUtxosRequest + 11, // 41: arkwallet.v1.WalletService.GenSeed:output_type -> arkwallet.v1.GenSeedResponse + 13, // 42: arkwallet.v1.WalletService.Create:output_type -> arkwallet.v1.CreateResponse + 15, // 43: arkwallet.v1.WalletService.Restore:output_type -> arkwallet.v1.RestoreResponse + 17, // 44: arkwallet.v1.WalletService.Unlock:output_type -> arkwallet.v1.UnlockResponse + 19, // 45: arkwallet.v1.WalletService.Lock:output_type -> arkwallet.v1.LockResponse + 21, // 46: arkwallet.v1.WalletService.Status:output_type -> arkwallet.v1.StatusResponse + 23, // 47: arkwallet.v1.WalletService.GetNetwork:output_type -> arkwallet.v1.GetNetworkResponse + 25, // 48: arkwallet.v1.WalletService.GetForfeitPubkey:output_type -> arkwallet.v1.GetForfeitPubkeyResponse + 27, // 49: arkwallet.v1.WalletService.DeriveConnectorAddress:output_type -> arkwallet.v1.DeriveConnectorAddressResponse + 29, // 50: arkwallet.v1.WalletService.DeriveAddresses:output_type -> arkwallet.v1.DeriveAddressesResponse + 31, // 51: arkwallet.v1.WalletService.SignTransaction:output_type -> arkwallet.v1.SignTransactionResponse + 33, // 52: arkwallet.v1.WalletService.SignTransactionTapscript:output_type -> arkwallet.v1.SignTransactionTapscriptResponse + 35, // 53: arkwallet.v1.WalletService.SelectUtxos:output_type -> arkwallet.v1.SelectUtxosResponse + 37, // 54: arkwallet.v1.WalletService.BroadcastTransaction:output_type -> arkwallet.v1.BroadcastTransactionResponse + 1, // 55: arkwallet.v1.WalletService.GetReadyUpdate:output_type -> arkwallet.v1.GetReadyUpdateResponse + 3, // 56: arkwallet.v1.WalletService.IsTransactionConfirmed:output_type -> arkwallet.v1.IsTransactionConfirmedResponse + 5, // 57: arkwallet.v1.WalletService.GetOutpointStatus:output_type -> arkwallet.v1.GetOutpointStatusResponse + 41, // 58: arkwallet.v1.WalletService.EstimateFees:output_type -> arkwallet.v1.EstimateFeesResponse + 43, // 59: arkwallet.v1.WalletService.FeeRate:output_type -> arkwallet.v1.FeeRateResponse + 45, // 60: arkwallet.v1.WalletService.ListConnectorUtxos:output_type -> arkwallet.v1.ListConnectorUtxosResponse + 47, // 61: arkwallet.v1.WalletService.MainAccountBalance:output_type -> arkwallet.v1.MainAccountBalanceResponse + 49, // 62: arkwallet.v1.WalletService.ConnectorsAccountBalance:output_type -> arkwallet.v1.ConnectorsAccountBalanceResponse + 51, // 63: arkwallet.v1.WalletService.LockConnectorUtxos:output_type -> arkwallet.v1.LockConnectorUtxosResponse + 53, // 64: arkwallet.v1.WalletService.GetDustAmount:output_type -> arkwallet.v1.GetDustAmountResponse + 61, // 65: arkwallet.v1.WalletService.GetTransaction:output_type -> arkwallet.v1.GetTransactionResponse + 63, // 66: arkwallet.v1.WalletService.SignMessage:output_type -> arkwallet.v1.SignMessageResponse + 65, // 67: arkwallet.v1.WalletService.VerifyMessageSignature:output_type -> arkwallet.v1.VerifyMessageSignatureResponse + 67, // 68: arkwallet.v1.WalletService.GetCurrentBlockTime:output_type -> arkwallet.v1.GetCurrentBlockTimeResponse + 71, // 69: arkwallet.v1.WalletService.Withdraw:output_type -> arkwallet.v1.WithdrawResponse + 55, // 70: arkwallet.v1.WalletService.WatchScripts:output_type -> arkwallet.v1.WatchScriptsResponse + 57, // 71: arkwallet.v1.WalletService.UnwatchScripts:output_type -> arkwallet.v1.UnwatchScriptsResponse + 59, // 72: arkwallet.v1.WalletService.UnwatchAllScripts:output_type -> arkwallet.v1.UnwatchAllScriptsResponse + 7, // 73: arkwallet.v1.WalletService.NotificationStream:output_type -> arkwallet.v1.NotificationStreamResponse + 73, // 74: arkwallet.v1.WalletService.LoadSignerKey:output_type -> arkwallet.v1.LoadSignerKeyResponse + 69, // 75: arkwallet.v1.WalletService.RescanUtxos:output_type -> arkwallet.v1.RescanUtxosResponse + 41, // [41:76] is the sub-list for method output_type + 6, // [6:41] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name @@ -3724,7 +3803,7 @@ func file_arkwallet_v1_bitcoin_wallet_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_arkwallet_v1_bitcoin_wallet_proto_rawDesc), len(file_arkwallet_v1_bitcoin_wallet_proto_rawDesc)), NumEnums: 0, - NumMessages: 75, + NumMessages: 77, NumExtensions: 0, NumServices: 1, }, diff --git a/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.rgw.go b/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.rgw.go index f84e111ed..34aa74ee4 100644 --- a/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.rgw.go +++ b/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet.pb.rgw.go @@ -463,6 +463,19 @@ func request_WalletService_UnwatchScripts_0(ctx context.Context, marshaler gatew } +func request_WalletService_UnwatchAllScripts_0(ctx context.Context, marshaler gateway.Marshaler, mux *gateway.ServeMux, client WalletServiceClient, req *http.Request, pathParams gateway.Params) (proto.Message, gateway.ServerMetadata, error) { + var protoReq UnwatchAllScriptsRequest + var metadata gateway.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, gateway.ErrMarshal{Err: err, Inbound: true} + } + + msg, err := client.UnwatchAllScripts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + func request_WalletService_NotificationStream_0(ctx context.Context, marshaler gateway.Marshaler, mux *gateway.ServeMux, client WalletServiceClient, req *http.Request, pathParams gateway.Params) (WalletService_NotificationStreamClient, gateway.ServerMetadata, error) { var protoReq NotificationStreamRequest var metadata gateway.ServerMetadata @@ -1278,6 +1291,28 @@ func RegisterWalletServiceHandlerClient(ctx context.Context, mux *gateway.ServeM mux.ForwardResponseMessage(annotatedContext, outboundMarshaler, w, req, resp) }) + mux.HandleWithParams("POST", "/v1/wallet/unwatch-all-scripts", func(w http.ResponseWriter, req *http.Request, pathParams gateway.Params) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := mux.MarshalerForRequest(req) + var err error + var annotatedContext context.Context + annotatedContext, err = gateway.AnnotateContext(ctx, mux, req, "/arkwallet.v1.WalletService/UnwatchAllScripts", gateway.WithHTTPPathPattern("/v1/wallet/unwatch-all-scripts")) + if err != nil { + mux.HTTPError(ctx, outboundMarshaler, w, req, err) + return + } + + resp, md, err := request_WalletService_UnwatchAllScripts_0(annotatedContext, inboundMarshaler, mux, client, req, pathParams) + annotatedContext = gateway.NewServerMetadataContext(annotatedContext, md) + if err != nil { + mux.HTTPError(annotatedContext, outboundMarshaler, w, req, err) + return + } + + mux.ForwardResponseMessage(annotatedContext, outboundMarshaler, w, req, resp) + }) + mux.HandleWithParams("GET", "/v1/wallet/notifications", func(w http.ResponseWriter, req *http.Request, pathParams gateway.Params) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() diff --git a/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet_grpc.pb.go b/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet_grpc.pb.go index 6d2366b18..5030f21d5 100644 --- a/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet_grpc.pb.go +++ b/api-spec/protobuf/gen/arkwallet/v1/bitcoin_wallet_grpc.pb.go @@ -50,6 +50,7 @@ const ( WalletService_Withdraw_FullMethodName = "/arkwallet.v1.WalletService/Withdraw" WalletService_WatchScripts_FullMethodName = "/arkwallet.v1.WalletService/WatchScripts" WalletService_UnwatchScripts_FullMethodName = "/arkwallet.v1.WalletService/UnwatchScripts" + WalletService_UnwatchAllScripts_FullMethodName = "/arkwallet.v1.WalletService/UnwatchAllScripts" WalletService_NotificationStream_FullMethodName = "/arkwallet.v1.WalletService/NotificationStream" WalletService_LoadSignerKey_FullMethodName = "/arkwallet.v1.WalletService/LoadSignerKey" WalletService_RescanUtxos_FullMethodName = "/arkwallet.v1.WalletService/RescanUtxos" @@ -92,6 +93,7 @@ type WalletServiceClient interface { Withdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*WithdrawResponse, error) WatchScripts(ctx context.Context, in *WatchScriptsRequest, opts ...grpc.CallOption) (*WatchScriptsResponse, error) UnwatchScripts(ctx context.Context, in *UnwatchScriptsRequest, opts ...grpc.CallOption) (*UnwatchScriptsResponse, error) + UnwatchAllScripts(ctx context.Context, in *UnwatchAllScriptsRequest, opts ...grpc.CallOption) (*UnwatchAllScriptsResponse, error) NotificationStream(ctx context.Context, in *NotificationStreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotificationStreamResponse], error) LoadSignerKey(ctx context.Context, in *LoadSignerKeyRequest, opts ...grpc.CallOption) (*LoadSignerKeyResponse, error) RescanUtxos(ctx context.Context, in *RescanUtxosRequest, opts ...grpc.CallOption) (*RescanUtxosResponse, error) @@ -424,6 +426,16 @@ func (c *walletServiceClient) UnwatchScripts(ctx context.Context, in *UnwatchScr return out, nil } +func (c *walletServiceClient) UnwatchAllScripts(ctx context.Context, in *UnwatchAllScriptsRequest, opts ...grpc.CallOption) (*UnwatchAllScriptsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UnwatchAllScriptsResponse) + err := c.cc.Invoke(ctx, WalletService_UnwatchAllScripts_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *walletServiceClient) NotificationStream(ctx context.Context, in *NotificationStreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NotificationStreamResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &WalletService_ServiceDesc.Streams[1], WalletService_NotificationStream_FullMethodName, cOpts...) @@ -500,6 +512,7 @@ type WalletServiceServer interface { Withdraw(context.Context, *WithdrawRequest) (*WithdrawResponse, error) WatchScripts(context.Context, *WatchScriptsRequest) (*WatchScriptsResponse, error) UnwatchScripts(context.Context, *UnwatchScriptsRequest) (*UnwatchScriptsResponse, error) + UnwatchAllScripts(context.Context, *UnwatchAllScriptsRequest) (*UnwatchAllScriptsResponse, error) NotificationStream(*NotificationStreamRequest, grpc.ServerStreamingServer[NotificationStreamResponse]) error LoadSignerKey(context.Context, *LoadSignerKeyRequest) (*LoadSignerKeyResponse, error) RescanUtxos(context.Context, *RescanUtxosRequest) (*RescanUtxosResponse, error) @@ -605,6 +618,9 @@ func (UnimplementedWalletServiceServer) WatchScripts(context.Context, *WatchScri func (UnimplementedWalletServiceServer) UnwatchScripts(context.Context, *UnwatchScriptsRequest) (*UnwatchScriptsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UnwatchScripts not implemented") } +func (UnimplementedWalletServiceServer) UnwatchAllScripts(context.Context, *UnwatchAllScriptsRequest) (*UnwatchAllScriptsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnwatchAllScripts not implemented") +} func (UnimplementedWalletServiceServer) NotificationStream(*NotificationStreamRequest, grpc.ServerStreamingServer[NotificationStreamResponse]) error { return status.Errorf(codes.Unimplemented, "method NotificationStream not implemented") } @@ -1185,6 +1201,24 @@ func _WalletService_UnwatchScripts_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _WalletService_UnwatchAllScripts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnwatchAllScriptsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WalletServiceServer).UnwatchAllScripts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WalletService_UnwatchAllScripts_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WalletServiceServer).UnwatchAllScripts(ctx, req.(*UnwatchAllScriptsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _WalletService_NotificationStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(NotificationStreamRequest) if err := stream.RecvMsg(m); err != nil { @@ -1359,6 +1393,10 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{ MethodName: "UnwatchScripts", Handler: _WalletService_UnwatchScripts_Handler, }, + { + MethodName: "UnwatchAllScripts", + Handler: _WalletService_UnwatchAllScripts_Handler, + }, { MethodName: "LoadSignerKey", Handler: _WalletService_LoadSignerKey_Handler, diff --git a/internal/core/application/fraud.go b/internal/core/application/fraud.go index d1f3440ed..c55cc2821 100644 --- a/internal/core/application/fraud.go +++ b/internal/core/application/fraud.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "fmt" "strings" - "sync" "github.com/arkade-os/arkd/internal/core/domain" "github.com/arkade-os/arkd/pkg/ark-lib/tree" @@ -28,13 +27,7 @@ import ( // If the vtxo wasn't settled, we broadcast the checkpoint tx signed by both parties when the vtxo // was spent offchain. Otherwise, the forfeit tx created and signed during the batch execution is // broadcasted. -// -// The function takes a mutex to ensure that only one goroutine can react to a fraud at the same -// time. -func (s *service) reactToFraud(ctx context.Context, vtxo domain.Vtxo, mutx *sync.Mutex) error { - mutx.Lock() - defer mutx.Unlock() - +func (s *service) reactToFraud(ctx context.Context, vtxo domain.Vtxo) error { // If the vtxo wasn't settled we must broadcast a checkpoint tx. if !vtxo.IsSettled() { ptx, err := s.broadcastCheckpointTx(ctx, vtxo) diff --git a/internal/core/application/service.go b/internal/core/application/service.go index aec0785a2..4343297fe 100644 --- a/internal/core/application/service.go +++ b/internal/core/application/service.go @@ -290,11 +290,33 @@ func NewService( svc.propagateTransactionEvent(txEvent) - go func() { - if err := svc.startWatchingVtxos(newVtxos); err != nil { - log.WithError(err).Warn("failed to start watching vtxos") - } - }() + root := round.VtxoTree.Root() + if root != nil { + go func(rootNode *tree.TxTreeNode) { + rootPtx, err := psbt.NewFromRawBytes(strings.NewReader(rootNode.Tx), true) + if err != nil { + log.WithError(err).Warn("failed to parse root tx") + return + } + scripts := make([]string, 0, len(rootPtx.UnsignedTx.TxOut)) + for _, out := range rootPtx.UnsignedTx.TxOut { + if bytes.Equal(out.PkScript, txutils.ANCHOR_PKSCRIPT) { + continue + } + if script.IsSubDustScript(out.PkScript) { + continue + } + scripts = append(scripts, hex.EncodeToString(out.PkScript)) + } + if len(scripts) > 0 { + if err := svc.scanner.WatchScripts(context.Background(), scripts); err != nil { + log.WithError(err).Warn("failed to watch scripts") + return + } + log.Debugf("watching %d scripts", len(scripts)) + } + }(root) + } if lastEvent := events[len(events)-1]; lastEvent.GetType() != domain.EventTypeBatchSwept { go svc.scheduleSweepBatchOutput(round) @@ -343,19 +365,13 @@ func NewService( } svc.propagateTransactionEvent(txEvent) - - go func() { - if err := svc.startWatchingVtxos(newVtxos); err != nil { - log.WithError(err).Warn("failed to start watching vtxos") - } - }() }, ) - if err := svc.restoreWatchingVtxos(); err != nil { - return nil, fmt.Errorf("failed to restore watching vtxos: %s", err) + if err := svc.restoreWatchingScripts(); err != nil { + return nil, fmt.Errorf("failed to restore watching scripts: %s", err) } - go svc.listenToScannerNotifications() + go svc.listenToScannerNotifications(ctx) return svc, nil } @@ -384,22 +400,8 @@ func (s *service) Stop() { s.sweeperCancel() s.sweeper.stop() - commitmentTxIds, err := s.repoManager.Rounds().GetSweepableRounds(ctx) - if err == nil { - tapkeys := make([]string, 0) - - for _, commitmentTxId := range commitmentTxIds { - keys, err := s.repoManager.Vtxos(). - GetVtxoPubKeysByCommitmentTxid(ctx, commitmentTxId, 0) - if err != nil { - log.WithError(err).Warn("failed to get vtxo tap keys") - continue - } - - tapkeys = append(tapkeys, keys...) - } - - s.stopWatchingVtxos(tapkeys) + if err := s.scanner.UnwatchAllScripts(ctx); err != nil { + log.WithError(err).Warn("failed to unwatch scripts") } // nolint @@ -3332,88 +3334,6 @@ func (s *service) finalizeRound(roundId string, roundTiming roundTiming) { log.Debugf("finalized round %s with commitment tx %s", roundId, commitmentTxid) } -func (s *service) listenToScannerNotifications() { - ctx := context.Background() - chVtxos := s.scanner.GetNotificationChannel(ctx) - - mutx := &sync.Mutex{} - for vtxoKeys := range chVtxos { - go func(vtxoKeys map[string][]ports.VtxoWithValue) { - for _, keys := range vtxoKeys { - for _, v := range keys { - outs := []domain.Outpoint{v.Outpoint} - vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, outs) - if err != nil { - log.WithError(err).Warn("failed to retrieve vtxos, skipping...") - return - } - if len(vtxos) <= 0 { - log.Warnf("vtxo %s not found, skipping...", v.String()) - return - } - - vtxo := vtxos[0] - - if vtxo.Preconfirmed { - go func() { - txs, err := s.repoManager.Rounds().GetTxsWithTxids( - ctx, []string{vtxo.Txid}, - ) - if err != nil { - log.WithError(err).Warn("failed to retrieve txs, skipping...") - return - } - - if len(txs) <= 0 { - log.Warnf("tx %s not found", vtxo.Txid) - return - } - - ptx, err := psbt.NewFromRawBytes(strings.NewReader(txs[0]), true) - if err != nil { - log.WithError(err).Warn("failed to parse tx, skipping...") - return - } - - // remove sweeper task for the associated checkpoint outputs - for _, in := range ptx.UnsignedTx.TxIn { - taskId := in.PreviousOutPoint.Hash.String() - s.sweeper.removeTask(taskId) - log.Debugf("sweeper: unscheduled task for tx %s", taskId) - } - }() - } - - if !vtxo.Unrolled { - go func() { - if err := s.repoManager.Vtxos().UnrollVtxos( - ctx, []domain.Outpoint{vtxo.Outpoint}, - ); err != nil { - log.WithError(err).Warnf( - "failed to mark vtxo %s as unrolled", vtxo.Outpoint.String(), - ) - } - - log.Debugf("vtxo %s unrolled", vtxo.Outpoint.String()) - }() - } - - if vtxo.Spent { - log.Infof("fraud detected on vtxo %s", vtxo.Outpoint.String()) - go func() { - if err := s.reactToFraud(ctx, vtxo, mutx); err != nil { - log.WithError(err).Warnf( - "failed to react to fraud for vtxo %s", vtxo.Outpoint.String(), - ) - } - }() - } - } - } - }(vtxoKeys) - } -} - func (s *service) propagateEvents(ctx context.Context, round *domain.Round) { lastEvent := round.Events()[len(round.Events())-1] events := make([]domain.Event, 0) @@ -3611,52 +3531,74 @@ func (s *service) getSpentVtxos(intents map[string]domain.Intent) []domain.Vtxo return vtxos } -func (s *service) startWatchingVtxos(vtxos []domain.Vtxo) error { - scripts, err := s.extractVtxosScriptsForScanner(vtxos) +func (s *service) restoreWatchingScripts() error { + ctx := context.Background() + + commitmentTxIds, err := s.repoManager.Rounds().GetSweepableRounds(ctx) if err != nil { return err } - if len(scripts) <= 0 { - return nil - } - - return s.scanner.WatchScripts(context.Background(), scripts) -} - -func (s *service) stopWatchingVtxos(tapkeys []string) { - scripts := make([]string, 0, len(tapkeys)) - for _, key := range tapkeys { - // script = OP_1 OP_PUSHBYTES_32 - scripts = append(scripts, fmt.Sprintf("5120%s", key)) - } + scripts := make([]string, 0) - if len(scripts) <= 0 { - return - } + for _, commitmentTxId := range commitmentTxIds { + flatVtxoTree, err := s.repoManager.Rounds().GetRoundVtxoTree(ctx, commitmentTxId) + if err != nil { + log.WithError(err).Warn("failed to get round vtxo tree") + continue + } - for { - if err := s.scanner.UnwatchScripts(context.Background(), scripts); err != nil { - log.WithError(err).Warn("failed to stop watching vtxos, retrying in a moment...") - time.Sleep(100 * time.Millisecond) + vtxoTree, err := tree.NewTxTree(flatVtxoTree) + if err != nil { + log.WithError(err).Warn("failed to create vtxo tree") continue } - log.Debugf("stopped watching %d vtxo scripts", len(tapkeys)) - break - } -} -func (s *service) restoreWatchingVtxos() error { - ctx := context.Background() + sweepableOutputs, err := findSweepableOutputs( + ctx, + s.wallet, + s.builder, + s.sweeper.scheduler.Unit(), + vtxoTree, + ) + if err != nil { + log.WithError(err).Warn("failed to find sweepable outputs") + continue + } - commitmentTxIds, err := s.repoManager.Rounds().GetSweepableRounds(ctx) - if err != nil { - return err - } + // if there are sweepable outputs, we need to watch the children batch scripts + if len(sweepableOutputs) > 0 { + for _, outputs := range sweepableOutputs { + for _, output := range outputs { + childrenTxs, err := s.repoManager.Rounds(). + GetChildrenTxs(ctx, output.Txid) + if err != nil { + log.WithError(err).Warn("failed to get children txs") + continue + } + for _, childTx := range childrenTxs { + ptx, err := psbt.NewFromRawBytes(strings.NewReader(childTx), true) + if err != nil { + log.WithError(err).Warn("failed to parse child tx") + continue + } + for _, output := range ptx.UnsignedTx.TxOut { + if bytes.Equal(output.PkScript, txutils.ANCHOR_PKSCRIPT) { + continue + } + if script.IsSubDustScript(output.PkScript) { + continue + } + scripts = append(scripts, hex.EncodeToString(output.PkScript)) + } + } + } + } - scripts := make([]string, 0) + continue + } - for _, commitmentTxId := range commitmentTxIds { + // else, fallback to the tapkeys of the vtxos tapKeys, err := s.repoManager.Vtxos().GetVtxoPubKeysByCommitmentTxid(ctx, commitmentTxId, 0) if err != nil { return err @@ -3675,57 +3617,10 @@ func (s *service) restoreWatchingVtxos() error { return err } - log.Debugf("restored watching %d vtxo scripts", len(scripts)) + log.Debugf("restored watching %d scripts", len(scripts)) return nil } -// extractVtxosScriptsForScanner extracts the scripts for the vtxos to be watched by the scanner -// it excludes subdust vtxos scripts and duplicates -// it logs errors and continues in order to not block the start/stop watching vtxos operations -func (s *service) extractVtxosScriptsForScanner(vtxos []domain.Vtxo) ([]string, error) { - dustLimit, err := s.wallet.GetDustAmount(context.Background()) - if err != nil { - return nil, err - } - - indexedScripts := make(map[string]struct{}) - scripts := make([]string, 0) - - for _, vtxo := range vtxos { - vtxoTapKeyBytes, err := hex.DecodeString(vtxo.PubKey) - if err != nil { - log.WithError(err).Warnf("failed to decode vtxo pubkey: %s", vtxo.PubKey) - continue - } - - vtxoTapKey, err := schnorr.ParsePubKey(vtxoTapKeyBytes) - if err != nil { - log.WithError(err).Warnf("failed to parse vtxo pubkey: %s", vtxo.PubKey) - continue - } - - if vtxo.Amount < dustLimit { - continue - } - - p2trScript, err := script.P2TRScript(vtxoTapKey) - if err != nil { - log.WithError(err). - Warnf("failed to compute P2TR script from vtxo pubkey: %s", vtxo.PubKey) - continue - } - - scriptHex := hex.EncodeToString(p2trScript) - - if _, ok := indexedScripts[scriptHex]; !ok { - indexedScripts[scriptHex] = struct{}{} - scripts = append(scripts, scriptHex) - } - } - - return scripts, nil -} - func (s *service) saveEvents( ctx context.Context, id string, events []domain.Event, ) error { diff --git a/internal/core/application/sweeper.go b/internal/core/application/sweeper.go index 299cd3625..d6fed446b 100644 --- a/internal/core/application/sweeper.go +++ b/internal/core/application/sweeper.go @@ -212,6 +212,7 @@ func (s *sweeper) removeTask(id string) { s.locker.Lock() defer s.locker.Unlock() delete(s.scheduledTasks, id) + log.Debugf("sweeper: unscheduled task for tx %s", id) } func (s *sweeper) scheduleCheckpointSweep( diff --git a/internal/core/application/watcher.go b/internal/core/application/watcher.go new file mode 100644 index 000000000..1b3bb4965 --- /dev/null +++ b/internal/core/application/watcher.go @@ -0,0 +1,189 @@ +package application + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "strings" + + "github.com/arkade-os/arkd/internal/core/domain" + "github.com/arkade-os/arkd/pkg/ark-lib/script" + "github.com/arkade-os/arkd/pkg/ark-lib/txutils" + "github.com/btcsuite/btcd/btcutil/psbt" + log "github.com/sirupsen/logrus" +) + +func (s *service) listenToScannerNotifications(ctx context.Context) { + ch := s.scanner.GetNotificationChannel(ctx) + + go func() { + for { + select { + case <-ctx.Done(): + return + case notifications := <-ch: + for _, notification := range notifications { + for _, outpoint := range notification { + go func() { + defer func() { + if r := recover(); r != nil { + log.WithError(fmt.Errorf("panic: %v", r)). + Error("panic while processing notification") + } + }() + if err := s.onNotification(ctx, outpoint.Outpoint); err != nil { + log.WithError(err).Error("error while processing notification") + } + }() + } + } + } + } + }() +} + +func (s *service) onNotification(ctx context.Context, outpoint domain.Outpoint) error { + // check if the outpoint is a vtxo + vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, []domain.Outpoint{outpoint}) + if err != nil { + log.WithError(err).Warn("failed to retrieve vtxos, skipping...") + return err + } + + if len(vtxos) > 0 { + vtxo := vtxos[0] + + // if the vtxo is spent by an ark tx, we need to subscribe to the children scripts + if len(vtxo.ArkTxid) > 0 { + go func() { + offchainTx, err := s.repoManager.OffchainTxs().GetOffchainTx(ctx, vtxo.ArkTxid) + if err != nil { + log.WithError(err).Warn("failed to get offchain tx, skipping...") + return + } + + arkPtx, err := psbt.NewFromRawBytes(strings.NewReader(offchainTx.ArkTx), true) + if err != nil { + log.WithError(err).Warn("failed to parse ark tx, skipping...") + return + } + + scripts := make([]string, 0) + for _, out := range arkPtx.UnsignedTx.TxOut { + if bytes.Equal(out.PkScript, txutils.ANCHOR_PKSCRIPT) { + continue + } + if script.IsSubDustScript(out.PkScript) { + continue + } + scripts = append(scripts, hex.EncodeToString(out.PkScript)) + } + + if err := s.wallet.WatchScripts(ctx, scripts); err != nil { + log.WithError(err).Warn("failed to watch scripts, skipping...") + return + } + }() + } + + // the vtxo is onchain, we need to update DB and sweeper state + if err := s.onVtxoOnchain(ctx, vtxo); err != nil { + return err + } + + return nil + } + + // if no vtxo found, it's a batch outpoint, we want to subscribe to the children scripts + + // we don't want to subscribe several times for the same batch outpoint + // handling the treeTxid:0 is enough + if outpoint.VOut > 0 { + return nil + } + + txs, err := s.repoManager.Rounds().GetChildrenTxs(ctx, outpoint.Txid) + if err != nil { + return err + } + if len(txs) == 0 { + return fmt.Errorf("no children txs found for batch outpoint %s", outpoint.String()) + } + + scripts := make([]string, 0) + for _, tx := range txs { + ptx, err := psbt.NewFromRawBytes(strings.NewReader(tx), true) + if err != nil { + return fmt.Errorf("failed to parse tx: %s", err) + } + for _, out := range ptx.UnsignedTx.TxOut { + if bytes.Equal(out.PkScript, txutils.ANCHOR_PKSCRIPT) { + continue + } + scripts = append(scripts, hex.EncodeToString(out.PkScript)) + } + } + + if err := s.wallet.WatchScripts(ctx, scripts); err != nil { + return fmt.Errorf("failed to watch scripts: %s", err) + } + + return nil +} + +func (s *service) onVtxoOnchain(ctx context.Context, vtxo domain.Vtxo) error { + if vtxo.Preconfirmed { + go func() { + txs, err := s.repoManager.Rounds().GetTxsWithTxids(ctx, []string{vtxo.Txid}) + if err != nil { + log.WithError(err).Warn("failed to get txs, skipping...") + return + } + if len(txs) == 0 { + log.Warn("tx not found, skipping...") + return + } + + ptx, err := psbt.NewFromRawBytes(strings.NewReader(txs[0]), true) + if err != nil { + log.WithError(err).Warn("failed to parse tx, skipping...") + return + } + + // An ark tx hit the chain, the related checkpoint txs were previously scheduled for the sweep + // and we can now remove those tasks. + for _, in := range ptx.UnsignedTx.TxIn { + taskId := in.PreviousOutPoint.Hash.String() + s.sweeper.removeTask(taskId) + } + }() + } + + if !vtxo.Unrolled { + go func() { + if err := s.repoManager.Vtxos().UnrollVtxos( + ctx, []domain.Outpoint{vtxo.Outpoint}, + ); err != nil { + log.WithError(err).Warnf( + "failed to mark vtxo %s as unrolled", vtxo.Outpoint.String(), + ) + } + + log.Debugf("vtxo %s unrolled", vtxo.Outpoint.String()) + }() + } + + if vtxo.Spent { + log.Infof("fraud detected on vtxo %s", vtxo.Outpoint.String()) + go func() { + if err := s.reactToFraud(ctx, vtxo); err != nil { + log.WithError(err).Warnf( + "failed to react to fraud for vtxo %s", vtxo.Outpoint.String(), + ) + } + }() + } + + return nil +} diff --git a/internal/core/domain/offchain_tx.go b/internal/core/domain/offchain_tx.go index 37da1b13d..9bd835ace 100644 --- a/internal/core/domain/offchain_tx.go +++ b/internal/core/domain/offchain_tx.go @@ -27,11 +27,6 @@ func (s OffchainTxStage) String() string { } } -type Tx struct { - Txid string - Str string -} - type OffchainTx struct { Stage Stage StartingTimestamp int64 diff --git a/internal/core/domain/round_repo.go b/internal/core/domain/round_repo.go index 52640689f..918f7480d 100644 --- a/internal/core/domain/round_repo.go +++ b/internal/core/domain/round_repo.go @@ -24,6 +24,7 @@ type RoundRepository interface { GetSweptRoundsConnectorAddress(ctx context.Context) ([]string, error) GetTxsWithTxids(ctx context.Context, txids []string) ([]string, error) GetRoundsWithCommitmentTxids(ctx context.Context, txids []string) (map[string]any, error) + GetChildrenTxs(ctx context.Context, txid string) ([]string, error) Close() } diff --git a/internal/core/ports/scanner.go b/internal/core/ports/scanner.go index 5703da257..69d3c6821 100644 --- a/internal/core/ports/scanner.go +++ b/internal/core/ports/scanner.go @@ -6,7 +6,7 @@ import ( "golang.org/x/net/context" ) -type VtxoWithValue struct { +type OutpointWithValue struct { domain.Outpoint Value uint64 } @@ -14,7 +14,8 @@ type VtxoWithValue struct { type BlockchainScanner interface { WatchScripts(ctx context.Context, scripts []string) error UnwatchScripts(ctx context.Context, scripts []string) error - GetNotificationChannel(ctx context.Context) <-chan map[string][]VtxoWithValue + UnwatchAllScripts(ctx context.Context) error + GetNotificationChannel(ctx context.Context) <-chan map[string][]OutpointWithValue IsTransactionConfirmed( ctx context.Context, txid string, ) (isConfirmed bool, blockTimestamp *BlockTimestamp, err error) diff --git a/internal/infrastructure/db/badger/ark_repo.go b/internal/infrastructure/db/badger/ark_repo.go index 355e89a42..baeed49a6 100644 --- a/internal/infrastructure/db/badger/ark_repo.go +++ b/internal/infrastructure/db/badger/ark_repo.go @@ -228,6 +228,40 @@ func (r *arkRepository) GetOffchainTx( return r.getOffchainTx(ctx, txid) } +func (r *arkRepository) GetChildrenTxs(ctx context.Context, txid string, +) ([]string, error) { + rounds, err := r.findRound( + ctx, + badgerhold.Where("Stage.Code").Eq(int(domain.RoundFinalizationStage)). + And("Stage.Ended").Eq(true), + ) + if err != nil { + return nil, err + } + for _, round := range rounds { + foundNode, err := findInVtxoTree(round.VtxoTree, txid) + if err != nil { + continue + } + + if foundNode != nil { + txs := make([]string, 0, len(foundNode.Children)) + for _, child := range foundNode.Children { + childNode, err := findInVtxoTree(round.VtxoTree, child) + if err != nil { + return nil, err + } + if childNode != nil { + txs = append(txs, childNode.Tx) + } + } + return txs, nil + } + } + + return nil, nil +} + func (r *arkRepository) Close() { // nolint r.store.Close() @@ -526,3 +560,12 @@ func (r arkRepository) findOffchainTxs(ctx context.Context, txids []string) ([]s } return txs, nil } + +func findInVtxoTree(tree tree.FlatTxTree, txid string) (*tree.TxTreeNode, error) { + for _, node := range tree { + if node.Txid == txid { + return &node, nil + } + } + return nil, fmt.Errorf("node not found in tree") +} diff --git a/internal/infrastructure/db/postgres/round_repo.go b/internal/infrastructure/db/postgres/round_repo.go index 0e16b5a93..3b3c54590 100644 --- a/internal/infrastructure/db/postgres/round_repo.go +++ b/internal/infrastructure/db/postgres/round_repo.go @@ -422,6 +422,10 @@ func (r *roundRepository) GetRoundsWithCommitmentTxids( return resp, nil } +func (r *roundRepository) GetChildrenTxs(ctx context.Context, txid string) ([]string, error) { + return r.querier.SelectChildrenTxs(ctx, txid) +} + func rowToReceiver(row queries.IntentWithReceiversVw) domain.Receiver { return domain.Receiver{ Amount: uint64(row.Amount.Int64), diff --git a/internal/infrastructure/db/postgres/sqlc/queries/query.sql.go b/internal/infrastructure/db/postgres/sqlc/queries/query.sql.go index 12f0aee6b..f5d1e167b 100644 --- a/internal/infrastructure/db/postgres/sqlc/queries/query.sql.go +++ b/internal/infrastructure/db/postgres/sqlc/queries/query.sql.go @@ -159,6 +159,39 @@ func (q *Queries) SelectAllVtxos(ctx context.Context) ([]SelectAllVtxosRow, erro return items, nil } +const selectChildrenTxs = `-- name: SelectChildrenTxs :many +SELECT t1.tx FROM tx t1 +WHERE t1.txid = ANY ( + SELECT jsonb_array_elements_text(jsonb_path_query_array(children, '$.*')) + FROM tx t2 + WHERE t2.type = 'tree' + AND t2.txid = $1 +) +` + +func (q *Queries) SelectChildrenTxs(ctx context.Context, txid string) ([]string, error) { + rows, err := q.db.QueryContext(ctx, selectChildrenTxs, txid) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var tx string + if err := rows.Scan(&tx); err != nil { + return nil, err + } + items = append(items, tx) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const selectConviction = `-- name: SelectConviction :one SELECT id, type, created_at, expires_at, crime_type, crime_round_id, crime_reason, pardoned, script FROM conviction WHERE id = $1 ` diff --git a/internal/infrastructure/db/postgres/sqlc/query.sql b/internal/infrastructure/db/postgres/sqlc/query.sql index df5dd54e9..1e73dc27d 100644 --- a/internal/infrastructure/db/postgres/sqlc/query.sql +++ b/internal/infrastructure/db/postgres/sqlc/query.sql @@ -235,6 +235,15 @@ SELECT offchain_tx.txid, offchain_tx.tx AS data FROM offchain_tx WHERE offchain_ UNION SELECT checkpoint_tx.txid, checkpoint_tx.tx AS data FROM checkpoint_tx WHERE checkpoint_tx.txid = ANY($1::varchar[]); +-- name: SelectChildrenTxs :many +SELECT t1.tx FROM tx t1 +WHERE t1.txid = ANY ( + SELECT jsonb_array_elements_text(jsonb_path_query_array(children, '$.*')) + FROM tx t2 + WHERE t2.type = 'tree' + AND t2.txid = @txid +); + -- name: SelectNotUnrolledVtxos :many SELECT sqlc.embed(vtxo_vw) FROM vtxo_vw WHERE unrolled = false; diff --git a/internal/infrastructure/db/service_test.go b/internal/infrastructure/db/service_test.go index e7d8b88fa..5b17a8b12 100644 --- a/internal/infrastructure/db/service_test.go +++ b/internal/infrastructure/db/service_test.go @@ -40,43 +40,81 @@ const ( ) var ( + rootTxid = randomString(32) + txid1 = randomString(32) + txid2 = randomString(32) + txid3 = randomString(32) + txid4 = randomString(32) + txid5 = randomString(32) + txid6 = randomString(32) + txid7 = randomString(32) + txid8 = randomString(32) + txid9 = randomString(32) vtxoTree = tree.FlatTxTree{ { - Txid: randomString(32), - Tx: randomTx(), - Children: nil, + Txid: rootTxid, + Tx: randomTx(), + Children: map[uint32]string{ + 0: txid1, + 1: txid2, + }, }, { - Txid: randomString(32), + Txid: txid1, Tx: randomTx(), Children: map[uint32]string{ - 0: randomString(32), + 0: txid3, }, }, { - Txid: randomString(32), + Txid: txid2, Tx: randomTx(), Children: map[uint32]string{ - 0: randomString(32), - 1: randomString(32), + 0: txid4, + 1: txid5, }, }, { - Txid: randomString(32), + Txid: txid3, Tx: randomTx(), Children: map[uint32]string{ - 0: randomString(32), - 1: randomString(32), + 0: txid6, + 1: txid7, }, }, { - Txid: randomString(32), + Txid: txid4, Tx: randomTx(), Children: map[uint32]string{ - 0: randomString(32), - 1: randomString(32), + 0: txid8, + 1: txid9, }, }, + { + Txid: txid5, + Tx: randomTx(), + Children: nil, + }, + { + Txid: txid6, + Tx: randomTx(), + Children: nil, + }, + { + Txid: txid7, + Tx: randomTx(), + Children: nil, + }, + { + Txid: txid8, + Tx: randomTx(), + Children: nil, + }, + { + Txid: txid9, + Tx: randomTx(), + Children: nil, + }, } connectorsTree = tree.FlatTxTree{ { @@ -542,6 +580,44 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) { require.NotNil(t, txs) require.Equal(t, 3, len(txs)) + // Test GetChildrenTxs with a tree node that has children + treeNodeWithChildren := vtxoTree[2] // Has 2 children + childrenTxs, err := svc.Rounds().GetChildrenTxs(ctx, treeNodeWithChildren.Txid) + require.NoError(t, err) + require.NotNil(t, childrenTxs) + require.Len(t, childrenTxs, len(treeNodeWithChildren.Children)) + + // Verify that returned transactions match the child transactions + expectedChildTxs := make(map[string]bool) + for _, childTxid := range treeNodeWithChildren.Children { + // Find the child node in the tree + for _, node := range finalizedRound.VtxoTree { + if node.Txid == childTxid { + expectedChildTxs[node.Tx] = true + break + } + } + } + require.Len(t, expectedChildTxs, len(childrenTxs)) + for _, childTx := range childrenTxs { + require.True( + t, + expectedChildTxs[childTx], + "Child transaction not found in expected transactions", + ) + } + + // Test GetChildrenTxs with a tree node that has no children + treeNodeWithoutChildren := vtxoTree[len(vtxoTree)-1] // Has no children + childrenTxs, err = svc.Rounds().GetChildrenTxs(ctx, treeNodeWithoutChildren.Txid) + require.NoError(t, err) + require.Empty(t, childrenTxs) + + // Test GetChildrenTxs with non-existent txid + childrenTxs, err = svc.Rounds().GetChildrenTxs(ctx, randomString(32)) + require.NoError(t, err) + require.Empty(t, childrenTxs) + sweepableRounds, err := svc.Rounds().GetSweepableRounds(ctx) require.NoError(t, err) require.Len(t, sweepableRounds, 1) diff --git a/internal/infrastructure/db/sqlite/round_repo.go b/internal/infrastructure/db/sqlite/round_repo.go index 5d02addd6..361cf5ee9 100644 --- a/internal/infrastructure/db/sqlite/round_repo.go +++ b/internal/infrastructure/db/sqlite/round_repo.go @@ -453,6 +453,10 @@ func (r *roundRepository) GetRoundsWithCommitmentTxids( return resp, nil } +func (r *roundRepository) GetChildrenTxs(ctx context.Context, txid string) ([]string, error) { + return r.querier.SelectChildrenTxs(ctx, txid) +} + func rowToReceiver(row queries.IntentWithReceiversVw) domain.Receiver { return domain.Receiver{ Amount: uint64(row.Amount.Int64), diff --git a/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go b/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go index 9c7127fa6..66954d517 100644 --- a/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go +++ b/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go @@ -157,6 +157,39 @@ func (q *Queries) SelectAllVtxos(ctx context.Context) ([]SelectAllVtxosRow, erro return items, nil } +const selectChildrenTxs = `-- name: SelectChildrenTxs :many +SELECT t1.tx FROM tx t1 +WHERE t1.txid IN ( + SELECT json_each.value + FROM tx t2, json_each(t2.children) + WHERE t2.type = 'tree' + AND t2.txid = ?1 +) +` + +func (q *Queries) SelectChildrenTxs(ctx context.Context, txid string) ([]string, error) { + rows, err := q.db.QueryContext(ctx, selectChildrenTxs, txid) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var tx string + if err := rows.Scan(&tx); err != nil { + return nil, err + } + items = append(items, tx) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const selectConviction = `-- name: SelectConviction :one SELECT id, type, created_at, expires_at, crime_type, crime_round_id, crime_reason, pardoned, script FROM conviction WHERE id = ?1 ` diff --git a/internal/infrastructure/db/sqlite/sqlc/query.sql b/internal/infrastructure/db/sqlite/sqlc/query.sql index 0617f4679..f23d285d8 100644 --- a/internal/infrastructure/db/sqlite/sqlc/query.sql +++ b/internal/infrastructure/db/sqlite/sqlc/query.sql @@ -240,6 +240,15 @@ SELECT offchain_tx.txid, offchain_tx.tx AS data FROM offchain_tx WHERE offchain_ UNION SELECT checkpoint_tx.txid, checkpoint_tx.tx AS data FROM checkpoint_tx WHERE checkpoint_tx.txid IN (sqlc.slice('ids3')); +-- name: SelectChildrenTxs :many +SELECT t1.tx FROM tx t1 +WHERE t1.txid IN ( + SELECT json_each.value + FROM tx t2, json_each(t2.children) + WHERE t2.type = 'tree' + AND t2.txid = @txid +); + -- name: SelectNotUnrolledVtxos :many SELECT sqlc.embed(vtxo_vw) FROM vtxo_vw WHERE unrolled = false; diff --git a/internal/infrastructure/tx-builder/covenantless/mocks_test.go b/internal/infrastructure/tx-builder/covenantless/mocks_test.go index 51e830453..1b948b776 100644 --- a/internal/infrastructure/tx-builder/covenantless/mocks_test.go +++ b/internal/infrastructure/tx-builder/covenantless/mocks_test.go @@ -249,14 +249,19 @@ func (m *mockedWallet) UnwatchScripts( return args.Error(0) } +func (m *mockedWallet) UnwatchAllScripts(ctx context.Context) error { + args := m.Called(ctx) + return args.Error(0) +} + func (m *mockedWallet) GetNotificationChannel( ctx context.Context, -) <-chan map[string][]ports.VtxoWithValue { +) <-chan map[string][]ports.OutpointWithValue { args := m.Called(ctx) - var res <-chan map[string][]ports.VtxoWithValue + var res <-chan map[string][]ports.OutpointWithValue if a := args.Get(0); a != nil { - res = a.(<-chan map[string][]ports.VtxoWithValue) + res = a.(<-chan map[string][]ports.OutpointWithValue) } return res } diff --git a/internal/infrastructure/wallet/wallet_client.go b/internal/infrastructure/wallet/wallet_client.go index 2ad752660..ef358dbf7 100644 --- a/internal/infrastructure/wallet/wallet_client.go +++ b/internal/infrastructure/wallet/wallet_client.go @@ -109,6 +109,11 @@ func (w *walletDaemonClient) UnwatchScripts(ctx context.Context, scripts []strin return err } +func (w *walletDaemonClient) UnwatchAllScripts(ctx context.Context) error { + _, err := w.client.UnwatchAllScripts(ctx, &arkwalletv1.UnwatchAllScriptsRequest{}) + return err +} + func (w *walletDaemonClient) SignMessage(ctx context.Context, message []byte) ([]byte, error) { resp, err := w.client.SignMessage(ctx, &arkwalletv1.SignMessageRequest{Message: message}) if err != nil { @@ -119,8 +124,8 @@ func (w *walletDaemonClient) SignMessage(ctx context.Context, message []byte) ([ func (w *walletDaemonClient) GetNotificationChannel( ctx context.Context, -) <-chan map[string][]ports.VtxoWithValue { - ch := make(chan map[string][]ports.VtxoWithValue) +) <-chan map[string][]ports.OutpointWithValue { + ch := make(chan map[string][]ports.OutpointWithValue) stream, err := w.client.NotificationStream(ctx, &arkwalletv1.NotificationStreamRequest{}) if err != nil { close(ch) @@ -141,11 +146,11 @@ func (w *walletDaemonClient) GetNotificationChannel( log.WithError(err).Warnf("failed to receive notification") return } - m := make(map[string][]ports.VtxoWithValue) + m := make(map[string][]ports.OutpointWithValue) for _, entry := range resp.Entries { - vtxos := make([]ports.VtxoWithValue, 0, len(entry.Vtxos)) + vtxos := make([]ports.OutpointWithValue, 0, len(entry.Vtxos)) for _, v := range entry.Vtxos { - vtxos = append(vtxos, ports.VtxoWithValue{ + vtxos = append(vtxos, ports.OutpointWithValue{ Outpoint: domain.Outpoint{ Txid: v.Txid, VOut: v.Vout, diff --git a/internal/test/e2e/e2e_test.go b/internal/test/e2e/e2e_test.go index f6d7316ff..2c7c329b9 100644 --- a/internal/test/e2e/e2e_test.go +++ b/internal/test/e2e/e2e_test.go @@ -2364,6 +2364,109 @@ func TestReactToFraud(t *testing.T) { require.NoError(t, err) require.NotContains(t, aliceVtxos, vtxoToFraud) }) + + t.Run("with arkd restart", func(t *testing.T) { + ctx := context.Background() + indexerSvc := setupIndexer(t) + sdkClient := setupArkSDK(t) + defer sdkClient.Stop() + + _, offchainAddress, boardingAddress, err := sdkClient.Receive(ctx) + require.NoError(t, err) + + faucetOnchain(t, boardingAddress, 0.00021) + time.Sleep(5 * time.Second) + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + vtxos, err := sdkClient.NotifyIncomingFunds(ctx, offchainAddress) + require.NoError(t, err) + require.NotNil(t, vtxos) + }() + + roundId, err := sdkClient.Settle(ctx) + require.NoError(t, err) + + wg.Wait() + time.Sleep(5 * time.Second) + + err = generateBlocks(1) + require.NoError(t, err) + + wg.Add(1) + go func() { + defer wg.Done() + vtxos, err := sdkClient.NotifyIncomingFunds(ctx, offchainAddress) + require.NoError(t, err) + require.NotNil(t, vtxos) + }() + + _, err = sdkClient.SendOffChain( + ctx, false, []types.Receiver{{To: offchainAddress, Amount: 1000}}, + ) + require.NoError(t, err) + + wg.Wait() + + time.Sleep(5 * time.Second) + + wg.Add(1) + go func() { + defer wg.Done() + vtxos, err := sdkClient.NotifyIncomingFunds(ctx, offchainAddress) + require.NoError(t, err) + require.NotNil(t, vtxos) + }() + _, err = sdkClient.Settle(ctx) + require.NoError(t, err) + + wg.Wait() + + _, spentVtxos, err := sdkClient.ListVtxos(ctx) + require.NoError(t, err) + require.NotEmpty(t, spentVtxos) + + var vtxo types.Vtxo + for _, v := range spentVtxos { + if !v.Preconfirmed && v.CommitmentTxids[0] == roundId { + vtxo = v + break + } + } + require.NotEmpty(t, vtxo) + + expl, err := mempool_explorer.NewExplorer( + "http://localhost:3000", arklib.BitcoinRegTest, + mempool_explorer.WithTracker(false), + ) + require.NoError(t, err) + + // restart arkd to test fraud detection after restart + err = restartArkd() + require.NoError(t, err) + + time.Sleep(5 * time.Second) + + branch, err := redemption.NewRedeemBranch(ctx, expl, indexerSvc, vtxo) + require.NoError(t, err) + + for parentTx, err := branch.NextRedeemTx(); err == nil; parentTx, err = branch.NextRedeemTx() { + bumpAndBroadcastTx(t, parentTx, expl) + } + + err = generateBlocks(30) + require.NoError(t, err) + + // Give time for the server to detect and process the fraud + time.Sleep(5 * time.Second) + + balance, err := sdkClient.Balance(ctx) + require.NoError(t, err) + + require.Empty(t, balance.OnchainBalance.LockedAmount) + }) }) } diff --git a/pkg/ark-lib/tree/tx_tree.go b/pkg/ark-lib/tree/tx_tree.go index d5578568a..67de5d6e6 100644 --- a/pkg/ark-lib/tree/tx_tree.go +++ b/pkg/ark-lib/tree/tx_tree.go @@ -35,9 +35,9 @@ type TxTreeNode struct { // The purpose of this struct is to facilitate the persistence of the tx tree in storage services. type FlatTxTree []TxTreeNode -func (c FlatTxTree) RootTxid() string { +func (c FlatTxTree) Root() *TxTreeNode { if len(c) == 1 { - return c[0].Txid + return &c[0] } // the root is the node not being a child of another one @@ -50,11 +50,19 @@ func (c FlatTxTree) RootTxid() string { for _, node := range c { if _, ok := allchildren[node.Txid]; !ok { - return node.Txid + return &node } } - return "" + return nil +} + +func (c FlatTxTree) RootTxid() string { + root := c.Root() + if root == nil { + return "" + } + return root.Txid } func (c FlatTxTree) Leaves() []TxTreeNode { diff --git a/pkg/arkd-wallet-btcwallet/interface/grpc/handlers/wallet_handler.go b/pkg/arkd-wallet-btcwallet/interface/grpc/handlers/wallet_handler.go index d68c718b8..17db79b53 100644 --- a/pkg/arkd-wallet-btcwallet/interface/grpc/handlers/wallet_handler.go +++ b/pkg/arkd-wallet-btcwallet/interface/grpc/handlers/wallet_handler.go @@ -97,6 +97,12 @@ func (h *WalletServiceHandler) UnwatchScripts( return &arkwalletv1.UnwatchScriptsResponse{}, nil } +func (h *WalletServiceHandler) UnwatchAllScripts( + ctx context.Context, request *arkwalletv1.UnwatchAllScriptsRequest, +) (*arkwalletv1.UnwatchAllScriptsResponse, error) { + return nil, fmt.Errorf("not implemented") +} + func (h *WalletServiceHandler) DeriveConnectorAddress( ctx context.Context, _ *arkwalletv1.DeriveConnectorAddressRequest, ) (*arkwalletv1.DeriveConnectorAddressResponse, error) { diff --git a/pkg/arkd-wallet/core/application/scanner/service.go b/pkg/arkd-wallet/core/application/scanner/service.go index 222a3695c..e1ebc1adc 100644 --- a/pkg/arkd-wallet/core/application/scanner/service.go +++ b/pkg/arkd-wallet/core/application/scanner/service.go @@ -108,6 +108,10 @@ func (s *scanner) UnwatchScripts(ctx context.Context, scripts []string) error { return s.nbxplorer.UnwatchAddresses(ctx, addresses...) } +func (s *scanner) UnwatchAllScripts(ctx context.Context) error { + return s.nbxplorer.UnwatchAllAddresses(ctx) +} + func (s *scanner) RescanUtxos(ctx context.Context, outpoints []wire.OutPoint) error { return s.nbxplorer.RescanUtxos(ctx, outpoints) } diff --git a/pkg/arkd-wallet/core/application/types.go b/pkg/arkd-wallet/core/application/types.go index 15d881be5..d03ed090c 100644 --- a/pkg/arkd-wallet/core/application/types.go +++ b/pkg/arkd-wallet/core/application/types.go @@ -52,6 +52,7 @@ type WalletService interface { type BlockchainScanner interface { WatchScripts(ctx context.Context, scripts []string) error UnwatchScripts(ctx context.Context, scripts []string) error + UnwatchAllScripts(ctx context.Context) error GetNotificationChannel(ctx context.Context) <-chan map[string][]Utxo IsTransactionConfirmed( ctx context.Context, txid string, diff --git a/pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go b/pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go index 7d0515200..079f1e5ff 100644 --- a/pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go +++ b/pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go @@ -631,6 +631,21 @@ func (n *nbxplorer) UnwatchAddresses(ctx context.Context, addresses ...string) e return nil } +// UnwatchAllAddresses removes all addresses from group via DELETE /v1/groups/{groupID}/children/delete endpoint. +func (n *nbxplorer) UnwatchAllAddresses(ctx context.Context) error { + if len(n.groupID) == 0 { + return fmt.Errorf("group ID is not set") + } + + endpoint := fmt.Sprintf("/v1/groups/%s", url.PathEscape(n.groupID)) + _, err := n.makeRequest(ctx, "DELETE", endpoint, nil) + if err != nil { + return fmt.Errorf("failed to delete group: %w", err) + } + n.groupID = "" + return nil +} + // GetAddressNotifications returns the channel where to listen for notifications about incoming // UTXOs for the watched addresses. // If no underlying group is set, an empty one will be created. diff --git a/pkg/arkd-wallet/core/ports/nbxplorer.go b/pkg/arkd-wallet/core/ports/nbxplorer.go index 2db70998e..577bc29fb 100644 --- a/pkg/arkd-wallet/core/ports/nbxplorer.go +++ b/pkg/arkd-wallet/core/ports/nbxplorer.go @@ -55,6 +55,7 @@ type Nbxplorer interface { IsSpent(ctx context.Context, outpoint wire.OutPoint) (spent bool, err error) WatchAddresses(ctx context.Context, addresses ...string) error UnwatchAddresses(ctx context.Context, addresses ...string) error + UnwatchAllAddresses(ctx context.Context) error GetAddressNotifications(ctx context.Context) (<-chan []Utxo, error) Close() error diff --git a/pkg/arkd-wallet/interface/grpc/handlers/wallet_handler.go b/pkg/arkd-wallet/interface/grpc/handlers/wallet_handler.go index 4fc39d034..6cf8201a5 100644 --- a/pkg/arkd-wallet/interface/grpc/handlers/wallet_handler.go +++ b/pkg/arkd-wallet/interface/grpc/handlers/wallet_handler.go @@ -118,6 +118,14 @@ func (h *WalletServiceHandler) UnwatchScripts( return &arkwalletv1.UnwatchScriptsResponse{}, nil } +func (h *WalletServiceHandler) UnwatchAllScripts( + ctx context.Context, _ *arkwalletv1.UnwatchAllScriptsRequest, +) (*arkwalletv1.UnwatchAllScriptsResponse, error) { + if err := h.scanner.UnwatchAllScripts(ctx); err != nil { + return nil, err + } + return &arkwalletv1.UnwatchAllScriptsResponse{}, nil +} func (h *WalletServiceHandler) DeriveConnectorAddress( ctx context.Context, _ *arkwalletv1.DeriveConnectorAddressRequest, ) (*arkwalletv1.DeriveConnectorAddressResponse, error) {