From a3deb2d5960d45422fb3542e56c5339c26177764 Mon Sep 17 00:00:00 2001 From: altafan <18440657+altafan@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:50:16 +0200 Subject: [PATCH 01/10] [SDK] Hotfix tx history order --- pkg/client-sdk/covenantless_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/client-sdk/covenantless_client.go b/pkg/client-sdk/covenantless_client.go index bbb3fb38e..758e7294c 100644 --- a/pkg/client-sdk/covenantless_client.go +++ b/pkg/client-sdk/covenantless_client.go @@ -756,7 +756,7 @@ func (a *covenantlessArkClient) GetTransactionHistory(ctx context.Context) ([]Tr if txi.CreatedAt.Equal(txj.CreatedAt) { return txi.Type > txj.Type } - return txi.CreatedAt.Before(txj.CreatedAt) + return txi.CreatedAt.After(txj.CreatedAt) }) return txs, nil From 26bcbc8163e0fb9e3ecd6326565a34be1ab8e65a Mon Sep 17 00:00:00 2001 From: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> Date: Tue, 1 Oct 2024 19:00:25 +0200 Subject: [PATCH 02/10] [SDK] Fix rest client and wasm (#341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixes Co-authored-by: João Bordalo * Fixes to rest client * Fixes to wasm --------- Co-authored-by: João Bordalo --- pkg/client-sdk/client/rest/client.go | 29 +++----- .../example/covenantless/wasm/README.md | 13 ++-- .../example/covenantless/wasm/index.html | 72 ++++++++++--------- pkg/client-sdk/wasm/browser/exports.go | 2 + pkg/client-sdk/wasm/browser/wrappers.go | 66 ++++++++++++++--- server/internal/core/application/covenant.go | 1 - .../internal/core/application/covenantless.go | 1 - 7 files changed, 115 insertions(+), 69 deletions(-) diff --git a/pkg/client-sdk/client/rest/client.go b/pkg/client-sdk/client/rest/client.go index 99d786319..d02ee98d3 100644 --- a/pkg/client-sdk/client/rest/client.go +++ b/pkg/client-sdk/client/rest/client.go @@ -38,6 +38,7 @@ func NewClient(aspUrl string) (client.ASPClient, error) { if err != nil { return nil, err } + // TODO: use twice the round interval. reqTimeout := 15 * time.Second treeCache := utils.NewCache[tree.CongestionTree]() @@ -223,25 +224,19 @@ func (a *restClient) SubmitSignedForfeitTxs( func (a *restClient) GetEventStream( ctx context.Context, paymentID string, ) (<-chan client.RoundEventChannel, func(), error) { + ctx, cancel := context.WithTimeout(ctx, a.requestTimeout) eventsCh := make(chan client.RoundEventChannel) - stopCh := make(chan struct{}) - go func(payID string) { + go func(payID string, eventsCh chan client.RoundEventChannel) { + ticker := time.NewTicker(1 * time.Second) defer close(eventsCh) - defer close(stopCh) - - timeout := time.After(a.requestTimeout) + defer ticker.Stop() for { select { - case <-stopCh: - return - case <-timeout: - eventsCh <- client.RoundEventChannel{ - Err: fmt.Errorf("timeout reached"), - } + case <-ctx.Done(): return - default: + case <-ticker.C: event, err := a.Ping(ctx, payID) if err != nil { eventsCh <- client.RoundEventChannel{ @@ -255,17 +250,11 @@ func (a *restClient) GetEventStream( Event: event, } } - - time.Sleep(1 * time.Second) } } - }(paymentID) - - close := func() { - stopCh <- struct{}{} - } + }(paymentID, eventsCh) - return eventsCh, close, nil + return eventsCh, cancel, nil } func (a *restClient) Ping( diff --git a/pkg/client-sdk/example/covenantless/wasm/README.md b/pkg/client-sdk/example/covenantless/wasm/README.md index aec4d085a..7b3d14dab 100644 --- a/pkg/client-sdk/example/covenantless/wasm/README.md +++ b/pkg/client-sdk/example/covenantless/wasm/README.md @@ -2,20 +2,23 @@ This example demonstrates how to compile ARK Go SDK to WebAssembly and use it in a web page. -1. Create a Go file with the main package, check [main.go](main.go). - -2. Copy `wasm_exec.js`: +1. Copy `wasm_exec.js` to a new directory: ```bash cp $(go env GOROOT)/misc/wasm/wasm_exec.js . ``` -3. Build the Go code to WebAssembly: +2. On the root directory of this repo, build the Go code to WebAssembly: ```bash - GOOS=js GOARCH=wasm go build -o main.wasm main.go + make build-wasm ``` +3. Move the wasm file to your directory + + ```bash + mv /pkg/client-sdk/build/ark-sdk.wasm . + 4. Load the WebAssembly module in a web page, check [index.html](index.html). 5. Serve the files: diff --git a/pkg/client-sdk/example/covenantless/wasm/index.html b/pkg/client-sdk/example/covenantless/wasm/index.html index 14ae0a15f..e1fb0c5b9 100644 --- a/pkg/client-sdk/example/covenantless/wasm/index.html +++ b/pkg/client-sdk/example/covenantless/wasm/index.html @@ -44,8 +44,7 @@ try { const addresses = await receive(); logMessage("Offchain address: " + addresses.offchainAddr); - logMessage("Onchain address: " + addresses.onchainAddr); - logMessage("If in regtest faucet onchain address: " + addresses.onchainAddr); + logMessage("Boarding address: " + addresses.boardingAddr); } catch (err) { logMessage("Receive error: " + err.message); } @@ -53,8 +52,10 @@ async function getBalance() { const bal = await balance(false); - logMessage("Onchain balance: " + bal.onchain_balance) - logMessage("Offchain balance: " + bal.offchain_balance) + logMessage("Offchain balance: " + bal.offchainBalance) + logMessage("Onchain balance: ") + logMessage(" Spendable: " + bal.onchainBalance.spendable) + logMessage(" Locked: " + bal.onchainBalance.locked) } @@ -88,6 +89,33 @@ } } + async function claimVtxos() { + const password = document.getElementById("c_password").value; + if (!password) { + logMessage("Claim error: password is required"); + return; + } + + try { + await unlock(password); + const txID = await claim(); + logMessage("Claimed money with tx ID: " + txID); + } catch (err) { + logMessage("Claim error: " + err.message); + } finally { + await lock(password); + } + } + + async function history() { + try { + const history = await getTransactionHistory(); + logMessage("Tx history: " + history); + } catch (err) { + logMessage("Tx history error: " + err.message); + } + } + async function config() { try { const aspUrl = await getAspUrl(); @@ -112,30 +140,6 @@ logMessage("Config error: " + err.message); } } - - async function board() { - const amountStr = document.getElementById("amount").value; - const amount = parseInt(amountStr, 10); - const password = document.getElementById("o_password").value; - if (!password) { - logMessage("Onboard error: password is required"); - return; - } - - try { - console.log("unlocking..."); - await unlock(password); - console.log(amount, password); - console.log("onboarding..."); - const txID = await onboard(amount); - logMessage("Onboarded with amount: " + amount + " and txID: " + txID + ", if in regtest mine a block"); - } catch (err) { - logMessage("Onboard error: " + err.message); - } finally { - await lock(password); - } - - } @@ -154,17 +158,19 @@

Wallet

-
- - - -
+
+ + +
+
+ +
diff --git a/pkg/client-sdk/wasm/browser/exports.go b/pkg/client-sdk/wasm/browser/exports.go index 25ee785dd..6c8d3c1af 100644 --- a/pkg/client-sdk/wasm/browser/exports.go +++ b/pkg/client-sdk/wasm/browser/exports.go @@ -30,8 +30,10 @@ func init() { js.Global().Set("receive", ReceiveWrapper()) js.Global().Set("sendOnChain", SendOnChainWrapper()) js.Global().Set("sendOffChain", SendOffChainWrapper()) + js.Global().Set("claim", ClaimWrapper()) js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper()) js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper()) + js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper()) js.Global().Set("log", LogWrapper()) js.Global().Set("getAspUrl", GetAspUrlWrapper()) diff --git a/pkg/client-sdk/wasm/browser/wrappers.go b/pkg/client-sdk/wasm/browser/wrappers.go index d5a17c13c..752ed1242 100644 --- a/pkg/client-sdk/wasm/browser/wrappers.go +++ b/pkg/client-sdk/wasm/browser/wrappers.go @@ -6,9 +6,12 @@ package browser import ( "context" "encoding/hex" + "encoding/json" "errors" "fmt" + "strconv" "syscall/js" + "time" arksdk "github.com/ark-network/ark/pkg/client-sdk" "github.com/ark-network/ark/pkg/client-sdk/wallet" @@ -119,21 +122,25 @@ func BalanceWrapper() js.Func { } var ( - onchainBalance int - offchainBalance int + onchainSpendableBalance int + onchainLockedBalance int + offchainBalance int ) - if resp == nil { - onchainBalance = 0 - offchainBalance = 0 - } else { - onchainBalance = int(resp.OnchainBalance.SpendableAmount) + if resp != nil { + onchainSpendableBalance = int(resp.OnchainBalance.SpendableAmount) + for _, b := range resp.OnchainBalance.LockedAmount { + onchainLockedBalance += int(b.Amount) + } offchainBalance = int(resp.OffchainBalance.Total) } result := map[string]interface{}{ - "onchain_balance": onchainBalance, - "offchain_balance": offchainBalance, + "onchainBalance": map[string]interface{}{ + "spendable": onchainSpendableBalance, + "locked": onchainLockedBalance, + }, + "offchainBalance": offchainBalance, } return js.ValueOf(result), nil @@ -204,6 +211,21 @@ func SendOffChainWrapper() js.Func { }) } +func ClaimWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + if len(args) != 0 { + return nil, errors.New("invalid number of args") + } + + resp, err := arkSdkClient.Claim(context.Background()) + if err != nil { + return nil, err + } + + return js.ValueOf(resp), nil + }) +} + func UnilateralRedeemWrapper() js.Func { return JSPromise(func(args []js.Value) (interface{}, error) { return nil, arkSdkClient.UnilateralRedeem(context.Background()) @@ -229,6 +251,32 @@ func CollaborativeRedeemWrapper() js.Func { }) } +func GetTransactionHistoryWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + history, err := arkSdkClient.GetTransactionHistory(context.Background()) + if err != nil { + return nil, err + } + rawHistory := make([]map[string]interface{}, 0) + for _, record := range history { + rawHistory = append(rawHistory, map[string]interface{}{ + "boardingTxid": record.BoardingTxid, + "roundTxid": record.RoundTxid, + "redeemTxid": record.RedeemTxid, + "amount": strconv.Itoa(int(record.Amount)), + "type": record.Type, + "isPending": record.IsPending, + "createdAt": record.CreatedAt.Format(time.RFC3339), + }) + } + result, err := json.MarshalIndent(rawHistory, "", " ") + if err != nil { + return nil, err + } + return js.ValueOf(string(result)), nil + }) +} + func GetAspUrlWrapper() js.Func { return js.FuncOf(func(this js.Value, p []js.Value) interface{} { data, _ := arkSdkClient.GetConfigData(context.Background()) diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index 7033ffe18..0cbc140e9 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -421,7 +421,6 @@ func (s *covenantService) startRound() { round := domain.NewRound(dustAmount) //nolint:all round.StartRegistration() - s.lastEvent = nil s.currentRound = round defer func() { diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index 801db282e..384d414b6 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -712,7 +712,6 @@ func (s *covenantlessService) startRound() { round := domain.NewRound(dustAmount) //nolint:all round.StartRegistration() - s.lastEvent = nil s.currentRound = round defer func() { From d37af7daf578ae34a9146a22a3f14ed012f36e7a Mon Sep 17 00:00:00 2001 From: Dusan Sekulic Date: Fri, 4 Oct 2024 12:23:16 +0200 Subject: [PATCH 03/10] Add GetTransactionsStream RPC (#345) * RPC GetPaymentsStream This introduces a new feature to the ArkService API that allows clients to subscribe to payment events. Here's a breakdown of the changes: 1. **OpenAPI Specification (`service.swagger.json`):** - A new endpoint `/v1/payments` is added to the API, supporting a `GET` operation for streaming payment events. - New definitions `v1GetPaymentsStreamResponse`, `v1RoundPayment`, and `v1AsyncPayment` are added to describe the structure of the streaming responses. 2. **Protobuf Definition (`service.proto`):** - Added a new RPC method `GetPaymentsStream` that streams `GetPaymentsStreamResponse` messages. - Defined new message types: `GetPaymentsStreamRequest`, `GetPaymentsStreamResponse`, `RoundPayment`, and `AsyncPayment`. 3. **Generated Protobuf Code (`service.pb.go`, `service.pb.gw.go`, `service_grpc.pb.go`):** - The generated code is updated to include the new RPC method and message types. - The gateway code includes functions to handle HTTP requests and responses for the new streaming endpoint. 4. **Application Logic (`covenant.go`, `covenantless.go`):** - New payment events channels are introduced (`paymentEventsCh`). - Payment events are propagated to these channels when a round is finalized or an async payment is completed. - New event types `RoundPaymentEvent` and `AsyncPaymentEvent` are defined, implementing a `PaymentEvent` interface. 5. **gRPC Handlers (`arkservice.go`):** - Added logic to handle `GetPaymentsStream` requests and manage payment listeners. - A new goroutine is started to listen to payment events and forward them to active listeners. Overall, this patch extends the ArkService to support real-time streaming of payment events, allowing clients to receive updates on both round payments and async payments as they occur. * Move emit events in updateVtxoSet & Use generics and parsers (#1) * Move sending event to updateVtxoSet * Use generics and parsers * pr review refactor * pr review refactor * fix --------- Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> --- .../swagger/ark/v1/service.swagger.json | 93 +++ api-spec/protobuf/ark/v1/service.proto | 26 + api-spec/protobuf/gen/ark/v1/service.pb.go | 673 ++++++++++++++---- api-spec/protobuf/gen/ark/v1/service.pb.gw.go | 50 ++ .../protobuf/gen/ark/v1/service_grpc.pb.go | 63 ++ server/internal/core/application/covenant.go | 54 +- .../internal/core/application/covenantless.go | 49 +- server/internal/core/application/types.go | 33 + .../interface/grpc/handlers/arkservice.go | 148 +++- .../interface/grpc/handlers/parser.go | 13 + 10 files changed, 996 insertions(+), 206 deletions(-) diff --git a/api-spec/openapi/swagger/ark/v1/service.swagger.json b/api-spec/openapi/swagger/ark/v1/service.swagger.json index 59488c071..66e550440 100644 --- a/api-spec/openapi/swagger/ark/v1/service.swagger.json +++ b/api-spec/openapi/swagger/ark/v1/service.swagger.json @@ -415,6 +415,37 @@ ] } }, + "/v1/transactions": { + "get": { + "operationId": "ArkService_GetTransactionsStream", + "responses": { + "200": { + "description": "A successful response.(streaming responses)", + "schema": { + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/v1GetTransactionsStreamResponse" + }, + "error": { + "$ref": "#/definitions/rpcStatus" + } + }, + "title": "Stream result of v1GetTransactionsStreamResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ArkService" + ] + } + }, "/v1/vtxos/{address}": { "get": { "operationId": "ArkService_ListVtxos", @@ -620,6 +651,17 @@ } } }, + "v1GetTransactionsStreamResponse": { + "type": "object", + "properties": { + "round": { + "$ref": "#/definitions/v1RoundTransaction" + }, + "redeem": { + "$ref": "#/definitions/v1RedeemTransaction" + } + } + }, "v1Input": { "type": "object", "properties": { @@ -727,6 +769,28 @@ } } }, + "v1RedeemTransaction": { + "type": "object", + "properties": { + "txid": { + "type": "string" + }, + "spentVtxos": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Outpoint" + } + }, + "spendableVtxos": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Vtxo" + } + } + } + }, "v1RegisterInputsForNextRoundRequest": { "type": "object", "properties": { @@ -896,6 +960,35 @@ ], "default": "ROUND_STAGE_UNSPECIFIED" }, + "v1RoundTransaction": { + "type": "object", + "properties": { + "txid": { + "type": "string" + }, + "spentVtxos": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Outpoint" + } + }, + "spendableVtxos": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Vtxo" + } + }, + "claimedBoardingUtxos": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1Outpoint" + } + } + } + }, "v1SubmitSignedForfeitTxsRequest": { "type": "object", "properties": { diff --git a/api-spec/protobuf/ark/v1/service.proto b/api-spec/protobuf/ark/v1/service.proto index 92ed368c2..2481ec46a 100755 --- a/api-spec/protobuf/ark/v1/service.proto +++ b/api-spec/protobuf/ark/v1/service.proto @@ -92,6 +92,11 @@ service ArkService { get: "/v1/vtxos/{address}" }; } + rpc GetTransactionsStream(GetTransactionsStreamRequest) returns (stream GetTransactionsStreamResponse) { + option (google.api.http) = { + get: "/v1/transactions" + }; + } } message GetInfoRequest {} @@ -321,3 +326,24 @@ message PendingPayment { string redeem_tx = 1; repeated string unconditional_forfeit_txs =2; } + +message GetTransactionsStreamRequest {} +message GetTransactionsStreamResponse { + oneof tx { + RoundTransaction round = 1; + RedeemTransaction redeem = 2; + } +} + +message RoundTransaction { + string txid = 1; + repeated Outpoint spent_vtxos = 2; + repeated Vtxo spendable_vtxos = 3; + repeated Outpoint claimed_boarding_utxos = 4; +} + +message RedeemTransaction { + string txid = 1; + repeated Outpoint spent_vtxos = 2; + repeated Vtxo spendable_vtxos = 3; +} \ No newline at end of file diff --git a/api-spec/protobuf/gen/ark/v1/service.pb.go b/api-spec/protobuf/gen/ark/v1/service.pb.go index b34b62f1a..e160681e4 100644 --- a/api-spec/protobuf/gen/ark/v1/service.pb.go +++ b/api-spec/protobuf/gen/ark/v1/service.pb.go @@ -2568,6 +2568,259 @@ func (x *PendingPayment) GetUnconditionalForfeitTxs() []string { return nil } +type GetTransactionsStreamRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetTransactionsStreamRequest) Reset() { + *x = GetTransactionsStreamRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ark_v1_service_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTransactionsStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTransactionsStreamRequest) ProtoMessage() {} + +func (x *GetTransactionsStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_ark_v1_service_proto_msgTypes[42] + 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 GetTransactionsStreamRequest.ProtoReflect.Descriptor instead. +func (*GetTransactionsStreamRequest) Descriptor() ([]byte, []int) { + return file_ark_v1_service_proto_rawDescGZIP(), []int{42} +} + +type GetTransactionsStreamResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Tx: + // + // *GetTransactionsStreamResponse_Round + // *GetTransactionsStreamResponse_Redeem + Tx isGetTransactionsStreamResponse_Tx `protobuf_oneof:"tx"` +} + +func (x *GetTransactionsStreamResponse) Reset() { + *x = GetTransactionsStreamResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ark_v1_service_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTransactionsStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTransactionsStreamResponse) ProtoMessage() {} + +func (x *GetTransactionsStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_ark_v1_service_proto_msgTypes[43] + 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 GetTransactionsStreamResponse.ProtoReflect.Descriptor instead. +func (*GetTransactionsStreamResponse) Descriptor() ([]byte, []int) { + return file_ark_v1_service_proto_rawDescGZIP(), []int{43} +} + +func (m *GetTransactionsStreamResponse) GetTx() isGetTransactionsStreamResponse_Tx { + if m != nil { + return m.Tx + } + return nil +} + +func (x *GetTransactionsStreamResponse) GetRound() *RoundTransaction { + if x, ok := x.GetTx().(*GetTransactionsStreamResponse_Round); ok { + return x.Round + } + return nil +} + +func (x *GetTransactionsStreamResponse) GetRedeem() *RedeemTransaction { + if x, ok := x.GetTx().(*GetTransactionsStreamResponse_Redeem); ok { + return x.Redeem + } + return nil +} + +type isGetTransactionsStreamResponse_Tx interface { + isGetTransactionsStreamResponse_Tx() +} + +type GetTransactionsStreamResponse_Round struct { + Round *RoundTransaction `protobuf:"bytes,1,opt,name=round,proto3,oneof"` +} + +type GetTransactionsStreamResponse_Redeem struct { + Redeem *RedeemTransaction `protobuf:"bytes,2,opt,name=redeem,proto3,oneof"` +} + +func (*GetTransactionsStreamResponse_Round) isGetTransactionsStreamResponse_Tx() {} + +func (*GetTransactionsStreamResponse_Redeem) isGetTransactionsStreamResponse_Tx() {} + +type RoundTransaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Txid string `protobuf:"bytes,1,opt,name=txid,proto3" json:"txid,omitempty"` + SpentVtxos []*Outpoint `protobuf:"bytes,2,rep,name=spent_vtxos,json=spentVtxos,proto3" json:"spent_vtxos,omitempty"` + SpendableVtxos []*Vtxo `protobuf:"bytes,3,rep,name=spendable_vtxos,json=spendableVtxos,proto3" json:"spendable_vtxos,omitempty"` + ClaimedBoardingUtxos []*Outpoint `protobuf:"bytes,4,rep,name=claimed_boarding_utxos,json=claimedBoardingUtxos,proto3" json:"claimed_boarding_utxos,omitempty"` +} + +func (x *RoundTransaction) Reset() { + *x = RoundTransaction{} + if protoimpl.UnsafeEnabled { + mi := &file_ark_v1_service_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RoundTransaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoundTransaction) ProtoMessage() {} + +func (x *RoundTransaction) ProtoReflect() protoreflect.Message { + mi := &file_ark_v1_service_proto_msgTypes[44] + 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 RoundTransaction.ProtoReflect.Descriptor instead. +func (*RoundTransaction) Descriptor() ([]byte, []int) { + return file_ark_v1_service_proto_rawDescGZIP(), []int{44} +} + +func (x *RoundTransaction) GetTxid() string { + if x != nil { + return x.Txid + } + return "" +} + +func (x *RoundTransaction) GetSpentVtxos() []*Outpoint { + if x != nil { + return x.SpentVtxos + } + return nil +} + +func (x *RoundTransaction) GetSpendableVtxos() []*Vtxo { + if x != nil { + return x.SpendableVtxos + } + return nil +} + +func (x *RoundTransaction) GetClaimedBoardingUtxos() []*Outpoint { + if x != nil { + return x.ClaimedBoardingUtxos + } + return nil +} + +type RedeemTransaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Txid string `protobuf:"bytes,1,opt,name=txid,proto3" json:"txid,omitempty"` + SpentVtxos []*Outpoint `protobuf:"bytes,2,rep,name=spent_vtxos,json=spentVtxos,proto3" json:"spent_vtxos,omitempty"` + SpendableVtxos []*Vtxo `protobuf:"bytes,3,rep,name=spendable_vtxos,json=spendableVtxos,proto3" json:"spendable_vtxos,omitempty"` +} + +func (x *RedeemTransaction) Reset() { + *x = RedeemTransaction{} + if protoimpl.UnsafeEnabled { + mi := &file_ark_v1_service_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RedeemTransaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RedeemTransaction) ProtoMessage() {} + +func (x *RedeemTransaction) ProtoReflect() protoreflect.Message { + mi := &file_ark_v1_service_proto_msgTypes[45] + 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 RedeemTransaction.ProtoReflect.Descriptor instead. +func (*RedeemTransaction) Descriptor() ([]byte, []int) { + return file_ark_v1_service_proto_rawDescGZIP(), []int{45} +} + +func (x *RedeemTransaction) GetTxid() string { + if x != nil { + return x.Txid + } + return "" +} + +func (x *RedeemTransaction) GetSpentVtxos() []*Outpoint { + if x != nil { + return x.SpentVtxos + } + return nil +} + +func (x *RedeemTransaction) GetSpendableVtxos() []*Vtxo { + if x != nil { + return x.SpendableVtxos + } + return nil +} + var File_ark_v1_service_proto protoreflect.FileDescriptor var file_ark_v1_service_proto_rawDesc = []byte{ @@ -2869,129 +3122,172 @@ var file_ark_v1_service_proto_rawDesc = []byte{ 0x78, 0x12, 0x3a, 0x0a, 0x19, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x2a, 0x98, 0x01, - 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, - 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, - 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, - 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, - 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, - 0x12, 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, - 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xe8, 0x0c, 0x0a, 0x0a, 0x41, 0x72, 0x6b, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, - 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, - 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, - 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, - 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, - 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, - 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, + 0x6e, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x22, 0x1e, 0x0a, + 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, + 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, + 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, + 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04, 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, + 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, + 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, + 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, + 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, + 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, + 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, + 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, + 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, + 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, + 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, + 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, + 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, + 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, + 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, + 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb, 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, + 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, + 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, + 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, + 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, - 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, - 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, - 0x6e, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, - 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, - 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, - 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, - 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, - 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, - 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, - 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, - 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, - 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x12, 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, - 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, - 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, + 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, + 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, + 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, + 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, + 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, + 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, + 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, + 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, + 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, - 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, - 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, - 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, - 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x7d, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, - 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, - 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, - 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, - 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, - 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, + 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, + 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, + 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, + 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, + 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, + 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, + 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, + 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, + 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, + 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, + 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, + 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, + 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -3007,7 +3303,7 @@ func file_ark_v1_service_proto_rawDescGZIP() []byte { } var file_ark_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_ark_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 42) +var file_ark_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 46) var file_ark_v1_service_proto_goTypes = []interface{}{ (RoundStage)(0), // 0: ark.v1.RoundStage (*GetInfoRequest)(nil), // 1: ark.v1.GetInfoRequest @@ -3052,6 +3348,10 @@ var file_ark_v1_service_proto_goTypes = []interface{}{ (*Node)(nil), // 40: ark.v1.Node (*Vtxo)(nil), // 41: ark.v1.Vtxo (*PendingPayment)(nil), // 42: ark.v1.PendingPayment + (*GetTransactionsStreamRequest)(nil), // 43: ark.v1.GetTransactionsStreamRequest + (*GetTransactionsStreamResponse)(nil), // 44: ark.v1.GetTransactionsStreamResponse + (*RoundTransaction)(nil), // 45: ark.v1.RoundTransaction + (*RedeemTransaction)(nil), // 46: ark.v1.RedeemTransaction } var file_ark_v1_service_proto_depIdxs = []int32{ 36, // 0: ark.v1.RegisterInputsForNextRoundRequest.inputs:type_name -> ark.v1.Input @@ -3081,39 +3381,48 @@ var file_ark_v1_service_proto_depIdxs = []int32{ 40, // 24: ark.v1.TreeLevel.nodes:type_name -> ark.v1.Node 35, // 25: ark.v1.Vtxo.outpoint:type_name -> ark.v1.Outpoint 42, // 26: ark.v1.Vtxo.pending_data:type_name -> ark.v1.PendingPayment - 1, // 27: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest - 3, // 28: ark.v1.ArkService.GetBoardingAddress:input_type -> ark.v1.GetBoardingAddressRequest - 5, // 29: ark.v1.ArkService.RegisterInputsForNextRound:input_type -> ark.v1.RegisterInputsForNextRoundRequest - 7, // 30: ark.v1.ArkService.RegisterOutputsForNextRound:input_type -> ark.v1.RegisterOutputsForNextRoundRequest - 9, // 31: ark.v1.ArkService.SubmitTreeNonces:input_type -> ark.v1.SubmitTreeNoncesRequest - 11, // 32: ark.v1.ArkService.SubmitTreeSignatures:input_type -> ark.v1.SubmitTreeSignaturesRequest - 13, // 33: ark.v1.ArkService.SubmitSignedForfeitTxs:input_type -> ark.v1.SubmitSignedForfeitTxsRequest - 15, // 34: ark.v1.ArkService.GetEventStream:input_type -> ark.v1.GetEventStreamRequest - 17, // 35: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest - 19, // 36: ark.v1.ArkService.CreatePayment:input_type -> ark.v1.CreatePaymentRequest - 21, // 37: ark.v1.ArkService.CompletePayment:input_type -> ark.v1.CompletePaymentRequest - 23, // 38: ark.v1.ArkService.GetRound:input_type -> ark.v1.GetRoundRequest - 25, // 39: ark.v1.ArkService.GetRoundById:input_type -> ark.v1.GetRoundByIdRequest - 27, // 40: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest - 2, // 41: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse - 4, // 42: ark.v1.ArkService.GetBoardingAddress:output_type -> ark.v1.GetBoardingAddressResponse - 6, // 43: ark.v1.ArkService.RegisterInputsForNextRound:output_type -> ark.v1.RegisterInputsForNextRoundResponse - 8, // 44: ark.v1.ArkService.RegisterOutputsForNextRound:output_type -> ark.v1.RegisterOutputsForNextRoundResponse - 10, // 45: ark.v1.ArkService.SubmitTreeNonces:output_type -> ark.v1.SubmitTreeNoncesResponse - 12, // 46: ark.v1.ArkService.SubmitTreeSignatures:output_type -> ark.v1.SubmitTreeSignaturesResponse - 14, // 47: ark.v1.ArkService.SubmitSignedForfeitTxs:output_type -> ark.v1.SubmitSignedForfeitTxsResponse - 16, // 48: ark.v1.ArkService.GetEventStream:output_type -> ark.v1.GetEventStreamResponse - 18, // 49: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse - 20, // 50: ark.v1.ArkService.CreatePayment:output_type -> ark.v1.CreatePaymentResponse - 22, // 51: ark.v1.ArkService.CompletePayment:output_type -> ark.v1.CompletePaymentResponse - 24, // 52: ark.v1.ArkService.GetRound:output_type -> ark.v1.GetRoundResponse - 26, // 53: ark.v1.ArkService.GetRoundById:output_type -> ark.v1.GetRoundByIdResponse - 28, // 54: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse - 41, // [41:55] is the sub-list for method output_type - 27, // [27:41] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 45, // 27: ark.v1.GetTransactionsStreamResponse.round:type_name -> ark.v1.RoundTransaction + 46, // 28: ark.v1.GetTransactionsStreamResponse.redeem:type_name -> ark.v1.RedeemTransaction + 35, // 29: ark.v1.RoundTransaction.spent_vtxos:type_name -> ark.v1.Outpoint + 41, // 30: ark.v1.RoundTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo + 35, // 31: ark.v1.RoundTransaction.claimed_boarding_utxos:type_name -> ark.v1.Outpoint + 35, // 32: ark.v1.RedeemTransaction.spent_vtxos:type_name -> ark.v1.Outpoint + 41, // 33: ark.v1.RedeemTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo + 1, // 34: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest + 3, // 35: ark.v1.ArkService.GetBoardingAddress:input_type -> ark.v1.GetBoardingAddressRequest + 5, // 36: ark.v1.ArkService.RegisterInputsForNextRound:input_type -> ark.v1.RegisterInputsForNextRoundRequest + 7, // 37: ark.v1.ArkService.RegisterOutputsForNextRound:input_type -> ark.v1.RegisterOutputsForNextRoundRequest + 9, // 38: ark.v1.ArkService.SubmitTreeNonces:input_type -> ark.v1.SubmitTreeNoncesRequest + 11, // 39: ark.v1.ArkService.SubmitTreeSignatures:input_type -> ark.v1.SubmitTreeSignaturesRequest + 13, // 40: ark.v1.ArkService.SubmitSignedForfeitTxs:input_type -> ark.v1.SubmitSignedForfeitTxsRequest + 15, // 41: ark.v1.ArkService.GetEventStream:input_type -> ark.v1.GetEventStreamRequest + 17, // 42: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest + 19, // 43: ark.v1.ArkService.CreatePayment:input_type -> ark.v1.CreatePaymentRequest + 21, // 44: ark.v1.ArkService.CompletePayment:input_type -> ark.v1.CompletePaymentRequest + 23, // 45: ark.v1.ArkService.GetRound:input_type -> ark.v1.GetRoundRequest + 25, // 46: ark.v1.ArkService.GetRoundById:input_type -> ark.v1.GetRoundByIdRequest + 27, // 47: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest + 43, // 48: ark.v1.ArkService.GetTransactionsStream:input_type -> ark.v1.GetTransactionsStreamRequest + 2, // 49: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse + 4, // 50: ark.v1.ArkService.GetBoardingAddress:output_type -> ark.v1.GetBoardingAddressResponse + 6, // 51: ark.v1.ArkService.RegisterInputsForNextRound:output_type -> ark.v1.RegisterInputsForNextRoundResponse + 8, // 52: ark.v1.ArkService.RegisterOutputsForNextRound:output_type -> ark.v1.RegisterOutputsForNextRoundResponse + 10, // 53: ark.v1.ArkService.SubmitTreeNonces:output_type -> ark.v1.SubmitTreeNoncesResponse + 12, // 54: ark.v1.ArkService.SubmitTreeSignatures:output_type -> ark.v1.SubmitTreeSignaturesResponse + 14, // 55: ark.v1.ArkService.SubmitSignedForfeitTxs:output_type -> ark.v1.SubmitSignedForfeitTxsResponse + 16, // 56: ark.v1.ArkService.GetEventStream:output_type -> ark.v1.GetEventStreamResponse + 18, // 57: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse + 20, // 58: ark.v1.ArkService.CreatePayment:output_type -> ark.v1.CreatePaymentResponse + 22, // 59: ark.v1.ArkService.CompletePayment:output_type -> ark.v1.CompletePaymentResponse + 24, // 60: ark.v1.ArkService.GetRound:output_type -> ark.v1.GetRoundResponse + 26, // 61: ark.v1.ArkService.GetRoundById:output_type -> ark.v1.GetRoundByIdResponse + 28, // 62: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse + 44, // 63: ark.v1.ArkService.GetTransactionsStream:output_type -> ark.v1.GetTransactionsStreamResponse + 49, // [49:64] is the sub-list for method output_type + 34, // [34:49] is the sub-list for method input_type + 34, // [34:34] is the sub-list for extension type_name + 34, // [34:34] is the sub-list for extension extendee + 0, // [0:34] is the sub-list for field type_name } func init() { file_ark_v1_service_proto_init() } @@ -3626,6 +3935,54 @@ func file_ark_v1_service_proto_init() { return nil } } + file_ark_v1_service_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTransactionsStreamRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ark_v1_service_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTransactionsStreamResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ark_v1_service_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoundTransaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ark_v1_service_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RedeemTransaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_ark_v1_service_proto_msgTypes[4].OneofWrappers = []interface{}{} file_ark_v1_service_proto_msgTypes[12].OneofWrappers = []interface{}{} @@ -3643,13 +4000,17 @@ func file_ark_v1_service_proto_init() { (*PingResponse_RoundSigning)(nil), (*PingResponse_RoundSigningNoncesGenerated)(nil), } + file_ark_v1_service_proto_msgTypes[43].OneofWrappers = []interface{}{ + (*GetTransactionsStreamResponse_Round)(nil), + (*GetTransactionsStreamResponse_Redeem)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ark_v1_service_proto_rawDesc, NumEnums: 1, - NumMessages: 42, + NumMessages: 46, NumExtensions: 0, NumServices: 1, }, diff --git a/api-spec/protobuf/gen/ark/v1/service.pb.gw.go b/api-spec/protobuf/gen/ark/v1/service.pb.gw.go index 2aae45194..6304976f9 100644 --- a/api-spec/protobuf/gen/ark/v1/service.pb.gw.go +++ b/api-spec/protobuf/gen/ark/v1/service.pb.gw.go @@ -482,6 +482,23 @@ func local_request_ArkService_ListVtxos_0(ctx context.Context, marshaler runtime } +func request_ArkService_GetTransactionsStream_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (ArkService_GetTransactionsStreamClient, runtime.ServerMetadata, error) { + var protoReq GetTransactionsStreamRequest + var metadata runtime.ServerMetadata + + stream, err := client.GetTransactionsStream(ctx, &protoReq) + if err != nil { + return nil, metadata, err + } + header, err := stream.Header() + if err != nil { + return nil, metadata, err + } + metadata.HeaderMD = header + return stream, metadata, nil + +} + // RegisterArkServiceHandlerServer registers the http handlers for service ArkService to "mux". // UnaryRPC :call ArkServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -821,6 +838,13 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, }) + mux.Handle("GET", pattern_ArkService_GetTransactionsStream_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") + _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + }) + return nil } @@ -1170,6 +1194,28 @@ func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, }) + mux.Handle("GET", pattern_ArkService_GetTransactionsStream_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/GetTransactionsStream", runtime.WithHTTPPathPattern("/v1/transactions")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ArkService_GetTransactionsStream_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ArkService_GetTransactionsStream_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1201,6 +1247,8 @@ var ( pattern_ArkService_GetRoundById_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 2}, []string{"v1", "round", "id"}, "")) pattern_ArkService_ListVtxos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "vtxos", "address"}, "")) + + pattern_ArkService_GetTransactionsStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "transactions"}, "")) ) var ( @@ -1231,4 +1279,6 @@ var ( forward_ArkService_GetRoundById_0 = runtime.ForwardResponseMessage forward_ArkService_ListVtxos_0 = runtime.ForwardResponseMessage + + forward_ArkService_GetTransactionsStream_0 = runtime.ForwardResponseStream ) diff --git a/api-spec/protobuf/gen/ark/v1/service_grpc.pb.go b/api-spec/protobuf/gen/ark/v1/service_grpc.pb.go index 98c74041a..6d4400e38 100644 --- a/api-spec/protobuf/gen/ark/v1/service_grpc.pb.go +++ b/api-spec/protobuf/gen/ark/v1/service_grpc.pb.go @@ -32,6 +32,7 @@ type ArkServiceClient interface { GetRound(ctx context.Context, in *GetRoundRequest, opts ...grpc.CallOption) (*GetRoundResponse, error) GetRoundById(ctx context.Context, in *GetRoundByIdRequest, opts ...grpc.CallOption) (*GetRoundByIdResponse, error) ListVtxos(ctx context.Context, in *ListVtxosRequest, opts ...grpc.CallOption) (*ListVtxosResponse, error) + GetTransactionsStream(ctx context.Context, in *GetTransactionsStreamRequest, opts ...grpc.CallOption) (ArkService_GetTransactionsStreamClient, error) } type arkServiceClient struct { @@ -191,6 +192,38 @@ func (c *arkServiceClient) ListVtxos(ctx context.Context, in *ListVtxosRequest, return out, nil } +func (c *arkServiceClient) GetTransactionsStream(ctx context.Context, in *GetTransactionsStreamRequest, opts ...grpc.CallOption) (ArkService_GetTransactionsStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &ArkService_ServiceDesc.Streams[1], "/ark.v1.ArkService/GetTransactionsStream", opts...) + if err != nil { + return nil, err + } + x := &arkServiceGetTransactionsStreamClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type ArkService_GetTransactionsStreamClient interface { + Recv() (*GetTransactionsStreamResponse, error) + grpc.ClientStream +} + +type arkServiceGetTransactionsStreamClient struct { + grpc.ClientStream +} + +func (x *arkServiceGetTransactionsStreamClient) Recv() (*GetTransactionsStreamResponse, error) { + m := new(GetTransactionsStreamResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // ArkServiceServer is the server API for ArkService service. // All implementations should embed UnimplementedArkServiceServer // for forward compatibility @@ -209,6 +242,7 @@ type ArkServiceServer interface { GetRound(context.Context, *GetRoundRequest) (*GetRoundResponse, error) GetRoundById(context.Context, *GetRoundByIdRequest) (*GetRoundByIdResponse, error) ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error) + GetTransactionsStream(*GetTransactionsStreamRequest, ArkService_GetTransactionsStreamServer) error } // UnimplementedArkServiceServer should be embedded to have forward compatible implementations. @@ -257,6 +291,9 @@ func (UnimplementedArkServiceServer) GetRoundById(context.Context, *GetRoundById func (UnimplementedArkServiceServer) ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListVtxos not implemented") } +func (UnimplementedArkServiceServer) GetTransactionsStream(*GetTransactionsStreamRequest, ArkService_GetTransactionsStreamServer) error { + return status.Errorf(codes.Unimplemented, "method GetTransactionsStream not implemented") +} // UnsafeArkServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ArkServiceServer will @@ -524,6 +561,27 @@ func _ArkService_ListVtxos_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _ArkService_GetTransactionsStream_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetTransactionsStreamRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ArkServiceServer).GetTransactionsStream(m, &arkServiceGetTransactionsStreamServer{stream}) +} + +type ArkService_GetTransactionsStreamServer interface { + Send(*GetTransactionsStreamResponse) error + grpc.ServerStream +} + +type arkServiceGetTransactionsStreamServer struct { + grpc.ServerStream +} + +func (x *arkServiceGetTransactionsStreamServer) Send(m *GetTransactionsStreamResponse) error { + return x.ServerStream.SendMsg(m) +} + // ArkService_ServiceDesc is the grpc.ServiceDesc for ArkService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -590,6 +648,11 @@ var ArkService_ServiceDesc = grpc.ServiceDesc{ Handler: _ArkService_GetEventStream_Handler, ServerStreams: true, }, + { + StreamName: "GetTransactionsStream", + Handler: _ArkService_GetTransactionsStream_Handler, + ServerStreams: true, + }, }, Metadata: "ark/v1/service.proto", } diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index 0cbc140e9..3f4b1534c 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -45,7 +45,8 @@ type covenantService struct { paymentRequests *paymentsMap forfeitTxs *forfeitTxsMap - eventsCh chan domain.RoundEvent + eventsCh chan domain.RoundEvent + transactionEventsCh chan TransactionEvent currentRoundLock sync.Mutex currentRound *domain.Round @@ -59,23 +60,30 @@ func NewCovenantService( builder ports.TxBuilder, scanner ports.BlockchainScanner, scheduler ports.SchedulerService, ) (Service, error) { - eventsCh := make(chan domain.RoundEvent) - paymentRequests := newPaymentsMap() - - forfeitTxs := newForfeitTxsMap(builder) pubkey, err := walletSvc.GetPubkey(context.Background()) if err != nil { return nil, fmt.Errorf("failed to fetch pubkey: %s", err) } - sweeper := newSweeper(walletSvc, repoManager, builder, scheduler) - svc := &covenantService{ - network, pubkey, - roundLifetime, roundInterval, unilateralExitDelay, boardingExitDelay, - walletSvc, repoManager, builder, scanner, sweeper, - paymentRequests, forfeitTxs, eventsCh, sync.Mutex{}, nil, nil, + network: network, + pubkey: pubkey, + roundLifetime: roundLifetime, + roundInterval: roundInterval, + unilateralExitDelay: unilateralExitDelay, + boardingExitDelay: boardingExitDelay, + wallet: walletSvc, + repoManager: repoManager, + builder: builder, + scanner: scanner, + sweeper: newSweeper(walletSvc, repoManager, builder, scheduler), + paymentRequests: newPaymentsMap(), + forfeitTxs: newForfeitTxsMap(builder), + eventsCh: make(chan domain.RoundEvent), + transactionEventsCh: make(chan TransactionEvent), + currentRoundLock: sync.Mutex{}, } + repoManager.RegisterEventsHandler( func(round *domain.Round) { go svc.propagateEvents(round) @@ -346,6 +354,10 @@ func (s *covenantService) GetEventsChannel(ctx context.Context) <-chan domain.Ro return s.eventsCh } +func (s *covenantService) GetTransactionEventsChannel(ctx context.Context) <-chan TransactionEvent { + return s.transactionEventsCh +} + func (s *covenantService) GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.Round, error) { return s.repoManager.Rounds().GetRoundWithTxid(ctx, poolTxid) } @@ -851,6 +863,26 @@ func (s *covenantService) updateVtxoSet(round *domain.Round) { } }() } + + go func() { + // nolint:all + tx, _ := psetv2.NewPsetFromBase64(round.UnsignedTx) + boardingInputs := make([]domain.VtxoKey, 0) + for _, in := range tx.Inputs { + if len(in.TapLeafScript) > 0 { + boardingInputs = append(boardingInputs, domain.VtxoKey{ + Txid: elementsutil.TxIDFromBytes(in.PreviousTxid), + VOut: in.PreviousTxIndex, + }) + } + } + s.transactionEventsCh <- RoundTransactionEvent{ + RoundTxID: round.Txid, + SpentVtxos: getSpentVtxos(round.Payments), + SpendableVtxos: s.getNewVtxos(round), + ClaimedBoardingInputs: boardingInputs, + } + }() } func (s *covenantService) propagateEvents(round *domain.Round) { diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index 384d414b6..f7dde6fb7 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -42,7 +42,8 @@ type covenantlessService struct { paymentRequests *paymentsMap forfeitTxs *forfeitTxsMap - eventsCh chan domain.RoundEvent + eventsCh chan domain.RoundEvent + transactionEventsCh chan TransactionEvent // cached data for the current round lastEvent domain.RoundEvent @@ -62,16 +63,11 @@ func NewCovenantlessService( builder ports.TxBuilder, scanner ports.BlockchainScanner, scheduler ports.SchedulerService, ) (Service, error) { - eventsCh := make(chan domain.RoundEvent) - paymentRequests := newPaymentsMap() - - forfeitTxs := newForfeitTxsMap(builder) pubkey, err := walletSvc.GetPubkey(context.Background()) if err != nil { return nil, fmt.Errorf("failed to fetch pubkey: %s", err) } - sweeper := newSweeper(walletSvc, repoManager, builder, scheduler) asyncPaymentsCache := make(map[string]struct { receivers []domain.Receiver expireAt int64 @@ -87,10 +83,11 @@ func NewCovenantlessService( repoManager: repoManager, builder: builder, scanner: scanner, - sweeper: sweeper, - paymentRequests: paymentRequests, - forfeitTxs: forfeitTxs, - eventsCh: eventsCh, + sweeper: newSweeper(walletSvc, repoManager, builder, scheduler), + paymentRequests: newPaymentsMap(), + forfeitTxs: newForfeitTxsMap(builder), + eventsCh: make(chan domain.RoundEvent), + transactionEventsCh: make(chan TransactionEvent), currentRoundLock: sync.Mutex{}, asyncPaymentsCache: asyncPaymentsCache, treeSigningSessions: make(map[string]*musigSigningSession), @@ -303,6 +300,14 @@ func (s *covenantlessService) CompleteAsyncPayment( delete(s.asyncPaymentsCache, redeemTxid) + go func() { + s.transactionEventsCh <- RedeemTransactionEvent{ + AsyncTxID: redeemTxid, + SpentVtxos: spentVtxos, + SpendableVtxos: vtxos, + } + }() + return nil } @@ -579,6 +584,10 @@ func (s *covenantlessService) GetEventsChannel(ctx context.Context) <-chan domai return s.eventsCh } +func (s *covenantlessService) GetTransactionEventsChannel(ctx context.Context) <-chan TransactionEvent { + return s.transactionEventsCh +} + func (s *covenantlessService) GetRoundByTxid(ctx context.Context, roundTxid string) (*domain.Round, error) { return s.repoManager.Rounds().GetRoundWithTxid(ctx, roundTxid) } @@ -1262,7 +1271,27 @@ func (s *covenantlessService) updateVtxoSet(round *domain.Round) { return } }() + } + go func() { + // nolint:all + tx, _ := psbt.NewFromRawBytes(strings.NewReader(round.UnsignedTx), true) + boardingInputs := make([]domain.VtxoKey, 0) + for i, in := range tx.Inputs { + if len(in.TaprootLeafScript) > 0 { + boardingInputs = append(boardingInputs, domain.VtxoKey{ + Txid: tx.UnsignedTx.TxIn[i].PreviousOutPoint.Hash.String(), + VOut: tx.UnsignedTx.TxIn[i].PreviousOutPoint.Index, + }) + } + } + s.transactionEventsCh <- RoundTransactionEvent{ + RoundTxID: round.Txid, + SpentVtxos: getSpentVtxos(round.Payments), + SpendableVtxos: s.getNewVtxos(round), + ClaimedBoardingInputs: boardingInputs, + } + }() } func (s *covenantlessService) propagateEvents(round *domain.Round) { diff --git a/server/internal/core/application/types.go b/server/internal/core/application/types.go index 81ece8e31..ea1440b10 100644 --- a/server/internal/core/application/types.go +++ b/server/internal/core/application/types.go @@ -50,6 +50,7 @@ type Service interface { ctx context.Context, roundID string, pubkey *secp256k1.PublicKey, signatures string, ) error + GetTransactionEventsChannel(ctx context.Context) <-chan TransactionEvent } type ServiceInfo struct { @@ -81,3 +82,35 @@ func (outpoint txOutpoint) GetTxid() string { func (outpoint txOutpoint) GetIndex() uint32 { return outpoint.vout } + +const ( + RoundTransaction TransactionEventType = "round_tx" + RedeemTransaction TransactionEventType = "redeem_tx" +) + +type TransactionEventType string + +type TransactionEvent interface { + Type() TransactionEventType +} + +type RoundTransactionEvent struct { + RoundTxID string + SpentVtxos []domain.VtxoKey + SpendableVtxos []domain.Vtxo + ClaimedBoardingInputs []domain.VtxoKey +} + +func (r RoundTransactionEvent) Type() TransactionEventType { + return RoundTransaction +} + +type RedeemTransactionEvent struct { + AsyncTxID string + SpentVtxos []domain.VtxoKey + SpendableVtxos []domain.Vtxo +} + +func (a RedeemTransactionEvent) Type() TransactionEventType { + return RedeemTransaction +} diff --git a/server/internal/interface/grpc/handlers/arkservice.go b/server/internal/interface/grpc/handlers/arkservice.go index a3ae1a46f..e8c52c947 100644 --- a/server/internal/interface/grpc/handlers/arkservice.go +++ b/server/internal/interface/grpc/handlers/arkservice.go @@ -15,27 +15,22 @@ import ( "google.golang.org/grpc/status" ) -type listener struct { - id string - done chan struct{} - ch chan *arkv1.GetEventStreamResponse -} - type handler struct { svc application.Service - listenersLock *sync.Mutex - listeners []*listener + eventsListenerHandler *listenerHanlder[*arkv1.GetEventStreamResponse] + transactionsListenerHandler *listenerHanlder[*arkv1.GetTransactionsStreamResponse] } func NewHandler(service application.Service) arkv1.ArkServiceServer { h := &handler{ - svc: service, - listenersLock: &sync.Mutex{}, - listeners: make([]*listener, 0), + svc: service, + eventsListenerHandler: newListenerHandler[*arkv1.GetEventStreamResponse](), + transactionsListenerHandler: newListenerHandler[*arkv1.GetTransactionsStreamResponse](), } go h.listenToEvents() + go h.listenToPaymentEvents() return h } @@ -234,14 +229,14 @@ func (h *handler) GetEventStream( ) error { doneCh := make(chan struct{}) - listener := &listener{ + listener := &listener[*arkv1.GetEventStreamResponse]{ id: uuid.NewString(), done: doneCh, ch: make(chan *arkv1.GetEventStreamResponse), } - h.pushListener(listener) - defer h.removeListener(listener.id) + h.eventsListenerHandler.pushListener(listener) + defer h.eventsListenerHandler.removeListener(listener.id) defer close(listener.ch) defer close(doneCh) @@ -488,21 +483,31 @@ func (h *handler) ListVtxos( }, nil } -func (h *handler) pushListener(l *listener) { - h.listenersLock.Lock() - defer h.listenersLock.Unlock() +func (h *handler) GetTransactionsStream( + _ *arkv1.GetTransactionsStreamRequest, + stream arkv1.ArkService_GetTransactionsStreamServer, +) error { + listener := &listener[*arkv1.GetTransactionsStreamResponse]{ + id: uuid.NewString(), + done: make(chan struct{}), + ch: make(chan *arkv1.GetTransactionsStreamResponse), + } - h.listeners = append(h.listeners, l) -} + h.transactionsListenerHandler.pushListener(listener) -func (h *handler) removeListener(id string) { - h.listenersLock.Lock() - defer h.listenersLock.Unlock() + defer func() { + h.transactionsListenerHandler.removeListener(listener.id) + close(listener.ch) + }() - for i, listener := range h.listeners { - if listener.id == id { - h.listeners = append(h.listeners[:i], h.listeners[i+1:]...) - return + for { + select { + case <-stream.Context().Done(): + return nil + case ev := <-listener.ch: + if err := stream.Send(ev); err != nil { + return err + } } } } @@ -582,9 +587,9 @@ func (h *handler) listenToEvents() { } if ev != nil { - logrus.Debugf("forwarding event to %d listeners", len(h.listeners)) - for _, l := range h.listeners { - go func(l *listener) { + logrus.Debugf("forwarding event to %d listeners", len(h.eventsListenerHandler.listeners)) + for _, l := range h.eventsListenerHandler.listeners { + go func(l *listener[*arkv1.GetEventStreamResponse]) { l.ch <- ev if shouldClose { l.done <- struct{}{} @@ -594,3 +599,88 @@ func (h *handler) listenToEvents() { } } } + +func (h *handler) listenToPaymentEvents() { + paymentEventsCh := h.svc.GetTransactionEventsChannel(context.Background()) + for event := range paymentEventsCh { + var paymentEvent *arkv1.GetTransactionsStreamResponse + + switch event.Type() { + case application.RoundTransaction: + paymentEvent = &arkv1.GetTransactionsStreamResponse{ + Tx: &arkv1.GetTransactionsStreamResponse_Round{ + Round: convertRoundPaymentEvent(event.(application.RoundTransactionEvent)), + }, + } + case application.RedeemTransaction: + paymentEvent = &arkv1.GetTransactionsStreamResponse{ + Tx: &arkv1.GetTransactionsStreamResponse_Redeem{ + Redeem: convertAsyncPaymentEvent(event.(application.RedeemTransactionEvent)), + }, + } + } + + if paymentEvent != nil { + logrus.Debugf("forwarding event to %d listeners", len(h.transactionsListenerHandler.listeners)) + for _, l := range h.transactionsListenerHandler.listeners { + go func(l *listener[*arkv1.GetTransactionsStreamResponse]) { + l.ch <- paymentEvent + }(l) + } + } + } +} + +func convertRoundPaymentEvent(e application.RoundTransactionEvent) *arkv1.RoundTransaction { + return &arkv1.RoundTransaction{ + Txid: e.RoundTxID, + SpentVtxos: vtxoKeyList(e.SpentVtxos).toProto(), + SpendableVtxos: vtxoList(e.SpendableVtxos).toProto(), + ClaimedBoardingUtxos: vtxoKeyList(e.ClaimedBoardingInputs).toProto(), + } +} + +func convertAsyncPaymentEvent(e application.RedeemTransactionEvent) *arkv1.RedeemTransaction { + return &arkv1.RedeemTransaction{ + Txid: e.AsyncTxID, + SpentVtxos: vtxoKeyList(e.SpentVtxos).toProto(), + SpendableVtxos: vtxoList(e.SpendableVtxos).toProto(), + } +} + +type listener[T any] struct { + id string + done chan struct{} + ch chan T +} + +type listenerHanlder[T any] struct { + lock *sync.Mutex + listeners []*listener[T] +} + +func newListenerHandler[T any]() *listenerHanlder[T] { + return &listenerHanlder[T]{ + lock: &sync.Mutex{}, + listeners: make([]*listener[T], 0), + } +} + +func (h *listenerHanlder[T]) pushListener(l *listener[T]) { + h.lock.Lock() + defer h.lock.Unlock() + + h.listeners = append(h.listeners, l) +} + +func (h *listenerHanlder[T]) removeListener(id string) { + h.lock.Lock() + defer h.lock.Unlock() + + for i, listener := range h.listeners { + if listener.id == id { + h.listeners = append(h.listeners[:i], h.listeners[i+1:]...) + return + } + } +} diff --git a/server/internal/interface/grpc/handlers/parser.go b/server/internal/interface/grpc/handlers/parser.go index 8ea1f5a38..c858577fe 100644 --- a/server/internal/interface/grpc/handlers/parser.go +++ b/server/internal/interface/grpc/handlers/parser.go @@ -92,6 +92,19 @@ func (v vtxoList) toProto() []*arkv1.Vtxo { return list } +type vtxoKeyList []domain.VtxoKey + +func (v vtxoKeyList) toProto() []*arkv1.Outpoint { + list := make([]*arkv1.Outpoint, 0, len(v)) + for _, vtxoKey := range v { + list = append(list, &arkv1.Outpoint{ + Txid: vtxoKey.Txid, + Vout: vtxoKey.VOut, + }) + } + return list +} + type congestionTree tree.CongestionTree func (t congestionTree) toProto() *arkv1.Tree { From 1d408921967fac01784275b8d98e12de775ca734 Mon Sep 17 00:00:00 2001 From: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:08:43 +0200 Subject: [PATCH 04/10] Add auto-unlocker (#347) * Add auto-unlocker * Fixes --- server/cmd/arkd/main.go | 6 ++- server/internal/app-config/config.go | 42 ++++++++++++++++++- server/internal/config/config.go | 18 +++++--- server/internal/core/ports/unlocker.go | 7 ++++ .../infrastructure/unlocker/file/service.go | 37 ++++++++++++++++ .../wallet/btc-embedded/wallet.go | 4 ++ server/internal/interface/grpc/service.go | 35 +++++++++++++++- 7 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 server/internal/core/ports/unlocker.go create mode 100644 server/internal/infrastructure/unlocker/file/service.go diff --git a/server/cmd/arkd/main.go b/server/cmd/arkd/main.go index 796a6871a..116ec596f 100755 --- a/server/cmd/arkd/main.go +++ b/server/cmd/arkd/main.go @@ -79,19 +79,21 @@ func mainAction(_ *cli.Context) error { BitcoindRpcPass: cfg.BitcoindRpcPass, BitcoindRpcHost: cfg.BitcoindRpcHost, BoardingExitDelay: cfg.BoardingExitDelay, + UnlockerType: cfg.UnlockerType, + UnlockerFilePath: cfg.UnlockerFilePath, } svc, err := grpcservice.NewService(svcConfig, appConfig) if err != nil { return err } - log.RegisterExitHandler(svc.Stop) - log.Info("starting service...") if err := svc.Start(); err != nil { return err } + log.RegisterExitHandler(svc.Stop) + sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, os.Interrupt) <-sigChan diff --git a/server/internal/app-config/config.go b/server/internal/app-config/config.go index 863418ed3..5e69960ad 100644 --- a/server/internal/app-config/config.go +++ b/server/internal/app-config/config.go @@ -11,6 +11,7 @@ import ( scheduler "github.com/ark-network/ark/server/internal/infrastructure/scheduler/gocron" txbuilder "github.com/ark-network/ark/server/internal/infrastructure/tx-builder/covenant" cltxbuilder "github.com/ark-network/ark/server/internal/infrastructure/tx-builder/covenantless" + fileunlocker "github.com/ark-network/ark/server/internal/infrastructure/unlocker/file" btcwallet "github.com/ark-network/ark/server/internal/infrastructure/wallet/btc-embedded" liquidwallet "github.com/ark-network/ark/server/internal/infrastructure/wallet/liquid-standalone" log "github.com/sirupsen/logrus" @@ -37,6 +38,9 @@ var ( "ocean": {}, "btcwallet": {}, } + supportedUnlockers = supportedType{ + "file": {}, + } supportedNetworks = supportedType{ common.Bitcoin.Name: {}, common.BitcoinTestNet.Name: {}, @@ -70,6 +74,9 @@ type Config struct { BitcoindRpcPass string BitcoindRpcHost string + UnlockerType string + UnlockerFilePath string + repo ports.RepoManager svc application.Service adminSvc application.AdminService @@ -77,6 +84,7 @@ type Config struct { txBuilder ports.TxBuilder scanner ports.BlockchainScanner scheduler ports.SchedulerService + unlocker ports.Unlocker } func (c *Config) Validate() error { @@ -95,6 +103,9 @@ func (c *Config) Validate() error { if !supportedScanners.supports(c.BlockchainScannerType) { return fmt.Errorf("blockchain scanner type not supported, please select one of: %s", supportedScanners) } + if len(c.UnlockerType) > 0 && !supportedUnlockers.supports(c.UnlockerType) { + return fmt.Errorf("unlocker type not supported, please select one of: %s", supportedUnlockers) + } if c.RoundInterval < 2 { return fmt.Errorf("invalid round interval, must be at least 2 seconds") } @@ -151,7 +162,7 @@ func (c *Config) Validate() error { return err } if err := c.walletService(); err != nil { - return fmt.Errorf("failed to connect to wallet: %s", err) + return err } if err := c.txBuilderService(); err != nil { return err @@ -165,6 +176,9 @@ func (c *Config) Validate() error { if err := c.adminService(); err != nil { return err } + if err := c.unlockerService(); err != nil { + return err + } return nil } @@ -185,6 +199,10 @@ func (c *Config) WalletService() ports.WalletService { return c.wallet } +func (c *Config) UnlockerService() ports.Unlocker { + return c.unlocker +} + func (c *Config) repoManager() error { var svc ports.RepoManager var err error @@ -227,7 +245,7 @@ func (c *Config) walletService() error { if common.IsLiquid(c.Network) { svc, err := liquidwallet.NewService(c.WalletAddr) if err != nil { - return err + return fmt.Errorf("failed to connect to wallet: %s", err) } c.wallet = svc @@ -353,6 +371,26 @@ func (c *Config) adminService() error { return nil } +func (c *Config) unlockerService() error { + if len(c.UnlockerType) <= 0 { + return nil + } + + var svc ports.Unlocker + var err error + switch c.UnlockerType { + case "file": + svc, err = fileunlocker.NewService(c.UnlockerFilePath) + default: + err = fmt.Errorf("unknown unlocker type") + } + if err != nil { + return err + } + c.unlocker = svc + return nil +} + type supportedType map[string]struct{} func (t supportedType) String() string { diff --git a/server/internal/config/config.go b/server/internal/config/config.go index eb1232962..ba44727a8 100644 --- a/server/internal/config/config.go +++ b/server/internal/config/config.go @@ -36,6 +36,8 @@ type Config struct { BitcoindRpcHost string TLSExtraIPs []string TLSExtraDomains []string + UnlockerType string + UnlockerFilePath string } var ( @@ -59,12 +61,14 @@ var ( // #nosec G101 BitcoindRpcUser = "BITCOIND_RPC_USER" // #nosec G101 - BitcoindRpcPass = "BITCOIND_RPC_PASS" - BitcoindRpcHost = "BITCOIND_RPC_HOST" - NoMacaroons = "NO_MACAROONS" - NoTLS = "NO_TLS" - TLSExtraIP = "TLS_EXTRA_IP" - TLSExtraDomain = "TLS_EXTRA_DOMAIN" + BitcoindRpcPass = "BITCOIND_RPC_PASS" + BitcoindRpcHost = "BITCOIND_RPC_HOST" + NoMacaroons = "NO_MACAROONS" + NoTLS = "NO_TLS" + TLSExtraIP = "TLS_EXTRA_IP" + TLSExtraDomain = "TLS_EXTRA_DOMAIN" + UnlockerType = "UNLOCKER_TYPE" + UnlockerFilePath = "UNLOCKER_FILE_PATH" defaultDatadir = common.AppDataDir("arkd", false) defaultRoundInterval = 5 @@ -142,6 +146,8 @@ func LoadConfig() (*Config, error) { NoMacaroons: viper.GetBool(NoMacaroons), TLSExtraIPs: viper.GetStringSlice(TLSExtraIP), TLSExtraDomains: viper.GetStringSlice(TLSExtraDomain), + UnlockerType: viper.GetString(UnlockerType), + UnlockerFilePath: viper.GetString(UnlockerFilePath), }, nil } diff --git a/server/internal/core/ports/unlocker.go b/server/internal/core/ports/unlocker.go new file mode 100644 index 000000000..7d5efa977 --- /dev/null +++ b/server/internal/core/ports/unlocker.go @@ -0,0 +1,7 @@ +package ports + +import "context" + +type Unlocker interface { + GetPassword(ctx context.Context) (string, error) +} diff --git a/server/internal/infrastructure/unlocker/file/service.go b/server/internal/infrastructure/unlocker/file/service.go new file mode 100644 index 000000000..65cca8496 --- /dev/null +++ b/server/internal/infrastructure/unlocker/file/service.go @@ -0,0 +1,37 @@ +package fileunlocker + +import ( + "bytes" + "context" + "fmt" + "os" + + "github.com/ark-network/ark/server/internal/core/ports" +) + +type service struct { + filePath string +} + +func NewService(filePath string) (ports.Unlocker, error) { + if _, err := os.Stat(filePath); err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("password file not found at path %s", filePath) + } + return nil, err + } + return &service{filePath: filePath}, nil +} + +func (s *service) GetPassword(_ context.Context) (string, error) { + buf, err := os.ReadFile(s.filePath) + if err != nil { + return "", err + } + + password := bytes.TrimFunc(buf, func(r rune) bool { + return r == 10 || r == 13 || r == 32 + }) + + return string(password), nil +} diff --git a/server/internal/infrastructure/wallet/btc-embedded/wallet.go b/server/internal/infrastructure/wallet/btc-embedded/wallet.go index 9d7c2fcd8..cea1923bf 100644 --- a/server/internal/infrastructure/wallet/btc-embedded/wallet.go +++ b/server/internal/infrastructure/wallet/btc-embedded/wallet.go @@ -304,6 +304,10 @@ func (s *service) Restore(_ context.Context, seed, password string) error { } func (s *service) Unlock(_ context.Context, password string) error { + if !s.walletInitialized() { + return fmt.Errorf("wallet not initialized") + } + if !s.walletLoaded() { pwd := []byte(password) opt := btcwallet.LoaderWithLocalWalletDB(s.cfg.Datadir, false, time.Minute) diff --git a/server/internal/interface/grpc/service.go b/server/internal/interface/grpc/service.go index fb7841df6..2d15c6f08 100644 --- a/server/internal/interface/grpc/service.go +++ b/server/internal/interface/grpc/service.go @@ -94,7 +94,13 @@ func NewService( func (s *service) Start() error { withoutAppSvc := false - return s.start(withoutAppSvc) + if err := s.start(withoutAppSvc); err != nil { + return err + } + if s.appConfig.UnlockerService() != nil { + return s.autoUnlock() + } + return nil } func (s *service) Stop() { @@ -314,6 +320,33 @@ func (s *service) onInit(password string) { log.Debugf("generated macaroons at path %s", datadir) } +func (s *service) autoUnlock() error { + ctx := context.Background() + wallet := s.appConfig.WalletService() + + status, err := wallet.Status(ctx) + if err != nil { + return fmt.Errorf("failed to get wallet status: %s", err) + } + if !status.IsInitialized() { + log.Debug("wallet not initiialized, skipping auto unlock") + return nil + } + + password, err := s.appConfig.UnlockerService().GetPassword(ctx) + if err != nil { + return fmt.Errorf("failed to get password: %s", err) + } + if err := wallet.Unlock(ctx, password); err != nil { + return fmt.Errorf("failed to auto unlock: %s", err) + } + + go s.onUnlock(password) + + log.Debug("service auto unlocked") + return nil +} + func router( grpcServer *grpc.Server, grpcGateway http.Handler, ) http.Handler { From 7606b4cd00bee27b94f9ea57f72efd688235bbb5 Mon Sep 17 00:00:00 2001 From: Louis Singer <41042567+louisinger@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:06:00 +0200 Subject: [PATCH 05/10] Drop unconditional forfeits txs in offline payment (#344) * remove unconditionnal forfeit tx * fix sqlite vtxo repo * remove pendingData struct * delete uncond_forfeits_tx table --- .../swagger/ark/v1/service.swagger.json | 30 +- api-spec/protobuf/ark/v1/service.proto | 8 +- api-spec/protobuf/gen/ark/v1/service.pb.go | 841 ++++++++---------- pkg/client-sdk/client/client.go | 19 +- pkg/client-sdk/client/grpc/client.go | 11 +- pkg/client-sdk/client/grpc/types.go | 21 +- pkg/client-sdk/client/rest/client.go | 36 +- .../ark_service/ark_service_client.go | 39 + ...vice_get_transactions_stream_parameters.go | 128 +++ ...rvice_get_transactions_stream_responses.go | 337 +++++++ .../models/v1_complete_payment_request.go | 3 - .../models/v1_create_payment_response.go | 3 - .../v1_get_transactions_stream_response.go | 160 ++++ .../rest/service/models/v1_pending_payment.go | 3 - .../service/models/v1_redeem_transaction.go | 186 ++++ .../service/models/v1_round_transaction.go | 248 ++++++ .../client/rest/service/models/v1_vtxo.go | 52 +- pkg/client-sdk/client_test.go | 26 +- pkg/client-sdk/covenantless_client.go | 5 +- server/internal/core/application/covenant.go | 6 +- .../internal/core/application/covenantless.go | 37 +- server/internal/core/application/types.go | 4 +- server/internal/core/domain/payment.go | 21 +- server/internal/core/ports/tx_builder.go | 2 +- .../migration/20240703120550_init.up.sql | 16 +- .../db/sqlite/sqlc/queries/models.go | 16 - .../db/sqlite/sqlc/queries/query.sql.go | 91 +- .../infrastructure/db/sqlite/sqlc/query.sql | 33 +- .../infrastructure/db/sqlite/vtxo_repo.go | 119 +-- .../tx-builder/covenant/builder.go | 4 +- .../tx-builder/covenantless/builder.go | 140 +-- .../interface/grpc/handlers/arkservice.go | 11 +- .../interface/grpc/handlers/parser.go | 9 +- 33 files changed, 1639 insertions(+), 1026 deletions(-) create mode 100644 pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_parameters.go create mode 100644 pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_responses.go create mode 100644 pkg/client-sdk/client/rest/service/models/v1_get_transactions_stream_response.go create mode 100644 pkg/client-sdk/client/rest/service/models/v1_redeem_transaction.go create mode 100644 pkg/client-sdk/client/rest/service/models/v1_round_transaction.go diff --git a/api-spec/openapi/swagger/ark/v1/service.swagger.json b/api-spec/openapi/swagger/ark/v1/service.swagger.json index 66e550440..36775ae81 100644 --- a/api-spec/openapi/swagger/ark/v1/service.swagger.json +++ b/api-spec/openapi/swagger/ark/v1/service.swagger.json @@ -511,12 +511,6 @@ "properties": { "signedRedeemTx": { "type": "string" - }, - "signedUnconditionalForfeitTxs": { - "type": "array", - "items": { - "type": "string" - } } } }, @@ -548,12 +542,6 @@ "signedRedeemTx": { "type": "string", "title": "signed only by the ASP" - }, - "usignedUnconditionalForfeitTxs": { - "type": "array", - "items": { - "type": "string" - } } } }, @@ -735,20 +723,6 @@ } } }, - "v1PendingPayment": { - "type": "object", - "properties": { - "redeemTx": { - "type": "string" - }, - "unconditionalForfeitTxs": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "v1PingResponse": { "type": "object", "properties": { @@ -1094,8 +1068,8 @@ "pending": { "type": "boolean" }, - "pendingData": { - "$ref": "#/definitions/v1PendingPayment" + "redeemTx": { + "type": "string" }, "amount": { "type": "string", diff --git a/api-spec/protobuf/ark/v1/service.proto b/api-spec/protobuf/ark/v1/service.proto index 2481ec46a..fae41efca 100755 --- a/api-spec/protobuf/ark/v1/service.proto +++ b/api-spec/protobuf/ark/v1/service.proto @@ -193,12 +193,10 @@ message CreatePaymentRequest { } message CreatePaymentResponse { string signed_redeem_tx = 1; // signed only by the ASP - repeated string usigned_unconditional_forfeit_txs = 2; } message CompletePaymentRequest { string signed_redeem_tx = 1; - repeated string signed_unconditional_forfeit_txs = 2; } message CompletePaymentResponse {} @@ -318,14 +316,10 @@ message Vtxo { int64 expire_at = 6; bool swept = 7; bool pending = 8; - PendingPayment pending_data = 9; + string redeem_tx = 9; uint64 amount = 10; } -message PendingPayment { - string redeem_tx = 1; - repeated string unconditional_forfeit_txs =2; -} message GetTransactionsStreamRequest {} message GetTransactionsStreamResponse { diff --git a/api-spec/protobuf/gen/ark/v1/service.pb.go b/api-spec/protobuf/gen/ark/v1/service.pb.go index e160681e4..9e7bbc9c8 100644 --- a/api-spec/protobuf/gen/ark/v1/service.pb.go +++ b/api-spec/protobuf/gen/ark/v1/service.pb.go @@ -1213,8 +1213,7 @@ type CreatePaymentResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SignedRedeemTx string `protobuf:"bytes,1,opt,name=signed_redeem_tx,json=signedRedeemTx,proto3" json:"signed_redeem_tx,omitempty"` // signed only by the ASP - UsignedUnconditionalForfeitTxs []string `protobuf:"bytes,2,rep,name=usigned_unconditional_forfeit_txs,json=usignedUnconditionalForfeitTxs,proto3" json:"usigned_unconditional_forfeit_txs,omitempty"` + SignedRedeemTx string `protobuf:"bytes,1,opt,name=signed_redeem_tx,json=signedRedeemTx,proto3" json:"signed_redeem_tx,omitempty"` // signed only by the ASP } func (x *CreatePaymentResponse) Reset() { @@ -1256,20 +1255,12 @@ func (x *CreatePaymentResponse) GetSignedRedeemTx() string { return "" } -func (x *CreatePaymentResponse) GetUsignedUnconditionalForfeitTxs() []string { - if x != nil { - return x.UsignedUnconditionalForfeitTxs - } - return nil -} - type CompletePaymentRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SignedRedeemTx string `protobuf:"bytes,1,opt,name=signed_redeem_tx,json=signedRedeemTx,proto3" json:"signed_redeem_tx,omitempty"` - SignedUnconditionalForfeitTxs []string `protobuf:"bytes,2,rep,name=signed_unconditional_forfeit_txs,json=signedUnconditionalForfeitTxs,proto3" json:"signed_unconditional_forfeit_txs,omitempty"` + SignedRedeemTx string `protobuf:"bytes,1,opt,name=signed_redeem_tx,json=signedRedeemTx,proto3" json:"signed_redeem_tx,omitempty"` } func (x *CompletePaymentRequest) Reset() { @@ -1311,13 +1302,6 @@ func (x *CompletePaymentRequest) GetSignedRedeemTx() string { return "" } -func (x *CompletePaymentRequest) GetSignedUnconditionalForfeitTxs() []string { - if x != nil { - return x.SignedUnconditionalForfeitTxs - } - return nil -} - type CompletePaymentResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2399,16 +2383,16 @@ type Vtxo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Outpoint *Outpoint `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` - Descriptor_ string `protobuf:"bytes,2,opt,name=descriptor,proto3" json:"descriptor,omitempty"` - Spent bool `protobuf:"varint,3,opt,name=spent,proto3" json:"spent,omitempty"` - RoundTxid string `protobuf:"bytes,4,opt,name=round_txid,json=roundTxid,proto3" json:"round_txid,omitempty"` - SpentBy string `protobuf:"bytes,5,opt,name=spent_by,json=spentBy,proto3" json:"spent_by,omitempty"` - ExpireAt int64 `protobuf:"varint,6,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` - Swept bool `protobuf:"varint,7,opt,name=swept,proto3" json:"swept,omitempty"` - Pending bool `protobuf:"varint,8,opt,name=pending,proto3" json:"pending,omitempty"` - PendingData *PendingPayment `protobuf:"bytes,9,opt,name=pending_data,json=pendingData,proto3" json:"pending_data,omitempty"` - Amount uint64 `protobuf:"varint,10,opt,name=amount,proto3" json:"amount,omitempty"` + Outpoint *Outpoint `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` + Descriptor_ string `protobuf:"bytes,2,opt,name=descriptor,proto3" json:"descriptor,omitempty"` + Spent bool `protobuf:"varint,3,opt,name=spent,proto3" json:"spent,omitempty"` + RoundTxid string `protobuf:"bytes,4,opt,name=round_txid,json=roundTxid,proto3" json:"round_txid,omitempty"` + SpentBy string `protobuf:"bytes,5,opt,name=spent_by,json=spentBy,proto3" json:"spent_by,omitempty"` + ExpireAt int64 `protobuf:"varint,6,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` + Swept bool `protobuf:"varint,7,opt,name=swept,proto3" json:"swept,omitempty"` + Pending bool `protobuf:"varint,8,opt,name=pending,proto3" json:"pending,omitempty"` + RedeemTx string `protobuf:"bytes,9,opt,name=redeem_tx,json=redeemTx,proto3" json:"redeem_tx,omitempty"` + Amount uint64 `protobuf:"varint,10,opt,name=amount,proto3" json:"amount,omitempty"` } func (x *Vtxo) Reset() { @@ -2499,11 +2483,11 @@ func (x *Vtxo) GetPending() bool { return false } -func (x *Vtxo) GetPendingData() *PendingPayment { +func (x *Vtxo) GetRedeemTx() string { if x != nil { - return x.PendingData + return x.RedeemTx } - return nil + return "" } func (x *Vtxo) GetAmount() uint64 { @@ -2513,61 +2497,6 @@ func (x *Vtxo) GetAmount() uint64 { return 0 } -type PendingPayment struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RedeemTx string `protobuf:"bytes,1,opt,name=redeem_tx,json=redeemTx,proto3" json:"redeem_tx,omitempty"` - UnconditionalForfeitTxs []string `protobuf:"bytes,2,rep,name=unconditional_forfeit_txs,json=unconditionalForfeitTxs,proto3" json:"unconditional_forfeit_txs,omitempty"` -} - -func (x *PendingPayment) Reset() { - *x = PendingPayment{} - if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[41] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PendingPayment) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PendingPayment) ProtoMessage() {} - -func (x *PendingPayment) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[41] - 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 PendingPayment.ProtoReflect.Descriptor instead. -func (*PendingPayment) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{41} -} - -func (x *PendingPayment) GetRedeemTx() string { - if x != nil { - return x.RedeemTx - } - return "" -} - -func (x *PendingPayment) GetUnconditionalForfeitTxs() []string { - if x != nil { - return x.UnconditionalForfeitTxs - } - return nil -} - type GetTransactionsStreamRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2577,7 +2506,7 @@ type GetTransactionsStreamRequest struct { func (x *GetTransactionsStreamRequest) Reset() { *x = GetTransactionsStreamRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[42] + mi := &file_ark_v1_service_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2590,7 +2519,7 @@ func (x *GetTransactionsStreamRequest) String() string { func (*GetTransactionsStreamRequest) ProtoMessage() {} func (x *GetTransactionsStreamRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[42] + mi := &file_ark_v1_service_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2603,7 +2532,7 @@ func (x *GetTransactionsStreamRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTransactionsStreamRequest.ProtoReflect.Descriptor instead. func (*GetTransactionsStreamRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{42} + return file_ark_v1_service_proto_rawDescGZIP(), []int{41} } type GetTransactionsStreamResponse struct { @@ -2621,7 +2550,7 @@ type GetTransactionsStreamResponse struct { func (x *GetTransactionsStreamResponse) Reset() { *x = GetTransactionsStreamResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[43] + mi := &file_ark_v1_service_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2634,7 +2563,7 @@ func (x *GetTransactionsStreamResponse) String() string { func (*GetTransactionsStreamResponse) ProtoMessage() {} func (x *GetTransactionsStreamResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[43] + mi := &file_ark_v1_service_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2647,7 +2576,7 @@ func (x *GetTransactionsStreamResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTransactionsStreamResponse.ProtoReflect.Descriptor instead. func (*GetTransactionsStreamResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{43} + return file_ark_v1_service_proto_rawDescGZIP(), []int{42} } func (m *GetTransactionsStreamResponse) GetTx() isGetTransactionsStreamResponse_Tx { @@ -2701,7 +2630,7 @@ type RoundTransaction struct { func (x *RoundTransaction) Reset() { *x = RoundTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[44] + mi := &file_ark_v1_service_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2714,7 +2643,7 @@ func (x *RoundTransaction) String() string { func (*RoundTransaction) ProtoMessage() {} func (x *RoundTransaction) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[44] + mi := &file_ark_v1_service_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2727,7 +2656,7 @@ func (x *RoundTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundTransaction.ProtoReflect.Descriptor instead. func (*RoundTransaction) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{44} + return file_ark_v1_service_proto_rawDescGZIP(), []int{43} } func (x *RoundTransaction) GetTxid() string { @@ -2771,7 +2700,7 @@ type RedeemTransaction struct { func (x *RedeemTransaction) Reset() { *x = RedeemTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[45] + mi := &file_ark_v1_service_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2784,7 +2713,7 @@ func (x *RedeemTransaction) String() string { func (*RedeemTransaction) ProtoMessage() {} func (x *RedeemTransaction) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[45] + mi := &file_ark_v1_service_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2797,7 +2726,7 @@ func (x *RedeemTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemTransaction.ProtoReflect.Descriptor instead. func (*RedeemTransaction) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{45} + return file_ark_v1_service_proto_rawDescGZIP(), []int{44} } func (x *RedeemTransaction) GetTxid() string { @@ -2976,318 +2905,300 @@ var file_ark_v1_service_proto_rawDesc = []byte{ 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, - 0x22, 0x8c, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x64, 0x65, - 0x65, 0x6d, 0x54, 0x78, 0x12, 0x49, 0x0a, 0x21, 0x75, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, - 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x6f, - 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x1e, 0x75, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x22, - 0x8b, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x64, 0x65, - 0x65, 0x6d, 0x54, 0x78, 0x12, 0x47, 0x0a, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x75, - 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x6f, 0x72, - 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1d, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x22, 0x19, 0x0a, - 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, - 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, - 0x37, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x25, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, - 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x3b, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x10, - 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x79, 0x0a, 0x11, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, - 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, - 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x2d, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, - 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, - 0x56, 0x74, 0x78, 0x6f, 0x73, 0x22, 0xbb, 0x01, 0x0a, 0x16, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x22, 0x41, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x64, 0x65, 0x65, + 0x6d, 0x54, 0x78, 0x22, 0x42, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, + 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, + 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, + 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x05, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x22, 0x25, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, + 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x14, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, + 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x22, 0x79, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, + 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, + 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, + 0x12, 0x2d, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, + 0x74, 0x78, 0x6f, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x22, + 0xbb, 0x01, 0x0a, 0x16, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, 0x09, 0x76, 0x74, 0x78, 0x6f, 0x5f, 0x74, 0x72, + 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, 0x76, 0x74, 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x66, 0x65, + 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6d, 0x69, + 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x44, 0x0a, + 0x13, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, + 0x78, 0x69, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xb8, 0x01, 0x0a, 0x11, 0x52, + 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, 0x09, 0x76, - 0x74, 0x78, 0x6f, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, 0x76, 0x74, - 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x79, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x65, 0x65, 0x52, - 0x61, 0x74, 0x65, 0x22, 0x44, 0x0a, 0x13, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x69, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x6f, 0x75, - 0x6e, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x22, 0xb8, 0x01, 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, - 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x72, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x10, 0x63, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x50, 0x75, 0x62, 0x6b, - 0x65, 0x79, 0x73, 0x12, 0x3a, 0x0a, 0x12, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, - 0x76, 0x74, 0x78, 0x6f, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x10, 0x75, - 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x12, - 0x2a, 0x0a, 0x11, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x5f, 0x74, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x6e, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x22, 0x53, 0x0a, 0x20, 0x52, - 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x6e, 0x63, 0x65, - 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, - 0x22, 0xf0, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, - 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, - 0x09, 0x76, 0x74, 0x78, 0x6f, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, - 0x76, 0x74, 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x66, - 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, - 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x67, 0x65, 0x22, 0x32, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x04, 0x76, 0x6f, 0x75, 0x74, 0x22, 0x55, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, - 0x0a, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0x5a, - 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0x31, 0x0a, 0x04, 0x54, 0x72, - 0x65, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, - 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x22, 0x2f, 0x0a, - 0x09, 0x54, 0x72, 0x65, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x05, 0x6e, 0x6f, - 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x4b, - 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x78, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0xc4, 0x02, 0x0a, 0x04, - 0x56, 0x74, 0x78, 0x6f, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x54, 0x78, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x65, 0x6e, 0x74, - 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x73, 0x77, 0x65, 0x70, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x39, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x22, 0x69, 0x0a, 0x0e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, - 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, - 0x78, 0x12, 0x3a, 0x0a, 0x19, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x5f, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x22, 0x1e, 0x0a, - 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, - 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, - 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, - 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, - 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04, 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, - 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, - 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, - 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, - 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, - 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, - 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, - 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, - 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, - 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, - 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, - 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, - 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, - 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, - 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, - 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, - 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb, 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, - 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, + 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x5f, 0x70, 0x75, + 0x62, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x3a, 0x0a, + 0x12, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x5f, 0x74, + 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x10, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x56, 0x74, 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x75, 0x6e, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x54, 0x78, 0x22, 0x53, 0x0a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x65, + 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xf0, 0x01, 0x0a, 0x05, 0x52, + 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, 0x09, 0x76, 0x74, 0x78, 0x6f, 0x5f, + 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, 0x76, 0x74, 0x78, 0x6f, 0x54, 0x72, + 0x65, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, + 0x54, 0x78, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, + 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x22, 0x32, 0x0a, + 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x76, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x76, 0x6f, 0x75, + 0x74, 0x22, 0x55, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, + 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, + 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0x5a, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x6f, 0x72, 0x22, 0x31, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12, 0x29, 0x0a, 0x06, + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, + 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x22, 0x2f, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x78, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x74, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, + 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0xa6, 0x02, 0x0a, 0x04, 0x56, 0x74, 0x78, 0x6f, 0x12, 0x2c, + 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x70, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x70, 0x65, + 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x69, + 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x12, 0x1b, 0x0a, 0x09, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x65, + 0x70, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x64, + 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1e, + 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, + 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, + 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, + 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04, 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, + 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, + 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, + 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, + 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, + 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, + 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, + 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, + 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, + 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, + 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, + 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, + 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, + 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, + 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, + 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, + 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb, 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, + 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, + 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, + 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, - 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, - 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, - 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, - 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, - 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, - 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, + 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, + 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, + 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, - 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, - 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, - 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, - 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, - 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, - 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, - 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, - 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, - 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, - 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, - 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, - 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, - 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, - 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, - 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, - 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, - 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, - 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, - 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, - 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, - 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, - 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, + 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, + 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, + 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, + 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, + 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, + 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, + 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, + 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, + 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, + 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, + 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, + 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, + 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, + 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, + 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, + 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, + 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, + 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, + 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, + 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -3303,7 +3214,7 @@ func file_ark_v1_service_proto_rawDescGZIP() []byte { } var file_ark_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_ark_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 46) +var file_ark_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 45) var file_ark_v1_service_proto_goTypes = []interface{}{ (RoundStage)(0), // 0: ark.v1.RoundStage (*GetInfoRequest)(nil), // 1: ark.v1.GetInfoRequest @@ -3347,11 +3258,10 @@ var file_ark_v1_service_proto_goTypes = []interface{}{ (*TreeLevel)(nil), // 39: ark.v1.TreeLevel (*Node)(nil), // 40: ark.v1.Node (*Vtxo)(nil), // 41: ark.v1.Vtxo - (*PendingPayment)(nil), // 42: ark.v1.PendingPayment - (*GetTransactionsStreamRequest)(nil), // 43: ark.v1.GetTransactionsStreamRequest - (*GetTransactionsStreamResponse)(nil), // 44: ark.v1.GetTransactionsStreamResponse - (*RoundTransaction)(nil), // 45: ark.v1.RoundTransaction - (*RedeemTransaction)(nil), // 46: ark.v1.RedeemTransaction + (*GetTransactionsStreamRequest)(nil), // 42: ark.v1.GetTransactionsStreamRequest + (*GetTransactionsStreamResponse)(nil), // 43: ark.v1.GetTransactionsStreamResponse + (*RoundTransaction)(nil), // 44: ark.v1.RoundTransaction + (*RedeemTransaction)(nil), // 45: ark.v1.RedeemTransaction } var file_ark_v1_service_proto_depIdxs = []int32{ 36, // 0: ark.v1.RegisterInputsForNextRoundRequest.inputs:type_name -> ark.v1.Input @@ -3380,49 +3290,48 @@ var file_ark_v1_service_proto_depIdxs = []int32{ 39, // 23: ark.v1.Tree.levels:type_name -> ark.v1.TreeLevel 40, // 24: ark.v1.TreeLevel.nodes:type_name -> ark.v1.Node 35, // 25: ark.v1.Vtxo.outpoint:type_name -> ark.v1.Outpoint - 42, // 26: ark.v1.Vtxo.pending_data:type_name -> ark.v1.PendingPayment - 45, // 27: ark.v1.GetTransactionsStreamResponse.round:type_name -> ark.v1.RoundTransaction - 46, // 28: ark.v1.GetTransactionsStreamResponse.redeem:type_name -> ark.v1.RedeemTransaction - 35, // 29: ark.v1.RoundTransaction.spent_vtxos:type_name -> ark.v1.Outpoint - 41, // 30: ark.v1.RoundTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo - 35, // 31: ark.v1.RoundTransaction.claimed_boarding_utxos:type_name -> ark.v1.Outpoint - 35, // 32: ark.v1.RedeemTransaction.spent_vtxos:type_name -> ark.v1.Outpoint - 41, // 33: ark.v1.RedeemTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo - 1, // 34: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest - 3, // 35: ark.v1.ArkService.GetBoardingAddress:input_type -> ark.v1.GetBoardingAddressRequest - 5, // 36: ark.v1.ArkService.RegisterInputsForNextRound:input_type -> ark.v1.RegisterInputsForNextRoundRequest - 7, // 37: ark.v1.ArkService.RegisterOutputsForNextRound:input_type -> ark.v1.RegisterOutputsForNextRoundRequest - 9, // 38: ark.v1.ArkService.SubmitTreeNonces:input_type -> ark.v1.SubmitTreeNoncesRequest - 11, // 39: ark.v1.ArkService.SubmitTreeSignatures:input_type -> ark.v1.SubmitTreeSignaturesRequest - 13, // 40: ark.v1.ArkService.SubmitSignedForfeitTxs:input_type -> ark.v1.SubmitSignedForfeitTxsRequest - 15, // 41: ark.v1.ArkService.GetEventStream:input_type -> ark.v1.GetEventStreamRequest - 17, // 42: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest - 19, // 43: ark.v1.ArkService.CreatePayment:input_type -> ark.v1.CreatePaymentRequest - 21, // 44: ark.v1.ArkService.CompletePayment:input_type -> ark.v1.CompletePaymentRequest - 23, // 45: ark.v1.ArkService.GetRound:input_type -> ark.v1.GetRoundRequest - 25, // 46: ark.v1.ArkService.GetRoundById:input_type -> ark.v1.GetRoundByIdRequest - 27, // 47: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest - 43, // 48: ark.v1.ArkService.GetTransactionsStream:input_type -> ark.v1.GetTransactionsStreamRequest - 2, // 49: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse - 4, // 50: ark.v1.ArkService.GetBoardingAddress:output_type -> ark.v1.GetBoardingAddressResponse - 6, // 51: ark.v1.ArkService.RegisterInputsForNextRound:output_type -> ark.v1.RegisterInputsForNextRoundResponse - 8, // 52: ark.v1.ArkService.RegisterOutputsForNextRound:output_type -> ark.v1.RegisterOutputsForNextRoundResponse - 10, // 53: ark.v1.ArkService.SubmitTreeNonces:output_type -> ark.v1.SubmitTreeNoncesResponse - 12, // 54: ark.v1.ArkService.SubmitTreeSignatures:output_type -> ark.v1.SubmitTreeSignaturesResponse - 14, // 55: ark.v1.ArkService.SubmitSignedForfeitTxs:output_type -> ark.v1.SubmitSignedForfeitTxsResponse - 16, // 56: ark.v1.ArkService.GetEventStream:output_type -> ark.v1.GetEventStreamResponse - 18, // 57: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse - 20, // 58: ark.v1.ArkService.CreatePayment:output_type -> ark.v1.CreatePaymentResponse - 22, // 59: ark.v1.ArkService.CompletePayment:output_type -> ark.v1.CompletePaymentResponse - 24, // 60: ark.v1.ArkService.GetRound:output_type -> ark.v1.GetRoundResponse - 26, // 61: ark.v1.ArkService.GetRoundById:output_type -> ark.v1.GetRoundByIdResponse - 28, // 62: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse - 44, // 63: ark.v1.ArkService.GetTransactionsStream:output_type -> ark.v1.GetTransactionsStreamResponse - 49, // [49:64] is the sub-list for method output_type - 34, // [34:49] is the sub-list for method input_type - 34, // [34:34] is the sub-list for extension type_name - 34, // [34:34] is the sub-list for extension extendee - 0, // [0:34] is the sub-list for field type_name + 44, // 26: ark.v1.GetTransactionsStreamResponse.round:type_name -> ark.v1.RoundTransaction + 45, // 27: ark.v1.GetTransactionsStreamResponse.redeem:type_name -> ark.v1.RedeemTransaction + 35, // 28: ark.v1.RoundTransaction.spent_vtxos:type_name -> ark.v1.Outpoint + 41, // 29: ark.v1.RoundTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo + 35, // 30: ark.v1.RoundTransaction.claimed_boarding_utxos:type_name -> ark.v1.Outpoint + 35, // 31: ark.v1.RedeemTransaction.spent_vtxos:type_name -> ark.v1.Outpoint + 41, // 32: ark.v1.RedeemTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo + 1, // 33: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest + 3, // 34: ark.v1.ArkService.GetBoardingAddress:input_type -> ark.v1.GetBoardingAddressRequest + 5, // 35: ark.v1.ArkService.RegisterInputsForNextRound:input_type -> ark.v1.RegisterInputsForNextRoundRequest + 7, // 36: ark.v1.ArkService.RegisterOutputsForNextRound:input_type -> ark.v1.RegisterOutputsForNextRoundRequest + 9, // 37: ark.v1.ArkService.SubmitTreeNonces:input_type -> ark.v1.SubmitTreeNoncesRequest + 11, // 38: ark.v1.ArkService.SubmitTreeSignatures:input_type -> ark.v1.SubmitTreeSignaturesRequest + 13, // 39: ark.v1.ArkService.SubmitSignedForfeitTxs:input_type -> ark.v1.SubmitSignedForfeitTxsRequest + 15, // 40: ark.v1.ArkService.GetEventStream:input_type -> ark.v1.GetEventStreamRequest + 17, // 41: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest + 19, // 42: ark.v1.ArkService.CreatePayment:input_type -> ark.v1.CreatePaymentRequest + 21, // 43: ark.v1.ArkService.CompletePayment:input_type -> ark.v1.CompletePaymentRequest + 23, // 44: ark.v1.ArkService.GetRound:input_type -> ark.v1.GetRoundRequest + 25, // 45: ark.v1.ArkService.GetRoundById:input_type -> ark.v1.GetRoundByIdRequest + 27, // 46: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest + 42, // 47: ark.v1.ArkService.GetTransactionsStream:input_type -> ark.v1.GetTransactionsStreamRequest + 2, // 48: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse + 4, // 49: ark.v1.ArkService.GetBoardingAddress:output_type -> ark.v1.GetBoardingAddressResponse + 6, // 50: ark.v1.ArkService.RegisterInputsForNextRound:output_type -> ark.v1.RegisterInputsForNextRoundResponse + 8, // 51: ark.v1.ArkService.RegisterOutputsForNextRound:output_type -> ark.v1.RegisterOutputsForNextRoundResponse + 10, // 52: ark.v1.ArkService.SubmitTreeNonces:output_type -> ark.v1.SubmitTreeNoncesResponse + 12, // 53: ark.v1.ArkService.SubmitTreeSignatures:output_type -> ark.v1.SubmitTreeSignaturesResponse + 14, // 54: ark.v1.ArkService.SubmitSignedForfeitTxs:output_type -> ark.v1.SubmitSignedForfeitTxsResponse + 16, // 55: ark.v1.ArkService.GetEventStream:output_type -> ark.v1.GetEventStreamResponse + 18, // 56: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse + 20, // 57: ark.v1.ArkService.CreatePayment:output_type -> ark.v1.CreatePaymentResponse + 22, // 58: ark.v1.ArkService.CompletePayment:output_type -> ark.v1.CompletePaymentResponse + 24, // 59: ark.v1.ArkService.GetRound:output_type -> ark.v1.GetRoundResponse + 26, // 60: ark.v1.ArkService.GetRoundById:output_type -> ark.v1.GetRoundByIdResponse + 28, // 61: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse + 43, // 62: ark.v1.ArkService.GetTransactionsStream:output_type -> ark.v1.GetTransactionsStreamResponse + 48, // [48:63] is the sub-list for method output_type + 33, // [33:48] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_ark_v1_service_proto_init() } @@ -3924,18 +3833,6 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PendingPayment); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ark_v1_service_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTransactionsStreamRequest); i { case 0: return &v.state @@ -3947,7 +3844,7 @@ func file_ark_v1_service_proto_init() { return nil } } - file_ark_v1_service_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_ark_v1_service_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTransactionsStreamResponse); i { case 0: return &v.state @@ -3959,7 +3856,7 @@ func file_ark_v1_service_proto_init() { return nil } } - file_ark_v1_service_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_ark_v1_service_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RoundTransaction); i { case 0: return &v.state @@ -3971,7 +3868,7 @@ func file_ark_v1_service_proto_init() { return nil } } - file_ark_v1_service_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_ark_v1_service_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RedeemTransaction); i { case 0: return &v.state @@ -4000,7 +3897,7 @@ func file_ark_v1_service_proto_init() { (*PingResponse_RoundSigning)(nil), (*PingResponse_RoundSigningNoncesGenerated)(nil), } - file_ark_v1_service_proto_msgTypes[43].OneofWrappers = []interface{}{ + file_ark_v1_service_proto_msgTypes[42].OneofWrappers = []interface{}{ (*GetTransactionsStreamResponse_Round)(nil), (*GetTransactionsStreamResponse_Redeem)(nil), } @@ -4010,7 +3907,7 @@ func file_ark_v1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ark_v1_service_proto_rawDesc, NumEnums: 1, - NumMessages: 46, + NumMessages: 45, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/client-sdk/client/client.go b/pkg/client-sdk/client/client.go index 05776aa70..3bd494ef2 100644 --- a/pkg/client-sdk/client/client.go +++ b/pkg/client-sdk/client/client.go @@ -42,9 +42,9 @@ type ASPClient interface { Ping(ctx context.Context, paymentID string) (RoundEvent, error) CreatePayment( ctx context.Context, inputs []Input, outputs []Output, - ) (string, []string, error) + ) (string, error) CompletePayment( - ctx context.Context, signedRedeemTx string, signedUnconditionalForfeitTxs []string, + ctx context.Context, signedRedeemTx string, ) error ListVtxos(ctx context.Context, addr string) ([]Vtxo, []Vtxo, error) GetRound(ctx context.Context, txID string) (*Round, error) @@ -80,14 +80,13 @@ type Input struct { type Vtxo struct { Outpoint - Descriptor string - Amount uint64 - RoundTxid string - ExpiresAt *time.Time - RedeemTx string - UnconditionalForfeitTxs []string - Pending bool - SpentBy string + Descriptor string + Amount uint64 + RoundTxid string + ExpiresAt *time.Time + RedeemTx string + Pending bool + SpentBy string } type Output struct { diff --git a/pkg/client-sdk/client/grpc/client.go b/pkg/client-sdk/client/grpc/client.go index 08c7240b2..f5448a5f8 100644 --- a/pkg/client-sdk/client/grpc/client.go +++ b/pkg/client-sdk/client/grpc/client.go @@ -238,24 +238,23 @@ func (a *grpcClient) Ping( func (a *grpcClient) CreatePayment( ctx context.Context, inputs []client.Input, outputs []client.Output, -) (string, []string, error) { +) (string, error) { req := &arkv1.CreatePaymentRequest{ Inputs: ins(inputs).toProto(), Outputs: outs(outputs).toProto(), } resp, err := a.svc.CreatePayment(ctx, req) if err != nil { - return "", nil, err + return "", err } - return resp.SignedRedeemTx, resp.UsignedUnconditionalForfeitTxs, nil + return resp.SignedRedeemTx, nil } func (a *grpcClient) CompletePayment( - ctx context.Context, redeemTx string, signedForfeitTxs []string, + ctx context.Context, redeemTx string, ) error { req := &arkv1.CompletePaymentRequest{ - SignedRedeemTx: redeemTx, - SignedUnconditionalForfeitTxs: signedForfeitTxs, + SignedRedeemTx: redeemTx, } _, err := a.svc.CompletePayment(ctx, req) return err diff --git a/pkg/client-sdk/client/grpc/types.go b/pkg/client-sdk/client/grpc/types.go index e04e7edf2..6442adc4b 100644 --- a/pkg/client-sdk/client/grpc/types.go +++ b/pkg/client-sdk/client/grpc/types.go @@ -118,25 +118,18 @@ func (v vtxo) toVtxo() client.Vtxo { t := time.Unix(v.GetExpireAt(), 0) expiresAt = &t } - var redeemTx string - var uncondForfeitTxs []string - if v.GetPendingData() != nil { - redeemTx = v.GetPendingData().GetRedeemTx() - uncondForfeitTxs = v.GetPendingData().GetUnconditionalForfeitTxs() - } return client.Vtxo{ Outpoint: client.Outpoint{ Txid: v.GetOutpoint().GetTxid(), VOut: v.GetOutpoint().GetVout(), }, - Amount: v.GetAmount(), - RoundTxid: v.GetRoundTxid(), - ExpiresAt: expiresAt, - Pending: v.GetPending(), - RedeemTx: redeemTx, - UnconditionalForfeitTxs: uncondForfeitTxs, - SpentBy: v.GetSpentBy(), - Descriptor: v.GetDescriptor_(), + Amount: v.GetAmount(), + RoundTxid: v.GetRoundTxid(), + ExpiresAt: expiresAt, + Pending: v.GetPending(), + RedeemTx: v.GetRedeemTx(), + SpentBy: v.GetSpentBy(), + Descriptor: v.GetDescriptor_(), } } diff --git a/pkg/client-sdk/client/rest/client.go b/pkg/client-sdk/client/rest/client.go index d02ee98d3..4a0ba48a6 100644 --- a/pkg/client-sdk/client/rest/client.go +++ b/pkg/client-sdk/client/rest/client.go @@ -338,7 +338,7 @@ func (a *restClient) Ping( func (a *restClient) CreatePayment( ctx context.Context, inputs []client.Input, outputs []client.Output, -) (string, []string, error) { +) (string, error) { ins := make([]*models.V1Input, 0, len(inputs)) for _, i := range inputs { ins = append(ins, &models.V1Input{ @@ -365,21 +365,19 @@ func (a *restClient) CreatePayment( ark_service.NewArkServiceCreatePaymentParams().WithBody(&body), ) if err != nil { - return "", nil, err + return "", err } - return resp.GetPayload().SignedRedeemTx, resp.GetPayload().UsignedUnconditionalForfeitTxs, nil + return resp.GetPayload().SignedRedeemTx, nil } func (a *restClient) CompletePayment( - ctx context.Context, signedRedeemTx string, signedUncondForfeitTxs []string, + ctx context.Context, signedRedeemTx string, ) error { req := &arkv1.CompletePaymentRequest{ - SignedRedeemTx: signedRedeemTx, - SignedUnconditionalForfeitTxs: signedUncondForfeitTxs, + SignedRedeemTx: signedRedeemTx, } body := models.V1CompletePaymentRequest{ - SignedRedeemTx: req.GetSignedRedeemTx(), - SignedUnconditionalForfeitTxs: req.GetSignedUnconditionalForfeitTxs(), + SignedRedeemTx: req.GetSignedRedeemTx(), } _, err := a.svc.ArkServiceCompletePayment( ark_service.NewArkServiceCompletePaymentParams().WithBody(&body), @@ -492,26 +490,18 @@ func (a *restClient) ListVtxos( return nil, nil, err } - var redeemTx string - var uncondForfeitTxs []string - if v.PendingData != nil { - redeemTx = v.PendingData.RedeemTx - uncondForfeitTxs = v.PendingData.UnconditionalForfeitTxs - } - spendableVtxos = append(spendableVtxos, client.Vtxo{ Outpoint: client.Outpoint{ Txid: v.Outpoint.Txid, VOut: uint32(v.Outpoint.Vout), }, - Amount: uint64(amount), - RoundTxid: v.RoundTxid, - ExpiresAt: expiresAt, - Pending: v.Pending, - RedeemTx: redeemTx, - UnconditionalForfeitTxs: uncondForfeitTxs, - SpentBy: v.SpentBy, - Descriptor: v.Descriptor, + Amount: uint64(amount), + RoundTxid: v.RoundTxid, + ExpiresAt: expiresAt, + Pending: v.Pending, + RedeemTx: v.RedeemTx, + SpentBy: v.SpentBy, + Descriptor: v.Descriptor, }) } diff --git a/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_client.go b/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_client.go index 43503ac2c..92db167c2 100644 --- a/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_client.go +++ b/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_client.go @@ -68,6 +68,8 @@ type ClientService interface { ArkServiceGetRoundByID(params *ArkServiceGetRoundByIDParams, opts ...ClientOption) (*ArkServiceGetRoundByIDOK, error) + ArkServiceGetTransactionsStream(params *ArkServiceGetTransactionsStreamParams, opts ...ClientOption) (*ArkServiceGetTransactionsStreamOK, error) + ArkServiceListVtxos(params *ArkServiceListVtxosParams, opts ...ClientOption) (*ArkServiceListVtxosOK, error) ArkServicePing(params *ArkServicePingParams, opts ...ClientOption) (*ArkServicePingOK, error) @@ -344,6 +346,43 @@ func (a *Client) ArkServiceGetRoundByID(params *ArkServiceGetRoundByIDParams, op return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +ArkServiceGetTransactionsStream ark service get transactions stream API +*/ +func (a *Client) ArkServiceGetTransactionsStream(params *ArkServiceGetTransactionsStreamParams, opts ...ClientOption) (*ArkServiceGetTransactionsStreamOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewArkServiceGetTransactionsStreamParams() + } + op := &runtime.ClientOperation{ + ID: "ArkService_GetTransactionsStream", + Method: "GET", + PathPattern: "/v1/transactions", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &ArkServiceGetTransactionsStreamReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*ArkServiceGetTransactionsStreamOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*ArkServiceGetTransactionsStreamDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* ArkServiceListVtxos ark service list vtxos API */ diff --git a/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_parameters.go b/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_parameters.go new file mode 100644 index 000000000..de88b2d1c --- /dev/null +++ b/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ark_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewArkServiceGetTransactionsStreamParams creates a new ArkServiceGetTransactionsStreamParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewArkServiceGetTransactionsStreamParams() *ArkServiceGetTransactionsStreamParams { + return &ArkServiceGetTransactionsStreamParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewArkServiceGetTransactionsStreamParamsWithTimeout creates a new ArkServiceGetTransactionsStreamParams object +// with the ability to set a timeout on a request. +func NewArkServiceGetTransactionsStreamParamsWithTimeout(timeout time.Duration) *ArkServiceGetTransactionsStreamParams { + return &ArkServiceGetTransactionsStreamParams{ + timeout: timeout, + } +} + +// NewArkServiceGetTransactionsStreamParamsWithContext creates a new ArkServiceGetTransactionsStreamParams object +// with the ability to set a context for a request. +func NewArkServiceGetTransactionsStreamParamsWithContext(ctx context.Context) *ArkServiceGetTransactionsStreamParams { + return &ArkServiceGetTransactionsStreamParams{ + Context: ctx, + } +} + +// NewArkServiceGetTransactionsStreamParamsWithHTTPClient creates a new ArkServiceGetTransactionsStreamParams object +// with the ability to set a custom HTTPClient for a request. +func NewArkServiceGetTransactionsStreamParamsWithHTTPClient(client *http.Client) *ArkServiceGetTransactionsStreamParams { + return &ArkServiceGetTransactionsStreamParams{ + HTTPClient: client, + } +} + +/* +ArkServiceGetTransactionsStreamParams contains all the parameters to send to the API endpoint + + for the ark service get transactions stream operation. + + Typically these are written to a http.Request. +*/ +type ArkServiceGetTransactionsStreamParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the ark service get transactions stream params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ArkServiceGetTransactionsStreamParams) WithDefaults() *ArkServiceGetTransactionsStreamParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the ark service get transactions stream params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ArkServiceGetTransactionsStreamParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the ark service get transactions stream params +func (o *ArkServiceGetTransactionsStreamParams) WithTimeout(timeout time.Duration) *ArkServiceGetTransactionsStreamParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the ark service get transactions stream params +func (o *ArkServiceGetTransactionsStreamParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the ark service get transactions stream params +func (o *ArkServiceGetTransactionsStreamParams) WithContext(ctx context.Context) *ArkServiceGetTransactionsStreamParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the ark service get transactions stream params +func (o *ArkServiceGetTransactionsStreamParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the ark service get transactions stream params +func (o *ArkServiceGetTransactionsStreamParams) WithHTTPClient(client *http.Client) *ArkServiceGetTransactionsStreamParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the ark service get transactions stream params +func (o *ArkServiceGetTransactionsStreamParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *ArkServiceGetTransactionsStreamParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_responses.go b/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_responses.go new file mode 100644 index 000000000..5df38c7e5 --- /dev/null +++ b/pkg/client-sdk/client/rest/service/arkservice/ark_service/ark_service_get_transactions_stream_responses.go @@ -0,0 +1,337 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package ark_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + + "github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models" +) + +// ArkServiceGetTransactionsStreamReader is a Reader for the ArkServiceGetTransactionsStream structure. +type ArkServiceGetTransactionsStreamReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ArkServiceGetTransactionsStreamReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewArkServiceGetTransactionsStreamOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewArkServiceGetTransactionsStreamDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewArkServiceGetTransactionsStreamOK creates a ArkServiceGetTransactionsStreamOK with default headers values +func NewArkServiceGetTransactionsStreamOK() *ArkServiceGetTransactionsStreamOK { + return &ArkServiceGetTransactionsStreamOK{} +} + +/* +ArkServiceGetTransactionsStreamOK describes a response with status code 200, with default header values. + +A successful response.(streaming responses) +*/ +type ArkServiceGetTransactionsStreamOK struct { + Payload *ArkServiceGetTransactionsStreamOKBody +} + +// IsSuccess returns true when this ark service get transactions stream o k response has a 2xx status code +func (o *ArkServiceGetTransactionsStreamOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this ark service get transactions stream o k response has a 3xx status code +func (o *ArkServiceGetTransactionsStreamOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this ark service get transactions stream o k response has a 4xx status code +func (o *ArkServiceGetTransactionsStreamOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this ark service get transactions stream o k response has a 5xx status code +func (o *ArkServiceGetTransactionsStreamOK) IsServerError() bool { + return false +} + +// IsCode returns true when this ark service get transactions stream o k response a status code equal to that given +func (o *ArkServiceGetTransactionsStreamOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the ark service get transactions stream o k response +func (o *ArkServiceGetTransactionsStreamOK) Code() int { + return 200 +} + +func (o *ArkServiceGetTransactionsStreamOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/transactions][%d] arkServiceGetTransactionsStreamOK %s", 200, payload) +} + +func (o *ArkServiceGetTransactionsStreamOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/transactions][%d] arkServiceGetTransactionsStreamOK %s", 200, payload) +} + +func (o *ArkServiceGetTransactionsStreamOK) GetPayload() *ArkServiceGetTransactionsStreamOKBody { + return o.Payload +} + +func (o *ArkServiceGetTransactionsStreamOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(ArkServiceGetTransactionsStreamOKBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewArkServiceGetTransactionsStreamDefault creates a ArkServiceGetTransactionsStreamDefault with default headers values +func NewArkServiceGetTransactionsStreamDefault(code int) *ArkServiceGetTransactionsStreamDefault { + return &ArkServiceGetTransactionsStreamDefault{ + _statusCode: code, + } +} + +/* +ArkServiceGetTransactionsStreamDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type ArkServiceGetTransactionsStreamDefault struct { + _statusCode int + + Payload *models.RPCStatus +} + +// IsSuccess returns true when this ark service get transactions stream default response has a 2xx status code +func (o *ArkServiceGetTransactionsStreamDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this ark service get transactions stream default response has a 3xx status code +func (o *ArkServiceGetTransactionsStreamDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this ark service get transactions stream default response has a 4xx status code +func (o *ArkServiceGetTransactionsStreamDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this ark service get transactions stream default response has a 5xx status code +func (o *ArkServiceGetTransactionsStreamDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this ark service get transactions stream default response a status code equal to that given +func (o *ArkServiceGetTransactionsStreamDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the ark service get transactions stream default response +func (o *ArkServiceGetTransactionsStreamDefault) Code() int { + return o._statusCode +} + +func (o *ArkServiceGetTransactionsStreamDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/transactions][%d] ArkService_GetTransactionsStream default %s", o._statusCode, payload) +} + +func (o *ArkServiceGetTransactionsStreamDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /v1/transactions][%d] ArkService_GetTransactionsStream default %s", o._statusCode, payload) +} + +func (o *ArkServiceGetTransactionsStreamDefault) GetPayload() *models.RPCStatus { + return o.Payload +} + +func (o *ArkServiceGetTransactionsStreamDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.RPCStatus) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +ArkServiceGetTransactionsStreamOKBody Stream result of v1GetTransactionsStreamResponse +swagger:model ArkServiceGetTransactionsStreamOKBody +*/ +type ArkServiceGetTransactionsStreamOKBody struct { + + // error + Error *models.RPCStatus `json:"error,omitempty"` + + // result + Result *models.V1GetTransactionsStreamResponse `json:"result,omitempty"` +} + +// Validate validates this ark service get transactions stream o k body +func (o *ArkServiceGetTransactionsStreamOKBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateError(formats); err != nil { + res = append(res, err) + } + + if err := o.validateResult(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *ArkServiceGetTransactionsStreamOKBody) validateError(formats strfmt.Registry) error { + if swag.IsZero(o.Error) { // not required + return nil + } + + if o.Error != nil { + if err := o.Error.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error") + } + return err + } + } + + return nil +} + +func (o *ArkServiceGetTransactionsStreamOKBody) validateResult(formats strfmt.Registry) error { + if swag.IsZero(o.Result) { // not required + return nil + } + + if o.Result != nil { + if err := o.Result.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result") + } + return err + } + } + + return nil +} + +// ContextValidate validate this ark service get transactions stream o k body based on the context it is used +func (o *ArkServiceGetTransactionsStreamOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := o.contextValidateError(ctx, formats); err != nil { + res = append(res, err) + } + + if err := o.contextValidateResult(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *ArkServiceGetTransactionsStreamOKBody) contextValidateError(ctx context.Context, formats strfmt.Registry) error { + + if o.Error != nil { + + if swag.IsZero(o.Error) { // not required + return nil + } + + if err := o.Error.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "error") + } + return err + } + } + + return nil +} + +func (o *ArkServiceGetTransactionsStreamOKBody) contextValidateResult(ctx context.Context, formats strfmt.Registry) error { + + if o.Result != nil { + + if swag.IsZero(o.Result) { // not required + return nil + } + + if err := o.Result.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("arkServiceGetTransactionsStreamOK" + "." + "result") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *ArkServiceGetTransactionsStreamOKBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *ArkServiceGetTransactionsStreamOKBody) UnmarshalBinary(b []byte) error { + var res ArkServiceGetTransactionsStreamOKBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/pkg/client-sdk/client/rest/service/models/v1_complete_payment_request.go b/pkg/client-sdk/client/rest/service/models/v1_complete_payment_request.go index c267a4e58..a907f6312 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_complete_payment_request.go +++ b/pkg/client-sdk/client/rest/service/models/v1_complete_payment_request.go @@ -19,9 +19,6 @@ type V1CompletePaymentRequest struct { // signed redeem tx SignedRedeemTx string `json:"signedRedeemTx,omitempty"` - - // signed unconditional forfeit txs - SignedUnconditionalForfeitTxs []string `json:"signedUnconditionalForfeitTxs"` } // Validate validates this v1 complete payment request diff --git a/pkg/client-sdk/client/rest/service/models/v1_create_payment_response.go b/pkg/client-sdk/client/rest/service/models/v1_create_payment_response.go index 7e6bf9521..1519005b8 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_create_payment_response.go +++ b/pkg/client-sdk/client/rest/service/models/v1_create_payment_response.go @@ -19,9 +19,6 @@ type V1CreatePaymentResponse struct { // signed only by the ASP SignedRedeemTx string `json:"signedRedeemTx,omitempty"` - - // usigned unconditional forfeit txs - UsignedUnconditionalForfeitTxs []string `json:"usignedUnconditionalForfeitTxs"` } // Validate validates this v1 create payment response diff --git a/pkg/client-sdk/client/rest/service/models/v1_get_transactions_stream_response.go b/pkg/client-sdk/client/rest/service/models/v1_get_transactions_stream_response.go new file mode 100644 index 000000000..5f8621cc4 --- /dev/null +++ b/pkg/client-sdk/client/rest/service/models/v1_get_transactions_stream_response.go @@ -0,0 +1,160 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// V1GetTransactionsStreamResponse v1 get transactions stream response +// +// swagger:model v1GetTransactionsStreamResponse +type V1GetTransactionsStreamResponse struct { + + // redeem + Redeem *V1RedeemTransaction `json:"redeem,omitempty"` + + // round + Round *V1RoundTransaction `json:"round,omitempty"` +} + +// Validate validates this v1 get transactions stream response +func (m *V1GetTransactionsStreamResponse) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateRedeem(formats); err != nil { + res = append(res, err) + } + + if err := m.validateRound(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1GetTransactionsStreamResponse) validateRedeem(formats strfmt.Registry) error { + if swag.IsZero(m.Redeem) { // not required + return nil + } + + if m.Redeem != nil { + if err := m.Redeem.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("redeem") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("redeem") + } + return err + } + } + + return nil +} + +func (m *V1GetTransactionsStreamResponse) validateRound(formats strfmt.Registry) error { + if swag.IsZero(m.Round) { // not required + return nil + } + + if m.Round != nil { + if err := m.Round.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("round") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("round") + } + return err + } + } + + return nil +} + +// ContextValidate validate this v1 get transactions stream response based on the context it is used +func (m *V1GetTransactionsStreamResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateRedeem(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateRound(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1GetTransactionsStreamResponse) contextValidateRedeem(ctx context.Context, formats strfmt.Registry) error { + + if m.Redeem != nil { + + if swag.IsZero(m.Redeem) { // not required + return nil + } + + if err := m.Redeem.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("redeem") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("redeem") + } + return err + } + } + + return nil +} + +func (m *V1GetTransactionsStreamResponse) contextValidateRound(ctx context.Context, formats strfmt.Registry) error { + + if m.Round != nil { + + if swag.IsZero(m.Round) { // not required + return nil + } + + if err := m.Round.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("round") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("round") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *V1GetTransactionsStreamResponse) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *V1GetTransactionsStreamResponse) UnmarshalBinary(b []byte) error { + var res V1GetTransactionsStreamResponse + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/client-sdk/client/rest/service/models/v1_pending_payment.go b/pkg/client-sdk/client/rest/service/models/v1_pending_payment.go index 76086b4ec..fba89e301 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_pending_payment.go +++ b/pkg/client-sdk/client/rest/service/models/v1_pending_payment.go @@ -19,9 +19,6 @@ type V1PendingPayment struct { // redeem tx RedeemTx string `json:"redeemTx,omitempty"` - - // unconditional forfeit txs - UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"` } // Validate validates this v1 pending payment diff --git a/pkg/client-sdk/client/rest/service/models/v1_redeem_transaction.go b/pkg/client-sdk/client/rest/service/models/v1_redeem_transaction.go new file mode 100644 index 000000000..228f3f5c6 --- /dev/null +++ b/pkg/client-sdk/client/rest/service/models/v1_redeem_transaction.go @@ -0,0 +1,186 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// V1RedeemTransaction v1 redeem transaction +// +// swagger:model v1RedeemTransaction +type V1RedeemTransaction struct { + + // spendable vtxos + SpendableVtxos []*V1Vtxo `json:"spendableVtxos"` + + // spent vtxos + SpentVtxos []*V1Outpoint `json:"spentVtxos"` + + // txid + Txid string `json:"txid,omitempty"` +} + +// Validate validates this v1 redeem transaction +func (m *V1RedeemTransaction) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateSpendableVtxos(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSpentVtxos(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1RedeemTransaction) validateSpendableVtxos(formats strfmt.Registry) error { + if swag.IsZero(m.SpendableVtxos) { // not required + return nil + } + + for i := 0; i < len(m.SpendableVtxos); i++ { + if swag.IsZero(m.SpendableVtxos[i]) { // not required + continue + } + + if m.SpendableVtxos[i] != nil { + if err := m.SpendableVtxos[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *V1RedeemTransaction) validateSpentVtxos(formats strfmt.Registry) error { + if swag.IsZero(m.SpentVtxos) { // not required + return nil + } + + for i := 0; i < len(m.SpentVtxos); i++ { + if swag.IsZero(m.SpentVtxos[i]) { // not required + continue + } + + if m.SpentVtxos[i] != nil { + if err := m.SpentVtxos[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this v1 redeem transaction based on the context it is used +func (m *V1RedeemTransaction) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateSpendableVtxos(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSpentVtxos(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1RedeemTransaction) contextValidateSpendableVtxos(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.SpendableVtxos); i++ { + + if m.SpendableVtxos[i] != nil { + + if swag.IsZero(m.SpendableVtxos[i]) { // not required + return nil + } + + if err := m.SpendableVtxos[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *V1RedeemTransaction) contextValidateSpentVtxos(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.SpentVtxos); i++ { + + if m.SpentVtxos[i] != nil { + + if swag.IsZero(m.SpentVtxos[i]) { // not required + return nil + } + + if err := m.SpentVtxos[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *V1RedeemTransaction) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *V1RedeemTransaction) UnmarshalBinary(b []byte) error { + var res V1RedeemTransaction + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/client-sdk/client/rest/service/models/v1_round_transaction.go b/pkg/client-sdk/client/rest/service/models/v1_round_transaction.go new file mode 100644 index 000000000..ced97b394 --- /dev/null +++ b/pkg/client-sdk/client/rest/service/models/v1_round_transaction.go @@ -0,0 +1,248 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// V1RoundTransaction v1 round transaction +// +// swagger:model v1RoundTransaction +type V1RoundTransaction struct { + + // claimed boarding utxos + ClaimedBoardingUtxos []*V1Outpoint `json:"claimedBoardingUtxos"` + + // spendable vtxos + SpendableVtxos []*V1Vtxo `json:"spendableVtxos"` + + // spent vtxos + SpentVtxos []*V1Outpoint `json:"spentVtxos"` + + // txid + Txid string `json:"txid,omitempty"` +} + +// Validate validates this v1 round transaction +func (m *V1RoundTransaction) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateClaimedBoardingUtxos(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSpendableVtxos(formats); err != nil { + res = append(res, err) + } + + if err := m.validateSpentVtxos(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1RoundTransaction) validateClaimedBoardingUtxos(formats strfmt.Registry) error { + if swag.IsZero(m.ClaimedBoardingUtxos) { // not required + return nil + } + + for i := 0; i < len(m.ClaimedBoardingUtxos); i++ { + if swag.IsZero(m.ClaimedBoardingUtxos[i]) { // not required + continue + } + + if m.ClaimedBoardingUtxos[i] != nil { + if err := m.ClaimedBoardingUtxos[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *V1RoundTransaction) validateSpendableVtxos(formats strfmt.Registry) error { + if swag.IsZero(m.SpendableVtxos) { // not required + return nil + } + + for i := 0; i < len(m.SpendableVtxos); i++ { + if swag.IsZero(m.SpendableVtxos[i]) { // not required + continue + } + + if m.SpendableVtxos[i] != nil { + if err := m.SpendableVtxos[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *V1RoundTransaction) validateSpentVtxos(formats strfmt.Registry) error { + if swag.IsZero(m.SpentVtxos) { // not required + return nil + } + + for i := 0; i < len(m.SpentVtxos); i++ { + if swag.IsZero(m.SpentVtxos[i]) { // not required + continue + } + + if m.SpentVtxos[i] != nil { + if err := m.SpentVtxos[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this v1 round transaction based on the context it is used +func (m *V1RoundTransaction) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateClaimedBoardingUtxos(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSpendableVtxos(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSpentVtxos(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1RoundTransaction) contextValidateClaimedBoardingUtxos(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.ClaimedBoardingUtxos); i++ { + + if m.ClaimedBoardingUtxos[i] != nil { + + if swag.IsZero(m.ClaimedBoardingUtxos[i]) { // not required + return nil + } + + if err := m.ClaimedBoardingUtxos[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("claimedBoardingUtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *V1RoundTransaction) contextValidateSpendableVtxos(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.SpendableVtxos); i++ { + + if m.SpendableVtxos[i] != nil { + + if swag.IsZero(m.SpendableVtxos[i]) { // not required + return nil + } + + if err := m.SpendableVtxos[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spendableVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *V1RoundTransaction) contextValidateSpentVtxos(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.SpentVtxos); i++ { + + if m.SpentVtxos[i] != nil { + + if swag.IsZero(m.SpentVtxos[i]) { // not required + return nil + } + + if err := m.SpentVtxos[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("spentVtxos" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *V1RoundTransaction) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *V1RoundTransaction) UnmarshalBinary(b []byte) error { + var res V1RoundTransaction + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/client-sdk/client/rest/service/models/v1_vtxo.go b/pkg/client-sdk/client/rest/service/models/v1_vtxo.go index bbf88a580..74e0f870a 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_vtxo.go +++ b/pkg/client-sdk/client/rest/service/models/v1_vtxo.go @@ -33,8 +33,8 @@ type V1Vtxo struct { // pending Pending bool `json:"pending,omitempty"` - // pending data - PendingData *V1PendingPayment `json:"pendingData,omitempty"` + // redeem tx + RedeemTx string `json:"redeemTx,omitempty"` // round txid RoundTxid string `json:"roundTxid,omitempty"` @@ -57,10 +57,6 @@ func (m *V1Vtxo) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validatePendingData(formats); err != nil { - res = append(res, err) - } - if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -86,25 +82,6 @@ func (m *V1Vtxo) validateOutpoint(formats strfmt.Registry) error { return nil } -func (m *V1Vtxo) validatePendingData(formats strfmt.Registry) error { - if swag.IsZero(m.PendingData) { // not required - return nil - } - - if m.PendingData != nil { - if err := m.PendingData.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("pendingData") - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("pendingData") - } - return err - } - } - - return nil -} - // ContextValidate validate this v1 vtxo based on the context it is used func (m *V1Vtxo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error @@ -113,10 +90,6 @@ func (m *V1Vtxo) ContextValidate(ctx context.Context, formats strfmt.Registry) e res = append(res, err) } - if err := m.contextValidatePendingData(ctx, formats); err != nil { - res = append(res, err) - } - if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -144,27 +117,6 @@ func (m *V1Vtxo) contextValidateOutpoint(ctx context.Context, formats strfmt.Reg return nil } -func (m *V1Vtxo) contextValidatePendingData(ctx context.Context, formats strfmt.Registry) error { - - if m.PendingData != nil { - - if swag.IsZero(m.PendingData) { // not required - return nil - } - - if err := m.PendingData.ContextValidate(ctx, formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("pendingData") - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("pendingData") - } - return err - } - } - - return nil -} - // MarshalBinary interface implementation func (m *V1Vtxo) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/client-sdk/client_test.go b/pkg/client-sdk/client_test.go index f7be9251c..36c018b00 100644 --- a/pkg/client-sdk/client_test.go +++ b/pkg/client-sdk/client_test.go @@ -196,13 +196,12 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) { Txid: vtxo.Outpoint.Txid, VOut: vtxo.Outpoint.Vout, }, - Amount: amount, - RoundTxid: vtxo.PoolTxid, - ExpiresAt: &expireAt, - RedeemTx: vtxo.PendingData.RedeemTx, - UnconditionalForfeitTxs: vtxo.PendingData.UnconditionalForfeitTxs, - Pending: vtxo.Pending, - SpentBy: vtxo.SpentBy, + Amount: amount, + RoundTxid: vtxo.PoolTxid, + ExpiresAt: &expireAt, + RedeemTx: vtxo.PendingData.RedeemTx, + Pending: vtxo.Pending, + SpentBy: vtxo.SpentBy, } } @@ -221,13 +220,12 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) { Txid: vtxo.Outpoint.Txid, VOut: vtxo.Outpoint.Vout, }, - Amount: amount, - RoundTxid: vtxo.PoolTxid, - ExpiresAt: &expireAt, - RedeemTx: vtxo.PendingData.RedeemTx, - UnconditionalForfeitTxs: vtxo.PendingData.UnconditionalForfeitTxs, - Pending: vtxo.Pending, - SpentBy: vtxo.SpentBy, + Amount: amount, + RoundTxid: vtxo.PoolTxid, + ExpiresAt: &expireAt, + RedeemTx: vtxo.PendingData.RedeemTx, + Pending: vtxo.Pending, + SpentBy: vtxo.SpentBy, } } diff --git a/pkg/client-sdk/covenantless_client.go b/pkg/client-sdk/covenantless_client.go index 758e7294c..b550979d8 100644 --- a/pkg/client-sdk/covenantless_client.go +++ b/pkg/client-sdk/covenantless_client.go @@ -653,8 +653,7 @@ func (a *covenantlessArkClient) SendAsync( }) } - redeemTx, unconditionalForfeitTxs, err := a.client.CreatePayment( - ctx, inputs, receiversOutput) + redeemTx, err := a.client.CreatePayment(ctx, inputs, receiversOutput) if err != nil { return "", err } @@ -667,7 +666,7 @@ func (a *covenantlessArkClient) SendAsync( } if err = a.client.CompletePayment( - ctx, signedRedeemTx, unconditionalForfeitTxs, + ctx, signedRedeemTx, ); err != nil { return "", err } diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index 3f4b1534c..1355d91db 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -320,12 +320,12 @@ func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) (dom return s.lastEvent, nil } -func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx string, unconditionalForfeitTxs []string) error { +func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx string) error { return fmt.Errorf("unimplemented") } -func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []ports.Input, receivers []domain.Receiver) (string, []string, error) { - return "", nil, fmt.Errorf("unimplemented") +func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []ports.Input, receivers []domain.Receiver) (string, error) { + return "", fmt.Errorf("unimplemented") } func (s *covenantService) SignVtxos(ctx context.Context, forfeitTxs []string) error { diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index f7dde6fb7..24dc361ce 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -139,7 +139,7 @@ func (s *covenantlessService) Stop() { } func (s *covenantlessService) CompleteAsyncPayment( - ctx context.Context, redeemTx string, unconditionalForfeitTxs []string, + ctx context.Context, redeemTx string, ) error { redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(redeemTx), true) if err != nil { @@ -274,11 +274,8 @@ func (s *covenantlessService) CompleteAsyncPayment( Amount: uint64(out.Value), }, ExpireAt: asyncPayData.expireAt, - AsyncPayment: &domain.AsyncPaymentTxs{ - RedeemTx: redeemTx, - UnconditionalForfeitTxs: unconditionalForfeitTxs, - }, - Pending: isPending, + RedeemTx: redeemTx, + Pending: isPending, }) } @@ -313,7 +310,7 @@ func (s *covenantlessService) CompleteAsyncPayment( func (s *covenantlessService) CreateAsyncPayment( ctx context.Context, inputs []ports.Input, receivers []domain.Receiver, -) (string, []string, error) { +) (string, error) { vtxosKeys := make([]domain.VtxoKey, 0, len(inputs)) for _, in := range inputs { vtxosKeys = append(vtxosKeys, in.VtxoKey) @@ -321,10 +318,10 @@ func (s *covenantlessService) CreateAsyncPayment( vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, vtxosKeys) if err != nil { - return "", nil, err + return "", err } if len(vtxos) <= 0 { - return "", nil, fmt.Errorf("vtxos not found") + return "", fmt.Errorf("vtxos not found") } vtxosInputs := make([]domain.Vtxo, 0, len(inputs)) @@ -332,18 +329,18 @@ func (s *covenantlessService) CreateAsyncPayment( expiration := vtxos[0].ExpireAt for _, vtxo := range vtxos { if vtxo.Spent { - return "", nil, fmt.Errorf("all vtxos must be unspent") + return "", fmt.Errorf("all vtxos must be unspent") } if vtxo.Redeemed { - return "", nil, fmt.Errorf("all vtxos must be redeemed") + return "", fmt.Errorf("all vtxos must be redeemed") } if vtxo.Swept { - return "", nil, fmt.Errorf("all vtxos must be swept") + return "", fmt.Errorf("all vtxos must be swept") } if vtxo.Pending { - return "", nil, fmt.Errorf("all vtxos must be claimed") + return "", fmt.Errorf("all vtxos must be claimed") } if vtxo.ExpireAt < expiration { @@ -353,19 +350,19 @@ func (s *covenantlessService) CreateAsyncPayment( vtxosInputs = append(vtxosInputs, vtxo) } - res, err := s.builder.BuildAsyncPaymentTransactions( + redeemTx, err := s.builder.BuildAsyncPaymentTransactions( vtxosInputs, s.pubkey, receivers, ) if err != nil { - return "", nil, fmt.Errorf("failed to build async payment txs: %s", err) + return "", fmt.Errorf("failed to build async payment txs: %s", err) } - redeemTx, err := psbt.NewFromRawBytes(strings.NewReader(res.RedeemTx), true) + redeemPtx, err := psbt.NewFromRawBytes(strings.NewReader(redeemTx), true) if err != nil { - return "", nil, fmt.Errorf("failed to parse redeem tx: %s", err) + return "", fmt.Errorf("failed to parse redeem tx: %s", err) } - s.asyncPaymentsCache[redeemTx.UnsignedTx.TxID()] = struct { + s.asyncPaymentsCache[redeemPtx.UnsignedTx.TxID()] = struct { receivers []domain.Receiver expireAt int64 }{ @@ -373,7 +370,7 @@ func (s *covenantlessService) CreateAsyncPayment( expireAt: expiration, } - return res.RedeemTx, res.UnconditionalForfeitTxs, nil + return redeemTx, nil } func (s *covenantlessService) GetBoardingAddress( @@ -1536,7 +1533,7 @@ func (s *covenantlessService) reactToFraud(ctx context.Context, vtxo domain.Vtxo log.Debugf("vtxo %s:%d has been spent by async payment", vtxo.Txid, vtxo.VOut) - redeemTxHex, err := s.builder.FinalizeAndExtract(asyncPayVtxo.AsyncPayment.RedeemTx) + redeemTxHex, err := s.builder.FinalizeAndExtract(asyncPayVtxo.RedeemTx) if err != nil { return fmt.Errorf("failed to finalize redeem tx: %s", err) } diff --git a/server/internal/core/application/types.go b/server/internal/core/application/types.go index ea1440b10..a833af5bc 100644 --- a/server/internal/core/application/types.go +++ b/server/internal/core/application/types.go @@ -33,9 +33,9 @@ type Service interface { // Async payments CreateAsyncPayment( ctx context.Context, inputs []ports.Input, receivers []domain.Receiver, - ) (string, []string, error) + ) (string, error) CompleteAsyncPayment( - ctx context.Context, redeemTx string, unconditionalForfeitTxs []string, + ctx context.Context, redeemTx string, ) error GetBoardingAddress( ctx context.Context, userPubkey *secp256k1.PublicKey, diff --git a/server/internal/core/domain/payment.go b/server/internal/core/domain/payment.go index 5e12a7db6..8005f2094 100644 --- a/server/internal/core/domain/payment.go +++ b/server/internal/core/domain/payment.go @@ -108,17 +108,12 @@ func (r Receiver) IsOnchain() bool { type Vtxo struct { VtxoKey Receiver - RoundTxid string - SpentBy string // round txid or async redeem txid - Spent bool - Redeemed bool - Swept bool - ExpireAt int64 - AsyncPayment *AsyncPaymentTxs // nil if not async vtxo - Pending bool -} - -type AsyncPaymentTxs struct { - RedeemTx string // always signed by the ASP when created - UnconditionalForfeitTxs []string + RoundTxid string + SpentBy string // round txid or async redeem txid + Spent bool + Redeemed bool + Swept bool + ExpireAt int64 + RedeemTx string // empty if in-round vtxo + Pending bool } diff --git a/server/internal/core/ports/tx_builder.go b/server/internal/core/ports/tx_builder.go index 46a610e8d..4751f93fe 100644 --- a/server/internal/core/ports/tx_builder.go +++ b/server/internal/core/ports/tx_builder.go @@ -42,7 +42,7 @@ type TxBuilder interface { BuildAsyncPaymentTransactions( vtxosToSpend []domain.Vtxo, aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver, - ) (*domain.AsyncPaymentTxs, error) + ) (string, error) VerifyAndCombinePartialTx(dest string, src string) (string, error) GetTxID(tx string) (string, error) } diff --git a/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql b/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql index 784b8d33b..cc9078be9 100644 --- a/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql +++ b/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql @@ -41,15 +41,6 @@ CREATE TABLE IF NOT EXISTS tx ( FOREIGN KEY (round_id) REFERENCES round(id) ); -CREATE TABLE IF NOT EXISTS uncond_forfeit_tx ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - tx TEXT NOT NULL, - vtxo_txid TEXT NOT NULL, - vtxo_vout INTEGER NOT NULL, - position INTEGER NOT NULL, - FOREIGN KEY (vtxo_txid, vtxo_vout) REFERENCES vtxo(txid, vout) -); - CREATE TABLE IF NOT EXISTS vtxo ( txid TEXT NOT NULL, vout INTEGER NOT NULL, @@ -85,9 +76,4 @@ ON payment.id=receiver.payment_id; CREATE VIEW payment_vtxo_vw AS SELECT vtxo.* FROM payment LEFT OUTER JOIN vtxo -ON payment.id=vtxo.payment_id; - -CREATE VIEW uncond_forfeit_tx_vw AS SELECT uncond_forfeit_tx.* -FROM vtxo -LEFT OUTER JOIN uncond_forfeit_tx -ON vtxo.txid=uncond_forfeit_tx.vtxo_txid AND vtxo.vout=uncond_forfeit_tx.vtxo_vout; \ No newline at end of file +ON payment.id=vtxo.payment_id; \ No newline at end of file diff --git a/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go b/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go index b451ecce9..392476cd5 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go +++ b/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go @@ -87,22 +87,6 @@ type Tx struct { IsLeaf sql.NullBool } -type UncondForfeitTx struct { - ID int64 - Tx string - VtxoTxid string - VtxoVout int64 - Position int64 -} - -type UncondForfeitTxVw struct { - ID sql.NullInt64 - Tx sql.NullString - VtxoTxid sql.NullString - VtxoVout sql.NullInt64 - Position sql.NullInt64 -} - type Vtxo struct { Txid string Vout int64 diff --git a/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go b/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go index f3972ef60..48dad1de4 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go +++ b/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go @@ -54,16 +54,12 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams } const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending, - uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo WHERE redeemed = false ` type SelectNotRedeemedVtxosRow struct { - Vtxo Vtxo - UncondForfeitTxVw UncondForfeitTxVw + Vtxo Vtxo } func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeemedVtxosRow, error) { @@ -89,11 +85,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem &i.Vtxo.RedeemTx, &i.Vtxo.Descriptor, &i.Vtxo.Pending, - &i.UncondForfeitTxVw.ID, - &i.UncondForfeitTxVw.Tx, - &i.UncondForfeitTxVw.VtxoTxid, - &i.UncondForfeitTxVw.VtxoVout, - &i.UncondForfeitTxVw.Position, ); err != nil { return nil, err } @@ -109,16 +100,12 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem } const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending, - uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo WHERE redeemed = false AND INSTR(descriptor, ?) > 0 ` type SelectNotRedeemedVtxosWithPubkeyRow struct { - Vtxo Vtxo - UncondForfeitTxVw UncondForfeitTxVw + Vtxo Vtxo } func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) { @@ -144,11 +131,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr st &i.Vtxo.RedeemTx, &i.Vtxo.Descriptor, &i.Vtxo.Pending, - &i.UncondForfeitTxVw.ID, - &i.UncondForfeitTxVw.Tx, - &i.UncondForfeitTxVw.VtxoTxid, - &i.UncondForfeitTxVw.VtxoVout, - &i.UncondForfeitTxVw.Position, ); err != nil { return nil, err } @@ -481,16 +463,12 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR } const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending, - uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo WHERE redeemed = false AND swept = false ` type SelectSweepableVtxosRow struct { - Vtxo Vtxo - UncondForfeitTxVw UncondForfeitTxVw + Vtxo Vtxo } func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVtxosRow, error) { @@ -516,11 +494,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt &i.Vtxo.RedeemTx, &i.Vtxo.Descriptor, &i.Vtxo.Pending, - &i.UncondForfeitTxVw.ID, - &i.UncondForfeitTxVw.Tx, - &i.UncondForfeitTxVw.VtxoTxid, - &i.UncondForfeitTxVw.VtxoVout, - &i.UncondForfeitTxVw.Position, ); err != nil { return nil, err } @@ -622,10 +595,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow } const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending, - uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo WHERE txid = ? AND vout = ? ` @@ -635,8 +605,7 @@ type SelectVtxoByOutpointParams struct { } type SelectVtxoByOutpointRow struct { - Vtxo Vtxo - UncondForfeitTxVw UncondForfeitTxVw + Vtxo Vtxo } func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutpointParams) (SelectVtxoByOutpointRow, error) { @@ -656,26 +625,17 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp &i.Vtxo.RedeemTx, &i.Vtxo.Descriptor, &i.Vtxo.Pending, - &i.UncondForfeitTxVw.ID, - &i.UncondForfeitTxVw.Tx, - &i.UncondForfeitTxVw.VtxoTxid, - &i.UncondForfeitTxVw.VtxoVout, - &i.UncondForfeitTxVw.Position, ) return i, err } const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending, - uncond_forfeit_tx_vw.id, uncond_forfeit_tx_vw.tx, uncond_forfeit_tx_vw.vtxo_txid, uncond_forfeit_tx_vw.vtxo_vout, uncond_forfeit_tx_vw.position -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo WHERE pool_tx = ? ` type SelectVtxosByPoolTxidRow struct { - Vtxo Vtxo - UncondForfeitTxVw UncondForfeitTxVw + Vtxo Vtxo } func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]SelectVtxosByPoolTxidRow, error) { @@ -701,11 +661,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S &i.Vtxo.RedeemTx, &i.Vtxo.Descriptor, &i.Vtxo.Pending, - &i.UncondForfeitTxVw.ID, - &i.UncondForfeitTxVw.Tx, - &i.UncondForfeitTxVw.VtxoTxid, - &i.UncondForfeitTxVw.VtxoVout, - &i.UncondForfeitTxVw.Position, ); err != nil { return nil, err } @@ -891,32 +846,6 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa return err } -const upsertUnconditionalForfeitTx = `-- name: UpsertUnconditionalForfeitTx :exec -INSERT INTO uncond_forfeit_tx (tx, vtxo_txid, vtxo_vout, position) -VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET - tx = EXCLUDED.tx, - vtxo_txid = EXCLUDED.vtxo_txid, - vtxo_vout = EXCLUDED.vtxo_vout, - position = EXCLUDED.position -` - -type UpsertUnconditionalForfeitTxParams struct { - Tx string - VtxoTxid string - VtxoVout int64 - Position int64 -} - -func (q *Queries) UpsertUnconditionalForfeitTx(ctx context.Context, arg UpsertUnconditionalForfeitTxParams) error { - _, err := q.db.ExecContext(ctx, upsertUnconditionalForfeitTx, - arg.Tx, - arg.VtxoTxid, - arg.VtxoVout, - arg.Position, - ) - return err -} - const upsertVtxo = `-- name: UpsertVtxo :exec INSERT INTO vtxo (txid, vout, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET diff --git a/server/internal/infrastructure/db/sqlite/sqlc/query.sql b/server/internal/infrastructure/db/sqlite/sqlc/query.sql index 9cb505e20..92787de19 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/query.sql +++ b/server/internal/infrastructure/db/sqlite/sqlc/query.sql @@ -111,14 +111,6 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?; -- name: SelectRoundIds :many SELECT id FROM round; --- name: UpsertUnconditionalForfeitTx :exec -INSERT INTO uncond_forfeit_tx (tx, vtxo_txid, vtxo_vout, position) -VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET - tx = EXCLUDED.tx, - vtxo_txid = EXCLUDED.vtxo_txid, - vtxo_vout = EXCLUDED.vtxo_vout, - position = EXCLUDED.position; - -- name: UpsertVtxo :exec INSERT INTO vtxo (txid, vout, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET @@ -134,38 +126,23 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE pending = EXCLUDED.pending; -- name: SelectSweepableVtxos :many -SELECT sqlc.embed(vtxo), - sqlc.embed(uncond_forfeit_tx_vw) -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT sqlc.embed(vtxo) FROM vtxo WHERE redeemed = false AND swept = false; -- name: SelectNotRedeemedVtxos :many -SELECT sqlc.embed(vtxo), - sqlc.embed(uncond_forfeit_tx_vw) -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT sqlc.embed(vtxo) FROM vtxo WHERE redeemed = false; -- name: SelectNotRedeemedVtxosWithPubkey :many -SELECT sqlc.embed(vtxo), - sqlc.embed(uncond_forfeit_tx_vw) -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT sqlc.embed(vtxo) FROM vtxo WHERE redeemed = false AND INSTR(descriptor, ?) > 0; -- name: SelectVtxoByOutpoint :one -SELECT sqlc.embed(vtxo), - sqlc.embed(uncond_forfeit_tx_vw) -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT sqlc.embed(vtxo) FROM vtxo WHERE txid = ? AND vout = ?; -- name: SelectVtxosByPoolTxid :many -SELECT sqlc.embed(vtxo), - sqlc.embed(uncond_forfeit_tx_vw) -FROM vtxo - LEFT OUTER JOIN uncond_forfeit_tx_vw ON vtxo.txid=uncond_forfeit_tx_vw.vtxo_txid AND vtxo.vout=uncond_forfeit_tx_vw.vtxo_vout +SELECT sqlc.embed(vtxo) FROM vtxo WHERE pool_tx = ?; -- name: MarkVtxoAsRedeemed :exec diff --git a/server/internal/infrastructure/db/sqlite/vtxo_repo.go b/server/internal/infrastructure/db/sqlite/vtxo_repo.go index 7347d98c9..af9ef53ae 100644 --- a/server/internal/infrastructure/db/sqlite/vtxo_repo.go +++ b/server/internal/infrastructure/db/sqlite/vtxo_repo.go @@ -37,10 +37,6 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro txBody := func(querierWithTx *queries.Queries) error { for i := range vtxos { vtxo := vtxos[i] - var redeemTx string - if vtxo.AsyncPayment != nil { - redeemTx = vtxo.AsyncPayment.RedeemTx - } if err := querierWithTx.UpsertVtxo( ctx, queries.UpsertVtxoParams{ Txid: vtxo.Txid, @@ -53,25 +49,12 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro Redeemed: vtxo.Redeemed, Swept: vtxo.Swept, ExpireAt: vtxo.ExpireAt, - RedeemTx: sql.NullString{String: redeemTx, Valid: true}, + RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true}, Pending: vtxo.Pending, }, ); err != nil { return err } - - if vtxo.AsyncPayment != nil { - for i, tx := range vtxo.AsyncPayment.UnconditionalForfeitTxs { - if err := querierWithTx.UpsertUnconditionalForfeitTx(ctx, queries.UpsertUnconditionalForfeitTxParams{ - Tx: tx, - VtxoTxid: vtxo.Txid, - VtxoVout: int64(vtxo.VOut), - Position: int64(i), - }); err != nil { - return err - } - } - } } return nil @@ -86,12 +69,9 @@ func (v *vxtoRepository) GetAllSweepableVtxos(ctx context.Context) ([]domain.Vtx return nil, err } - rows := make([]vtxoWithUnconditionalForfeitTxs, 0, len(res)) + rows := make([]queries.Vtxo, 0, len(res)) for _, row := range res { - rows = append(rows, vtxoWithUnconditionalForfeitTxs{ - vtxo: row.Vtxo, - tx: row.UncondForfeitTxVw, - }) + rows = append(rows, row.Vtxo) } return readRows(rows) } @@ -99,7 +79,7 @@ func (v *vxtoRepository) GetAllSweepableVtxos(ctx context.Context) ([]domain.Vtx func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]domain.Vtxo, []domain.Vtxo, error) { withPubkey := len(pubkey) > 0 - var rows []vtxoWithUnconditionalForfeitTxs + var rows []queries.Vtxo if withPubkey { if len(pubkey) == 66 { pubkey = pubkey[2:] @@ -109,24 +89,18 @@ func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]doma if err != nil { return nil, nil, err } - rows = make([]vtxoWithUnconditionalForfeitTxs, 0, len(res)) + rows = make([]queries.Vtxo, 0, len(res)) for _, row := range res { - rows = append(rows, vtxoWithUnconditionalForfeitTxs{ - vtxo: row.Vtxo, - tx: row.UncondForfeitTxVw, - }) + rows = append(rows, row.Vtxo) } } else { res, err := v.querier.SelectNotRedeemedVtxos(ctx) if err != nil { return nil, nil, err } - rows = make([]vtxoWithUnconditionalForfeitTxs, 0, len(res)) + rows = make([]queries.Vtxo, 0, len(res)) for _, row := range res { - rows = append(rows, vtxoWithUnconditionalForfeitTxs{ - vtxo: row.Vtxo, - tx: row.UncondForfeitTxVw, - }) + rows = append(rows, row.Vtxo) } } @@ -163,12 +137,7 @@ func (v *vxtoRepository) GetVtxos(ctx context.Context, outpoints []domain.VtxoKe return nil, err } - result, err := readRows([]vtxoWithUnconditionalForfeitTxs{ - { - vtxo: res.Vtxo, - tx: res.UncondForfeitTxVw, - }, - }) + result, err := readRows([]queries.Vtxo{res.Vtxo}) if err != nil { return nil, err } @@ -188,12 +157,9 @@ func (v *vxtoRepository) GetVtxosForRound(ctx context.Context, txid string) ([]d if err != nil { return nil, err } - rows := make([]vtxoWithUnconditionalForfeitTxs, 0, len(res)) + rows := make([]queries.Vtxo, 0, len(res)) for _, row := range res { - rows = append(rows, vtxoWithUnconditionalForfeitTxs{ - vtxo: row.Vtxo, - tx: row.UncondForfeitTxVw, - }) + rows = append(rows, row.Vtxo) } return readRows(rows) @@ -281,18 +247,7 @@ func (v *vxtoRepository) UpdateExpireAt(ctx context.Context, vtxos []domain.Vtxo return execTx(ctx, v.db, txBody) } -func rowToVtxo(row queries.Vtxo, uncondForfeitTxs []queries.UncondForfeitTxVw) domain.Vtxo { - var asyncPayment *domain.AsyncPaymentTxs - if row.RedeemTx.Valid && len(uncondForfeitTxs) > 0 { - txs := make([]string, len(uncondForfeitTxs)) - for _, tx := range uncondForfeitTxs { - txs[tx.Position.Int64] = tx.Tx.String - } - asyncPayment = &domain.AsyncPaymentTxs{ - RedeemTx: row.RedeemTx.String, - UnconditionalForfeitTxs: txs, - } - } +func rowToVtxo(row queries.Vtxo) domain.Vtxo { return domain.Vtxo{ VtxoKey: domain.VtxoKey{ Txid: row.Txid, @@ -302,49 +257,21 @@ func rowToVtxo(row queries.Vtxo, uncondForfeitTxs []queries.UncondForfeitTxVw) d Descriptor: row.Descriptor.String, Amount: uint64(row.Amount), }, - RoundTxid: row.PoolTx, - SpentBy: row.SpentBy, - Spent: row.Spent, - Redeemed: row.Redeemed, - Swept: row.Swept, - ExpireAt: row.ExpireAt, - AsyncPayment: asyncPayment, - Pending: row.Pending, + RoundTxid: row.PoolTx, + SpentBy: row.SpentBy, + Spent: row.Spent, + Redeemed: row.Redeemed, + Swept: row.Swept, + ExpireAt: row.ExpireAt, + RedeemTx: row.RedeemTx.String, + Pending: row.Pending, } } -type vtxoWithUnconditionalForfeitTxs struct { - vtxo queries.Vtxo - tx queries.UncondForfeitTxVw -} - -func readRows(rows []vtxoWithUnconditionalForfeitTxs) ([]domain.Vtxo, error) { - uncondForfeitTxsMap := make(map[domain.VtxoKey][]queries.UncondForfeitTxVw) - for _, row := range rows { - if !row.vtxo.RedeemTx.Valid { - continue - } - vtxoKey := domain.VtxoKey{ - Txid: row.vtxo.Txid, - VOut: uint32(row.vtxo.Vout), - } - if _, ok := uncondForfeitTxsMap[vtxoKey]; !ok { - uncondForfeitTxsMap[vtxoKey] = make([]queries.UncondForfeitTxVw, 0) - } - if row.tx.Tx.Valid { - uncondForfeitTxsMap[vtxoKey] = append( - uncondForfeitTxsMap[vtxoKey], row.tx, - ) - } - } +func readRows(rows []queries.Vtxo) ([]domain.Vtxo, error) { vtxos := make([]domain.Vtxo, 0, len(rows)) - for _, row := range rows { - vtxoKey := domain.VtxoKey{ - Txid: row.vtxo.Txid, - VOut: uint32(row.vtxo.Vout), - } - uncondForfeitTxs := uncondForfeitTxsMap[vtxoKey] - vtxos = append(vtxos, rowToVtxo(row.vtxo, uncondForfeitTxs)) + for _, vtxo := range rows { + vtxos = append(vtxos, rowToVtxo(vtxo)) } return vtxos, nil diff --git a/server/internal/infrastructure/tx-builder/covenant/builder.go b/server/internal/infrastructure/tx-builder/covenant/builder.go index db0c87274..37dcaee58 100644 --- a/server/internal/infrastructure/tx-builder/covenant/builder.go +++ b/server/internal/infrastructure/tx-builder/covenant/builder.go @@ -365,8 +365,8 @@ func (b *txBuilder) FindLeaves( func (b *txBuilder) BuildAsyncPaymentTransactions( _ []domain.Vtxo, _ *secp256k1.PublicKey, _ []domain.Receiver, -) (*domain.AsyncPaymentTxs, error) { - return nil, fmt.Errorf("not implemented") +) (string, error) { + return "", fmt.Errorf("not implemented") } func (b *txBuilder) createPoolTx( diff --git a/server/internal/infrastructure/tx-builder/covenantless/builder.go b/server/internal/infrastructure/tx-builder/covenantless/builder.go index 99c284936..bfa8ee3e6 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/builder.go +++ b/server/internal/infrastructure/tx-builder/covenantless/builder.go @@ -402,28 +402,25 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri func (b *txBuilder) BuildAsyncPaymentTransactions( vtxos []domain.Vtxo, aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver, -) (*domain.AsyncPaymentTxs, error) { +) (string, error) { if len(vtxos) <= 0 { - return nil, fmt.Errorf("missing vtxos") + return "", fmt.Errorf("missing vtxos") } ins := make([]*wire.OutPoint, 0, len(vtxos)) outs := make([]*wire.TxOut, 0, len(receivers)) - unconditionalForfeitTxs := make([]string, 0, len(vtxos)) + witnessUtxos := make(map[int]*wire.TxOut) + tapscripts := make(map[int]*psbt.TaprootTapLeafScript) + redeemTxWeightEstimator := &input.TxWeightEstimator{} - for _, vtxo := range vtxos { + for index, vtxo := range vtxos { if vtxo.Spent || vtxo.Redeemed || vtxo.Swept { - return nil, fmt.Errorf("all vtxos must be unspent") - } - - aspScript, err := common.P2TRScript(aspPubKey) - if err != nil { - return nil, err + return "", fmt.Errorf("all vtxos must be unspent") } vtxoTxID, err := chainhash.NewHashFromStr(vtxo.Txid) if err != nil { - return nil, err + return "", err } vtxoOutpoint := &wire.OutPoint{ @@ -433,104 +430,60 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor) if err != nil { - return nil, err + return "", err } vtxoTapKey, vtxoTree, err := vtxoScript.TapTree() if err != nil { - return nil, err + return "", err } vtxoOutputScript, err := common.P2TRScript(vtxoTapKey) if err != nil { - return nil, err + return "", err } - var tapscript *waddrmgr.Tapscript - forfeitTxWeightEstimator := &input.TxWeightEstimator{} + witnessUtxos[index] = &wire.TxOut{ + Value: int64(vtxo.Amount), + PkScript: vtxoOutputScript, + } if defaultVtxoScript, ok := vtxoScript.(*bitcointree.DefaultVtxoScript); ok { - forfeitClosure := &bitcointree.MultisigClosure{ + forfeitLeaf := bitcointree.MultisigClosure{ Pubkey: defaultVtxoScript.Owner, AspPubkey: defaultVtxoScript.Asp, } - forfeitLeaf, err := forfeitClosure.Leaf() + tapLeaf, err := forfeitLeaf.Leaf() if err != nil { - return nil, err + return "", err } - forfeitProof, err := vtxoTree.GetTaprootMerkleProof(forfeitLeaf.TapHash()) + leafProof, err := vtxoTree.GetTaprootMerkleProof(tapLeaf.TapHash()) if err != nil { - return nil, err + return "", err } - ctrlBlock, err := txscript.ParseControlBlock(forfeitProof.ControlBlock) + tapscripts[index] = &psbt.TaprootTapLeafScript{ + ControlBlock: leafProof.ControlBlock, + Script: leafProof.Script, + LeafVersion: txscript.BaseLeafVersion, + } + + ctrlBlock, err := txscript.ParseControlBlock(leafProof.ControlBlock) if err != nil { - return nil, err + return "", err } - tapscript = &waddrmgr.Tapscript{ - RevealedScript: forfeitProof.Script, + redeemTxWeightEstimator.AddTapscriptInput(64*2, &waddrmgr.Tapscript{ + RevealedScript: leafProof.Script, ControlBlock: ctrlBlock, - } - forfeitTxWeightEstimator.AddTapscriptInput(64*2, tapscript) - forfeitTxWeightEstimator.AddP2TROutput() // ASP output + }) } else { - return nil, fmt.Errorf("vtxo script is not a default vtxo script, cannot be async spent") - } - - forfeitTxFee, err := b.wallet.MinRelayFee(context.Background(), uint64(forfeitTxWeightEstimator.VSize())) - if err != nil { - return nil, err - } - - if forfeitTxFee >= vtxo.Amount { - return nil, fmt.Errorf("forfeit tx fee is higher than the amount of the vtxo") - } - - output := &wire.TxOut{ - PkScript: aspScript, - Value: int64(vtxo.Amount - forfeitTxFee), + return "", fmt.Errorf("vtxo %s:%d script is not default script, can't be async spent", vtxo.Txid, vtxo.VOut) } - unconditionnalForfeitPtx, err := psbt.New( - []*wire.OutPoint{vtxoOutpoint}, - []*wire.TxOut{output}, - 2, - 0, - []uint32{wire.MaxTxInSequenceNum}, - ) - if err != nil { - return nil, err - } - - unconditionnalForfeitPtx.Inputs[0].WitnessUtxo = &wire.TxOut{ - Value: int64(vtxo.Amount), - PkScript: vtxoOutputScript, - } - - ctrlBlock, err := tapscript.ControlBlock.ToBytes() - if err != nil { - return nil, err - } - - unconditionnalForfeitPtx.Inputs[0].TaprootLeafScript = []*psbt.TaprootTapLeafScript{ - { - Script: tapscript.RevealedScript, - ControlBlock: ctrlBlock, - LeafVersion: txscript.BaseLeafVersion, - }, - } - - forfeitTx, err := unconditionnalForfeitPtx.B64Encode() - if err != nil { - return nil, err - } - - unconditionalForfeitTxs = append(unconditionalForfeitTxs, forfeitTx) ins = append(ins, vtxoOutpoint) - redeemTxWeightEstimator.AddTapscriptInput(64*2, tapscript) } for range receivers { @@ -539,27 +492,27 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( redeemTxMinRelayFee, err := b.wallet.MinRelayFee(context.Background(), uint64(redeemTxWeightEstimator.VSize())) if err != nil { - return nil, err + return "", err } if redeemTxMinRelayFee >= receivers[len(receivers)-1].Amount { - return nil, fmt.Errorf("redeem tx fee is higher than the amount of the change receiver") + return "", fmt.Errorf("redeem tx fee is higher than the amount of the change receiver") } for i, receiver := range receivers { offchainScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor) if err != nil { - return nil, err + return "", err } receiverVtxoTaprootKey, _, err := offchainScript.TapTree() if err != nil { - return nil, err + return "", err } newVtxoScript, err := common.P2TRScript(receiverVtxoTaprootKey) if err != nil { - return nil, err + return "", err } // Deduct the min relay fee from the very last receiver which is supposed @@ -583,34 +536,27 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( ins, outs, 2, 0, sequences, ) if err != nil { - return nil, err + return "", err } for i := range redeemPtx.Inputs { - unconditionnalForfeitPsbt, _ := psbt.NewFromRawBytes( - strings.NewReader(unconditionalForfeitTxs[i]), true, - ) - redeemPtx.Inputs[i].WitnessUtxo = unconditionnalForfeitPsbt.Inputs[0].WitnessUtxo - redeemPtx.Inputs[i].TaprootInternalKey = unconditionnalForfeitPsbt.Inputs[0].TaprootInternalKey - redeemPtx.Inputs[i].TaprootLeafScript = unconditionnalForfeitPsbt.Inputs[0].TaprootLeafScript + redeemPtx.Inputs[i].WitnessUtxo = witnessUtxos[i] + redeemPtx.Inputs[i].TaprootLeafScript = []*psbt.TaprootTapLeafScript{tapscripts[i]} } redeemTx, err := redeemPtx.B64Encode() if err != nil { - return nil, err + return "", err } signedRedeemTx, err := b.wallet.SignTransactionTapscript( context.Background(), redeemTx, nil, ) if err != nil { - return nil, err + return "", err } - return &domain.AsyncPaymentTxs{ - RedeemTx: signedRedeemTx, - UnconditionalForfeitTxs: unconditionalForfeitTxs, - }, nil + return signedRedeemTx, nil } // TODO use lnd CoinSelect to craft the pool tx diff --git a/server/internal/interface/grpc/handlers/arkservice.go b/server/internal/interface/grpc/handlers/arkservice.go index e8c52c947..b0dd9a1e3 100644 --- a/server/internal/interface/grpc/handlers/arkservice.go +++ b/server/internal/interface/grpc/handlers/arkservice.go @@ -362,7 +362,7 @@ func (h *handler) CreatePayment( } } - redeemTx, unconditionalForfeitTxs, err := h.svc.CreateAsyncPayment( + redeemTx, err := h.svc.CreateAsyncPayment( ctx, inputs, receivers, ) if err != nil { @@ -370,8 +370,7 @@ func (h *handler) CreatePayment( } return &arkv1.CreatePaymentResponse{ - SignedRedeemTx: redeemTx, - UsignedUnconditionalForfeitTxs: unconditionalForfeitTxs, + SignedRedeemTx: redeemTx, }, nil } @@ -382,12 +381,8 @@ func (h *handler) CompletePayment( return nil, status.Error(codes.InvalidArgument, "missing signed redeem tx") } - if len(req.GetSignedUnconditionalForfeitTxs()) <= 0 { - return nil, status.Error(codes.InvalidArgument, "missing signed unconditional forfeit txs") - } - if err := h.svc.CompleteAsyncPayment( - ctx, req.GetSignedRedeemTx(), req.GetSignedUnconditionalForfeitTxs(), + ctx, req.GetSignedRedeemTx(), ); err != nil { return nil, err } diff --git a/server/internal/interface/grpc/handlers/parser.go b/server/internal/interface/grpc/handlers/parser.go index c858577fe..22038433d 100644 --- a/server/internal/interface/grpc/handlers/parser.go +++ b/server/internal/interface/grpc/handlers/parser.go @@ -65,13 +65,6 @@ type vtxoList []domain.Vtxo func (v vtxoList) toProto() []*arkv1.Vtxo { list := make([]*arkv1.Vtxo, 0, len(v)) for _, vv := range v { - var pendingData *arkv1.PendingPayment - if vv.AsyncPayment != nil { - pendingData = &arkv1.PendingPayment{ - RedeemTx: vv.AsyncPayment.RedeemTx, - UnconditionalForfeitTxs: vv.AsyncPayment.UnconditionalForfeitTxs, - } - } list = append(list, &arkv1.Vtxo{ Outpoint: &arkv1.Outpoint{ Txid: vv.Txid, @@ -84,7 +77,7 @@ func (v vtxoList) toProto() []*arkv1.Vtxo { ExpireAt: vv.ExpireAt, SpentBy: vv.SpentBy, Swept: vv.Swept, - PendingData: pendingData, + RedeemTx: vv.RedeemTx, Pending: vv.Pending, }) } From 0d39bb6b9ff8477dbcc03d9c158e78476aa72d75 Mon Sep 17 00:00:00 2001 From: Louis Singer <41042567+louisinger@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:12:46 +0200 Subject: [PATCH 06/10] Add integration tests for sweeping rounds (#339) * add "block" scheduler type + sweep integration test * increase timeout in integrationtests * remove config logs * rename scheduler package name * rename package * rename packages --- common/bip68.go | 52 +++---- common/bip68_test.go | 43 ----- common/bitcointree/script.go | 20 ++- common/bitcointree/script_test.go | 30 ++++ common/descriptor/expression.go | 13 +- common/fixtures/bip68.json | 67 -------- common/tree/script.go | 20 ++- docker-compose.clark.regtest.yml | 3 +- docker-compose.regtest.yml | 4 +- pkg/client-sdk/explorer/explorer.go | 2 +- server/Makefile | 4 +- server/internal/app-config/config.go | 43 +++-- server/internal/core/application/admin.go | 18 ++- server/internal/core/application/covenant.go | 8 +- .../internal/core/application/covenantless.go | 10 +- server/internal/core/application/sweeper.go | 23 ++- server/internal/core/application/utils.go | 23 ++- server/internal/core/ports/scanner.go | 2 +- server/internal/core/ports/scheduler.go | 13 +- server/internal/core/ports/tx_builder.go | 2 +- .../infrastructure/db/badger/vtxo_repo.go | 2 +- .../infrastructure/db/sqlite/vtxo_repo.go | 2 +- .../infrastructure/scheduler/block/service.go | 147 ++++++++++++++++++ .../scheduler/gocron/service.go | 23 +-- .../tx-builder/covenant/builder.go | 6 +- .../tx-builder/covenant/mocks_test.go | 9 +- .../tx-builder/covenant/sweep.go | 2 +- .../tx-builder/covenantless/builder.go | 8 +- .../tx-builder/covenantless/mocks_test.go | 9 +- .../tx-builder/covenantless/sweep.go | 2 +- .../wallet/btc-embedded/bitcoind.go | 20 ++- .../wallet/btc-embedded/esplora.go | 17 +- .../wallet/btc-embedded/wallet.go | 4 +- .../wallet/liquid-standalone/transaction.go | 22 +-- server/test/e2e/covenant/e2e_test.go | 50 ++++-- server/test/e2e/covenantless/e2e_test.go | 30 ++++ server/test/e2e/test_utils.go | 3 + 37 files changed, 477 insertions(+), 279 deletions(-) delete mode 100644 common/bip68_test.go create mode 100644 common/bitcointree/script_test.go delete mode 100644 common/fixtures/bip68.json create mode 100644 server/internal/infrastructure/scheduler/block/service.go diff --git a/common/bip68.go b/common/bip68.go index 333dc4529..e163da06b 100644 --- a/common/bip68.go +++ b/common/bip68.go @@ -1,8 +1,10 @@ package common import ( - "encoding/hex" "fmt" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/txscript" ) const ( @@ -18,42 +20,33 @@ func closerToModulo512(x uint) uint { return x - (x % 512) } -func BIP68EncodeAsNumber(seconds uint) (uint32, error) { - seconds = closerToModulo512(seconds) - if seconds > SECONDS_MAX { - return 0, fmt.Errorf("seconds too large, max is %d", SECONDS_MAX) - } - if seconds%SECONDS_MOD != 0 { - return 0, fmt.Errorf("seconds must be a multiple of %d", SECONDS_MOD) +func BIP68Sequence(locktime uint) (uint32, error) { + isSeconds := locktime >= 512 + if isSeconds { + locktime = closerToModulo512(locktime) + if locktime > SECONDS_MAX { + return 0, fmt.Errorf("seconds too large, max is %d", SECONDS_MAX) + } + if locktime%SECONDS_MOD != 0 { + return 0, fmt.Errorf("seconds must be a multiple of %d", SECONDS_MOD) + } } - asNumber := SEQUENCE_LOCKTIME_TYPE_FLAG | (seconds >> SEQUENCE_LOCKTIME_GRANULARITY) - return uint32(asNumber), nil + return blockchain.LockTimeToSequence(isSeconds, uint32(locktime)), nil } -// BIP68Encode returns the encoded sequence locktime for the given number of seconds. -func BIP68Encode(seconds uint) ([]byte, error) { - asNumber, err := BIP68EncodeAsNumber(seconds) - if err != nil { - return nil, err - } - hexString := fmt.Sprintf("%x", asNumber) - reversed, err := hex.DecodeString(hexString) +func BIP68DecodeSequence(sequence []byte) (uint, error) { + scriptNumber, err := txscript.MakeScriptNum(sequence, true, len(sequence)) if err != nil { - return nil, err + return 0, err } - for i, j := 0, len(reversed)-1; i < j; i, j = i+1, j-1 { - reversed[i], reversed[j] = reversed[j], reversed[i] - } - return reversed, nil -} -func BIP68Decode(sequence []byte) (uint, error) { - var asNumber int64 - for i := len(sequence) - 1; i >= 0; i-- { - asNumber = asNumber<<8 | int64(sequence[i]) + if scriptNumber >= txscript.OP_1 && scriptNumber <= txscript.OP_16 { + scriptNumber = scriptNumber - (txscript.OP_1 - 1) } + asNumber := int64(scriptNumber) + if asNumber&SEQUENCE_LOCKTIME_DISABLE_FLAG != 0 { return 0, fmt.Errorf("sequence is disabled") } @@ -61,5 +54,6 @@ func BIP68Decode(sequence []byte) (uint, error) { seconds := asNumber & SEQUENCE_LOCKTIME_MASK << SEQUENCE_LOCKTIME_GRANULARITY return uint(seconds), nil } - return 0, fmt.Errorf("sequence is encoded as block number") + + return uint(asNumber), nil } diff --git a/common/bip68_test.go b/common/bip68_test.go deleted file mode 100644 index 1a4d63d67..000000000 --- a/common/bip68_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package common_test - -import ( - "encoding/json" - "os" - "testing" - - sdk "github.com/ark-network/ark/common" - "github.com/stretchr/testify/require" -) - -func TestBIP68(t *testing.T) { - data, err := os.ReadFile("fixtures/bip68.json") - require.NoError(t, err) - - var testCases []struct { - Input uint `json:"seconds"` - Expected int64 `json:"sequence"` - Desc string `json:"description"` - } - err = json.Unmarshal(data, &testCases) - require.NoError(t, err) - require.NotEmpty(t, testCases) - - for _, tc := range testCases { - t.Run(tc.Desc, func(t *testing.T) { - actual, err := sdk.BIP68Encode(tc.Input) - require.NoError(t, err) - - var asNumber int64 - for i := len(actual) - 1; i >= 0; i-- { - asNumber = asNumber<<8 | int64(actual[i]) - } - - require.Equal(t, tc.Expected, asNumber) - - decoded, err := sdk.BIP68Decode(actual) - require.NoError(t, err) - - require.Equal(t, tc.Input, decoded) - }) - } -} diff --git a/common/bitcointree/script.go b/common/bitcointree/script.go index 1944f4362..5e799fe8f 100644 --- a/common/bitcointree/script.go +++ b/common/bitcointree/script.go @@ -109,9 +109,12 @@ func (d *CSVSigClosure) Decode(script []byte) (bool, error) { return false, nil } - sequence := script[1:csvIndex] + sequence := script[:csvIndex] + if len(sequence) > 1 { + sequence = sequence[1:] + } - seconds, err := common.BIP68Decode(sequence) + seconds, err := common.BIP68DecodeSequence(sequence) if err != nil { return false, err } @@ -162,15 +165,18 @@ func decodeChecksigScript(script []byte) (bool, *secp256k1.PublicKey, error) { // checkSequenceVerifyScript without checksig func encodeCsvScript(seconds uint) ([]byte, error) { - sequence, err := common.BIP68Encode(seconds) + sequence, err := common.BIP68Sequence(seconds) if err != nil { return nil, err } - return txscript.NewScriptBuilder().AddData(sequence).AddOps([]byte{ - txscript.OP_CHECKSEQUENCEVERIFY, - txscript.OP_DROP, - }).Script() + return txscript.NewScriptBuilder(). + AddInt64(int64(sequence)). + AddOps([]byte{ + txscript.OP_CHECKSEQUENCEVERIFY, + txscript.OP_DROP, + }). + Script() } // checkSequenceVerifyScript + checksig diff --git a/common/bitcointree/script_test.go b/common/bitcointree/script_test.go new file mode 100644 index 000000000..d71c8d598 --- /dev/null +++ b/common/bitcointree/script_test.go @@ -0,0 +1,30 @@ +package bitcointree_test + +import ( + "testing" + + "github.com/ark-network/ark/common/bitcointree" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/stretchr/testify/require" +) + +func TestRoundTripCSV(t *testing.T) { + seckey, err := secp256k1.GeneratePrivateKey() + require.NoError(t, err) + + csvSig := &bitcointree.CSVSigClosure{ + Pubkey: seckey.PubKey(), + Seconds: 1024, + } + + leaf, err := csvSig.Leaf() + require.NoError(t, err) + + var cl bitcointree.CSVSigClosure + + valid, err := cl.Decode(leaf.Script) + require.NoError(t, err) + require.True(t, valid) + + require.Equal(t, csvSig.Seconds, cl.Seconds) +} diff --git a/common/descriptor/expression.go b/common/descriptor/expression.go index b2ca9aedf..cbf5d1bdc 100644 --- a/common/descriptor/expression.go +++ b/common/descriptor/expression.go @@ -130,15 +130,18 @@ func (e *Older) Parse(policy string) error { } func (e *Older) Script(bool) (string, error) { - sequence, err := common.BIP68Encode(e.Timeout) + sequence, err := common.BIP68Sequence(e.Timeout) if err != nil { return "", err } - script, err := txscript.NewScriptBuilder().AddData(sequence).AddOps([]byte{ - txscript.OP_CHECKSEQUENCEVERIFY, - txscript.OP_DROP, - }).Script() + script, err := txscript.NewScriptBuilder(). + AddInt64(int64(sequence)). + AddOps([]byte{ + txscript.OP_CHECKSEQUENCEVERIFY, + txscript.OP_DROP, + }). + Script() if err != nil { return "", err } diff --git a/common/fixtures/bip68.json b/common/fixtures/bip68.json deleted file mode 100644 index fac4f860a..000000000 --- a/common/fixtures/bip68.json +++ /dev/null @@ -1,67 +0,0 @@ -[ - { - "description": "0x00400000 (00000000010000000000000000000000)", - "seconds": 0, - "sequence": 4194304 - }, - { - "description": "0x00400001 (00000000010000000000000000000001)", - "seconds": 512, - "sequence": 4194305 - }, - { - "description": "0x00400002 (00000000010000000000000000000010)", - "seconds": 1024, - "sequence": 4194306 - }, - { - "description": "0x00400003 (00000000010000000000000000000011)", - "seconds": 1536, - "sequence": 4194307 - }, - { - "description": "0x00400004 (00000000010000000000000000000100)", - "seconds": 2048, - "sequence": 4194308 - }, - { - "description": "0x00400005 (00000000010000000000000000000101)", - "seconds": 2560, - "sequence": 4194309 - }, - { - "description": "0x00400006 (00000000010000000000000000000110)", - "seconds": 3072, - "sequence": 4194310 - }, - { - "description": "0x00400007 (00000000010000000000000000000111)", - "seconds": 3584, - "sequence": 4194311 - }, - { - "description": "0x00400008 (00000000010000000000000000001000)", - "seconds": 4096, - "sequence": 4194312 - }, - { - "description": "0x00400009 (00000000010000000000000000001001)", - "seconds": 4608, - "sequence": 4194313 - }, - { - "description": "0x0040000a (00000000010000000000000000001010)", - "seconds": 5120, - "sequence": 4194314 - }, - { - "description": "0x0040000b (00000000010000000000000000001011)", - "seconds": 5632, - "sequence": 4194315 - }, - { - "description": "0x0040000c (00000000010000000000000000001100)", - "seconds": 6144, - "sequence": 4194316 - } -] \ No newline at end of file diff --git a/common/tree/script.go b/common/tree/script.go index 8a0a7e1b7..1a7f56f65 100644 --- a/common/tree/script.go +++ b/common/tree/script.go @@ -130,9 +130,12 @@ func (d *CSVSigClosure) Decode(script []byte) (bool, error) { return false, nil } - sequence := script[1:csvIndex] + sequence := script[:csvIndex] + if len(sequence) > 1 { + sequence = sequence[1:] + } - seconds, err := common.BIP68Decode(sequence) + seconds, err := common.BIP68DecodeSequence(sequence) if err != nil { return false, err } @@ -369,15 +372,18 @@ func decodeChecksigScript(script []byte) (bool, *secp256k1.PublicKey, error) { // checkSequenceVerifyScript without checksig func encodeCsvScript(seconds uint) ([]byte, error) { - sequence, err := common.BIP68Encode(seconds) + sequence, err := common.BIP68Sequence(seconds) if err != nil { return nil, err } - return txscript.NewScriptBuilder().AddData(sequence).AddOps([]byte{ - txscript.OP_CHECKSEQUENCEVERIFY, - txscript.OP_DROP, - }).Script() + return txscript.NewScriptBuilder(). + AddInt64(int64(sequence)). + AddOps([]byte{ + txscript.OP_CHECKSEQUENCEVERIFY, + txscript.OP_DROP, + }). + Script() } // checkSequenceVerifyScript + checksig diff --git a/docker-compose.clark.regtest.yml b/docker-compose.clark.regtest.yml index 6c24f6a82..4b2d8dd09 100644 --- a/docker-compose.clark.regtest.yml +++ b/docker-compose.clark.regtest.yml @@ -9,12 +9,13 @@ services: - ARK_ROUND_INTERVAL=10 - ARK_NETWORK=regtest - ARK_LOG_LEVEL=5 - - ARK_ROUND_LIFETIME=512 + - ARK_ROUND_LIFETIME=20 - ARK_TX_BUILDER_TYPE=covenantless - ARK_ESPLORA_URL=http://chopsticks:3000 - ARK_BITCOIND_RPC_USER=admin1 - ARK_BITCOIND_RPC_PASS=123 - ARK_BITCOIND_RPC_HOST=bitcoin:18443 + - ARK_SCHEDULER_TYPE=block - ARK_NO_TLS=true - ARK_NO_MACAROONS=true - ARK_DATADIR=/app/data diff --git a/docker-compose.regtest.yml b/docker-compose.regtest.yml index 5fa207258..834da4341 100644 --- a/docker-compose.regtest.yml +++ b/docker-compose.regtest.yml @@ -31,7 +31,9 @@ services: - ARK_ROUND_INTERVAL=10 - ARK_NETWORK=liquidregtest - ARK_LOG_LEVEL=5 - - ARK_ROUND_LIFETIME=512 + - ARK_ESPLORA_URL=http://chopsticks-liquid:3000 + - ARK_ROUND_LIFETIME=20 + - ARK_SCHEDULER_TYPE=block - ARK_DB_TYPE=sqlite - ARK_TX_BUILDER_TYPE=covenant - ARK_PORT=6060 diff --git a/pkg/client-sdk/explorer/explorer.go b/pkg/client-sdk/explorer/explorer.go index 5aa18e438..949656f6e 100644 --- a/pkg/client-sdk/explorer/explorer.go +++ b/pkg/client-sdk/explorer/explorer.go @@ -35,7 +35,7 @@ type Utxo struct { } func (u *Utxo) Sequence() (uint32, error) { - return common.BIP68EncodeAsNumber(u.Delay) + return common.BIP68Sequence(u.Delay) } func newUtxo(explorerUtxo ExplorerUtxo, delay uint) Utxo { diff --git a/server/Makefile b/server/Makefile index 86d6d4d6c..d79bd8dd8 100755 --- a/server/Makefile +++ b/server/Makefile @@ -23,8 +23,8 @@ help: ## intergrationtest: runs integration tests integrationtest: @echo "Running integration tests..." - @go test -v -count 1 -timeout 300s github.com/ark-network/ark/server/test/e2e/covenant - @go test -v -count 1 -timeout 300s github.com/ark-network/ark/server/test/e2e/covenantless + @go test -v -count 1 -timeout 400s github.com/ark-network/ark/server/test/e2e/covenant + @go test -v -count 1 -timeout 400s github.com/ark-network/ark/server/test/e2e/covenantless ## lint: lint codebase lint: diff --git a/server/internal/app-config/config.go b/server/internal/app-config/config.go index 5e69960ad..59e6e75b7 100644 --- a/server/internal/app-config/config.go +++ b/server/internal/app-config/config.go @@ -8,7 +8,8 @@ import ( "github.com/ark-network/ark/server/internal/core/application" "github.com/ark-network/ark/server/internal/core/ports" "github.com/ark-network/ark/server/internal/infrastructure/db" - scheduler "github.com/ark-network/ark/server/internal/infrastructure/scheduler/gocron" + blockscheduler "github.com/ark-network/ark/server/internal/infrastructure/scheduler/block" + timescheduler "github.com/ark-network/ark/server/internal/infrastructure/scheduler/gocron" txbuilder "github.com/ark-network/ark/server/internal/infrastructure/tx-builder/covenant" cltxbuilder "github.com/ark-network/ark/server/internal/infrastructure/tx-builder/covenantless" fileunlocker "github.com/ark-network/ark/server/internal/infrastructure/unlocker/file" @@ -29,6 +30,7 @@ var ( } supportedSchedulers = supportedType{ "gocron": {}, + "block": {}, } supportedTxBuilders = supportedType{ "covenant": {}, @@ -115,11 +117,23 @@ func (c *Config) Validate() error { if len(c.WalletAddr) <= 0 { return fmt.Errorf("missing onchain wallet address") } - // round life time must be a multiple of 512 if c.RoundLifetime < minAllowedSequence { - return fmt.Errorf( - "invalid round lifetime, must be a at least %d", minAllowedSequence, - ) + if c.SchedulerType != "block" { + return fmt.Errorf("scheduler type must be block if round lifetime is expressed in blocks") + } + } else { + if c.SchedulerType != "gocron" { + return fmt.Errorf("scheduler type must be gocron if round lifetime is expressed in seconds") + } + + // round life time must be a multiple of 512 if expressed in seconds + if c.RoundLifetime%minAllowedSequence != 0 { + c.RoundLifetime -= c.RoundLifetime % minAllowedSequence + log.Infof( + "round lifetime must be a multiple of %d, rounded to %d", + minAllowedSequence, c.RoundLifetime, + ) + } } if c.UnilateralExitDelay < minAllowedSequence { @@ -134,14 +148,6 @@ func (c *Config) Validate() error { ) } - if c.RoundLifetime%minAllowedSequence != 0 { - c.RoundLifetime -= c.RoundLifetime % minAllowedSequence - log.Infof( - "round lifetime must be a multiple of %d, rounded to %d", - minAllowedSequence, c.RoundLifetime, - ) - } - if c.UnilateralExitDelay%minAllowedSequence != 0 { c.UnilateralExitDelay -= c.UnilateralExitDelay % minAllowedSequence log.Infof( @@ -328,7 +334,9 @@ func (c *Config) schedulerService() error { var err error switch c.SchedulerType { case "gocron": - svc = scheduler.NewScheduler() + svc = timescheduler.NewScheduler() + case "block": + svc, err = blockscheduler.NewScheduler(c.EsploraURL) default: err = fmt.Errorf("unknown scheduler type") } @@ -367,7 +375,12 @@ func (c *Config) appService() error { } func (c *Config) adminService() error { - c.adminSvc = application.NewAdminService(c.wallet, c.repo, c.txBuilder) + unit := ports.UnixTime + if c.RoundLifetime < minAllowedSequence { + unit = ports.BlockHeight + } + + c.adminSvc = application.NewAdminService(c.wallet, c.repo, c.txBuilder, unit) return nil } diff --git a/server/internal/core/application/admin.go b/server/internal/core/application/admin.go index 329352d55..f640f489f 100644 --- a/server/internal/core/application/admin.go +++ b/server/internal/core/application/admin.go @@ -50,16 +50,18 @@ type AdminService interface { } type adminService struct { - walletSvc ports.WalletService - repoManager ports.RepoManager - txBuilder ports.TxBuilder + walletSvc ports.WalletService + repoManager ports.RepoManager + txBuilder ports.TxBuilder + sweeperTimeUnit ports.TimeUnit } -func NewAdminService(walletSvc ports.WalletService, repoManager ports.RepoManager, txBuilder ports.TxBuilder) AdminService { +func NewAdminService(walletSvc ports.WalletService, repoManager ports.RepoManager, txBuilder ports.TxBuilder, timeUnit ports.TimeUnit) AdminService { return &adminService{ - walletSvc: walletSvc, - repoManager: repoManager, - txBuilder: txBuilder, + walletSvc: walletSvc, + repoManager: repoManager, + txBuilder: txBuilder, + sweeperTimeUnit: timeUnit, } } @@ -130,7 +132,7 @@ func (a *adminService) GetScheduledSweeps(ctx context.Context) ([]ScheduledSweep for _, round := range sweepableRounds { sweepable, err := findSweepableOutputs( - ctx, a.walletSvc, a.txBuilder, round.CongestionTree, + ctx, a.walletSvc, a.txBuilder, a.sweeperTimeUnit, round.CongestionTree, ) if err != nil { return nil, err diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index 1355d91db..abd8b2b07 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -177,7 +177,7 @@ func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input) return "", fmt.Errorf("failed to parse tx %s: %s", input.Txid, err) } - confirmed, blocktime, err := s.wallet.IsTransactionConfirmed(ctx, input.Txid) + confirmed, _, blocktime, err := s.wallet.IsTransactionConfirmed(ctx, input.Txid) if err != nil { return "", fmt.Errorf("failed to check tx %s: %s", input.Txid, err) } @@ -910,12 +910,10 @@ func (s *covenantService) scheduleSweepVtxosForRound(round *domain.Round) { return } - expirationTimestamp := time.Now().Add( - time.Duration(s.roundLifetime+30) * time.Second, - ) + expirationTime := s.sweeper.scheduler.AddNow(s.roundLifetime) if err := s.sweeper.schedule( - expirationTimestamp.Unix(), round.Txid, round.CongestionTree, + expirationTime, round.Txid, round.CongestionTree, ); err != nil { log.WithError(err).Warn("failed to schedule sweep tx") } diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index 24dc361ce..432c5a548 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -421,7 +421,7 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp return "", fmt.Errorf("failed to deserialize tx %s: %s", input.Txid, err) } - confirmed, blocktime, err := s.wallet.IsTransactionConfirmed(ctx, input.Txid) + confirmed, _, blocktime, err := s.wallet.IsTransactionConfirmed(ctx, input.Txid) if err != nil { return "", fmt.Errorf("failed to check tx %s: %s", input.Txid, err) } @@ -1316,13 +1316,9 @@ func (s *covenantlessService) scheduleSweepVtxosForRound(round *domain.Round) { return } - expirationTimestamp := time.Now().Add( - time.Duration(s.roundLifetime+30) * time.Second, - ) + expirationTimestamp := s.sweeper.scheduler.AddNow(s.roundLifetime) - if err := s.sweeper.schedule( - expirationTimestamp.Unix(), round.Txid, round.CongestionTree, - ); err != nil { + if err := s.sweeper.schedule(expirationTimestamp, round.Txid, round.CongestionTree); err != nil { log.WithError(err).Warn("failed to schedule sweep tx") } } diff --git a/server/internal/core/application/sweeper.go b/server/internal/core/application/sweeper.go index 33ddb383a..d66064421 100644 --- a/server/internal/core/application/sweeper.go +++ b/server/internal/core/application/sweeper.go @@ -3,6 +3,7 @@ package application import ( "context" "fmt" + "sync" "time" "github.com/ark-network/ark/common/tree" @@ -22,6 +23,7 @@ type sweeper struct { scheduler ports.SchedulerService // cache of scheduled tasks, avoid scheduling the same sweep event multiple times + locker sync.Locker scheduledTasks map[string]struct{} } @@ -36,6 +38,7 @@ func newSweeper( repoManager, builder, scheduler, + &sync.Mutex{}, make(map[string]struct{}), } } @@ -62,6 +65,8 @@ func (s *sweeper) stop() { // removeTask update the cached map of scheduled tasks func (s *sweeper) removeTask(treeRootTxid string) { + s.locker.Lock() + defer s.locker.Unlock() delete(s.scheduledTasks, treeRootTxid) } @@ -84,13 +89,22 @@ func (s *sweeper) schedule( } task := s.createTask(roundTxid, congestionTree) - fancyTime := time.Unix(expirationTimestamp, 0).Format("2006-01-02 15:04:05") + + var fancyTime string + if s.scheduler.Unit() == ports.UnixTime { + fancyTime = time.Unix(expirationTimestamp, 0).Format("2006-01-02 15:04:05") + } else { + fancyTime = fmt.Sprintf("block %d", expirationTimestamp) + } log.Debugf("scheduled sweep for round %s at %s", roundTxid, fancyTime) + if err := s.scheduler.ScheduleTaskOnce(expirationTimestamp, task); err != nil { return err } + s.locker.Lock() s.scheduledTasks[root.Txid] = struct{}{} + s.locker.Unlock() if err := s.updateVtxoExpirationTime(congestionTree, expirationTimestamp); err != nil { log.WithError(err).Error("error while updating vtxo expiration time") @@ -120,7 +134,7 @@ func (s *sweeper) createTask( vtxoKeys := make([]domain.VtxoKey, 0) // vtxos associated to the sweep inputs // inspect the congestion tree to find onchain shared outputs - sharedOutputs, err := findSweepableOutputs(ctx, s.wallet, s.builder, congestionTree) + sharedOutputs, err := findSweepableOutputs(ctx, s.wallet, s.builder, s.scheduler.Unit(), congestionTree) if err != nil { log.WithError(err).Error("error while inspecting congestion tree") return @@ -128,7 +142,7 @@ func (s *sweeper) createTask( for expiredAt, inputs := range sharedOutputs { // if the shared outputs are not expired, schedule a sweep task for it - if time.Unix(expiredAt, 0).After(time.Now()) { + if s.scheduler.AfterNow(expiredAt) { subtrees, err := computeSubTrees(congestionTree, inputs) if err != nil { log.WithError(err).Error("error while computing subtrees") @@ -136,8 +150,7 @@ func (s *sweeper) createTask( } for _, subTree := range subtrees { - // mitigate the risk to get BIP68 non-final errors by scheduling the task 30 seconds after the expiration time - if err := s.schedule(int64(expiredAt), roundTxid, subTree); err != nil { + if err := s.schedule(expiredAt, roundTxid, subTree); err != nil { log.WithError(err).Error("error while scheduling sweep task") continue } diff --git a/server/internal/core/application/utils.go b/server/internal/core/application/utils.go index 8be468b02..f797b4098 100644 --- a/server/internal/core/application/utils.go +++ b/server/internal/core/application/utils.go @@ -259,17 +259,18 @@ func findSweepableOutputs( ctx context.Context, walletSvc ports.WalletService, txbuilder ports.TxBuilder, + schedulerUnit ports.TimeUnit, congestionTree tree.CongestionTree, ) (map[int64][]ports.SweepInput, error) { sweepableOutputs := make(map[int64][]ports.SweepInput) - blocktimeCache := make(map[string]int64) // txid -> blocktime + blocktimeCache := make(map[string]int64) // txid -> blocktime / blockheight nodesToCheck := congestionTree[0] // init with the root for len(nodesToCheck) > 0 { newNodesToCheck := make([]tree.Node, 0) for _, node := range nodesToCheck { - isConfirmed, blocktime, err := walletSvc.IsTransactionConfirmed(ctx, node.Txid) + isConfirmed, height, blocktime, err := walletSvc.IsTransactionConfirmed(ctx, node.Txid) if err != nil { return nil, err } @@ -279,21 +280,31 @@ func findSweepableOutputs( if !isConfirmed { if _, ok := blocktimeCache[node.ParentTxid]; !ok { - isConfirmed, blocktime, err := walletSvc.IsTransactionConfirmed(ctx, node.ParentTxid) + isConfirmed, height, blocktime, err := walletSvc.IsTransactionConfirmed(ctx, node.ParentTxid) if !isConfirmed || err != nil { return nil, fmt.Errorf("tx %s not found", node.ParentTxid) } - blocktimeCache[node.ParentTxid] = blocktime + if schedulerUnit == ports.BlockHeight { + blocktimeCache[node.ParentTxid] = height + } else { + blocktimeCache[node.ParentTxid] = blocktime + } } - expirationTime, sweepInput, err = txbuilder.GetSweepInput(blocktimeCache[node.ParentTxid], node) + var lifetime int64 + lifetime, sweepInput, err = txbuilder.GetSweepInput(node) if err != nil { return nil, err } + expirationTime = blocktimeCache[node.ParentTxid] + lifetime } else { // cache the blocktime for future use - blocktimeCache[node.Txid] = int64(blocktime) + if schedulerUnit == ports.BlockHeight { + blocktimeCache[node.Txid] = height + } else { + blocktimeCache[node.Txid] = blocktime + } // if the tx is onchain, it means that the input is spent // add the children to the nodes in order to check them during the next iteration diff --git a/server/internal/core/ports/scanner.go b/server/internal/core/ports/scanner.go index 3a8325674..f4969c2ce 100644 --- a/server/internal/core/ports/scanner.go +++ b/server/internal/core/ports/scanner.go @@ -14,5 +14,5 @@ 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 - IsTransactionConfirmed(ctx context.Context, txid string) (isConfirmed bool, blocktime int64, err error) + IsTransactionConfirmed(ctx context.Context, txid string) (isConfirmed bool, blocknumber int64, blocktime int64, err error) } diff --git a/server/internal/core/ports/scheduler.go b/server/internal/core/ports/scheduler.go index c1333fdaa..4262b1f72 100644 --- a/server/internal/core/ports/scheduler.go +++ b/server/internal/core/ports/scheduler.go @@ -1,9 +1,18 @@ package ports +type TimeUnit int + +const ( + UnixTime TimeUnit = iota + BlockHeight +) + type SchedulerService interface { Start() Stop() - ScheduleTask(interval int64, immediate bool, task func()) error - ScheduleTaskOnce(delay int64, task func()) error + Unit() TimeUnit + AddNow(lifetime int64) int64 + AfterNow(expiry int64) bool + ScheduleTaskOnce(at int64, task func()) error } diff --git a/server/internal/core/ports/tx_builder.go b/server/internal/core/ports/tx_builder.go index 4751f93fe..4b1bc104a 100644 --- a/server/internal/core/ports/tx_builder.go +++ b/server/internal/core/ports/tx_builder.go @@ -34,7 +34,7 @@ type TxBuilder interface { ) (roundTx string, congestionTree tree.CongestionTree, connectorAddress string, err error) BuildForfeitTxs(poolTx string, payments []domain.Payment, minRelayFeeRate chainfee.SatPerKVByte) (connectors []string, forfeitTxs []string, err error) BuildSweepTx(inputs []SweepInput) (signedSweepTx string, err error) - GetSweepInput(parentblocktime int64, node tree.Node) (expirationtime int64, sweepInput SweepInput, err error) + GetSweepInput(node tree.Node) (lifetime int64, sweepInput SweepInput, err error) FinalizeAndExtract(tx string) (txhex string, err error) VerifyTapscriptPartialSigs(tx string) (valid bool, txid string, err error) // FindLeaves returns all the leaves txs that are reachable from the given outpoint diff --git a/server/internal/infrastructure/db/badger/vtxo_repo.go b/server/internal/infrastructure/db/badger/vtxo_repo.go index 160892525..3c6698cf0 100644 --- a/server/internal/infrastructure/db/badger/vtxo_repo.go +++ b/server/internal/infrastructure/db/badger/vtxo_repo.go @@ -117,7 +117,7 @@ func (r *vtxoRepository) GetAllVtxos( spentVtxos := make([]domain.Vtxo, 0, len(vtxos)) unspentVtxos := make([]domain.Vtxo, 0, len(vtxos)) for _, vtxo := range vtxos { - if vtxo.Spent { + if vtxo.Spent || vtxo.Swept { spentVtxos = append(spentVtxos, vtxo) } else { unspentVtxos = append(unspentVtxos, vtxo) diff --git a/server/internal/infrastructure/db/sqlite/vtxo_repo.go b/server/internal/infrastructure/db/sqlite/vtxo_repo.go index af9ef53ae..c0113c205 100644 --- a/server/internal/infrastructure/db/sqlite/vtxo_repo.go +++ b/server/internal/infrastructure/db/sqlite/vtxo_repo.go @@ -113,7 +113,7 @@ func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]doma spentVtxos := make([]domain.Vtxo, 0) for _, vtxo := range vtxos { - if vtxo.Spent { + if vtxo.Spent || vtxo.Swept { spentVtxos = append(spentVtxos, vtxo) } else { unspentVtxos = append(unspentVtxos, vtxo) diff --git a/server/internal/infrastructure/scheduler/block/service.go b/server/internal/infrastructure/scheduler/block/service.go new file mode 100644 index 000000000..d22a60f02 --- /dev/null +++ b/server/internal/infrastructure/scheduler/block/service.go @@ -0,0 +1,147 @@ +package blockscheduler + +import ( + "fmt" + "net/http" + "net/url" + "sync" + "time" + + "github.com/ark-network/ark/server/internal/core/ports" + "github.com/sirupsen/logrus" +) + +const tipHeightEndpoit = "/blocks/tip/height" + +type service struct { + tipURL string + lock sync.Locker + taskes map[int64][]func() + stopCh chan struct{} +} + +func NewScheduler(esploraURL string) (ports.SchedulerService, error) { + if len(esploraURL) == 0 { + return nil, fmt.Errorf("esplora URL is required") + } + + tipURL, err := url.JoinPath(esploraURL, tipHeightEndpoit) + if err != nil { + return nil, err + } + + return &service{ + tipURL, + &sync.Mutex{}, + make(map[int64][]func()), + make(chan struct{}), + }, nil +} + +func (s *service) Start() { + go func() { + for { + select { + case <-s.stopCh: + return + default: + time.Sleep(10 * time.Second) + taskes, err := s.popTaskes() + if err != nil { + fmt.Println("error fetching tasks:", err) + continue + } + + logrus.Debugf("fetched %d tasks", len(taskes)) + for _, task := range taskes { + go task() + } + } + } + }() +} + +func (s *service) Stop() { + s.stopCh <- struct{}{} + close(s.stopCh) +} + +func (s *service) Unit() ports.TimeUnit { + return ports.BlockHeight +} + +func (s *service) AddNow(lifetime int64) int64 { + tip, err := s.fetchTipHeight() + if err != nil { + return 0 + } + + return tip + lifetime +} + +func (s *service) AfterNow(expiry int64) bool { + tip, err := s.fetchTipHeight() + if err != nil { + return false + } + + return expiry > tip +} + +func (s *service) ScheduleTaskOnce(at int64, task func()) error { + s.lock.Lock() + defer s.lock.Unlock() + + if _, ok := s.taskes[at]; !ok { + s.taskes[at] = make([]func(), 0) + } + + s.taskes[at] = append(s.taskes[at], task) + + return nil +} + +func (s *service) popTaskes() ([]func(), error) { + s.lock.Lock() + defer s.lock.Unlock() + + tip, err := s.fetchTipHeight() + if err != nil { + return nil, err + } + + taskes := make([]func(), 0) + + for height, tasks := range s.taskes { + if height > tip { + continue + } + + taskes = append(taskes, tasks...) + delete(s.taskes, height) + } + + return taskes, nil +} + +func (s *service) fetchTipHeight() (int64, error) { + resp, err := http.Get(s.tipURL) + if err != nil { + return 0, err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + var tip int64 + if _, err := fmt.Fscanf(resp.Body, "%d", &tip); err != nil { + return 0, err + } + + logrus.Debugf("fetching tip height from %s, got %d", s.tipURL, tip) + + return tip, nil +} diff --git a/server/internal/infrastructure/scheduler/gocron/service.go b/server/internal/infrastructure/scheduler/gocron/service.go index bcacda158..1aea7c6f3 100644 --- a/server/internal/infrastructure/scheduler/gocron/service.go +++ b/server/internal/infrastructure/scheduler/gocron/service.go @@ -1,4 +1,4 @@ -package scheduler +package timescheduler import ( "fmt" @@ -17,6 +17,18 @@ func NewScheduler() ports.SchedulerService { return &service{svc} } +func (s *service) Unit() ports.TimeUnit { + return ports.UnixTime +} + +func (s *service) AddNow(lifetime int64) int64 { + return time.Now().Add(time.Duration(lifetime) * time.Second).Unix() +} + +func (s *service) AfterNow(expiry int64) bool { + return time.Unix(expiry, 0).After(time.Now()) +} + func (s *service) Start() { s.scheduler.StartAsync() } @@ -25,15 +37,6 @@ func (s *service) Stop() { s.scheduler.Stop() } -func (s *service) ScheduleTask(interval int64, immediate bool, task func()) error { - if immediate { - _, err := s.scheduler.Every(int(interval)).Seconds().Do(task) - return err - } - _, err := s.scheduler.Every(int(interval)).Seconds().WaitForSchedule().Do(task) - return err -} - func (s *service) ScheduleTaskOnce(at int64, task func()) error { delay := at - time.Now().Unix() if delay < 0 { diff --git a/server/internal/infrastructure/tx-builder/covenant/builder.go b/server/internal/infrastructure/tx-builder/covenant/builder.go index 37dcaee58..58af969ab 100644 --- a/server/internal/infrastructure/tx-builder/covenant/builder.go +++ b/server/internal/infrastructure/tx-builder/covenant/builder.go @@ -195,7 +195,7 @@ func (b *txBuilder) BuildRoundTx( return } -func (b *txBuilder) GetSweepInput(parentblocktime int64, node tree.Node) (expirationtime int64, sweepInput ports.SweepInput, err error) { +func (b *txBuilder) GetSweepInput(node tree.Node) (lifetime int64, sweepInput ports.SweepInput, err error) { pset, err := psetv2.NewPsetFromBase64(node.Tx) if err != nil { return -1, nil, err @@ -215,8 +215,6 @@ func (b *txBuilder) GetSweepInput(parentblocktime int64, node tree.Node) (expira return -1, nil, err } - expirationTime := parentblocktime + lifetime - txhex, err := b.wallet.GetTransaction(context.Background(), txid) if err != nil { return -1, nil, err @@ -241,7 +239,7 @@ func (b *txBuilder) GetSweepInput(parentblocktime int64, node tree.Node) (expira amount: inputValue, } - return expirationTime, sweepInput, nil + return lifetime, sweepInput, nil } func (b *txBuilder) VerifyTapscriptPartialSigs(tx string) (bool, string, error) { diff --git a/server/internal/infrastructure/tx-builder/covenant/mocks_test.go b/server/internal/infrastructure/tx-builder/covenant/mocks_test.go index 61b8104ae..a9090af5c 100644 --- a/server/internal/infrastructure/tx-builder/covenant/mocks_test.go +++ b/server/internal/infrastructure/tx-builder/covenant/mocks_test.go @@ -151,7 +151,7 @@ func (m *mockedWallet) GetDustAmount(ctx context.Context) (uint64, error) { return res, args.Error(1) } -func (m *mockedWallet) IsTransactionConfirmed(ctx context.Context, txid string) (bool, int64, error) { +func (m *mockedWallet) IsTransactionConfirmed(ctx context.Context, txid string) (bool, int64, int64, error) { args := m.Called(ctx, txid) var res bool @@ -159,12 +159,17 @@ func (m *mockedWallet) IsTransactionConfirmed(ctx context.Context, txid string) res = a.(bool) } + var height int64 + if h := args.Get(1); h != nil { + height = h.(int64) + } + var blocktime int64 if b := args.Get(1); b != nil { blocktime = b.(int64) } - return res, blocktime, args.Error(2) + return res, height, blocktime, args.Error(2) } func (m *mockedWallet) SignTransactionTapscript(ctx context.Context, pset string, inputIndexes []int) (string, error) { diff --git a/server/internal/infrastructure/tx-builder/covenant/sweep.go b/server/internal/infrastructure/tx-builder/covenant/sweep.go index f00cabef1..314b5f687 100644 --- a/server/internal/infrastructure/tx-builder/covenant/sweep.go +++ b/server/internal/infrastructure/tx-builder/covenant/sweep.go @@ -90,7 +90,7 @@ func sweepTransaction( return nil, err } - sequence, err := common.BIP68EncodeAsNumber(sweepClosure.Seconds) + sequence, err := common.BIP68Sequence(sweepClosure.Seconds) if err != nil { return nil, err } diff --git a/server/internal/infrastructure/tx-builder/covenantless/builder.go b/server/internal/infrastructure/tx-builder/covenantless/builder.go index bfa8ee3e6..3fc7a3d6a 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/builder.go +++ b/server/internal/infrastructure/tx-builder/covenantless/builder.go @@ -324,7 +324,7 @@ func (b *txBuilder) BuildRoundTx( return } -func (b *txBuilder) GetSweepInput(parentblocktime int64, node tree.Node) (expirationtime int64, sweepInput ports.SweepInput, err error) { +func (b *txBuilder) GetSweepInput(node tree.Node) (lifetime int64, sweepInput ports.SweepInput, err error) { partialTx, err := psbt.NewFromRawBytes(strings.NewReader(node.Tx), true) if err != nil { return -1, nil, err @@ -343,8 +343,6 @@ func (b *txBuilder) GetSweepInput(parentblocktime int64, node tree.Node) (expira return -1, nil, err } - expirationTime := parentblocktime + lifetime - txhex, err := b.wallet.GetTransaction(context.Background(), txid.String()) if err != nil { return -1, nil, err @@ -365,7 +363,7 @@ func (b *txBuilder) GetSweepInput(parentblocktime int64, node tree.Node) (expira amount: tx.TxOut[index].Value, } - return expirationTime, sweepInput, nil + return lifetime, sweepInput, nil } func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) ([]tree.Node, error) { @@ -1220,6 +1218,8 @@ func extractSweepLeaf(input psbt.PInput) (sweepLeaf *psbt.TaprootTapLeafScript, if err != nil { return nil, nil, 0, err } + + fmt.Println("closure", valid) if valid && closure.Seconds > 0 { sweepLeaf = leaf lifetime = int64(closure.Seconds) diff --git a/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go b/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go index 224c35638..259a977a3 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go +++ b/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go @@ -172,7 +172,7 @@ func (m *mockedWallet) GetDustAmount(ctx context.Context) (uint64, error) { return res, args.Error(1) } -func (m *mockedWallet) IsTransactionConfirmed(ctx context.Context, txid string) (bool, int64, error) { +func (m *mockedWallet) IsTransactionConfirmed(ctx context.Context, txid string) (bool, int64, int64, error) { args := m.Called(ctx, txid) var res bool @@ -180,12 +180,17 @@ func (m *mockedWallet) IsTransactionConfirmed(ctx context.Context, txid string) res = a.(bool) } + var height int64 + if h := args.Get(1); h != nil { + height = h.(int64) + } + var blocktime int64 if b := args.Get(1); b != nil { blocktime = b.(int64) } - return res, blocktime, args.Error(2) + return res, height, blocktime, args.Error(2) } func (m *mockedWallet) SignTransactionTapscript(ctx context.Context, pset string, inputIndexes []int) (string, error) { diff --git a/server/internal/infrastructure/tx-builder/covenantless/sweep.go b/server/internal/infrastructure/tx-builder/covenantless/sweep.go index 3c26d7d24..f9029c8b6 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/sweep.go +++ b/server/internal/infrastructure/tx-builder/covenantless/sweep.go @@ -37,7 +37,7 @@ func sweepTransaction( return nil, fmt.Errorf("invalid csv script") } - sequence, err := common.BIP68EncodeAsNumber(sweepClosure.Seconds) + sequence, err := common.BIP68Sequence(sweepClosure.Seconds) if err != nil { return nil, err } diff --git a/server/internal/infrastructure/wallet/btc-embedded/bitcoind.go b/server/internal/infrastructure/wallet/btc-embedded/bitcoind.go index 5cd96697b..e75d1bafe 100644 --- a/server/internal/infrastructure/wallet/btc-embedded/bitcoind.go +++ b/server/internal/infrastructure/wallet/btc-embedded/bitcoind.go @@ -33,21 +33,31 @@ func (b *bitcoindRPCClient) broadcast(txhex string) error { return nil } -func (b *bitcoindRPCClient) getTxStatus(txid string) (isConfirmed bool, blocktime int64, err error) { +func (b *bitcoindRPCClient) getTxStatus(txid string) (isConfirmed bool, height, blocktime int64, err error) { txhash, err := chainhash.NewHashFromStr(txid) if err != nil { - return false, 0, err + return false, 0, 0, err } tx, err := b.chainClient.GetRawTransactionVerbose(txhash) if err != nil { if strings.Contains(err.Error(), "No such mempool or blockchain transaction") { - return false, 0, nil + return false, 0, 0, nil } - return false, 0, err + return false, 0, 0, err } - return tx.Confirmations > 0, tx.Blocktime, nil + blockHash, err := chainhash.NewHashFromStr(tx.BlockHash) + if err != nil { + return false, 0, 0, err + } + + blockHeight, err := b.chainClient.GetBlockHeight(blockHash) + if err != nil { + return false, 0, 0, err + } + + return tx.Confirmations > 0, int64(blockHeight), tx.Blocktime, nil } func (b *bitcoindRPCClient) getTx(txid string) (*wire.MsgTx, error) { diff --git a/server/internal/infrastructure/wallet/btc-embedded/esplora.go b/server/internal/infrastructure/wallet/btc-embedded/esplora.go index cd3051dd2..64f2a3232 100644 --- a/server/internal/infrastructure/wallet/btc-embedded/esplora.go +++ b/server/internal/infrastructure/wallet/btc-embedded/esplora.go @@ -20,8 +20,9 @@ type esploraClient struct { type esploraTx struct { Status struct { - Confirmed bool `json:"confirmed"` - BlockTime int64 `json:"block_time"` + Confirmed bool `json:"confirmed"` + BlockTime int64 `json:"block_time"` + BlockNumber int64 `json:"block_height"` } `json:"status"` } @@ -79,30 +80,30 @@ func (f *esploraClient) getTx(txid string) (*wire.MsgTx, error) { return &tx, nil } -func (f *esploraClient) getTxStatus(txid string) (isConfirmed bool, blocktime int64, err error) { +func (f *esploraClient) getTxStatus(txid string) (isConfirmed bool, blocknumber, blocktime int64, err error) { endpoint, err := url.JoinPath(f.url, "tx", txid) if err != nil { - return false, 0, err + return false, 0, 0, err } resp, err := http.DefaultClient.Get(endpoint) if err != nil { - return false, 0, err + return false, 0, 0, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return false, 0, err + return false, 0, 0, err } var response esploraTx if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return false, 0, err + return false, 0, 0, err } - return response.Status.Confirmed, response.Status.BlockTime, nil + return response.Status.Confirmed, response.Status.BlockNumber, response.Status.BlockTime, nil } // GetFeeMap returns a map of sat/vbyte fees for different confirmation targets diff --git a/server/internal/infrastructure/wallet/btc-embedded/wallet.go b/server/internal/infrastructure/wallet/btc-embedded/wallet.go index cea1923bf..e5a95b8bf 100644 --- a/server/internal/infrastructure/wallet/btc-embedded/wallet.go +++ b/server/internal/infrastructure/wallet/btc-embedded/wallet.go @@ -92,7 +92,7 @@ var ( // add additional chain API not supported by the chain.Interface type type extraChainAPI interface { getTx(txid string) (*wire.MsgTx, error) - getTxStatus(txid string) (isConfirmed bool, blocktime int64, err error) + getTxStatus(txid string) (isConfirmed bool, blockHeight, blocktime int64, err error) broadcast(txHex string) error } @@ -957,7 +957,7 @@ func (s *service) GetNotificationChannel( func (s *service) IsTransactionConfirmed( ctx context.Context, txid string, -) (isConfirmed bool, blocktime int64, err error) { +) (isConfirmed bool, blocknumber int64, blocktime int64, err error) { return s.extraAPI.getTxStatus(txid) } diff --git a/server/internal/infrastructure/wallet/liquid-standalone/transaction.go b/server/internal/infrastructure/wallet/liquid-standalone/transaction.go index 99a03b4ed..81bf981b7 100644 --- a/server/internal/infrastructure/wallet/liquid-standalone/transaction.go +++ b/server/internal/infrastructure/wallet/liquid-standalone/transaction.go @@ -158,21 +158,21 @@ func (s *service) BroadcastTransaction( func (s *service) IsTransactionConfirmed( ctx context.Context, txid string, -) (bool, int64, error) { - _, isConfirmed, blocktime, err := s.getTransaction(ctx, txid) +) (bool, int64, int64, error) { + _, isConfirmed, blockheight, blocktime, err := s.getTransaction(ctx, txid) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "missing transaction") { - return isConfirmed, 0, nil + return isConfirmed, 0, 0, nil } - return false, 0, err + return false, 0, 0, err } - return isConfirmed, blocktime, nil + return isConfirmed, blockheight, blocktime, nil } func (s *service) WaitForSync(ctx context.Context, txid string) error { for { time.Sleep(5 * time.Second) - _, _, _, err := s.getTransaction(ctx, txid) + _, _, _, _, err := s.getTransaction(ctx, txid) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "missing transaction") { continue @@ -351,7 +351,7 @@ func (s *service) EstimateFees( } func (s *service) GetTransaction(ctx context.Context, txid string) (string, error) { - txHex, _, _, err := s.getTransaction(ctx, txid) + txHex, _, _, _, err := s.getTransaction(ctx, txid) if err != nil { return "", err } @@ -361,18 +361,18 @@ func (s *service) GetTransaction(ctx context.Context, txid string) (string, erro func (s *service) getTransaction( ctx context.Context, txid string, -) (string, bool, int64, error) { +) (string, bool, int64, int64, error) { res, err := s.txClient.GetTransaction(ctx, &pb.GetTransactionRequest{ Txid: txid, }) if err != nil { - return "", false, 0, err + return "", false, 0, 0, err } if res.GetBlockDetails().GetTimestamp() > 0 { - return res.GetTxHex(), true, res.BlockDetails.GetTimestamp(), nil + return res.GetTxHex(), true, int64(res.GetBlockDetails().GetHeight()), res.BlockDetails.GetTimestamp(), nil } // if not confirmed, we return now + 1 min to estimate the next blocktime - return res.GetTxHex(), false, time.Now().Add(time.Minute).Unix(), nil + return res.GetTxHex(), false, 0, time.Now().Add(time.Minute).Unix(), nil } diff --git a/server/test/e2e/covenant/e2e_test.go b/server/test/e2e/covenant/e2e_test.go index d509fe276..ea74ec682 100644 --- a/server/test/e2e/covenant/e2e_test.go +++ b/server/test/e2e/covenant/e2e_test.go @@ -202,6 +202,36 @@ func TestReactToSpentVtxosRedemption(t *testing.T) { require.Empty(t, balance.OnchainBalance.LockedAmount) } +func TestSweep(t *testing.T) { + var receive utils.ArkReceive + receiveStr, err := runArkCommand("receive") + require.NoError(t, err) + + err = json.Unmarshal([]byte(receiveStr), &receive) + require.NoError(t, err) + + _, err = utils.RunCommand("nigiri", "faucet", "--liquid", receive.Boarding) + require.NoError(t, err) + + time.Sleep(5 * time.Second) + + _, err = runArkCommand("claim", "--password", utils.Password) + require.NoError(t, err) + + time.Sleep(3 * time.Second) + + _, err = utils.RunCommand("nigiri", "rpc", "--liquid", "generatetoaddress", "100", "el1qqwk722tghgkgmh3r2ph4d2apwj0dy9xnzlenzklx8jg3z299fpaw56trre9gpk6wmw0u4qycajqeva3t7lzp7wnacvwxha59r") + require.NoError(t, err) + + time.Sleep(40 * time.Second) + + var balance utils.ArkBalance + balanceStr, err := runArkCommand("balance") + require.NoError(t, err) + require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance)) + require.Zero(t, balance.Offchain.Total) // all funds should be swept +} + func runArkCommand(arg ...string) (string, error) { args := append([]string{"exec", "-t", "arkd", "ark"}, arg...) return utils.RunCommand("docker", args...) @@ -276,20 +306,12 @@ func setupAspWallet() error { return fmt.Errorf("failed to parse response: %s", err) } - if _, err := utils.RunCommand("nigiri", "faucet", "--liquid", addr.Address); err != nil { - return fmt.Errorf("failed to fund wallet: %s", err) - } - if _, err := utils.RunCommand("nigiri", "faucet", "--liquid", addr.Address); err != nil { - return fmt.Errorf("failed to fund wallet: %s", err) - } - if _, err := utils.RunCommand("nigiri", "faucet", "--liquid", addr.Address); err != nil { - return fmt.Errorf("failed to fund wallet: %s", err) - } - if _, err := utils.RunCommand("nigiri", "faucet", "--liquid", addr.Address); err != nil { - return fmt.Errorf("failed to fund wallet: %s", err) - } - if _, err := utils.RunCommand("nigiri", "faucet", "--liquid", addr.Address); err != nil { - return fmt.Errorf("failed to fund wallet: %s", err) + const numberOfFaucet = 6 + + for i := 0; i < numberOfFaucet; i++ { + if _, err := utils.RunCommand("nigiri", "faucet", "--liquid", addr.Address); err != nil { + return fmt.Errorf("failed to fund wallet: %s with address %s", err, addr.Address) + } } time.Sleep(5 * time.Second) diff --git a/server/test/e2e/covenantless/e2e_test.go b/server/test/e2e/covenantless/e2e_test.go index 18bb1161c..61f51b190 100644 --- a/server/test/e2e/covenantless/e2e_test.go +++ b/server/test/e2e/covenantless/e2e_test.go @@ -362,6 +362,36 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) { } +func TestSweep(t *testing.T) { + var receive utils.ArkReceive + receiveStr, err := runClarkCommand("receive") + require.NoError(t, err) + + err = json.Unmarshal([]byte(receiveStr), &receive) + require.NoError(t, err) + + _, err = utils.RunCommand("nigiri", "faucet", receive.Boarding) + require.NoError(t, err) + + time.Sleep(5 * time.Second) + + _, err = runClarkCommand("claim", "--password", utils.Password) + require.NoError(t, err) + + time.Sleep(3 * time.Second) + + _, err = utils.RunCommand("nigiri", "rpc", "generatetoaddress", "100", "bcrt1qe8eelqalnch946nzhefd5ajhgl2afjw5aegc59") + require.NoError(t, err) + + time.Sleep(40 * time.Second) + + var balance utils.ArkBalance + balanceStr, err := runClarkCommand("balance") + require.NoError(t, err) + require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance)) + require.Zero(t, balance.Offchain.Total) // all funds should be swept +} + func runClarkCommand(arg ...string) (string, error) { args := append([]string{"exec", "-t", "clarkd", "ark"}, arg...) return utils.RunCommand("docker", args...) diff --git a/server/test/e2e/test_utils.go b/server/test/e2e/test_utils.go index 8ce209511..c28ba9edc 100644 --- a/server/test/e2e/test_utils.go +++ b/server/test/e2e/test_utils.go @@ -35,6 +35,9 @@ func GenerateBlock() error { if _, err := RunCommand("nigiri", "rpc", "--liquid", "generatetoaddress", "1", "el1qqwk722tghgkgmh3r2ph4d2apwj0dy9xnzlenzklx8jg3z299fpaw56trre9gpk6wmw0u4qycajqeva3t7lzp7wnacvwxha59r"); err != nil { return err } + if _, err := RunCommand("nigiri", "rpc", "generatetoaddress", "1", "bcrt1qe8eelqalnch946nzhefd5ajhgl2afjw5aegc59"); err != nil { + return err + } time.Sleep(6 * time.Second) return nil From bc8df6802e707ee1401f28ac0e28db1b73d3941e Mon Sep 17 00:00:00 2001 From: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:33:55 +0200 Subject: [PATCH 07/10] Add env unlocker (#350) --- server/cmd/arkd/main.go | 1 + server/internal/app-config/config.go | 7 +++++- server/internal/config/config.go | 3 +++ .../infrastructure/unlocker/env/service.go | 23 +++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 server/internal/infrastructure/unlocker/env/service.go diff --git a/server/cmd/arkd/main.go b/server/cmd/arkd/main.go index 116ec596f..f66bf22a1 100755 --- a/server/cmd/arkd/main.go +++ b/server/cmd/arkd/main.go @@ -81,6 +81,7 @@ func mainAction(_ *cli.Context) error { BoardingExitDelay: cfg.BoardingExitDelay, UnlockerType: cfg.UnlockerType, UnlockerFilePath: cfg.UnlockerFilePath, + UnlockerPassword: cfg.UnlockerPassword, } svc, err := grpcservice.NewService(svcConfig, appConfig) if err != nil { diff --git a/server/internal/app-config/config.go b/server/internal/app-config/config.go index 59e6e75b7..24add35a4 100644 --- a/server/internal/app-config/config.go +++ b/server/internal/app-config/config.go @@ -12,6 +12,7 @@ import ( timescheduler "github.com/ark-network/ark/server/internal/infrastructure/scheduler/gocron" txbuilder "github.com/ark-network/ark/server/internal/infrastructure/tx-builder/covenant" cltxbuilder "github.com/ark-network/ark/server/internal/infrastructure/tx-builder/covenantless" + envunlocker "github.com/ark-network/ark/server/internal/infrastructure/unlocker/env" fileunlocker "github.com/ark-network/ark/server/internal/infrastructure/unlocker/file" btcwallet "github.com/ark-network/ark/server/internal/infrastructure/wallet/btc-embedded" liquidwallet "github.com/ark-network/ark/server/internal/infrastructure/wallet/liquid-standalone" @@ -41,6 +42,7 @@ var ( "btcwallet": {}, } supportedUnlockers = supportedType{ + "env": {}, "file": {}, } supportedNetworks = supportedType{ @@ -77,7 +79,8 @@ type Config struct { BitcoindRpcHost string UnlockerType string - UnlockerFilePath string + UnlockerFilePath string // file unlocker + UnlockerPassword string // env unlocker repo ports.RepoManager svc application.Service @@ -394,6 +397,8 @@ func (c *Config) unlockerService() error { switch c.UnlockerType { case "file": svc, err = fileunlocker.NewService(c.UnlockerFilePath) + case "env": + svc, err = envunlocker.NewService(c.UnlockerPassword) default: err = fmt.Errorf("unknown unlocker type") } diff --git a/server/internal/config/config.go b/server/internal/config/config.go index ba44727a8..94e0578db 100644 --- a/server/internal/config/config.go +++ b/server/internal/config/config.go @@ -38,6 +38,7 @@ type Config struct { TLSExtraDomains []string UnlockerType string UnlockerFilePath string + UnlockerPassword string } var ( @@ -69,6 +70,7 @@ var ( TLSExtraDomain = "TLS_EXTRA_DOMAIN" UnlockerType = "UNLOCKER_TYPE" UnlockerFilePath = "UNLOCKER_FILE_PATH" + UnlockerPassword = "UNLOCKER_PASSWORD" defaultDatadir = common.AppDataDir("arkd", false) defaultRoundInterval = 5 @@ -148,6 +150,7 @@ func LoadConfig() (*Config, error) { TLSExtraDomains: viper.GetStringSlice(TLSExtraDomain), UnlockerType: viper.GetString(UnlockerType), UnlockerFilePath: viper.GetString(UnlockerFilePath), + UnlockerPassword: viper.GetString(UnlockerPassword), }, nil } diff --git a/server/internal/infrastructure/unlocker/env/service.go b/server/internal/infrastructure/unlocker/env/service.go new file mode 100644 index 000000000..ca02c32c0 --- /dev/null +++ b/server/internal/infrastructure/unlocker/env/service.go @@ -0,0 +1,23 @@ +package envunlocker + +import ( + "context" + "fmt" + + "github.com/ark-network/ark/server/internal/core/ports" +) + +type service struct { + password string +} + +func NewService(password string) (ports.Unlocker, error) { + if len(password) <= 0 { + return nil, fmt.Errorf("missing password in env") + } + return &service{password}, nil +} + +func (s *service) GetPassword(_ context.Context) (string, error) { + return s.password, nil +} From ea5fa220239883108d8bb872b400986f6e65ff02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Bordalo?= Date: Wed, 9 Oct 2024 17:53:58 +0200 Subject: [PATCH 08/10] [SDK] Add sendAsync & dump to wasm (#346) * use bitcoin receivers * add sendAsync to wasm * add dump() to wasm --- pkg/client-sdk/wasm/browser/exports.go | 2 ++ pkg/client-sdk/wasm/browser/wrappers.go | 41 +++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/pkg/client-sdk/wasm/browser/exports.go b/pkg/client-sdk/wasm/browser/exports.go index 6c8d3c1af..348857cd3 100644 --- a/pkg/client-sdk/wasm/browser/exports.go +++ b/pkg/client-sdk/wasm/browser/exports.go @@ -30,11 +30,13 @@ func init() { js.Global().Set("receive", ReceiveWrapper()) js.Global().Set("sendOnChain", SendOnChainWrapper()) js.Global().Set("sendOffChain", SendOffChainWrapper()) + js.Global().Set("sendAsync", SendAsyncWrapper()) js.Global().Set("claim", ClaimWrapper()) js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper()) js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper()) js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper()) js.Global().Set("log", LogWrapper()) + js.Global().Set("dump", DumpWrapper()) js.Global().Set("getAspUrl", GetAspUrlWrapper()) js.Global().Set("getAspPubKeyHex", GetAspPubkeyWrapper()) diff --git a/pkg/client-sdk/wasm/browser/wrappers.go b/pkg/client-sdk/wasm/browser/wrappers.go index 752ed1242..741f7c51b 100644 --- a/pkg/client-sdk/wasm/browser/wrappers.go +++ b/pkg/client-sdk/wasm/browser/wrappers.go @@ -164,6 +164,19 @@ func ReceiveWrapper() js.Func { }) } +func DumpWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + if arkSdkClient == nil { + return nil, errors.New("ARK SDK client is not initialized") + } + seed, err := arkSdkClient.Dump(context.Background()) + if err != nil { + return nil, err + } + return js.ValueOf(seed), nil + }) +} + func SendOnChainWrapper() js.Func { return JSPromise(func(args []js.Value) (interface{}, error) { if len(args) != 1 { @@ -172,7 +185,7 @@ func SendOnChainWrapper() js.Func { receivers := make([]arksdk.Receiver, args[0].Length()) for i := 0; i < args[0].Length(); i++ { receiver := args[0].Index(i) - receivers[i] = arksdk.NewLiquidReceiver( + receivers[i] = arksdk.NewBitcoinReceiver( receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()), ) } @@ -196,7 +209,7 @@ func SendOffChainWrapper() js.Func { receivers := make([]arksdk.Receiver, args[1].Length()) for i := 0; i < args[1].Length(); i++ { receiver := args[1].Index(i) - receivers[i] = arksdk.NewLiquidReceiver( + receivers[i] = arksdk.NewBitcoinReceiver( receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()), ) } @@ -211,6 +224,30 @@ func SendOffChainWrapper() js.Func { }) } +func SendAsyncWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + if len(args) != 2 { + return nil, errors.New("invalid number of args") + } + withExpiryCoinselect := args[0].Bool() + receivers := make([]arksdk.Receiver, args[1].Length()) + for i := 0; i < args[1].Length(); i++ { + receiver := args[1].Index(i) + receivers[i] = arksdk.NewBitcoinReceiver( + receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()), + ) + } + + txID, err := arkSdkClient.SendAsync( + context.Background(), withExpiryCoinselect, receivers, + ) + if err != nil { + return nil, err + } + return js.ValueOf(txID), nil + }) +} + func ClaimWrapper() js.Func { return JSPromise(func(args []js.Value) (interface{}, error) { if len(args) != 0 { From 03670c9c4b765071efe98cddbb12499a9097063f Mon Sep 17 00:00:00 2001 From: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:09:19 +0200 Subject: [PATCH 09/10] Move btc wallet syncing in background (#352) * Move btc wallet syncing in bg * Fix * Fix e2e test * Use neutrino in regtest for e2e * Revert * Fix --- docker-compose.clark.regtest.yml | 4 +- server/internal/core/ports/wallet.go | 1 + .../tx-builder/covenant/mocks_test.go | 10 + .../tx-builder/covenantless/mocks_test.go | 10 + .../wallet/btc-embedded/wallet.go | 208 +++++++++++++----- .../wallet/liquid-standalone/service.go | 6 + .../interface/grpc/handlers/walletservice.go | 32 ++- server/internal/interface/grpc/service.go | 20 +- server/test/e2e/covenantless/e2e_test.go | 47 +++- 9 files changed, 258 insertions(+), 80 deletions(-) diff --git a/docker-compose.clark.regtest.yml b/docker-compose.clark.regtest.yml index 4b2d8dd09..384dd4917 100644 --- a/docker-compose.clark.regtest.yml +++ b/docker-compose.clark.regtest.yml @@ -12,9 +12,7 @@ services: - ARK_ROUND_LIFETIME=20 - ARK_TX_BUILDER_TYPE=covenantless - ARK_ESPLORA_URL=http://chopsticks:3000 - - ARK_BITCOIND_RPC_USER=admin1 - - ARK_BITCOIND_RPC_PASS=123 - - ARK_BITCOIND_RPC_HOST=bitcoin:18443 + - ARK_NEUTRINO_PEER=bitcoin:18444 - ARK_SCHEDULER_TYPE=block - ARK_NO_TLS=true - ARK_NO_MACAROONS=true diff --git a/server/internal/core/ports/wallet.go b/server/internal/core/ports/wallet.go index b78c9c243..5d2de7516 100644 --- a/server/internal/core/ports/wallet.go +++ b/server/internal/core/ports/wallet.go @@ -13,6 +13,7 @@ var ErrNonFinalBIP68 = errors.New("non-final BIP68 sequence") type WalletService interface { BlockchainScanner + GetSyncedUpdate(ctx context.Context) <-chan struct{} GenSeed(ctx context.Context) (string, error) Create(ctx context.Context, seed, password string) error Restore(ctx context.Context, seed, password string) error diff --git a/server/internal/infrastructure/tx-builder/covenant/mocks_test.go b/server/internal/infrastructure/tx-builder/covenant/mocks_test.go index a9090af5c..f5f5d7179 100644 --- a/server/internal/infrastructure/tx-builder/covenant/mocks_test.go +++ b/server/internal/infrastructure/tx-builder/covenant/mocks_test.go @@ -13,6 +13,16 @@ type mockedWallet struct { mock.Mock } +func (m *mockedWallet) GetSyncedUpdate(ctx context.Context) <-chan struct{} { + args := m.Called(ctx) + + var res chan struct{} + if a := args.Get(0); a != nil { + res = a.(chan struct{}) + } + return res +} + func (m *mockedWallet) GenSeed(ctx context.Context) (string, error) { args := m.Called(ctx) diff --git a/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go b/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go index 259a977a3..bb1a834f0 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go +++ b/server/internal/infrastructure/tx-builder/covenantless/mocks_test.go @@ -13,6 +13,16 @@ type mockedWallet struct { mock.Mock } +func (m *mockedWallet) GetSyncedUpdate(ctx context.Context) <-chan struct{} { + args := m.Called(ctx) + + var res chan struct{} + if a := args.Get(0); a != nil { + res = a.(chan struct{}) + } + return res +} + func (m *mockedWallet) GenSeed(ctx context.Context) (string, error) { args := m.Called(ctx) diff --git a/server/internal/infrastructure/wallet/btc-embedded/wallet.go b/server/internal/infrastructure/wallet/btc-embedded/wallet.go index e5a95b8bf..aec6d3412 100644 --- a/server/internal/infrastructure/wallet/btc-embedded/wallet.go +++ b/server/internal/infrastructure/wallet/btc-embedded/wallet.go @@ -83,10 +83,14 @@ const ( ) var ( - ErrWalletNotLoaded = fmt.Errorf("wallet not loaded, create or unlock it first") - p2wpkhKeyScope = waddrmgr.KeyScopeBIP0084 - p2trKeyScope = waddrmgr.KeyScopeBIP0086 - outputLockDuration = time.Minute + ErrNotLoaded = fmt.Errorf("wallet not loaded, create or unlock it first") + ErrNotSynced = fmt.Errorf("wallet still syncing, please retry later") + ErrNotReady = fmt.Errorf("wallet not ready, please init and wait for it to complete syncing") + ErrNotUnlocked = fmt.Errorf("wallet is locked, please unlock it to perform this operation") + ErrAlreadyInitialized = fmt.Errorf("wallet already initialized") + p2wpkhKeyScope = waddrmgr.KeyScopeBIP0084 + p2trKeyScope = waddrmgr.KeyScopeBIP0086 + outputLockDuration = time.Minute ) // add additional chain API not supported by the chain.Interface type @@ -110,6 +114,9 @@ type service struct { // holds the data related to the ASP key used in Vtxo scripts aspKeyAddr waddrmgr.ManagedPubKeyAddress + + isSynced bool + syncedCh chan struct{} } // WithNeutrino creates a start a neutrino node using the provided service datadir @@ -144,15 +151,6 @@ func WithNeutrino(initialPeer string, esploraURL string) WalletOption { return err } - if err := neutrinoSvc.Start(); err != nil { - return err - } - - // wait for neutrino to sync - for !neutrinoSvc.IsCurrent() { - time.Sleep(1 * time.Second) - } - chainSrc := chain.NewNeutrinoClient(netParams, neutrinoSvc) scanner := chain.NewNeutrinoClient(netParams, neutrinoSvc) @@ -268,6 +266,7 @@ func NewService(cfg WalletConfig, options ...WalletOption) (ports.WalletService, cfg: cfg, watchedScriptsLock: sync.RWMutex{}, watchedScripts: make(map[string]struct{}), + syncedCh: make(chan struct{}), } for _, option := range options { @@ -280,11 +279,14 @@ func NewService(cfg WalletConfig, options ...WalletOption) (ports.WalletService, } func (s *service) Close() { - if s.walletLoaded() { - if err := s.wallet.Stop(); err != nil { - log.WithError(err).Warn("failed to gracefully stop the wallet, forcing shutdown") - } + if s.isLoaded() { + s.wallet.InternalWallet().Stop() } + s.chainSource.Stop() +} + +func (s *service) GetSyncedUpdate(_ context.Context) <-chan struct{} { + return s.syncedCh } func (s *service) GenSeed(_ context.Context) (string, error) { @@ -304,11 +306,11 @@ func (s *service) Restore(_ context.Context, seed, password string) error { } func (s *service) Unlock(_ context.Context, password string) error { - if !s.walletInitialized() { + if !s.isInitialized() { return fmt.Errorf("wallet not initialized") } - if !s.walletLoaded() { + if !s.isLoaded() { pwd := []byte(password) opt := btcwallet.LoaderWithLocalWalletDB(s.cfg.Datadir, false, time.Minute) config := btcwallet.Config{ @@ -332,16 +334,6 @@ func (s *service) Unlock(_ context.Context, password string) error { return fmt.Errorf("failed to start wallet: %s", err) } - for { - if !wallet.InternalWallet().ChainSynced() { - log.Debugf("waiting sync: current height %d", wallet.InternalWallet().Manager.SyncedTo().Height) - time.Sleep(3 * time.Second) - continue - } - break - } - log.Debugf("chain synced") - addrs, err := wallet.ListAddresses(string(aspKeyAccount), false) if err != nil { return err @@ -381,14 +373,18 @@ func (s *service) Unlock(_ context.Context, password string) error { } s.wallet = wallet + + go s.listenToSynced() + return nil } + return s.wallet.InternalWallet().Unlock([]byte(password), nil) } func (s *service) Lock(_ context.Context, _ string) error { - if !s.walletLoaded() { - return ErrWalletNotLoaded + if !s.isLoaded() { + return ErrNotLoaded } s.wallet.InternalWallet().Lock() @@ -404,14 +400,15 @@ func (s *service) BroadcastTransaction(ctx context.Context, txHex string) (strin if err := tx.Deserialize(hex.NewDecoder(strings.NewReader(txHex))); err != nil { return "", err } - if err := s.wallet.PublishTransaction(&tx, ""); err != nil { - return "", err - } return tx.TxHash().String(), nil } func (s *service) ConnectorsAccountBalance(ctx context.Context) (uint64, uint64, error) { + if err := s.safeCheck(); err != nil { + return 0, 0, err + } + utxos, err := s.listUtxos(p2trKeyScope) if err != nil { return 0, 0, err @@ -426,6 +423,10 @@ func (s *service) ConnectorsAccountBalance(ctx context.Context) (uint64, uint64, } func (s *service) MainAccountBalance(ctx context.Context) (uint64, uint64, error) { + if err := s.safeCheck(); err != nil { + return 0, 0, err + } + utxos, err := s.listUtxos(p2wpkhKeyScope) if err != nil { return 0, 0, err @@ -440,8 +441,11 @@ func (s *service) MainAccountBalance(ctx context.Context) (uint64, uint64, error } func (s *service) DeriveAddresses(ctx context.Context, num int) ([]string, error) { - addresses := make([]string, 0, num) + if err := s.safeCheck(); err != nil { + return nil, err + } + addresses := make([]string, 0, num) for i := 0; i < num; i++ { addr, err := s.deriveNextAddress() if err != nil { @@ -459,6 +463,10 @@ func (s *service) DeriveAddresses(ctx context.Context, num int) ([]string, error } func (s *service) DeriveConnectorAddress(ctx context.Context) (string, error) { + if err := s.safeCheck(); err != nil { + return "", err + } + addr, err := s.wallet.NewAddress(lnwallet.TaprootPubkey, false, string(connectorAccount)) if err != nil { return "", err @@ -468,10 +476,17 @@ func (s *service) DeriveConnectorAddress(ctx context.Context) (string, error) { } func (s *service) GetPubkey(ctx context.Context) (*secp256k1.PublicKey, error) { + if !s.isLoaded() { + return nil, ErrNotLoaded + } return s.aspKeyAddr.PubKey(), nil } func (s *service) GetForfeitAddress(ctx context.Context) (string, error) { + if err := s.safeCheck(); err != nil { + return "", err + } + addrs, err := s.wallet.ListAddresses(string(mainAccount), false) if err != nil { return "", err @@ -512,6 +527,10 @@ func (s *service) GetForfeitAddress(ctx context.Context) (string, error) { } func (s *service) ListConnectorUtxos(ctx context.Context, connectorAddress string) ([]ports.TxInput, error) { + if err := s.safeCheck(); err != nil { + return nil, err + } + w := s.wallet.InternalWallet() addr, err := btcutil.DecodeAddress(connectorAddress, w.ChainParams()) @@ -542,6 +561,10 @@ func (s *service) ListConnectorUtxos(ctx context.Context, connectorAddress strin } func (s *service) LockConnectorUtxos(ctx context.Context, utxos []ports.TxOutpoint) error { + if err := s.safeCheck(); err != nil { + return err + } + w := s.wallet.InternalWallet() for _, utxo := range utxos { @@ -562,6 +585,10 @@ func (s *service) LockConnectorUtxos(ctx context.Context, utxos []ports.TxOutpoi } func (s *service) SelectUtxos(ctx context.Context, _ string, amount uint64) ([]ports.TxInput, uint64, error) { + if err := s.safeCheck(); err != nil { + return nil, 0, err + } + w := s.wallet.InternalWallet() utxos, err := s.listUtxos(p2wpkhKeyScope) @@ -616,6 +643,10 @@ func (s *service) SelectUtxos(ctx context.Context, _ string, amount uint64) ([]p } func (s *service) SignTransaction(ctx context.Context, partialTx string, extractRawTx bool) (string, error) { + if err := s.safeCheck(); err != nil { + return "", err + } + ptx, err := psbt.NewFromRawBytes( strings.NewReader(partialTx), true, @@ -701,6 +732,10 @@ func (s *service) SignTransaction(ctx context.Context, partialTx string, extract } func (s *service) SignTransactionTapscript(ctx context.Context, partialTx string, inputIndexes []int) (string, error) { + if err := s.safeCheck(); err != nil { + return "", err + } + partial, err := psbt.NewFromRawBytes( strings.NewReader(partialTx), true, @@ -739,9 +774,9 @@ func (s *service) SignTransactionTapscript(ctx context.Context, partialTx string } func (s *service) Status(ctx context.Context) (ports.WalletStatus, error) { - if !s.walletLoaded() { + if !s.isLoaded() { return status{ - initialized: s.walletInitialized(), + initialized: s.isInitialized(), }, nil } @@ -754,6 +789,10 @@ func (s *service) Status(ctx context.Context) (ports.WalletStatus, error) { } func (s *service) WaitForSync(ctx context.Context, txid string) error { + if err := s.safeCheck(); err != nil { + return err + } + w := s.wallet.InternalWallet() txhash, err := chainhash.NewHashFromStr(txid) @@ -869,6 +908,10 @@ func (s *service) EstimateFees(ctx context.Context, partialTx string) (uint64, e } func (s *service) WatchScripts(ctx context.Context, scripts []string) error { + if !s.isSynced { + return ErrNotSynced + } + addresses := make([]btcutil.Address, 0, len(scripts)) for _, script := range scripts { @@ -904,6 +947,10 @@ func (s *service) WatchScripts(ctx context.Context, scripts []string) error { } func (s *service) UnwatchScripts(ctx context.Context, scripts []string) error { + if !s.isSynced { + return ErrNotSynced + } + s.watchedScriptsLock.Lock() defer s.watchedScriptsLock.Unlock() for _, script := range scripts { @@ -1014,6 +1061,10 @@ func (s *service) castNotification(tx *wtxmgr.TxRecord) map[string][]ports.VtxoW } func (s *service) create(mnemonic, password string, addrGap uint32) error { + if s.isInitialized() { + return ErrAlreadyInitialized + } + if len(mnemonic) <= 0 { return fmt.Errorf("missing hd seed") } @@ -1057,24 +1108,62 @@ func (s *service) create(mnemonic, password string, addrGap uint32) error { return fmt.Errorf("failed to start wallet: %s", err) } - for { - if !wallet.InternalWallet().ChainSynced() { - log.Debugf("waiting sync: current height %d", wallet.InternalWallet().Manager.SyncedTo().Height) - time.Sleep(3 * time.Second) - continue - } - break - } - log.Debugf("chain synced") - if err := s.initAspKeyAddress(wallet); err != nil { return err } s.wallet = wallet + + go s.listenToSynced() + return nil } +func (s *service) listenToSynced() { + counter := 0 + for { + if s.wallet.InternalWallet().ChainSynced() { + log.Debug("wallet: syncing completed") + s.isSynced = true + s.syncedCh <- struct{}{} + return + } + + isRestore, progress, err := s.wallet.GetRecoveryInfo() + if err != nil { + log.Warnf("wallet: failed to check if wallet is synced: %s", err) + } else { + if !isRestore { + if counter%6 == 0 { + log.Debug("wallet: syncing in progress...") + } + counter++ + } else { + switch progress { + case 0: + // nolint: all + if counter%6 == 0 { + _, bestBlock, _ := s.wallet.IsSynced() + if bestBlock > 0 { + log.Debugf("wallet: waiting for chain source to be synced, last block fetched: %s", time.Unix(bestBlock, 0)) + } + } + counter++ + case 1: + log.Debug("wallet: syncing completed") + s.isSynced = true + s.syncedCh <- struct{}{} + return + default: + log.Debugf("wallet: syncing progress %.0f%%", progress*100) + } + } + } + + time.Sleep(10 * time.Second) + } +} + // initAspKeyAccount creates the asp key account if it doesn't exist func (s *service) initAspKeyAccount(wallet *btcwallet.BtcWallet) error { w := wallet.InternalWallet() @@ -1173,18 +1262,31 @@ func (s *service) initAspKeyAddress(wallet *btcwallet.BtcWallet) error { } func (s *service) deriveNextAddress() (btcutil.Address, error) { - if !s.walletLoaded() { - return nil, ErrWalletNotLoaded + if !s.isLoaded() { + return nil, ErrNotLoaded } return s.wallet.NewAddress(lnwallet.WitnessPubKey, false, string(mainAccount)) } -func (s *service) walletLoaded() bool { +func (s *service) safeCheck() error { + if !s.isLoaded() { + if s.isInitialized() { + return ErrNotUnlocked + } + return ErrNotReady + } + if !s.isSynced { + return ErrNotSynced + } + return nil +} + +func (s *service) isLoaded() bool { return s.wallet != nil } -func (s *service) walletInitialized() bool { +func (s *service) isInitialized() bool { opts := []btcwallet.LoaderOption{btcwallet.LoaderWithLocalWalletDB(s.cfg.Datadir, false, time.Minute)} loader, err := btcwallet.NewWalletLoader( s.cfg.chainParams(), 0, opts..., @@ -1241,10 +1343,6 @@ func withChainSource(chainSource chain.Interface) WalletOption { return fmt.Errorf("chain source already set") } - if err := chainSource.Start(); err != nil { - return fmt.Errorf("failed to start chain source: %s", err) - } - s.chainSource = chainSource return nil } diff --git a/server/internal/infrastructure/wallet/liquid-standalone/service.go b/server/internal/infrastructure/wallet/liquid-standalone/service.go index 3ea133f7c..0c4837c4a 100644 --- a/server/internal/infrastructure/wallet/liquid-standalone/service.go +++ b/server/internal/infrastructure/wallet/liquid-standalone/service.go @@ -24,6 +24,7 @@ type service struct { notifyClient pb.NotificationServiceClient chVtxos chan map[string][]ports.VtxoWithValue isListening bool + syncedCh chan struct{} } func NewService(addr string) (ports.WalletService, error) { @@ -44,6 +45,7 @@ func NewService(addr string) (ports.WalletService, error) { txClient: txClient, notifyClient: notifyClient, chVtxos: chVtxos, + syncedCh: make(chan struct{}), } ctx := context.Background() @@ -65,6 +67,10 @@ func (s *service) Close() { s.conn.Close() } +func (s *service) GetSyncedUpdate(_ context.Context) <-chan struct{} { + return s.syncedCh +} + func (s *service) GenSeed(ctx context.Context) (string, error) { res, err := s.walletClient.GenSeed(ctx, &pb.GenSeedRequest{}) if err != nil { diff --git a/server/internal/interface/grpc/handlers/walletservice.go b/server/internal/interface/grpc/handlers/walletservice.go index 041b6d4f1..708bc1c8f 100644 --- a/server/internal/interface/grpc/handlers/walletservice.go +++ b/server/internal/interface/grpc/handlers/walletservice.go @@ -6,18 +6,22 @@ import ( arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1" "github.com/ark-network/ark/server/internal/core/ports" + log "github.com/sirupsen/logrus" ) type walletInitHandler struct { walletService ports.WalletService onInit func(password string) onUnlock func(password string) + onReady func() } func NewWalletInitializerHandler( - walletService ports.WalletService, onInit, onUnlock func(string), + walletService ports.WalletService, onInit, onUnlock func(string), onReady func(), ) arkv1.WalletInitializerServiceServer { - return &walletInitHandler{walletService, onInit, onUnlock} + svc := walletInitHandler{walletService, onInit, onUnlock, onReady} + go svc.listenWhenReady() + return &svc } func (a *walletInitHandler) GenSeed(ctx context.Context, _ *arkv1.GenSeedRequest) (*arkv1.GenSeedResponse, error) { @@ -77,6 +81,17 @@ func (a *walletInitHandler) Unlock(ctx context.Context, req *arkv1.UnlockRequest go a.onUnlock(req.GetPassword()) + go func() { + status, err := a.walletService.Status(context.Background()) + if err != nil { + log.Warnf("failed to get wallet status: %s", err) + return + } + if status.IsUnlocked() && status.IsSynced() { + a.onReady() + } + }() + return &arkv1.UnlockResponse{}, nil } @@ -93,6 +108,19 @@ func (a *walletInitHandler) GetStatus(ctx context.Context, _ *arkv1.GetStatusReq }, nil } +func (a *walletInitHandler) listenWhenReady() { + ctx := context.Background() + <-a.walletService.GetSyncedUpdate(ctx) + + status, err := a.walletService.Status(ctx) + if err != nil { + log.Warnf("failed to get wallet status: %s", err) + } + if status.IsUnlocked() && status.IsSynced() { + a.onReady() + } +} + type walletHandler struct { walletService ports.WalletService } diff --git a/server/internal/interface/grpc/service.go b/server/internal/interface/grpc/service.go index 2d15c6f08..0d48897ee 100644 --- a/server/internal/interface/grpc/service.go +++ b/server/internal/interface/grpc/service.go @@ -183,7 +183,7 @@ func (s *service) newServer(tlsConfig *tls.Config, withAppSvc bool) error { arkv1.RegisterWalletServiceServer(grpcServer, walletHandler) walletInitHandler := handlers.NewWalletInitializerHandler( - s.appConfig.WalletService(), s.onInit, s.onUnlock, + s.appConfig.WalletService(), s.onInit, s.onUnlock, s.onReady, ) arkv1.RegisterWalletInitializerServiceServer(grpcServer, walletInitHandler) @@ -271,14 +271,6 @@ func (s *service) newServer(tlsConfig *tls.Config, withAppSvc bool) error { } func (s *service) onUnlock(password string) { - withoutAppSvc := false - s.stop(withoutAppSvc) - - withAppSvc := true - if err := s.start(withAppSvc); err != nil { - panic(err) - } - if s.config.NoMacaroons { return } @@ -320,6 +312,16 @@ func (s *service) onInit(password string) { log.Debugf("generated macaroons at path %s", datadir) } +func (s *service) onReady() { + withoutAppSvc := false + s.stop(withoutAppSvc) + + withAppSvc := true + if err := s.start(withAppSvc); err != nil { + panic(err) + } +} + func (s *service) autoUnlock() error { ctx := context.Background() wallet := s.appConfig.WalletService() diff --git a/server/test/e2e/covenantless/e2e_test.go b/server/test/e2e/covenantless/e2e_test.go index 61f51b190..f83954a37 100644 --- a/server/test/e2e/covenantless/e2e_test.go +++ b/server/test/e2e/covenantless/e2e_test.go @@ -445,25 +445,50 @@ func setupAspWallet() error { return fmt.Errorf("failed to unlock wallet: %s", err) } - time.Sleep(time.Second) - - req, err = http.NewRequest("GET", "http://localhost:7070/v1/admin/wallet/address", nil) - if err != nil { - return fmt.Errorf("failed to prepare new address request: %s", err) + var status struct { + Initialized bool `json:"initialized"` + Unlocked bool `json:"unlocked"` + Synced bool `json:"synced"` } - req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=") + for { + time.Sleep(time.Second) - resp, err := adminHttpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to get new address: %s", err) + req, err := http.NewRequest("GET", "http://localhost:7070/v1/admin/wallet/status", nil) + if err != nil { + return fmt.Errorf("failed to prepare status request: %s", err) + } + resp, err := adminHttpClient.Do(req) + if err != nil { + return fmt.Errorf("failed to get status: %s", err) + } + if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { + return fmt.Errorf("failed to parse status response: %s", err) + } + if status.Initialized && status.Unlocked && status.Synced { + break + } } var addr struct { Address string `json:"address"` } + for addr.Address == "" { + time.Sleep(time.Second) - if err := json.NewDecoder(resp.Body).Decode(&addr); err != nil { - return fmt.Errorf("failed to parse response: %s", err) + req, err = http.NewRequest("GET", "http://localhost:7070/v1/admin/wallet/address", nil) + if err != nil { + return fmt.Errorf("failed to prepare new address request: %s", err) + } + req.Header.Set("Authorization", "Basic YWRtaW46YWRtaW4=") + + resp, err := adminHttpClient.Do(req) + if err != nil { + return fmt.Errorf("failed to get new address: %s", err) + } + + if err := json.NewDecoder(resp.Body).Decode(&addr); err != nil { + return fmt.Errorf("failed to parse response: %s", err) + } } const numberOfFaucet = 15 // must cover the liquidity needed for all tests From b1c9261f1449b78a28912a31e20b66003cbb8519 Mon Sep 17 00:00:00 2001 From: Dusan Sekulic Date: Thu, 17 Oct 2024 17:36:48 +0200 Subject: [PATCH 10/10] [SDK] Persist tx history, vtxos & Provide transaction feed (#354) * SDK - GetTransactionsStream add to rest/grpc no session, pattern or user messages provided * SDK - AppDataRepository added, store refactor This patch introduces a significant refactor of the Ark SDK codebase. The primary changes involve: 1. **Domain Package Introduction**: A new `domain` package has been created under `store`, encapsulating core data structures and interfaces such as `ConfigData`, `Transaction`, `SdkRepository`, and various repository interfaces. This enhances modularity and separation of concerns. 2. **Replacement of Store with Domain Objects**: All references to the `store` package's `StoreData` have been replaced with `domain.ConfigData`. This change reflects a shift towards using domain-driven design, where the core business logic is represented by domain objects. 3. **Repository Pattern Implementation**: The code has been refactored to use repositories for accessing and managing data. `ConfigRepository` and `TransactionRepository` interfaces are introduced, providing methods for managing configuration data and transactions, respectively. 4. **New Storage Implementations**: New storage implementations using BadgerDB are introduced for persisting transactions (`transaction_repository.go`). This change indicates a move towards more robust and scalable data storage solutions. 5. **Service Layer Introduction**: A service layer (`service.go`) is introduced, which acts as a facade for the underlying data repositories. This layer abstracts the data access logic and provides a unified interface for interacting with configuration and transaction data. 6. **Code Simplification and Cleanup**: The patch removes redundant code and simplifies the existing codebase by consolidating store-related logic and enhancing the overall structure of the code. 7. **Testing Enhancements**: Test cases are updated to reflect the changes in the data access layer, ensuring that the new repository pattern and domain objects are properly tested. Overall, this refactor aims to improve the maintainability, scalability, and testability of the codebase by adopting best practices such as domain-driven design, repository pattern, and separation of concerns. * 'SDK listen for tx stream This diff represents a substantial refactor and enhancement of the `ark-sdk` client, which involves modifications to multiple files, primarily in the `pkg/client-sdk` and `server/internal/core/application` directories. Here's a summary of the key changes: 1. **Configuration and Initialization**: - Refactor to replace file-based configuration store with a domain-based approach. - Introduced `ListenTransactionStream` configuration to allow real-time transaction event listening. 2. **Client SDK Enhancements**: - The client SDK now supports asynchronous transaction streaming and handling. - Introduced methods to get transaction event channels and stop the client gracefully. - Expanded the `ArkClient` interface to include these new functionalities. 3. **Transaction Handling**: - Added support for listening to and processing transaction events in real-time. - Implemented methods to handle different types of transactions, including boarding, redeem, and round transactions. - Expanded the `Transaction` struct to include more metadata, such as `BoardingVOut`. 4. **Repository Changes**: - Introduced `VtxoRepository` for managing VTXO-related operations. - Modified `AppDataRepository` to include `VtxoRepository`. 5. **Example and Test Adjustments**: - Updated example code to utilize the new transaction streaming capabilities. - Adjusted tests to reflect changes in client initialization and configuration handling. 6. **Dependency Updates**: - Updated `go.mod` and `go.sum` to include new dependencies such as `badger` for data storage and potentially updated versions of existing packages. These changes collectively enhance the functionality of the `ark-sdk` client by enabling real-time transaction handling, improving configuration management, and providing a more robust and scalable client architecture. This update seems to be aimed at improving the efficiency and responsiveness of the client application, likely in response to increased demands for real-time data processing and analysis.' * merge * go work sync * test fix * Tiny refactor (#2) * tiny refactor * renaming * Renamings * Fixes (#3) --------- Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com> --- client/main.go | 55 +- go.work.sum | 148 +++++ pkg/client-sdk/ark_sdk.go | 8 +- pkg/client-sdk/client.go | 119 ++-- pkg/client-sdk/client/client.go | 20 + pkg/client-sdk/client/grpc/client.go | 86 +++ pkg/client-sdk/client/rest/client.go | 91 +++ pkg/client-sdk/client_test.go | 109 ++-- pkg/client-sdk/covenant_client.go | 225 ++++++-- pkg/client-sdk/covenantless_client.go | 520 +++++++++++++++--- .../example/covenant/alice_to_bob.go | 23 +- .../example/covenant/wasm/index.html | 7 +- .../example/covenantless/alice_to_bob.go | 73 ++- .../example/covenantless/wasm/index.html | 9 +- pkg/client-sdk/go.mod | 13 + pkg/client-sdk/go.sum | 188 +++++++ pkg/client-sdk/store/file/config_store.go | 142 +++++ pkg/client-sdk/store/file/store.go | 239 -------- pkg/client-sdk/store/file/types.go | 80 +++ pkg/client-sdk/store/file/utils.go | 44 ++ pkg/client-sdk/store/inmemory/config_store.go | 57 ++ pkg/client-sdk/store/inmemory/store.go | 55 -- .../store/kv/transaction_repository.go | 133 +++++ pkg/client-sdk/store/kv/utils.go | 47 ++ pkg/client-sdk/store/kv/vtxo_repository.go | 92 ++++ pkg/client-sdk/store/service.go | 82 +++ .../store/{store_test.go => service_test.go} | 75 ++- pkg/client-sdk/store/store.go | 36 -- pkg/client-sdk/types.go | 44 +- pkg/client-sdk/types/interfaces.go | 35 ++ pkg/client-sdk/types/types.go | 105 ++++ .../wallet/singlekey/bitcoin_wallet.go | 10 +- .../wallet/singlekey/liquid_wallet.go | 10 +- .../wallet/singlekey/store/store_test.go | 8 +- pkg/client-sdk/wallet/singlekey/wallet.go | 4 +- pkg/client-sdk/wallet/wallet_test.go | 4 +- pkg/client-sdk/wasm/browser/config_store.go | 28 +- pkg/client-sdk/wasm/browser/exports.go | 22 +- pkg/client-sdk/wasm/browser/store.go | 34 ++ pkg/client-sdk/wasm/browser/wrappers.go | 16 +- pkg/client-sdk/wasm/main.go | 27 +- .../internal/core/application/covenantless.go | 41 +- server/test/e2e/covenant/e2e_test.go | 11 +- server/test/e2e/covenantless/e2e_test.go | 11 +- 44 files changed, 2461 insertions(+), 725 deletions(-) create mode 100644 pkg/client-sdk/store/file/config_store.go delete mode 100644 pkg/client-sdk/store/file/store.go create mode 100644 pkg/client-sdk/store/file/types.go create mode 100644 pkg/client-sdk/store/file/utils.go create mode 100644 pkg/client-sdk/store/inmemory/config_store.go delete mode 100644 pkg/client-sdk/store/inmemory/store.go create mode 100644 pkg/client-sdk/store/kv/transaction_repository.go create mode 100644 pkg/client-sdk/store/kv/utils.go create mode 100644 pkg/client-sdk/store/kv/vtxo_repository.go create mode 100644 pkg/client-sdk/store/service.go rename pkg/client-sdk/store/{store_test.go => service_test.go} (56%) delete mode 100644 pkg/client-sdk/store/store.go create mode 100644 pkg/client-sdk/types/interfaces.go create mode 100644 pkg/client-sdk/types/types.go create mode 100644 pkg/client-sdk/wasm/browser/store.go diff --git a/client/main.go b/client/main.go index 4dc9659fc..41c55de84 100644 --- a/client/main.go +++ b/client/main.go @@ -13,7 +13,7 @@ import ( "github.com/ark-network/ark/common" arksdk "github.com/ark-network/ark/pkg/client-sdk" "github.com/ark-network/ark/pkg/client-sdk/store" - filestore "github.com/ark-network/ark/pkg/client-sdk/store/file" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/urfave/cli/v2" "golang.org/x/term" @@ -213,12 +213,7 @@ func initArkSdk(ctx *cli.Context) error { } func config(ctx *cli.Context) error { - cfgStore, err := getConfigStore(ctx.String(datadirFlag.Name)) - if err != nil { - return err - } - - cfgData, err := cfgStore.GetData(ctx.Context) + cfgData, err := arkSdkClient.GetConfigData(ctx.Context) if err != nil { return err } @@ -235,6 +230,7 @@ func config(ctx *cli.Context) error { "boarding_descriptor_template": cfgData.BoardingDescriptorTemplate, "explorer_url": cfgData.ExplorerURL, "forfeit_address": cfgData.ForfeitAddress, + "with_transaction_feed": cfgData.WithTransactionFeed, } return printJSON(cfg) @@ -296,16 +292,16 @@ func send(ctx *cli.Context) error { return fmt.Errorf("missing destination, use --to and --amount or --receivers") } - configStore, err := getConfigStore(ctx.String(datadirFlag.Name)) + configData, err := arkSdkClient.GetConfigData(ctx.Context) if err != nil { return err } - cfgData, err := configStore.GetData(ctx.Context) + net, err := getNetwork(ctx, configData) if err != nil { return err } - net := getNetwork(ctx, cfgData) + isBitcoin := isBtcChain(net) var receivers []arksdk.Receiver @@ -378,11 +374,16 @@ func redeem(ctx *cli.Context) error { } func getArkSdkClient(ctx *cli.Context) (arksdk.ArkClient, error) { - cfgStore, err := getConfigStore(ctx.String(datadirFlag.Name)) + dataDir := ctx.String(datadirFlag.Name) + sdkRepository, err := store.NewStore(store.Config{ + ConfigStoreType: types.FileStore, + BaseDir: dataDir, + }) if err != nil { return nil, err } - cfgData, err := cfgStore.GetData(ctx.Context) + + cfgData, err := sdkRepository.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } @@ -392,41 +393,41 @@ func getArkSdkClient(ctx *cli.Context) (arksdk.ArkClient, error) { return nil, fmt.Errorf("CLI not initialized, run 'init' cmd to initialize") } - net := getNetwork(ctx, cfgData) + net, err := getNetwork(ctx, cfgData) + if err != nil { + return nil, err + } if isBtcChain(net) { return loadOrCreateClient( - arksdk.LoadCovenantlessClient, arksdk.NewCovenantlessClient, cfgStore, + arksdk.LoadCovenantlessClient, arksdk.NewCovenantlessClient, sdkRepository, ) } return loadOrCreateClient( - arksdk.LoadCovenantClient, arksdk.NewCovenantClient, cfgStore, + arksdk.LoadCovenantClient, arksdk.NewCovenantClient, sdkRepository, ) } func loadOrCreateClient( - loadFunc, newFunc func(store.ConfigStore) (arksdk.ArkClient, error), - store store.ConfigStore, + loadFunc, newFunc func(types.Store) (arksdk.ArkClient, error), + sdkRepository types.Store, ) (arksdk.ArkClient, error) { - client, err := loadFunc(store) + client, err := loadFunc(sdkRepository) if err != nil { if errors.Is(err, arksdk.ErrNotInitialized) { - return newFunc(store) + return newFunc(sdkRepository) } return nil, err } return client, err } -func getConfigStore(dataDir string) (store.ConfigStore, error) { - return filestore.NewConfigStore(dataDir) -} - -func getNetwork(ctx *cli.Context, configData *store.StoreData) string { - if configData == nil { - return strings.ToLower(ctx.String(networkFlag.Name)) +func getNetwork(ctx *cli.Context, cfgData *types.Config) (string, error) { + if cfgData == nil { + return ctx.String(networkFlag.Name), nil } - return configData.Network.Name + + return cfgData.Network.Name, nil } func isBtcChain(network string) bool { diff --git a/go.work.sum b/go.work.sum index 59e2af44f..c0d80e571 100644 --- a/go.work.sum +++ b/go.work.sum @@ -366,6 +366,7 @@ cloud.google.com/go/websecurityscanner v1.6.11/go.mod h1:vhAZjksELSg58EZfUQ1BMEx cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= cloud.google.com/go/workflows v1.12.10 h1:EGJeZmwgE71jxFOI5s9iKST2Bivif3DSzlqVbiXACXQ= cloud.google.com/go/workflows v1.12.10/go.mod h1:RcKqCiOmKs8wFUEf3EwWZPH5eHc7Oq0kamIyOUCk0IE= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -393,7 +394,9 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/ClickHouse/clickhouse-go v1.4.3 h1:iAFMa2UrQdR5bHJ2/yaSLffZkxpcOYQMCUuKeNXGdqc= github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -401,10 +404,13 @@ github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQ github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= @@ -418,9 +424,11 @@ github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybF github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/ark-network/ark/api-spec v0.0.0-20240812233307-18e343b31899/go.mod h1:0B5seq/gzuGL8OZGUaO12yj73ZJKAde8L+nmLQAZ7IA= +github.com/ark-network/ark/api-spec v0.0.0-20241009155358-ea5fa2202398/go.mod h1:TRbR7D04k6Tx//Eo/DUay+hFTT42NPCo8RmjcyrQ1DY= github.com/ark-network/ark/common v0.0.0-20240812222508-b097e943fb45/go.mod h1:8DYeb06Dl8onmrV09xfsdDMGv5HoVtWoKhLBLXOYHew= github.com/ark-network/ark/common v0.0.0-20240812233307-18e343b31899/go.mod h1:8DYeb06Dl8onmrV09xfsdDMGv5HoVtWoKhLBLXOYHew= github.com/ark-network/ark/common v0.0.0-20240815203029-edc4534dfc87/go.mod h1:aYAGDfoeBLofnZt9n85wusFyCkrS7hvwdo5TynBlkuY= +github.com/ark-network/ark/common v0.0.0-20241009155358-ea5fa2202398/go.mod h1:zA5/UUj+G/WXDmihyzIVWViTo9rqKf/lb/Mw5MjzusM= github.com/ark-network/ark/pkg/client-sdk v0.0.0-20240812230256-910716f72d1a/go.mod h1:avKeK73ezowttW3PaycYB4mChaqigAxr4q8pFwIuHww= github.com/ark-network/ark/pkg/client-sdk v0.0.0-20240913171921-2174e4b04d86/go.mod h1:CRN5aL3u3Q/3tCQLp/ND7NT34/GRsG1ccLk5aX2r7mQ= github.com/ark-network/ark/server/pkg/kvdb v0.0.0-20240812230256-910716f72d1a/go.mod h1:ivr4Qm16kbJMTovsdscYiV1s1vPOYmEBtp9EgrHFGi4= @@ -430,6 +438,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IU github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.49.6 h1:yNldzF5kzLBRvKlKz1S0bkvc2+04R1kt13KfBWQBfFA= github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk= @@ -474,22 +484,31 @@ github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026/go.mod h github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.2.2/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= +github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= github.com/btcsuite/btcd/btcutil v1.1.4/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil/psbt v1.1.9/go.mod h1:ehBEvU91lxSlXtA+zZz3iFYx7Yq9eqnKx4/kSrnsvMY= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcwallet v0.16.10-0.20240706055350-e391a1c31df2/go.mod h1:SLFUSQbP8ON/wxholYMfVLvGPJyk7boczOW/ob+nww4= github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3/go.mod h1:T2xSiKGpUkSLCh68aF+FMXmKK9mFqNdHl9VaqOr+JjU= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk= github.com/btcsuite/btcwallet/wallet/txrules v1.2.1/go.mod h1:MVSqRkju/IGxImXYPfBkG65FgEZYA4fXchheILMVl8g= github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 h1:nmcKAVTv/cmYrs0A4hbiC6Qw+WTLYy/14SmTt3mLnCo= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4/go.mod h1:YqJR8WAAHiKIPesZTr9Cx9Az4fRhRLcJ6GcxzRUZCAc= github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4= +github.com/btcsuite/btcwallet/wtxmgr v1.5.3 h1:QrWCio9Leh3DwkWfp+A1SURj8pYn3JuTLv3waP5uEro= github.com/btcsuite/btcwallet/wtxmgr v1.5.3/go.mod h1:M4nQpxGTXiDlSOODKXboXX7NFthmiBNjzAKKNS7Fhjg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -513,6 +532,7 @@ github.com/cockroachdb/cockroach-go/v2 v2.1.1 h1:3XzfSMuUT0wBe1a3o5C0eOTcArhmmFA github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -520,6 +540,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMu github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -532,17 +553,26 @@ github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08 github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/decred/dcrd/lru v1.1.3 h1:w9EAbvGLyzm6jTjF83UKuqZEiUtJmvRhQDOCEIvSuE0= +github.com/decred/dcrd/lru v1.1.3/go.mod h1:Tw0i0pJyiLEx/oZdHLe1Wdv/Y7EGzAX+sYftnmxBR4o= github.com/dgraph-io/badger/v4 v4.1.0/go.mod h1:P50u28d39ibBRmIJuQC/NSdBOg46HnHw7al2SW5QRHg= +github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= @@ -560,22 +590,47 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fergusstrange/embedded-postgres v1.28.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsouza/fake-gcs-server v1.17.0 h1:OeH75kBZcZa3ZE+zz/mFdJ2btt9FgqfjI7gIh9+5fvk= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= +github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= +github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= @@ -590,15 +645,18 @@ github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -606,8 +664,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v23.5.9+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -618,11 +678,13 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= @@ -646,6 +708,7 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMW github.com/hashicorp/consul/api v1.28.2 h1:mXfkRHrpHN4YY3RqL09nXU1eHKLNiuAN4kHvDQ16k/8= github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -653,12 +716,14 @@ github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -668,8 +733,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= @@ -677,13 +744,18 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -691,29 +763,35 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= @@ -721,10 +799,14 @@ github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsN github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedib0t/go-pretty/v6 v6.2.7 h1:4823Lult/tJ0VI1PgW3aSKw59pMWQ6Kzv9b3Bj6MwY0= github.com/jedib0t/go-pretty/v6 v6.2.7/go.mod h1:FMkOpgGD3EZ91cW8g/96RfxoV7bdeJyzXPYgz1L1ln0= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 h1:NO5tuyw++EGLnz56Q8KMyDZRwJwWO8jQnj285J3FOmY= github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg= github.com/juju/mgotest v1.0.1 h1:XvuZ2whmkHZ5G+Y/wQaSe28p2FyTwcBaqTzStn+QaLc= @@ -744,12 +826,16 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= +github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -764,34 +850,48 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/neutrino v0.16.0/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= +github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd h1:D8aRocHpoCv43hL8egXEMYyPmyOiefFHZ66338KQB2s= github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= github.com/lightninglabs/neutrino/cache v1.1.0/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= github.com/lightninglabs/neutrino/cache v1.1.1/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= +github.com/lightningnetwork/lnd v0.18.2-beta h1:Qv4xQ2ka05vqzmdkFdISHCHP6CzHoYNVKfD18XPjHsM= +github.com/lightningnetwork/lnd v0.18.2-beta/go.mod h1:cGQR1cVEZFZQcCx2VBbDY8xwGjCz+SupSopU1HpjP2I= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= +github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= github.com/lightningnetwork/lnd/fn v1.0.4/go.mod h1:K9gbvdl5z4XmRcqWUVqvvVcuRKtmq9BNQ+cWYlk+vjw= github.com/lightningnetwork/lnd/fn v1.1.0/go.mod h1:P027+0CyELd92H9gnReUkGGAqbFA1HwjHWdfaDFD51U= +github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kfmOrqHbY5zoY= github.com/lightningnetwork/lnd/kvdb v1.4.10/go.mod h1:J2diNABOoII9UrMnxXS5w7vZwP7CA1CStrl8MnIrb3A= github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= +github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= +github.com/lightningnetwork/lnd/sqldb v1.0.3 h1:zLfAwOvM+6+3+hahYO9Q3h8pVV0TghAR7iJ5YMLCd3I= github.com/lightningnetwork/lnd/sqldb v1.0.3/go.mod h1:4cQOkdymlZ1znnjuRNvMoatQGJkRneTj2CoPSPaQhWo= github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= github.com/lightningnetwork/lnd/tlv v1.0.2/go.mod h1:fICAfsqk1IOsC1J7G9IdsWX1EqWRMqEDCNxZJSKr9C4= github.com/lightningnetwork/lnd/tor v1.1.2/go.mod h1:j7T9uJ2NLMaHwE7GiBGnpYLn4f7NRoTM6qj+ul6/ycA= +github.com/ltcsuite/ltcd v0.23.5 h1:MFWjmx2hCwxrUu9v0wdIPOSN7PHg9BWQeh+AO4FsVLI= github.com/ltcsuite/ltcd v0.23.5/go.mod h1:JV6swXR5m0cYFi0VYdQPp3UnMdaDQxaRUCaU1PPjb+g= github.com/ltcsuite/ltcd/btcec/v2 v2.3.2 h1:HVArUNQGqGaSSoyYkk9qGht74U0/uNhS0n7jV9rkmno= github.com/ltcsuite/ltcd/btcec/v2 v2.3.2/go.mod h1:T1t5TjbjPnryvlGQ+RpSKGuU8KhjNN7rS5+IznPj1VM= +github.com/ltcsuite/ltcd/chaincfg/chainhash v1.0.2 h1:xuWxvRKxLvOKuS7/Q/7I3tpc3cWAB0+hZpU8YdVqkzg= github.com/ltcsuite/ltcd/chaincfg/chainhash v1.0.2/go.mod h1:nkLkAFGhursWf2U68gt61hPieK1I+0m78e+2aevNyD8= github.com/ltcsuite/ltcd/ltcutil v1.1.3 h1:8AapjCPLIt/wtYe6Odfk1EC2y9mcbpgjyxyCoNjAkFI= github.com/ltcsuite/ltcd/ltcutil v1.1.3/go.mod h1:z8txd/ohBFrOMBUT70K8iZvHJD/Vc3gzx+6BP6cBxQw= github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/pkger v0.15.1 h1:3MPelV53RnGSW07izx5xGxl4e/sdRD6zqseIk0rMASY= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -801,6 +901,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -817,9 +918,13 @@ github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8Ie github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -840,24 +945,34 @@ github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba h1:fhFP5RliM2HW/8XdcO5QngSfFli9GcRIpMXvypTQt6E= github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs= github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pierrec/lz4/v4 v4.1.16 h1:kQPfno+wyx6C5572ABwV+Uo3pDFzQ7yhyGchSyRda0c= github.com/pierrec/lz4/v4 v4.1.16/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -865,15 +980,18 @@ github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -895,6 +1013,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/sagikazarmark/crypt v0.19.0 h1:WMyLTjHBo64UvNcWqpzY3pbZTYgnemZU8FBZigKc42E= github.com/sagikazarmark/crypt v0.19.0/go.mod h1:c6vimRziqqERhtSe0MhIvzE1w54FrCHtrXb5NH/ja78= github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -909,8 +1028,10 @@ github.com/snowflakedb/gosnowflake v1.6.19 h1:KSHXrQ5o7uso25hNIzi/RObXtnSGkFgie9 github.com/snowflakedb/gosnowflake v1.6.19/go.mod h1:FM1+PWUdwB9udFDsXdfD58NONC0m+MlOSmQRvimobSM= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= @@ -919,20 +1040,27 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/timshannon/badgerhold/v4 v4.0.3 h1:W6pd2qckoXw2cl8eH0ZCV/9CXNaXvaM26tzFi5Tj+v8= github.com/timshannon/badgerhold/v4 v4.0.3/go.mod h1:IkZIr0kcZLMdD7YJfW/G6epb6ZXHD/h0XR2BTk/VZg8= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -943,8 +1071,14 @@ github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI= +github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8= +github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7 h1:X7DtNv+YWy76kELMZB/xVkIJ7YNp2vpgMFVsDcQA40U= github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7/go.mod h1:Zrvx8XgpWvSPdz1lXnuN083CkoZnzwxBLEB03S8et1I= +github.com/vulpemventures/go-bip39 v1.0.2 h1:+BgKOVo04okKf1wA4Fhv8ccvql+qFyzVUdVJKkb48r0= github.com/vulpemventures/go-bip39 v1.0.2/go.mod h1:mjFmuv9D6BtANI6iscMmbHhmBOwjMCFfny3mxHnuDrk= +github.com/vulpemventures/go-elements v0.5.4 h1:l94xoa9aYPPWiOB7Pmi08rKYvdk/n/sQIbLkQfEAASc= +github.com/vulpemventures/go-elements v0.5.4/go.mod h1:Tvhb+rZWv3lxoI5CdK03J3V+e2QVr/7UAnCYILxFSq4= github.com/xanzy/go-gitlab v0.15.0 h1:rWtwKTgEnXyNUGrOArN7yyc3THRkpYcKXIXia9abywQ= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -956,8 +1090,11 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= @@ -984,7 +1121,10 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= @@ -1011,6 +1151,7 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1214,6 +1355,7 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCM gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/juju/environschema.v1 v1.0.0 h1:51vT1bzbP9fntQ0I9ECSlku2p19Szj/N2beZFeIH2kM= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= @@ -1222,6 +1364,7 @@ gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= @@ -1237,6 +1380,7 @@ modernc.org/db v1.0.0 h1:2c6NdCfaLnshSvY7OU09cyAY0gYXUZj4lmg5ItHyucg= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= modernc.org/file v1.0.0 h1:9/PdvjVxd5+LcWUQIfapAWRGOkDLK90rloa8s/au06A= modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= @@ -1244,15 +1388,19 @@ modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/internal v1.0.0 h1:XMDsFDcBDsibbBnHB2xzljZ+B1yrOVLEFkKL2u15Glw= modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= +modernc.org/libc v1.59.3 h1:A4QAp1lRSn2/b4aU+wBtq+yeKgq/2BUevrj0p1ZNy2M= modernc.org/libc v1.59.3/go.mod h1:EY/egGEU7Ju66eU6SBqCNYaFUDuc4npICkMWnU5EE3A= modernc.org/lldb v1.0.0 h1:6vjDJxQEfhlOLwl4bhpwIz00uyFK4EmSYcbwqwbynsc= modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/ql v1.0.0 h1:bIQ/trWNVjQPlinI6jdOQsi195SIturGo3mp5hsDqVU= modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/pkg/client-sdk/ark_sdk.go b/pkg/client-sdk/ark_sdk.go index f44f62642..87eca1bef 100644 --- a/pkg/client-sdk/ark_sdk.go +++ b/pkg/client-sdk/ark_sdk.go @@ -4,11 +4,11 @@ import ( "context" "github.com/ark-network/ark/pkg/client-sdk/client" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" ) type ArkClient interface { - GetConfigData(ctx context.Context) (*store.StoreData, error) + GetConfigData(ctx context.Context) (*types.Config, error) Init(ctx context.Context, args InitArgs) error InitWithWallet(ctx context.Context, args InitWithWalletArgs) error IsLocked(ctx context.Context) bool @@ -27,8 +27,10 @@ type ArkClient interface { SendAsync(ctx context.Context, withExpiryCoinselect bool, receivers []Receiver) (string, error) Claim(ctx context.Context) (string, error) ListVtxos(ctx context.Context) (spendable, spent []client.Vtxo, err error) - GetTransactionHistory(ctx context.Context) ([]Transaction, error) Dump(ctx context.Context) (seed string, err error) + GetTransactionHistory(ctx context.Context) ([]types.Transaction, error) + GetTransactionEventChannel() chan types.TransactionEvent + Stop() error } type Receiver interface { diff --git a/pkg/client-sdk/client.go b/pkg/client-sdk/client.go index 7adb3f0f1..a26edfea4 100644 --- a/pkg/client-sdk/client.go +++ b/pkg/client-sdk/client.go @@ -11,7 +11,7 @@ import ( "github.com/ark-network/ark/pkg/client-sdk/client" "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey" walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store" @@ -28,8 +28,8 @@ const ( // wallet SingleKeyWallet = wallet.SingleKeyWallet // store - FileStore = store.FileStore - InMemoryStore = store.InMemoryStore + FileStore = types.FileStore + InMemoryStore = types.InMemoryStore // explorer BitcoinExplorer = explorer.BitcoinExplorer LiquidExplorer = explorer.LiquidExplorer @@ -53,23 +53,65 @@ var ( ) type arkClient struct { - *store.StoreData + *types.Config wallet wallet.WalletService - store store.ConfigStore + store types.Store explorer explorer.Explorer client client.ASPClient + + txStreamCtxCancel context.CancelFunc } func (a *arkClient) GetConfigData( _ context.Context, -) (*store.StoreData, error) { - if a.StoreData == nil { +) (*types.Config, error) { + if a.Config == nil { return nil, fmt.Errorf("client sdk not initialized") } - return a.StoreData, nil + return a.Config, nil +} + +func (a *arkClient) Unlock(ctx context.Context, pasword string) error { + _, err := a.wallet.Unlock(ctx, pasword) + return err +} + +func (a *arkClient) Lock(ctx context.Context, pasword string) error { + return a.wallet.Lock(ctx, pasword) +} + +func (a *arkClient) IsLocked(ctx context.Context) bool { + return a.wallet.IsLocked() +} + +func (a *arkClient) Dump(ctx context.Context) (string, error) { + return a.wallet.Dump(ctx) +} + +func (a *arkClient) Receive(ctx context.Context) (string, string, error) { + offchainAddr, boardingAddr, err := a.wallet.NewAddress(ctx, false) + if err != nil { + return "", "", err + } + + return offchainAddr, boardingAddr, nil +} + +func (a *arkClient) GetTransactionEventChannel() chan types.TransactionEvent { + return a.store.TransactionStore().GetEventChannel() +} + +func (a *arkClient) Stop() error { + if a.Config.WithTransactionFeed { + a.txStreamCtxCancel() + } + + a.store.Close() + + return nil } -func (a *arkClient) InitWithWallet( +func (a *arkClient) initWithWallet( ctx context.Context, args InitWithWalletArgs, ) error { if err := args.validate(); err != nil { @@ -104,7 +146,7 @@ func (a *arkClient) InitWithWallet( return fmt.Errorf("failed to parse asp pubkey: %s", err) } - storeData := store.StoreData{ + storeData := types.Config{ AspUrl: args.AspUrl, AspPubkey: aspPubkey, WalletType: args.Wallet.GetType(), @@ -116,18 +158,19 @@ func (a *arkClient) InitWithWallet( Dust: info.Dust, BoardingDescriptorTemplate: info.BoardingDescriptorTemplate, ForfeitAddress: info.ForfeitAddress, + WithTransactionFeed: args.ListenTransactionStream, } - if err := a.store.AddData(ctx, storeData); err != nil { + if err := a.store.ConfigStore().AddData(ctx, storeData); err != nil { return err } if _, err := args.Wallet.Create(ctx, args.Password, args.Seed); err != nil { //nolint:all - a.store.CleanData(ctx) + a.store.ConfigStore().CleanData(ctx) return err } - a.StoreData = &storeData + a.Config = &storeData a.wallet = args.Wallet a.explorer = explorerSvc a.client = clientSvc @@ -135,7 +178,7 @@ func (a *arkClient) InitWithWallet( return nil } -func (a *arkClient) Init( +func (a *arkClient) init( ctx context.Context, args InitArgs, ) error { if err := args.validate(); err != nil { @@ -170,7 +213,7 @@ func (a *arkClient) Init( return fmt.Errorf("failed to parse asp pubkey: %s", err) } - storeData := store.StoreData{ + cfgData := types.Config{ AspUrl: args.AspUrl, AspPubkey: aspPubkey, WalletType: args.WalletType, @@ -183,23 +226,24 @@ func (a *arkClient) Init( BoardingDescriptorTemplate: info.BoardingDescriptorTemplate, ExplorerURL: args.ExplorerURL, ForfeitAddress: info.ForfeitAddress, + WithTransactionFeed: args.ListenTransactionStream, } - walletSvc, err := getWallet(a.store, &storeData, supportedWallets) + walletSvc, err := getWallet(a.store.ConfigStore(), &cfgData, supportedWallets) if err != nil { return err } - if err := a.store.AddData(ctx, storeData); err != nil { + if err := a.store.ConfigStore().AddData(ctx, cfgData); err != nil { return err } if _, err := walletSvc.Create(ctx, args.Password, args.Seed); err != nil { //nolint:all - a.store.CleanData(ctx) + a.store.ConfigStore().CleanData(ctx) return err } - a.StoreData = &storeData + a.Config = &cfgData a.wallet = walletSvc a.explorer = explorerSvc a.client = clientSvc @@ -207,32 +251,6 @@ func (a *arkClient) Init( return nil } -func (a *arkClient) Unlock(ctx context.Context, pasword string) error { - _, err := a.wallet.Unlock(ctx, pasword) - return err -} - -func (a *arkClient) Lock(ctx context.Context, pasword string) error { - return a.wallet.Lock(ctx, pasword) -} - -func (a *arkClient) IsLocked(ctx context.Context) bool { - return a.wallet.IsLocked() -} - -func (a *arkClient) Dump(ctx context.Context) (string, error) { - return a.wallet.Dump(ctx) -} - -func (a *arkClient) Receive(ctx context.Context) (string, string, error) { - offchainAddr, boardingAddr, err := a.wallet.NewAddress(ctx, false) - if err != nil { - return "", "", err - } - - return offchainAddr, boardingAddr, nil -} - func (a *arkClient) ping( ctx context.Context, paymentID string, ) func() { @@ -270,11 +288,11 @@ func getExplorer(explorerURL, network string) (explorer.Explorer, error) { } func getWallet( - storeSvc store.ConfigStore, data *store.StoreData, supportedWallets utils.SupportedType[struct{}], + configStore types.ConfigStore, data *types.Config, supportedWallets utils.SupportedType[struct{}], ) (wallet.WalletService, error) { switch data.WalletType { case wallet.SingleKeyWallet: - return getSingleKeyWallet(storeSvc, data.Network.Name) + return getSingleKeyWallet(configStore, data.Network.Name) default: return nil, fmt.Errorf( "unsuported wallet type '%s', please select one of: %s", @@ -284,12 +302,13 @@ func getWallet( } func getSingleKeyWallet( - configStore store.ConfigStore, network string, + configStore types.ConfigStore, network string, ) (wallet.WalletService, error) { walletStore, err := getWalletStore(configStore.GetType(), configStore.GetDatadir()) if err != nil { return nil, err } + if strings.Contains(network, "liquid") { return singlekeywallet.NewLiquidWallet(configStore, walletStore) } @@ -298,9 +317,9 @@ func getSingleKeyWallet( func getWalletStore(storeType, datadir string) (walletstore.WalletStore, error) { switch storeType { - case store.InMemoryStore: + case types.InMemoryStore: return inmemorystore.NewWalletStore() - case store.FileStore: + case types.FileStore: return filestore.NewWalletStore(datadir) default: return nil, fmt.Errorf("unknown wallet store type") diff --git a/pkg/client-sdk/client/client.go b/pkg/client-sdk/client/client.go index 3bd494ef2..ce895de32 100644 --- a/pkg/client-sdk/client/client.go +++ b/pkg/client-sdk/client/client.go @@ -50,6 +50,7 @@ type ASPClient interface { GetRound(ctx context.Context, txID string) (*Round, error) GetRoundByID(ctx context.Context, roundID string) (*Round, error) Close() + GetTransactionsStream(ctx context.Context) (<-chan TransactionEvent, func(), error) } type Info struct { @@ -170,3 +171,22 @@ type RoundSigningNoncesGeneratedEvent struct { } func (e RoundSigningNoncesGeneratedEvent) isRoundEvent() {} + +type TransactionEvent struct { + Round *RoundTransaction + Redeem *RedeemTransaction + Err error +} + +type RoundTransaction struct { + Txid string + SpentVtxos []Outpoint + SpendableVtxos []Vtxo + ClaimedBoardingUtxos []Outpoint +} + +type RedeemTransaction struct { + Txid string + SpentVtxos []Outpoint + SpendableVtxos []Vtxo +} diff --git a/pkg/client-sdk/client/grpc/client.go b/pkg/client-sdk/client/grpc/client.go index f5448a5f8..1628fa0ac 100644 --- a/pkg/client-sdk/client/grpc/client.go +++ b/pkg/client-sdk/client/grpc/client.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "fmt" + "io" "strings" "time" @@ -329,3 +330,88 @@ func (c *grpcClient) Close() { //nolint:all c.conn.Close() } + +func (c *grpcClient) GetTransactionsStream( + ctx context.Context, +) (<-chan client.TransactionEvent, func(), error) { + stream, err := c.svc.GetTransactionsStream(ctx, &arkv1.GetTransactionsStreamRequest{}) + if err != nil { + return nil, nil, err + } + + eventCh := make(chan client.TransactionEvent) + + go func() { + defer close(eventCh) + for { + resp, err := stream.Recv() + if err == io.EOF { + return + } + if err != nil { + eventCh <- client.TransactionEvent{Err: err} + return + } + + switch tx := resp.Tx.(type) { + case *arkv1.GetTransactionsStreamResponse_Round: + eventCh <- client.TransactionEvent{ + Round: &client.RoundTransaction{ + Txid: tx.Round.Txid, + SpentVtxos: outpointsFromProto(tx.Round.SpentVtxos), + SpendableVtxos: vtxosFromProto(tx.Round.SpendableVtxos), + ClaimedBoardingUtxos: outpointsFromProto(tx.Round.ClaimedBoardingUtxos), + }, + } + case *arkv1.GetTransactionsStreamResponse_Redeem: + eventCh <- client.TransactionEvent{ + Redeem: &client.RedeemTransaction{ + Txid: tx.Redeem.Txid, + SpentVtxos: outpointsFromProto(tx.Redeem.SpentVtxos), + SpendableVtxos: vtxosFromProto(tx.Redeem.SpendableVtxos), + }, + } + } + } + }() + + closeFn := func() { + if err := stream.CloseSend(); err != nil { + logrus.Warnf("failed to close stream: %v", err) + } + } + + return eventCh, closeFn, nil +} + +func outpointsFromProto(protoOutpoints []*arkv1.Outpoint) []client.Outpoint { + outpoints := make([]client.Outpoint, len(protoOutpoints)) + for i, o := range protoOutpoints { + outpoints[i] = client.Outpoint{ + Txid: o.Txid, + VOut: o.Vout, + } + } + return outpoints +} + +func vtxosFromProto(protoVtxos []*arkv1.Vtxo) []client.Vtxo { + vtxos := make([]client.Vtxo, len(protoVtxos)) + for i, v := range protoVtxos { + expiresAt := time.Unix(v.ExpireAt, 0) + vtxos[i] = client.Vtxo{ + Outpoint: client.Outpoint{ + Txid: v.Outpoint.Txid, + VOut: v.Outpoint.Vout, + }, + Descriptor: v.Descriptor_, + Amount: v.Amount, + RoundTxid: v.RoundTxid, + ExpiresAt: &expiresAt, + RedeemTx: v.RedeemTx, + Pending: v.Pending, + SpentBy: v.SpentBy, + } + } + return vtxos +} diff --git a/pkg/client-sdk/client/rest/client.go b/pkg/client-sdk/client/rest/client.go index 4a0ba48a6..bfdcaf86c 100644 --- a/pkg/client-sdk/client/rest/client.go +++ b/pkg/client-sdk/client/rest/client.go @@ -615,3 +615,94 @@ func (t treeFromProto) parse() tree.CongestionTree { return congestionTree } + +func (c *restClient) GetTransactionsStream(ctx context.Context) (<-chan client.TransactionEvent, func(), error) { + ctx, cancel := context.WithTimeout(ctx, c.requestTimeout) + eventsCh := make(chan client.TransactionEvent) + + go func() { + ticker := time.NewTicker(1 * time.Second) + defer close(eventsCh) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + resp, err := c.svc.ArkServiceGetTransactionsStream(ark_service.NewArkServiceGetTransactionsStreamParams()) + if err != nil { + eventsCh <- client.TransactionEvent{Err: err} + return + } + + if resp.Payload.Result.Round != nil { + eventsCh <- client.TransactionEvent{ + Round: &client.RoundTransaction{ + Txid: resp.Payload.Result.Round.Txid, + SpentVtxos: outpointsFromRest(resp.Payload.Result.Round.SpentVtxos), + SpendableVtxos: vtxosFromRest(resp.Payload.Result.Round.SpendableVtxos), + ClaimedBoardingUtxos: outpointsFromRest(resp.Payload.Result.Round.ClaimedBoardingUtxos), + }, + } + } else if resp.Payload.Result.Redeem != nil { + eventsCh <- client.TransactionEvent{ + Redeem: &client.RedeemTransaction{ + Txid: resp.Payload.Result.Redeem.Txid, + SpentVtxos: outpointsFromRest(resp.Payload.Result.Redeem.SpentVtxos), + SpendableVtxos: vtxosFromRest(resp.Payload.Result.Redeem.SpendableVtxos), + }, + } + } + } + } + }() + + return eventsCh, cancel, nil +} + +func outpointsFromRest(restOutpoints []*models.V1Outpoint) []client.Outpoint { + outpoints := make([]client.Outpoint, len(restOutpoints)) + for i, o := range restOutpoints { + outpoints[i] = client.Outpoint{ + Txid: o.Txid, + VOut: uint32(o.Vout), + } + } + return outpoints +} + +func vtxosFromRest(restVtxos []*models.V1Vtxo) []client.Vtxo { + vtxos := make([]client.Vtxo, len(restVtxos)) + for i, v := range restVtxos { + var expiresAt *time.Time + if v.ExpireAt != "" && v.ExpireAt != "0" { + expAt, err := strconv.Atoi(v.ExpireAt) + if err != nil { + return nil + } + t := time.Unix(int64(expAt), 0) + expiresAt = &t + } + + amount, err := strconv.Atoi(v.Amount) + if err != nil { + return nil + } + + vtxos[i] = client.Vtxo{ + Outpoint: client.Outpoint{ + Txid: v.Outpoint.Txid, + VOut: uint32(v.Outpoint.Vout), + }, + Descriptor: v.Descriptor, + Amount: uint64(amount), + RoundTxid: v.RoundTxid, + ExpiresAt: expiresAt, + RedeemTx: v.RedeemTx, + Pending: v.Pending, + SpentBy: v.SpentBy, + } + } + return vtxos +} diff --git a/pkg/client-sdk/client_test.go b/pkg/client-sdk/client_test.go index 36c018b00..b615bb5b1 100644 --- a/pkg/client-sdk/client_test.go +++ b/pkg/client-sdk/client_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/ark-network/ark/pkg/client-sdk/client" + sdktypes "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/stretchr/testify/require" ) @@ -15,90 +16,106 @@ func TestVtxosToTxs(t *testing.T) { tests := []struct { name string fixture string - want []Transaction + want []sdktypes.Transaction }{ { name: "Alice Before Sending Async", fixture: aliceBeforeSendingAsync, - want: []Transaction{}, + want: []sdktypes.Transaction{}, }, { name: "Alice After Sending Async", fixture: aliceAfterSendingAsync, - want: []Transaction{ + want: []sdktypes.Transaction{ { - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - Amount: 1000, - Type: TxSent, - IsPending: false, - CreatedAt: time.Unix(1726054898, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", + }, + Amount: 1000, + Type: sdktypes.TxSent, + IsPending: false, + CreatedAt: time.Unix(1726054898, 0), }, }, }, { name: "Bob Before Claiming Async", fixture: bobBeforeClaimingAsync, - want: []Transaction{ + want: []sdktypes.Transaction{ { - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - Amount: 1000, - Type: TxReceived, - IsPending: true, - CreatedAt: time.Unix(1726054898, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", + }, + Amount: 1000, + Type: sdktypes.TxReceived, + IsPending: true, + CreatedAt: time.Unix(1726054898, 0), }, { - RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - Amount: 2000, - Type: TxReceived, - IsPending: true, - CreatedAt: time.Unix(1726486359, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", + }, + Amount: 2000, + Type: sdktypes.TxReceived, + IsPending: true, + CreatedAt: time.Unix(1726486359, 0), }, }, }, { name: "Bob After Claiming Async", fixture: bobAfterClaimingAsync, - want: []Transaction{ + want: []sdktypes.Transaction{ { - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - Amount: 1000, - Type: TxReceived, - IsPending: false, - CreatedAt: time.Unix(1726054898, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", + }, + Amount: 1000, + Type: sdktypes.TxReceived, + IsPending: false, + CreatedAt: time.Unix(1726054898, 0), }, { - RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - Amount: 2000, - Type: TxReceived, - IsPending: false, - CreatedAt: time.Unix(1726486359, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", + }, + Amount: 2000, + Type: sdktypes.TxReceived, + IsPending: false, + CreatedAt: time.Unix(1726486359, 0), }, }, }, { name: "Bob After Sending Async", fixture: bobAfterSendingAsync, - want: []Transaction{ + want: []sdktypes.Transaction{ { - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - Amount: 1000, - Type: TxReceived, - IsPending: false, - CreatedAt: time.Unix(1726054898, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", + }, + Amount: 1000, + Type: sdktypes.TxReceived, + IsPending: false, + CreatedAt: time.Unix(1726054898, 0), }, { - RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - Amount: 2000, - Type: TxReceived, - IsPending: false, - CreatedAt: time.Unix(1726486359, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", + }, + Amount: 2000, + Type: sdktypes.TxReceived, + IsPending: false, + CreatedAt: time.Unix(1726486359, 0), }, { - RedeemTxid: "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0", - Amount: 2100, - Type: TxSent, - IsPending: false, - CreatedAt: time.Unix(1726503865, 0), + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0", + }, + Amount: 2100, + Type: sdktypes.TxSent, + IsPending: false, + CreatedAt: time.Unix(1726503865, 0), }, }, }, diff --git a/pkg/client-sdk/covenant_client.go b/pkg/client-sdk/covenant_client.go index f1e0b812f..3ad4543de 100644 --- a/pkg/client-sdk/covenant_client.go +++ b/pkg/client-sdk/covenant_client.go @@ -17,7 +17,7 @@ import ( "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" "github.com/ark-network/ark/pkg/client-sdk/redemption" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -56,86 +56,199 @@ type covenantArkClient struct { *arkClient } -func NewCovenantClient(storeSvc store.ConfigStore) (ArkClient, error) { - data, err := storeSvc.GetData(context.Background()) +func NewCovenantClient(sdkStore types.Store) (ArkClient, error) { + cfgData, err := sdkStore.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } - if data != nil { + + if cfgData != nil { return nil, ErrAlreadyInitialized } - return &covenantArkClient{&arkClient{store: storeSvc}}, nil + return &covenantArkClient{ + &arkClient{ + store: sdkStore, + }, + }, nil } -func LoadCovenantClient(storeSvc store.ConfigStore) (ArkClient, error) { - if storeSvc == nil { - return nil, fmt.Errorf("missin store service") +func LoadCovenantClient(sdkStore types.Store) (ArkClient, error) { + if sdkStore == nil { + return nil, fmt.Errorf("missin sdk repository") } - data, err := storeSvc.GetData(context.Background()) + cfgData, err := sdkStore.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } - if data == nil { + + if cfgData == nil { return nil, ErrNotInitialized } clientSvc, err := getClient( - supportedClients, data.ClientType, data.AspUrl, + supportedClients, cfgData.ClientType, cfgData.AspUrl, ) if err != nil { return nil, fmt.Errorf("failed to setup transport client: %s", err) } - explorerSvc, err := getExplorer(data.ExplorerURL, data.Network.Name) + explorerSvc, err := getExplorer(cfgData.ExplorerURL, cfgData.Network.Name) if err != nil { return nil, fmt.Errorf("failed to setup explorer: %s", err) } - walletSvc, err := getWallet(storeSvc, data, supportedWallets) + walletSvc, err := getWallet( + sdkStore.ConfigStore(), + cfgData, + supportedWallets, + ) if err != nil { return nil, fmt.Errorf("faile to setup wallet: %s", err) } - return &covenantArkClient{ - &arkClient{data, walletSvc, storeSvc, explorerSvc, clientSvc}, - }, nil + covenantClient := covenantArkClient{ + &arkClient{ + Config: cfgData, + wallet: walletSvc, + store: sdkStore, + explorer: explorerSvc, + client: clientSvc, + }, + } + + if cfgData.WithTransactionFeed { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + covenantClient.txStreamCtxCancel = txStreamCtxCancel + go covenantClient.listenForTxStream(txStreamCtx) + go covenantClient.listenForBoardingUtxos(txStreamCtx) + } + + return &covenantClient, nil } func LoadCovenantClientWithWallet( - storeSvc store.ConfigStore, walletSvc wallet.WalletService, + sdkStore types.Store, walletSvc wallet.WalletService, ) (ArkClient, error) { - if storeSvc == nil { - return nil, fmt.Errorf("missin store service") + if sdkStore == nil { + return nil, fmt.Errorf("missin sdk repository") } + if walletSvc == nil { return nil, fmt.Errorf("missin wallet service") } - data, err := storeSvc.GetData(context.Background()) + cfgData, err := sdkStore.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } - if data == nil { + if cfgData == nil { return nil, ErrNotInitialized } clientSvc, err := getClient( - supportedClients, data.ClientType, data.AspUrl, + supportedClients, cfgData.ClientType, cfgData.AspUrl, ) if err != nil { return nil, fmt.Errorf("failed to setup transport client: %s", err) } - explorerSvc, err := getExplorer(data.ExplorerURL, data.Network.Name) + explorerSvc, err := getExplorer(cfgData.ExplorerURL, cfgData.Network.Name) if err != nil { return nil, fmt.Errorf("failed to setup explorer: %s", err) } - return &covenantArkClient{ - &arkClient{data, walletSvc, storeSvc, explorerSvc, clientSvc}, - }, nil + covenantClient := covenantArkClient{ + &arkClient{ + Config: cfgData, + wallet: walletSvc, + store: sdkStore, + explorer: explorerSvc, + client: clientSvc, + }, + } + + if cfgData.WithTransactionFeed { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + covenantClient.txStreamCtxCancel = txStreamCtxCancel + go covenantClient.listenForTxStream(txStreamCtx) + go covenantClient.listenForBoardingUtxos(txStreamCtx) + } + + return &covenantClient, nil +} + +func (a *covenantArkClient) Init(ctx context.Context, args InitArgs) error { + err := a.arkClient.init(ctx, args) + if err != nil { + return err + } + + if args.ListenTransactionStream { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + a.txStreamCtxCancel = txStreamCtxCancel + go a.listenForTxStream(txStreamCtx) + go a.listenForBoardingUtxos(txStreamCtx) + } + + return nil +} + +func (a *covenantArkClient) InitWithWallet(ctx context.Context, args InitWithWalletArgs) error { + err := a.arkClient.initWithWallet(ctx, args) + if err != nil { + return err + } + + if a.WithTransactionFeed { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + a.txStreamCtxCancel = txStreamCtxCancel + go a.listenForTxStream(txStreamCtx) + go a.listenForBoardingUtxos(txStreamCtx) + } + + return nil +} + +func (a *covenantArkClient) listenForTxStream(ctx context.Context) { + eventChan, closeFunc, err := a.client.GetTransactionsStream(ctx) + if err != nil { + log.WithError(err).Error("Failed to get transaction stream") + return + } + defer closeFunc() + + for { + select { + case event, ok := <-eventChan: + if !ok { + return + } + + a.processTransactionEvent(event) + case <-ctx.Done(): + return + } + } +} + +func (a *covenantArkClient) processTransactionEvent( + event client.TransactionEvent, +) { + // TODO considering current covenant state where all payments happening in round + //and that this is going to change we leave this unimplemented until asnc payments are implemented + //also with current state it is not possible to cover some edge cases like when in a round there + //are multiple boarding inputs + spent vtxo with change in spendable + received in the same round +} + +func (a *covenantArkClient) listenForBoardingUtxos( + ctx context.Context, +) { + // TODO considering current covenant state where all payments happening in round + //and that this is going to change we leave this unimplemented until asnc payments are implemented + //also with current state it is not possible to cover some edge cases like when in a round there + //are multiple boarding inputs + spent vtxo with change in spendable + received in the same round } func (a *covenantArkClient) ListVtxos( @@ -549,13 +662,19 @@ func (a *covenantArkClient) Claim(ctx context.Context) (string, error) { ) } -func (a *covenantArkClient) GetTransactionHistory(ctx context.Context) ([]Transaction, error) { +func (a *covenantArkClient) GetTransactionHistory( + ctx context.Context, +) ([]types.Transaction, error) { + if a.Config.WithTransactionFeed { + return a.store.TransactionStore().GetAllTransactions(ctx) + } + spendableVtxos, spentVtxos, err := a.ListVtxos(ctx) if err != nil { return nil, err } - config, err := a.store.GetData(ctx) + config, err := a.store.ConfigStore().GetData(ctx) if err != nil { return nil, err } @@ -1183,7 +1302,7 @@ func (a *covenantArkClient) validateCongestionTree( if !utils.IsOnchainOnly(receivers) { if err := tree.ValidateCongestionTree( - event.Tree, poolTx, a.StoreData.AspPubkey, a.RoundLifetime, + event.Tree, poolTx, a.Config.AspPubkey, a.RoundLifetime, ); err != nil { return err } @@ -1666,7 +1785,9 @@ func (a *covenantArkClient) offchainAddressToDefaultVtxoDescriptor(addr string) return vtxoScript.ToDescriptor(), nil } -func (a *covenantArkClient) getBoardingTxs(ctx context.Context) (transactions []Transaction) { +func (a *covenantArkClient) getBoardingTxs( + ctx context.Context, +) (transactions []types.Transaction) { utxos, err := a.getClaimableBoardingUtxos(ctx) if err != nil { return nil @@ -1683,21 +1804,29 @@ func (a *covenantArkClient) getBoardingTxs(ctx context.Context) (transactions [] } for _, u := range allUtxos { - transactions = append(transactions, Transaction{ - BoardingTxid: u.Txid, - Amount: u.Amount, - Type: TxReceived, - CreatedAt: u.CreatedAt, + pending := false + if isPending[u.Txid] { + pending = true + } + + transactions = append(transactions, types.Transaction{ + TransactionKey: types.TransactionKey{ + BoardingTxid: u.Txid, + }, + Amount: u.Amount, + Type: types.TxReceived, + CreatedAt: u.CreatedAt, + IsPending: pending, }) } return } func vtxosToTxsCovenant( - roundLifetime int64, spendable, spent []client.Vtxo, boardingTxs []Transaction, -) ([]Transaction, error) { - transactions := make([]Transaction, 0) - unconfirmedBoardingTxs := make([]Transaction, 0) + roundLifetime int64, spendable, spent []client.Vtxo, boardingTxs []types.Transaction, +) ([]types.Transaction, error) { + transactions := make([]types.Transaction, 0) + unconfirmedBoardingTxs := make([]types.Transaction, 0) for _, tx := range boardingTxs { emptyTime := time.Time{} if tx.CreatedAt == emptyTime { @@ -1722,9 +1851,9 @@ func vtxosToTxsCovenant( } } // what kind of tx was this? send or receive? - txType := TxReceived + txType := types.TxReceived if amount < 0 { - txType = TxSent + txType = types.TxSent } // get redeem txid redeemTxid := "" @@ -1736,12 +1865,14 @@ func vtxosToTxsCovenant( redeemTxid = txid } // add transaction - transactions = append(transactions, Transaction{ - RoundTxid: v.RoundTxid, - RedeemTxid: redeemTxid, - Amount: uint64(math.Abs(float64(amount))), - Type: txType, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), + transactions = append(transactions, types.Transaction{ + TransactionKey: types.TransactionKey{ + RoundTxid: v.RoundTxid, + RedeemTxid: redeemTxid, + }, + Amount: uint64(math.Abs(float64(amount))), + Type: txType, + CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), }) } diff --git a/pkg/client-sdk/covenantless_client.go b/pkg/client-sdk/covenantless_client.go index b550979d8..6f9ebdaf3 100644 --- a/pkg/client-sdk/covenantless_client.go +++ b/pkg/client-sdk/covenantless_client.go @@ -18,7 +18,7 @@ import ( "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" "github.com/ark-network/ark/pkg/client-sdk/redemption" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" @@ -57,86 +57,468 @@ type covenantlessArkClient struct { *arkClient } -func NewCovenantlessClient(storeSvc store.ConfigStore) (ArkClient, error) { - data, err := storeSvc.GetData(context.Background()) +func NewCovenantlessClient(sdkStore types.Store) (ArkClient, error) { + cfgData, err := sdkStore.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } - if data != nil { + + if cfgData != nil { return nil, ErrAlreadyInitialized } - return &covenantlessArkClient{&arkClient{store: storeSvc}}, nil + return &covenantlessArkClient{ + &arkClient{ + store: sdkStore, + }, + }, nil } -func LoadCovenantlessClient(storeSvc store.ConfigStore) (ArkClient, error) { - if storeSvc == nil { - return nil, fmt.Errorf("missin store service") +func LoadCovenantlessClient(sdkStore types.Store) (ArkClient, error) { + if sdkStore == nil { + return nil, fmt.Errorf("missin sdk repository") } - data, err := storeSvc.GetData(context.Background()) + cfgData, err := sdkStore.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } - if data == nil { + if cfgData == nil { return nil, ErrNotInitialized } clientSvc, err := getClient( - supportedClients, data.ClientType, data.AspUrl, + supportedClients, cfgData.ClientType, cfgData.AspUrl, ) if err != nil { return nil, fmt.Errorf("failed to setup transport client: %s", err) } - explorerSvc, err := getExplorer(data.ExplorerURL, data.Network.Name) + explorerSvc, err := getExplorer(cfgData.ExplorerURL, cfgData.Network.Name) if err != nil { return nil, fmt.Errorf("failed to setup explorer: %s", err) } - walletSvc, err := getWallet(storeSvc, data, supportedWallets) + walletSvc, err := getWallet( + sdkStore.ConfigStore(), + cfgData, + supportedWallets, + ) if err != nil { return nil, fmt.Errorf("faile to setup wallet: %s", err) } - return &covenantlessArkClient{ - &arkClient{data, walletSvc, storeSvc, explorerSvc, clientSvc}, - }, nil + covenantlessClient := covenantlessArkClient{ + &arkClient{ + Config: cfgData, + wallet: walletSvc, + store: sdkStore, + explorer: explorerSvc, + client: clientSvc, + }, + } + + if cfgData.WithTransactionFeed { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + covenantlessClient.txStreamCtxCancel = txStreamCtxCancel + go covenantlessClient.listenForTransactions(txStreamCtx) + go covenantlessClient.listenForBoardingUtxos(txStreamCtx) + } + + return &covenantlessClient, nil } func LoadCovenantlessClientWithWallet( - storeSvc store.ConfigStore, walletSvc wallet.WalletService, + sdkStore types.Store, walletSvc wallet.WalletService, ) (ArkClient, error) { - if storeSvc == nil { - return nil, fmt.Errorf("missin store service") + if sdkStore == nil { + return nil, fmt.Errorf("missin sdk repository") } + if walletSvc == nil { return nil, fmt.Errorf("missin wallet service") } - data, err := storeSvc.GetData(context.Background()) + cfgData, err := sdkStore.ConfigStore().GetData(context.Background()) if err != nil { return nil, err } - if data == nil { + if cfgData == nil { return nil, ErrNotInitialized } clientSvc, err := getClient( - supportedClients, data.ClientType, data.AspUrl, + supportedClients, cfgData.ClientType, cfgData.AspUrl, ) if err != nil { return nil, fmt.Errorf("failed to setup transport client: %s", err) } - explorerSvc, err := getExplorer(data.ExplorerURL, data.Network.Name) + explorerSvc, err := getExplorer(cfgData.ExplorerURL, cfgData.Network.Name) if err != nil { return nil, fmt.Errorf("failed to setup explorer: %s", err) } - return &covenantlessArkClient{ - &arkClient{data, walletSvc, storeSvc, explorerSvc, clientSvc}, - }, nil + covenantlessClient := covenantlessArkClient{ + &arkClient{ + Config: cfgData, + wallet: walletSvc, + store: sdkStore, + explorer: explorerSvc, + client: clientSvc, + }, + } + + if cfgData.WithTransactionFeed { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + covenantlessClient.txStreamCtxCancel = txStreamCtxCancel + go covenantlessClient.listenForTransactions(txStreamCtx) + go covenantlessClient.listenForBoardingUtxos(txStreamCtx) + } + + return &covenantlessClient, nil +} + +func (a *covenantlessArkClient) Init(ctx context.Context, args InitArgs) error { + if err := a.arkClient.init(ctx, args); err != nil { + return err + } + + if args.ListenTransactionStream { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + a.txStreamCtxCancel = txStreamCtxCancel + go a.listenForTransactions(txStreamCtx) + go a.listenForBoardingUtxos(txStreamCtx) + } + + return nil +} + +func (a *covenantlessArkClient) InitWithWallet(ctx context.Context, args InitWithWalletArgs) error { + if err := a.arkClient.initWithWallet(ctx, args); err != nil { + return err + } + + if a.WithTransactionFeed { + txStreamCtx, txStreamCtxCancel := context.WithCancel(context.Background()) + a.txStreamCtxCancel = txStreamCtxCancel + go a.listenForTransactions(txStreamCtx) + go a.listenForBoardingUtxos(txStreamCtx) + } + + return nil +} + +func (a *covenantlessArkClient) listenForTransactions(ctx context.Context) { + eventChan, closeFunc, err := a.client.GetTransactionsStream(ctx) + if err != nil { + log.WithError(err).Error("Failed to get transaction stream") + return + } + defer closeFunc() + + offchainAddr, _, err := a.wallet.NewAddress(ctx, true) + if err != nil { + log.WithError(err).Error("Failed to get new address") + return + } + + desc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddr) + if err != nil { + log.WithError(err).Error("Failed to get descriptor for new address") + return + } + + for { + select { + case event, ok := <-eventChan: + if !ok { + continue + } + + if event.Err != nil { + log.WithError(event.Err).Error("Error in transaction stream") + continue + } + + pendingBoardingTxsMap, newPendingBoardingTxs, err := a.getBoardingPendingTransactions(ctx) + if err != nil { + log.WithError(err).Error("Failed to get pending transactions") + continue + } + + if err := a.store.TransactionStore(). + AddTransactions(ctx, newPendingBoardingTxs); err != nil { + log.WithError(err).Error("Failed to insert new boarding transactions") + continue + } + + a.processTransactionEvent(desc, event, pendingBoardingTxsMap) + case <-ctx.Done(): + return + } + } +} + +func (a *covenantlessArkClient) listenForBoardingUtxos(ctx context.Context) { + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + _, newPendingBoardingTxs, err := a.getBoardingPendingTransactions(ctx) + if err != nil { + log.WithError(err).Error("Failed to get pending transactions") + continue + } + + if err := a.store.TransactionStore(). + AddTransactions(ctx, newPendingBoardingTxs); err != nil { + log.WithError(err).Error("Failed to insert new boarding transactions") + continue + } + case <-ctx.Done(): + return + } + } +} + +func (a *covenantlessArkClient) getBoardingPendingTransactions( + ctx context.Context, +) (map[string]types.Transaction, []types.Transaction, error) { + oldTxs, err := a.store.TransactionStore().GetAllTransactions(ctx) + if err != nil { + return nil, nil, err + } + + pendingBoardingTxsMap := make(map[string]types.Transaction) + for _, tx := range oldTxs { + if tx.IsBoarding() { + if tx.IsPending { + pendingBoardingTxsMap[tx.BoardingTxid] = tx + } + } + } + + boardingTxs, _, err := a.getBoardingTxs(ctx) + if err != nil { + return nil, nil, err + } + newPendingBoardingTxs := make([]types.Transaction, 0) + for _, tx := range boardingTxs { + if tx.IsBoarding() && tx.IsPending { + if _, ok := pendingBoardingTxsMap[tx.BoardingTxid]; !ok { + newPendingBoardingTxs = append(newPendingBoardingTxs, tx) + pendingBoardingTxsMap[tx.BoardingTxid] = tx + } + } + } + + return pendingBoardingTxsMap, newPendingBoardingTxs, nil +} + +func (a *covenantlessArkClient) processTransactionEvent( + descriptor string, + event client.TransactionEvent, + pendingBoardingTxsMap map[string]types.Transaction, +) { + if event.Round != nil { + boardingAmount := 0 + boardingTxsToUpdate := make([]types.Transaction, 0) + for _, v := range event.Round.ClaimedBoardingUtxos { + boardingKey := fmt.Sprintf("%v-%v", v.Txid, v.VOut) + if tx, ok := pendingBoardingTxsMap[boardingKey]; ok { + boardingAmount += int(tx.Amount) + tx.IsPending = false + boardingTxsToUpdate = append(boardingTxsToUpdate, tx) + } + } + + if err := a.store.TransactionStore(). + UpdateTransactions(context.Background(), boardingTxsToUpdate); err != nil { + log.WithError(err).Error("Failed to update boarding transactions") + } + + spentKeys := make([]types.VtxoKey, 0, len(event.Round.SpentVtxos)) + for _, v := range event.Round.SpentVtxos { + spentKeys = append(spentKeys, types.VtxoKey{ + Txid: v.Txid, + VOut: v.VOut, + }) + } + + vtxos, err := a.store.VtxoStore(). + GetVtxos(context.Background(), spentKeys) + if err != nil { + log.WithError(err).Error("Failed to get spent vtxos") + return + } + + if len(vtxos) > 0 { + vtxosToUpdate := make([]types.Vtxo, 0) + for _, v := range vtxos { + v.Spent = true + v.Pending = false + vtxosToUpdate = append(vtxosToUpdate, v) + } + + if err := a.store.VtxoStore(). + UpdateVtxos(context.Background(), vtxosToUpdate); err != nil { + log.WithError(err).Error("Failed to update spent vtxos") + } + } + + vtxosToInsert := make([]types.Vtxo, 0) + txsToInsert := make([]types.Transaction, 0) + for _, v := range event.Round.SpendableVtxos { + if v.Descriptor == descriptor { + vtxosToInsert = append(vtxosToInsert, types.Vtxo{ + VtxoKey: types.VtxoKey{ + Txid: v.Txid, + VOut: v.VOut, + }, + Amount: v.Amount, + ExpiresAt: v.ExpiresAt, + RedeemTx: event.Round.Txid, + Pending: false, + SpentBy: v.SpentBy, + Spent: false, + }) + + if boardingAmount == int(v.Amount) { + continue + } + + txsToInsert = append(txsToInsert, types.Transaction{ + TransactionKey: types.TransactionKey{ + RoundTxid: event.Round.Txid, + }, + Amount: v.Amount, + Type: types.TxReceived, + CreatedAt: time.Now(), //TODO is this ok? + }) + } + } + + if err := a.store.VtxoStore(). + AddVtxos(context.Background(), vtxosToInsert); err != nil { + log.WithError(err).Error("Failed to insert new vtxos") + return + } + + if err := a.store.TransactionStore(). + AddTransactions(context.Background(), txsToInsert); err != nil { + log.WithError(err).Error("Failed to insert received transaction") + return + } + + } + + if event.Redeem != nil { + vtxosToInsert := make([]types.Vtxo, 0) + spentKeys := make([]types.VtxoKey, 0, len(event.Redeem.SpentVtxos)) + for _, v := range event.Redeem.SpentVtxos { + spentKeys = append(spentKeys, types.VtxoKey{ + Txid: v.Txid, + VOut: v.VOut, + }) + } + + vtxos, err := a.store.VtxoStore(). + GetVtxos(context.Background(), spentKeys) + if err != nil { + log.WithError(err).Error("Failed to get spent vtxos") + return + } + + if len(vtxos) > 0 { + inputAmount := uint64(0) + vtxosToUpdate := make([]types.Vtxo, 0) + for _, v := range vtxos { + v.Spent = true + vtxosToUpdate = append(vtxosToUpdate, v) + inputAmount += v.Amount + } + + if err := a.store.VtxoStore(). + UpdateVtxos(context.Background(), vtxosToUpdate); err != nil { + log.WithError(err).Error("Failed to update spent vtxos") + return + } + + outputAmount := uint64(0) + for _, v := range event.Redeem.SpendableVtxos { + if v.Descriptor == descriptor { + vtxosToInsert = append(vtxosToInsert, types.Vtxo{ + VtxoKey: types.VtxoKey{ + Txid: v.Txid, + VOut: v.VOut, + }, + Amount: v.Amount, + ExpiresAt: v.ExpiresAt, + RedeemTx: event.Redeem.Txid, + Pending: true, + SpentBy: v.SpentBy, + Spent: false, + }) + outputAmount += v.Amount + } + } + + tx := types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: event.Redeem.Txid, + }, + Amount: inputAmount - outputAmount, + Type: types.TxSent, + IsPending: true, + CreatedAt: time.Now(), //TODO is this ok? + } + + if err := a.store.TransactionStore(). + AddTransactions(context.Background(), []types.Transaction{tx}); err != nil { + log.WithError(err).Error("Failed to insert received transaction") + } + } else { + for _, v := range event.Redeem.SpendableVtxos { + if v.Descriptor == descriptor { + vtxosToInsert = append(vtxosToInsert, types.Vtxo{ + VtxoKey: types.VtxoKey{ + Txid: v.Txid, + VOut: v.VOut, + }, + Amount: v.Amount, + ExpiresAt: v.ExpiresAt, + RedeemTx: event.Redeem.Txid, + Pending: true, + SpentBy: v.SpentBy, + Spent: false, + }) + + tx := types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: event.Redeem.Txid, + }, + Amount: v.Amount, + Type: types.TxReceived, + IsPending: true, + CreatedAt: time.Now(), //TODO is this ok? + } + if err := a.store.TransactionStore(). + AddTransactions(context.Background(), []types.Transaction{tx}); err != nil { + log.WithError(err).Error("Failed to insert received transaction") + } + } + } + } + + if err := a.store.VtxoStore(). + AddVtxos(context.Background(), vtxosToInsert); err != nil { + log.WithError(err).Error("Failed to insert new vtxos") + return + } + } } func (a *covenantlessArkClient) ListVtxos( @@ -725,8 +1107,14 @@ func (a *covenantlessArkClient) Claim(ctx context.Context) (string, error) { ) } -func (a *covenantlessArkClient) GetTransactionHistory(ctx context.Context) ([]Transaction, error) { - if a.StoreData == nil { +func (a *covenantlessArkClient) GetTransactionHistory( + ctx context.Context, +) ([]types.Transaction, error) { + if a.Config.WithTransactionFeed { + return a.store.TransactionStore().GetAllTransactions(ctx) + } + + if a.Config == nil { return nil, fmt.Errorf("client not initialized") } @@ -741,7 +1129,7 @@ func (a *covenantlessArkClient) GetTransactionHistory(ctx context.Context) ([]Tr } offchainTxs, err := vtxosToTxsCovenantless( - a.StoreData.RoundLifetime, spendableVtxos, spentVtxos, ignoreVtxos, + a.Config.RoundLifetime, spendableVtxos, spentVtxos, ignoreVtxos, ) if err != nil { return nil, err @@ -1401,7 +1789,7 @@ func (a *covenantlessArkClient) validateCongestionTree( if !utils.IsOnchainOnly(receivers) { if err := bitcointree.ValidateCongestionTree( - event.Tree, poolTx, a.StoreData.AspPubkey, a.RoundLifetime, + event.Tree, poolTx, a.Config.AspPubkey, a.RoundLifetime, ); err != nil { return err } @@ -2061,7 +2449,7 @@ func (a *covenantlessArkClient) offchainAddressToDefaultVtxoDescriptor(addr stri // offchain tx history and prevent duplicates. func (a *covenantlessArkClient) getBoardingTxs( ctx context.Context, -) ([]Transaction, map[string]struct{}, error) { +) ([]types.Transaction, map[string]struct{}, error) { utxos, err := a.getClaimableBoardingUtxos(ctx) if err != nil { return nil, nil, err @@ -2077,20 +2465,22 @@ func (a *covenantlessArkClient) getBoardingTxs( return nil, nil, err } - unconfirmedTxs := make([]Transaction, 0) - confirmedTxs := make([]Transaction, 0) + unconfirmedTxs := make([]types.Transaction, 0) + confirmedTxs := make([]types.Transaction, 0) for _, u := range allUtxos { pending := false if isPending[u.Txid] { pending = true } - tx := Transaction{ - BoardingTxid: u.Txid, - Amount: u.Amount, - Type: TxReceived, - IsPending: pending, - CreatedAt: u.CreatedAt, + tx := types.Transaction{ + TransactionKey: types.TransactionKey{ + BoardingTxid: u.Txid, + }, + Amount: u.Amount, + Type: types.TxReceived, + IsPending: pending, + CreatedAt: u.CreatedAt, } emptyTime := time.Time{} @@ -2116,19 +2506,21 @@ func findVtxosBySpentBy(allVtxos []client.Vtxo, txid string) (vtxos []client.Vtx func vtxosToTxsCovenantless( roundLifetime int64, spendable, spent []client.Vtxo, ignoreVtxos map[string]struct{}, -) ([]Transaction, error) { - transactions := make([]Transaction, 0) +) ([]types.Transaction, error) { + transactions := make([]types.Transaction, 0) - indexedTxs := make(map[string]Transaction) + indexedTxs := make(map[string]types.Transaction) for _, v := range spent { // If the vtxo was pending and is spent => it's been claimed. if v.Pending { - transactions = append(transactions, Transaction{ - RedeemTxid: v.Txid, - Amount: v.Amount, - Type: TxReceived, - IsPending: false, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), + transactions = append(transactions, types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: v.Txid, + }, + Amount: v.Amount, + Type: types.TxReceived, + IsPending: false, + CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), }) // Delete any duplicate in the indexed list. delete(indexedTxs, v.SpentBy) @@ -2151,13 +2543,15 @@ func vtxosToTxsCovenantless( // Add a transaction to the indexed list if not existing, it will be deleted if it's a duplicate. tx, ok := indexedTxs[v.SpentBy] if !ok { - indexedTxs[v.SpentBy] = Transaction{ - RedeemTxid: v.SpentBy, - RoundTxid: v.SpentBy, - Amount: v.Amount, - Type: TxSent, - IsPending: false, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), + indexedTxs[v.SpentBy] = types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: v.SpentBy, + RoundTxid: v.SpentBy, + }, + Amount: v.Amount, + Type: types.TxSent, + IsPending: false, + CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), } continue } @@ -2184,13 +2578,15 @@ func vtxosToTxsCovenantless( if v.RoundTxid == "" { redeemTxid = v.Txid } - transactions = append(transactions, Transaction{ - RedeemTxid: redeemTxid, - RoundTxid: v.RoundTxid, - Amount: v.Amount, - Type: TxReceived, - IsPending: v.Pending, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), + transactions = append(transactions, types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: redeemTxid, + RoundTxid: v.RoundTxid, + }, + Amount: v.Amount, + Type: types.TxReceived, + IsPending: v.Pending, + CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), }) continue } diff --git a/pkg/client-sdk/example/covenant/alice_to_bob.go b/pkg/client-sdk/example/covenant/alice_to_bob.go index 1ef228cf1..99e628f6a 100644 --- a/pkg/client-sdk/example/covenant/alice_to_bob.go +++ b/pkg/client-sdk/example/covenant/alice_to_bob.go @@ -5,12 +5,15 @@ import ( "fmt" "io" "os/exec" + "path" "strings" "sync" "time" + "github.com/ark-network/ark/common" arksdk "github.com/ark-network/ark/pkg/client-sdk" - inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" log "github.com/sirupsen/logrus" ) @@ -26,7 +29,7 @@ func main() { log.Info("alice is setting up her ark wallet...") - aliceArkClient, err := setupArkClient() + aliceArkClient, err := setupArkClient("alice") if err != nil { log.Fatal(err) } @@ -70,7 +73,7 @@ func main() { fmt.Println("") log.Info("bob is setting up his ark wallet...") - bobArkClient, err := setupArkClient() + bobArkClient, err := setupArkClient("bob") if err != nil { log.Fatal(err) } @@ -133,12 +136,18 @@ func main() { log.Infof("bob offchain balance: %d", bobBalance.OffchainBalance.Total) } -func setupArkClient() (arksdk.ArkClient, error) { - storeSvc, err := inmemorystore.NewConfigStore() +func setupArkClient(wallet string) (arksdk.ArkClient, error) { + dbDir := common.AppDataDir(path.Join("ark-example", wallet), false) + appDataStore, err := store.NewStore(store.Config{ + ConfigStoreType: types.FileStore, + AppDataStoreType: types.KVStore, + BaseDir: dbDir, + }) if err != nil { - return nil, fmt.Errorf("failed to setup store: %s", err) + return nil, fmt.Errorf("failed to setup app data store: %s", err) } - client, err := arksdk.NewCovenantClient(storeSvc) + + client, err := arksdk.NewCovenantClient(appDataStore) if err != nil { return nil, fmt.Errorf("failed to setup ark client: %s", err) } diff --git a/pkg/client-sdk/example/covenant/wasm/index.html b/pkg/client-sdk/example/covenant/wasm/index.html index 9a01f2709..c61858203 100644 --- a/pkg/client-sdk/example/covenant/wasm/index.html +++ b/pkg/client-sdk/example/covenant/wasm/index.html @@ -19,10 +19,11 @@ async function initWallet() { try { const chain = "liquid"; - const walletType = "singlekey" - const clientType = "rest" + const walletType = "singlekey"; + const clientType = "rest"; const privateKey = document.getElementById("prvkey").value; const password = document.getElementById("i_password").value; + const explorerUrl = ""; if (!password) { logMessage("Init error: password is required"); return; @@ -32,7 +33,7 @@ logMessage("Init error: asp url is required"); return; } - await init(walletType, clientType, aspUrl, privateKey, password, chain); + await init(walletType, clientType, aspUrl, privateKey, password, chain, explorerUrl); logMessage("wallet initialized and connected to ASP"); await config(); } catch (err) { diff --git a/pkg/client-sdk/example/covenantless/alice_to_bob.go b/pkg/client-sdk/example/covenantless/alice_to_bob.go index 3755da896..2563c1144 100644 --- a/pkg/client-sdk/example/covenantless/alice_to_bob.go +++ b/pkg/client-sdk/example/covenantless/alice_to_bob.go @@ -5,12 +5,15 @@ import ( "fmt" "io" "os/exec" + "path" "strings" "sync" "time" + "github.com/ark-network/ark/common" arksdk "github.com/ark-network/ark/pkg/client-sdk" - inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" log "github.com/sirupsen/logrus" ) @@ -22,15 +25,36 @@ var ( ) func main() { - ctx := context.Background() + var ( + ctx = context.Background() + err error + + aliceArkClient arksdk.ArkClient + bobArkClient arksdk.ArkClient + ) + defer func() { + if aliceArkClient != nil { + if err := bobArkClient.Stop(); err != nil { + log.Error(err) + } + } + + if bobArkClient != nil { + if err := aliceArkClient.Stop(); err != nil { + log.Error(err) + } + } + }() log.Info("alice is setting up her ark wallet...") - aliceArkClient, err := setupArkClient() + aliceArkClient, err = setupArkClient("alice") if err != nil { log.Fatal(err) } + logTxEvents("alice", aliceArkClient) + if err := aliceArkClient.Unlock(ctx, password); err != nil { log.Fatal(err) } @@ -70,11 +94,13 @@ func main() { fmt.Println("") log.Info("bob is setting up his ark wallet...") - bobArkClient, err := setupArkClient() + bobArkClient, err = setupArkClient("bob") if err != nil { log.Fatal(err) } + logTxEvents("bob", bobArkClient) + if err := bobArkClient.Unlock(ctx, password); err != nil { log.Fatal(err) } @@ -139,23 +165,32 @@ func main() { } log.Infof("bob claimed the incoming payment in round %s", roundTxid) + + time.Sleep(500 * time.Second) } -func setupArkClient() (arksdk.ArkClient, error) { - storeSvc, err := inmemorystore.NewConfigStore() +func setupArkClient(wallet string) (arksdk.ArkClient, error) { + dbDir := common.AppDataDir(path.Join("ark-example", wallet), false) + appDataStore, err := store.NewStore(store.Config{ + ConfigStoreType: types.FileStore, + AppDataStoreType: types.KVStore, + BaseDir: dbDir, + }) if err != nil { - return nil, fmt.Errorf("failed to setup store: %s", err) + return nil, fmt.Errorf("failed to setup app data store: %s", err) } - client, err := arksdk.NewCovenantlessClient(storeSvc) + + client, err := arksdk.NewCovenantlessClient(appDataStore) if err != nil { return nil, fmt.Errorf("failed to setup ark client: %s", err) } if err := client.Init(context.Background(), arksdk.InitArgs{ - WalletType: walletType, - ClientType: clientType, - AspUrl: aspUrl, - Password: password, + WalletType: walletType, + ClientType: clientType, + AspUrl: aspUrl, + Password: password, + ListenTransactionStream: true, }); err != nil { return nil, fmt.Errorf("failed to initialize wallet: %s", err) } @@ -233,3 +268,17 @@ func generateBlock() error { time.Sleep(6 * time.Second) return nil } + +func logTxEvents(wallet string, client arksdk.ArkClient) { + txsChan := client.GetTransactionEventChannel() + go func() { + for txEvent := range txsChan { + msg := fmt.Sprintf("[EVENT]%s: tx event: %s, %d", wallet, txEvent.Event, txEvent.Tx.Amount) + if txEvent.Tx.IsBoarding() { + msg += fmt.Sprintf(", boarding tx: %s", txEvent.Tx.BoardingTxid) + } + log.Infoln(msg) + } + }() + log.Infof("%s tx event listener started", wallet) +} diff --git a/pkg/client-sdk/example/covenantless/wasm/index.html b/pkg/client-sdk/example/covenantless/wasm/index.html index e1fb0c5b9..65f71be8c 100644 --- a/pkg/client-sdk/example/covenantless/wasm/index.html +++ b/pkg/client-sdk/example/covenantless/wasm/index.html @@ -19,10 +19,11 @@ async function initWallet() { try { const chain = "bitcoin"; - const walletType = "singlekey" - const clientType = "rest" + const walletType = "singlekey"; + const clientType = "rest"; const privateKey = document.getElementById("prvkey").value; const password = document.getElementById("i_password").value; + const explorerUrl = ""; if (!password) { logMessage("Init error: password is required"); return; @@ -32,7 +33,7 @@ logMessage("Init error: asp url is required"); return; } - await init(walletType, clientType, aspUrl, privateKey, password, chain); + await init(walletType, clientType, aspUrl, privateKey, password, chain, explorerUrl); logMessage("wallet initialized and connected to ASP"); await config(); } catch (err) { @@ -80,7 +81,7 @@ const amount = parseInt(amountStr, 10); await unlock(password); - const txID = await sendOffChain(false, [{ To: address, Amount: amount }]); + const txID = await sendAsync(false, [{ To: address, Amount: amount }]); logMessage("Sent money with tx ID: " + txID); } catch (err) { logMessage("Send error: " + err.message); diff --git a/pkg/client-sdk/go.mod b/pkg/client-sdk/go.mod index 0cfe30cd8..0cf2be7e1 100644 --- a/pkg/client-sdk/go.mod +++ b/pkg/client-sdk/go.mod @@ -11,6 +11,7 @@ require ( github.com/btcsuite/btcd/btcutil/psbt v1.1.9 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 + github.com/dgraph-io/badger/v4 v4.2.0 github.com/go-openapi/errors v0.22.0 github.com/go-openapi/runtime v0.28.0 github.com/go-openapi/strfmt v0.23.0 @@ -19,6 +20,7 @@ require ( github.com/lightningnetwork/lnd v0.18.2-beta github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 + github.com/timshannon/badgerhold/v4 v4.0.3 github.com/vulpemventures/go-elements v0.5.4 golang.org/x/crypto v0.26.0 google.golang.org/grpc v1.65.0 @@ -31,9 +33,12 @@ require ( github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/decred/dcrd/lru v1.1.3 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -42,25 +47,33 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.2.1 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/jessevdk/go-flags v1.6.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jrick/logrotate v1.0.0 // indirect github.com/kkdai/bstream v1.0.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/lightningnetwork/lnd/fn v1.2.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo v1.16.4 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.30.0 // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect diff --git a/pkg/client-sdk/go.sum b/pkg/client-sdk/go.sum index 6a6d428c4..e71942bca 100644 --- a/pkg/client-sdk/go.sum +++ b/pkg/client-sdk/go.sum @@ -1,5 +1,8 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= @@ -31,6 +34,18 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -45,9 +60,24 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3 github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/decred/dcrd/lru v1.1.3 h1:w9EAbvGLyzm6jTjF83UKuqZEiUtJmvRhQDOCEIvSuE0= github.com/decred/dcrd/lru v1.1.3/go.mod h1:Tw0i0pJyiLEx/oZdHLe1Wdv/Y7EGzAX+sYftnmxBR4o= +github.com/dgraph-io/badger/v4 v4.1.0/go.mod h1:P50u28d39ibBRmIJuQC/NSdBOg46HnHw7al2SW5QRHg= +github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= +github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -73,26 +103,58 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v23.5.9+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= +github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= @@ -101,23 +163,36 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lightningnetwork/lnd v0.18.2-beta h1:Qv4xQ2ka05vqzmdkFdISHCHP6CzHoYNVKfD18XPjHsM= github.com/lightningnetwork/lnd v0.18.2-beta/go.mod h1:cGQR1cVEZFZQcCx2VBbDY8xwGjCz+SupSopU1HpjP2I= github.com/lightningnetwork/lnd/fn v1.2.1 h1:pPsVGrwi9QBwdLJzaEGK33wmiVKOxs/zc8H7+MamFf0= github.com/lightningnetwork/lnd/fn v1.2.1/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -125,84 +200,187 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/timshannon/badgerhold/v4 v4.0.3 h1:W6pd2qckoXw2cl8eH0ZCV/9CXNaXvaM26tzFi5Tj+v8= +github.com/timshannon/badgerhold/v4 v4.0.3/go.mod h1:IkZIr0kcZLMdD7YJfW/G6epb6ZXHD/h0XR2BTk/VZg8= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI= github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8= github.com/vulpemventures/go-elements v0.5.4 h1:l94xoa9aYPPWiOB7Pmi08rKYvdk/n/sQIbLkQfEAASc= github.com/vulpemventures/go-elements v0.5.4/go.mod h1:Tvhb+rZWv3lxoI5CdK03J3V+e2QVr/7UAnCYILxFSq4= github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo= github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 h1:V71AcdLZr2p8dC9dbOIMCpqi4EmRl8wUwnJzXXLmbmc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -210,18 +388,28 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/client-sdk/store/file/config_store.go b/pkg/client-sdk/store/file/config_store.go new file mode 100644 index 000000000..bfd2c2d10 --- /dev/null +++ b/pkg/client-sdk/store/file/config_store.go @@ -0,0 +1,142 @@ +package filestore + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + + "github.com/ark-network/ark/pkg/client-sdk/types" +) + +const ( + configStoreFilename = "state.json" +) + +type configStore struct { + filePath string +} + +func NewConfigStore(baseDir string) (types.ConfigStore, error) { + if len(baseDir) <= 0 { + return nil, fmt.Errorf("missing base directory") + } + + datadir := cleanAndExpandPath(baseDir) + if err := makeDirectoryIfNotExists(datadir); err != nil { + return nil, fmt.Errorf("failed to initialize datadir: %s", err) + } + filePath := filepath.Join(datadir, configStoreFilename) + + store := &configStore{filePath} + + if _, err := store.open(); err != nil { + return nil, fmt.Errorf("failed to open store: %s", err) + } + + return store, nil +} + +func (s *configStore) Close() {} + +func (s *configStore) GetType() string { + return "file" +} + +func (s *configStore) GetDatadir() string { + return filepath.Dir(s.filePath) +} + +func (s *configStore) AddData(ctx context.Context, data types.Config) error { + sd := &storeData{ + AspUrl: data.AspUrl, + AspPubkey: hex.EncodeToString(data.AspPubkey.SerializeCompressed()), + WalletType: data.WalletType, + ClientType: data.ClientType, + Network: data.Network.Name, + RoundLifetime: fmt.Sprintf("%d", data.RoundLifetime), + RoundInterval: fmt.Sprintf("%d", data.RoundInterval), + UnilateralExitDelay: fmt.Sprintf("%d", data.UnilateralExitDelay), + Dust: fmt.Sprintf("%d", data.Dust), + BoardingDescriptorTemplate: data.BoardingDescriptorTemplate, + ExplorerURL: data.ExplorerURL, + ForfeitAddress: data.ForfeitAddress, + WithTransactionFeed: strconv.FormatBool(data.WithTransactionFeed), + } + + if err := s.write(sd); err != nil { + return fmt.Errorf("failed to write to store: %s", err) + } + return nil +} + +func (s *configStore) GetData(_ context.Context) (*types.Config, error) { + sd, err := s.open() + if err != nil { + return nil, err + } + if sd.isEmpty() { + return nil, nil + } + + data := sd.decode() + return &data, nil +} + +func (s *configStore) CleanData(ctx context.Context) error { + if err := s.write(&storeData{}); err != nil { + return fmt.Errorf("failed to write to store: %s", err) + } + return nil +} + +func (s *configStore) open() (*storeData, error) { + file, err := os.ReadFile(s.filePath) + if err != nil { + if !os.IsNotExist(err) { + return nil, fmt.Errorf("failed to open store: %s", err) + } + if err := s.write(&storeData{}); err != nil { + return nil, fmt.Errorf("failed to initialize store: %s", err) + } + return nil, nil + } + + data := &storeData{} + if err := json.Unmarshal(file, data); err != nil { + return nil, fmt.Errorf("failed to read file store: %s", err) + } + return data, nil +} + +func (s *configStore) write(data *storeData) error { + file, err := os.ReadFile(s.filePath) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } + currentData := map[string]string{} + if len(file) > 0 { + if err := json.Unmarshal(file, ¤tData); err != nil { + return fmt.Errorf("failed to read file store: %s", err) + } + } + + mergedData := merge(currentData, data.asMap()) + + jsonString, err := json.Marshal(mergedData) + if err != nil { + return err + } + + err = os.WriteFile(s.filePath, jsonString, 0755) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/client-sdk/store/file/store.go b/pkg/client-sdk/store/file/store.go deleted file mode 100644 index 10df500d1..000000000 --- a/pkg/client-sdk/store/file/store.go +++ /dev/null @@ -1,239 +0,0 @@ -package filestore - -import ( - "context" - "encoding/hex" - "encoding/json" - "fmt" - "os" - "os/user" - "path/filepath" - "strconv" - "strings" - - "github.com/ark-network/ark/pkg/client-sdk/internal/utils" - "github.com/ark-network/ark/pkg/client-sdk/store" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -const ( - filename = "state.json" -) - -type storeData struct { - AspUrl string `json:"asp_url"` - AspPubkey string `json:"asp_pubkey"` - WalletType string `json:"wallet_type"` - ClientType string `json:"client_type"` - Network string `json:"network"` - RoundLifetime string `json:"round_lifetime"` - RoundInterval string `json:"round_interval"` - UnilateralExitDelay string `json:"unilateral_exit_delay"` - Dust string `json:"dust"` - BoardingDescriptorTemplate string `json:"boarding_descriptor_template"` - ExplorerURL string `json:"explorer_url"` - ForfeitAddress string `json:"forfeit_address"` -} - -func (d storeData) isEmpty() bool { - return d == storeData{} -} - -func (d storeData) decode() store.StoreData { - network := utils.NetworkFromString(d.Network) - roundLifetime, _ := strconv.Atoi(d.RoundLifetime) - roundInterval, _ := strconv.Atoi(d.RoundInterval) - unilateralExitDelay, _ := strconv.Atoi(d.UnilateralExitDelay) - dust, _ := strconv.Atoi(d.Dust) - buf, _ := hex.DecodeString(d.AspPubkey) - aspPubkey, _ := secp256k1.ParsePubKey(buf) - explorerURL := d.ExplorerURL - return store.StoreData{ - AspUrl: d.AspUrl, - AspPubkey: aspPubkey, - WalletType: d.WalletType, - ClientType: d.ClientType, - Network: network, - RoundLifetime: int64(roundLifetime), - UnilateralExitDelay: int64(unilateralExitDelay), - RoundInterval: int64(roundInterval), - Dust: uint64(dust), - BoardingDescriptorTemplate: d.BoardingDescriptorTemplate, - ExplorerURL: explorerURL, - ForfeitAddress: d.ForfeitAddress, - } -} - -func (d storeData) asMap() map[string]string { - return map[string]string{ - "asp_url": d.AspUrl, - "asp_pubkey": d.AspPubkey, - "wallet_type": d.WalletType, - "client_type": d.ClientType, - "network": d.Network, - "round_lifetime": d.RoundLifetime, - "round_interval": d.RoundInterval, - "unilateral_exit_delay": d.UnilateralExitDelay, - "dust": d.Dust, - "boarding_descriptor_template": d.BoardingDescriptorTemplate, - "explorer_url": d.ExplorerURL, - "forfeit_address": d.ForfeitAddress, - } -} - -type Store struct { - filePath string -} - -func NewConfigStore(baseDir string) (store.ConfigStore, error) { - if len(baseDir) <= 0 { - return nil, fmt.Errorf("missing base directory") - } - datadir := cleanAndExpandPath(baseDir) - if err := makeDirectoryIfNotExists(datadir); err != nil { - return nil, fmt.Errorf("failed to initialize datadir: %s", err) - } - filePath := filepath.Join(datadir, filename) - - fileStore := &Store{filePath} - - if _, err := fileStore.open(); err != nil { - return nil, fmt.Errorf("failed to open store: %s", err) - } - - return fileStore, nil -} - -func (s *Store) GetType() string { - return store.FileStore -} - -func (s *Store) GetDatadir() string { - return filepath.Dir(s.filePath) -} - -func (s *Store) AddData(ctx context.Context, data store.StoreData) error { - sd := &storeData{ - AspUrl: data.AspUrl, - AspPubkey: hex.EncodeToString(data.AspPubkey.SerializeCompressed()), - WalletType: data.WalletType, - ClientType: data.ClientType, - Network: data.Network.Name, - RoundLifetime: fmt.Sprintf("%d", data.RoundLifetime), - RoundInterval: fmt.Sprintf("%d", data.RoundInterval), - UnilateralExitDelay: fmt.Sprintf("%d", data.UnilateralExitDelay), - Dust: fmt.Sprintf("%d", data.Dust), - BoardingDescriptorTemplate: data.BoardingDescriptorTemplate, - ExplorerURL: data.ExplorerURL, - ForfeitAddress: data.ForfeitAddress, - } - - if err := s.write(sd); err != nil { - return fmt.Errorf("failed to write to store: %s", err) - } - return nil -} - -func (s *Store) GetData(_ context.Context) (*store.StoreData, error) { - sd, err := s.open() - if err != nil { - return nil, err - } - if sd.isEmpty() { - return nil, nil - } - - data := sd.decode() - return &data, nil -} - -func (s *Store) CleanData(ctx context.Context) error { - if err := s.write(&storeData{}); err != nil { - return fmt.Errorf("failed to write to store: %s", err) - } - return nil -} - -func (s *Store) open() (*storeData, error) { - file, err := os.ReadFile(s.filePath) - if err != nil { - if !os.IsNotExist(err) { - return nil, fmt.Errorf("failed to open store: %s", err) - } - if err := s.write(&storeData{}); err != nil { - return nil, fmt.Errorf("failed to initialize store: %s", err) - } - return nil, nil - } - - data := &storeData{} - if err := json.Unmarshal(file, data); err != nil { - return nil, fmt.Errorf("failed to read file store: %s", err) - } - return data, nil -} - -func (s *Store) write(data *storeData) error { - file, err := os.ReadFile(s.filePath) - if err != nil { - if !os.IsNotExist(err) { - return err - } - } - currentData := map[string]string{} - if len(file) > 0 { - if err := json.Unmarshal(file, ¤tData); err != nil { - return fmt.Errorf("failed to read file store: %s", err) - } - } - - mergedData := merge(currentData, data.asMap()) - - jsonString, err := json.Marshal(mergedData) - if err != nil { - return err - } - - err = os.WriteFile(s.filePath, jsonString, 0755) - if err != nil { - return err - } - - return nil -} - -func cleanAndExpandPath(path string) string { - // Expand initial ~ to OS specific home directory. - if strings.HasPrefix(path, "~") { - var homeDir string - u, err := user.Current() - if err == nil { - homeDir = u.HomeDir - } else { - homeDir = os.Getenv("HOME") - } - - path = strings.Replace(path, "~", homeDir, 1) - } - - // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, - // but the variables can still be expanded via POSIX-style $VARIABLE. - return filepath.Clean(os.ExpandEnv(path)) -} - -func makeDirectoryIfNotExists(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - return os.MkdirAll(path, os.ModeDir|0755) - } - return nil -} - -func merge(maps ...map[string]string) map[string]string { - merge := make(map[string]string, 0) - for _, m := range maps { - for k, v := range m { - merge[k] = v - } - } - return merge -} diff --git a/pkg/client-sdk/store/file/types.go b/pkg/client-sdk/store/file/types.go new file mode 100644 index 000000000..f1f49c4c0 --- /dev/null +++ b/pkg/client-sdk/store/file/types.go @@ -0,0 +1,80 @@ +package filestore + +import ( + "encoding/hex" + "strconv" + + "github.com/ark-network/ark/pkg/client-sdk/internal/utils" + "github.com/ark-network/ark/pkg/client-sdk/types" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +type storeData struct { + AspUrl string `json:"asp_url"` + AspPubkey string `json:"asp_pubkey"` + WalletType string `json:"wallet_type"` + ClientType string `json:"client_type"` + Network string `json:"network"` + RoundLifetime string `json:"round_lifetime"` + RoundInterval string `json:"round_interval"` + UnilateralExitDelay string `json:"unilateral_exit_delay"` + Dust string `json:"dust"` + BoardingDescriptorTemplate string `json:"boarding_descriptor_template"` + ExplorerURL string `json:"explorer_url"` + ForfeitAddress string `json:"forfeit_address"` + WithTransactionFeed string `json:"with_transaction_feed"` +} + +func (d storeData) isEmpty() bool { + if d.AspUrl == "" && + d.AspPubkey == "" { + return true + } + + return false +} + +func (d storeData) decode() types.Config { + network := utils.NetworkFromString(d.Network) + roundLifetime, _ := strconv.Atoi(d.RoundLifetime) + roundInterval, _ := strconv.Atoi(d.RoundInterval) + unilateralExitDelay, _ := strconv.Atoi(d.UnilateralExitDelay) + withTransactionFeed, _ := strconv.ParseBool(d.WithTransactionFeed) + dust, _ := strconv.Atoi(d.Dust) + buf, _ := hex.DecodeString(d.AspPubkey) + aspPubkey, _ := secp256k1.ParsePubKey(buf) + explorerURL := d.ExplorerURL + return types.Config{ + AspUrl: d.AspUrl, + AspPubkey: aspPubkey, + WalletType: d.WalletType, + ClientType: d.ClientType, + Network: network, + RoundLifetime: int64(roundLifetime), + UnilateralExitDelay: int64(unilateralExitDelay), + RoundInterval: int64(roundInterval), + Dust: uint64(dust), + BoardingDescriptorTemplate: d.BoardingDescriptorTemplate, + ExplorerURL: explorerURL, + ForfeitAddress: d.ForfeitAddress, + WithTransactionFeed: withTransactionFeed, + } +} + +func (d storeData) asMap() map[string]string { + return map[string]string{ + "asp_url": d.AspUrl, + "asp_pubkey": d.AspPubkey, + "wallet_type": d.WalletType, + "client_type": d.ClientType, + "network": d.Network, + "round_lifetime": d.RoundLifetime, + "round_interval": d.RoundInterval, + "unilateral_exit_delay": d.UnilateralExitDelay, + "dust": d.Dust, + "boarding_descriptor_template": d.BoardingDescriptorTemplate, + "explorer_url": d.ExplorerURL, + "forfeit_address": d.ForfeitAddress, + "with_transaction_feed": d.WithTransactionFeed, + } +} diff --git a/pkg/client-sdk/store/file/utils.go b/pkg/client-sdk/store/file/utils.go new file mode 100644 index 000000000..85a86ae91 --- /dev/null +++ b/pkg/client-sdk/store/file/utils.go @@ -0,0 +1,44 @@ +package filestore + +import ( + "os" + "os/user" + "path/filepath" + "strings" +) + +func cleanAndExpandPath(path string) string { + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + var homeDir string + u, err := user.Current() + if err == nil { + homeDir = u.HomeDir + } else { + homeDir = os.Getenv("HOME") + } + + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but the variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} + +func makeDirectoryIfNotExists(path string) error { + if _, err := os.Stat(path); os.IsNotExist(err) { + return os.MkdirAll(path, os.ModeDir|0755) + } + return nil +} + +func merge(maps ...map[string]string) map[string]string { + merge := make(map[string]string, 0) + for _, m := range maps { + for k, v := range m { + merge[k] = v + } + } + return merge +} diff --git a/pkg/client-sdk/store/inmemory/config_store.go b/pkg/client-sdk/store/inmemory/config_store.go new file mode 100644 index 000000000..9ff068234 --- /dev/null +++ b/pkg/client-sdk/store/inmemory/config_store.go @@ -0,0 +1,57 @@ +package inmemorystore + +import ( + "context" + "sync" + + "github.com/ark-network/ark/pkg/client-sdk/types" +) + +type store struct { + data *types.Config + lock *sync.RWMutex +} + +func NewConfigStore() (types.ConfigStore, error) { + lock := &sync.RWMutex{} + return &store{lock: lock}, nil +} + +func (s *store) Close() {} + +func (s *store) GetType() string { + return "inmemory" +} + +func (s *store) GetDatadir() string { + return "" +} + +func (s *store) AddData( + _ context.Context, data types.Config, +) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = &data + return nil +} + +func (s *store) GetData(_ context.Context) (*types.Config, error) { + s.lock.RLock() + defer s.lock.RUnlock() + + if s.data == nil { + return nil, nil + } + + return s.data, nil +} + +func (s *store) CleanData(_ context.Context) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = nil + return nil +} diff --git a/pkg/client-sdk/store/inmemory/store.go b/pkg/client-sdk/store/inmemory/store.go deleted file mode 100644 index c46d8e30b..000000000 --- a/pkg/client-sdk/store/inmemory/store.go +++ /dev/null @@ -1,55 +0,0 @@ -package inmemorystore - -import ( - "context" - "sync" - - "github.com/ark-network/ark/pkg/client-sdk/store" -) - -type Store struct { - data *store.StoreData - lock *sync.RWMutex -} - -func NewConfigStore() (store.ConfigStore, error) { - lock := &sync.RWMutex{} - return &Store{lock: lock}, nil -} - -func (s *Store) GetType() string { - return store.InMemoryStore -} - -func (s *Store) GetDatadir() string { - return "" -} - -func (s *Store) AddData( - _ context.Context, data store.StoreData, -) error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = &data - return nil -} - -func (s *Store) GetData(_ context.Context) (*store.StoreData, error) { - s.lock.RLock() - defer s.lock.RUnlock() - - if s.data == nil { - return nil, nil - } - - return s.data, nil -} - -func (s *Store) CleanData(_ context.Context) error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = nil - return nil -} diff --git a/pkg/client-sdk/store/kv/transaction_repository.go b/pkg/client-sdk/store/kv/transaction_repository.go new file mode 100644 index 000000000..307431839 --- /dev/null +++ b/pkg/client-sdk/store/kv/transaction_repository.go @@ -0,0 +1,133 @@ +package kvstore + +import ( + "context" + "fmt" + "path/filepath" + "sort" + "sync" + + "github.com/ark-network/ark/pkg/client-sdk/types" + "github.com/dgraph-io/badger/v4" + log "github.com/sirupsen/logrus" + "github.com/timshannon/badgerhold/v4" +) + +const ( + transactionStoreDir = "transactions" +) + +type txStore struct { + db *badgerhold.Store + lock *sync.Mutex + eventCh chan types.TransactionEvent +} + +func NewTransactionStore( + dir string, logger badger.Logger, +) (types.TransactionStore, error) { + badgerDb, err := createDB(filepath.Join(dir, transactionStoreDir), logger) + if err != nil { + return nil, fmt.Errorf("failed to open round events store: %s", err) + } + return &txStore{ + db: badgerDb, + lock: &sync.Mutex{}, + eventCh: make(chan types.TransactionEvent), + }, nil +} + +func (s *txStore) AddTransactions( + _ context.Context, txs []types.Transaction, +) error { + for _, tx := range txs { + if err := s.db.Insert(tx.TransactionKey.String(), &tx); err != nil { + return err + } + go func(tx types.Transaction) { + var eventType types.EventType + + if tx.IsOOR() { + switch tx.Type { + case types.TxSent: + eventType = types.OORSent + case types.TxReceived: + eventType = types.OORReceived + } + } + + if tx.IsBoarding() { + eventType = types.BoardingPending + } + + s.sendEvent(types.TransactionEvent{ + Tx: tx, + Event: eventType, + }) + }(tx) + } + return nil +} + +func (s *txStore) UpdateTransactions( + _ context.Context, txs []types.Transaction, +) error { + for _, tx := range txs { + if err := s.db.Upsert(tx.TransactionKey.String(), &tx); err != nil { + return err + } + go func(tx types.Transaction) { + var event types.EventType + + if tx.IsOOR() { + event = types.OORSettled + } + + if tx.IsBoarding() { + event = types.BoardingSettled + } + + s.sendEvent(types.TransactionEvent{ + Tx: tx, + Event: event, + }) + }(tx) + } + return nil +} + +func (s *txStore) GetAllTransactions( + _ context.Context, +) ([]types.Transaction, error) { + var txs []types.Transaction + err := s.db.Find(&txs, nil) + + sort.Slice(txs, func(i, j int) bool { + txi := txs[i] + txj := txs[j] + if txi.CreatedAt.Equal(txj.CreatedAt) { + return txi.Type > txj.Type + } + return txi.CreatedAt.After(txj.CreatedAt) + }) + + return txs, err +} + +func (s *txStore) GetEventChannel() chan types.TransactionEvent { + return s.eventCh +} + +func (s *txStore) Close() { + if err := s.db.Close(); err != nil { + log.Debugf("error on closing transactions db: %s", err) + } + close(s.eventCh) +} + +func (s *txStore) sendEvent(event types.TransactionEvent) { + s.lock.Lock() + defer s.lock.Unlock() + + s.eventCh <- event +} diff --git a/pkg/client-sdk/store/kv/utils.go b/pkg/client-sdk/store/kv/utils.go new file mode 100644 index 000000000..68fae592c --- /dev/null +++ b/pkg/client-sdk/store/kv/utils.go @@ -0,0 +1,47 @@ +package kvstore + +import ( + "time" + + "github.com/dgraph-io/badger/v4" + "github.com/dgraph-io/badger/v4/options" + "github.com/timshannon/badgerhold/v4" +) + +func createDB(dbDir string, logger badger.Logger) (*badgerhold.Store, error) { + isInMemory := len(dbDir) <= 0 + + opts := badger.DefaultOptions(dbDir) + opts.Logger = logger + + if isInMemory { + opts.InMemory = true + } else { + opts.Compression = options.ZSTD + } + + db, err := badgerhold.Open(badgerhold.Options{ + Encoder: badgerhold.DefaultEncode, + Decoder: badgerhold.DefaultDecode, + SequenceBandwith: 100, + Options: opts, + }) + if err != nil { + return nil, err + } + + if !isInMemory { + ticker := time.NewTicker(30 * time.Minute) + + go func() { + for { + <-ticker.C + if err := db.Badger().RunValueLogGC(0.5); err != nil && err != badger.ErrNoRewrite { + logger.Errorf("%s", err) + } + } + }() + } + + return db, nil +} diff --git a/pkg/client-sdk/store/kv/vtxo_repository.go b/pkg/client-sdk/store/kv/vtxo_repository.go new file mode 100644 index 000000000..aa94fc97f --- /dev/null +++ b/pkg/client-sdk/store/kv/vtxo_repository.go @@ -0,0 +1,92 @@ +package kvstore + +import ( + "context" + "errors" + "fmt" + "path/filepath" + + "github.com/ark-network/ark/pkg/client-sdk/types" + "github.com/dgraph-io/badger/v4" + log "github.com/sirupsen/logrus" + "github.com/timshannon/badgerhold/v4" +) + +const ( + vtxoStoreDir = "vtxos" +) + +type vtxoStore struct { + db *badgerhold.Store +} + +func NewVtxoStore(dir string, logger badger.Logger) (types.VtxoStore, error) { + badgerDb, err := createDB(filepath.Join(dir, vtxoStoreDir), logger) + if err != nil { + return nil, fmt.Errorf("failed to open round events store: %s", err) + } + return &vtxoStore{badgerDb}, nil +} + +func (s *vtxoStore) AddVtxos(_ context.Context, vtxos []types.Vtxo) error { + for _, vtxo := range vtxos { + if err := s.db.Insert(vtxo.VtxoKey.String(), &vtxo); err != nil { + return err + } + } + return nil +} + +func (s *vtxoStore) UpdateVtxos(_ context.Context, vtxos []types.Vtxo) error { + for _, vtxo := range vtxos { + if err := s.db.Update(vtxo.VtxoKey.String(), &vtxo); err != nil { + return err + } + } + return nil +} + +func (s *vtxoStore) GetAllVtxos( + _ context.Context, +) (spendable, spent []types.Vtxo, err error) { + var allVtxos []types.Vtxo + err = s.db.Find(&allVtxos, nil) + if err != nil { + return nil, nil, err + } + + for _, vtxo := range allVtxos { + if vtxo.Spent { + spent = append(spent, vtxo) + } else { + spendable = append(spendable, vtxo) + } + } + return +} + +func (s *vtxoStore) GetVtxos( + _ context.Context, keys []types.VtxoKey, +) ([]types.Vtxo, error) { + var vtxos []types.Vtxo + for _, key := range keys { + var vtxo types.Vtxo + err := s.db.Get(key.String(), &vtxo) + if err != nil { + if errors.Is(err, badgerhold.ErrNotFound) { + continue + } + + return nil, err + } + vtxos = append(vtxos, vtxo) + } + + return vtxos, nil +} + +func (s *vtxoStore) Close() { + if err := s.db.Close(); err != nil { + log.Debugf("error on closing db: %s", err) + } +} diff --git a/pkg/client-sdk/store/service.go b/pkg/client-sdk/store/service.go new file mode 100644 index 000000000..2d698b96e --- /dev/null +++ b/pkg/client-sdk/store/service.go @@ -0,0 +1,82 @@ +package store + +import ( + "fmt" + + filestore "github.com/ark-network/ark/pkg/client-sdk/store/file" + inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + kvstore "github.com/ark-network/ark/pkg/client-sdk/store/kv" + "github.com/ark-network/ark/pkg/client-sdk/types" +) + +type service struct { + configStore types.ConfigStore + vtxoStore types.VtxoStore + txStore types.TransactionStore +} + +type Config struct { + ConfigStoreType string + AppDataStoreType string + + BaseDir string +} + +func NewStore(storeConfig Config) (types.Store, error) { + var ( + configStore types.ConfigStore + vtxoStore types.VtxoStore + txStore types.TransactionStore + err error + + dir = storeConfig.BaseDir + ) + + switch storeConfig.ConfigStoreType { + case types.InMemoryStore: + configStore, err = inmemorystore.NewConfigStore() + case types.FileStore: + configStore, err = filestore.NewConfigStore(dir) + default: + err = fmt.Errorf("unknown config store type") + } + if err != nil { + return nil, err + } + + if len(storeConfig.AppDataStoreType) > 0 { + switch storeConfig.AppDataStoreType { + case types.KVStore: + vtxoStore, err = kvstore.NewVtxoStore(dir, nil) + if err != nil { + return nil, err + } + txStore, err = kvstore.NewTransactionStore(dir, nil) + default: + err = fmt.Errorf("unknown appdata store type") + } + if err != nil { + return nil, err + } + } + + return &service{configStore, vtxoStore, txStore}, nil +} + +func (s *service) ConfigStore() types.ConfigStore { + return s.configStore +} + +func (s *service) VtxoStore() types.VtxoStore { + return s.vtxoStore +} + +func (s *service) TransactionStore() types.TransactionStore { + return s.txStore +} + +func (s *service) Close() { + s.configStore.Close() + s.vtxoStore.Close() + s.txStore.Close() +} diff --git a/pkg/client-sdk/store/store_test.go b/pkg/client-sdk/store/service_test.go similarity index 56% rename from pkg/client-sdk/store/store_test.go rename to pkg/client-sdk/store/service_test.go index 45320f692..8be0fd6e0 100644 --- a/pkg/client-sdk/store/store_test.go +++ b/pkg/client-sdk/store/service_test.go @@ -3,21 +3,24 @@ package store_test import ( "context" "testing" + "time" "github.com/ark-network/ark/common" "github.com/ark-network/ark/pkg/client-sdk/client" "github.com/ark-network/ark/pkg/client-sdk/store" - filestore "github.com/ark-network/ark/pkg/client-sdk/store/file" - inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + filedb "github.com/ark-network/ark/pkg/client-sdk/store/file" + inmemorydb "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + sdktypes "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" "github.com/btcsuite/btcd/btcec/v2" + log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" ) func TestStore(t *testing.T) { key, _ := btcec.NewPrivateKey() ctx := context.Background() - testStoreData := store.StoreData{ + testStoreData := sdktypes.Config{ AspUrl: "localhost:7070", AspPubkey: key.PubKey(), WalletType: wallet.SingleKeyWallet, @@ -35,10 +38,10 @@ func TestStore(t *testing.T) { name string }{ { - name: store.InMemoryStore, + name: sdktypes.InMemoryStore, }, { - name: store.FileStore, + name: sdktypes.FileStore, }, } @@ -47,13 +50,13 @@ func TestStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - var storeSvc store.ConfigStore + var storeSvc sdktypes.ConfigStore var err error switch tt.name { - case store.InMemoryStore: - storeSvc, err = inmemorystore.NewConfigStore() - case store.FileStore: - storeSvc, err = filestore.NewConfigStore(t.TempDir()) + case sdktypes.InMemoryStore: + storeSvc, err = inmemorydb.NewConfigStore() + case sdktypes.FileStore: + storeSvc, err = filedb.NewConfigStore(t.TempDir()) } require.NoError(t, err) require.NotNil(t, storeSvc) @@ -91,3 +94,55 @@ func TestStore(t *testing.T) { }) } } + +func TestNewService(t *testing.T) { + ctx := context.Background() + testDir := t.TempDir() + + dbConfig := store.Config{ + ConfigStoreType: sdktypes.FileStore, + AppDataStoreType: sdktypes.KVStore, + BaseDir: testDir, + } + + service, err := store.NewStore(dbConfig) + require.NoError(t, err) + require.NotNil(t, service) + + go func() { + eventCh := service.TransactionStore().GetEventChannel() + for tx := range eventCh { + log.Infof("Tx inserted: %d %v", tx.Tx.Amount, tx.Tx.Type) + } + }() + + txStore := service.TransactionStore() + require.NotNil(t, txStore) + + testTxs := []sdktypes.Transaction{ + { + TransactionKey: sdktypes.TransactionKey{ + RoundTxid: "tx1", + }, + Amount: 1000, + Type: sdktypes.TxSent, + CreatedAt: time.Now(), + }, + { + TransactionKey: sdktypes.TransactionKey{ + RoundTxid: "tx2", + }, + Amount: 2000, + Type: sdktypes.TxReceived, + CreatedAt: time.Now(), + }, + } + err = txStore.AddTransactions(ctx, testTxs) + require.NoError(t, err) + + retrievedTxs, err := txStore.GetAllTransactions(ctx) + require.NoError(t, err) + require.Len(t, retrievedTxs, 2) + + service.Close() +} diff --git a/pkg/client-sdk/store/store.go b/pkg/client-sdk/store/store.go deleted file mode 100644 index 3c0f85cfa..000000000 --- a/pkg/client-sdk/store/store.go +++ /dev/null @@ -1,36 +0,0 @@ -package store - -import ( - "context" - - "github.com/ark-network/ark/common" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -const ( - InMemoryStore = "inmemory" - FileStore = "file" -) - -type StoreData struct { - AspUrl string - AspPubkey *secp256k1.PublicKey - WalletType string - ClientType string - Network common.Network - RoundLifetime int64 - RoundInterval int64 - UnilateralExitDelay int64 - Dust uint64 - BoardingDescriptorTemplate string - ExplorerURL string - ForfeitAddress string -} - -type ConfigStore interface { - GetType() string - GetDatadir() string - AddData(ctx context.Context, data StoreData) error - GetData(ctx context.Context) (*StoreData, error) - CleanData(ctx context.Context) error -} diff --git a/pkg/client-sdk/types.go b/pkg/client-sdk/types.go index bda1a9480..5ddc4cfe7 100644 --- a/pkg/client-sdk/types.go +++ b/pkg/client-sdk/types.go @@ -2,7 +2,6 @@ package arksdk import ( "fmt" - "time" grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc" restclient "github.com/ark-network/ark/pkg/client-sdk/client/rest" @@ -21,12 +20,13 @@ var ( ) type InitArgs struct { - ClientType string - WalletType string - AspUrl string - Seed string - Password string - ExplorerURL string + ClientType string + WalletType string + AspUrl string + Seed string + Password string + ExplorerURL string + ListenTransactionStream bool } func (a InitArgs) validate() error { @@ -60,12 +60,13 @@ func (a InitArgs) validate() error { } type InitWithWalletArgs struct { - ClientType string - Wallet wallet.WalletService - AspUrl string - Seed string - Password string - ExplorerURL string + ClientType string + Wallet wallet.WalletService + AspUrl string + Seed string + Password string + ExplorerURL string + ListenTransactionStream bool } func (a InitWithWalletArgs) validate() error { @@ -122,20 +123,3 @@ type balanceRes struct { offchainBalanceByExpiration map[int64]uint64 err error } - -const ( - TxSent TxType = "sent" - TxReceived TxType = "received" -) - -type TxType string - -type Transaction struct { - BoardingTxid string - RoundTxid string - RedeemTxid string - Amount uint64 - Type TxType - IsPending bool - CreatedAt time.Time -} diff --git a/pkg/client-sdk/types/interfaces.go b/pkg/client-sdk/types/interfaces.go new file mode 100644 index 000000000..8f46ca284 --- /dev/null +++ b/pkg/client-sdk/types/interfaces.go @@ -0,0 +1,35 @@ +package types + +import "context" + +type Store interface { + ConfigStore() ConfigStore + TransactionStore() TransactionStore + VtxoStore() VtxoStore + Close() +} + +type ConfigStore interface { + GetType() string + GetDatadir() string + AddData(ctx context.Context, data Config) error + GetData(ctx context.Context) (*Config, error) + CleanData(ctx context.Context) error + Close() +} + +type TransactionStore interface { + AddTransactions(ctx context.Context, txs []Transaction) error + UpdateTransactions(ctx context.Context, txs []Transaction) error + GetAllTransactions(ctx context.Context) ([]Transaction, error) + GetEventChannel() chan TransactionEvent + Close() +} + +type VtxoStore interface { + AddVtxos(ctx context.Context, vtxos []Vtxo) error + UpdateVtxos(ctx context.Context, vtxos []Vtxo) error + GetAllVtxos(ctx context.Context) (spendable []Vtxo, spent []Vtxo, err error) + GetVtxos(ctx context.Context, keys []VtxoKey) ([]Vtxo, error) + Close() +} diff --git a/pkg/client-sdk/types/types.go b/pkg/client-sdk/types/types.go new file mode 100644 index 000000000..8bb005ea3 --- /dev/null +++ b/pkg/client-sdk/types/types.go @@ -0,0 +1,105 @@ +package types + +import ( + "fmt" + "strconv" + "time" + + "github.com/ark-network/ark/common" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +const ( + InMemoryStore = "inmemory" + FileStore = "file" + KVStore = "kv" +) + +type Config struct { + AspUrl string + AspPubkey *secp256k1.PublicKey + WalletType string + ClientType string + Network common.Network + RoundLifetime int64 + RoundInterval int64 + UnilateralExitDelay int64 + Dust uint64 + BoardingDescriptorTemplate string + ExplorerURL string + ForfeitAddress string + WithTransactionFeed bool +} + +type VtxoKey struct { + Txid string + VOut uint32 +} + +func (v VtxoKey) String() string { + return fmt.Sprintf("%s:%s", v.Txid, strconv.Itoa(int(v.VOut))) +} + +type Vtxo struct { + VtxoKey + Amount uint64 + RoundTxid string + ExpiresAt *time.Time + RedeemTx string + UnconditionalForfeitTxs []string + Pending bool + SpentBy string + Spent bool +} + +const ( + TxSent TxType = "SENT" + TxReceived TxType = "RECEIVED" +) + +type TxType string + +type TransactionKey struct { + BoardingTxid string + RoundTxid string + RedeemTxid string +} + +func (t TransactionKey) String() string { + return fmt.Sprintf("%s%s%s", t.BoardingTxid, t.RoundTxid, t.RedeemTxid) +} + +type Transaction struct { + TransactionKey + Amount uint64 + Type TxType + IsPending bool + CreatedAt time.Time +} + +func (t Transaction) IsRound() bool { + return t.RoundTxid != "" +} + +func (t Transaction) IsBoarding() bool { + return t.BoardingTxid != "" +} + +func (t Transaction) IsOOR() bool { + return t.RedeemTxid != "" +} + +const ( + BoardingPending EventType = "BOARDING_PENDING" + BoardingSettled EventType = "BOARDING_SETTLED" + OORSent EventType = "OOR_SENT" + OORReceived EventType = "OOR_RECEIVED" + OORSettled EventType = "OOR_SETTLED" +) + +type EventType string + +type TransactionEvent struct { + Tx Transaction + Event EventType +} diff --git a/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go b/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go index 825e4dc44..0f0aef8e8 100644 --- a/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go +++ b/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go @@ -11,7 +11,7 @@ import ( "github.com/ark-network/ark/common/bitcointree" "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store" "github.com/btcsuite/btcd/btcec/v2/schnorr" @@ -26,14 +26,18 @@ type bitcoinWallet struct { } func NewBitcoinWallet( - configStore store.ConfigStore, walletStore walletstore.WalletStore, + configStore types.ConfigStore, walletStore walletstore.WalletStore, ) (wallet.WalletService, error) { walletData, err := walletStore.GetWallet() if err != nil { return nil, err } return &bitcoinWallet{ - &singlekeyWallet{configStore, walletStore, nil, walletData}, + &singlekeyWallet{ + configStore: configStore, + walletStore: walletStore, + walletData: walletData, + }, }, nil } diff --git a/pkg/client-sdk/wallet/singlekey/liquid_wallet.go b/pkg/client-sdk/wallet/singlekey/liquid_wallet.go index 4153e498f..316eb5f2a 100644 --- a/pkg/client-sdk/wallet/singlekey/liquid_wallet.go +++ b/pkg/client-sdk/wallet/singlekey/liquid_wallet.go @@ -11,7 +11,7 @@ import ( "github.com/ark-network/ark/common/tree" "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store" "github.com/btcsuite/btcd/btcec/v2/schnorr" @@ -27,14 +27,18 @@ type liquidWallet struct { } func NewLiquidWallet( - configStore store.ConfigStore, walletStore walletstore.WalletStore, + configStore types.ConfigStore, walletStore walletstore.WalletStore, ) (wallet.WalletService, error) { walletData, err := walletStore.GetWallet() if err != nil { return nil, err } return &liquidWallet{ - &singlekeyWallet{configStore, walletStore, nil, walletData}, + &singlekeyWallet{ + configStore: configStore, + walletStore: walletStore, + walletData: walletData, + }, }, nil } diff --git a/pkg/client-sdk/wallet/singlekey/store/store_test.go b/pkg/client-sdk/wallet/singlekey/store/store_test.go index 99eea210f..a903d507b 100644 --- a/pkg/client-sdk/wallet/singlekey/store/store_test.go +++ b/pkg/client-sdk/wallet/singlekey/store/store_test.go @@ -3,7 +3,7 @@ package store_test import ( "testing" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store" filestore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store/file" inmemorystore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store/inmemory" @@ -24,10 +24,10 @@ func TestWalletStore(t *testing.T) { args []interface{} }{ { - name: store.InMemoryStore, + name: types.InMemoryStore, }, { - name: store.FileStore, + name: types.FileStore, }, } @@ -38,7 +38,7 @@ func TestWalletStore(t *testing.T) { var storeSvc walletstore.WalletStore var err error - if tt.name == store.InMemoryStore { + if tt.name == types.InMemoryStore { storeSvc, err = inmemorystore.NewWalletStore() } else { storeSvc, err = filestore.NewWalletStore(t.TempDir()) diff --git a/pkg/client-sdk/wallet/singlekey/wallet.go b/pkg/client-sdk/wallet/singlekey/wallet.go index b959d43a5..04fe9236f 100644 --- a/pkg/client-sdk/wallet/singlekey/wallet.go +++ b/pkg/client-sdk/wallet/singlekey/wallet.go @@ -7,14 +7,14 @@ import ( "fmt" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store" "github.com/decred/dcrd/dcrec/secp256k1/v4" ) type singlekeyWallet struct { - configStore store.ConfigStore + configStore types.ConfigStore walletStore walletstore.WalletStore privateKey *secp256k1.PrivateKey walletData *walletstore.WalletData diff --git a/pkg/client-sdk/wallet/wallet_test.go b/pkg/client-sdk/wallet/wallet_test.go index 07404c5ec..beb40e8a7 100644 --- a/pkg/client-sdk/wallet/wallet_test.go +++ b/pkg/client-sdk/wallet/wallet_test.go @@ -7,8 +7,8 @@ import ( "github.com/ark-network/ark/common" "github.com/ark-network/ark/pkg/client-sdk/client" - "github.com/ark-network/ark/pkg/client-sdk/store" inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + sdktypes "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey" inmemorywalletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store/inmemory" @@ -20,7 +20,7 @@ func TestWallet(t *testing.T) { ctx := context.Background() key, _ := btcec.NewPrivateKey() password := "password" - testStoreData := store.StoreData{ + testStoreData := sdktypes.Config{ AspUrl: "localhost:7070", AspPubkey: key.PubKey(), WalletType: wallet.SingleKeyWallet, diff --git a/pkg/client-sdk/wasm/browser/config_store.go b/pkg/client-sdk/wasm/browser/config_store.go index 35044849c..a35c8cd40 100644 --- a/pkg/client-sdk/wasm/browser/config_store.go +++ b/pkg/client-sdk/wasm/browser/config_store.go @@ -12,7 +12,7 @@ import ( "syscall/js" "github.com/ark-network/ark/pkg/client-sdk/internal/utils" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/decred/dcrd/dcrec/secp256k1/v4" ) @@ -33,26 +33,26 @@ type storeData struct { Dust string `json:"dust"` ForfeitAddress string `json:"forfeit_address"` BoardingDescriptorTemplate string `json:"boarding_descriptor_template"` + WithTransactionFeed string `json:"with_transaction_feed"` } -type localStorageStore struct { +type configStore struct { store js.Value } -func NewLocalStorageStore() (store.ConfigStore, error) { - store := js.Global().Get("localStorage") - return &localStorageStore{store}, nil +func NewConfigStore(store js.Value) types.ConfigStore { + return &configStore{store} } -func (s *localStorageStore) GetType() string { +func (s *configStore) GetType() string { return LocalStorageStore } -func (s *localStorageStore) GetDatadir() string { +func (s *configStore) GetDatadir() string { return "" } -func (s *localStorageStore) AddData(ctx context.Context, data store.StoreData) error { +func (s *configStore) AddData(ctx context.Context, data types.Config) error { sd := &storeData{ AspUrl: data.AspUrl, AspPubkey: hex.EncodeToString(data.AspPubkey.SerializeCompressed()), @@ -70,7 +70,7 @@ func (s *localStorageStore) AddData(ctx context.Context, data store.StoreData) e return s.writeData(sd) } -func (s *localStorageStore) GetData(ctx context.Context) (*store.StoreData, error) { +func (s *configStore) GetData(ctx context.Context) (*types.Config, error) { key := s.store.Call("getItem", "asp_pubkey") if key.IsNull() || key.IsUndefined() { return nil, nil @@ -92,8 +92,9 @@ func (s *localStorageStore) GetData(ctx context.Context) (*store.StoreData, erro roundInterval, _ := strconv.Atoi(s.store.Call("getItem", "round_interval").String()) unilateralExitDelay, _ := strconv.Atoi(s.store.Call("getItem", "unilateral_exit_delay").String()) dust, _ := strconv.Atoi(s.store.Call("getItem", "min_relay_fee").String()) + withTxFeed, _ := strconv.ParseBool(s.store.Call("getItem", "with_transaction_feed").String()) - return &store.StoreData{ + return &types.Config{ AspUrl: s.store.Call("getItem", "asp_url").String(), AspPubkey: aspPubkey, WalletType: s.store.Call("getItem", "wallet_type").String(), @@ -106,17 +107,20 @@ func (s *localStorageStore) GetData(ctx context.Context) (*store.StoreData, erro ExplorerURL: s.store.Call("getItem", "explorer_url").String(), ForfeitAddress: s.store.Call("getItem", "forfeit_address").String(), BoardingDescriptorTemplate: s.store.Call("getItem", "boarding_descriptor_template").String(), + WithTransactionFeed: withTxFeed, }, nil } -func (s *localStorageStore) CleanData(ctx context.Context) error { +func (s *configStore) CleanData(ctx context.Context) error { if err := s.writeData(&storeData{}); err != nil { return fmt.Errorf("failed to write to store: %s", err) } return nil } -func (s *localStorageStore) writeData(data *storeData) error { +func (s *configStore) Close() {} + +func (s *configStore) writeData(data *storeData) error { dataMap := make(map[string]string) buf, err := json.Marshal(data) if err != nil { diff --git a/pkg/client-sdk/wasm/browser/exports.go b/pkg/client-sdk/wasm/browser/exports.go index 348857cd3..25ab37dde 100644 --- a/pkg/client-sdk/wasm/browser/exports.go +++ b/pkg/client-sdk/wasm/browser/exports.go @@ -10,7 +10,7 @@ import ( "syscall/js" arksdk "github.com/ark-network/ark/pkg/client-sdk" - "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" "github.com/ark-network/ark/pkg/client-sdk/wallet" singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey" walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store" @@ -18,7 +18,7 @@ import ( var ( arkSdkClient arksdk.ArkClient - configStore store.ConfigStore + store types.Store ) func init() { @@ -49,11 +49,11 @@ func init() { } func NewCovenantClient( - ctx context.Context, storeSvc store.ConfigStore, + ctx context.Context, storeSvc types.Store, ) error { var err error - data, err := storeSvc.GetData(ctx) + data, err := storeSvc.ConfigStore().GetData(ctx) if err != nil { return err } @@ -64,7 +64,7 @@ func NewCovenantClient( var walletSvc wallet.WalletService switch data.WalletType { case arksdk.SingleKeyWallet: - walletSvc, err = getSingleKeyWallet(storeSvc, data.Network.Name) + walletSvc, err = getSingleKeyWallet(storeSvc.ConfigStore(), data.Network.Name) if err != nil { return err } @@ -78,17 +78,17 @@ func NewCovenantClient( js.Global().Get("console").Call("error", err.Error()) return err } - configStore = storeSvc + store = storeSvc select {} } func NewCovenantlessClient( - ctx context.Context, storeSvc store.ConfigStore, + ctx context.Context, storeSvc types.Store, ) error { var err error - data, err := storeSvc.GetData(ctx) + data, err := storeSvc.ConfigStore().GetData(ctx) if err != nil { return err } @@ -99,7 +99,7 @@ func NewCovenantlessClient( var walletSvc wallet.WalletService switch data.WalletType { case arksdk.SingleKeyWallet: - walletSvc, err = getSingleKeyWallet(storeSvc, data.Network.Name) + walletSvc, err = getSingleKeyWallet(storeSvc.ConfigStore(), data.Network.Name) if err != nil { return err } @@ -113,7 +113,7 @@ func NewCovenantlessClient( js.Global().Get("console").Call("error", err.Error()) return err } - configStore = storeSvc + store = storeSvc select {} } @@ -127,7 +127,7 @@ func getWalletStore(storeType string) (walletstore.WalletStore, error) { } func getSingleKeyWallet( - configStore store.ConfigStore, network string, + configStore types.ConfigStore, network string, ) (wallet.WalletService, error) { walletStore, err := getWalletStore(configStore.GetType()) if err != nil { diff --git a/pkg/client-sdk/wasm/browser/store.go b/pkg/client-sdk/wasm/browser/store.go new file mode 100644 index 000000000..45584883d --- /dev/null +++ b/pkg/client-sdk/wasm/browser/store.go @@ -0,0 +1,34 @@ +//go:build js && wasm +// +build js,wasm + +package browser + +import ( + "syscall/js" + + "github.com/ark-network/ark/pkg/client-sdk/types" +) + +// TODO: support vtxo and transaction stores localstorage impls. +type localStorageStore struct { + configStore types.ConfigStore +} + +func NewLocalStorageStore() types.Store { + configStore := NewConfigStore(js.Global().Get("localStorage")) + return &localStorageStore{configStore} +} + +func (s *localStorageStore) ConfigStore() types.ConfigStore { + return s.configStore +} + +func (s *localStorageStore) VtxoStore() types.VtxoStore { + return nil +} + +func (s *localStorageStore) TransactionStore() types.TransactionStore { + return nil +} + +func (s *localStorageStore) Close() {} diff --git a/pkg/client-sdk/wasm/browser/wrappers.go b/pkg/client-sdk/wasm/browser/wrappers.go index 741f7c51b..822baaaf8 100644 --- a/pkg/client-sdk/wasm/browser/wrappers.go +++ b/pkg/client-sdk/wasm/browser/wrappers.go @@ -35,7 +35,9 @@ func LogWrapper() js.Func { func InitWrapper() js.Func { return JSPromise(func(args []js.Value) (interface{}, error) { - if len(args) != 6 { + // TODO: add another withTransactionFeed args to configure client listen to + // new txs from the server. Requires server to use websockets. + if len(args) != 7 { return nil, errors.New("invalid number of args") } chain := args[5].String() @@ -43,6 +45,7 @@ func InitWrapper() js.Func { return nil, errors.New("invalid chain, select either 'bitcoin' or 'liquid'") } + configStore := store.ConfigStore() var walletSvc wallet.WalletService switch args[0].String() { case arksdk.SingleKeyWallet: @@ -66,11 +69,12 @@ func InitWrapper() js.Func { } err := arkSdkClient.InitWithWallet(context.Background(), arksdk.InitWithWalletArgs{ - ClientType: args[1].String(), - Wallet: walletSvc, - AspUrl: args[2].String(), - Seed: args[3].String(), - Password: args[4].String(), + ClientType: args[1].String(), + Wallet: walletSvc, + AspUrl: args[2].String(), + Seed: args[3].String(), + Password: args[4].String(), + ExplorerURL: args[6].String(), }) // Add this log message diff --git a/pkg/client-sdk/wasm/main.go b/pkg/client-sdk/wasm/main.go index 9feaf27db..9de19911b 100644 --- a/pkg/client-sdk/wasm/main.go +++ b/pkg/client-sdk/wasm/main.go @@ -5,36 +5,19 @@ package main import ( "context" - inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + "github.com/ark-network/ark/pkg/client-sdk/wasm/browser" ) func main() { c := make(chan struct{}, 0) - var ( - ctx = context.Background() - ) - store, err := browser.NewLocalStorageStore() - if err != nil { + ctx := context.Background() + storeSvc := browser.NewLocalStorageStore() + + if err := browser.NewCovenantlessClient(ctx, storeSvc); err != nil { browser.ConsoleError(err) return } - if store != nil { - if err := browser.NewCovenantlessClient(ctx, store); err != nil { - browser.ConsoleError(err) - return - } - } else { - storeSvc, err := inmemorystore.NewConfigStore() - if err != nil { - browser.ConsoleError(err) - return - } - if err := browser.NewCovenantlessClient(ctx, storeSvc); err != nil { - browser.ConsoleError(err) - return - } - } println("ARK SDK WebAssembly module initialized") <-c } diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index 432c5a548..a0a736b2a 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -1050,7 +1050,8 @@ func (s *covenantlessService) finalizeRound() { log.Debugf("signing round transaction %s\n", round.Id) - boardingInputs := make([]int, 0) + boardingInputsIndexes := make([]int, 0) + boardingInputs := make([]domain.VtxoKey, 0) roundTx, err := psbt.NewFromRawBytes(strings.NewReader(round.UnsignedTx), true) if err != nil { changes = round.Fail(fmt.Errorf("failed to parse round tx: %s", err)) @@ -1067,14 +1068,18 @@ func (s *covenantlessService) finalizeRound() { return } - boardingInputs = append(boardingInputs, i) + boardingInputsIndexes = append(boardingInputsIndexes, i) + boardingInputs = append(boardingInputs, domain.VtxoKey{ + Txid: roundTx.UnsignedTx.TxIn[i].PreviousOutPoint.Hash.String(), + VOut: roundTx.UnsignedTx.TxIn[i].PreviousOutPoint.Index, + }) } } signedRoundTx := round.UnsignedTx - if len(boardingInputs) > 0 { - signedRoundTx, err = s.wallet.SignTransactionTapscript(ctx, signedRoundTx, boardingInputs) + if len(boardingInputsIndexes) > 0 { + signedRoundTx, err = s.wallet.SignTransactionTapscript(ctx, signedRoundTx, boardingInputsIndexes) if err != nil { changes = round.Fail(fmt.Errorf("failed to sign round tx: %s", err)) log.WithError(err).Warn("failed to sign round tx") @@ -1102,6 +1107,15 @@ func (s *covenantlessService) finalizeRound() { return } + go func() { + s.transactionEventsCh <- RoundTransactionEvent{ + RoundTxID: round.Txid, + SpentVtxos: getSpentVtxos(round.Payments), + SpendableVtxos: s.getNewVtxos(round), + ClaimedBoardingInputs: boardingInputs, + } + }() + log.Debugf("finalized round %s with pool tx %s", round.Id, round.Txid) } @@ -1270,25 +1284,6 @@ func (s *covenantlessService) updateVtxoSet(round *domain.Round) { }() } - go func() { - // nolint:all - tx, _ := psbt.NewFromRawBytes(strings.NewReader(round.UnsignedTx), true) - boardingInputs := make([]domain.VtxoKey, 0) - for i, in := range tx.Inputs { - if len(in.TaprootLeafScript) > 0 { - boardingInputs = append(boardingInputs, domain.VtxoKey{ - Txid: tx.UnsignedTx.TxIn[i].PreviousOutPoint.Hash.String(), - VOut: tx.UnsignedTx.TxIn[i].PreviousOutPoint.Index, - }) - } - } - s.transactionEventsCh <- RoundTransactionEvent{ - RoundTxID: round.Txid, - SpentVtxos: getSpentVtxos(round.Payments), - SpendableVtxos: s.getNewVtxos(round), - ClaimedBoardingInputs: boardingInputs, - } - }() } func (s *covenantlessService) propagateEvents(round *domain.Round) { diff --git a/server/test/e2e/covenant/e2e_test.go b/server/test/e2e/covenant/e2e_test.go index ea74ec682..2fce392ff 100644 --- a/server/test/e2e/covenant/e2e_test.go +++ b/server/test/e2e/covenant/e2e_test.go @@ -16,7 +16,8 @@ import ( grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc" "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/redemption" - inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" utils "github.com/ark-network/ark/server/test/e2e" "github.com/stretchr/testify/require" ) @@ -320,10 +321,14 @@ func setupAspWallet() error { } func setupArkSDK(t *testing.T) (arksdk.ArkClient, client.ASPClient) { - storeSvc, err := inmemorystore.NewConfigStore() + appDataStore, err := store.NewStore(store.Config{ + ConfigStoreType: types.FileStore, + AppDataStoreType: types.KVStore, + BaseDir: t.TempDir(), + }) require.NoError(t, err) - client, err := arksdk.NewCovenantClient(storeSvc) + client, err := arksdk.NewCovenantClient(appDataStore) require.NoError(t, err) err = client.Init(context.Background(), arksdk.InitArgs{ diff --git a/server/test/e2e/covenantless/e2e_test.go b/server/test/e2e/covenantless/e2e_test.go index f83954a37..d696319a1 100644 --- a/server/test/e2e/covenantless/e2e_test.go +++ b/server/test/e2e/covenantless/e2e_test.go @@ -16,7 +16,8 @@ import ( grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc" "github.com/ark-network/ark/pkg/client-sdk/explorer" "github.com/ark-network/ark/pkg/client-sdk/redemption" - inmemorystore "github.com/ark-network/ark/pkg/client-sdk/store/inmemory" + "github.com/ark-network/ark/pkg/client-sdk/store" + "github.com/ark-network/ark/pkg/client-sdk/types" utils "github.com/ark-network/ark/server/test/e2e" "github.com/stretchr/testify/require" ) @@ -505,10 +506,14 @@ func setupAspWallet() error { } func setupArkSDK(t *testing.T) (arksdk.ArkClient, client.ASPClient) { - storeSvc, err := inmemorystore.NewConfigStore() + appDataStore, err := store.NewStore(store.Config{ + ConfigStoreType: types.FileStore, + AppDataStoreType: types.KVStore, + BaseDir: t.TempDir(), + }) require.NoError(t, err) - client, err := arksdk.NewCovenantlessClient(storeSvc) + client, err := arksdk.NewCovenantlessClient(appDataStore) require.NoError(t, err) err = client.Init(context.Background(), arksdk.InitArgs{