Vars and fields renaming (#387)

* Rename asp > server

* Rename pool > round

* Consolidate naming for pubkey/prvkey vars and types

* Fix

* Fix

* Fix wasm

* Rename congestionTree > vtxoTree

* Fix wasm

* Rename payment > request

* Rename congestionTree > vtxoTree after syncing with master

* Fix Send API in SDK

* Fix wasm

* Fix wasm

* Fixes

* Fixes after review

* Fix

* Fix naming

* Fix

* Fix e2e tests
This commit is contained in:
Pietralberto Mazza
2024-11-26 15:57:16 +01:00
committed by GitHub
parent 12d666bfdf
commit 7f937e8418
109 changed files with 2292 additions and 2325 deletions

View File

@@ -32,10 +32,10 @@ For a quick-start with Docker, head over to our [Quick Start guide](https://arkd
## Repository Structure ## Repository Structure
- [`api-spec`](./api-spec/): Ark Protocol Buffer API specification - [`api-spec`](./api-spec/): Ark Protocol Buffer API specification
- [`client`](./client/): `ark` Single-key wallet CLI for interacting with the ASP - [`client`](./client/): `ark` Single-key wallet CLI for interacting with the server
- [`common`](./common/): Shared code between the server and client - [`common`](./common/): Shared code between the server and client
- [`pkg/client-sdk`](./pkg/client-sdk/): Go SDK for interacting with ASPs running the Ark protocol. It offers WASM bindings to interact with the SDK from the browser and other environments. - [`pkg/client-sdk`](./pkg/client-sdk/): Go SDK for interacting with servers running the Ark protocol. It offers WASM bindings to interact with the SDK from the browser and other environments.
- [`server`](./server/): `arkd` Ark Service Provider (ASP) - the always-on daemon - [`server`](./server/): `arkd` Ark server - the always-on daemon
## Development ## Development

View File

@@ -163,7 +163,7 @@
] ]
} }
}, },
"/v1/round/ping/{paymentId}": { "/v1/round/ping/{requestId}": {
"get": { "get": {
"operationId": "ArkService_Ping", "operationId": "ArkService_Ping",
"responses": { "responses": {
@@ -182,7 +182,8 @@
}, },
"parameters": [ "parameters": [
{ {
"name": "paymentId", "name": "requestId",
"description": "The id used to register inputs and ouptuts.",
"in": "path", "in": "path",
"required": true, "required": true,
"type": "string" "type": "string"
@@ -770,7 +771,7 @@
"title": "VTXO outpoint signed with script's secret key" "title": "VTXO outpoint signed with script's secret key"
} }
}, },
"description": "This message is used to prove to the ASP that the user controls the vtxo without revealing the whole VTXO taproot tree." "description": "This message is used to prove to the server that the user controls the vtxo without revealing the whole VTXO taproot tree."
}, },
"v1PingResponse": { "v1PingResponse": {
"type": "object" "type": "object"
@@ -821,18 +822,16 @@
"v1RegisterInputsForNextRoundResponse": { "v1RegisterInputsForNextRoundResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "requestId": {
"type": "string", "type": "string"
"description": "Mocks wabisabi's blinded credentials."
} }
} }
}, },
"v1RegisterOutputsForNextRoundRequest": { "v1RegisterOutputsForNextRoundRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "requestId": {
"type": "string", "type": "string"
"description": "Mocks wabisabi's blinded credentials."
}, },
"outputs": { "outputs": {
"type": "array", "type": "array",
@@ -840,7 +839,7 @@
"type": "object", "type": "object",
"$ref": "#/definitions/v1Output" "$ref": "#/definitions/v1Output"
}, },
"description": "List of receivers for a registered payment." "description": "List of receivers for to convert to leaves in the next VTXO tree."
} }
} }
}, },
@@ -1058,7 +1057,7 @@
}, },
"signedRoundTx": { "signedRoundTx": {
"type": "string", "type": "string",
"description": "If payment has boarding input, the user must sign the associated inputs." "description": "The user has to sign also the round tx if he registerd a boarding UTXO."
} }
} }
}, },

View File

@@ -19,7 +19,7 @@ service ArkService {
}; };
}; };
/* In-Round Payment APIs */ /* In-Round Transaction APIs */
rpc RegisterInputsForNextRound(RegisterInputsForNextRoundRequest) returns (RegisterInputsForNextRoundResponse) { rpc RegisterInputsForNextRound(RegisterInputsForNextRoundRequest) returns (RegisterInputsForNextRoundResponse) {
option (google.api.http) = { option (google.api.http) = {
@@ -58,10 +58,12 @@ service ArkService {
}; };
rpc Ping(PingRequest) returns (PingResponse) { rpc Ping(PingRequest) returns (PingResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/v1/round/ping/{payment_id}" get: "/v1/round/ping/{request_id}"
}; };
}; };
/* Out-of-Round Transaction APIs */
rpc SubmitRedeemTx(SubmitRedeemTxRequest) returns (SubmitRedeemTxResponse) { rpc SubmitRedeemTx(SubmitRedeemTxRequest) returns (SubmitRedeemTxResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/v1/redeem-tx" post: "/v1/redeem-tx"
@@ -134,7 +136,7 @@ message GetBoardingAddressResponse {
} }
} }
/* In-Round Payment API messages */ /* In-Round Transaction API messages */
message RegisterInputsForNextRoundRequest { message RegisterInputsForNextRoundRequest {
repeated Input inputs = 1; repeated Input inputs = 1;
@@ -142,14 +144,12 @@ message RegisterInputsForNextRoundRequest {
repeated string notes = 3; repeated string notes = 3;
} }
message RegisterInputsForNextRoundResponse { message RegisterInputsForNextRoundResponse {
// Mocks wabisabi's blinded credentials. string request_id = 1;
string id = 1;
} }
message RegisterOutputsForNextRoundRequest { message RegisterOutputsForNextRoundRequest {
// Mocks wabisabi's blinded credentials. string request_id = 1;
string id = 1; // List of receivers for to convert to leaves in the next VTXO tree.
// List of receivers for a registered payment.
repeated Output outputs = 2; repeated Output outputs = 2;
} }
message RegisterOutputsForNextRoundResponse {} message RegisterOutputsForNextRoundResponse {}
@@ -171,7 +171,7 @@ message SubmitTreeSignaturesResponse {}
message SubmitSignedForfeitTxsRequest { message SubmitSignedForfeitTxsRequest {
// Forfeit txs signed by the user. // Forfeit txs signed by the user.
repeated string signed_forfeit_txs = 1; repeated string signed_forfeit_txs = 1;
// If payment has boarding input, the user must sign the associated inputs. // The user has to sign also the round tx if he registerd a boarding UTXO.
optional string signed_round_tx = 2; optional string signed_round_tx = 2;
} }
message SubmitSignedForfeitTxsResponse {} message SubmitSignedForfeitTxsResponse {}
@@ -188,7 +188,8 @@ message GetEventStreamResponse {
} }
message PingRequest { message PingRequest {
string payment_id = 1; // The id used to register inputs and ouptuts.
string request_id = 1;
} }
message PingResponse {} message PingResponse {}
@@ -343,7 +344,7 @@ message RedeemTransaction {
repeated Vtxo spendable_vtxos = 3; repeated Vtxo spendable_vtxos = 3;
} }
// This message is used to prove to the ASP that the user controls the vtxo without revealing the whole VTXO taproot tree. // This message is used to prove to the server that the user controls the vtxo without revealing the whole VTXO taproot tree.
message OwnershipProof { message OwnershipProof {
string control_block = 1; string control_block = 1;
string script = 2; string script = 2;

View File

@@ -439,8 +439,7 @@ type RegisterInputsForNextRoundResponse struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// Mocks wabisabi's blinded credentials. RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
} }
func (x *RegisterInputsForNextRoundResponse) Reset() { func (x *RegisterInputsForNextRoundResponse) Reset() {
@@ -475,9 +474,9 @@ func (*RegisterInputsForNextRoundResponse) Descriptor() ([]byte, []int) {
return file_ark_v1_service_proto_rawDescGZIP(), []int{5} return file_ark_v1_service_proto_rawDescGZIP(), []int{5}
} }
func (x *RegisterInputsForNextRoundResponse) GetId() string { func (x *RegisterInputsForNextRoundResponse) GetRequestId() string {
if x != nil { if x != nil {
return x.Id return x.RequestId
} }
return "" return ""
} }
@@ -487,9 +486,8 @@ type RegisterOutputsForNextRoundRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// Mocks wabisabi's blinded credentials. RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // List of receivers for to convert to leaves in the next VTXO tree.
// List of receivers for a registered payment.
Outputs []*Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"` Outputs []*Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"`
} }
@@ -525,9 +523,9 @@ func (*RegisterOutputsForNextRoundRequest) Descriptor() ([]byte, []int) {
return file_ark_v1_service_proto_rawDescGZIP(), []int{6} return file_ark_v1_service_proto_rawDescGZIP(), []int{6}
} }
func (x *RegisterOutputsForNextRoundRequest) GetId() string { func (x *RegisterOutputsForNextRoundRequest) GetRequestId() string {
if x != nil { if x != nil {
return x.Id return x.RequestId
} }
return "" return ""
} }
@@ -786,7 +784,7 @@ type SubmitSignedForfeitTxsRequest struct {
// Forfeit txs signed by the user. // Forfeit txs signed by the user.
SignedForfeitTxs []string `protobuf:"bytes,1,rep,name=signed_forfeit_txs,json=signedForfeitTxs,proto3" json:"signed_forfeit_txs,omitempty"` SignedForfeitTxs []string `protobuf:"bytes,1,rep,name=signed_forfeit_txs,json=signedForfeitTxs,proto3" json:"signed_forfeit_txs,omitempty"`
// If payment has boarding input, the user must sign the associated inputs. // The user has to sign also the round tx if he registerd a boarding UTXO.
SignedRoundTx *string `protobuf:"bytes,2,opt,name=signed_round_tx,json=signedRoundTx,proto3,oneof" json:"signed_round_tx,omitempty"` SignedRoundTx *string `protobuf:"bytes,2,opt,name=signed_round_tx,json=signedRoundTx,proto3,oneof" json:"signed_round_tx,omitempty"`
} }
@@ -1040,7 +1038,8 @@ type PingRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
PaymentId string `protobuf:"bytes,1,opt,name=payment_id,json=paymentId,proto3" json:"payment_id,omitempty"` // The id used to register inputs and ouptuts.
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
} }
func (x *PingRequest) Reset() { func (x *PingRequest) Reset() {
@@ -1075,9 +1074,9 @@ func (*PingRequest) Descriptor() ([]byte, []int) {
return file_ark_v1_service_proto_rawDescGZIP(), []int{16} return file_ark_v1_service_proto_rawDescGZIP(), []int{16}
} }
func (x *PingRequest) GetPaymentId() string { func (x *PingRequest) GetRequestId() string {
if x != nil { if x != nil {
return x.PaymentId return x.RequestId
} }
return "" return ""
} }
@@ -2658,7 +2657,7 @@ func (x *RedeemTransaction) GetSpendableVtxos() []*Vtxo {
return nil return nil
} }
// This message is used to prove to the ASP that the user controls the vtxo without revealing the whole VTXO taproot tree. // This message is used to prove to the server that the user controls the vtxo without revealing the whole VTXO taproot tree.
type OwnershipProof struct { type OwnershipProof struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -3136,433 +3135,435 @@ var file_ark_v1_service_proto_rawDesc = []byte{
0x6c, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6c, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f,
0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73,
0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x70, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x70,
0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x34, 0x0a, 0x22, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x43, 0x0a, 0x22, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x22, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x6d, 0x0a, 0x22, 0x52, 0x65,
0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72,
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x64, 0x12, 0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12,
0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x23, 0x52, 0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x23, 0x52, 0x65, 0x67,
0x73, 0x65, 0x22, 0x6d, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e,
0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x08, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x22, 0x6d, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f,
0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x72,
0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72,
0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1f,
0x73, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20,
0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x79, 0x0a, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22,
0x1b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x1a, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e,
0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x79, 0x0a, 0x1b, 0x53,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f,
0x27, 0x0a, 0x0f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18,
0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x65, 0x65, 0x53, 0x69, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a,
0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x6d, 0x0f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73,
0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74,
0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65,
0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x69, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x6d, 0x69,
0x67, 0x6e, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78,
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e,
0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x01,
0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66,
0x09, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
0x54, 0x78, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x22, 0x20, 0x0a, 0x1e, 0x53, 0x75, 0x62, 0x00, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78,
0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72,
0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x22, 0x20, 0x0a, 0x1e, 0x53, 0x75, 0x62, 0x6d, 0x69,
0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78,
0x75, 0x65, 0x73, 0x74, 0x22, 0xa7, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x74,
0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x4f, 0x0a, 0x12, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x72,
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69,
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x46, 0x0a, 0x0f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69,
0x7a, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x46,
0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x6e,
0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, 0x69,
0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, 0x69, 0x6c,
0x65, 0x64, 0x12, 0x40, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e,
0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45,
0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67,
0x6e, 0x69, 0x6e, 0x67, 0x12, 0x6f, 0x0a, 0x1e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x69,
0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x5f, 0x67, 0x65, 0x6e,
0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 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, 0x48, 0x00, 0x52, 0x1b, 0x72, 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, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x2c,
0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
0x0a, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x0e, 0x0a, 0x0c,
0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x0a, 0x15,
0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 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, 0x22, 0x42, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x64, 0x65,
0x65, 0x6d, 0x54, 0x78, 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, 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, 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, 0x9d, 0x01, 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, 0x20, 0x0a,
0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12,
0x34, 0x0a, 0x0a, 0x74, 0x61, 0x70, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x70,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x61, 0x70, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74,
0x5f, 0x74, 0x72, 0x65, 0x65, 0x22, 0x3a, 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, 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, 0xc2, 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, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65,
0x6e, 0x74, 0x18, 0x02, 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, 0x03, 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, 0x04, 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, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x78,
0x70, 0x69, 0x72, 0x65, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x18,
0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a,
0x69, 0x73, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08,
0x52, 0x09, 0x69, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72,
0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x08, 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, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 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, 0x22, 0x6b, 0x0a, 0x0e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68,
0x69, 0x70, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x0a, 0x06,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
0x72, 0x65, 0x22, 0x70, 0x0a, 0x12, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f,
0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 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, 0x2c, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f,
0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70,
0x72, 0x6f, 0x6f, 0x66, 0x22, 0x75, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x73, 0x74, 0x72,
0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69,
0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x73, 0x74, 0x72,
0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x74, 0x78,
0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x4f, 0x75, 0x74, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x53,
0x65, 0x74, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e,
0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x52, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x54, 0x61, 0x70,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x73, 0x22, 0x85, 0x02, 0x0a, 0x0a, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x48, 0x6f, 0x75, 0x72,
0x12, 0x42, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74,
0x54, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x65, 0x6e, 0x64,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x64,
0x54, 0x69, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x6e, 0x64,
0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x6e,
0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 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, 0xf0, 0x0e, 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, 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, 0x65, 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, 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, 0x73, 0x74, 0x22, 0xa7, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53,
0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a,
0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x12, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x61,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x72, 0x6f, 0x75,
0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46,
0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x0a, 0x0f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65,
0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x12, 0x69, 0x0a, 0x0e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45,
0x54, 0x78, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e,
0x69, 0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f,
0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61,
0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65,
0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x76, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64,
0x31, 0x2f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x2d, 0x74, 0x78, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x12, 0x40, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e,
0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65,
0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69,
0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x6e, 0x67, 0x12, 0x6f, 0x0a, 0x1e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e,
0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72,
0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x72, 0x6b,
0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67,
0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x45,
0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x1b, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67,
0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61,
0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x2c, 0x0a, 0x0b,
0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72,
0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x69,
0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x0a, 0x15, 0x53, 0x75,
0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75,
0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78,
0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78,
0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x22, 0x42, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d,
0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x69,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x01,
0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x64, 0x65,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x65, 0x6d, 0x54, 0x78, 0x22, 0x25, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64,
0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18,
0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x10, 0x47,
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x12, 0x73, 0x0a, 0x11, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x53, 0x65, 0x74, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d,
0x74, 0x12, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x05, 0x72,
0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x25, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64,
0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 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, 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, 0x9d, 0x01, 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, 0x20, 0x0a, 0x0a, 0x64,
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
0x00, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x34, 0x0a,
0x0a, 0x74, 0x61, 0x70, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x12, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x70, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x61, 0x70, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x74,
0x72, 0x65, 0x65, 0x22, 0x3a, 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, 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, 0xc2, 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, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x74,
0x18, 0x02, 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, 0x03, 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, 0x04, 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, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x78, 0x70, 0x69,
0x72, 0x65, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x18, 0x06, 0x20,
0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73,
0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
0x69, 0x73, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x64,
0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x08, 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, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16,
0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x41, 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, 0x22, 0x6b, 0x0a, 0x0e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70,
0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
0x22, 0x70, 0x0a, 0x12, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x4f, 0x75,
0x74, 0x70, 0x6f, 0x69, 0x6e, 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, 0x2c, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x77, 0x6e,
0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f,
0x6f, 0x66, 0x22, 0x75, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65,
0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27,
0x0a, 0x0f, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65,
0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e,
0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x52, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x53, 0x65, 0x74,
0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x2f, 0x6e, 0x6f, 0x73, 0x74, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65,
0x72, 0x12, 0x83, 0x01, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01,
0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69,
0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x52, 0x05, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52,
0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x54, 0x61, 0x70, 0x73, 0x63,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73,
0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x2f, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x22,
0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x85, 0x02, 0x0a, 0x0a, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x48, 0x6f, 0x75, 0x72, 0x12, 0x42,
0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d,
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74,
0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x64, 0x54, 0x69,
0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01,
0x6f, 0x74, 0x6f, 0x33, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x70,
0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49,
0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 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, 0xf0, 0x0e, 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, 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,
0x65, 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, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 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, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x69,
0x0a, 0x0e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78,
0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74,
0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52,
0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f,
0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x2d, 0x74, 0x78, 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, 0x12, 0x73, 0x0a, 0x11, 0x53, 0x65,
0x74, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12,
0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x73, 0x74,
0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f,
0x73, 0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22,
0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x2f, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x12,
0x83, 0x01, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52,
0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x73, 0x74, 0x72, 0x52, 0x65, 0x63,
0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e,
0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x73,
0x74, 0x72, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15,
0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x2f, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x2f, 0x64,
0x65, 0x6c, 0x65, 0x74, 0x65, 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 ( var (

View File

@@ -233,14 +233,14 @@ func request_ArkService_Ping_0(ctx context.Context, marshaler runtime.Marshaler,
_ = err _ = err
) )
val, ok = pathParams["payment_id"] val, ok = pathParams["request_id"]
if !ok { if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "payment_id") return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "request_id")
} }
protoReq.PaymentId, err = runtime.String(val) protoReq.RequestId, err = runtime.String(val)
if err != nil { if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "payment_id", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "request_id", err)
} }
msg, err := client.Ping(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) msg, err := client.Ping(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
@@ -259,14 +259,14 @@ func local_request_ArkService_Ping_0(ctx context.Context, marshaler runtime.Mars
_ = err _ = err
) )
val, ok = pathParams["payment_id"] val, ok = pathParams["request_id"]
if !ok { if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "payment_id") return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "request_id")
} }
protoReq.PaymentId, err = runtime.String(val) protoReq.RequestId, err = runtime.String(val)
if err != nil { if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "payment_id", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "request_id", err)
} }
msg, err := server.Ping(ctx, &protoReq) msg, err := server.Ping(ctx, &protoReq)
@@ -722,7 +722,7 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error var err error
var annotatedContext context.Context var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/Ping", runtime.WithHTTPPathPattern("/v1/round/ping/{payment_id}")) annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/Ping", runtime.WithHTTPPathPattern("/v1/round/ping/{request_id}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@@ -1119,7 +1119,7 @@ func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux,
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error var err error
var annotatedContext context.Context var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/Ping", runtime.WithHTTPPathPattern("/v1/round/ping/{payment_id}")) annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/Ping", runtime.WithHTTPPathPattern("/v1/round/ping/{request_id}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@@ -1309,7 +1309,7 @@ var (
pattern_ArkService_GetEventStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "events"}, "")) pattern_ArkService_GetEventStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "events"}, ""))
pattern_ArkService_Ping_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "round", "ping", "payment_id"}, "")) pattern_ArkService_Ping_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "round", "ping", "request_id"}, ""))
pattern_ArkService_SubmitRedeemTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "redeem-tx"}, "")) pattern_ArkService_SubmitRedeemTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "redeem-tx"}, ""))

View File

@@ -97,8 +97,8 @@ var (
Usage: "optional private key to encrypt", Usage: "optional private key to encrypt",
} }
urlFlag = &cli.StringFlag{ urlFlag = &cli.StringFlag{
Name: "asp-url", Name: "server-url",
Usage: "the url of the ASP to connect to", Usage: "the url of the Ark server to connect to",
Required: true, Required: true,
} }
receiversFlag = &cli.StringFlag{ receiversFlag = &cli.StringFlag{
@@ -150,7 +150,7 @@ var (
var ( var (
initCommand = cli.Command{ initCommand = cli.Command{
Name: "init", Name: "init",
Usage: "Initialize Ark wallet with encryption password, connect to ASP", Usage: "Initialize Ark wallet with encryption password, connect to Ark server",
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
return initArkSdk(ctx) return initArkSdk(ctx)
}, },
@@ -180,7 +180,7 @@ var (
} }
settleCmd = cli.Command{ settleCmd = cli.Command{
Name: "settle", Name: "settle",
Usage: "Settle onboarding funds or oor payments", Usage: "Settle onboarding or pending funds",
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
return settle(ctx) return settle(ctx)
}, },
@@ -196,7 +196,7 @@ var (
} }
sendCommand = cli.Command{ sendCommand = cli.Command{
Name: "send", Name: "send",
Usage: "Send funds onchain, offchain, or asynchronously", Usage: "Send funds offchain",
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
return send(ctx) return send(ctx)
}, },
@@ -243,7 +243,7 @@ func initArkSdk(ctx *cli.Context) error {
ctx.Context, arksdk.InitArgs{ ctx.Context, arksdk.InitArgs{
ClientType: clientType, ClientType: clientType,
WalletType: arksdk.SingleKeyWallet, WalletType: arksdk.SingleKeyWallet,
AspUrl: ctx.String(urlFlag.Name), ServerUrl: ctx.String(urlFlag.Name),
Seed: ctx.String(privateKeyFlag.Name), Seed: ctx.String(privateKeyFlag.Name),
Password: string(password), Password: string(password),
ExplorerURL: ctx.String(explorerFlag.Name), ExplorerURL: ctx.String(explorerFlag.Name),
@@ -258,8 +258,8 @@ func config(ctx *cli.Context) error {
} }
cfg := map[string]interface{}{ cfg := map[string]interface{}{
"asp_url": cfgData.AspUrl, "server_url": cfgData.ServerUrl,
"asp_pubkey": hex.EncodeToString(cfgData.AspPubkey.SerializeCompressed()), "server_pubkey": hex.EncodeToString(cfgData.ServerPubKey.SerializeCompressed()),
"wallet_type": cfgData.WalletType, "wallet_type": cfgData.WalletType,
"client_tyep": cfgData.ClientType, "client_tyep": cfgData.ClientType,
"network": cfgData.Network.Name, "network": cfgData.Network.Name,
@@ -368,7 +368,7 @@ func send(ctx *cli.Context) error {
if isBitcoin { if isBitcoin {
return sendCovenantLess(ctx, receivers) return sendCovenantLess(ctx, receivers)
} }
return sendCovenant(ctx.Context, receivers) return sendCovenant(ctx, receivers)
} }
func balance(ctx *cli.Context) error { func balance(ctx *cli.Context) error {
@@ -525,9 +525,27 @@ func parseReceivers(receveirsJSON string, isBitcoin bool) ([]arksdk.Receiver, er
} }
func sendCovenantLess(ctx *cli.Context, receivers []arksdk.Receiver) error { func sendCovenantLess(ctx *cli.Context, receivers []arksdk.Receiver) error {
var onchainReceivers, offchainReceivers []arksdk.Receiver
for _, receiver := range receivers {
if receiver.IsOnchain() {
onchainReceivers = append(onchainReceivers, receiver)
} else {
offchainReceivers = append(offchainReceivers, receiver)
}
}
if len(onchainReceivers) > 0 {
txid, err := arkSdkClient.SendOnChain(ctx.Context, onchainReceivers)
if err != nil {
return err
}
return printJSON(map[string]interface{}{"txid": txid})
}
computeExpiration := ctx.Bool(enableExpiryCoinselectFlag.Name) computeExpiration := ctx.Bool(enableExpiryCoinselectFlag.Name)
redeemTx, err := arkSdkClient.SendAsync( redeemTx, err := arkSdkClient.SendOffChain(
ctx.Context, computeExpiration, receivers, ctx.Context, computeExpiration, offchainReceivers,
) )
if err != nil { if err != nil {
return err return err
@@ -540,7 +558,7 @@ func sendCovenantLess(ctx *cli.Context, receivers []arksdk.Receiver) error {
return printJSON(map[string]string{"txid": ptx.UnsignedTx.TxHash().String()}) return printJSON(map[string]string{"txid": ptx.UnsignedTx.TxHash().String()})
} }
func sendCovenant(ctx context.Context, receivers []arksdk.Receiver) error { func sendCovenant(ctx *cli.Context, receivers []arksdk.Receiver) error {
var onchainReceivers, offchainReceivers []arksdk.Receiver var onchainReceivers, offchainReceivers []arksdk.Receiver
for _, receiver := range receivers { for _, receiver := range receivers {
@@ -552,22 +570,21 @@ func sendCovenant(ctx context.Context, receivers []arksdk.Receiver) error {
} }
if len(onchainReceivers) > 0 { if len(onchainReceivers) > 0 {
txID, err := arkSdkClient.SendOnChain(ctx, onchainReceivers) txID, err := arkSdkClient.SendOnChain(ctx.Context, onchainReceivers)
if err != nil { if err != nil {
return err return err
} }
return printJSON(map[string]interface{}{"txid": txID}) return printJSON(map[string]interface{}{"txid": txID})
} }
if len(offchainReceivers) > 0 { computeExpiration := ctx.Bool(enableExpiryCoinselectFlag.Name)
txID, err := arkSdkClient.SendOffChain(ctx, false, offchainReceivers) txid, err := arkSdkClient.SendOffChain(
ctx.Context, computeExpiration, offchainReceivers,
)
if err != nil { if err != nil {
return err return err
} }
return printJSON(map[string]interface{}{"txid": txID}) return printJSON(map[string]interface{}{"txid": txid})
}
return nil
} }
func readPassword(ctx *cli.Context) ([]byte, error) { func readPassword(ctx *cli.Context) ([]byte, error) {

View File

@@ -17,11 +17,11 @@ import (
// CraftSharedOutput returns the taproot script and the amount of the initial root output // CraftSharedOutput returns the taproot script and the amount of the initial root output
func CraftSharedOutput( func CraftSharedOutput(
cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf, cosigners []*secp256k1.PublicKey, server *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64, feeSatsPerNode uint64, roundLifetime int64,
) ([]byte, int64, error) { ) ([]byte, int64, error) {
aggregatedKey, _, err := createAggregatedKeyWithSweep( aggregatedKey, _, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime, cosigners, server, roundLifetime,
) )
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@@ -34,21 +34,21 @@ func CraftSharedOutput(
amount := root.getAmount() + int64(feeSatsPerNode) amount := root.getAmount() + int64(feeSatsPerNode)
scriptPubKey, err := common.P2TRScript(aggregatedKey.FinalKey) scriptPubkey, err := common.P2TRScript(aggregatedKey.FinalKey)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
return scriptPubKey, amount, err return scriptPubkey, amount, err
} }
// CraftCongestionTree creates all the tree's transactions // BuildVtxoTree creates all the tree's transactions
func CraftCongestionTree( func BuildVtxoTree(
initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf, initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, server *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64, feeSatsPerNode uint64, roundLifetime int64,
) (tree.CongestionTree, error) { ) (tree.VtxoTree, error) {
aggregatedKey, sweepTapLeaf, err := createAggregatedKeyWithSweep( aggregatedKey, sweepTapLeaf, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime, cosigners, server, roundLifetime,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -59,7 +59,7 @@ func CraftCongestionTree(
return nil, err return nil, err
} }
congestionTree := make(tree.CongestionTree, 0) vtxoTree := make(tree.VtxoTree, 0)
ins := []*wire.OutPoint{initialInput} ins := []*wire.OutPoint{initialInput}
nodes := []node{root} nodes := []node{root}
@@ -95,12 +95,12 @@ func CraftCongestionTree(
} }
} }
congestionTree = append(congestionTree, treeLevel) vtxoTree = append(vtxoTree, treeLevel)
nodes = append([]node{}, nextNodes...) nodes = append([]node{}, nextNodes...)
ins = append([]*wire.OutPoint{}, nextInputsArgs...) ins = append([]*wire.OutPoint{}, nextInputsArgs...)
} }
return congestionTree, nil return vtxoTree, nil
} }
type node interface { type node interface {
@@ -252,7 +252,7 @@ func createRootNode(
nodes := make([]node, 0, len(receivers)) nodes := make([]node, 0, len(receivers))
for _, r := range receivers { for _, r := range receivers {
pubkeyBytes, err := hex.DecodeString(r.Pubkey) pubkeyBytes, err := hex.DecodeString(r.PubKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -280,10 +280,10 @@ func createRootNode(
} }
func createAggregatedKeyWithSweep( func createAggregatedKeyWithSweep(
cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, roundLifetime int64, cosigners []*secp256k1.PublicKey, server *secp256k1.PublicKey, roundLifetime int64,
) (*musig2.AggregateKey, *psbt.TaprootTapLeafScript, error) { ) (*musig2.AggregateKey, *psbt.TaprootTapLeafScript, error) {
sweepClosure := &tree.CSVSigClosure{ sweepClosure := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{aspPubkey}}, MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{server}},
Seconds: uint(roundLifetime), Seconds: uint(roundLifetime),
} }

View File

@@ -7,29 +7,24 @@ import (
) )
func BuildForfeitTxs( func BuildForfeitTxs(
connectorTx *psbt.Packet, connectorTx *psbt.Packet, vtxoInput *wire.OutPoint,
vtxoInput *wire.OutPoint, vtxoAmount, connectorAmount, feeAmount uint64,
vtxoAmount, vtxoScript, serverScript []byte,
connectorAmount,
feeAmount uint64,
vtxoScript,
aspScript []byte,
) (forfeitTxs []*psbt.Packet, err error) { ) (forfeitTxs []*psbt.Packet, err error) {
version := int32(2)
locktime := uint32(0)
connectors, prevouts := getConnectorInputs(connectorTx, int64(connectorAmount)) connectors, prevouts := getConnectorInputs(connectorTx, int64(connectorAmount))
for i, connectorInput := range connectors { for i, connectorInput := range connectors {
connectorPrevout := prevouts[i] connectorPrevout := prevouts[i]
partialTx, err := psbt.New( ins := []*wire.OutPoint{connectorInput, vtxoInput}
[]*wire.OutPoint{connectorInput, vtxoInput}, outs := []*wire.TxOut{{
[]*wire.TxOut{{
Value: int64(vtxoAmount) + int64(connectorAmount) - int64(feeAmount), Value: int64(vtxoAmount) + int64(connectorAmount) - int64(feeAmount),
PkScript: aspScript, PkScript: serverScript,
}}, }}
2, sequences := []uint32{wire.MaxTxInSequenceNum, wire.MaxTxInSequenceNum}
0, partialTx, err := psbt.New(ins, outs, version, locktime, sequences)
[]uint32{wire.MaxTxInSequenceNum, wire.MaxTxInSequenceNum},
)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -20,8 +20,8 @@ import (
) )
var ( var (
ErrCongestionTreeNotSet = errors.New("congestion tree not set") ErrMissingVtxoTree = errors.New("missing vtxo tree")
ErrAggregateKeyNotSet = errors.New("aggregate key not set") ErrMissingAggregateKey = errors.New("missing aggregate key")
) )
type Musig2Nonce struct { type Musig2Nonce struct {
@@ -62,7 +62,7 @@ type CoordinatorSession interface {
AggregateNonces() (TreeNonces, error) AggregateNonces() (TreeNonces, error)
AddSig(*btcec.PublicKey, TreePartialSigs) error AddSig(*btcec.PublicKey, TreePartialSigs) error
// SignTree combines the signatures and add them to the tree's psbts // SignTree combines the signatures and add them to the tree's psbts
SignTree() (tree.CongestionTree, error) SignTree() (tree.VtxoTree, error)
} }
func (n TreeNonces) Encode(w io.Writer) error { func (n TreeNonces) Encode(w io.Writer) error {
@@ -111,7 +111,7 @@ func ValidateTreeSigs(
scriptRoot []byte, scriptRoot []byte,
finalAggregatedKey *btcec.PublicKey, finalAggregatedKey *btcec.PublicKey,
roundSharedOutputAmount int64, roundSharedOutputAmount int64,
vtxoTree tree.CongestionTree, vtxoTree tree.VtxoTree,
) error { ) error {
prevoutFetcherFactory, err := prevOutFetcherFactory(finalAggregatedKey, vtxoTree, roundSharedOutputAmount) prevoutFetcherFactory, err := prevOutFetcherFactory(finalAggregatedKey, vtxoTree, roundSharedOutputAmount)
if err != nil { if err != nil {
@@ -163,7 +163,7 @@ func ValidateTreeSigs(
func NewTreeSignerSession( func NewTreeSignerSession(
signer *btcec.PrivateKey, signer *btcec.PrivateKey,
roundSharedOutputAmount int64, roundSharedOutputAmount int64,
vtxoTree tree.CongestionTree, vtxoTree tree.VtxoTree,
scriptRoot []byte, scriptRoot []byte,
) SignerSession { ) SignerSession {
return &treeSignerSession{ return &treeSignerSession{
@@ -176,7 +176,7 @@ func NewTreeSignerSession(
type treeSignerSession struct { type treeSignerSession struct {
secretKey *btcec.PrivateKey secretKey *btcec.PrivateKey
tree tree.CongestionTree tree tree.VtxoTree
myNonces [][]*musig2.Nonces myNonces [][]*musig2.Nonces
keys []*btcec.PublicKey keys []*btcec.PublicKey
aggregateNonces TreeNonces aggregateNonces TreeNonces
@@ -187,7 +187,7 @@ type treeSignerSession struct {
func (t *treeSignerSession) generateNonces() error { func (t *treeSignerSession) generateNonces() error {
if t.tree == nil { if t.tree == nil {
return ErrCongestionTreeNotSet return ErrMissingVtxoTree
} }
myNonces := make([][]*musig2.Nonces, 0) myNonces := make([][]*musig2.Nonces, 0)
@@ -213,7 +213,7 @@ func (t *treeSignerSession) generateNonces() error {
func (t *treeSignerSession) GetNonces() (TreeNonces, error) { func (t *treeSignerSession) GetNonces() (TreeNonces, error) {
if t.tree == nil { if t.tree == nil {
return nil, ErrCongestionTreeNotSet return nil, ErrMissingVtxoTree
} }
if t.myNonces == nil { if t.myNonces == nil {
@@ -267,11 +267,11 @@ func (t *treeSignerSession) SetAggregatedNonces(nonces TreeNonces) error {
func (t *treeSignerSession) Sign() (TreePartialSigs, error) { func (t *treeSignerSession) Sign() (TreePartialSigs, error) {
if t.tree == nil { if t.tree == nil {
return nil, ErrCongestionTreeNotSet return nil, ErrMissingVtxoTree
} }
if t.keys == nil { if t.keys == nil {
return nil, ErrAggregateKeyNotSet return nil, ErrMissingAggregateKey
} }
if t.aggregateNonces == nil { if t.aggregateNonces == nil {
@@ -331,7 +331,7 @@ func (t *treeSignerSession) signPartial(partialTx *psbt.Packet, posx int, posy i
type treeCoordinatorSession struct { type treeCoordinatorSession struct {
scriptRoot []byte scriptRoot []byte
tree tree.CongestionTree tree tree.VtxoTree
keys []*btcec.PublicKey keys []*btcec.PublicKey
nonces []TreeNonces nonces []TreeNonces
sigs []TreePartialSigs sigs []TreePartialSigs
@@ -340,7 +340,7 @@ type treeCoordinatorSession struct {
func NewTreeCoordinatorSession( func NewTreeCoordinatorSession(
roundSharedOutputAmount int64, roundSharedOutputAmount int64,
vtxoTree tree.CongestionTree, vtxoTree tree.VtxoTree,
scriptRoot []byte, scriptRoot []byte,
keys []*btcec.PublicKey, keys []*btcec.PublicKey,
) (CoordinatorSession, error) { ) (CoordinatorSession, error) {
@@ -428,7 +428,7 @@ func (t *treeCoordinatorSession) AggregateNonces() (TreeNonces, error) {
} }
// SignTree implements CoordinatorSession. // SignTree implements CoordinatorSession.
func (t *treeCoordinatorSession) SignTree() (tree.CongestionTree, error) { func (t *treeCoordinatorSession) SignTree() (tree.VtxoTree, error) {
var missingSigs int var missingSigs int
for _, sig := range t.sigs { for _, sig := range t.sigs {
if sig == nil { if sig == nil {
@@ -508,7 +508,7 @@ func (t *treeCoordinatorSession) SignTree() (tree.CongestionTree, error) {
func prevOutFetcherFactory( func prevOutFetcherFactory(
finalAggregatedKey *btcec.PublicKey, finalAggregatedKey *btcec.PublicKey,
vtxoTree tree.CongestionTree, vtxoTree tree.VtxoTree,
roundSharedOutputAmount int64, roundSharedOutputAmount int64,
) ( ) (
func(partial *psbt.Packet) (txscript.PrevOutputFetcher, error), func(partial *psbt.Packet) (txscript.PrevOutputFetcher, error),

View File

@@ -28,33 +28,33 @@ func TestRoundTripSignTree(t *testing.T) {
for _, f := range fixtures.Valid { for _, f := range fixtures.Valid {
// Generate 20 cosigners // Generate 20 cosigners
cosigners := make([]*secp256k1.PrivateKey, 20) cosigners := make([]*secp256k1.PrivateKey, 20)
cosignerPubKeys := make([]*btcec.PublicKey, 20) cosignerPubkeys := make([]*btcec.PublicKey, 20)
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
privKey, err := secp256k1.GeneratePrivateKey() prvkey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
cosigners[i] = privKey cosigners[i] = prvkey
cosignerPubKeys[i] = privKey.PubKey() cosignerPubkeys[i] = prvkey.PubKey()
} }
asp, err := secp256k1.GeneratePrivateKey() server, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
_, sharedOutputAmount, err := bitcointree.CraftSharedOutput( _, sharedOutputAmount, err := bitcointree.CraftSharedOutput(
cosignerPubKeys, cosignerPubkeys,
asp.PubKey(), server.PubKey(),
castReceivers(f.Receivers), castReceivers(f.Receivers),
minRelayFee, minRelayFee,
lifetime, lifetime,
) )
require.NoError(t, err) require.NoError(t, err)
vtxoTree, err := bitcointree.CraftCongestionTree( vtxoTree, err := bitcointree.BuildVtxoTree(
&wire.OutPoint{ &wire.OutPoint{
Hash: *testTxid, Hash: *testTxid,
Index: 0, Index: 0,
}, },
cosignerPubKeys, cosignerPubkeys,
asp.PubKey(), server.PubKey(),
castReceivers(f.Receivers), castReceivers(f.Receivers),
minRelayFee, minRelayFee,
lifetime, lifetime,
@@ -62,7 +62,7 @@ func TestRoundTripSignTree(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
sweepClosure := &tree.CSVSigClosure{ sweepClosure := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{asp.PubKey()}}, MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{server.PubKey()}},
Seconds: uint(lifetime), Seconds: uint(lifetime),
} }
@@ -73,11 +73,11 @@ func TestRoundTripSignTree(t *testing.T) {
sweepTapTree := txscript.AssembleTaprootScriptTree(sweepTapLeaf) sweepTapTree := txscript.AssembleTaprootScriptTree(sweepTapLeaf)
root := sweepTapTree.RootNode.TapHash() root := sweepTapTree.RootNode.TapHash()
aspCoordinator, err := bitcointree.NewTreeCoordinatorSession( serverCoordinator, err := bitcointree.NewTreeCoordinatorSession(
sharedOutputAmount, sharedOutputAmount,
vtxoTree, vtxoTree,
root.CloneBytes(), root.CloneBytes(),
cosignerPubKeys, cosignerPubkeys,
) )
require.NoError(t, err) require.NoError(t, err)
@@ -91,16 +91,16 @@ func TestRoundTripSignTree(t *testing.T) {
for i, session := range signerSessions { for i, session := range signerSessions {
nonces, err := session.GetNonces() nonces, err := session.GetNonces()
require.NoError(t, err) require.NoError(t, err)
err = aspCoordinator.AddNonce(cosignerPubKeys[i], nonces) err = serverCoordinator.AddNonce(cosignerPubkeys[i], nonces)
require.NoError(t, err) require.NoError(t, err)
} }
aggregatedNonce, err := aspCoordinator.AggregateNonces() aggregatedNonce, err := serverCoordinator.AggregateNonces()
require.NoError(t, err) require.NoError(t, err)
// Set keys and aggregated nonces for all signers // Set keys and aggregated nonces for all signers
for _, session := range signerSessions { for _, session := range signerSessions {
err = session.SetKeys(cosignerPubKeys) err = session.SetKeys(cosignerPubkeys)
require.NoError(t, err) require.NoError(t, err)
err = session.SetAggregatedNonces(aggregatedNonce) err = session.SetAggregatedNonces(aggregatedNonce)
require.NoError(t, err) require.NoError(t, err)
@@ -110,15 +110,15 @@ func TestRoundTripSignTree(t *testing.T) {
for i, session := range signerSessions { for i, session := range signerSessions {
sig, err := session.Sign() sig, err := session.Sign()
require.NoError(t, err) require.NoError(t, err)
err = aspCoordinator.AddSig(cosignerPubKeys[i], sig) err = serverCoordinator.AddSig(cosignerPubkeys[i], sig)
require.NoError(t, err) require.NoError(t, err)
} }
signedTree, err := aspCoordinator.SignTree() signedTree, err := serverCoordinator.SignTree()
require.NoError(t, err) require.NoError(t, err)
// verify the tree // verify the tree
aggregatedKey, err := bitcointree.AggregateKeys(cosignerPubKeys, root.CloneBytes()) aggregatedKey, err := bitcointree.AggregateKeys(cosignerPubkeys, root.CloneBytes())
require.NoError(t, err) require.NoError(t, err)
err = bitcointree.ValidateTreeSigs( err = bitcointree.ValidateTreeSigs(
@@ -140,7 +140,7 @@ func castReceivers(receivers []receiverFixture) []tree.VtxoLeaf {
receiversOut := make([]tree.VtxoLeaf, 0, len(receivers)) receiversOut := make([]tree.VtxoLeaf, 0, len(receivers))
for _, r := range receivers { for _, r := range receivers {
receiversOut = append(receiversOut, tree.VtxoLeaf{ receiversOut = append(receiversOut, tree.VtxoLeaf{
Pubkey: r.Pubkey, PubKey: r.Pubkey,
Amount: uint64(r.Amount), Amount: uint64(r.Amount),
}) })
} }

View File

@@ -2,7 +2,6 @@ package bitcointree
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"strings" "strings"
@@ -15,36 +14,36 @@ import (
) )
var ( var (
ErrInvalidPoolTransaction = errors.New("invalid pool transaction") ErrInvalidRoundTx = fmt.Errorf("invalid round transaction")
ErrInvalidPoolTransactionOutputs = errors.New("invalid number of outputs in pool transaction") ErrInvalidRoundTxOutputs = fmt.Errorf("invalid number of outputs in round transaction")
ErrEmptyTree = errors.New("empty congestion tree") ErrEmptyTree = fmt.Errorf("empty vtxo tree")
ErrInvalidRootLevel = errors.New("root level must have only one node") ErrInvalidRootLevel = fmt.Errorf("root level must have only one node")
ErrNoLeaves = errors.New("no leaves in the tree") ErrNoLeaves = fmt.Errorf("no leaves in the tree")
ErrNodeTransactionEmpty = errors.New("node transaction is empty") ErrNodeTxEmpty = fmt.Errorf("node transaction is empty")
ErrNodeTxidEmpty = errors.New("node txid is empty") ErrNodeTxidEmpty = fmt.Errorf("node txid is empty")
ErrNodeParentTxidEmpty = errors.New("node parent txid is empty") ErrNodeParentTxidEmpty = fmt.Errorf("node parent txid is empty")
ErrNodeTxidDifferent = errors.New("node txid differs from node transaction") ErrNodeTxidDifferent = fmt.Errorf("node txid differs from node transaction")
ErrNumberOfInputs = errors.New("node transaction should have only one input") ErrNumberOfInputs = fmt.Errorf("node transaction should have only one input")
ErrNumberOfOutputs = errors.New("node transaction should have only three or two outputs") ErrNumberOfOutputs = fmt.Errorf("node transaction should have only three or two outputs")
ErrParentTxidInput = errors.New("parent txid should be the input of the node transaction") ErrParentTxidInput = fmt.Errorf("parent txid should be the input of the node transaction")
ErrNumberOfChildren = errors.New("node branch transaction should have two children") ErrNumberOfChildren = fmt.Errorf("node branch transaction should have two children")
ErrLeafChildren = errors.New("leaf node should have max 1 child") ErrLeafChildren = fmt.Errorf("leaf node should have max 1 child")
ErrInvalidChildTxid = errors.New("invalid child txid") ErrInvalidChildTxid = fmt.Errorf("invalid child txid")
ErrNumberOfTapscripts = errors.New("input should have 1 tapscript leaf") ErrNumberOfTapscripts = fmt.Errorf("input should have 1 tapscript leaf")
ErrInternalKey = errors.New("invalid taproot internal key") ErrInternalKey = fmt.Errorf("invalid taproot internal key")
ErrInvalidTaprootScript = errors.New("invalid taproot script") ErrInvalidTaprootScript = fmt.Errorf("invalid taproot script")
ErrInvalidControlBlock = errors.New("invalid control block") ErrInvalidControlBlock = fmt.Errorf("invalid control block")
ErrInvalidTaprootScriptLen = errors.New("invalid taproot script length (expected 32 bytes)") ErrInvalidTaprootScriptLen = fmt.Errorf("invalid taproot script length (expected 32 bytes)")
ErrInvalidLeafTaprootScript = errors.New("invalid leaf taproot script") ErrInvalidLeafTaprootScript = fmt.Errorf("invalid leaf taproot script")
ErrInvalidAmount = errors.New("children amount is different from parent amount") ErrInvalidAmount = fmt.Errorf("children amount is different from parent amount")
ErrInvalidSweepSequence = errors.New("invalid sweep sequence") ErrInvalidSweepSequence = fmt.Errorf("invalid sweep sequence")
ErrInvalidASP = errors.New("invalid ASP") ErrInvalidServer = fmt.Errorf("invalid server")
ErrMissingFeeOutput = errors.New("missing fee output") ErrMissingFeeOutput = fmt.Errorf("missing fee output")
ErrInvalidLeftOutput = errors.New("invalid left output") ErrInvalidLeftOutput = fmt.Errorf("invalid left output")
ErrInvalidRightOutput = errors.New("invalid right output") ErrInvalidRightOutput = fmt.Errorf("invalid right output")
ErrMissingSweepTapscript = errors.New("missing sweep tapscript") ErrMissingSweepTapscript = fmt.Errorf("missing sweep tapscript")
ErrInvalidLeaf = errors.New("leaf node shouldn't have children") ErrInvalidLeaf = fmt.Errorf("leaf node shouldn't have children")
ErrWrongPoolTxID = errors.New("root input should be the pool tx outpoint") ErrWrongRoundTxid = fmt.Errorf("the input of the tree root is not the round tx's shared output")
) )
// 0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 // 0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0
@@ -62,28 +61,28 @@ func UnspendableKey() *secp256k1.PublicKey {
return key return key
} }
// ValidateCongestionTree checks if the given congestion tree is valid // ValidateVtxoTree checks if the given vtxo tree is valid
// poolTxID & poolTxIndex & poolTxAmount are used to validate the root input outpoint // roundTxid & roundTxIndex & roundTxAmount are used to validate the root input outpoint
// aspPublicKey & roundLifetime are used to validate the sweep tapscript leaves // serverPubkey & roundLifetime are used to validate the sweep tapscript leaves
// besides that, the function validates: // besides that, the function validates:
// - the number of nodes // - the number of nodes
// - the number of leaves // - the number of leaves
// - children coherence with parent // - children coherence with parent
// - every control block and taproot output scripts // - every control block and taproot output scripts
// - input and output amounts // - input and output amounts
func ValidateCongestionTree( func ValidateVtxoTree(
vtxoTree tree.CongestionTree, poolTx string, aspPublicKey *secp256k1.PublicKey, roundLifetime int64, vtxoTree tree.VtxoTree, roundTx string, serverPubkey *secp256k1.PublicKey, roundLifetime int64,
) error { ) error {
poolTransaction, err := psbt.NewFromRawBytes(strings.NewReader(poolTx), true) roundTransaction, err := psbt.NewFromRawBytes(strings.NewReader(roundTx), true)
if err != nil { if err != nil {
return ErrInvalidPoolTransaction return ErrInvalidRoundTx
} }
if len(poolTransaction.Outputs) < sharedOutputIndex+1 { if len(roundTransaction.Outputs) < sharedOutputIndex+1 {
return ErrInvalidPoolTransactionOutputs return ErrInvalidRoundTxOutputs
} }
poolTxAmount := poolTransaction.UnsignedTx.TxOut[sharedOutputIndex].Value roundTxAmount := roundTransaction.UnsignedTx.TxOut[sharedOutputIndex].Value
nbNodes := vtxoTree.NumberOfNodes() nbNodes := vtxoTree.NumberOfNodes()
if nbNodes == 0 { if nbNodes == 0 {
@@ -94,7 +93,7 @@ func ValidateCongestionTree(
return ErrInvalidRootLevel return ErrInvalidRootLevel
} }
// check that root input is connected to the pool tx // check that root input is connected to the round tx
rootPsetB64 := vtxoTree[0][0].Tx rootPsetB64 := vtxoTree[0][0].Tx
rootPset, err := psbt.NewFromRawBytes(strings.NewReader(rootPsetB64), true) rootPset, err := psbt.NewFromRawBytes(strings.NewReader(rootPsetB64), true)
if err != nil { if err != nil {
@@ -106,9 +105,9 @@ func ValidateCongestionTree(
} }
rootInput := rootPset.UnsignedTx.TxIn[0] rootInput := rootPset.UnsignedTx.TxIn[0]
if chainhash.Hash(rootInput.PreviousOutPoint.Hash).String() != poolTransaction.UnsignedTx.TxHash().String() || if chainhash.Hash(rootInput.PreviousOutPoint.Hash).String() != roundTransaction.UnsignedTx.TxHash().String() ||
rootInput.PreviousOutPoint.Index != sharedOutputIndex { rootInput.PreviousOutPoint.Index != sharedOutputIndex {
return ErrWrongPoolTxID return ErrWrongRoundTxid
} }
sumRootValue := int64(0) sumRootValue := int64(0)
@@ -116,7 +115,7 @@ func ValidateCongestionTree(
sumRootValue += output.Value sumRootValue += output.Value
} }
if sumRootValue >= poolTxAmount { if sumRootValue >= roundTxAmount {
return ErrInvalidAmount return ErrInvalidAmount
} }
@@ -125,7 +124,7 @@ func ValidateCongestionTree(
} }
sweepClosure := &tree.CSVSigClosure{ sweepClosure := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{aspPublicKey}}, MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{serverPubkey}},
Seconds: uint(roundLifetime), Seconds: uint(roundLifetime),
} }
@@ -152,9 +151,9 @@ func ValidateCongestionTree(
return nil return nil
} }
func validateNodeTransaction(node tree.Node, tree tree.CongestionTree, tapTreeRoot []byte) error { func validateNodeTransaction(node tree.Node, tree tree.VtxoTree, tapTreeRoot []byte) error {
if node.Tx == "" { if node.Tx == "" {
return ErrNodeTransactionEmpty return ErrNodeTxEmpty
} }
if node.Txid == "" { if node.Txid == "" {

View File

@@ -26,8 +26,8 @@ func ParseVtxoScript(scripts []string) (VtxoScript, error) {
return nil, fmt.Errorf("invalid vtxo scripts: %s", scripts) return nil, fmt.Errorf("invalid vtxo scripts: %s", scripts)
} }
func NewDefaultVtxoScript(owner, asp *secp256k1.PublicKey, exitDelay uint) VtxoScript { func NewDefaultVtxoScript(owner, server *secp256k1.PublicKey, exitDelay uint) VtxoScript {
base := tree.NewDefaultVtxoScript(owner, asp, exitDelay) base := tree.NewDefaultVtxoScript(owner, server, exitDelay)
return &TapscriptsVtxoScript{*base} return &TapscriptsVtxoScript{*base}
} }

View File

@@ -7,14 +7,14 @@ import (
"github.com/vulpemventures/go-elements/psetv2" "github.com/vulpemventures/go-elements/psetv2"
) )
func ValidateConnectors(poolTx string, connectors []string) error { func ValidateConnectors(roundTx string, connectors []string) error {
ptx, err := psetv2.NewPsetFromBase64(poolTx) ptx, err := psetv2.NewPsetFromBase64(roundTx)
if err != nil { if err != nil {
return fmt.Errorf("invalid pool tx: %s", err) return fmt.Errorf("invalid round tx: %s", err)
} }
utx, err := ptx.UnsignedTx() utx, err := ptx.UnsignedTx()
if err != nil { if err != nil {
return fmt.Errorf("invalid pool tx: %s", err) return fmt.Errorf("invalid round tx: %s", err)
} }
prevConnectorTxid := utx.TxHash().String() prevConnectorTxid := utx.TxHash().String()
prevConnectorVout := uint32(1) prevConnectorVout := uint32(1)

View File

@@ -8,12 +8,12 @@ import (
"github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4"
) )
// tr(unspendable, { and(pk(user), pk(asp)), and(older(timeout), pk(user)) }) // tr(unspendable, { and(pk(user), pk(server)), and(older(timeout), pk(user)) })
const DefaultVtxoDescriptorTemplate = "tr(%s,{ and(pk(%s), pk(%s)), and(older(%d), pk(%s)) })" const DefaultVtxoDescriptorTemplate = "tr(%s,{ and(pk(%s), pk(%s)), and(older(%d), pk(%s)) })"
func ParseDefaultVtxoDescriptor( func ParseDefaultVtxoDescriptor(
descriptor string, descriptor string,
) (user, asp *secp256k1.PublicKey, timeout uint, err error) { ) (user, server *secp256k1.PublicKey, timeout uint, err error) {
desc, err := ParseTaprootDescriptor(descriptor) desc, err := ParseTaprootDescriptor(descriptor)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, 0, err
@@ -42,7 +42,7 @@ func ParseDefaultVtxoDescriptor(
return nil, nil, 0, err return nil, nil, 0, err
} }
asp, err = schnorr.ParsePubKey(keyBytes) server, err = schnorr.ParsePubKey(keyBytes)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, 0, err
} }
@@ -70,7 +70,7 @@ func ParseDefaultVtxoDescriptor(
return nil, nil, 0, errors.New("boarding descriptor is invalid") return nil, nil, 0, errors.New("boarding descriptor is invalid")
} }
if asp == nil { if server == nil {
return nil, nil, 0, errors.New("boarding descriptor is invalid") return nil, nil, 0, errors.New("boarding descriptor is invalid")
} }

View File

@@ -8,24 +8,24 @@ import (
"github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4"
) )
// Address represents an Ark address with HRP, ASP public key, and VTXO Taproot public key // Address represents an Ark address with HRP, server public key, and VTXO Taproot public key
type Address struct { type Address struct {
HRP string HRP string
Asp *secp256k1.PublicKey Server *secp256k1.PublicKey
VtxoTapKey *secp256k1.PublicKey VtxoTapKey *secp256k1.PublicKey
} }
// Encode converts the address to its bech32m string representation // Encode converts the address to its bech32m string representation
func (a *Address) Encode() (string, error) { func (a *Address) Encode() (string, error) {
if a.Asp == nil { if a.Server == nil {
return "", fmt.Errorf("missing asp public key") return "", fmt.Errorf("missing server public key")
} }
if a.VtxoTapKey == nil { if a.VtxoTapKey == nil {
return "", fmt.Errorf("missing vtxo tap public key") return "", fmt.Errorf("missing vtxo tap public key")
} }
combinedKey := append( combinedKey := append(
schnorr.SerializePubKey(a.Asp), schnorr.SerializePubKey(a.VtxoTapKey)..., schnorr.SerializePubKey(a.Server), schnorr.SerializePubKey(a.VtxoTapKey)...,
) )
grp, err := bech32.ConvertBits(combinedKey, 8, 5, true) grp, err := bech32.ConvertBits(combinedKey, 8, 5, true)
if err != nil { if err != nil {
@@ -52,19 +52,19 @@ func DecodeAddress(addr string) (*Address, error) {
return nil, err return nil, err
} }
aKey, err := schnorr.ParsePubKey(grp[:32]) serverKey, err := schnorr.ParsePubKey(grp[:32])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse public key: %s", err) return nil, fmt.Errorf("failed to parse public key: %s", err)
} }
vtxoKey, err := schnorr.ParsePubKey(grp[32:]) vtxoKey, err := schnorr.ParsePubKey(grp[32:])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse asp public key: %s", err) return nil, fmt.Errorf("failed to parse server public key: %s", err)
} }
return &Address{ return &Address{
HRP: prefix, HRP: prefix,
Asp: aKey, Server: serverKey,
VtxoTapKey: vtxoKey, VtxoTapKey: vtxoKey,
}, nil }, nil
} }

View File

@@ -27,7 +27,7 @@ func TestAddressEncoding(t *testing.T) {
Valid []struct { Valid []struct {
Addr string `json:"addr"` Addr string `json:"addr"`
ExpectedUserKey string `json:"expectedUserKey"` ExpectedUserKey string `json:"expectedUserKey"`
ExpectedAspKey string `json:"expectedAspKey"` ExpectedServerKey string `json:"expectedServerKey"`
} `json:"valid"` } `json:"valid"`
Invalid []struct { Invalid []struct {
Addr string `json:"addr"` Addr string `json:"addr"`
@@ -43,14 +43,14 @@ func TestAddressEncoding(t *testing.T) {
addr, err := common.DecodeAddress(f.Addr) addr, err := common.DecodeAddress(f.Addr)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, addr.HRP) require.NotEmpty(t, addr.HRP)
require.NotNil(t, addr.Asp) require.NotNil(t, addr.Server)
require.NotNil(t, addr.VtxoTapKey) require.NotNil(t, addr.VtxoTapKey)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, f.ExpectedUserKey, hex.EncodeToString(addr.VtxoTapKey.SerializeCompressed())) require.Equal(t, f.ExpectedUserKey, hex.EncodeToString(addr.VtxoTapKey.SerializeCompressed()))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, f.ExpectedAspKey, hex.EncodeToString(addr.Asp.SerializeCompressed())) require.Equal(t, f.ExpectedServerKey, hex.EncodeToString(addr.Server.SerializeCompressed()))
encoded, err := addr.Encode() encoded, err := addr.Encode()
require.NoError(t, err) require.NoError(t, err)

View File

@@ -30,7 +30,7 @@ func ComputeForfeitTxFee(
feeRate chainfee.SatPerKVByte, feeRate chainfee.SatPerKVByte,
tapscript *waddrmgr.Tapscript, tapscript *waddrmgr.Tapscript,
witnessSize int, witnessSize int,
aspScriptClass txscript.ScriptClass, serverScriptClass txscript.ScriptClass,
) (uint64, error) { ) (uint64, error) {
txWeightEstimator := &input.TxWeightEstimator{} txWeightEstimator := &input.TxWeightEstimator{}
@@ -40,7 +40,7 @@ func ComputeForfeitTxFee(
tapscript, tapscript,
) )
switch aspScriptClass { switch serverScriptClass {
case txscript.PubKeyHashTy: case txscript.PubKeyHashTy:
txWeightEstimator.AddP2PKHOutput() txWeightEstimator.AddP2PKHOutput()
case txscript.ScriptHashTy: case txscript.ScriptHashTy:
@@ -52,7 +52,7 @@ func ComputeForfeitTxFee(
case txscript.WitnessV1TaprootTy: case txscript.WitnessV1TaprootTy:
txWeightEstimator.AddP2TROutput() txWeightEstimator.AddP2TROutput()
default: default:
return 0, fmt.Errorf("unknown asp script class: %v", aspScriptClass) return 0, fmt.Errorf("unknown server script class: %v", serverScriptClass)
} }
return uint64(feeRate.FeeForVSize(lntypes.VByte(txWeightEstimator.VSize())).ToUnit(btcutil.AmountSatoshi)), nil return uint64(feeRate.FeeForVSize(lntypes.VByte(txWeightEstimator.VSize())).ToUnit(btcutil.AmountSatoshi)), nil

View File

@@ -4,7 +4,7 @@
{ {
"addr": "tark1x0lm8hhr2wc6n6lyemtyh9rz8rg2ftpkfun46aca56kjg3ws0tsztfpuanaquxc6faedvjk3tax0575y6perapg3e95654pk8r4fjecs5fyd2", "addr": "tark1x0lm8hhr2wc6n6lyemtyh9rz8rg2ftpkfun46aca56kjg3ws0tsztfpuanaquxc6faedvjk3tax0575y6perapg3e95654pk8r4fjecs5fyd2",
"expectedUserKey": "0225a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "expectedUserKey": "0225a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967",
"expectedAspKey": "0233ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0" "expectedServerKey": "0233ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0"
} }
], ],
"invalid": [ "invalid": [

View File

@@ -12,15 +12,15 @@ import (
"github.com/vulpemventures/go-elements/taproot" "github.com/vulpemventures/go-elements/taproot"
) )
func CraftCongestionTree( func BuildVtxoTree(
asset string, aspPubkey *secp256k1.PublicKey, receivers []VtxoLeaf, asset string, serverPubkey *secp256k1.PublicKey, receivers []VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64, feeSatsPerNode uint64, roundLifetime int64,
) ( ) (
buildCongestionTree TreeFactory, factoryFn TreeFactory,
sharedOutputScript []byte, sharedOutputAmount uint64, err error, sharedOutputScript []byte, sharedOutputAmount uint64, err error,
) { ) {
root, err := createPartialCongestionTree( root, err := buildTreeNodes(
asset, aspPubkey, receivers, feeSatsPerNode, roundLifetime, asset, serverPubkey, receivers, feeSatsPerNode, roundLifetime,
) )
if err != nil { if err != nil {
return return
@@ -36,7 +36,7 @@ func CraftCongestionTree(
return return
} }
sharedOutputAmount = root.getAmount() + root.feeSats sharedOutputAmount = root.getAmount() + root.feeSats
buildCongestionTree = root.createFinalCongestionTree() factoryFn = root.buildVtxoTree()
return return
} }
@@ -321,16 +321,16 @@ func (n *node) getTx(
return pset, nil return pset, nil
} }
func (n *node) createFinalCongestionTree() TreeFactory { func (n *node) buildVtxoTree() TreeFactory {
return func(poolTxInput psetv2.InputArgs) (CongestionTree, error) { return func(roundTxInput psetv2.InputArgs) (VtxoTree, error) {
congestionTree := make(CongestionTree, 0) vtxoTree := make(VtxoTree, 0)
_, taprootTree, err := n.getWitnessData() _, taprootTree, err := n.getWitnessData()
if err != nil { if err != nil {
return nil, err return nil, err
} }
ins := []psetv2.InputArgs{poolTxInput} ins := []psetv2.InputArgs{roundTxInput}
inTrees := []*taproot.IndexedElementsTapScriptTree{taprootTree} inTrees := []*taproot.IndexedElementsTapScriptTree{taprootTree}
nodes := []*node{n} nodes := []*node{n}
@@ -366,7 +366,7 @@ func (n *node) createFinalCongestionTree() TreeFactory {
} }
} }
congestionTree = append(congestionTree, treeLevel) vtxoTree = append(vtxoTree, treeLevel)
nodes = append([]*node{}, nextNodes...) nodes = append([]*node{}, nextNodes...)
ins = append([]psetv2.InputArgs{}, nextInputsArgs...) ins = append([]psetv2.InputArgs{}, nextInputsArgs...)
inTrees = append( inTrees = append(
@@ -374,12 +374,12 @@ func (n *node) createFinalCongestionTree() TreeFactory {
) )
} }
return congestionTree, nil return vtxoTree, nil
} }
} }
func createPartialCongestionTree( func buildTreeNodes(
asset string, aspPubkey *secp256k1.PublicKey, receivers []VtxoLeaf, asset string, serverPubkey *secp256k1.PublicKey, receivers []VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64, feeSatsPerNode uint64, roundLifetime int64,
) (root *node, err error) { ) (root *node, err error) {
if len(receivers) == 0 { if len(receivers) == 0 {
@@ -388,7 +388,7 @@ func createPartialCongestionTree(
nodes := make([]*node, 0, len(receivers)) nodes := make([]*node, 0, len(receivers))
for _, r := range receivers { for _, r := range receivers {
pubkeyBytes, err := hex.DecodeString(r.Pubkey) pubkeyBytes, err := hex.DecodeString(r.PubKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -399,7 +399,7 @@ func createPartialCongestionTree(
} }
leafNode := &node{ leafNode := &node{
sweepKey: aspPubkey, sweepKey: serverPubkey,
receivers: []vtxoOutput{{pubkey, r.Amount}}, receivers: []vtxoOutput{{pubkey, r.Amount}},
asset: asset, asset: asset,
feeSats: feeSatsPerNode, feeSats: feeSatsPerNode,

View File

@@ -4,7 +4,7 @@ import (
"errors" "errors"
) )
// Node is a struct embedding the transaction and the parent txid of a congestion tree node // Node is a struct embedding the transaction and the parent txid of a vtxo tree node
type Node struct { type Node struct {
Txid string Txid string
Tx string Tx string
@@ -14,28 +14,28 @@ type Node struct {
var ( var (
ErrParentNotFound = errors.New("parent not found") ErrParentNotFound = errors.New("parent not found")
ErrLeafNotFound = errors.New("leaf not found in congestion tree") ErrLeafNotFound = errors.New("leaf not found in vtxo tree")
) )
// CongestionTree is reprensented as a matrix of TreeNode struct // VtxoTree is reprensented as a matrix of TreeNode struct
// the first level of the matrix is the root of the tree // the first level of the matrix is the root of the tree
type CongestionTree [][]Node type VtxoTree [][]Node
// Root returns the root node of the congestion tree // Root returns the root node of the vtxo tree
func (c CongestionTree) Root() (Node, error) { func (c VtxoTree) Root() (Node, error) {
if len(c) <= 0 { if len(c) <= 0 {
return Node{}, errors.New("empty congestion tree") return Node{}, errors.New("empty vtxo tree")
} }
if len(c[0]) <= 0 { if len(c[0]) <= 0 {
return Node{}, errors.New("empty congestion tree") return Node{}, errors.New("empty vtxo tree")
} }
return c[0][0], nil return c[0][0], nil
} }
// Leaves returns the leaves of the congestion tree (the vtxos txs) // Leaves returns the leaves of the vtxo tree
func (c CongestionTree) Leaves() []Node { func (c VtxoTree) Leaves() []Node {
leaves := c[len(c)-1] leaves := c[len(c)-1]
for _, level := range c[:len(c)-1] { for _, level := range c[:len(c)-1] {
for _, node := range level { for _, node := range level {
@@ -49,7 +49,7 @@ func (c CongestionTree) Leaves() []Node {
} }
// Children returns all the nodes that have the given node as parent // Children returns all the nodes that have the given node as parent
func (c CongestionTree) Children(nodeTxid string) []Node { func (c VtxoTree) Children(nodeTxid string) []Node {
var children []Node var children []Node
for _, level := range c { for _, level := range c {
for _, node := range level { for _, node := range level {
@@ -62,8 +62,8 @@ func (c CongestionTree) Children(nodeTxid string) []Node {
return children return children
} }
// NumberOfNodes returns the total number of pset in the congestion tree // NumberOfNodes returns the total number of pset in the vtxo tree
func (c CongestionTree) NumberOfNodes() int { func (c VtxoTree) NumberOfNodes() int {
var count int var count int
for _, level := range c { for _, level := range c {
count += len(level) count += len(level)
@@ -71,8 +71,8 @@ func (c CongestionTree) NumberOfNodes() int {
return count return count
} }
// Branch returns the branch of the given vtxo txid from root to leaf in the order of the congestion tree // Branch returns the branch of the given vtxo txid from root to leaf in the order of the vtxo tree
func (c CongestionTree) Branch(vtxoTxid string) ([]Node, error) { func (c VtxoTree) Branch(vtxoTxid string) ([]Node, error) {
branch := make([]Node, 0) branch := make([]Node, 0)
leaves := c.Leaves() leaves := c.Leaves()
@@ -102,7 +102,7 @@ func (c CongestionTree) Branch(vtxoTxid string) ([]Node, error) {
return branch, nil return branch, nil
} }
func (n Node) findParent(tree CongestionTree) (Node, error) { func (n Node) findParent(tree VtxoTree) (Node, error) {
for _, level := range tree { for _, level := range tree {
for _, node := range level { for _, node := range level {
if node.Txid == n.ParentTxid { if node.Txid == n.ParentTxid {

View File

@@ -8,13 +8,9 @@ import (
) )
func BuildForfeitTxs( func BuildForfeitTxs(
connectorTx *psetv2.Pset, connectorTx *psetv2.Pset, vtxoInput psetv2.InputArgs,
vtxoInput psetv2.InputArgs, vtxoAmount, connectorAmount, feeAmount uint64,
vtxoAmount, vtxoScript, serverScript []byte,
connectorAmount,
feeAmount uint64,
vtxoScript,
aspScript []byte,
) (forfeitTxs []*psetv2.Pset, err error) { ) (forfeitTxs []*psetv2.Pset, err error) {
connectors, prevouts := getConnectorInputs(connectorTx, connectorAmount) connectors, prevouts := getConnectorInputs(connectorTx, connectorAmount)
@@ -63,7 +59,7 @@ func BuildForfeitTxs(
{ {
Asset: asset, Asset: asset,
Amount: vtxoAmount + connectorAmount - feeAmount, Amount: vtxoAmount + connectorAmount - feeAmount,
Script: aspScript, Script: serverScript,
}, },
{ {
Asset: asset, Asset: asset,

View File

@@ -281,10 +281,10 @@ func (f *MultisigClosure) Witness(controlBlock []byte, signatures map[string][]b
// Add signatures in the reverse order as public keys // Add signatures in the reverse order as public keys
for i := len(f.PubKeys) - 1; i >= 0; i-- { for i := len(f.PubKeys) - 1; i >= 0; i-- {
pubKey := f.PubKeys[i] pubkey := f.PubKeys[i]
sig, ok := signatures[hex.EncodeToString(schnorr.SerializePubKey(pubKey))] sig, ok := signatures[hex.EncodeToString(schnorr.SerializePubKey(pubkey))]
if !ok { if !ok {
return nil, fmt.Errorf("missing signature for public key %x", schnorr.SerializePubKey(pubKey)) return nil, fmt.Errorf("missing signature for public key %x", schnorr.SerializePubKey(pubkey))
} }
witness = append(witness, sig) witness = append(witness, sig)
} }

View File

@@ -36,17 +36,17 @@ func TestRoundTripCSV(t *testing.T) {
func TestMultisigClosure(t *testing.T) { func TestMultisigClosure(t *testing.T) {
// Generate some test keys // Generate some test keys
privKey1, err := secp256k1.GeneratePrivateKey() prvkey1, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKey1 := privKey1.PubKey() pubkey1 := prvkey1.PubKey()
privKey2, err := secp256k1.GeneratePrivateKey() prvkey2, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKey2 := privKey2.PubKey() pubkey2 := prvkey2.PubKey()
t.Run("valid 2-of-2 multisig", func(t *testing.T) { t.Run("valid 2-of-2 multisig", func(t *testing.T) {
closure := &tree.MultisigClosure{ closure := &tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1, pubKey2}, PubKeys: []*secp256k1.PublicKey{pubkey1, pubkey2},
} }
// Generate script // Generate script
@@ -62,18 +62,18 @@ func TestMultisigClosure(t *testing.T) {
// Compare serialized pubkeys // Compare serialized pubkeys
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKey1), schnorr.SerializePubKey(pubkey1),
schnorr.SerializePubKey(decodedClosure.PubKeys[0]), schnorr.SerializePubKey(decodedClosure.PubKeys[0]),
) )
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKey2), schnorr.SerializePubKey(pubkey2),
schnorr.SerializePubKey(decodedClosure.PubKeys[1]), schnorr.SerializePubKey(decodedClosure.PubKeys[1]),
) )
}) })
t.Run("valid single key multisig", func(t *testing.T) { t.Run("valid single key multisig", func(t *testing.T) {
closure := &tree.MultisigClosure{ closure := &tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1}, PubKeys: []*secp256k1.PublicKey{pubkey1},
} }
script, err := closure.Script() script, err := closure.Script()
@@ -87,7 +87,7 @@ func TestMultisigClosure(t *testing.T) {
// Compare serialized pubkey // Compare serialized pubkey
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKey1), schnorr.SerializePubKey(pubkey1),
schnorr.SerializePubKey(decodedClosure.PubKeys[0]), schnorr.SerializePubKey(decodedClosure.PubKeys[0]),
) )
}) })
@@ -110,9 +110,9 @@ func TestMultisigClosure(t *testing.T) {
}) })
t.Run("invalid script - missing checksig", func(t *testing.T) { t.Run("invalid script - missing checksig", func(t *testing.T) {
pubKeyBytes := schnorr.SerializePubKey(pubKey1) pubkeyBytes := schnorr.SerializePubKey(pubkey1)
script := []byte{txscript.OP_DATA_32} script := []byte{txscript.OP_DATA_32}
script = append(script, pubKeyBytes...) script = append(script, pubkeyBytes...)
// Missing OP_CHECKSIG // Missing OP_CHECKSIG
closure := &tree.MultisigClosure{} closure := &tree.MultisigClosure{}
@@ -122,9 +122,9 @@ func TestMultisigClosure(t *testing.T) {
}) })
t.Run("invalid script - extra data after checksig", func(t *testing.T) { t.Run("invalid script - extra data after checksig", func(t *testing.T) {
pubKeyBytes := schnorr.SerializePubKey(pubKey1) pubkeyBytes := schnorr.SerializePubKey(pubkey1)
script := []byte{txscript.OP_DATA_32} script := []byte{txscript.OP_DATA_32}
script = append(script, pubKeyBytes...) script = append(script, pubkeyBytes...)
script = append(script, txscript.OP_CHECKSIG) script = append(script, txscript.OP_CHECKSIG)
script = append(script, 0x00) // Extra unwanted byte script = append(script, 0x00) // Extra unwanted byte
@@ -136,7 +136,7 @@ func TestMultisigClosure(t *testing.T) {
t.Run("witness size", func(t *testing.T) { t.Run("witness size", func(t *testing.T) {
closure := &tree.MultisigClosure{ closure := &tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1, pubKey2}, PubKeys: []*secp256k1.PublicKey{pubkey1, pubkey2},
} }
require.Equal(t, 128, closure.WitnessSize()) // 64 * 2 bytes require.Equal(t, 128, closure.WitnessSize()) // 64 * 2 bytes
@@ -144,15 +144,15 @@ func TestMultisigClosure(t *testing.T) {
t.Run("valid 12-of-12 multisig", func(t *testing.T) { t.Run("valid 12-of-12 multisig", func(t *testing.T) {
// Generate 12 keys // Generate 12 keys
pubKeys := make([]*secp256k1.PublicKey, 12) pubkeys := make([]*secp256k1.PublicKey, 12)
for i := 0; i < 12; i++ { for i := 0; i < 12; i++ {
privKey, err := secp256k1.GeneratePrivateKey() prvkey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKeys[i] = privKey.PubKey() pubkeys[i] = prvkey.PubKey()
} }
closure := &tree.MultisigClosure{ closure := &tree.MultisigClosure{
PubKeys: pubKeys, PubKeys: pubkeys,
} }
// Generate script // Generate script
@@ -169,7 +169,7 @@ func TestMultisigClosure(t *testing.T) {
// Compare all serialized pubkeys // Compare all serialized pubkeys
for i := 0; i < 12; i++ { for i := 0; i < 12; i++ {
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKeys[i]), schnorr.SerializePubKey(pubkeys[i]),
schnorr.SerializePubKey(decodedClosure.PubKeys[i]), schnorr.SerializePubKey(decodedClosure.PubKeys[i]),
) )
} }
@@ -181,18 +181,18 @@ func TestMultisigClosure(t *testing.T) {
func TestCSVSigClosure(t *testing.T) { func TestCSVSigClosure(t *testing.T) {
// Generate test keys // Generate test keys
privKey1, err := secp256k1.GeneratePrivateKey() prvkey1, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKey1 := privKey1.PubKey() pubkey1 := prvkey1.PubKey()
privKey2, err := secp256k1.GeneratePrivateKey() prvkey2, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKey2 := privKey2.PubKey() pubkey2 := prvkey2.PubKey()
t.Run("valid single key CSV", func(t *testing.T) { t.Run("valid single key CSV", func(t *testing.T) {
csvSig := &tree.CSVSigClosure{ csvSig := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{ MultisigClosure: tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1}, PubKeys: []*secp256k1.PublicKey{pubkey1},
}, },
Seconds: 1024, Seconds: 1024,
} }
@@ -207,7 +207,7 @@ func TestCSVSigClosure(t *testing.T) {
require.Equal(t, uint32(1024), uint32(decodedCSV.Seconds)) require.Equal(t, uint32(1024), uint32(decodedCSV.Seconds))
require.Equal(t, 1, len(decodedCSV.PubKeys)) require.Equal(t, 1, len(decodedCSV.PubKeys))
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKey1), schnorr.SerializePubKey(pubkey1),
schnorr.SerializePubKey(decodedCSV.PubKeys[0]), schnorr.SerializePubKey(decodedCSV.PubKeys[0]),
) )
}) })
@@ -215,7 +215,7 @@ func TestCSVSigClosure(t *testing.T) {
t.Run("valid 2-of-2 CSV", func(t *testing.T) { t.Run("valid 2-of-2 CSV", func(t *testing.T) {
csvSig := &tree.CSVSigClosure{ csvSig := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{ MultisigClosure: tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1, pubKey2}, PubKeys: []*secp256k1.PublicKey{pubkey1, pubkey2},
}, },
Seconds: 2016, // ~2 weeks Seconds: 2016, // ~2 weeks
} }
@@ -230,11 +230,11 @@ func TestCSVSigClosure(t *testing.T) {
require.Equal(t, uint32(2016), uint32(decodedCSV.Seconds)) require.Equal(t, uint32(2016), uint32(decodedCSV.Seconds))
require.Equal(t, 2, len(decodedCSV.PubKeys)) require.Equal(t, 2, len(decodedCSV.PubKeys))
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKey1), schnorr.SerializePubKey(pubkey1),
schnorr.SerializePubKey(decodedCSV.PubKeys[0]), schnorr.SerializePubKey(decodedCSV.PubKeys[0]),
) )
require.Equal(t, require.Equal(t,
schnorr.SerializePubKey(pubKey2), schnorr.SerializePubKey(pubkey2),
schnorr.SerializePubKey(decodedCSV.PubKeys[1]), schnorr.SerializePubKey(decodedCSV.PubKeys[1]),
) )
}) })
@@ -248,9 +248,9 @@ func TestCSVSigClosure(t *testing.T) {
t.Run("invalid CSV value", func(t *testing.T) { t.Run("invalid CSV value", func(t *testing.T) {
// Create a script with invalid CSV value // Create a script with invalid CSV value
pubKeyBytes := schnorr.SerializePubKey(pubKey1) pubkeyBytes := schnorr.SerializePubKey(pubkey1)
script := []byte{txscript.OP_DATA_32} script := []byte{txscript.OP_DATA_32}
script = append(script, pubKeyBytes...) script = append(script, pubkeyBytes...)
script = append(script, txscript.OP_CHECKSIG) script = append(script, txscript.OP_CHECKSIG)
script = append(script, 0xFF) // Invalid CSV value script = append(script, 0xFF) // Invalid CSV value
@@ -263,7 +263,7 @@ func TestCSVSigClosure(t *testing.T) {
t.Run("witness size", func(t *testing.T) { t.Run("witness size", func(t *testing.T) {
csvSig := &tree.CSVSigClosure{ csvSig := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{ MultisigClosure: tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1, pubKey2}, PubKeys: []*secp256k1.PublicKey{pubkey1, pubkey2},
}, },
Seconds: 1024, Seconds: 1024,
} }
@@ -274,7 +274,7 @@ func TestCSVSigClosure(t *testing.T) {
t.Run("max timelock", func(t *testing.T) { t.Run("max timelock", func(t *testing.T) {
csvSig := &tree.CSVSigClosure{ csvSig := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{ MultisigClosure: tree.MultisigClosure{
PubKeys: []*secp256k1.PublicKey{pubKey1}, PubKeys: []*secp256k1.PublicKey{pubkey1},
}, },
Seconds: 65535, // Maximum allowed value Seconds: 65535, // Maximum allowed value
} }
@@ -438,22 +438,22 @@ func TestCSVSigClosureWitness(t *testing.T) {
func TestDecodeChecksigAdd(t *testing.T) { func TestDecodeChecksigAdd(t *testing.T) {
// Generate some test public keys // Generate some test public keys
pubKey1, err := secp256k1.GeneratePrivateKey() pubkey1, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKey2, err := secp256k1.GeneratePrivateKey() pubkey2, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKey3, err := secp256k1.GeneratePrivateKey() pubkey3, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
pubKeys := []*secp256k1.PublicKey{pubKey1.PubKey(), pubKey2.PubKey(), pubKey3.PubKey()} pubkeys := []*secp256k1.PublicKey{pubkey1.PubKey(), pubkey2.PubKey(), pubkey3.PubKey()}
// Create a script for 3-of-3 multisig using CHECKSIGADD // Create a script for 3-of-3 multisig using CHECKSIGADD
scriptBuilder := txscript.NewScriptBuilder(). scriptBuilder := txscript.NewScriptBuilder().
AddData(schnorr.SerializePubKey(pubKeys[0])). AddData(schnorr.SerializePubKey(pubkeys[0])).
AddOp(txscript.OP_CHECKSIG). AddOp(txscript.OP_CHECKSIG).
AddData(schnorr.SerializePubKey(pubKeys[1])). AddData(schnorr.SerializePubKey(pubkeys[1])).
AddOp(txscript.OP_CHECKSIGADD). AddOp(txscript.OP_CHECKSIGADD).
AddData(schnorr.SerializePubKey(pubKeys[2])). AddData(schnorr.SerializePubKey(pubkeys[2])).
AddOp(txscript.OP_CHECKSIGADD). AddOp(txscript.OP_CHECKSIGADD).
AddInt64(3). AddInt64(3).
AddOp(txscript.OP_EQUAL) AddOp(txscript.OP_EQUAL)

View File

@@ -4,9 +4,9 @@ import (
"github.com/vulpemventures/go-elements/psetv2" "github.com/vulpemventures/go-elements/psetv2"
) )
type TreeFactory func(outpoint psetv2.InputArgs) (CongestionTree, error) type TreeFactory func(outpoint psetv2.InputArgs) (VtxoTree, error)
type VtxoLeaf struct { type VtxoLeaf struct {
Pubkey string PubKey string
Amount uint64 Amount uint64
} }

View File

@@ -13,37 +13,37 @@ import (
) )
var ( var (
ErrInvalidPoolTransaction = errors.New("invalid pool transaction") ErrInvalidRoundTx = fmt.Errorf("invalid round transaction")
ErrInvalidPoolTransactionOutputs = errors.New("invalid number of outputs in pool transaction") ErrInvalidRoundTxOutputs = fmt.Errorf("invalid number of outputs in round transaction")
ErrEmptyTree = errors.New("empty congestion tree") ErrEmptyTree = fmt.Errorf("empty vtxo tree")
ErrInvalidRootLevel = errors.New("root level must have only one node") ErrInvalidRootLevel = fmt.Errorf("root level must have only one node")
ErrNoLeaves = errors.New("no leaves in the tree") ErrNoLeaves = fmt.Errorf("no leaves in the tree")
ErrNodeTransactionEmpty = errors.New("node transaction is empty") ErrNodeTxEmpty = fmt.Errorf("node transaction is empty")
ErrNodeTxidEmpty = errors.New("node txid is empty") ErrNodeTxidEmpty = fmt.Errorf("node txid is empty")
ErrNodeParentTxidEmpty = errors.New("node parent txid is empty") ErrNodeParentTxidEmpty = fmt.Errorf("node parent txid is empty")
ErrNodeTxidDifferent = errors.New("node txid differs from node transaction") ErrNodeTxidDifferent = fmt.Errorf("node txid differs from node transaction")
ErrNumberOfInputs = errors.New("node transaction should have only one input") ErrNumberOfInputs = fmt.Errorf("node transaction should have only one input")
ErrNumberOfOutputs = errors.New("node transaction should have only three or two outputs") ErrNumberOfOutputs = fmt.Errorf("node transaction should have only three or two outputs")
ErrParentTxidInput = errors.New("parent txid should be the input of the node transaction") ErrParentTxidInput = fmt.Errorf("parent txid should be the input of the node transaction")
ErrNumberOfChildren = errors.New("node branch transaction should have two children") ErrNumberOfChildren = fmt.Errorf("node branch transaction should have two children")
ErrLeafChildren = errors.New("leaf node should have max 1 child") ErrLeafChildren = fmt.Errorf("leaf node should have max 1 child")
ErrInvalidChildTxid = errors.New("invalid child txid") ErrInvalidChildTxid = fmt.Errorf("invalid child txid")
ErrNumberOfTapscripts = errors.New("input should have two tapscripts leaves") ErrNumberOfTapscripts = fmt.Errorf("input should have 1 tapscript leaf")
ErrInternalKey = errors.New("taproot internal key is not unspendable") ErrInternalKey = fmt.Errorf("invalid taproot internal key")
ErrInvalidTaprootScript = errors.New("invalid taproot script") ErrInvalidTaprootScript = fmt.Errorf("invalid taproot script")
ErrInvalidTaprootScriptLen = errors.New("invalid taproot script length (expected 32 bytes)") ErrInvalidTaprootScriptLen = fmt.Errorf("invalid taproot script length (expected 32 bytes)")
ErrInvalidLeafTaprootScript = errors.New("invalid leaf taproot script") ErrInvalidLeafTaprootScript = fmt.Errorf("invalid leaf taproot script")
ErrInvalidAmount = errors.New("children amount is different from parent amount") ErrInvalidAmount = fmt.Errorf("children amount is different from parent amount")
ErrInvalidAsset = errors.New("invalid output asset") ErrInvalidAsset = errors.New("invalid output asset")
ErrInvalidSweepSequence = errors.New("invalid sweep sequence") ErrInvalidSweepSequence = fmt.Errorf("invalid sweep sequence")
ErrInvalidASP = errors.New("invalid ASP") ErrInvalidServer = fmt.Errorf("invalid server")
ErrMissingFeeOutput = errors.New("missing fee output") ErrMissingFeeOutput = fmt.Errorf("missing fee output")
ErrInvalidLeftOutput = errors.New("invalid left output") ErrInvalidLeftOutput = fmt.Errorf("invalid left output")
ErrInvalidRightOutput = errors.New("invalid right output") ErrInvalidRightOutput = fmt.Errorf("invalid right output")
ErrMissingSweepTapscript = errors.New("missing sweep tapscript") ErrMissingSweepTapscript = fmt.Errorf("missing sweep tapscript")
ErrMissingBranchTapscript = errors.New("missing branch tapscript") ErrMissingBranchTapscript = errors.New("missing branch tapscript")
ErrInvalidLeaf = errors.New("leaf node shouldn't have children") ErrInvalidLeaf = fmt.Errorf("leaf node shouldn't have children")
ErrWrongPoolTxID = errors.New("root input should be the pool tx outpoint") ErrWrongRoundTxid = fmt.Errorf("the input of the tree root is not the round tx's shared output")
) )
// 0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 // 0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0
@@ -61,36 +61,36 @@ func UnspendableKey() *secp256k1.PublicKey {
return key return key
} }
// ValidateCongestionTree checks if the given congestion tree is valid // ValidateVtxoTree checks if the given vtxo tree is valid
// poolTxID & poolTxIndex & poolTxAmount are used to validate the root input outpoint // roundTxid & roundTxIndex & roundTxAmount are used to validate the root input outpoint
// aspPublicKey & roundLifetime are used to validate the sweep tapscript leaves // serverPubkey & roundLifetime are used to validate the sweep tapscript leaves
// besides that, the function validates: // besides that, the function validates:
// - the number of nodes // - the number of nodes
// - the number of leaves // - the number of leaves
// - children coherence with parent // - children coherence with parent
// - every control block and taproot output scripts // - every control block and taproot output scripts
// - input and output amounts // - input and output amounts
func ValidateCongestionTree( func ValidateVtxoTree(
tree CongestionTree, poolTx string, aspPublicKey *secp256k1.PublicKey, tree VtxoTree, roundTx string, serverPubkey *secp256k1.PublicKey,
roundLifetime int64, roundLifetime int64,
) error { ) error {
poolTransaction, err := psetv2.NewPsetFromBase64(poolTx) roundTransaction, err := psetv2.NewPsetFromBase64(roundTx)
if err != nil { if err != nil {
return ErrInvalidPoolTransaction return ErrInvalidRoundTx
} }
if len(poolTransaction.Outputs) < sharedOutputIndex+1 { if len(roundTransaction.Outputs) < sharedOutputIndex+1 {
return ErrInvalidPoolTransactionOutputs return ErrInvalidRoundTxOutputs
} }
poolTxAmount := poolTransaction.Outputs[sharedOutputIndex].Value roundTxAmount := roundTransaction.Outputs[sharedOutputIndex].Value
utx, err := poolTransaction.UnsignedTx() utx, err := roundTransaction.UnsignedTx()
if err != nil { if err != nil {
return ErrInvalidPoolTransaction return ErrInvalidRoundTx
} }
poolTxID := utx.TxHash().String() roundTxid := utx.TxHash().String()
nbNodes := tree.NumberOfNodes() nbNodes := tree.NumberOfNodes()
if nbNodes == 0 { if nbNodes == 0 {
@@ -101,7 +101,7 @@ func ValidateCongestionTree(
return ErrInvalidRootLevel return ErrInvalidRootLevel
} }
// check that root input is connected to the pool tx // check that root input is connected to the round tx
rootPsetB64 := tree[0][0].Tx rootPsetB64 := tree[0][0].Tx
rootPset, err := psetv2.NewPsetFromBase64(rootPsetB64) rootPset, err := psetv2.NewPsetFromBase64(rootPsetB64)
if err != nil { if err != nil {
@@ -113,9 +113,9 @@ func ValidateCongestionTree(
} }
rootInput := rootPset.Inputs[0] rootInput := rootPset.Inputs[0]
if chainhash.Hash(rootInput.PreviousTxid).String() != poolTxID || if chainhash.Hash(rootInput.PreviousTxid).String() != roundTxid ||
rootInput.PreviousTxIndex != sharedOutputIndex { rootInput.PreviousTxIndex != sharedOutputIndex {
return ErrWrongPoolTxID return ErrWrongRoundTxid
} }
sumRootValue := uint64(0) sumRootValue := uint64(0)
@@ -123,7 +123,7 @@ func ValidateCongestionTree(
sumRootValue += output.Value sumRootValue += output.Value
} }
if sumRootValue != poolTxAmount { if sumRootValue != roundTxAmount {
return ErrInvalidAmount return ErrInvalidAmount
} }
@@ -135,7 +135,7 @@ func ValidateCongestionTree(
for _, level := range tree { for _, level := range tree {
for _, node := range level { for _, node := range level {
if err := validateNodeTransaction( if err := validateNodeTransaction(
node, tree, UnspendableKey(), aspPublicKey, roundLifetime, node, tree, UnspendableKey(), serverPubkey, roundLifetime,
); err != nil { ); err != nil {
return err return err
} }
@@ -146,12 +146,12 @@ func ValidateCongestionTree(
} }
func validateNodeTransaction( func validateNodeTransaction(
node Node, tree CongestionTree, node Node, tree VtxoTree,
expectedInternalKey, expectedPublicKeyASP *secp256k1.PublicKey, expectedInternalKey, expectedServerPubkey *secp256k1.PublicKey,
expectedSequence int64, expectedSequence int64,
) error { ) error {
if node.Tx == "" { if node.Tx == "" {
return ErrNodeTransactionEmpty return ErrNodeTxEmpty
} }
if node.Txid == "" { if node.Txid == "" {
@@ -238,21 +238,21 @@ func validateNodeTransaction(
switch c := closure.(type) { switch c := closure.(type) {
case *CSVSigClosure: case *CSVSigClosure:
isASP := len(c.MultisigClosure.PubKeys) == 1 && bytes.Equal( isServer := len(c.MultisigClosure.PubKeys) == 1 && bytes.Equal(
schnorr.SerializePubKey(c.MultisigClosure.PubKeys[0]), schnorr.SerializePubKey(c.MultisigClosure.PubKeys[0]),
schnorr.SerializePubKey(expectedPublicKeyASP), schnorr.SerializePubKey(expectedServerPubkey),
) )
isSweepDelay := int64(c.Seconds) == expectedSequence isSweepDelay := int64(c.Seconds) == expectedSequence
if isASP && !isSweepDelay { if isServer && !isSweepDelay {
return ErrInvalidSweepSequence return ErrInvalidSweepSequence
} }
if isSweepDelay && !isASP { if isSweepDelay && !isServer {
return ErrInvalidASP return ErrInvalidServer
} }
if isASP && isSweepDelay { if isServer && isSweepDelay {
sweepLeafFound = true sweepLeafFound = true
} }
case *UnrollClosure: case *UnrollClosure:

View File

@@ -26,14 +26,14 @@ func ParseVtxoScript(scripts []string) (VtxoScript, error) {
return v, err return v, err
} }
func NewDefaultVtxoScript(owner, asp *secp256k1.PublicKey, exitDelay uint) *TapscriptsVtxoScript { func NewDefaultVtxoScript(owner, server *secp256k1.PublicKey, exitDelay uint) *TapscriptsVtxoScript {
return &TapscriptsVtxoScript{ return &TapscriptsVtxoScript{
[]Closure{ []Closure{
&CSVSigClosure{ &CSVSigClosure{
MultisigClosure: MultisigClosure{PubKeys: []*secp256k1.PublicKey{owner}}, MultisigClosure: MultisigClosure{PubKeys: []*secp256k1.PublicKey{owner}},
Seconds: exitDelay, Seconds: exitDelay,
}, },
&MultisigClosure{PubKeys: []*secp256k1.PublicKey{owner, asp}}, &MultisigClosure{PubKeys: []*secp256k1.PublicKey{owner, server}},
}, },
} }
} }
@@ -73,19 +73,19 @@ func (v *TapscriptsVtxoScript) Decode(scripts []string) error {
return nil return nil
} }
func (v *TapscriptsVtxoScript) Validate(asp *secp256k1.PublicKey, minExitDelay uint) error { func (v *TapscriptsVtxoScript) Validate(server *secp256k1.PublicKey, minExitDelay uint) error {
aspXonly := schnorr.SerializePubKey(asp) serverXonly := schnorr.SerializePubKey(server)
for _, forfeit := range v.ForfeitClosures() { for _, forfeit := range v.ForfeitClosures() {
// must contain asp pubkey // must contain server pubkey
found := false found := false
for _, pubkey := range forfeit.PubKeys { for _, pubkey := range forfeit.PubKeys {
if bytes.Equal(schnorr.SerializePubKey(pubkey), aspXonly) { if bytes.Equal(schnorr.SerializePubKey(pubkey), serverXonly) {
found = true found = true
break break
} }
} }
if !found { if !found {
return fmt.Errorf("invalid forfeit closure, ASP pubkey not found") return fmt.Errorf("invalid forfeit closure, server pubkey not found")
} }
} }

View File

@@ -26,7 +26,7 @@ type TaprootTree interface {
} }
/* /*
A vtxo script is defined as a taproot contract with at least 1 forfeit closure (User && ASP) and 1 exit closure (A after t). A vtxo script is defined as a taproot contract with at least 1 forfeit closure (User && Server) and 1 exit closure (A after t).
It may also contain others closures implementing specific use cases. It may also contain others closures implementing specific use cases.
VtxoScript abstracts the taproot complexity behind vtxo contracts. VtxoScript abstracts the taproot complexity behind vtxo contracts.

View File

@@ -24,7 +24,6 @@ type ArkClient interface {
CollaborativeRedeem( CollaborativeRedeem(
ctx context.Context, addr string, amount uint64, withExpiryCoinselect bool, ctx context.Context, addr string, amount uint64, withExpiryCoinselect bool,
) (string, error) ) (string, error)
SendAsync(ctx context.Context, withExpiryCoinselect bool, receivers []Receiver) (string, error)
Settle(ctx context.Context) (string, error) Settle(ctx context.Context) (string, error)
ListVtxos(ctx context.Context) (spendable, spent []client.Vtxo, err error) ListVtxos(ctx context.Context) (spendable, spent []client.Vtxo, err error)
Dump(ctx context.Context) (seed string, err error) Dump(ctx context.Context) (seed string, err error)

View File

@@ -57,7 +57,7 @@ type arkClient struct {
wallet wallet.WalletService wallet wallet.WalletService
store types.Store store types.Store
explorer explorer.Explorer explorer explorer.Explorer
client client.ASPClient client client.TransportClient
txStreamCtxCancel context.CancelFunc txStreamCtxCancel context.CancelFunc
} }
@@ -119,7 +119,7 @@ func (a *arkClient) initWithWallet(
} }
clientSvc, err := getClient( clientSvc, err := getClient(
supportedClients, args.ClientType, args.AspUrl, supportedClients, args.ClientType, args.ServerUrl,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to setup client: %s", err) return fmt.Errorf("failed to setup client: %s", err)
@@ -127,7 +127,7 @@ func (a *arkClient) initWithWallet(
info, err := clientSvc.GetInfo(ctx) info, err := clientSvc.GetInfo(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to connect to asp: %s", err) return fmt.Errorf("failed to connect to server: %s", err)
} }
explorerSvc, err := getExplorer(args.ExplorerURL, info.Network) explorerSvc, err := getExplorer(args.ExplorerURL, info.Network)
@@ -137,18 +137,18 @@ func (a *arkClient) initWithWallet(
network := utils.NetworkFromString(info.Network) network := utils.NetworkFromString(info.Network)
buf, err := hex.DecodeString(info.Pubkey) buf, err := hex.DecodeString(info.PubKey)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse asp pubkey: %s", err) return fmt.Errorf("failed to parse server pubkey: %s", err)
} }
aspPubkey, err := secp256k1.ParsePubKey(buf) serverPubkey, err := secp256k1.ParsePubKey(buf)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse asp pubkey: %s", err) return fmt.Errorf("failed to parse server pubkey: %s", err)
} }
storeData := types.Config{ storeData := types.Config{
AspUrl: args.AspUrl, ServerUrl: args.ServerUrl,
AspPubkey: aspPubkey, ServerPubKey: serverPubkey,
WalletType: args.Wallet.GetType(), WalletType: args.Wallet.GetType(),
ClientType: args.ClientType, ClientType: args.ClientType,
Network: network, Network: network,
@@ -186,7 +186,7 @@ func (a *arkClient) init(
} }
clientSvc, err := getClient( clientSvc, err := getClient(
supportedClients, args.ClientType, args.AspUrl, supportedClients, args.ClientType, args.ServerUrl,
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to setup client: %s", err) return fmt.Errorf("failed to setup client: %s", err)
@@ -194,7 +194,7 @@ func (a *arkClient) init(
info, err := clientSvc.GetInfo(ctx) info, err := clientSvc.GetInfo(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to connect to asp: %s", err) return fmt.Errorf("failed to connect to server: %s", err)
} }
explorerSvc, err := getExplorer(args.ExplorerURL, info.Network) explorerSvc, err := getExplorer(args.ExplorerURL, info.Network)
@@ -204,18 +204,18 @@ func (a *arkClient) init(
network := utils.NetworkFromString(info.Network) network := utils.NetworkFromString(info.Network)
buf, err := hex.DecodeString(info.Pubkey) buf, err := hex.DecodeString(info.PubKey)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse asp pubkey: %s", err) return fmt.Errorf("failed to parse server pubkey: %s", err)
} }
aspPubkey, err := secp256k1.ParsePubKey(buf) serverPubkey, err := secp256k1.ParsePubKey(buf)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse asp pubkey: %s", err) return fmt.Errorf("failed to parse server pubkey: %s", err)
} }
cfgData := types.Config{ cfgData := types.Config{
AspUrl: args.AspUrl, ServerUrl: args.ServerUrl,
AspPubkey: aspPubkey, ServerPubKey: serverPubkey,
WalletType: args.WalletType, WalletType: args.WalletType,
ClientType: args.ClientType, ClientType: args.ClientType,
Network: network, Network: network,
@@ -252,17 +252,17 @@ func (a *arkClient) init(
} }
func (a *arkClient) ping( func (a *arkClient) ping(
ctx context.Context, paymentID string, ctx context.Context, requestID string,
) func() { ) func() {
ticker := time.NewTicker(5 * time.Second) ticker := time.NewTicker(5 * time.Second)
go func(t *time.Ticker) { go func(t *time.Ticker) {
if err := a.client.Ping(ctx, paymentID); err != nil { if err := a.client.Ping(ctx, requestID); err != nil {
logrus.Warnf("failed to ping asp: %s", err) logrus.Warnf("failed to ping server: %s", err)
} }
for range t.C { for range t.C {
if err := a.client.Ping(ctx, paymentID); err != nil { if err := a.client.Ping(ctx, requestID); err != nil {
logrus.Warnf("failed to ping asp: %s", err) logrus.Warnf("failed to ping server: %s", err)
} }
} }
}(ticker) }(ticker)
@@ -292,10 +292,10 @@ func (a *arkClient) ListVtxos(
} }
func getClient( func getClient(
supportedClients utils.SupportedType[utils.ClientFactory], clientType, aspUrl string, supportedClients utils.SupportedType[utils.ClientFactory], clientType, serverUrl string,
) (client.ASPClient, error) { ) (client.TransportClient, error) {
factory := supportedClients[clientType] factory := supportedClients[clientType]
return factory(aspUrl) return factory(serverUrl)
} }
func getExplorer(explorerURL, network string) (explorer.Explorer, error) { func getExplorer(explorerURL, network string) (explorer.Explorer, error) {

View File

@@ -22,7 +22,7 @@ type RoundEvent interface {
isRoundEvent() isRoundEvent()
} }
type ASPClient interface { type TransportClient interface {
GetInfo(ctx context.Context) (*Info, error) GetInfo(ctx context.Context) (*Info, error)
RegisterInputsForNextRound( RegisterInputsForNextRound(
ctx context.Context, inputs []Input, ephemeralKey string, ctx context.Context, inputs []Input, ephemeralKey string,
@@ -31,7 +31,7 @@ type ASPClient interface {
ctx context.Context, notes []string, ephemeralKey string, ctx context.Context, notes []string, ephemeralKey string,
) (string, error) ) (string, error)
RegisterOutputsForNextRound( RegisterOutputsForNextRound(
ctx context.Context, paymentID string, outputs []Output, ctx context.Context, requestID string, outputs []Output,
) error ) error
SubmitTreeNonces( SubmitTreeNonces(
ctx context.Context, roundID, cosignerPubkey string, nonces bitcointree.TreeNonces, ctx context.Context, roundID, cosignerPubkey string, nonces bitcointree.TreeNonces,
@@ -43,9 +43,9 @@ type ASPClient interface {
ctx context.Context, signedForfeitTxs []string, signedRoundTx string, ctx context.Context, signedForfeitTxs []string, signedRoundTx string,
) error ) error
GetEventStream( GetEventStream(
ctx context.Context, paymentID string, ctx context.Context, requestID string,
) (<-chan RoundEventChannel, func(), error) ) (<-chan RoundEventChannel, func(), error)
Ping(ctx context.Context, paymentID string) error Ping(ctx context.Context, requestID string) error
SubmitRedeemTx( SubmitRedeemTx(
ctx context.Context, signedRedeemTx string, ctx context.Context, signedRedeemTx string,
) (string, error) ) (string, error)
@@ -59,7 +59,7 @@ type ASPClient interface {
} }
type Info struct { type Info struct {
Pubkey string PubKey string
RoundLifetime int64 RoundLifetime int64
UnilateralExitDelay int64 UnilateralExitDelay int64
RoundInterval int64 RoundInterval int64
@@ -90,7 +90,7 @@ type Input struct {
type Vtxo struct { type Vtxo struct {
Outpoint Outpoint
Pubkey string PubKey string
Amount uint64 Amount uint64
RoundTxid string RoundTxid string
ExpiresAt time.Time ExpiresAt time.Time
@@ -100,8 +100,8 @@ type Vtxo struct {
SpentBy string SpentBy string
} }
func (v Vtxo) Address(asp *secp256k1.PublicKey, net common.Network) (string, error) { func (v Vtxo) Address(server *secp256k1.PublicKey, net common.Network) (string, error) {
pubkeyBytes, err := hex.DecodeString(v.Pubkey) pubkeyBytes, err := hex.DecodeString(v.PubKey)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -113,7 +113,7 @@ func (v Vtxo) Address(asp *secp256k1.PublicKey, net common.Network) (string, err
a := &common.Address{ a := &common.Address{
HRP: net.Addr, HRP: net.Addr,
Asp: asp, Server: server,
VtxoTapKey: pubkey, VtxoTapKey: pubkey,
} }
@@ -160,7 +160,7 @@ type Round struct {
StartedAt *time.Time StartedAt *time.Time
EndedAt *time.Time EndedAt *time.Time
Tx string Tx string
Tree tree.CongestionTree Tree tree.VtxoTree
ForfeitTxs []string ForfeitTxs []string
Connectors []string Connectors []string
Stage RoundStage Stage RoundStage
@@ -169,7 +169,7 @@ type Round struct {
type RoundFinalizationEvent struct { type RoundFinalizationEvent struct {
ID string ID string
Tx string Tx string
Tree tree.CongestionTree Tree tree.VtxoTree
Connectors []string Connectors []string
MinRelayFeeRate chainfee.SatPerKVByte MinRelayFeeRate chainfee.SatPerKVByte
} }
@@ -192,8 +192,8 @@ func (e RoundFailedEvent) isRoundEvent() {}
type RoundSigningStartedEvent struct { type RoundSigningStartedEvent struct {
ID string ID string
UnsignedTree tree.CongestionTree UnsignedTree tree.VtxoTree
CosignersPublicKeys []*secp256k1.PublicKey CosignersPubKeys []*secp256k1.PublicKey
UnsignedRoundTx string UnsignedRoundTx string
} }

View File

@@ -23,31 +23,31 @@ import (
type grpcClient struct { type grpcClient struct {
conn *grpc.ClientConn conn *grpc.ClientConn
svc arkv1.ArkServiceClient svc arkv1.ArkServiceClient
treeCache *utils.Cache[tree.CongestionTree] treeCache *utils.Cache[tree.VtxoTree]
} }
func NewClient(aspUrl string) (client.ASPClient, error) { func NewClient(serverUrl string) (client.TransportClient, error) {
if len(aspUrl) <= 0 { if len(serverUrl) <= 0 {
return nil, fmt.Errorf("missing asp url") return nil, fmt.Errorf("missing server url")
} }
creds := insecure.NewCredentials() creds := insecure.NewCredentials()
port := 80 port := 80
if strings.HasPrefix(aspUrl, "https://") { if strings.HasPrefix(serverUrl, "https://") {
aspUrl = strings.TrimPrefix(aspUrl, "https://") serverUrl = strings.TrimPrefix(serverUrl, "https://")
creds = credentials.NewTLS(nil) creds = credentials.NewTLS(nil)
port = 443 port = 443
} }
if !strings.Contains(aspUrl, ":") { if !strings.Contains(serverUrl, ":") {
aspUrl = fmt.Sprintf("%s:%d", aspUrl, port) serverUrl = fmt.Sprintf("%s:%d", serverUrl, port)
} }
conn, err := grpc.NewClient(aspUrl, grpc.WithTransportCredentials(creds)) conn, err := grpc.NewClient(serverUrl, grpc.WithTransportCredentials(creds))
if err != nil { if err != nil {
return nil, err return nil, err
} }
svc := arkv1.NewArkServiceClient(conn) svc := arkv1.NewArkServiceClient(conn)
treeCache := utils.NewCache[tree.CongestionTree]() treeCache := utils.NewCache[tree.VtxoTree]()
return &grpcClient{conn, svc, treeCache}, nil return &grpcClient{conn, svc, treeCache}, nil
} }
@@ -59,7 +59,7 @@ func (a *grpcClient) GetInfo(ctx context.Context) (*client.Info, error) {
return nil, err return nil, err
} }
return &client.Info{ return &client.Info{
Pubkey: resp.GetPubkey(), PubKey: resp.GetPubkey(),
RoundLifetime: resp.GetRoundLifetime(), RoundLifetime: resp.GetRoundLifetime(),
UnilateralExitDelay: resp.GetUnilateralExitDelay(), UnilateralExitDelay: resp.GetUnilateralExitDelay(),
RoundInterval: resp.GetRoundInterval(), RoundInterval: resp.GetRoundInterval(),
@@ -84,20 +84,20 @@ func (a *grpcClient) GetBoardingAddress(
} }
func (a *grpcClient) RegisterInputsForNextRound( func (a *grpcClient) RegisterInputsForNextRound(
ctx context.Context, inputs []client.Input, ephemeralPublicKey string, ctx context.Context, inputs []client.Input, ephemeralPubkey string,
) (string, error) { ) (string, error) {
req := &arkv1.RegisterInputsForNextRoundRequest{ req := &arkv1.RegisterInputsForNextRoundRequest{
Inputs: ins(inputs).toProto(), Inputs: ins(inputs).toProto(),
} }
if len(ephemeralPublicKey) > 0 { if len(ephemeralPubkey) > 0 {
req.EphemeralPubkey = &ephemeralPublicKey req.EphemeralPubkey = &ephemeralPubkey
} }
resp, err := a.svc.RegisterInputsForNextRound(ctx, req) resp, err := a.svc.RegisterInputsForNextRound(ctx, req)
if err != nil { if err != nil {
return "", err return "", err
} }
return resp.GetId(), nil return resp.GetRequestId(), nil
} }
func (a *grpcClient) RegisterNotesForNextRound( func (a *grpcClient) RegisterNotesForNextRound(
@@ -113,14 +113,14 @@ func (a *grpcClient) RegisterNotesForNextRound(
if err != nil { if err != nil {
return "", err return "", err
} }
return resp.GetId(), nil return resp.GetRequestId(), nil
} }
func (a *grpcClient) RegisterOutputsForNextRound( func (a *grpcClient) RegisterOutputsForNextRound(
ctx context.Context, paymentID string, outputs []client.Output, ctx context.Context, requestID string, outputs []client.Output,
) error { ) error {
req := &arkv1.RegisterOutputsForNextRoundRequest{ req := &arkv1.RegisterOutputsForNextRoundRequest{
Id: paymentID, RequestId: requestID,
Outputs: outs(outputs).toProto(), Outputs: outs(outputs).toProto(),
} }
_, err := a.svc.RegisterOutputsForNextRound(ctx, req) _, err := a.svc.RegisterOutputsForNextRound(ctx, req)
@@ -191,7 +191,7 @@ func (a *grpcClient) SubmitSignedForfeitTxs(
} }
func (a *grpcClient) GetEventStream( func (a *grpcClient) GetEventStream(
ctx context.Context, paymentID string, ctx context.Context, requestID string,
) (<-chan client.RoundEventChannel, func(), error) { ) (<-chan client.RoundEventChannel, func(), error) {
req := &arkv1.GetEventStreamRequest{} req := &arkv1.GetEventStreamRequest{}
stream, err := a.svc.GetEventStream(ctx, req) stream, err := a.svc.GetEventStream(ctx, req)
@@ -236,10 +236,10 @@ func (a *grpcClient) GetEventStream(
} }
func (a *grpcClient) Ping( func (a *grpcClient) Ping(
ctx context.Context, paymentID string, ctx context.Context, requestID string,
) error { ) error {
req := &arkv1.PingRequest{ req := &arkv1.PingRequest{
PaymentId: paymentID, RequestId: requestID,
} }
_, err := a.svc.Ping(ctx, req) _, err := a.svc.Ping(ctx, req)
return err return err

View File

@@ -88,7 +88,7 @@ func (e event) toRoundEvent() (client.RoundEvent, error) {
return client.RoundSigningStartedEvent{ return client.RoundSigningStartedEvent{
ID: ee.GetId(), ID: ee.GetId(),
UnsignedTree: treeFromProto{ee.GetUnsignedVtxoTree()}.parse(), UnsignedTree: treeFromProto{ee.GetUnsignedVtxoTree()}.parse(),
CosignersPublicKeys: pubkeys, CosignersPubKeys: pubkeys,
UnsignedRoundTx: ee.GetUnsignedRoundTx(), UnsignedRoundTx: ee.GetUnsignedRoundTx(),
}, nil }, nil
} }
@@ -123,7 +123,7 @@ func (v vtxo) toVtxo() client.Vtxo {
IsPending: v.GetIsPending(), IsPending: v.GetIsPending(),
RedeemTx: v.GetRedeemTx(), RedeemTx: v.GetRedeemTx(),
SpentBy: v.GetSpentBy(), SpentBy: v.GetSpentBy(),
Pubkey: v.GetPubkey(), PubKey: v.GetPubkey(),
CreatedAt: time.Unix(v.GetCreatedAt(), 0), CreatedAt: time.Unix(v.GetCreatedAt(), 0),
} }
} }
@@ -166,8 +166,8 @@ type treeFromProto struct {
*arkv1.Tree *arkv1.Tree
} }
func (t treeFromProto) parse() tree.CongestionTree { func (t treeFromProto) parse() tree.VtxoTree {
levels := make(tree.CongestionTree, 0, len(t.GetLevels())) levels := make(tree.VtxoTree, 0, len(t.GetLevels()))
for _, level := range t.GetLevels() { for _, level := range t.GetLevels() {
nodes := make([]tree.Node, 0, len(level.Nodes)) nodes := make([]tree.Node, 0, len(level.Nodes))

View File

@@ -32,12 +32,12 @@ type restClient struct {
serverURL string serverURL string
svc ark_service.ClientService svc ark_service.ClientService
requestTimeout time.Duration requestTimeout time.Duration
treeCache *utils.Cache[tree.CongestionTree] treeCache *utils.Cache[tree.VtxoTree]
} }
func NewClient(serverURL string) (client.ASPClient, error) { func NewClient(serverURL string) (client.TransportClient, error) {
if len(serverURL) <= 0 { if len(serverURL) <= 0 {
return nil, fmt.Errorf("missing asp url") return nil, fmt.Errorf("missing server url")
} }
svc, err := newRestClient(serverURL) svc, err := newRestClient(serverURL)
if err != nil { if err != nil {
@@ -45,7 +45,7 @@ func NewClient(serverURL string) (client.ASPClient, error) {
} }
// TODO: use twice the round interval. // TODO: use twice the round interval.
reqTimeout := 15 * time.Second reqTimeout := 15 * time.Second
treeCache := utils.NewCache[tree.CongestionTree]() treeCache := utils.NewCache[tree.VtxoTree]()
return &restClient{serverURL, svc, reqTimeout, treeCache}, nil return &restClient{serverURL, svc, reqTimeout, treeCache}, nil
} }
@@ -79,7 +79,7 @@ func (a *restClient) GetInfo(
} }
return &client.Info{ return &client.Info{
Pubkey: resp.Payload.Pubkey, PubKey: resp.Payload.Pubkey,
RoundLifetime: int64(roundLifetime), RoundLifetime: int64(roundLifetime),
UnilateralExitDelay: int64(unilateralExitDelay), UnilateralExitDelay: int64(unilateralExitDelay),
RoundInterval: int64(roundInterval), RoundInterval: int64(roundInterval),
@@ -108,7 +108,7 @@ func (a *restClient) GetBoardingAddress(
} }
func (a *restClient) RegisterInputsForNextRound( func (a *restClient) RegisterInputsForNextRound(
ctx context.Context, inputs []client.Input, ephemeralPublicKey string, ctx context.Context, inputs []client.Input, ephemeralPubkey string,
) (string, error) { ) (string, error) {
ins := make([]*models.V1Input, 0, len(inputs)) ins := make([]*models.V1Input, 0, len(inputs))
for _, i := range inputs { for _, i := range inputs {
@@ -125,8 +125,8 @@ func (a *restClient) RegisterInputsForNextRound(
body := &models.V1RegisterInputsForNextRoundRequest{ body := &models.V1RegisterInputsForNextRoundRequest{
Inputs: ins, Inputs: ins,
} }
if len(ephemeralPublicKey) > 0 { if len(ephemeralPubkey) > 0 {
body.EphemeralPubkey = ephemeralPublicKey body.EphemeralPubkey = ephemeralPubkey
} }
resp, err := a.svc.ArkServiceRegisterInputsForNextRound( resp, err := a.svc.ArkServiceRegisterInputsForNextRound(
@@ -136,7 +136,7 @@ func (a *restClient) RegisterInputsForNextRound(
return "", err return "", err
} }
return resp.Payload.ID, nil return resp.Payload.RequestID, nil
} }
func (a *restClient) RegisterNotesForNextRound( func (a *restClient) RegisterNotesForNextRound(
@@ -154,11 +154,11 @@ func (a *restClient) RegisterNotesForNextRound(
if err != nil { if err != nil {
return "", err return "", err
} }
return resp.Payload.ID, nil return resp.Payload.RequestID, nil
} }
func (a *restClient) RegisterOutputsForNextRound( func (a *restClient) RegisterOutputsForNextRound(
ctx context.Context, paymentID string, outputs []client.Output, ctx context.Context, requestID string, outputs []client.Output,
) error { ) error {
outs := make([]*models.V1Output, 0, len(outputs)) outs := make([]*models.V1Output, 0, len(outputs))
for _, o := range outputs { for _, o := range outputs {
@@ -168,7 +168,7 @@ func (a *restClient) RegisterOutputsForNextRound(
}) })
} }
body := models.V1RegisterOutputsForNextRoundRequest{ body := models.V1RegisterOutputsForNextRoundRequest{
ID: paymentID, RequestID: requestID,
Outputs: outs, Outputs: outs,
} }
@@ -246,7 +246,7 @@ func (a *restClient) SubmitSignedForfeitTxs(
} }
func (c *restClient) GetEventStream( func (c *restClient) GetEventStream(
ctx context.Context, paymentID string, ctx context.Context, requestID string,
) (<-chan client.RoundEventChannel, func(), error) { ) (<-chan client.RoundEventChannel, func(), error) {
eventsCh := make(chan client.RoundEventChannel) eventsCh := make(chan client.RoundEventChannel)
@@ -356,7 +356,7 @@ func (c *restClient) GetEventStream(
event = client.RoundSigningStartedEvent{ event = client.RoundSigningStartedEvent{
ID: e.ID, ID: e.ID,
UnsignedTree: treeFromProto{e.UnsignedVtxoTree}.parse(), UnsignedTree: treeFromProto{e.UnsignedVtxoTree}.parse(),
CosignersPublicKeys: pubkeys, CosignersPubKeys: pubkeys,
UnsignedRoundTx: e.UnsignedRoundTx, UnsignedRoundTx: e.UnsignedRoundTx,
} }
case resp.Result.RoundSigningNoncesGenerated != nil: case resp.Result.RoundSigningNoncesGenerated != nil:
@@ -384,10 +384,10 @@ func (c *restClient) GetEventStream(
} }
func (a *restClient) Ping( func (a *restClient) Ping(
ctx context.Context, paymentID string, ctx context.Context, requestID string,
) error { ) error {
r := ark_service.NewArkServicePingParams() r := ark_service.NewArkServicePingParams()
r.SetPaymentID(paymentID) r.SetRequestID(requestID)
_, err := a.svc.ArkServicePing(r) _, err := a.svc.ArkServicePing(r)
return err return err
} }
@@ -573,8 +573,8 @@ type treeFromProto struct {
*models.V1Tree *models.V1Tree
} }
func (t treeFromProto) parse() tree.CongestionTree { func (t treeFromProto) parse() tree.VtxoTree {
congestionTree := make(tree.CongestionTree, 0, len(t.Levels)) vtxoTree := make(tree.VtxoTree, 0, len(t.Levels))
for _, l := range t.Levels { for _, l := range t.Levels {
level := make([]tree.Node, 0, len(l.Nodes)) level := make([]tree.Node, 0, len(l.Nodes))
for _, n := range l.Nodes { for _, n := range l.Nodes {
@@ -584,13 +584,13 @@ func (t treeFromProto) parse() tree.CongestionTree {
ParentTxid: n.ParentTxid, ParentTxid: n.ParentTxid,
}) })
} }
congestionTree = append(congestionTree, level) vtxoTree = append(vtxoTree, level)
} }
for j, treeLvl := range congestionTree { for j, treeLvl := range vtxoTree {
for i, node := range treeLvl { for i, node := range treeLvl {
if len(congestionTree.Children(node.Txid)) == 0 { if len(vtxoTree.Children(node.Txid)) == 0 {
congestionTree[j][i] = tree.Node{ vtxoTree[j][i] = tree.Node{
Txid: node.Txid, Txid: node.Txid,
Tx: node.Tx, Tx: node.Tx,
ParentTxid: node.ParentTxid, ParentTxid: node.ParentTxid,
@@ -600,7 +600,7 @@ func (t treeFromProto) parse() tree.CongestionTree {
} }
} }
return congestionTree return vtxoTree
} }
func (c *restClient) GetTransactionsStream(ctx context.Context) (<-chan client.TransactionEvent, func(), error) { func (c *restClient) GetTransactionsStream(ctx context.Context) (<-chan client.TransactionEvent, func(), error) {
@@ -725,7 +725,7 @@ func vtxosFromRest(restVtxos []*models.V1Vtxo) []client.Vtxo {
Txid: v.Outpoint.Txid, Txid: v.Outpoint.Txid,
VOut: uint32(v.Outpoint.Vout), VOut: uint32(v.Outpoint.Vout),
}, },
Pubkey: v.Pubkey, PubKey: v.Pubkey,
Amount: uint64(amount), Amount: uint64(amount),
RoundTxid: v.RoundTxid, RoundTxid: v.RoundTxid,
ExpiresAt: expiresAt, ExpiresAt: expiresAt,

View File

@@ -396,7 +396,7 @@ func (a *Client) ArkServicePing(params *ArkServicePingParams, opts ...ClientOpti
op := &runtime.ClientOperation{ op := &runtime.ClientOperation{
ID: "ArkService_Ping", ID: "ArkService_Ping",
Method: "GET", Method: "GET",
PathPattern: "/v1/round/ping/{paymentId}", PathPattern: "/v1/round/ping/{requestId}",
ProducesMediaTypes: []string{"application/json"}, ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"},
Schemes: []string{"http"}, Schemes: []string{"http"},

View File

@@ -61,8 +61,11 @@ ArkServicePingParams contains all the parameters to send to the API endpoint
*/ */
type ArkServicePingParams struct { type ArkServicePingParams struct {
// PaymentID. /* RequestID.
PaymentID string
The id used to register inputs and ouptuts.
*/
RequestID string
timeout time.Duration timeout time.Duration
Context context.Context Context context.Context
@@ -117,15 +120,15 @@ func (o *ArkServicePingParams) SetHTTPClient(client *http.Client) {
o.HTTPClient = client o.HTTPClient = client
} }
// WithPaymentID adds the paymentID to the ark service ping params // WithRequestID adds the requestID to the ark service ping params
func (o *ArkServicePingParams) WithPaymentID(paymentID string) *ArkServicePingParams { func (o *ArkServicePingParams) WithRequestID(requestID string) *ArkServicePingParams {
o.SetPaymentID(paymentID) o.SetRequestID(requestID)
return o return o
} }
// SetPaymentID adds the paymentId to the ark service ping params // SetRequestID adds the requestId to the ark service ping params
func (o *ArkServicePingParams) SetPaymentID(paymentID string) { func (o *ArkServicePingParams) SetRequestID(requestID string) {
o.PaymentID = paymentID o.RequestID = requestID
} }
// WriteToRequest writes these params to a swagger request // WriteToRequest writes these params to a swagger request
@@ -136,8 +139,8 @@ func (o *ArkServicePingParams) WriteToRequest(r runtime.ClientRequest, reg strfm
} }
var res []error var res []error
// path param paymentId // path param requestId
if err := r.SetPathParam("paymentId", o.PaymentID); err != nil { if err := r.SetPathParam("requestId", o.RequestID); err != nil {
return err return err
} }

View File

@@ -88,12 +88,12 @@ func (o *ArkServicePingOK) Code() int {
func (o *ArkServicePingOK) Error() string { func (o *ArkServicePingOK) Error() string {
payload, _ := json.Marshal(o.Payload) payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/round/ping/{paymentId}][%d] arkServicePingOK %s", 200, payload) return fmt.Sprintf("[GET /v1/round/ping/{requestId}][%d] arkServicePingOK %s", 200, payload)
} }
func (o *ArkServicePingOK) String() string { func (o *ArkServicePingOK) String() string {
payload, _ := json.Marshal(o.Payload) payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/round/ping/{paymentId}][%d] arkServicePingOK %s", 200, payload) return fmt.Sprintf("[GET /v1/round/ping/{requestId}][%d] arkServicePingOK %s", 200, payload)
} }
func (o *ArkServicePingOK) GetPayload() models.V1PingResponse { func (o *ArkServicePingOK) GetPayload() models.V1PingResponse {
@@ -160,12 +160,12 @@ func (o *ArkServicePingDefault) Code() int {
func (o *ArkServicePingDefault) Error() string { func (o *ArkServicePingDefault) Error() string {
payload, _ := json.Marshal(o.Payload) payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/round/ping/{paymentId}][%d] ArkService_Ping default %s", o._statusCode, payload) return fmt.Sprintf("[GET /v1/round/ping/{requestId}][%d] ArkService_Ping default %s", o._statusCode, payload)
} }
func (o *ArkServicePingDefault) String() string { func (o *ArkServicePingDefault) String() string {
payload, _ := json.Marshal(o.Payload) payload, _ := json.Marshal(o.Payload)
return fmt.Sprintf("[GET /v1/round/ping/{paymentId}][%d] ArkService_Ping default %s", o._statusCode, payload) return fmt.Sprintf("[GET /v1/round/ping/{requestId}][%d] ArkService_Ping default %s", o._statusCode, payload)
} }
func (o *ArkServicePingDefault) GetPayload() *models.RPCStatus { func (o *ArkServicePingDefault) GetPayload() *models.RPCStatus {

View File

@@ -12,7 +12,7 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
// V1OwnershipProof This message is used to prove to the ASP that the user controls the vtxo without revealing the whole VTXO taproot tree. // V1OwnershipProof This message is used to prove to the server that the user controls the vtxo without revealing the whole VTXO taproot tree.
// //
// swagger:model v1OwnershipProof // swagger:model v1OwnershipProof
type V1OwnershipProof struct { type V1OwnershipProof struct {

View File

@@ -17,8 +17,8 @@ import (
// swagger:model v1RegisterInputsForNextRoundResponse // swagger:model v1RegisterInputsForNextRoundResponse
type V1RegisterInputsForNextRoundResponse struct { type V1RegisterInputsForNextRoundResponse struct {
// Mocks wabisabi's blinded credentials. // request Id
ID string `json:"id,omitempty"` RequestID string `json:"requestId,omitempty"`
} }
// Validate validates this v1 register inputs for next round response // Validate validates this v1 register inputs for next round response

View File

@@ -19,11 +19,11 @@ import (
// swagger:model v1RegisterOutputsForNextRoundRequest // swagger:model v1RegisterOutputsForNextRoundRequest
type V1RegisterOutputsForNextRoundRequest struct { type V1RegisterOutputsForNextRoundRequest struct {
// Mocks wabisabi's blinded credentials. // List of receivers for to convert to leaves in the next VTXO tree.
ID string `json:"id,omitempty"`
// List of receivers for a registered payment.
Outputs []*V1Output `json:"outputs"` Outputs []*V1Output `json:"outputs"`
// request Id
RequestID string `json:"requestId,omitempty"`
} }
// Validate validates this v1 register outputs for next round request // Validate validates this v1 register outputs for next round request

View File

@@ -20,7 +20,7 @@ type V1SubmitSignedForfeitTxsRequest struct {
// Forfeit txs signed by the user. // Forfeit txs signed by the user.
SignedForfeitTxs []string `json:"signedForfeitTxs"` SignedForfeitTxs []string `json:"signedForfeitTxs"`
// If payment has boarding input, the user must sign the associated inputs. // The user has to sign also the round tx if he registerd a boarding UTXO.
SignedRoundTx string `json:"signedRoundTx,omitempty"` SignedRoundTx string `json:"signedRoundTx,omitempty"`
} }

View File

@@ -87,7 +87,7 @@ func LoadCovenantClient(sdkStore types.Store) (ArkClient, error) {
} }
clientSvc, err := getClient( clientSvc, err := getClient(
supportedClients, cfgData.ClientType, cfgData.AspUrl, supportedClients, cfgData.ClientType, cfgData.ServerUrl,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup transport client: %s", err) return nil, fmt.Errorf("failed to setup transport client: %s", err)
@@ -147,7 +147,7 @@ func LoadCovenantClientWithWallet(
} }
clientSvc, err := getClient( clientSvc, err := getClient(
supportedClients, cfgData.ClientType, cfgData.AspUrl, supportedClients, cfgData.ClientType, cfgData.ServerUrl,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup transport client: %s", err) return nil, fmt.Errorf("failed to setup transport client: %s", err)
@@ -243,18 +243,18 @@ func (a *covenantArkClient) listenForTxStream(ctx context.Context) {
func (a *covenantArkClient) processTransactionEvent( func (a *covenantArkClient) processTransactionEvent(
event client.TransactionEvent, event client.TransactionEvent,
) { ) {
// TODO considering current covenant state where all payments happening in round // TODO: considering current covenant state where all transactions happening in round
//and that this is going to change we leave this unimplemented until asnc payments are implemented // and that this is going to change we leave this unimplemented for now.
//also with current state it is not possible to cover some edge cases like when in a round there // 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 // are multiple boarding inputs + spent vtxo with change in spendable + received in the same round
} }
func (a *covenantArkClient) listenForBoardingUtxos( func (a *covenantArkClient) listenForBoardingUtxos(
ctx context.Context, ctx context.Context,
) { ) {
// TODO considering current covenant state where all payments happening in round // TODO considering current covenant state where all transactions happening in round
//and that this is going to change we leave this unimplemented until asnc payments are implemented // and that this is going to change we leave this unimplemented for now.
//also with current state it is not possible to cover some edge cases like when in a round there // 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 // are multiple boarding inputs + spent vtxo with change in spendable + received in the same round
} }
@@ -525,7 +525,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
for _, offchainAddr := range offchainAddrs { for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos { for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network) vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -584,16 +584,16 @@ func (a *covenantArkClient) CollaborativeRedeem(
}) })
} }
paymentID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "") requestID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "")
if err != nil { if err != nil {
return "", err return "", err
} }
if err := a.client.RegisterOutputsForNextRound(ctx, paymentID, receivers); err != nil { if err := a.client.RegisterOutputsForNextRound(ctx, requestID, receivers); err != nil {
return "", err return "", err
} }
roundTxID, err := a.handleRoundStream(ctx, paymentID, selectedCoins, selectedBoardingUtxos, receivers) roundTxID, err := a.handleRoundStream(ctx, requestID, selectedCoins, selectedBoardingUtxos, receivers)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -601,13 +601,6 @@ func (a *covenantArkClient) CollaborativeRedeem(
return roundTxID, nil return roundTxID, nil
} }
func (a *covenantArkClient) SendAsync(
ctx context.Context,
withExpiryCoinselect bool, receivers []Receiver,
) (string, error) {
return "", fmt.Errorf("not implemented")
}
func (a *covenantArkClient) Settle( func (a *covenantArkClient) Settle(
ctx context.Context, ctx context.Context,
) (string, error) { ) (string, error) {
@@ -889,7 +882,7 @@ func (a *covenantArkClient) sendOffchain(
return "", fmt.Errorf("wallet is locked") return "", fmt.Errorf("wallet is locked")
} }
expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey) expectedServerPubkey := schnorr.SerializePubKey(a.ServerPubKey)
outputs := make([]client.Output, 0) outputs := make([]client.Output, 0)
sumOfReceivers := uint64(0) sumOfReceivers := uint64(0)
@@ -900,10 +893,10 @@ func (a *covenantArkClient) sendOffchain(
return "", fmt.Errorf("invalid receiver address: %s", err) return "", fmt.Errorf("invalid receiver address: %s", err)
} }
rcvAspPubKey := schnorr.SerializePubKey(rcvAddr.Asp) rcvServerPubKkey := schnorr.SerializePubKey(rcvAddr.Server)
if !bytes.Equal(expectedAspPubKey, rcvAspPubKey) { if !bytes.Equal(expectedServerPubkey, rcvServerPubKkey) {
return "", fmt.Errorf("invalid receiver address '%s': expected ASP %s, got %s", receiver.To(), hex.EncodeToString(expectedAspPubKey), hex.EncodeToString(rcvAspPubKey)) return "", fmt.Errorf("invalid receiver address '%s': expected server %s, got %s", receiver.To(), hex.EncodeToString(expectedServerPubkey), hex.EncodeToString(rcvServerPubKkey))
} }
if receiver.Amount() < a.Dust { if receiver.Amount() < a.Dust {
@@ -934,7 +927,7 @@ func (a *covenantArkClient) sendOffchain(
for _, offchainAddr := range offchainAddrs { for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos { for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network) vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -1018,21 +1011,21 @@ func (a *covenantArkClient) sendOffchain(
}) })
} }
paymentID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "") requestID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "")
if err != nil { if err != nil {
return "", err return "", err
} }
if err := a.client.RegisterOutputsForNextRound( if err := a.client.RegisterOutputsForNextRound(
ctx, paymentID, outputs, ctx, requestID, outputs,
); err != nil { ); err != nil {
return "", err return "", err
} }
log.Infof("payment registered with id: %s", paymentID) log.Infof("payout registered with id: %s", requestID)
roundTxID, err := a.handleRoundStream( roundTxID, err := a.handleRoundStream(
ctx, paymentID, selectedCoins, boardingUtxos, outputs, ctx, requestID, selectedCoins, boardingUtxos, outputs,
) )
if err != nil { if err != nil {
return "", err return "", err
@@ -1116,19 +1109,19 @@ func (a *covenantArkClient) addInputs(
func (a *covenantArkClient) handleRoundStream( func (a *covenantArkClient) handleRoundStream(
ctx context.Context, ctx context.Context,
paymentID string, requestID string,
vtxosToSign []client.TapscriptsVtxo, vtxosToSign []client.TapscriptsVtxo,
boardingUtxos []types.Utxo, boardingUtxos []types.Utxo,
receivers []client.Output, receivers []client.Output,
) (string, error) { ) (string, error) {
eventsCh, close, err := a.client.GetEventStream(ctx, paymentID) eventsCh, close, err := a.client.GetEventStream(ctx, requestID)
if err != nil { if err != nil {
return "", err return "", err
} }
var pingStop func() var pingStop func()
for pingStop == nil { for pingStop == nil {
pingStop = a.ping(ctx, paymentID) pingStop = a.ping(ctx, requestID)
} }
defer func() { defer func() {
@@ -1166,7 +1159,7 @@ func (a *covenantArkClient) handleRoundStream(
continue continue
} }
log.Info("finalizing payment... ") log.Info("submitting forfeit transactions... ")
if err := a.client.SubmitSignedForfeitTxs(ctx, signedForfeitTxs, signedRoundTx); err != nil { if err := a.client.SubmitSignedForfeitTxs(ctx, signedForfeitTxs, signedRoundTx); err != nil {
return "", err return "", err
} }
@@ -1185,7 +1178,7 @@ func (a *covenantArkClient) handleRoundFinalization(
boardingUtxos []types.Utxo, boardingUtxos []types.Utxo,
receivers []client.Output, receivers []client.Output,
) (signedForfeits []string, signedRoundTx string, err error) { ) (signedForfeits []string, signedRoundTx string, err error) {
if err = a.validateCongestionTree(event, receivers); err != nil { if err = a.validateVtxoTree(event, receivers); err != nil {
return return
} }
@@ -1270,11 +1263,11 @@ func (a *covenantArkClient) handleRoundFinalization(
return signedForfeits, signedRoundTx, nil return signedForfeits, signedRoundTx, nil
} }
func (a *covenantArkClient) validateCongestionTree( func (a *covenantArkClient) validateVtxoTree(
event client.RoundFinalizationEvent, receivers []client.Output, event client.RoundFinalizationEvent, receivers []client.Output,
) error { ) error {
poolTx := event.Tx roundTx := event.Tx
ptx, err := psetv2.NewPsetFromBase64(poolTx) ptx, err := psetv2.NewPsetFromBase64(roundTx)
if err != nil { if err != nil {
return err return err
} }
@@ -1282,14 +1275,14 @@ func (a *covenantArkClient) validateCongestionTree(
connectors := event.Connectors connectors := event.Connectors
if !utils.IsOnchainOnly(receivers) { if !utils.IsOnchainOnly(receivers) {
if err := tree.ValidateCongestionTree( if err := tree.ValidateVtxoTree(
event.Tree, poolTx, a.Config.AspPubkey, a.RoundLifetime, event.Tree, roundTx, a.Config.ServerPubKey, a.RoundLifetime,
); err != nil { ); err != nil {
return err return err
} }
} }
if err := common.ValidateConnectors(poolTx, connectors); err != nil { if err := common.ValidateConnectors(roundTx, connectors); err != nil {
return err return err
} }
@@ -1299,15 +1292,13 @@ func (a *covenantArkClient) validateCongestionTree(
return err return err
} }
log.Infoln("congestion tree validated")
return nil return nil
} }
func (a *covenantArkClient) validateReceivers( func (a *covenantArkClient) validateReceivers(
ptx *psetv2.Pset, ptx *psetv2.Pset,
receivers []client.Output, receivers []client.Output,
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
) error { ) error {
for _, receiver := range receivers { for _, receiver := range receivers {
isOnChain, onchainScript, err := utils.ParseLiquidAddress( isOnChain, onchainScript, err := utils.ParseLiquidAddress(
@@ -1323,7 +1314,7 @@ func (a *covenantArkClient) validateReceivers(
} }
} else { } else {
if err := a.validateOffChainReceiver( if err := a.validateOffChainReceiver(
congestionTree, receiver, vtxoTree, receiver,
); err != nil { ); err != nil {
return err return err
} }
@@ -1357,7 +1348,7 @@ func (a *covenantArkClient) validateOnChainReceiver(
} }
func (a *covenantArkClient) validateOffChainReceiver( func (a *covenantArkClient) validateOffChainReceiver(
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
receiver client.Output, receiver client.Output,
) error { ) error {
found := false found := false
@@ -1369,7 +1360,7 @@ func (a *covenantArkClient) validateOffChainReceiver(
vtxoTapKey := schnorr.SerializePubKey(addr.VtxoTapKey) vtxoTapKey := schnorr.SerializePubKey(addr.VtxoTapKey)
leaves := congestionTree.Leaves() leaves := vtxoTree.Leaves()
for _, leaf := range leaves { for _, leaf := range leaves {
tx, err := psetv2.NewPsetFromBase64(leaf.Tx) tx, err := psetv2.NewPsetFromBase64(leaf.Tx)
if err != nil { if err != nil {
@@ -1614,22 +1605,22 @@ func (a *covenantArkClient) coinSelectOnchain(
func (a *covenantArkClient) getRedeemBranches( func (a *covenantArkClient) getRedeemBranches(
ctx context.Context, vtxos []client.Vtxo, ctx context.Context, vtxos []client.Vtxo,
) (map[string]*redemption.CovenantRedeemBranch, error) { ) (map[string]*redemption.CovenantRedeemBranch, error) {
congestionTrees := make(map[string]tree.CongestionTree, 0) vtxoTrees := make(map[string]tree.VtxoTree, 0)
redeemBranches := make(map[string]*redemption.CovenantRedeemBranch, 0) redeemBranches := make(map[string]*redemption.CovenantRedeemBranch, 0)
for i := range vtxos { for i := range vtxos {
vtxo := vtxos[i] vtxo := vtxos[i]
if _, ok := congestionTrees[vtxo.RoundTxid]; !ok { if _, ok := vtxoTrees[vtxo.RoundTxid]; !ok {
round, err := a.client.GetRound(ctx, vtxo.RoundTxid) round, err := a.client.GetRound(ctx, vtxo.RoundTxid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
congestionTrees[vtxo.RoundTxid] = round.Tree vtxoTrees[vtxo.RoundTxid] = round.Tree
} }
redeemBranch, err := redemption.NewCovenantRedeemBranch( redeemBranch, err := redemption.NewCovenantRedeemBranch(
a.explorer, congestionTrees[vtxo.RoundTxid], vtxo, a.explorer, vtxoTrees[vtxo.RoundTxid], vtxo,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -91,7 +91,7 @@ func LoadCovenantlessClient(sdkStore types.Store) (ArkClient, error) {
} }
clientSvc, err := getClient( clientSvc, err := getClient(
supportedClients, cfgData.ClientType, cfgData.AspUrl, supportedClients, cfgData.ClientType, cfgData.ServerUrl,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup transport client: %s", err) return nil, fmt.Errorf("failed to setup transport client: %s", err)
@@ -151,7 +151,7 @@ func LoadCovenantlessClientWithWallet(
} }
clientSvc, err := getClient( clientSvc, err := getClient(
supportedClients, cfgData.ClientType, cfgData.AspUrl, supportedClients, cfgData.ClientType, cfgData.ServerUrl,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup transport client: %s", err) return nil, fmt.Errorf("failed to setup transport client: %s", err)
@@ -395,7 +395,7 @@ func (a *covenantlessArkClient) processTransactionEvent(
vtxosToInsert := make([]types.Vtxo, 0) vtxosToInsert := make([]types.Vtxo, 0)
txsToInsert := make([]types.Transaction, 0) txsToInsert := make([]types.Transaction, 0)
for _, v := range event.Round.SpendableVtxos { for _, v := range event.Round.SpendableVtxos {
if v.Pubkey == pubkey { if v.PubKey == pubkey {
vtxosToInsert = append(vtxosToInsert, types.Vtxo{ vtxosToInsert = append(vtxosToInsert, types.Vtxo{
VtxoKey: types.VtxoKey{ VtxoKey: types.VtxoKey{
Txid: v.Txid, Txid: v.Txid,
@@ -474,7 +474,7 @@ func (a *covenantlessArkClient) processTransactionEvent(
outputAmount := uint64(0) outputAmount := uint64(0)
for _, v := range event.Redeem.SpendableVtxos { for _, v := range event.Redeem.SpendableVtxos {
if v.Pubkey == pubkey { if v.PubKey == pubkey {
vtxosToInsert = append(vtxosToInsert, types.Vtxo{ vtxosToInsert = append(vtxosToInsert, types.Vtxo{
VtxoKey: types.VtxoKey{ VtxoKey: types.VtxoKey{
Txid: v.Txid, Txid: v.Txid,
@@ -507,7 +507,7 @@ func (a *covenantlessArkClient) processTransactionEvent(
} }
} else { } else {
for _, v := range event.Redeem.SpendableVtxos { for _, v := range event.Redeem.SpendableVtxos {
if v.Pubkey == pubkey { if v.PubKey == pubkey {
vtxosToInsert = append(vtxosToInsert, types.Vtxo{ vtxosToInsert = append(vtxosToInsert, types.Vtxo{
VtxoKey: types.VtxoKey{ VtxoKey: types.VtxoKey{
Txid: v.Txid, Txid: v.Txid,
@@ -708,13 +708,126 @@ func (a *covenantlessArkClient) SendOffChain(
ctx context.Context, ctx context.Context,
withExpiryCoinselect bool, receivers []Receiver, withExpiryCoinselect bool, receivers []Receiver,
) (string, error) { ) (string, error) {
if len(receivers) <= 0 {
return "", fmt.Errorf("missing receivers")
}
netParams := utils.ToBitcoinNetwork(a.Network)
for _, receiver := range receivers { for _, receiver := range receivers {
if receiver.IsOnchain() { isOnchain, _, err := utils.ParseBitcoinAddress(receiver.To(), netParams)
return "", fmt.Errorf("invalid receiver address '%s': must be offchain", receiver.To()) if err != nil {
return "", err
}
if isOnchain {
return "", fmt.Errorf("all receiver addresses must be offchain addresses")
} }
} }
return a.sendOffchain(ctx, withExpiryCoinselect, receivers) offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return "", err
}
expectedServerPubkey := schnorr.SerializePubKey(a.ServerPubKey)
sumOfReceivers := uint64(0)
for _, receiver := range receivers {
rcvAddr, err := common.DecodeAddress(receiver.To())
if err != nil {
return "", fmt.Errorf("invalid receiver address: %s", err)
}
rcvServerPubkey := schnorr.SerializePubKey(rcvAddr.Server)
if !bytes.Equal(expectedServerPubkey, rcvServerPubkey) {
return "", fmt.Errorf("invalid receiver address '%s': expected server %s, got %s", receiver.To(), hex.EncodeToString(expectedServerPubkey), hex.EncodeToString(rcvServerPubkey))
}
if receiver.Amount() < a.Dust {
return "", fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount(), a.Dust)
}
sumOfReceivers += receiver.Amount()
}
vtxos := make([]client.TapscriptsVtxo, 0)
opts := &CoinSelectOptions{
WithExpirySorting: withExpiryCoinselect,
}
spendableVtxos, err := a.getVtxos(ctx, opts)
if err != nil {
return "", err
}
for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil {
return "", err
}
if vtxoAddr == offchainAddr.Address {
vtxos = append(vtxos, client.TapscriptsVtxo{
Vtxo: v,
Tapscripts: offchainAddr.Tapscripts,
})
}
}
}
// do not include boarding utxos
_, selectedCoins, changeAmount, err := utils.CoinSelect(
nil, vtxos, sumOfReceivers, a.Dust, withExpiryCoinselect,
)
if err != nil {
return "", err
}
if changeAmount > 0 {
receivers = append(receivers, NewBitcoinReceiver(offchainAddrs[0].Address, changeAmount))
}
inputs := make([]redeemTxInput, 0, len(selectedCoins))
for _, coin := range selectedCoins {
vtxoScript, err := bitcointree.ParseVtxoScript(coin.Tapscripts)
if err != nil {
return "", err
}
forfeitClosure := vtxoScript.ForfeitClosures()[0]
forfeitScript, err := forfeitClosure.Script()
if err != nil {
return "", err
}
forfeitLeaf := txscript.NewBaseTapLeaf(forfeitScript)
inputs = append(inputs, redeemTxInput{
coin,
forfeitLeaf.TapHash(),
})
}
feeRate := chainfee.FeePerKwFloor
redeemTx, err := buildRedeemTx(inputs, receivers, feeRate.FeePerVByte())
if err != nil {
return "", err
}
signedRedeemTx, err := a.wallet.SignTransaction(ctx, a.explorer, redeemTx)
if err != nil {
return "", err
}
signedRedeemTx, err = a.client.SubmitRedeemTx(ctx, signedRedeemTx)
if err != nil {
return "", err
}
return signedRedeemTx, nil
} }
func (a *covenantlessArkClient) RedeemNotes(ctx context.Context, notes []string) (string, error) { func (a *covenantlessArkClient) RedeemNotes(ctx context.Context, notes []string) (string, error) {
@@ -741,7 +854,7 @@ func (a *covenantlessArkClient) RedeemNotes(ctx context.Context, notes []string)
return "", err return "", err
} }
paymentID, err := a.client.RegisterNotesForNextRound( requestID, err := a.client.RegisterNotesForNextRound(
ctx, notes, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()), ctx, notes, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
) )
if err != nil { if err != nil {
@@ -756,15 +869,15 @@ func (a *covenantlessArkClient) RedeemNotes(ctx context.Context, notes []string)
receiversOutput := []client.Output{output} receiversOutput := []client.Output{output}
if err := a.client.RegisterOutputsForNextRound( if err := a.client.RegisterOutputsForNextRound(
ctx, paymentID, receiversOutput, ctx, requestID, receiversOutput,
); err != nil { ); err != nil {
return "", err return "", err
} }
log.Infof("payment registered with id: %s", paymentID) log.Infof("payout registered with id: %s", requestID)
roundTxID, err := a.handleRoundStream( roundTxID, err := a.handleRoundStream(
ctx, paymentID, nil, nil, receiversOutput, roundEphemeralKey, ctx, requestID, nil, nil, receiversOutput, roundEphemeralKey,
) )
if err != nil { if err != nil {
return "", err return "", err
@@ -865,7 +978,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
for _, offchainAddr := range offchainAddrs { for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos { for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network) vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -929,7 +1042,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
return "", err return "", err
} }
paymentID, err := a.client.RegisterInputsForNextRound( requestID, err := a.client.RegisterInputsForNextRound(
ctx, ctx,
inputs, inputs,
hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()), hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
@@ -938,12 +1051,12 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
return "", err return "", err
} }
if err := a.client.RegisterOutputsForNextRound(ctx, paymentID, receivers); err != nil { if err := a.client.RegisterOutputsForNextRound(ctx, requestID, receivers); err != nil {
return "", err return "", err
} }
roundTxID, err := a.handleRoundStream( roundTxID, err := a.handleRoundStream(
ctx, paymentID, selectedCoins, selectedBoardingCoins, receivers, roundEphemeralKey, ctx, requestID, selectedCoins, selectedBoardingCoins, receivers, roundEphemeralKey,
) )
if err != nil { if err != nil {
return "", err return "", err
@@ -952,132 +1065,6 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
return roundTxID, nil return roundTxID, nil
} }
func (a *covenantlessArkClient) SendAsync(
ctx context.Context,
withExpiryCoinselect bool, receivers []Receiver,
) (string, error) {
if len(receivers) <= 0 {
return "", fmt.Errorf("missing receivers")
}
netParams := utils.ToBitcoinNetwork(a.Network)
for _, receiver := range receivers {
isOnchain, _, err := utils.ParseBitcoinAddress(receiver.To(), netParams)
if err != nil {
return "", err
}
if isOnchain {
return "", fmt.Errorf("all receiver addresses must be offchain addresses")
}
}
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return "", err
}
expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey)
sumOfReceivers := uint64(0)
for _, receiver := range receivers {
rcvAddr, err := common.DecodeAddress(receiver.To())
if err != nil {
return "", fmt.Errorf("invalid receiver address: %s", err)
}
rcvAspPubKey := schnorr.SerializePubKey(rcvAddr.Asp)
if !bytes.Equal(expectedAspPubKey, rcvAspPubKey) {
return "", fmt.Errorf("invalid receiver address '%s': expected ASP %s, got %s", receiver.To(), hex.EncodeToString(expectedAspPubKey), hex.EncodeToString(rcvAspPubKey))
}
if receiver.Amount() < a.Dust {
return "", fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount(), a.Dust)
}
sumOfReceivers += receiver.Amount()
}
vtxos := make([]client.TapscriptsVtxo, 0)
opts := &CoinSelectOptions{
WithExpirySorting: withExpiryCoinselect,
}
spendableVtxos, err := a.getVtxos(ctx, opts)
if err != nil {
return "", err
}
for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network)
if err != nil {
return "", err
}
if vtxoAddr == offchainAddr.Address {
vtxos = append(vtxos, client.TapscriptsVtxo{
Vtxo: v,
Tapscripts: offchainAddr.Tapscripts,
})
}
}
}
// do not include boarding utxos
_, selectedCoins, changeAmount, err := utils.CoinSelect(
nil, vtxos, sumOfReceivers, a.Dust, withExpiryCoinselect,
)
if err != nil {
return "", err
}
if changeAmount > 0 {
receivers = append(receivers, NewBitcoinReceiver(offchainAddrs[0].Address, changeAmount))
}
inputs := make([]redeemTxInput, 0, len(selectedCoins))
for _, coin := range selectedCoins {
vtxoScript, err := bitcointree.ParseVtxoScript(coin.Tapscripts)
if err != nil {
return "", err
}
forfeitClosure := vtxoScript.ForfeitClosures()[0]
forfeitScript, err := forfeitClosure.Script()
if err != nil {
return "", err
}
forfeitLeaf := txscript.NewBaseTapLeaf(forfeitScript)
inputs = append(inputs, redeemTxInput{
coin,
forfeitLeaf.TapHash(),
})
}
feeRate := chainfee.FeePerKwFloor
redeemTx, err := buildRedeemTx(inputs, receivers, feeRate.FeePerVByte())
if err != nil {
return "", err
}
signedRedeemTx, err := a.wallet.SignTransaction(ctx, a.explorer, redeemTx)
if err != nil {
return "", err
}
signedRedeemTx, err = a.client.SubmitRedeemTx(ctx, signedRedeemTx)
if err != nil {
return "", err
}
return signedRedeemTx, nil
}
func (a *covenantlessArkClient) Settle(ctx context.Context) (string, error) { func (a *covenantlessArkClient) Settle(ctx context.Context) (string, error) {
return a.sendOffchain(ctx, false, nil) return a.sendOffchain(ctx, false, nil)
} }
@@ -1136,7 +1123,7 @@ func (a *covenantlessArkClient) SetNostrNotificationRecipient(ctx context.Contex
descriptorVtxos := make([]client.TapscriptsVtxo, 0) descriptorVtxos := make([]client.TapscriptsVtxo, 0)
for _, offchainAddr := range offchainAddrs { for _, offchainAddr := range offchainAddrs {
for _, vtxo := range spendableVtxos { for _, vtxo := range spendableVtxos {
vtxoAddr, err := vtxo.Address(a.AspPubkey, a.Network) vtxoAddr, err := vtxo.Address(a.ServerPubKey, a.Network)
if err != nil { if err != nil {
return err return err
} }
@@ -1362,7 +1349,7 @@ func (a *covenantlessArkClient) sendOffchain(
return "", fmt.Errorf("wallet is locked") return "", fmt.Errorf("wallet is locked")
} }
expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey) expectedServerPubkey := schnorr.SerializePubKey(a.ServerPubKey)
outputs := make([]client.Output, 0) outputs := make([]client.Output, 0)
sumOfReceivers := uint64(0) sumOfReceivers := uint64(0)
@@ -1373,10 +1360,10 @@ func (a *covenantlessArkClient) sendOffchain(
return "", fmt.Errorf("invalid receiver address: %s", err) return "", fmt.Errorf("invalid receiver address: %s", err)
} }
rcvAspPubKey := schnorr.SerializePubKey(rcvAddr.Asp) rcvServerPubkey := schnorr.SerializePubKey(rcvAddr.Server)
if !bytes.Equal(expectedAspPubKey, rcvAspPubKey) { if !bytes.Equal(expectedServerPubkey, rcvServerPubkey) {
return "", fmt.Errorf("invalid receiver address '%s': expected ASP %s, got %s", receiver.To(), hex.EncodeToString(expectedAspPubKey), hex.EncodeToString(rcvAspPubKey)) return "", fmt.Errorf("invalid receiver address '%s': expected server %s, got %s", receiver.To(), hex.EncodeToString(expectedServerPubkey), hex.EncodeToString(rcvServerPubkey))
} }
if receiver.Amount() < a.Dust { if receiver.Amount() < a.Dust {
@@ -1408,7 +1395,7 @@ func (a *covenantlessArkClient) sendOffchain(
for _, offchainAddr := range offchainAddrs { for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos { for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network) vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -1495,7 +1482,7 @@ func (a *covenantlessArkClient) sendOffchain(
return "", err return "", err
} }
paymentID, err := a.client.RegisterInputsForNextRound( requestID, err := a.client.RegisterInputsForNextRound(
ctx, inputs, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()), ctx, inputs, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
) )
if err != nil { if err != nil {
@@ -1503,15 +1490,15 @@ func (a *covenantlessArkClient) sendOffchain(
} }
if err := a.client.RegisterOutputsForNextRound( if err := a.client.RegisterOutputsForNextRound(
ctx, paymentID, outputs, ctx, requestID, outputs,
); err != nil { ); err != nil {
return "", err return "", err
} }
log.Infof("payment registered with id: %s", paymentID) log.Infof("registered inputs and outputs with request id: %s", requestID)
roundTxID, err := a.handleRoundStream( roundTxID, err := a.handleRoundStream(
ctx, paymentID, selectedCoins, selectedBoardingCoins, outputs, roundEphemeralKey, ctx, requestID, selectedCoins, selectedBoardingCoins, outputs, roundEphemeralKey,
) )
if err != nil { if err != nil {
return "", err return "", err
@@ -1594,7 +1581,7 @@ func (a *covenantlessArkClient) addInputs(
func (a *covenantlessArkClient) handleRoundStream( func (a *covenantlessArkClient) handleRoundStream(
ctx context.Context, ctx context.Context,
paymentID string, requestID string,
vtxosToSign []client.TapscriptsVtxo, vtxosToSign []client.TapscriptsVtxo,
boardingUtxos []types.Utxo, boardingUtxos []types.Utxo,
receivers []client.Output, receivers []client.Output,
@@ -1605,14 +1592,14 @@ func (a *covenantlessArkClient) handleRoundStream(
return "", err return "", err
} }
eventsCh, close, err := a.client.GetEventStream(ctx, paymentID) eventsCh, close, err := a.client.GetEventStream(ctx, requestID)
if err != nil { if err != nil {
return "", err return "", err
} }
var pingStop func() var pingStop func()
for pingStop == nil { for pingStop == nil {
pingStop = a.ping(ctx, paymentID) pingStop = a.ping(ctx, requestID)
} }
defer func() { defer func() {
@@ -1697,7 +1684,7 @@ func (a *covenantlessArkClient) handleRoundStream(
continue continue
} }
log.Info("finalizing payment... ") log.Info("submitting forfeit transactions... ")
if err := a.client.SubmitSignedForfeitTxs(ctx, signedForfeitTxs, signedRoundTx); err != nil { if err := a.client.SubmitSignedForfeitTxs(ctx, signedForfeitTxs, signedRoundTx); err != nil {
return "", err return "", err
} }
@@ -1715,7 +1702,7 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
ctx context.Context, ephemeralKey *secp256k1.PrivateKey, event client.RoundSigningStartedEvent, ctx context.Context, ephemeralKey *secp256k1.PrivateKey, event client.RoundSigningStartedEvent,
) (signerSession bitcointree.SignerSession, err error) { ) (signerSession bitcointree.SignerSession, err error) {
sweepClosure := tree.CSVSigClosure{ sweepClosure := tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{a.AspPubkey}}, MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{a.ServerPubKey}},
Seconds: uint(a.RoundLifetime), Seconds: uint(a.RoundLifetime),
} }
@@ -1740,7 +1727,7 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
ephemeralKey, sharedOutputValue, event.UnsignedTree, root.CloneBytes(), ephemeralKey, sharedOutputValue, event.UnsignedTree, root.CloneBytes(),
) )
if err = signerSession.SetKeys(event.CosignersPublicKeys); err != nil { if err = signerSession.SetKeys(event.CosignersPubKeys); err != nil {
return return
} }
@@ -1749,9 +1736,9 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
return return
} }
myPubKey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed()) myPubkey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed())
err = a.arkClient.client.SubmitTreeNonces(ctx, event.ID, myPubKey, nonces) err = a.arkClient.client.SubmitTreeNonces(ctx, event.ID, myPubkey, nonces)
return return
} }
@@ -1794,8 +1781,8 @@ func (a *covenantlessArkClient) handleRoundFinalization(
boardingUtxos []types.Utxo, boardingUtxos []types.Utxo,
receivers []client.Output, receivers []client.Output,
) ([]string, string, error) { ) ([]string, string, error) {
if err := a.validateCongestionTree(event, receivers); err != nil { if err := a.validateVtxoTree(event, receivers); err != nil {
return nil, "", fmt.Errorf("failed to verify congestion tree: %s", err) return nil, "", fmt.Errorf("failed to verify vtxo tree: %s", err)
} }
var forfeits []string var forfeits []string
@@ -1880,24 +1867,25 @@ func (a *covenantlessArkClient) handleRoundFinalization(
return forfeits, signedRoundTx, nil return forfeits, signedRoundTx, nil
} }
func (a *covenantlessArkClient) validateCongestionTree( func (a *covenantlessArkClient) validateVtxoTree(
event client.RoundFinalizationEvent, receivers []client.Output, event client.RoundFinalizationEvent, receivers []client.Output,
) error { ) error {
poolTx := event.Tx roundTx := event.Tx
ptx, err := psbt.NewFromRawBytes(strings.NewReader(poolTx), true) ptx, err := psbt.NewFromRawBytes(strings.NewReader(roundTx), true)
if err != nil { if err != nil {
return err return err
} }
if !utils.IsOnchainOnly(receivers) { if !utils.IsOnchainOnly(receivers) {
if err := bitcointree.ValidateCongestionTree( if err := bitcointree.ValidateVtxoTree(
event.Tree, poolTx, a.Config.AspPubkey, a.RoundLifetime, event.Tree, roundTx, a.Config.ServerPubKey, a.RoundLifetime,
); err != nil { ); err != nil {
return err return err
} }
} }
// if err := common.ValidateConnectors(poolTx, event.Connectors); err != nil { // TODO: common.ValidateConnectors is for covenant version (liquid), add covenantless (bitcoin) version
// if err := common.ValidateConnectors(roundTx, event.Connectors); err != nil {
// return err // return err
// } // }
@@ -1907,15 +1895,13 @@ func (a *covenantlessArkClient) validateCongestionTree(
return err return err
} }
log.Info("congestion tree validated")
return nil return nil
} }
func (a *covenantlessArkClient) validateReceivers( func (a *covenantlessArkClient) validateReceivers(
ptx *psbt.Packet, ptx *psbt.Packet,
receivers []client.Output, receivers []client.Output,
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
) error { ) error {
netParams := utils.ToBitcoinNetwork(a.Network) netParams := utils.ToBitcoinNetwork(a.Network)
for _, receiver := range receivers { for _, receiver := range receivers {
@@ -1932,7 +1918,7 @@ func (a *covenantlessArkClient) validateReceivers(
} }
} else { } else {
if err := a.validateOffChainReceiver( if err := a.validateOffChainReceiver(
congestionTree, receiver, vtxoTree, receiver,
); err != nil { ); err != nil {
return err return err
} }
@@ -1966,7 +1952,7 @@ func (a *covenantlessArkClient) validateOnChainReceiver(
} }
func (a *covenantlessArkClient) validateOffChainReceiver( func (a *covenantlessArkClient) validateOffChainReceiver(
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
receiver client.Output, receiver client.Output,
) error { ) error {
found := false found := false
@@ -1978,7 +1964,7 @@ func (a *covenantlessArkClient) validateOffChainReceiver(
vtxoTapKey := schnorr.SerializePubKey(rcvAddr.VtxoTapKey) vtxoTapKey := schnorr.SerializePubKey(rcvAddr.VtxoTapKey)
leaves := congestionTree.Leaves() leaves := vtxoTree.Leaves()
for _, leaf := range leaves { for _, leaf := range leaves {
tx, err := psbt.NewFromRawBytes(strings.NewReader(leaf.Tx), true) tx, err := psbt.NewFromRawBytes(strings.NewReader(leaf.Tx), true)
if err != nil { if err != nil {
@@ -2247,7 +2233,7 @@ func (a *covenantlessArkClient) coinSelectOnchain(
func (a *covenantlessArkClient) getRedeemBranches( func (a *covenantlessArkClient) getRedeemBranches(
ctx context.Context, vtxos []client.Vtxo, ctx context.Context, vtxos []client.Vtxo,
) (map[string]*redemption.CovenantlessRedeemBranch, error) { ) (map[string]*redemption.CovenantlessRedeemBranch, error) {
congestionTrees := make(map[string]tree.CongestionTree, 0) vtxoTrees := make(map[string]tree.VtxoTree, 0)
redeemBranches := make(map[string]*redemption.CovenantlessRedeemBranch, 0) redeemBranches := make(map[string]*redemption.CovenantlessRedeemBranch, 0)
for i := range vtxos { for i := range vtxos {
@@ -2258,17 +2244,17 @@ func (a *covenantlessArkClient) getRedeemBranches(
continue continue
} }
if _, ok := congestionTrees[vtxo.RoundTxid]; !ok { if _, ok := vtxoTrees[vtxo.RoundTxid]; !ok {
round, err := a.client.GetRound(ctx, vtxo.RoundTxid) round, err := a.client.GetRound(ctx, vtxo.RoundTxid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
congestionTrees[vtxo.RoundTxid] = round.Tree vtxoTrees[vtxo.RoundTxid] = round.Tree
} }
redeemBranch, err := redemption.NewCovenantlessRedeemBranch( redeemBranch, err := redemption.NewCovenantlessRedeemBranch(
a.explorer, congestionTrees[vtxo.RoundTxid], vtxo, a.explorer, vtxoTrees[vtxo.RoundTxid], vtxo,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -18,7 +18,7 @@ import (
) )
var ( var (
aspUrl = "localhost:6060" serverUrl = "localhost:6060"
clientType = arksdk.GrpcClient clientType = arksdk.GrpcClient
password = "password" password = "password"
walletType = arksdk.SingleKeyWallet walletType = arksdk.SingleKeyWallet
@@ -110,7 +110,7 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
log.Infof("payment completed in round tx: %s", txid) log.Infof("transaction completed in round: %s", txid)
if err := generateBlock(); err != nil { if err := generateBlock(); err != nil {
log.Fatal(err) log.Fatal(err)
@@ -155,7 +155,7 @@ func setupArkClient(wallet string) (arksdk.ArkClient, error) {
if err := client.Init(context.Background(), arksdk.InitArgs{ if err := client.Init(context.Background(), arksdk.InitArgs{
WalletType: walletType, WalletType: walletType,
ClientType: clientType, ClientType: clientType,
AspUrl: aspUrl, ServerUrl: serverUrl,
Password: password, Password: password,
}); err != nil { }); err != nil {
return nil, fmt.Errorf("failed to initialize wallet: %s", err) return nil, fmt.Errorf("failed to initialize wallet: %s", err)

View File

@@ -28,13 +28,13 @@
logMessage("Init error: password is required"); logMessage("Init error: password is required");
return; return;
} }
const aspUrl = document.getElementById("aspUrl").value; const serverUrl = document.getElementById("serverUrl").value;
if (!aspUrl) { if (!serverUrl) {
logMessage("Init error: asp url is required"); logMessage("Init error: server url is required");
return; return;
} }
await init(walletType, clientType, aspUrl, privateKey, password, chain, explorerUrl); await init(walletType, clientType, serverUrl, privateKey, password, chain, explorerUrl);
logMessage("wallet initialized and connected to ASP"); logMessage("wallet initialized and connected to server");
await config(); await config();
} catch (err) { } catch (err) {
logMessage("Init error: " + err.message); logMessage("Init error: " + err.message);
@@ -91,11 +91,11 @@
async function config() { async function config() {
try { try {
const aspUrl = await getAspUrl(); const serverUrl = await getServerUrl();
logMessage("ASP URL: " + aspUrl); logMessage("Server URL: " + serverUrl);
const aspPubKeyHex = await getAspPubKeyHex(); const serverPubkeyHex = await getServerPubkeyHex();
logMessage("ASP PubKey: " + aspPubKeyHex); logMessage("Server PubKey: " + serverPubkeyHex);
const walletType = await getWalletType(); const walletType = await getWalletType();
logMessage("Wallet Type: " + walletType); logMessage("Wallet Type: " + walletType);
@@ -145,7 +145,7 @@
<h2>Wallet</h2> <h2>Wallet</h2>
<div> <div>
<button onclick="initWallet()">Init</button> <button onclick="initWallet()">Init</button>
<input type="text" id="aspUrl" placeholder="http://localhost:6060"> <input type="text" id="serverUrl" placeholder="http://localhost:6060">
<input type="password" id="i_password" placeholder="password"> <input type="password" id="i_password" placeholder="password">
<input type="text" id="prvkey" placeholder="Optional: privkey (hex)"> <input type="text" id="prvkey" placeholder="Optional: privkey (hex)">
</div> </div>

View File

@@ -18,7 +18,7 @@ import (
) )
var ( var (
aspUrl = "localhost:7070" serverUrl = "localhost:7070"
clientType = arksdk.GrpcClient clientType = arksdk.GrpcClient
password = "password" password = "password"
walletType = arksdk.SingleKeyWallet walletType = arksdk.SingleKeyWallet
@@ -128,11 +128,11 @@ func main() {
fmt.Println("") fmt.Println("")
log.Infof("alice is sending %d sats to bob offchain...", amount) log.Infof("alice is sending %d sats to bob offchain...", amount)
if _, err = aliceArkClient.SendAsync(ctx, false, receivers); err != nil { if _, err = aliceArkClient.SendOffChain(ctx, false, receivers); err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Info("payment completed out of round") log.Info("transaction completed out of round")
if err := generateBlock(); err != nil { if err := generateBlock(); err != nil {
log.Fatal(err) log.Fatal(err)
@@ -188,7 +188,7 @@ func setupArkClient(wallet string) (arksdk.ArkClient, error) {
if err := client.Init(context.Background(), arksdk.InitArgs{ if err := client.Init(context.Background(), arksdk.InitArgs{
WalletType: walletType, WalletType: walletType,
ClientType: clientType, ClientType: clientType,
AspUrl: aspUrl, ServerUrl: serverUrl,
Password: password, Password: password,
WithTransactionFeed: true, WithTransactionFeed: true,
}); err != nil { }); err != nil {

View File

@@ -28,13 +28,13 @@
logMessage("Init error: password is required"); logMessage("Init error: password is required");
return; return;
} }
const aspUrl = document.getElementById("aspUrl").value; const serverUrl = document.getElementById("serverUrl").value;
if (!aspUrl) { if (!serverUrl) {
logMessage("Init error: asp url is required"); logMessage("Init error: server url is required");
return; return;
} }
await init(walletType, clientType, aspUrl, privateKey, password, chain, explorerUrl); await init(walletType, clientType, serverUrl, privateKey, password, chain, explorerUrl);
logMessage("wallet initialized and connected to ASP"); logMessage("wallet initialized and connected to server");
await config(); await config();
} catch (err) { } catch (err) {
logMessage("Init error: " + err.message); logMessage("Init error: " + err.message);
@@ -119,11 +119,11 @@
async function config() { async function config() {
try { try {
const aspUrl = await getAspUrl(); const serverUrl = await getServerUrl();
logMessage("ASP URL: " + aspUrl); logMessage("Server URL: " + serverUrl);
const aspPubKeyHex = await getAspPubKeyHex(); const serverPubkeyHex = await getServerPubkeyHex();
logMessage("ASP PubKey: " + aspPubKeyHex); logMessage("Server Pubkey: " + serverPubkeyHex);
const walletType = await getWalletType(); const walletType = await getWalletType();
logMessage("Wallet Type: " + walletType); logMessage("Wallet Type: " + walletType);
@@ -149,7 +149,7 @@
<h2>Wallet</h2> <h2>Wallet</h2>
<div> <div>
<button onclick="initWallet()">Init</button> <button onclick="initWallet()">Init</button>
<input type="text" id="aspUrl" placeholder="http://localhost:7070"> <input type="text" id="serverUrl" placeholder="http://localhost:7070">
<input type="password" id="i_password" placeholder="password"> <input type="password" id="i_password" placeholder="password">
<input type="text" id="prvkey" placeholder="Optional: privkey (hex)"> <input type="text" id="prvkey" placeholder="Optional: privkey (hex)">
</div> </div>

View File

@@ -22,7 +22,7 @@ func (t SupportedType[V]) Supports(typeStr string) bool {
return ok return ok
} }
type ClientFactory func(string) (client.ASPClient, error) type ClientFactory func(string) (client.TransportClient, error)
type Cache[V any] struct { type Cache[V any] struct {
mapping map[string]V mapping map[string]V

View File

@@ -173,11 +173,11 @@ func ToBitcoinNetwork(net common.Network) chaincfg.Params {
} }
func GenerateRandomPrivateKey() (*secp256k1.PrivateKey, error) { func GenerateRandomPrivateKey() (*secp256k1.PrivateKey, error) {
privKey, err := btcec.NewPrivateKey() prvkey, err := btcec.NewPrivateKey()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return privKey, nil return prvkey, nil
} }
func HashPassword(password []byte) []byte { func HashPassword(password []byte) []byte {

View File

@@ -24,9 +24,9 @@ type CovenantRedeemBranch struct {
func NewCovenantRedeemBranch( func NewCovenantRedeemBranch(
explorer explorer.Explorer, explorer explorer.Explorer,
congestionTree tree.CongestionTree, vtxo client.Vtxo, vtxoTree tree.VtxoTree, vtxo client.Vtxo,
) (*CovenantRedeemBranch, error) { ) (*CovenantRedeemBranch, error) {
sweepClosure, seconds, err := findCovenantSweepClosure(congestionTree) sweepClosure, seconds, err := findCovenantSweepClosure(vtxoTree)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -36,7 +36,7 @@ func NewCovenantRedeemBranch(
return nil, err return nil, err
} }
nodes, err := congestionTree.Branch(vtxo.Txid) nodes, err := vtxoTree.Branch(vtxo.Txid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -182,9 +182,9 @@ func (r *CovenantRedeemBranch) offchainPath() ([]*psetv2.Pset, error) {
} }
func findCovenantSweepClosure( func findCovenantSweepClosure(
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
) (*taproot.TapElementsLeaf, uint, error) { ) (*taproot.TapElementsLeaf, uint, error) {
root, err := congestionTree.Root() root, err := vtxoTree.Root()
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }

View File

@@ -23,9 +23,9 @@ type CovenantlessRedeemBranch struct {
func NewCovenantlessRedeemBranch( func NewCovenantlessRedeemBranch(
explorer explorer.Explorer, explorer explorer.Explorer,
congestionTree tree.CongestionTree, vtxo client.Vtxo, vtxoTree tree.VtxoTree, vtxo client.Vtxo,
) (*CovenantlessRedeemBranch, error) { ) (*CovenantlessRedeemBranch, error) {
_, seconds, err := findCovenantlessSweepClosure(congestionTree) _, seconds, err := findCovenantlessSweepClosure(vtxoTree)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -35,7 +35,7 @@ func NewCovenantlessRedeemBranch(
return nil, err return nil, err
} }
nodes, err := congestionTree.Branch(vtxo.Txid) nodes, err := vtxoTree.Branch(vtxo.Txid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -155,9 +155,9 @@ func (r *CovenantlessRedeemBranch) OffchainPath() ([]*psbt.Packet, error) {
} }
func findCovenantlessSweepClosure( func findCovenantlessSweepClosure(
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
) (*txscript.TapLeaf, uint, error) { ) (*txscript.TapLeaf, uint, error) {
root, err := congestionTree.Root() root, err := vtxoTree.Root()
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }

View File

@@ -52,8 +52,8 @@ func (s *configStore) GetDatadir() string {
func (s *configStore) AddData(ctx context.Context, data types.Config) error { func (s *configStore) AddData(ctx context.Context, data types.Config) error {
sd := &storeData{ sd := &storeData{
AspUrl: data.AspUrl, ServerUrl: data.ServerUrl,
AspPubkey: hex.EncodeToString(data.AspPubkey.SerializeCompressed()), ServerPubKey: hex.EncodeToString(data.ServerPubKey.SerializeCompressed()),
WalletType: data.WalletType, WalletType: data.WalletType,
ClientType: data.ClientType, ClientType: data.ClientType,
Network: data.Network.Name, Network: data.Network.Name,

View File

@@ -10,8 +10,8 @@ import (
) )
type storeData struct { type storeData struct {
AspUrl string `json:"asp_url"` ServerUrl string `json:"server_url"`
AspPubkey string `json:"asp_pubkey"` ServerPubKey string `json:"server_pubkey"`
WalletType string `json:"wallet_type"` WalletType string `json:"wallet_type"`
ClientType string `json:"client_type"` ClientType string `json:"client_type"`
Network string `json:"network"` Network string `json:"network"`
@@ -26,8 +26,8 @@ type storeData struct {
} }
func (d storeData) isEmpty() bool { func (d storeData) isEmpty() bool {
if d.AspUrl == "" && if d.ServerUrl == "" &&
d.AspPubkey == "" { d.ServerPubKey == "" {
return true return true
} }
@@ -41,12 +41,12 @@ func (d storeData) decode() types.Config {
unilateralExitDelay, _ := strconv.Atoi(d.UnilateralExitDelay) unilateralExitDelay, _ := strconv.Atoi(d.UnilateralExitDelay)
withTransactionFeed, _ := strconv.ParseBool(d.WithTransactionFeed) withTransactionFeed, _ := strconv.ParseBool(d.WithTransactionFeed)
dust, _ := strconv.Atoi(d.Dust) dust, _ := strconv.Atoi(d.Dust)
buf, _ := hex.DecodeString(d.AspPubkey) buf, _ := hex.DecodeString(d.ServerPubKey)
aspPubkey, _ := secp256k1.ParsePubKey(buf) serverPubkey, _ := secp256k1.ParsePubKey(buf)
explorerURL := d.ExplorerURL explorerURL := d.ExplorerURL
return types.Config{ return types.Config{
AspUrl: d.AspUrl, ServerUrl: d.ServerUrl,
AspPubkey: aspPubkey, ServerPubKey: serverPubkey,
WalletType: d.WalletType, WalletType: d.WalletType,
ClientType: d.ClientType, ClientType: d.ClientType,
Network: network, Network: network,
@@ -63,8 +63,8 @@ func (d storeData) decode() types.Config {
func (d storeData) asMap() map[string]string { func (d storeData) asMap() map[string]string {
return map[string]string{ return map[string]string{
"asp_url": d.AspUrl, "server_url": d.ServerUrl,
"asp_pubkey": d.AspPubkey, "server_pubkey": d.ServerPubKey,
"wallet_type": d.WalletType, "wallet_type": d.WalletType,
"client_type": d.ClientType, "client_type": d.ClientType,
"network": d.Network, "network": d.Network,

View File

@@ -21,8 +21,8 @@ func TestStore(t *testing.T) {
key, _ := btcec.NewPrivateKey() key, _ := btcec.NewPrivateKey()
ctx := context.Background() ctx := context.Background()
testStoreData := sdktypes.Config{ testStoreData := sdktypes.Config{
AspUrl: "localhost:7070", ServerUrl: "localhost:7070",
AspPubkey: key.PubKey(), ServerPubKey: key.PubKey(),
WalletType: wallet.SingleKeyWallet, WalletType: wallet.SingleKeyWallet,
ClientType: client.GrpcClient, ClientType: client.GrpcClient,
Network: common.LiquidRegTest, Network: common.LiquidRegTest,

View File

@@ -4,10 +4,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
utils "github.com/ark-network/ark/server/test/e2e"
"github.com/playwright-community/playwright-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io" "io"
"net/http" "net/http"
"os" "os"
@@ -18,6 +14,11 @@ import (
"testing" "testing"
"time" "time"
utils "github.com/ark-network/ark/server/test/e2e"
"github.com/playwright-community/playwright-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/shirou/gopsutil/net" "github.com/shirou/gopsutil/net"
) )
@@ -51,7 +52,7 @@ func TestMain(m *testing.M) {
os.Exit(1) os.Exit(1)
} }
_, err = runClarkCommand("init", "--asp-url", "localhost:7070", "--password", utils.Password, "--network", "regtest", "--explorer", "http://chopsticks:3000") _, err = runClarkCommand("init", "--server-url", "localhost:7070", "--password", utils.Password, "--network", "regtest", "--explorer", "http://chopsticks:3000")
if err != nil { if err != nil {
fmt.Printf("error initializing ark config: %s", err) fmt.Printf("error initializing ark config: %s", err)
os.Exit(1) os.Exit(1)
@@ -139,9 +140,9 @@ func TestWasm(t *testing.T) {
amount := 1000 amount := 1000
t.Logf("Alice is sending %d sats to Bob offchain...", amount) t.Logf("Alice is sending %d sats to Bob offchain...", amount)
require.NoError(t, sendAsync(alicePage, bobAddr.OffchainAddr, amount)) require.NoError(t, sendOffChain(alicePage, bobAddr.OffchainAddr, amount))
t.Log("Payment completed out of round") t.Log("Transaction completed out of round")
t.Logf("Bob settling the received funds...") t.Logf("Bob settling the received funds...")
txID, err = settle(bobPage) txID, err = settle(bobPage)
@@ -231,8 +232,8 @@ func initWallet(page playwright.Page) error {
const privateKey = ""; const privateKey = "";
const password = "pass"; const password = "pass";
const explorerUrl = ""; const explorerUrl = "";
const aspUrl = "http://localhost:7070"; const serverUrl = "http://localhost:7070";
return await init(walletType, clientType, aspUrl, privateKey, password, chain, explorerUrl); return await init(walletType, clientType, serverUrl, privateKey, password, chain, explorerUrl);
} catch (err) { } catch (err) {
console.error("Init error:", err); console.error("Init error:", err);
throw err; throw err;
@@ -336,10 +337,10 @@ func settle(page playwright.Page) (string, error) {
return fmt.Sprint(result), nil return fmt.Sprint(result), nil
} }
func sendAsync(page playwright.Page, addr string, amount int) error { func sendOffChain(page playwright.Page, addr string, amount int) error {
_, err := page.Evaluate(fmt.Sprintf(`async () => { _, err := page.Evaluate(fmt.Sprintf(`async () => {
try { try {
return await sendAsync(false, [{To:"%s", Amount:%d}]); return await sendOffChain(false, [{To:"%s", Amount:%d}]);
} catch (err) { } catch (err) {
console.error("Error:", err); console.error("Error:", err);
throw err; throw err;

View File

@@ -23,7 +23,7 @@ var (
type InitArgs struct { type InitArgs struct {
ClientType string ClientType string
WalletType string WalletType string
AspUrl string ServerUrl string
Seed string Seed string
Password string Password string
ExplorerURL string ExplorerURL string
@@ -51,8 +51,8 @@ func (a InitArgs) validate() error {
) )
} }
if len(a.AspUrl) <= 0 { if len(a.ServerUrl) <= 0 {
return fmt.Errorf("missing asp url") return fmt.Errorf("missing server url")
} }
if len(a.Password) <= 0 { if len(a.Password) <= 0 {
return fmt.Errorf("missing password") return fmt.Errorf("missing password")
@@ -63,7 +63,7 @@ func (a InitArgs) validate() error {
type InitWithWalletArgs struct { type InitWithWalletArgs struct {
ClientType string ClientType string
Wallet wallet.WalletService Wallet wallet.WalletService
AspUrl string ServerUrl string
Seed string Seed string
Password string Password string
ExplorerURL string ExplorerURL string
@@ -82,8 +82,8 @@ func (a InitWithWalletArgs) validate() error {
return fmt.Errorf("client type not supported, please select one of: %s", supportedClients) return fmt.Errorf("client type not supported, please select one of: %s", supportedClients)
} }
if len(a.AspUrl) <= 0 { if len(a.ServerUrl) <= 0 {
return fmt.Errorf("missing asp url") return fmt.Errorf("missing server url")
} }
if len(a.Password) <= 0 { if len(a.Password) <= 0 {
return fmt.Errorf("missing password") return fmt.Errorf("missing password")

View File

@@ -16,8 +16,8 @@ const (
) )
type Config struct { type Config struct {
AspUrl string ServerUrl string
AspPubkey *secp256k1.PublicKey ServerPubKey *secp256k1.PublicKey
WalletType string WalletType string
ClientType string ClientType string
Network common.Network Network common.Network

View File

@@ -193,7 +193,7 @@ func (s *bitcoinWallet) SignTransaction(
) )
txsighashes := txscript.NewTxSigHashes(updater.Upsbt.UnsignedTx, prevoutFetcher) txsighashes := txscript.NewTxSigHashes(updater.Upsbt.UnsignedTx, prevoutFetcher)
myPubkey := schnorr.SerializePubKey(s.walletData.Pubkey) myPubkey := schnorr.SerializePubKey(s.walletData.PubKey)
for i, input := range ptx.Inputs { for i, input := range ptx.Inputs {
if len(input.TaprootLeafScript) > 0 { if len(input.TaprootLeafScript) > 0 {
@@ -246,7 +246,7 @@ func (s *bitcoinWallet) SignTransaction(
return "", err return "", err
} }
if !sig.Verify(preimage, s.walletData.Pubkey) { if !sig.Verify(preimage, s.walletData.PubKey) {
return "", fmt.Errorf("signature verification failed") return "", fmt.Errorf("signature verification failed")
} }
@@ -308,8 +308,8 @@ func (w *bitcoinWallet) getAddress(
netParams := utils.ToBitcoinNetwork(data.Network) netParams := utils.ToBitcoinNetwork(data.Network)
defaultVtxoScript := bitcointree.NewDefaultVtxoScript( defaultVtxoScript := bitcointree.NewDefaultVtxoScript(
w.walletData.Pubkey, w.walletData.PubKey,
data.AspPubkey, data.ServerPubKey,
uint(data.UnilateralExitDelay), uint(data.UnilateralExitDelay),
) )
@@ -320,13 +320,13 @@ func (w *bitcoinWallet) getAddress(
offchainAddress := &common.Address{ offchainAddress := &common.Address{
HRP: data.Network.Addr, HRP: data.Network.Addr,
Asp: data.AspPubkey, Server: data.ServerPubKey,
VtxoTapKey: vtxoTapKey, VtxoTapKey: vtxoTapKey,
} }
boardingVtxoScript := bitcointree.NewDefaultVtxoScript( boardingVtxoScript := bitcointree.NewDefaultVtxoScript(
w.walletData.Pubkey, w.walletData.PubKey,
data.AspPubkey, data.ServerPubKey,
uint(data.UnilateralExitDelay*2), uint(data.UnilateralExitDelay*2),
) )

View File

@@ -211,7 +211,7 @@ func (s *liquidWallet) SignTransaction(
prevoutsAssets = append(prevoutsAssets, input.WitnessUtxo.Asset) prevoutsAssets = append(prevoutsAssets, input.WitnessUtxo.Asset)
} }
myPubkey := schnorr.SerializePubKey(s.walletData.Pubkey) myPubkey := schnorr.SerializePubKey(s.walletData.PubKey)
for i, input := range pset.Inputs { for i, input := range pset.Inputs {
if len(input.TapLeafScript) > 0 { if len(input.TapLeafScript) > 0 {
@@ -265,7 +265,7 @@ func (s *liquidWallet) SignTransaction(
tapScriptSig := psetv2.TapScriptSig{ tapScriptSig := psetv2.TapScriptSig{
PartialSig: psetv2.PartialSig{ PartialSig: psetv2.PartialSig{
PubKey: schnorr.SerializePubKey(s.walletData.Pubkey), PubKey: schnorr.SerializePubKey(s.walletData.PubKey),
Signature: sig.Serialize(), Signature: sig.Serialize(),
}, },
LeafHash: hash.CloneBytes(), LeafHash: hash.CloneBytes(),
@@ -330,8 +330,8 @@ func (w *liquidWallet) getAddress(
liquidNet := utils.ToElementsNetwork(data.Network) liquidNet := utils.ToElementsNetwork(data.Network)
vtxoScript := tree.NewDefaultVtxoScript( vtxoScript := tree.NewDefaultVtxoScript(
w.walletData.Pubkey, w.walletData.PubKey,
data.AspPubkey, data.ServerPubKey,
uint(data.UnilateralExitDelay), uint(data.UnilateralExitDelay),
) )
@@ -342,13 +342,13 @@ func (w *liquidWallet) getAddress(
offchainAddr := &common.Address{ offchainAddr := &common.Address{
HRP: data.Network.Addr, HRP: data.Network.Addr,
Asp: data.AspPubkey, Server: data.ServerPubKey,
VtxoTapKey: vtxoTapKey, VtxoTapKey: vtxoTapKey,
} }
boardingVtxoScript := tree.NewDefaultVtxoScript( boardingVtxoScript := tree.NewDefaultVtxoScript(
w.walletData.Pubkey, w.walletData.PubKey,
data.AspPubkey, data.ServerPubKey,
uint(data.UnilateralExitDelay*2), uint(data.UnilateralExitDelay*2),
) )

View File

@@ -20,7 +20,7 @@ const (
type walletData struct { type walletData struct {
EncryptedPrvkey string `json:"encrypted_private_key"` EncryptedPrvkey string `json:"encrypted_private_key"`
PasswordHash string `json:"password_hash"` PasswordHash string `json:"password_hash"`
Pubkey string `json:"pubkey"` PubKey string `json:"pubkey"`
} }
func (d walletData) isEmpty() bool { func (d walletData) isEmpty() bool {
@@ -30,12 +30,12 @@ func (d walletData) isEmpty() bool {
func (d walletData) decode() walletstore.WalletData { func (d walletData) decode() walletstore.WalletData {
encryptedPrvkey, _ := hex.DecodeString(d.EncryptedPrvkey) encryptedPrvkey, _ := hex.DecodeString(d.EncryptedPrvkey)
passwordHash, _ := hex.DecodeString(d.PasswordHash) passwordHash, _ := hex.DecodeString(d.PasswordHash)
buf, _ := hex.DecodeString(d.Pubkey) buf, _ := hex.DecodeString(d.PubKey)
pubkey, _ := secp256k1.ParsePubKey(buf) pubkey, _ := secp256k1.ParsePubKey(buf)
return walletstore.WalletData{ return walletstore.WalletData{
EncryptedPrvkey: encryptedPrvkey, EncryptedPrvkey: encryptedPrvkey,
PasswordHash: passwordHash, PasswordHash: passwordHash,
Pubkey: pubkey, PubKey: pubkey,
} }
} }
@@ -43,7 +43,7 @@ func (d walletData) asMap() map[string]string {
return map[string]string{ return map[string]string{
"encrypted_private_key": d.EncryptedPrvkey, "encrypted_private_key": d.EncryptedPrvkey,
"password_hash": d.PasswordHash, "password_hash": d.PasswordHash,
"pubkey": d.Pubkey, "pubkey": d.PubKey,
} }
} }
@@ -71,7 +71,7 @@ func (s *fileStore) AddWallet(data walletstore.WalletData) error {
wd := &walletData{ wd := &walletData{
EncryptedPrvkey: hex.EncodeToString(data.EncryptedPrvkey), EncryptedPrvkey: hex.EncodeToString(data.EncryptedPrvkey),
PasswordHash: hex.EncodeToString(data.PasswordHash), PasswordHash: hex.EncodeToString(data.PasswordHash),
Pubkey: hex.EncodeToString(data.Pubkey.SerializeCompressed()), PubKey: hex.EncodeToString(data.PubKey.SerializeCompressed()),
} }
if err := s.write(wd); err != nil { if err := s.write(wd); err != nil {

View File

@@ -7,7 +7,7 @@ import (
type WalletData struct { type WalletData struct {
EncryptedPrvkey []byte EncryptedPrvkey []byte
PasswordHash []byte PasswordHash []byte
Pubkey *secp256k1.PublicKey PubKey *secp256k1.PublicKey
} }
type WalletStore interface { type WalletStore interface {

View File

@@ -16,7 +16,7 @@ func TestWalletStore(t *testing.T) {
testWalletData := walletstore.WalletData{ testWalletData := walletstore.WalletData{
EncryptedPrvkey: make([]byte, 32), EncryptedPrvkey: make([]byte, 32),
PasswordHash: make([]byte, 32), PasswordHash: make([]byte, 32),
Pubkey: key.PubKey(), PubKey: key.PubKey(),
} }
tests := []struct { tests := []struct {

View File

@@ -29,18 +29,18 @@ func (w *singlekeyWallet) Create(
) (string, error) { ) (string, error) {
var privateKey *secp256k1.PrivateKey var privateKey *secp256k1.PrivateKey
if len(seed) <= 0 { if len(seed) <= 0 {
privKey, err := utils.GenerateRandomPrivateKey() prvkey, err := utils.GenerateRandomPrivateKey()
if err != nil { if err != nil {
return "", err return "", err
} }
privateKey = privKey privateKey = prvkey
} else { } else {
privKeyBytes, err := hex.DecodeString(seed) prvkeyBytes, err := hex.DecodeString(seed)
if err != nil { if err != nil {
return "", err return "", err
} }
privateKey = secp256k1.PrivKeyFromBytes(privKeyBytes) privateKey = secp256k1.PrivKeyFromBytes(prvkeyBytes)
} }
pwd := []byte(password) pwd := []byte(password)
@@ -55,7 +55,7 @@ func (w *singlekeyWallet) Create(
walletData := walletstore.WalletData{ walletData := walletstore.WalletData{
EncryptedPrvkey: encryptedPrivateKey, EncryptedPrvkey: encryptedPrivateKey,
PasswordHash: passwordHash, PasswordHash: passwordHash,
Pubkey: pubkey, PubKey: pubkey,
} }
if err := w.walletStore.AddWallet(walletData); err != nil { if err := w.walletStore.AddWallet(walletData); err != nil {
return "", err return "", err

View File

@@ -21,8 +21,8 @@ func TestWallet(t *testing.T) {
key, _ := btcec.NewPrivateKey() key, _ := btcec.NewPrivateKey()
password := "password" password := "password"
testStoreData := sdktypes.Config{ testStoreData := sdktypes.Config{
AspUrl: "localhost:7070", ServerUrl: "localhost:7070",
AspPubkey: key.PubKey(), ServerPubKey: key.PubKey(),
WalletType: wallet.SingleKeyWallet, WalletType: wallet.SingleKeyWallet,
ClientType: client.GrpcClient, ClientType: client.GrpcClient,
Network: common.LiquidRegTest, Network: common.LiquidRegTest,

View File

@@ -21,8 +21,8 @@ const (
) )
type storeData struct { type storeData struct {
AspUrl string `json:"asp_url"` ServerUrl string `json:"server_url"`
AspPubkey string `json:"asp_pubkey"` ServerPubKey string `json:"server_pubkey"`
WalletType string `json:"wallet_type"` WalletType string `json:"wallet_type"`
ClientType string `json:"client_type"` ClientType string `json:"client_type"`
ExplorerURL string `json:"explorer_url"` ExplorerURL string `json:"explorer_url"`
@@ -54,8 +54,8 @@ func (s *configStore) GetDatadir() string {
func (s *configStore) AddData(ctx context.Context, data types.Config) error { func (s *configStore) AddData(ctx context.Context, data types.Config) error {
sd := &storeData{ sd := &storeData{
AspUrl: data.AspUrl, ServerUrl: data.ServerUrl,
AspPubkey: hex.EncodeToString(data.AspPubkey.SerializeCompressed()), ServerPubKey: hex.EncodeToString(data.ServerPubKey.SerializeCompressed()),
WalletType: data.WalletType, WalletType: data.WalletType,
ClientType: data.ClientType, ClientType: data.ClientType,
Network: data.Network.Name, Network: data.Network.Name,
@@ -71,7 +71,7 @@ func (s *configStore) AddData(ctx context.Context, data types.Config) error {
} }
func (s *configStore) GetData(ctx context.Context) (*types.Config, error) { func (s *configStore) GetData(ctx context.Context) (*types.Config, error) {
key := s.store.Call("getItem", "asp_pubkey") key := s.store.Call("getItem", "server_pubkey")
if key.IsNull() || key.IsUndefined() { if key.IsNull() || key.IsUndefined() {
return nil, nil return nil, nil
} }
@@ -83,7 +83,7 @@ func (s *configStore) GetData(ctx context.Context) (*types.Config, error) {
return nil, nil return nil, nil
} }
aspPubkey, err := secp256k1.ParsePubKey(buf) serverPubkey, err := secp256k1.ParsePubKey(buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -95,8 +95,8 @@ func (s *configStore) GetData(ctx context.Context) (*types.Config, error) {
withTxFeed, _ := strconv.ParseBool(s.store.Call("getItem", "with_transaction_feed").String()) withTxFeed, _ := strconv.ParseBool(s.store.Call("getItem", "with_transaction_feed").String())
return &types.Config{ return &types.Config{
AspUrl: s.store.Call("getItem", "asp_url").String(), ServerUrl: s.store.Call("getItem", "server_url").String(),
AspPubkey: aspPubkey, ServerPubKey: serverPubkey,
WalletType: s.store.Call("getItem", "wallet_type").String(), WalletType: s.store.Call("getItem", "wallet_type").String(),
ClientType: s.store.Call("getItem", "client_type").String(), ClientType: s.store.Call("getItem", "client_type").String(),
Network: network, Network: network,

View File

@@ -30,7 +30,6 @@ func init() {
js.Global().Set("receive", ReceiveWrapper()) js.Global().Set("receive", ReceiveWrapper())
js.Global().Set("sendOnChain", SendOnChainWrapper()) js.Global().Set("sendOnChain", SendOnChainWrapper())
js.Global().Set("sendOffChain", SendOffChainWrapper()) js.Global().Set("sendOffChain", SendOffChainWrapper())
js.Global().Set("sendAsync", SendAsyncWrapper())
js.Global().Set("settle", SettleWrapper()) js.Global().Set("settle", SettleWrapper())
js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper()) js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper())
js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper()) js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper())
@@ -41,8 +40,8 @@ func init() {
js.Global().Set("setNostrNotificationRecipient", SetNostrNotificationRecipientWrapper()) js.Global().Set("setNostrNotificationRecipient", SetNostrNotificationRecipientWrapper())
js.Global().Set("listVtxos", ListVtxosWrapper()) js.Global().Set("listVtxos", ListVtxosWrapper())
js.Global().Set("getAspUrl", GetAspUrlWrapper()) js.Global().Set("getServerUrl", GetServerUrlWrapper())
js.Global().Set("getAspPubKeyHex", GetAspPubkeyWrapper()) js.Global().Set("getServerPubkeyHex", GetServerPubkeyWrapper())
js.Global().Set("getWalletType", GetWalletTypeWrapper()) js.Global().Set("getWalletType", GetWalletTypeWrapper())
js.Global().Set("getClientType", GetClientTypeWrapper()) js.Global().Set("getClientType", GetClientTypeWrapper())
js.Global().Set("getNetwork", GetNetworkWrapper()) js.Global().Set("getNetwork", GetNetworkWrapper())

View File

@@ -16,18 +16,18 @@ import (
type walletData struct { type walletData struct {
EncryptedPrvkey string `json:"encrypted_private_key"` EncryptedPrvkey string `json:"encrypted_private_key"`
PasswordHash string `json:"password_hash"` PasswordHash string `json:"password_hash"`
Pubkey string `json:"pubkey"` PubKey string `json:"pubkey"`
} }
func (d walletData) decode() *walletstore.WalletData { func (d walletData) decode() *walletstore.WalletData {
encryptedPrvkey, _ := hex.DecodeString(d.EncryptedPrvkey) encryptedPrvkey, _ := hex.DecodeString(d.EncryptedPrvkey)
passwordHash, _ := hex.DecodeString(d.PasswordHash) passwordHash, _ := hex.DecodeString(d.PasswordHash)
buf, _ := hex.DecodeString(d.Pubkey) buf, _ := hex.DecodeString(d.PubKey)
pubkey, _ := secp256k1.ParsePubKey(buf) pubkey, _ := secp256k1.ParsePubKey(buf)
return &walletstore.WalletData{ return &walletstore.WalletData{
EncryptedPrvkey: encryptedPrvkey, EncryptedPrvkey: encryptedPrvkey,
PasswordHash: passwordHash, PasswordHash: passwordHash,
Pubkey: pubkey, PubKey: pubkey,
} }
} }
@@ -44,7 +44,7 @@ func (s *walletStore) AddWallet(data walletstore.WalletData) error {
wd := &walletData{ wd := &walletData{
EncryptedPrvkey: hex.EncodeToString(data.EncryptedPrvkey), EncryptedPrvkey: hex.EncodeToString(data.EncryptedPrvkey),
PasswordHash: hex.EncodeToString(data.PasswordHash), PasswordHash: hex.EncodeToString(data.PasswordHash),
Pubkey: hex.EncodeToString(data.Pubkey.SerializeCompressed()), PubKey: hex.EncodeToString(data.PubKey.SerializeCompressed()),
} }
if err := s.writeData(wd); err != nil { if err := s.writeData(wd); err != nil {
@@ -57,7 +57,7 @@ func (s *walletStore) GetWallet() (*walletstore.WalletData, error) {
data := walletData{ data := walletData{
EncryptedPrvkey: s.store.Call("getItem", "encrypted_private_key").String(), EncryptedPrvkey: s.store.Call("getItem", "encrypted_private_key").String(),
PasswordHash: s.store.Call("getItem", "password_hash").String(), PasswordHash: s.store.Call("getItem", "password_hash").String(),
Pubkey: s.store.Call("getItem", "pubkey").String(), PubKey: s.store.Call("getItem", "pubkey").String(),
} }
return data.decode(), nil return data.decode(), nil
} }

View File

@@ -72,7 +72,7 @@ func InitWrapper() js.Func {
err := arkSdkClient.InitWithWallet(context.Background(), arksdk.InitWithWalletArgs{ err := arkSdkClient.InitWithWallet(context.Background(), arksdk.InitWithWalletArgs{
ClientType: args[1].String(), ClientType: args[1].String(),
Wallet: walletSvc, Wallet: walletSvc,
AspUrl: args[2].String(), ServerUrl: args[2].String(),
Seed: args[3].String(), Seed: args[3].String(),
Password: args[4].String(), Password: args[4].String(),
ExplorerURL: args[6].String(), ExplorerURL: args[6].String(),
@@ -227,28 +227,6 @@ func SendOnChainWrapper() js.Func {
} }
func SendOffChainWrapper() js.Func { func SendOffChainWrapper() 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, err := parseReceivers(args[0])
if err != nil {
return nil, err
}
txID, err := arkSdkClient.SendOffChain(
context.Background(), withExpiryCoinselect, receivers,
)
if err != nil {
return nil, err
}
return js.ValueOf(txID), nil
})
}
func SendAsyncWrapper() js.Func {
return JSPromise(func(args []js.Value) (interface{}, error) { return JSPromise(func(args []js.Value) (interface{}, error) {
if len(args) != 2 { if len(args) != 2 {
return nil, errors.New("invalid number of args") return nil, errors.New("invalid number of args")
@@ -260,11 +238,7 @@ func SendAsyncWrapper() js.Func {
return nil, err return nil, err
} }
if receivers == nil || len(receivers) == 0 { txID, err := arkSdkClient.SendOffChain(
return nil, errors.New("no receivers specified")
}
txID, err := arkSdkClient.SendAsync(
context.Background(), withExpiryCoinselect, receivers, context.Background(), withExpiryCoinselect, receivers,
) )
if err != nil { if err != nil {
@@ -340,25 +314,25 @@ func GetTransactionHistoryWrapper() js.Func {
}) })
} }
func GetAspUrlWrapper() js.Func { func GetServerUrlWrapper() js.Func {
return js.FuncOf(func(this js.Value, p []js.Value) interface{} { return js.FuncOf(func(this js.Value, p []js.Value) interface{} {
data, _ := arkSdkClient.GetConfigData(context.Background()) data, _ := arkSdkClient.GetConfigData(context.Background())
var url string var url string
if data != nil { if data != nil {
url = data.AspUrl url = data.ServerUrl
} }
return js.ValueOf(url) return js.ValueOf(url)
}) })
} }
func GetAspPubkeyWrapper() js.Func { func GetServerPubkeyWrapper() js.Func {
return js.FuncOf(func(this js.Value, p []js.Value) interface{} { return js.FuncOf(func(this js.Value, p []js.Value) interface{} {
data, _ := arkSdkClient.GetConfigData(context.Background()) data, _ := arkSdkClient.GetConfigData(context.Background())
var aspPubkey string var serverPubkey string
if data != nil { if data != nil {
aspPubkey = hex.EncodeToString(data.AspPubkey.SerializeCompressed()) serverPubkey = hex.EncodeToString(data.ServerPubKey.SerializeCompressed())
} }
return js.ValueOf(aspPubkey) return js.ValueOf(serverPubkey)
}) })
} }

View File

@@ -1,5 +1,5 @@
# Ark Server # Ark Server
The Ark Server is a Go implementation of an Ark Service Provider (ASP). An ASP it's an always-on server that provides bitcoin liquidity in the Ark layer-two protocol. It supports Bitcoin and Liquid (with more experimantal features using covenants). This is a Go implementation of the Ark server. An Ark server it's an always-on server that provides bitcoin liquidity in the Ark layer-two protocol. It supports Bitcoin and Liquid (with more experimantal features using covenants).
**ALPHA STAGE SOFTWARE: USE AT YOUR OWN RISK!** **ALPHA STAGE SOFTWARE: USE AT YOUR OWN RISK!**

View File

@@ -89,11 +89,11 @@ func (a *adminService) GetRoundDetails(ctx context.Context, roundId string) (*Ro
OutputsVtxos: []string{}, OutputsVtxos: []string{},
} }
for _, payment := range round.Payments { for _, request := range round.TxRequests {
// TODO: Add fees amount // TODO: Add fees amount
roundDetails.ForfeitedAmount += payment.TotalInputAmount() roundDetails.ForfeitedAmount += request.TotalInputAmount()
for _, receiver := range payment.Receivers { for _, receiver := range request.Receivers {
if receiver.IsOnchain() { if receiver.IsOnchain() {
roundDetails.TotalExitAmount += receiver.Amount roundDetails.TotalExitAmount += receiver.Amount
roundDetails.ExitAddresses = append(roundDetails.ExitAddresses, receiver.OnchainAddress) roundDetails.ExitAddresses = append(roundDetails.ExitAddresses, receiver.OnchainAddress)
@@ -103,7 +103,7 @@ func (a *adminService) GetRoundDetails(ctx context.Context, roundId string) (*Ro
roundDetails.TotalVtxosAmount += receiver.Amount roundDetails.TotalVtxosAmount += receiver.Amount
} }
for _, input := range payment.Inputs { for _, input := range request.Inputs {
roundDetails.InputsVtxos = append(roundDetails.InputsVtxos, input.Txid) roundDetails.InputsVtxos = append(roundDetails.InputsVtxos, input.Txid)
} }
} }
@@ -134,7 +134,7 @@ func (a *adminService) GetScheduledSweeps(ctx context.Context) ([]ScheduledSweep
for _, round := range sweepableRounds { for _, round := range sweepableRounds {
sweepable, err := findSweepableOutputs( sweepable, err := findSweepableOutputs(
ctx, a.walletSvc, a.txBuilder, a.sweeperTimeUnit, round.CongestionTree, ctx, a.walletSvc, a.txBuilder, a.sweeperTimeUnit, round.VtxoTree,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -44,7 +44,7 @@ type covenantService struct {
scanner ports.BlockchainScanner scanner ports.BlockchainScanner
sweeper *sweeper sweeper *sweeper
paymentRequests *paymentsMap txRequests *txRequestsQueue
forfeitTxs *forfeitTxsMap forfeitTxs *forfeitTxsMap
eventsCh chan domain.RoundEvent eventsCh chan domain.RoundEvent
@@ -94,7 +94,7 @@ func NewCovenantService(
builder: builder, builder: builder,
scanner: scanner, scanner: scanner,
sweeper: newSweeper(walletSvc, repoManager, builder, scheduler, notificationPrefix), sweeper: newSweeper(walletSvc, repoManager, builder, scheduler, notificationPrefix),
paymentRequests: newPaymentsMap(), txRequests: newTxRequestsQueue(),
forfeitTxs: newForfeitTxsMap(builder), forfeitTxs: newForfeitTxsMap(builder),
eventsCh: make(chan domain.RoundEvent), eventsCh: make(chan domain.RoundEvent),
transactionEventsCh: make(chan TransactionEvent), transactionEventsCh: make(chan TransactionEvent),
@@ -287,14 +287,14 @@ func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input)
vtxosInputs = append(vtxosInputs, vtxo) vtxosInputs = append(vtxosInputs, vtxo)
} }
payment, err := domain.NewPayment(vtxosInputs) request, err := domain.NewTxRequest(vtxosInputs)
if err != nil { if err != nil {
return "", err return "", err
} }
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil { if err := s.txRequests.push(*request, boardingInputs); err != nil {
return "", err return "", err
} }
return payment.Id, nil return request.Id, nil
} }
func (s *covenantService) newBoardingInput(tx *transaction.Transaction, input ports.Input) (*ports.BoardingInput, error) { func (s *covenantService) newBoardingInput(tx *transaction.Transaction, input ports.Input) (*ports.BoardingInput, error) {
@@ -344,7 +344,7 @@ func (s *covenantService) newBoardingInput(tx *transaction.Transaction, input po
func (s *covenantService) ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error { func (s *covenantService) ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error {
// Check credentials // Check credentials
payment, ok := s.paymentRequests.view(creds) request, ok := s.txRequests.view(creds)
if !ok { if !ok {
return fmt.Errorf("invalid credentials") return fmt.Errorf("invalid credentials")
} }
@@ -360,14 +360,14 @@ func (s *covenantService) ClaimVtxos(ctx context.Context, creds string, receiver
} }
} }
if err := payment.AddReceivers(receivers); err != nil { if err := request.AddReceivers(receivers); err != nil {
return err return err
} }
return s.paymentRequests.update(*payment) return s.txRequests.update(*request)
} }
func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) error { func (s *covenantService) UpdateTxRequestStatus(_ context.Context, id string) error {
return s.paymentRequests.updatePingTimestamp(id) return s.txRequests.updatePingTimestamp(id)
} }
func (s *covenantService) SubmitRedeemTx(context.Context, string) (string, error) { func (s *covenantService) SubmitRedeemTx(context.Context, string) (string, error) {
@@ -397,7 +397,7 @@ func (s *covenantService) ListVtxos(ctx context.Context, address string) ([]doma
return nil, nil, fmt.Errorf("failed to decode address: %s", err) return nil, nil, fmt.Errorf("failed to decode address: %s", err)
} }
if !bytes.Equal(schnorr.SerializePubKey(decodedAddress.Asp), schnorr.SerializePubKey(s.pubkey)) { if !bytes.Equal(schnorr.SerializePubKey(decodedAddress.Server), schnorr.SerializePubKey(s.pubkey)) {
return nil, nil, fmt.Errorf("address does not match server pubkey") return nil, nil, fmt.Errorf("address does not match server pubkey")
} }
@@ -414,8 +414,8 @@ func (s *covenantService) GetTransactionEventsChannel(ctx context.Context) <-cha
return s.transactionEventsCh return s.transactionEventsCh
} }
func (s *covenantService) GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.Round, error) { func (s *covenantService) GetRoundByTxid(ctx context.Context, roundTxid string) (*domain.Round, error) {
return s.repoManager.Rounds().GetRoundWithTxid(ctx, poolTxid) return s.repoManager.Rounds().GetRoundWithTxid(ctx, roundTxid)
} }
func (s *covenantService) GetCurrentRound(ctx context.Context) (*domain.Round, error) { func (s *covenantService) GetCurrentRound(ctx context.Context) (*domain.Round, error) {
@@ -472,11 +472,11 @@ func (s *covenantService) GetInfo(ctx context.Context) (*ServiceInfo, error) {
}, nil }, nil
} }
func (s *covenantService) RegisterCosignerPubkey(ctx context.Context, paymentId string, _ string) error { func (s *covenantService) RegisterCosignerPubkey(ctx context.Context, requestID string, _ string) error {
// if the user sends an ephemeral pubkey, something is going wrong client-side // if the user sends an ephemeral pubkey, something is going wrong client-side
// we should delete the associated payment // we should delete the associated tx request
if err := s.paymentRequests.delete(paymentId); err != nil { if err := s.txRequests.delete(requestID); err != nil {
log.WithError(err).Warn("failed to delete payment") log.WithError(err).Warnf("failed to delete tx request %s", requestID)
} }
return ErrTreeSigningNotRequired return ErrTreeSigningNotRequired
@@ -554,22 +554,22 @@ func (s *covenantService) startFinalization() {
return return
} }
// TODO: understand how many payments must be popped from the queue and actually registered for the round // TODO: understand how many tx requests must be popped from the queue and actually registered for the round
num := s.paymentRequests.len() num := s.txRequests.len()
if num == 0 { if num == 0 {
roundAborted = true roundAborted = true
err := fmt.Errorf("no payments registered") err := fmt.Errorf("no tx requests registered")
round.Fail(fmt.Errorf("round aborted: %s", err)) round.Fail(fmt.Errorf("round aborted: %s", err))
log.WithError(err).Debugf("round %s aborted", round.Id) log.WithError(err).Debugf("round %s aborted", round.Id)
return return
} }
if num > paymentsThreshold { if num > txRequestsThreshold {
num = paymentsThreshold num = txRequestsThreshold
} }
payments, boardingInputs, _, _ := s.paymentRequests.pop(num) requests, boardingInputs, _, _ := s.txRequests.pop(num)
if _, err := round.RegisterPayments(payments); err != nil { if _, err := round.RegisterTxRequests(requests); err != nil {
round.Fail(fmt.Errorf("failed to register payments: %s", err)) round.Fail(fmt.Errorf("failed to register tx requests: %s", err))
log.WithError(err).Warn("failed to register payments") log.WithError(err).Warn("failed to register tx requests")
return return
} }
@@ -580,18 +580,18 @@ func (s *covenantService) startFinalization() {
return return
} }
unsignedPoolTx, tree, connectorAddress, connectors, err := s.builder.BuildRoundTx(s.pubkey, payments, boardingInputs, sweptRounds) unsignedRoundTx, tree, connectorAddress, connectors, err := s.builder.BuildRoundTx(s.pubkey, requests, boardingInputs, sweptRounds)
if err != nil { if err != nil {
round.Fail(fmt.Errorf("failed to create pool tx: %s", err)) round.Fail(fmt.Errorf("failed to create round tx: %s", err))
log.WithError(err).Warn("failed to create pool tx") log.WithError(err).Warn("failed to create round tx")
return return
} }
log.Debugf("pool tx created for round %s", round.Id) log.Debugf("round tx created for round %s", round.Id)
s.forfeitTxs.init(connectors, payments) s.forfeitTxs.init(connectors, requests)
if _, err := round.StartFinalization( if _, err := round.StartFinalization(
connectorAddress, connectors, tree, unsignedPoolTx, connectorAddress, connectors, tree, unsignedRoundTx,
); err != nil { ); err != nil {
round.Fail(fmt.Errorf("failed to start finalization: %s", err)) round.Fail(fmt.Errorf("failed to start finalization: %s", err))
log.WithError(err).Warn("failed to start finalization") log.WithError(err).Warn("failed to start finalization")
@@ -670,8 +670,8 @@ func (s *covenantService) finalizeRound() {
txid, err := s.wallet.BroadcastTransaction(ctx, signedRoundTx) txid, err := s.wallet.BroadcastTransaction(ctx, signedRoundTx)
if err != nil { if err != nil {
log.Debugf("failed to broadcast round tx: %s", signedRoundTx) log.Debugf("failed to broadcast round tx: %s", signedRoundTx)
changes = round.Fail(fmt.Errorf("failed to broadcast pool tx: %s", err)) changes = round.Fail(fmt.Errorf("failed to broadcast round tx: %s", err))
log.WithError(err).Warn("failed to broadcast pool tx") log.WithError(err).Warn("failed to broadcast round tx")
return return
} }
@@ -682,7 +682,7 @@ func (s *covenantService) finalizeRound() {
return return
} }
log.Debugf("finalized round %s with pool tx %s", round.Id, round.Txid) log.Debugf("finalized round %s with round tx %s", round.Id, round.Txid)
} }
func (s *covenantService) listenToScannerNotifications() { func (s *covenantService) listenToScannerNotifications() {
@@ -906,7 +906,7 @@ func (s *covenantService) updateVtxoSet(round *domain.Round) {
ctx := context.Background() ctx := context.Background()
repo := s.repoManager.Vtxos() repo := s.repoManager.Vtxos()
spentVtxos := getSpentVtxos(round.Payments) spentVtxos := getSpentVtxos(round.TxRequests)
if len(spentVtxos) > 0 { if len(spentVtxos) > 0 {
for { for {
if err := repo.SpendVtxos(ctx, spentVtxos, round.Txid); err != nil { if err := repo.SpendVtxos(ctx, spentVtxos, round.Txid); err != nil {
@@ -970,8 +970,8 @@ func (s *covenantService) updateVtxoSet(round *domain.Round) {
} }
} }
s.transactionEventsCh <- RoundTransactionEvent{ s.transactionEventsCh <- RoundTransactionEvent{
RoundTxID: round.Txid, RoundTxid: round.Txid,
SpentVtxos: getSpentVtxos(round.Payments), SpentVtxos: getSpentVtxos(round.TxRequests),
SpendableVtxos: s.getNewVtxos(round), SpendableVtxos: s.getNewVtxos(round),
ClaimedBoardingInputs: boardingInputs, ClaimedBoardingInputs: boardingInputs,
} }
@@ -984,7 +984,7 @@ func (s *covenantService) propagateEvents(round *domain.Round) {
case domain.RoundFinalizationStarted: case domain.RoundFinalizationStarted:
ev := domain.RoundFinalizationStarted{ ev := domain.RoundFinalizationStarted{
Id: e.Id, Id: e.Id,
CongestionTree: e.CongestionTree, VtxoTree: e.VtxoTree,
Connectors: e.Connectors, Connectors: e.Connectors,
RoundTx: e.RoundTx, RoundTx: e.RoundTx,
MinRelayFeeRate: int64(s.wallet.MinRelayFeeRate(context.Background())), MinRelayFeeRate: int64(s.wallet.MinRelayFeeRate(context.Background())),
@@ -1006,20 +1006,20 @@ func (s *covenantService) scheduleSweepVtxosForRound(round *domain.Round) {
expirationTime := s.sweeper.scheduler.AddNow(s.roundLifetime) expirationTime := s.sweeper.scheduler.AddNow(s.roundLifetime)
if err := s.sweeper.schedule( if err := s.sweeper.schedule(
expirationTime, round.Txid, round.CongestionTree, expirationTime, round.Txid, round.VtxoTree,
); err != nil { ); err != nil {
log.WithError(err).Warn("failed to schedule sweep tx") log.WithError(err).Warn("failed to schedule sweep tx")
} }
} }
func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo { func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo {
if len(round.CongestionTree) <= 0 { if len(round.VtxoTree) <= 0 {
return nil return nil
} }
createdAt := time.Now().Unix() createdAt := time.Now().Unix()
leaves := round.CongestionTree.Leaves() leaves := round.VtxoTree.Leaves()
vtxos := make([]domain.Vtxo, 0) vtxos := make([]domain.Vtxo, 0)
for _, node := range leaves { for _, node := range leaves {
tx, _ := psetv2.NewPsetFromBase64(node.Tx) tx, _ := psetv2.NewPsetFromBase64(node.Tx)
@@ -1038,7 +1038,7 @@ func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo {
vtxos = append(vtxos, domain.Vtxo{ vtxos = append(vtxos, domain.Vtxo{
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)}, VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
Pubkey: vtxoPubkey, PubKey: vtxoPubkey,
Amount: uint64(out.Value), Amount: uint64(out.Value),
RoundTxid: round.Txid, RoundTxid: round.Txid,
CreatedAt: createdAt, CreatedAt: createdAt,
@@ -1113,7 +1113,7 @@ func (s *covenantService) restoreWatchingVtxos() error {
func (s *covenantService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string, error) { func (s *covenantService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string, error) {
indexedScripts := make(map[string]struct{}) indexedScripts := make(map[string]struct{})
for _, vtxo := range vtxos { for _, vtxo := range vtxos {
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey) vtxoTapKeyBytes, err := hex.DecodeString(vtxo.PubKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -46,7 +46,7 @@ type covenantlessService struct {
scanner ports.BlockchainScanner scanner ports.BlockchainScanner
sweeper *sweeper sweeper *sweeper
paymentRequests *paymentsMap txRequests *txRequestsQueue
forfeitTxs *forfeitTxsMap forfeitTxs *forfeitTxsMap
eventsCh chan domain.RoundEvent eventsCh chan domain.RoundEvent
@@ -99,7 +99,7 @@ func NewCovenantlessService(
builder: builder, builder: builder,
scanner: scanner, scanner: scanner,
sweeper: newSweeper(walletSvc, repoManager, builder, scheduler, noteUriPrefix), sweeper: newSweeper(walletSvc, repoManager, builder, scheduler, noteUriPrefix),
paymentRequests: newPaymentsMap(), txRequests: newTxRequestsQueue(),
forfeitTxs: newForfeitTxsMap(builder), forfeitTxs: newForfeitTxsMap(builder),
eventsCh: make(chan domain.RoundEvent), eventsCh: make(chan domain.RoundEvent),
transactionEventsCh: make(chan TransactionEvent), transactionEventsCh: make(chan TransactionEvent),
@@ -260,37 +260,37 @@ func (s *covenantlessService) SubmitRedeemTx(
} }
// verify that the user signs a forfeit closure // verify that the user signs a forfeit closure
var userPubKey *secp256k1.PublicKey var userPubkey *secp256k1.PublicKey
aspXOnlyPubKey := schnorr.SerializePubKey(s.pubkey) serverXOnlyPubkey := schnorr.SerializePubKey(s.pubkey)
for _, sig := range input.TaprootScriptSpendSig { for _, sig := range input.TaprootScriptSpendSig {
if !bytes.Equal(sig.XOnlyPubKey, aspXOnlyPubKey) { if !bytes.Equal(sig.XOnlyPubKey, serverXOnlyPubkey) {
parsed, err := schnorr.ParsePubKey(sig.XOnlyPubKey) parsed, err := schnorr.ParsePubKey(sig.XOnlyPubKey)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to parse pubkey: %s", err) return "", fmt.Errorf("failed to parse pubkey: %s", err)
} }
userPubKey = parsed userPubkey = parsed
break break
} }
} }
if userPubKey == nil { if userPubkey == nil {
return "", fmt.Errorf("redeem transaction is not signed") return "", fmt.Errorf("redeem transaction is not signed")
} }
vtxoPublicKeyBytes, err := hex.DecodeString(vtxo.Pubkey) vtxoPubkeyBuf, err := hex.DecodeString(vtxo.PubKey)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to decode vtxo pubkey: %s", err) return "", fmt.Errorf("failed to decode vtxo pubkey: %s", err)
} }
vtxoTapKey, err := schnorr.ParsePubKey(vtxoPublicKeyBytes) vtxoPubkey, err := schnorr.ParsePubKey(vtxoPubkeyBuf)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to parse vtxo pubkey: %s", err) return "", fmt.Errorf("failed to parse vtxo pubkey: %s", err)
} }
// verify witness utxo // verify witness utxo
pkscript, err := common.P2TRScript(vtxoTapKey) pkscript, err := common.P2TRScript(vtxoPubkey)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to get pkscript: %s", err) return "", fmt.Errorf("failed to get pkscript: %s", err)
} }
@@ -378,8 +378,15 @@ func (s *covenantlessService) SubmitRedeemTx(
return "", fmt.Errorf("no valid vtxo found") return "", fmt.Errorf("no valid vtxo found")
} }
newVtxos := make([]domain.Vtxo, 0, len(redeemPtx.UnsignedTx.TxOut)) // sign the redeem tx
signedRedeemTx, err := s.wallet.SignTransactionTapscript(ctx, redeemTx, nil)
if err != nil {
return "", fmt.Errorf("failed to sign redeem tx: %s", err)
}
// Create new vtxos, update spent vtxos state
newVtxos := make([]domain.Vtxo, 0, len(redeemPtx.UnsignedTx.TxOut))
for outIndex, out := range outputs { for outIndex, out := range outputs {
vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:]) vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:])
if err != nil { if err != nil {
@@ -393,24 +400,15 @@ func (s *covenantlessService) SubmitRedeemTx(
Txid: redeemTxid, Txid: redeemTxid,
VOut: uint32(outIndex), VOut: uint32(outIndex),
}, },
Pubkey: vtxoPubkey, PubKey: vtxoPubkey,
Amount: uint64(out.Value), Amount: uint64(out.Value),
ExpireAt: expiration, ExpireAt: expiration,
RoundTxid: roundTxid, RoundTxid: roundTxid,
RedeemTx: redeemTx, RedeemTx: signedRedeemTx,
CreatedAt: time.Now().Unix(), CreatedAt: time.Now().Unix(),
}) })
} }
// sign the redeem tx
signedRedeemTx, err := s.wallet.SignTransactionTapscript(ctx, redeemTx, nil)
if err != nil {
return "", fmt.Errorf("failed to sign redeem tx: %s", err)
}
// create new vtxos, update spent vtxos state
if err := s.repoManager.Vtxos().AddVtxos(ctx, newVtxos); err != nil { if err := s.repoManager.Vtxos().AddVtxos(ctx, newVtxos); err != nil {
return "", fmt.Errorf("failed to add vtxos: %s", err) return "", fmt.Errorf("failed to add vtxos: %s", err)
} }
@@ -429,7 +427,7 @@ func (s *covenantlessService) SubmitRedeemTx(
go func() { go func() {
s.transactionEventsCh <- RedeemTransactionEvent{ s.transactionEventsCh <- RedeemTransactionEvent{
AsyncTxID: redeemTxid, RedeemTxid: redeemTxid,
SpentVtxos: spentVtxoKeys, SpentVtxos: spentVtxoKeys,
SpendableVtxos: newVtxos, SpendableVtxos: newVtxos,
} }
@@ -492,16 +490,16 @@ func (s *covenantlessService) SpendNotes(ctx context.Context, notes []note.Note)
} }
} }
payment, err := domain.NewPayment(make([]domain.Vtxo, 0)) request, err := domain.NewTxRequest(make([]domain.Vtxo, 0))
if err != nil { if err != nil {
return "", fmt.Errorf("failed to create payment: %s", err) return "", fmt.Errorf("failed to create tx request: %s", err)
} }
if err := s.paymentRequests.pushWithNotes(*payment, notes); err != nil { if err := s.txRequests.pushWithNotes(*request, notes); err != nil {
return "", fmt.Errorf("failed to push payment: %s", err) return "", fmt.Errorf("failed to push tx requests: %s", err)
} }
return payment.Id, nil return request.Id, nil
} }
func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Input) (string, error) { func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Input) (string, error) {
@@ -600,14 +598,14 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp
vtxosInputs = append(vtxosInputs, vtxo) vtxosInputs = append(vtxosInputs, vtxo)
} }
payment, err := domain.NewPayment(vtxosInputs) request, err := domain.NewTxRequest(vtxosInputs)
if err != nil { if err != nil {
return "", err return "", err
} }
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil { if err := s.txRequests.push(*request, boardingInputs); err != nil {
return "", err return "", err
} }
return payment.Id, nil return request.Id, nil
} }
func (s *covenantlessService) newBoardingInput(tx wire.MsgTx, input ports.Input) (*ports.BoardingInput, error) { func (s *covenantlessService) newBoardingInput(tx wire.MsgTx, input ports.Input) (*ports.BoardingInput, error) {
@@ -627,12 +625,12 @@ func (s *covenantlessService) newBoardingInput(tx wire.MsgTx, input ports.Input)
return nil, fmt.Errorf("failed to get taproot key: %s", err) return nil, fmt.Errorf("failed to get taproot key: %s", err)
} }
expectedScriptPubKey, err := common.P2TRScript(tapKey) expectedScriptPubkey, err := common.P2TRScript(tapKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get script pubkey: %s", err) return nil, fmt.Errorf("failed to get script pubkey: %s", err)
} }
if !bytes.Equal(output.PkScript, expectedScriptPubKey) { if !bytes.Equal(output.PkScript, expectedScriptPubkey) {
return nil, fmt.Errorf("descriptor does not match script in transaction output") return nil, fmt.Errorf("descriptor does not match script in transaction output")
} }
@@ -648,7 +646,7 @@ func (s *covenantlessService) newBoardingInput(tx wire.MsgTx, input ports.Input)
func (s *covenantlessService) ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error { func (s *covenantlessService) ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error {
// Check credentials // Check credentials
payment, ok := s.paymentRequests.view(creds) request, ok := s.txRequests.view(creds)
if !ok { if !ok {
return fmt.Errorf("invalid credentials") return fmt.Errorf("invalid credentials")
} }
@@ -664,14 +662,14 @@ func (s *covenantlessService) ClaimVtxos(ctx context.Context, creds string, rece
} }
} }
if err := payment.AddReceivers(receivers); err != nil { if err := request.AddReceivers(receivers); err != nil {
return err return err
} }
return s.paymentRequests.update(*payment) return s.txRequests.update(*request)
} }
func (s *covenantlessService) UpdatePaymentStatus(_ context.Context, id string) error { func (s *covenantlessService) UpdateTxRequestStatus(_ context.Context, id string) error {
return s.paymentRequests.updatePingTimestamp(id) return s.txRequests.updatePingTimestamp(id)
} }
func (s *covenantlessService) SignVtxos(ctx context.Context, forfeitTxs []string) error { func (s *covenantlessService) SignVtxos(ctx context.Context, forfeitTxs []string) error {
@@ -697,7 +695,7 @@ func (s *covenantlessService) ListVtxos(ctx context.Context, address string) ([]
return nil, nil, fmt.Errorf("failed to decode address: %s", err) return nil, nil, fmt.Errorf("failed to decode address: %s", err)
} }
if !bytes.Equal(schnorr.SerializePubKey(decodedAddress.Asp), schnorr.SerializePubKey(s.pubkey)) { if !bytes.Equal(schnorr.SerializePubKey(decodedAddress.Server), schnorr.SerializePubKey(s.pubkey)) {
return nil, nil, fmt.Errorf("address does not match server pubkey") return nil, nil, fmt.Errorf("address does not match server pubkey")
} }
@@ -818,18 +816,18 @@ func calcNextMarketHour(marketHourStartTime, marketHourEndTime time.Time, period
} }
} }
func (s *covenantlessService) RegisterCosignerPubkey(ctx context.Context, paymentId string, pubkey string) error { func (s *covenantlessService) RegisterCosignerPubkey(ctx context.Context, requestID string, pubkey string) error {
pubkeyBytes, err := hex.DecodeString(pubkey) pubkeyBytes, err := hex.DecodeString(pubkey)
if err != nil { if err != nil {
return fmt.Errorf("failed to decode hex pubkey: %s", err) return fmt.Errorf("failed to decode hex pubkey: %s", err)
} }
ephemeralPublicKey, err := secp256k1.ParsePubKey(pubkeyBytes) ephemeralPubkey, err := secp256k1.ParsePubKey(pubkeyBytes)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse pubkey: %s", err) return fmt.Errorf("failed to parse pubkey: %s", err)
} }
return s.paymentRequests.pushEphemeralKey(paymentId, ephemeralPublicKey) return s.txRequests.pushEphemeralKey(requestID, ephemeralPubkey)
} }
func (s *covenantlessService) RegisterCosignerNonces( func (s *covenantlessService) RegisterCosignerNonces(
@@ -853,7 +851,7 @@ func (s *covenantlessService) RegisterCosignerNonces(
session.nonces[pubkey] = nonces session.nonces[pubkey] = nonces
if len(session.nonces) == session.nbCosigners-1 { // exclude the ASP if len(session.nonces) == session.nbCosigners-1 { // exclude the server
go func() { go func() {
session.nonceDoneC <- struct{}{} session.nonceDoneC <- struct{}{}
}() }()
@@ -884,7 +882,7 @@ func (s *covenantlessService) RegisterCosignerSignatures(
session.signatures[pubkey] = signatures session.signatures[pubkey] = signatures
if len(session.signatures) == session.nbCosigners-1 { // exclude the ASP if len(session.signatures) == session.nbCosigners-1 { // exclude the server
go func() { go func() {
session.sigDoneC <- struct{}{} session.sigDoneC <- struct{}{}
}() }()
@@ -992,31 +990,31 @@ func (s *covenantlessService) startFinalization() {
return return
} }
// TODO: understand how many payments must be popped from the queue and actually registered for the round // TODO: understand how many tx requests must be popped from the queue and actually registered for the round
num := s.paymentRequests.len() num := s.txRequests.len()
if num == 0 { if num == 0 {
roundAborted = true roundAborted = true
err := fmt.Errorf("no payments registered") err := fmt.Errorf("no tx requests registered")
round.Fail(fmt.Errorf("round aborted: %s", err)) round.Fail(fmt.Errorf("round aborted: %s", err))
log.WithError(err).Debugf("round %s aborted", round.Id) log.WithError(err).Debugf("round %s aborted", round.Id)
return return
} }
if num > paymentsThreshold { if num > txRequestsThreshold {
num = paymentsThreshold num = txRequestsThreshold
} }
payments, boardingInputs, cosigners, paymentsNotes := s.paymentRequests.pop(num) requests, boardingInputs, cosigners, redeeemedNotes := s.txRequests.pop(num)
if len(payments) > len(cosigners) { if len(requests) > len(cosigners) {
err := fmt.Errorf("missing ephemeral key for payments") err := fmt.Errorf("missing ephemeral key for tx requests")
round.Fail(fmt.Errorf("round aborted: %s", err)) round.Fail(fmt.Errorf("round aborted: %s", err))
log.WithError(err).Debugf("round %s aborted", round.Id) log.WithError(err).Debugf("round %s aborted", round.Id)
return return
} }
notes = paymentsNotes notes = redeeemedNotes
if _, err := round.RegisterPayments(payments); err != nil { if _, err := round.RegisterTxRequests(requests); err != nil {
round.Fail(fmt.Errorf("failed to register payments: %s", err)) round.Fail(fmt.Errorf("failed to register tx requests: %s", err))
log.WithError(err).Warn("failed to register payments") log.WithError(err).Warn("failed to register tx requests")
return return
} }
@@ -1038,7 +1036,7 @@ func (s *covenantlessService) startFinalization() {
unsignedRoundTx, vtxoTree, connectorAddress, connectors, err := s.builder.BuildRoundTx( unsignedRoundTx, vtxoTree, connectorAddress, connectors, err := s.builder.BuildRoundTx(
s.pubkey, s.pubkey,
payments, requests,
boardingInputs, boardingInputs,
sweptRounds, sweptRounds,
cosigners..., cosigners...,
@@ -1050,10 +1048,10 @@ func (s *covenantlessService) startFinalization() {
} }
log.Debugf("round tx created for round %s", round.Id) log.Debugf("round tx created for round %s", round.Id)
s.forfeitTxs.init(connectors, payments) s.forfeitTxs.init(connectors, requests)
if len(vtxoTree) > 0 { if len(vtxoTree) > 0 {
log.Debugf("signing congestion tree for round %s", round.Id) log.Debugf("signing vtxo tree for round %s", round.Id)
signingSession := newMusigSigningSession(len(cosigners)) signingSession := newMusigSigningSession(len(cosigners))
s.treeSigningSessions[round.Id] = signingSession s.treeSigningSessions[round.Id] = signingSession
@@ -1094,11 +1092,11 @@ func (s *covenantlessService) startFinalization() {
return return
} }
aspSignerSession := bitcointree.NewTreeSignerSession( serverSignerSession := bitcointree.NewTreeSignerSession(
ephemeralKey, sharedOutputAmount, vtxoTree, root.CloneBytes(), ephemeralKey, sharedOutputAmount, vtxoTree, root.CloneBytes(),
) )
nonces, err := aspSignerSession.GetNonces() nonces, err := serverSignerSession.GetNonces()
if err != nil { if err != nil {
round.Fail(fmt.Errorf("failed to get nonces: %s", err)) round.Fail(fmt.Errorf("failed to get nonces: %s", err))
log.WithError(err).Warn("failed to get nonces") log.WithError(err).Warn("failed to get nonces")
@@ -1142,33 +1140,33 @@ func (s *covenantlessService) startFinalization() {
s.propagateRoundSigningNoncesGeneratedEvent(aggragatedNonces) s.propagateRoundSigningNoncesGeneratedEvent(aggragatedNonces)
if err := aspSignerSession.SetKeys(cosigners); err != nil { if err := serverSignerSession.SetKeys(cosigners); err != nil {
round.Fail(fmt.Errorf("failed to set keys: %s", err)) round.Fail(fmt.Errorf("failed to set keys: %s", err))
log.WithError(err).Warn("failed to set keys") log.WithError(err).Warn("failed to set keys")
return return
} }
if err := aspSignerSession.SetAggregatedNonces(aggragatedNonces); err != nil { if err := serverSignerSession.SetAggregatedNonces(aggragatedNonces); err != nil {
round.Fail(fmt.Errorf("failed to set aggregated nonces: %s", err)) round.Fail(fmt.Errorf("failed to set aggregated nonces: %s", err))
log.WithError(err).Warn("failed to set aggregated nonces") log.WithError(err).Warn("failed to set aggregated nonces")
return return
} }
// sign the tree as ASP // sign the tree as server
aspTreeSigs, err := aspSignerSession.Sign() serverTreeSigs, err := serverSignerSession.Sign()
if err != nil { if err != nil {
round.Fail(fmt.Errorf("failed to sign tree: %s", err)) round.Fail(fmt.Errorf("failed to sign tree: %s", err))
log.WithError(err).Warn("failed to sign tree") log.WithError(err).Warn("failed to sign tree")
return return
} }
if err := coordinator.AddSig(ephemeralKey.PubKey(), aspTreeSigs); err != nil { if err := coordinator.AddSig(ephemeralKey.PubKey(), serverTreeSigs); err != nil {
round.Fail(fmt.Errorf("failed to add signature: %s", err)) round.Fail(fmt.Errorf("failed to add signature: %s", err))
log.WithError(err).Warn("failed to add signature") log.WithError(err).Warn("failed to add signature")
return return
} }
log.Debugf("ASP tree signed for round %s", round.Id) log.Debugf("tree signed by us for round %s", round.Id)
signaturesTimer := time.NewTimer(thirdOfRemainingDuration) signaturesTimer := time.NewTimer(thirdOfRemainingDuration)
@@ -1199,7 +1197,7 @@ func (s *covenantlessService) startFinalization() {
return return
} }
log.Debugf("congestion tree signed for round %s", round.Id) log.Debugf("vtxo tree signed for round %s", round.Id)
vtxoTree = signedTree vtxoTree = signedTree
} }
@@ -1216,11 +1214,11 @@ func (s *covenantlessService) startFinalization() {
} }
func (s *covenantlessService) propagateRoundSigningStartedEvent( func (s *covenantlessService) propagateRoundSigningStartedEvent(
unsignedCongestionTree tree.CongestionTree, cosigners []*secp256k1.PublicKey, unsignedVtxoTree tree.VtxoTree, cosigners []*secp256k1.PublicKey,
) { ) {
ev := RoundSigningStarted{ ev := RoundSigningStarted{
Id: s.currentRound.Id, Id: s.currentRound.Id,
UnsignedVtxoTree: unsignedCongestionTree, UnsignedVtxoTree: unsignedVtxoTree,
Cosigners: cosigners, Cosigners: cosigners,
UnsignedRoundTx: s.currentRound.UnsignedTx, UnsignedRoundTx: s.currentRound.UnsignedTx,
} }
@@ -1311,7 +1309,7 @@ func (s *covenantlessService) finalizeRound(notes []note.Note) {
txid, err := s.wallet.BroadcastTransaction(ctx, signedRoundTx) txid, err := s.wallet.BroadcastTransaction(ctx, signedRoundTx)
if err != nil { if err != nil {
changes = round.Fail(fmt.Errorf("failed to broadcast pool tx: %s", err)) changes = round.Fail(fmt.Errorf("failed to broadcast round tx: %s", err))
return return
} }
@@ -1331,14 +1329,14 @@ func (s *covenantlessService) finalizeRound(notes []note.Note) {
go func() { go func() {
s.transactionEventsCh <- RoundTransactionEvent{ s.transactionEventsCh <- RoundTransactionEvent{
RoundTxID: round.Txid, RoundTxid: round.Txid,
SpentVtxos: getSpentVtxos(round.Payments), SpentVtxos: getSpentVtxos(round.TxRequests),
SpendableVtxos: s.getNewVtxos(round), SpendableVtxos: s.getNewVtxos(round),
ClaimedBoardingInputs: boardingInputs, ClaimedBoardingInputs: boardingInputs,
} }
}() }()
log.Debugf("finalized round %s with pool tx %s", round.Id, round.Txid) log.Debugf("finalized round %s with round tx %s", round.Id, round.Txid)
} }
func (s *covenantlessService) listenToScannerNotifications() { func (s *covenantlessService) listenToScannerNotifications() {
@@ -1491,7 +1489,7 @@ func (s *covenantlessService) updateVtxoSet(round *domain.Round) {
ctx := context.Background() ctx := context.Background()
repo := s.repoManager.Vtxos() repo := s.repoManager.Vtxos()
spentVtxos := getSpentVtxos(round.Payments) spentVtxos := getSpentVtxos(round.TxRequests)
if len(spentVtxos) > 0 { if len(spentVtxos) > 0 {
for { for {
if err := repo.SpendVtxos(ctx, spentVtxos, round.Txid); err != nil { if err := repo.SpendVtxos(ctx, spentVtxos, round.Txid); err != nil {
@@ -1544,7 +1542,7 @@ func (s *covenantlessService) propagateEvents(round *domain.Round) {
case domain.RoundFinalizationStarted: case domain.RoundFinalizationStarted:
ev := domain.RoundFinalizationStarted{ ev := domain.RoundFinalizationStarted{
Id: e.Id, Id: e.Id,
CongestionTree: e.CongestionTree, VtxoTree: e.VtxoTree,
Connectors: e.Connectors, Connectors: e.Connectors,
RoundTx: e.RoundTx, RoundTx: e.RoundTx,
MinRelayFeeRate: int64(s.wallet.MinRelayFeeRate(context.Background())), MinRelayFeeRate: int64(s.wallet.MinRelayFeeRate(context.Background())),
@@ -1565,19 +1563,19 @@ func (s *covenantlessService) scheduleSweepVtxosForRound(round *domain.Round) {
expirationTimestamp := s.sweeper.scheduler.AddNow(s.roundLifetime) expirationTimestamp := s.sweeper.scheduler.AddNow(s.roundLifetime)
if err := s.sweeper.schedule(expirationTimestamp, round.Txid, round.CongestionTree); err != nil { if err := s.sweeper.schedule(expirationTimestamp, round.Txid, round.VtxoTree); err != nil {
log.WithError(err).Warn("failed to schedule sweep tx") log.WithError(err).Warn("failed to schedule sweep tx")
} }
} }
func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo { func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo {
if len(round.CongestionTree) <= 0 { if len(round.VtxoTree) <= 0 {
return nil return nil
} }
createdAt := time.Now().Unix() createdAt := time.Now().Unix()
leaves := round.CongestionTree.Leaves() leaves := round.VtxoTree.Leaves()
vtxos := make([]domain.Vtxo, 0) vtxos := make([]domain.Vtxo, 0)
for _, node := range leaves { for _, node := range leaves {
tx, err := psbt.NewFromRawBytes(strings.NewReader(node.Tx), true) tx, err := psbt.NewFromRawBytes(strings.NewReader(node.Tx), true)
@@ -1593,10 +1591,9 @@ func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo {
} }
vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)) vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey))
vtxos = append(vtxos, domain.Vtxo{ vtxos = append(vtxos, domain.Vtxo{
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)}, VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
Pubkey: vtxoPubkey, PubKey: vtxoPubkey,
Amount: uint64(out.Value), Amount: uint64(out.Value),
RoundTxid: round.Txid, RoundTxid: round.Txid,
CreatedAt: createdAt, CreatedAt: createdAt,
@@ -1672,7 +1669,7 @@ func (s *covenantlessService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string
indexedScripts := make(map[string]struct{}) indexedScripts := make(map[string]struct{})
for _, vtxo := range vtxos { for _, vtxo := range vtxos {
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey) vtxoTapKeyBytes, err := hex.DecodeString(vtxo.PubKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1731,7 +1728,7 @@ func (s *covenantlessService) reactToFraud(ctx context.Context, vtxo domain.Vtxo
if err != nil { if err != nil {
vtxosRepo := s.repoManager.Vtxos() vtxosRepo := s.repoManager.Vtxos()
// if the round is not found, the utxo may be spent by an async payment redeem tx // If the round is not found, the utxo may be spent by an out of round tx
vtxos, err := vtxosRepo.GetVtxos(ctx, []domain.VtxoKey{ vtxos, err := vtxosRepo.GetVtxos(ctx, []domain.VtxoKey{
{Txid: vtxo.SpentBy, VOut: 0}, {Txid: vtxo.SpentBy, VOut: 0},
}) })
@@ -1739,14 +1736,14 @@ func (s *covenantlessService) reactToFraud(ctx context.Context, vtxo domain.Vtxo
return fmt.Errorf("failed to retrieve round: %s", err) return fmt.Errorf("failed to retrieve round: %s", err)
} }
asyncPayVtxo := vtxos[0] storedVtxo := vtxos[0]
if asyncPayVtxo.Redeemed { // redeem tx is already onchain if storedVtxo.Redeemed { // redeem tx is already onchain
return nil return nil
} }
log.Debugf("vtxo %s:%d has been spent by async payment", vtxo.Txid, vtxo.VOut) log.Debugf("vtxo %s:%d has been spent by out of round transaction", vtxo.Txid, vtxo.VOut)
redeemTxHex, err := s.builder.FinalizeAndExtract(asyncPayVtxo.RedeemTx) redeemTxHex, err := s.builder.FinalizeAndExtract(storedVtxo.RedeemTx)
if err != nil { if err != nil {
return fmt.Errorf("failed to finalize redeem tx: %s", err) return fmt.Errorf("failed to finalize redeem tx: %s", err)
} }

View File

@@ -1,6 +1,6 @@
/* /*
* This package contains intermediary events that are used only by the covenantless version * This package contains intermediary events that are used only by the covenantless version
* they let to sign the congestion tree using musig2 algorithm * they let to sign the vtxo tree using musig2 algorithm
* they are not included in domain because they don't mutate the Round state and should not be persisted * they are not included in domain because they don't mutate the Round state and should not be persisted
*/ */
package application package application
@@ -17,7 +17,7 @@ import (
// signer should react to this event by generating a musig2 nonce for each transaction in the tree // signer should react to this event by generating a musig2 nonce for each transaction in the tree
type RoundSigningStarted struct { type RoundSigningStarted struct {
Id string Id string
UnsignedVtxoTree tree.CongestionTree UnsignedVtxoTree tree.VtxoTree
Cosigners []*secp256k1.PublicKey Cosigners []*secp256k1.PublicKey
UnsignedRoundTx string UnsignedRoundTx string
} }

View File

@@ -2,10 +2,10 @@ package application
import "fmt" import "fmt"
type errPaymentNotFound struct { type errTxRequestNotFound struct {
id string id string
} }
func (e errPaymentNotFound) Error() string { func (e errTxRequestNotFound) Error() string {
return fmt.Sprintf("payment %s not found", e.id) return fmt.Sprintf("tx request %s not found", e.id)
} }

View File

@@ -33,7 +33,7 @@ func (p OwnershipProof) validate(vtxo domain.Vtxo) error {
rootHash := p.ControlBlock.RootHash(p.Script) rootHash := p.ControlBlock.RootHash(p.Script)
vtxoTapKey := txscript.ComputeTaprootOutputKey(bitcointree.UnspendableKey(), rootHash) vtxoTapKey := txscript.ComputeTaprootOutputKey(bitcointree.UnspendableKey(), rootHash)
if hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)) != vtxo.Pubkey { if hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)) != vtxo.PubKey {
return fmt.Errorf("invalid control block") return fmt.Errorf("invalid control block")
} }

View File

@@ -17,7 +17,7 @@ import (
// sweeper is an unexported service running while the main application service is started // sweeper is an unexported service running while the main application service is started
// it is responsible for sweeping onchain shared outputs that expired // it is responsible for sweeping onchain shared outputs that expired
// it also handles delaying the sweep events in case some parts of the tree are broadcasted // it also handles delaying the sweep events in case some parts of the tree are broadcasted
// when a round is finalized, the main application service schedules a sweep event on the newly created congestion tree // when a round is finalized, the main application service schedules a sweep event on the newly created vtxo tree
type sweeper struct { type sweeper struct {
wallet ports.WalletService wallet ports.WalletService
repoManager ports.RepoManager repoManager ports.RepoManager
@@ -58,7 +58,7 @@ func (s *sweeper) start() error {
} }
for _, round := range allRounds { for _, round := range allRounds {
task := s.createTask(round.Txid, round.CongestionTree) task := s.createTask(round.Txid, round.VtxoTree)
task() task()
} }
@@ -78,14 +78,14 @@ func (s *sweeper) removeTask(treeRootTxid string) {
// schedule set up a task to be executed once at the given timestamp // schedule set up a task to be executed once at the given timestamp
func (s *sweeper) schedule( func (s *sweeper) schedule(
expirationTimestamp int64, roundTxid string, congestionTree tree.CongestionTree, expirationTimestamp int64, roundTxid string, vtxoTree tree.VtxoTree,
) error { ) error {
if len(congestionTree) <= 0 { // skip if len(vtxoTree) <= 0 { // skip
log.Debugf("skipping sweep scheduling (round tx %s), empty congestion tree", roundTxid) log.Debugf("skipping sweep scheduling (round tx %s), empty vtxo tree", roundTxid)
return nil return nil
} }
root, err := congestionTree.Root() root, err := vtxoTree.Root()
if err != nil { if err != nil {
return err return err
} }
@@ -94,7 +94,7 @@ func (s *sweeper) schedule(
return nil return nil
} }
task := s.createTask(roundTxid, congestionTree) task := s.createTask(roundTxid, vtxoTree)
var fancyTime string var fancyTime string
if s.scheduler.Unit() == ports.UnixTime { if s.scheduler.Unit() == ports.UnixTime {
@@ -112,7 +112,7 @@ func (s *sweeper) schedule(
s.scheduledTasks[root.Txid] = struct{}{} s.scheduledTasks[root.Txid] = struct{}{}
s.locker.Unlock() s.locker.Unlock()
if err := s.updateVtxoExpirationTime(congestionTree, expirationTimestamp); err != nil { if err := s.updateVtxoExpirationTime(vtxoTree, expirationTimestamp); err != nil {
log.WithError(err).Error("error while updating vtxo expiration time") log.WithError(err).Error("error while updating vtxo expiration time")
} }
@@ -120,14 +120,14 @@ func (s *sweeper) schedule(
} }
// createTask returns a function passed as handler in the scheduler // createTask returns a function passed as handler in the scheduler
// it tries to craft a sweep tx containing the onchain outputs of the given congestion tree // it tries to craft a sweep tx containing the onchain outputs of the given vtxo tree
// if some parts of the tree have been broadcasted in the meantine, it will schedule the next taskes for the remaining parts of the tree // if some parts of the tree have been broadcasted in the meantine, it will schedule the next taskes for the remaining parts of the tree
func (s *sweeper) createTask( func (s *sweeper) createTask(
roundTxid string, congestionTree tree.CongestionTree, roundTxid string, vtxoTree tree.VtxoTree,
) func() { ) func() {
return func() { return func() {
ctx := context.Background() ctx := context.Background()
root, err := congestionTree.Root() root, err := vtxoTree.Root()
if err != nil { if err != nil {
log.WithError(err).Error("error while getting root node") log.WithError(err).Error("error while getting root node")
return return
@@ -139,17 +139,17 @@ func (s *sweeper) createTask(
sweepInputs := make([]ports.SweepInput, 0) sweepInputs := make([]ports.SweepInput, 0)
vtxoKeys := make([]domain.VtxoKey, 0) // vtxos associated to the sweep inputs vtxoKeys := make([]domain.VtxoKey, 0) // vtxos associated to the sweep inputs
// inspect the congestion tree to find onchain shared outputs // inspect the vtxo tree to find onchain shared outputs
sharedOutputs, err := findSweepableOutputs(ctx, s.wallet, s.builder, s.scheduler.Unit(), congestionTree) sharedOutputs, err := findSweepableOutputs(ctx, s.wallet, s.builder, s.scheduler.Unit(), vtxoTree)
if err != nil { if err != nil {
log.WithError(err).Error("error while inspecting congestion tree") log.WithError(err).Error("error while inspecting vtxo tree")
return return
} }
for expiredAt, inputs := range sharedOutputs { for expiredAt, inputs := range sharedOutputs {
// if the shared outputs are not expired, schedule a sweep task for it // if the shared outputs are not expired, schedule a sweep task for it
if s.scheduler.AfterNow(expiredAt) { if s.scheduler.AfterNow(expiredAt) {
subtrees, err := computeSubTrees(congestionTree, inputs) subtrees, err := computeSubTrees(vtxoTree, inputs)
if err != nil { if err != nil {
log.WithError(err).Error("error while computing subtrees") log.WithError(err).Error("error while computing subtrees")
continue continue
@@ -185,7 +185,7 @@ func (s *sweeper) createTask(
} }
} else { } else {
// if it's not a vtxo, find all the vtxos leaves reachable from that input // if it's not a vtxo, find all the vtxos leaves reachable from that input
vtxosLeaves, err := s.builder.FindLeaves(congestionTree, input.GetHash().String(), input.GetIndex()) vtxosLeaves, err := s.builder.FindLeaves(vtxoTree, input.GetHash().String(), input.GetIndex())
if err != nil { if err != nil {
log.WithError(err).Error("error while finding vtxos leaves") log.WithError(err).Error("error while finding vtxos leaves")
continue continue
@@ -301,7 +301,7 @@ func (s *sweeper) createTask(
} }
func (s *sweeper) updateVtxoExpirationTime( func (s *sweeper) updateVtxoExpirationTime(
tree tree.CongestionTree, tree tree.VtxoTree,
expirationTime int64, expirationTime int64,
) error { ) error {
leaves := tree.Leaves() leaves := tree.Leaves()
@@ -376,13 +376,13 @@ func (s *sweeper) createAndSendNotes(ctx context.Context, vtxosKeys []domain.Vtx
} }
} }
func computeSubTrees(congestionTree tree.CongestionTree, inputs []ports.SweepInput) ([]tree.CongestionTree, error) { func computeSubTrees(vtxoTree tree.VtxoTree, inputs []ports.SweepInput) ([]tree.VtxoTree, error) {
subTrees := make(map[string]tree.CongestionTree, 0) subTrees := make(map[string]tree.VtxoTree, 0)
// for each sweepable input, create a sub congestion tree // for each sweepable input, create a sub vtxo tree
// it allows to skip the part of the tree that has been broadcasted in the next task // it allows to skip the part of the tree that has been broadcasted in the next task
for _, input := range inputs { for _, input := range inputs {
subTree, err := computeSubTree(congestionTree, input.GetHash().String()) subTree, err := computeSubTree(vtxoTree, input.GetHash().String())
if err != nil { if err != nil {
log.WithError(err).Error("error while finding sub tree") log.WithError(err).Error("error while finding sub tree")
continue continue
@@ -398,7 +398,7 @@ func computeSubTrees(congestionTree tree.CongestionTree, inputs []ports.SweepInp
} }
// filter out the sub trees, remove the ones that are included in others // filter out the sub trees, remove the ones that are included in others
filteredSubTrees := make([]tree.CongestionTree, 0) filteredSubTrees := make([]tree.VtxoTree, 0)
for i, subTree := range subTrees { for i, subTree := range subTrees {
notIncludedInOtherTrees := true notIncludedInOtherTrees := true
@@ -426,19 +426,19 @@ func computeSubTrees(congestionTree tree.CongestionTree, inputs []ports.SweepInp
return filteredSubTrees, nil return filteredSubTrees, nil
} }
func computeSubTree(congestionTree tree.CongestionTree, newRoot string) (tree.CongestionTree, error) { func computeSubTree(vtxoTree tree.VtxoTree, newRoot string) (tree.VtxoTree, error) {
for _, level := range congestionTree { for _, level := range vtxoTree {
for _, node := range level { for _, node := range level {
if node.Txid == newRoot || node.ParentTxid == newRoot { if node.Txid == newRoot || node.ParentTxid == newRoot {
newTree := make(tree.CongestionTree, 0) newTree := make(tree.VtxoTree, 0)
newTree = append(newTree, []tree.Node{node}) newTree = append(newTree, []tree.Node{node})
children := congestionTree.Children(node.Txid) children := vtxoTree.Children(node.Txid)
for len(children) > 0 { for len(children) > 0 {
newTree = append(newTree, children) newTree = append(newTree, children)
newChildren := make([]tree.Node, 0) newChildren := make([]tree.Node, 0)
for _, child := range children { for _, child := range children {
newChildren = append(newChildren, congestionTree.Children(child.Txid)...) newChildren = append(newChildren, vtxoTree.Children(child.Txid)...)
} }
children = newChildren children = newChildren
} }
@@ -451,7 +451,7 @@ func computeSubTree(congestionTree tree.CongestionTree, newRoot string) (tree.Co
return nil, fmt.Errorf("failed to create subtree, new root not found") return nil, fmt.Errorf("failed to create subtree, new root not found")
} }
func containsTree(tr0 tree.CongestionTree, tr1 tree.CongestionTree) (bool, error) { func containsTree(tr0 tree.VtxoTree, tr1 tree.VtxoTree) (bool, error) {
tr1Root, err := tr1.Root() tr1Root, err := tr1.Root()
if err != nil { if err != nil {
return false, err return false, err
@@ -468,7 +468,7 @@ func containsTree(tr0 tree.CongestionTree, tr1 tree.CongestionTree) (bool, error
return false, nil return false, nil
} }
// assuming the pset is a leaf in the congestion tree, returns the vtxo outpoint // assuming the pset is a leaf in the vtxo tree, returns the vtxo outpoint
func extractVtxoOutpoint(leaf tree.Node) (*domain.VtxoKey, error) { func extractVtxoOutpoint(leaf tree.Node) (*domain.VtxoKey, error) {
if !leaf.Leaf { if !leaf.Leaf {
return nil, fmt.Errorf("node is not a leaf") return nil, fmt.Errorf("node is not a leaf")

View File

@@ -11,7 +11,7 @@ import (
) )
var ( var (
paymentsThreshold = int64(128) txRequestsThreshold = int64(128)
) )
type Service interface { type Service interface {
@@ -22,11 +22,11 @@ type Service interface {
ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error
SignVtxos(ctx context.Context, forfeitTxs []string) error SignVtxos(ctx context.Context, forfeitTxs []string) error
SignRoundTx(ctx context.Context, roundTx string) error SignRoundTx(ctx context.Context, roundTx string) error
GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.Round, error) GetRoundByTxid(ctx context.Context, roundTxid string) (*domain.Round, error)
GetRoundById(ctx context.Context, id string) (*domain.Round, error) GetRoundById(ctx context.Context, id string) (*domain.Round, error)
GetCurrentRound(ctx context.Context) (*domain.Round, error) GetCurrentRound(ctx context.Context) (*domain.Round, error)
GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent
UpdatePaymentStatus(ctx context.Context, paymentId string) error UpdateTxRequestStatus(ctx context.Context, requestID string) error
ListVtxos( ListVtxos(
ctx context.Context, address string, ctx context.Context, address string,
) (spendableVtxos, spentVtxos []domain.Vtxo, err error) ) (spendableVtxos, spentVtxos []domain.Vtxo, err error)
@@ -36,7 +36,7 @@ type Service interface {
ctx context.Context, userPubkey *secp256k1.PublicKey, ctx context.Context, userPubkey *secp256k1.PublicKey,
) (address string, scripts []string, err error) ) (address string, scripts []string, err error)
// Tree signing methods // Tree signing methods
RegisterCosignerPubkey(ctx context.Context, paymentId string, ephemeralPublicKey string) error RegisterCosignerPubkey(ctx context.Context, requestID string, ephemeralPubkey string) error
RegisterCosignerNonces( RegisterCosignerNonces(
ctx context.Context, roundID string, ctx context.Context, roundID string,
pubkey *secp256k1.PublicKey, nonces string, pubkey *secp256k1.PublicKey, nonces string,
@@ -106,7 +106,7 @@ type TransactionEvent interface {
} }
type RoundTransactionEvent struct { type RoundTransactionEvent struct {
RoundTxID string RoundTxid string
SpentVtxos []domain.VtxoKey SpentVtxos []domain.VtxoKey
SpendableVtxos []domain.Vtxo SpendableVtxos []domain.Vtxo
ClaimedBoardingInputs []domain.VtxoKey ClaimedBoardingInputs []domain.VtxoKey
@@ -117,7 +117,7 @@ func (r RoundTransactionEvent) Type() TransactionEventType {
} }
type RedeemTransactionEvent struct { type RedeemTransactionEvent struct {
AsyncTxID string RedeemTxid string
SpentVtxos []domain.VtxoKey SpentVtxos []domain.VtxoKey
SpendableVtxos []domain.Vtxo SpendableVtxos []domain.Vtxo
} }

View File

@@ -16,32 +16,32 @@ import (
"github.com/nbd-wtf/go-nostr/nip19" "github.com/nbd-wtf/go-nostr/nip19"
) )
type timedPayment struct { type timedTxRequest struct {
domain.Payment domain.TxRequest
boardingInputs []ports.BoardingInput boardingInputs []ports.BoardingInput
notes []note.Note notes []note.Note
timestamp time.Time timestamp time.Time
pingTimestamp time.Time pingTimestamp time.Time
} }
type paymentsMap struct { type txRequestsQueue struct {
lock *sync.RWMutex lock *sync.RWMutex
payments map[string]*timedPayment requests map[string]*timedTxRequest
ephemeralKeys map[string]*secp256k1.PublicKey ephemeralKeys map[string]*secp256k1.PublicKey
} }
func newPaymentsMap() *paymentsMap { func newTxRequestsQueue() *txRequestsQueue {
paymentsById := make(map[string]*timedPayment) requestsById := make(map[string]*timedTxRequest)
lock := &sync.RWMutex{} lock := &sync.RWMutex{}
return &paymentsMap{lock, paymentsById, make(map[string]*secp256k1.PublicKey)} return &txRequestsQueue{lock, requestsById, make(map[string]*secp256k1.PublicKey)}
} }
func (m *paymentsMap) len() int64 { func (m *txRequestsQueue) len() int64 {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
count := int64(0) count := int64(0)
for _, p := range m.payments { for _, p := range m.requests {
if len(p.Receivers) > 0 { if len(p.Receivers) > 0 {
count++ count++
} }
@@ -49,154 +49,154 @@ func (m *paymentsMap) len() int64 {
return count return count
} }
func (m *paymentsMap) delete(id string) error { func (m *txRequestsQueue) delete(id string) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
if _, ok := m.payments[id]; !ok { if _, ok := m.requests[id]; !ok {
return errPaymentNotFound{id} return errTxRequestNotFound{id}
} }
delete(m.payments, id) delete(m.requests, id)
return nil return nil
} }
func (m *paymentsMap) pushWithNotes(payment domain.Payment, notes []note.Note) error { func (m *txRequestsQueue) pushWithNotes(request domain.TxRequest, notes []note.Note) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
if _, ok := m.payments[payment.Id]; ok { if _, ok := m.requests[request.Id]; ok {
return fmt.Errorf("duplicated payment %s", payment.Id) return fmt.Errorf("duplicated tx request %s", request.Id)
} }
for _, note := range notes { for _, note := range notes {
for _, payment := range m.payments { for _, txRequest := range m.requests {
for _, pNote := range payment.notes { for _, rNote := range txRequest.notes {
if note.ID == pNote.ID { if note.ID == rNote.ID {
return fmt.Errorf("duplicated note %s", note) return fmt.Errorf("duplicated note %s", note)
} }
} }
} }
} }
m.payments[payment.Id] = &timedPayment{payment, make([]ports.BoardingInput, 0), notes, time.Now(), time.Time{}} m.requests[request.Id] = &timedTxRequest{request, make([]ports.BoardingInput, 0), notes, time.Now(), time.Time{}}
return nil return nil
} }
func (m *paymentsMap) push( func (m *txRequestsQueue) push(
payment domain.Payment, request domain.TxRequest,
boardingInputs []ports.BoardingInput, boardingInputs []ports.BoardingInput,
) error { ) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
if _, ok := m.payments[payment.Id]; ok { if _, ok := m.requests[request.Id]; ok {
return fmt.Errorf("duplicated payment %s", payment.Id) return fmt.Errorf("duplicated tx request %s", request.Id)
} }
for _, input := range payment.Inputs { for _, input := range request.Inputs {
for _, pay := range m.payments { for _, pay := range m.requests {
for _, pInput := range pay.Inputs { for _, pInput := range pay.Inputs {
if input.VtxoKey.Txid == pInput.VtxoKey.Txid && input.VtxoKey.VOut == pInput.VtxoKey.VOut { if input.VtxoKey.Txid == pInput.VtxoKey.Txid && input.VtxoKey.VOut == pInput.VtxoKey.VOut {
return fmt.Errorf("duplicated input, %s:%d already used by payment %s", input.VtxoKey.Txid, input.VtxoKey.VOut, pay.Id) return fmt.Errorf("duplicated input, %s:%d already used by tx request %s", input.VtxoKey.Txid, input.VtxoKey.VOut, pay.Id)
} }
} }
} }
} }
for _, input := range boardingInputs { for _, input := range boardingInputs {
for _, pay := range m.payments { for _, request := range m.requests {
for _, pBoardingInput := range pay.boardingInputs { for _, pBoardingInput := range request.boardingInputs {
if input.Txid == pBoardingInput.Txid && input.VOut == pBoardingInput.VOut { if input.Txid == pBoardingInput.Txid && input.VOut == pBoardingInput.VOut {
return fmt.Errorf("duplicated boarding input, %s:%d already used by payment %s", input.Txid, input.VOut, pay.Id) return fmt.Errorf("duplicated boarding input, %s:%d already used by tx request %s", input.Txid, input.VOut, request.Id)
} }
} }
} }
} }
m.payments[payment.Id] = &timedPayment{payment, boardingInputs, make([]note.Note, 0), time.Now(), time.Time{}} m.requests[request.Id] = &timedTxRequest{request, boardingInputs, make([]note.Note, 0), time.Now(), time.Time{}}
return nil return nil
} }
func (m *paymentsMap) pushEphemeralKey(paymentId string, pubkey *secp256k1.PublicKey) error { func (m *txRequestsQueue) pushEphemeralKey(requestID string, pubkey *secp256k1.PublicKey) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
if _, ok := m.payments[paymentId]; !ok { if _, ok := m.requests[requestID]; !ok {
return fmt.Errorf("payment %s not found, cannot register signing ephemeral public key", paymentId) return fmt.Errorf("tx request %s not found, cannot register signing ephemeral public key", requestID)
} }
m.ephemeralKeys[paymentId] = pubkey m.ephemeralKeys[requestID] = pubkey
return nil return nil
} }
func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, []*secp256k1.PublicKey, []note.Note) { func (m *txRequestsQueue) pop(num int64) ([]domain.TxRequest, []ports.BoardingInput, []*secp256k1.PublicKey, []note.Note) {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
paymentsByTime := make([]timedPayment, 0, len(m.payments)) requestsByTime := make([]timedTxRequest, 0, len(m.requests))
for _, p := range m.payments { for _, p := range m.requests {
// Skip payments without registered receivers. // Skip tx requests without registered receivers.
if len(p.Receivers) <= 0 { if len(p.Receivers) <= 0 {
continue continue
} }
// Skip payments for which users didn't notify to be online in the last minute. // Skip tx requests for which users didn't notify to be online in the last minute.
if p.pingTimestamp.IsZero() || time.Since(p.pingTimestamp).Minutes() > 1 { if p.pingTimestamp.IsZero() || time.Since(p.pingTimestamp).Minutes() > 1 {
continue continue
} }
paymentsByTime = append(paymentsByTime, *p) requestsByTime = append(requestsByTime, *p)
} }
sort.SliceStable(paymentsByTime, func(i, j int) bool { sort.SliceStable(requestsByTime, func(i, j int) bool {
return paymentsByTime[i].timestamp.Before(paymentsByTime[j].timestamp) return requestsByTime[i].timestamp.Before(requestsByTime[j].timestamp)
}) })
if num < 0 || num > int64(len(paymentsByTime)) { if num < 0 || num > int64(len(requestsByTime)) {
num = int64(len(paymentsByTime)) num = int64(len(requestsByTime))
} }
payments := make([]domain.Payment, 0, num) requests := make([]domain.TxRequest, 0, num)
boardingInputs := make([]ports.BoardingInput, 0) boardingInputs := make([]ports.BoardingInput, 0)
cosigners := make([]*secp256k1.PublicKey, 0, num) cosigners := make([]*secp256k1.PublicKey, 0, num)
notes := make([]note.Note, 0) notes := make([]note.Note, 0)
for _, p := range paymentsByTime[:num] { for _, p := range requestsByTime[:num] {
boardingInputs = append(boardingInputs, p.boardingInputs...) boardingInputs = append(boardingInputs, p.boardingInputs...)
payments = append(payments, p.Payment) requests = append(requests, p.TxRequest)
if pubkey, ok := m.ephemeralKeys[p.Payment.Id]; ok { if pubkey, ok := m.ephemeralKeys[p.TxRequest.Id]; ok {
cosigners = append(cosigners, pubkey) cosigners = append(cosigners, pubkey)
delete(m.ephemeralKeys, p.Payment.Id) delete(m.ephemeralKeys, p.TxRequest.Id)
} }
notes = append(notes, p.notes...) notes = append(notes, p.notes...)
delete(m.payments, p.Id) delete(m.requests, p.Id)
} }
return payments, boardingInputs, cosigners, notes return requests, boardingInputs, cosigners, notes
} }
func (m *paymentsMap) update(payment domain.Payment) error { func (m *txRequestsQueue) update(request domain.TxRequest) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
p, ok := m.payments[payment.Id] r, ok := m.requests[request.Id]
if !ok { if !ok {
return fmt.Errorf("payment %s not found", payment.Id) return fmt.Errorf("tx request %s not found", request.Id)
} }
// sum inputs = vtxos + boarding utxos + notes // sum inputs = vtxos + boarding utxos + notes
sumOfInputs := uint64(0) sumOfInputs := uint64(0)
for _, input := range payment.Inputs { for _, input := range request.Inputs {
sumOfInputs += input.Amount sumOfInputs += input.Amount
} }
for _, boardingInput := range p.boardingInputs { for _, boardingInput := range r.boardingInputs {
sumOfInputs += boardingInput.Amount sumOfInputs += boardingInput.Amount
} }
for _, note := range p.notes { for _, note := range r.notes {
sumOfInputs += uint64(note.Value) sumOfInputs += uint64(note.Value)
} }
// sum outputs = receivers VTXOs // sum outputs = receivers VTXOs
sumOfOutputs := uint64(0) sumOfOutputs := uint64(0)
for _, receiver := range payment.Receivers { for _, receiver := range request.Receivers {
sumOfOutputs += receiver.Amount sumOfOutputs += receiver.Amount
} }
@@ -204,37 +204,37 @@ func (m *paymentsMap) update(payment domain.Payment) error {
return fmt.Errorf("sum of inputs %d does not match sum of outputs %d", sumOfInputs, sumOfOutputs) return fmt.Errorf("sum of inputs %d does not match sum of outputs %d", sumOfInputs, sumOfOutputs)
} }
p.Payment = payment r.TxRequest = request
return nil return nil
} }
func (m *paymentsMap) updatePingTimestamp(id string) error { func (m *txRequestsQueue) updatePingTimestamp(id string) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
payment, ok := m.payments[id] request, ok := m.requests[id]
if !ok { if !ok {
return errPaymentNotFound{id} return errTxRequestNotFound{id}
} }
payment.pingTimestamp = time.Now() request.pingTimestamp = time.Now()
return nil return nil
} }
func (m *paymentsMap) view(id string) (*domain.Payment, bool) { func (m *txRequestsQueue) view(id string) (*domain.TxRequest, bool) {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
payment, ok := m.payments[id] request, ok := m.requests[id]
if !ok { if !ok {
return nil, false return nil, false
} }
return &domain.Payment{ return &domain.TxRequest{
Id: payment.Id, Id: request.Id,
Inputs: payment.Inputs, Inputs: request.Inputs,
Receivers: payment.Receivers, Receivers: request.Receivers,
}, true }, true
} }
@@ -251,10 +251,10 @@ func newForfeitTxsMap(txBuilder ports.TxBuilder) *forfeitTxsMap {
return &forfeitTxsMap{&sync.RWMutex{}, txBuilder, make(map[domain.VtxoKey][]string), nil, nil} return &forfeitTxsMap{&sync.RWMutex{}, txBuilder, make(map[domain.VtxoKey][]string), nil, nil}
} }
func (m *forfeitTxsMap) init(connectors []string, payments []domain.Payment) { func (m *forfeitTxsMap) init(connectors []string, requests []domain.TxRequest) {
vtxosToSign := make([]domain.Vtxo, 0) vtxosToSign := make([]domain.Vtxo, 0)
for _, payment := range payments { for _, request := range requests {
vtxosToSign = append(vtxosToSign, payment.Inputs...) vtxosToSign = append(vtxosToSign, request.Inputs...)
} }
m.lock.Lock() m.lock.Lock()
@@ -318,18 +318,18 @@ func (m *forfeitTxsMap) pop() ([]string, error) {
return txs, nil return txs, nil
} }
// onchainOutputs iterates over all the nodes' outputs in the congestion tree and checks their onchain state // onchainOutputs iterates over all the nodes' outputs in the vtxo tree and checks their onchain state
// returns the sweepable outputs as ports.SweepInput mapped by their expiration time // returns the sweepable outputs as ports.SweepInput mapped by their expiration time
func findSweepableOutputs( func findSweepableOutputs(
ctx context.Context, ctx context.Context,
walletSvc ports.WalletService, walletSvc ports.WalletService,
txbuilder ports.TxBuilder, txbuilder ports.TxBuilder,
schedulerUnit ports.TimeUnit, schedulerUnit ports.TimeUnit,
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
) (map[int64][]ports.SweepInput, error) { ) (map[int64][]ports.SweepInput, error) {
sweepableOutputs := make(map[int64][]ports.SweepInput) sweepableOutputs := make(map[int64][]ports.SweepInput)
blocktimeCache := make(map[string]int64) // txid -> blocktime / blockheight blocktimeCache := make(map[string]int64) // txid -> blocktime / blockheight
nodesToCheck := congestionTree[0] // init with the root nodesToCheck := vtxoTree[0] // init with the root
for len(nodesToCheck) > 0 { for len(nodesToCheck) > 0 {
newNodesToCheck := make([]tree.Node, 0) newNodesToCheck := make([]tree.Node, 0)
@@ -375,7 +375,7 @@ func findSweepableOutputs(
// add the children to the nodes in order to check them during the next iteration // add the children to the nodes in order to check them during the next iteration
// We will return the error below, but are we going to schedule the tasks for the "children roots"? // We will return the error below, but are we going to schedule the tasks for the "children roots"?
if !node.Leaf { if !node.Leaf {
children := congestionTree.Children(node.Txid) children := vtxoTree.Children(node.Txid)
newNodesToCheck = append(newNodesToCheck, children...) newNodesToCheck = append(newNodesToCheck, children...)
} }
continue continue
@@ -393,10 +393,10 @@ func findSweepableOutputs(
return sweepableOutputs, nil return sweepableOutputs, nil
} }
func getSpentVtxos(payments map[string]domain.Payment) []domain.VtxoKey { func getSpentVtxos(requests map[string]domain.TxRequest) []domain.VtxoKey {
vtxos := make([]domain.VtxoKey, 0) vtxos := make([]domain.VtxoKey, 0)
for _, p := range payments { for _, request := range requests {
for _, vtxo := range p.Inputs { for _, vtxo := range request.Inputs {
vtxos = append(vtxos, vtxo.VtxoKey) vtxos = append(vtxos, vtxo.VtxoKey)
} }
} }
@@ -460,12 +460,12 @@ func nip19toNostrProfile(nostrRecipient string, defaultRelays []string) (string,
nprofileRecipient = nostrRecipient nprofileRecipient = nostrRecipient
case "npub": case "npub":
recipientPublicKey, ok := result.(string) recipientPubkey, ok := result.(string)
if !ok { if !ok {
return "", fmt.Errorf("invalid NIP-19 result: %v", result) return "", fmt.Errorf("invalid NIP-19 result: %v", result)
} }
nprofileRecipient, err = nip19.EncodeProfile(recipientPublicKey, defaultRelays) nprofileRecipient, err = nip19.EncodeProfile(recipientPubkey, defaultRelays)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to encode nostr profile: %s", err) return "", fmt.Errorf("failed to encode nostr profile: %s", err)
} }

View File

@@ -10,7 +10,7 @@ func (r RoundStarted) IsEvent() {}
func (r RoundFinalizationStarted) IsEvent() {} func (r RoundFinalizationStarted) IsEvent() {}
func (r RoundFinalized) IsEvent() {} func (r RoundFinalized) IsEvent() {}
func (r RoundFailed) IsEvent() {} func (r RoundFailed) IsEvent() {}
func (r PaymentsRegistered) IsEvent() {} func (r TxRequestsRegistered) IsEvent() {}
type RoundStarted struct { type RoundStarted struct {
Id string Id string
@@ -19,7 +19,7 @@ type RoundStarted struct {
type RoundFinalizationStarted struct { type RoundFinalizationStarted struct {
Id string Id string
CongestionTree tree.CongestionTree // BTC: signed VtxoTree tree.VtxoTree
Connectors []string Connectors []string
ConnectorAddress string ConnectorAddress string
RoundTx string RoundTx string
@@ -39,7 +39,7 @@ type RoundFailed struct {
Timestamp int64 Timestamp int64
} }
type PaymentsRegistered struct { type TxRequestsRegistered struct {
Id string Id string
Payments []Payment TxRequests []TxRequest
} }

View File

@@ -11,66 +11,66 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
type Payment struct { type TxRequest struct {
Id string Id string
Inputs []Vtxo Inputs []Vtxo
Receivers []Receiver Receivers []Receiver
} }
func NewPayment(inputs []Vtxo) (*Payment, error) { func NewTxRequest(inputs []Vtxo) (*TxRequest, error) {
p := &Payment{ request := &TxRequest{
Id: uuid.New().String(), Id: uuid.New().String(),
Inputs: inputs, Inputs: inputs,
} }
if err := p.validate(true); err != nil { if err := request.validate(true); err != nil {
return nil, err return nil, err
} }
return p, nil return request, nil
} }
func (p *Payment) AddReceivers(receivers []Receiver) (err error) { func (r *TxRequest) AddReceivers(receivers []Receiver) (err error) {
if p.Receivers == nil { if r.Receivers == nil {
p.Receivers = make([]Receiver, 0) r.Receivers = make([]Receiver, 0)
} }
p.Receivers = append(p.Receivers, receivers...) r.Receivers = append(r.Receivers, receivers...)
defer func() { defer func() {
if err != nil { if err != nil {
p.Receivers = p.Receivers[:len(p.Receivers)-len(receivers)] r.Receivers = r.Receivers[:len(r.Receivers)-len(receivers)]
} }
}() }()
err = p.validate(false) err = r.validate(false)
return return
} }
func (p Payment) TotalInputAmount() uint64 { func (r TxRequest) TotalInputAmount() uint64 {
tot := uint64(0) tot := uint64(0)
for _, in := range p.Inputs { for _, in := range r.Inputs {
tot += in.Amount tot += in.Amount
} }
return tot return tot
} }
func (p Payment) TotalOutputAmount() uint64 { func (r TxRequest) TotalOutputAmount() uint64 {
tot := uint64(0) tot := uint64(0)
for _, r := range p.Receivers { for _, r := range r.Receivers {
tot += r.Amount tot += r.Amount
} }
return tot return tot
} }
func (p Payment) validate(ignoreOuts bool) error { func (r TxRequest) validate(ignoreOuts bool) error {
if len(p.Id) <= 0 { if len(r.Id) <= 0 {
return fmt.Errorf("missing id") return fmt.Errorf("missing id")
} }
if ignoreOuts { if ignoreOuts {
return nil return nil
} }
if len(p.Receivers) <= 0 { if len(r.Receivers) <= 0 {
return fmt.Errorf("missing outputs") return fmt.Errorf("missing outputs")
} }
for _, r := range p.Receivers { for _, r := range r.Receivers {
if len(r.OnchainAddress) <= 0 && len(r.Pubkey) <= 0 { if len(r.OnchainAddress) <= 0 && len(r.PubKey) <= 0 {
return fmt.Errorf("missing receiver destination") return fmt.Errorf("missing receiver destination")
} }
if r.Amount == 0 { if r.Amount == 0 {
@@ -107,7 +107,7 @@ func (k VtxoKey) Hash() string {
type Receiver struct { type Receiver struct {
Amount uint64 Amount uint64
OnchainAddress string // onchain OnchainAddress string // onchain
Pubkey string // offchain PubKey string // offchain
} }
func (r Receiver) IsOnchain() bool { func (r Receiver) IsOnchain() bool {
@@ -117,9 +117,9 @@ func (r Receiver) IsOnchain() bool {
type Vtxo struct { type Vtxo struct {
VtxoKey VtxoKey
Amount uint64 Amount uint64
Pubkey string PubKey string
RoundTxid string RoundTxid string
SpentBy string // round txid or async redeem txid SpentBy string // round txid or redeem txid
Spent bool Spent bool
Redeemed bool Redeemed bool
Swept bool Swept bool
@@ -129,7 +129,7 @@ type Vtxo struct {
} }
func (v Vtxo) TapKey() (*secp256k1.PublicKey, error) { func (v Vtxo) TapKey() (*secp256k1.PublicKey, error) {
pubkeyBytes, err := hex.DecodeString(v.Pubkey) pubkeyBytes, err := hex.DecodeString(v.PubKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -16,36 +16,36 @@ var inputs = []domain.Vtxo{
Txid: "0000000000000000000000000000000000000000000000000000000000000000", Txid: "0000000000000000000000000000000000000000000000000000000000000000",
VOut: 0, VOut: 0,
}, },
Pubkey: pubkey, PubKey: pubkey,
Amount: 1000, Amount: 1000,
}, },
} }
func TestPayment(t *testing.T) { func TestTxRequest(t *testing.T) {
t.Run("new_payment", func(t *testing.T) { t.Run("new_tx_request", func(t *testing.T) {
t.Run("valid", func(t *testing.T) { t.Run("valid", func(t *testing.T) {
payment, err := domain.NewPayment(inputs) request, err := domain.NewTxRequest(inputs)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, payment) require.NotNil(t, request)
require.NotEmpty(t, payment.Id) require.NotEmpty(t, request.Id)
require.Exactly(t, inputs, payment.Inputs) require.Exactly(t, inputs, request.Inputs)
require.Empty(t, payment.Receivers) require.Empty(t, request.Receivers)
}) })
}) })
t.Run("add_receivers", func(t *testing.T) { t.Run("add_receivers", func(t *testing.T) {
t.Run("valid", func(t *testing.T) { t.Run("valid", func(t *testing.T) {
payment, err := domain.NewPayment(inputs) request, err := domain.NewTxRequest(inputs)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, payment) require.NotNil(t, request)
err = payment.AddReceivers([]domain.Receiver{ err = request.AddReceivers([]domain.Receiver{
{ {
Pubkey: pubkey, PubKey: pubkey,
Amount: 450, Amount: 450,
}, },
{ {
Pubkey: pubkey, PubKey: pubkey,
Amount: 550, Amount: 550,
}, },
}) })
@@ -63,12 +63,12 @@ func TestPayment(t *testing.T) {
}, },
} }
payment, err := domain.NewPayment(inputs) request, err := domain.NewTxRequest(inputs)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, payment) require.NotNil(t, request)
for _, f := range fixtures { for _, f := range fixtures {
err := payment.AddReceivers(f.receivers) err := request.AddReceivers(f.receivers)
require.EqualError(t, err, f.expectedErr) require.EqualError(t, err, f.expectedErr)
} }
}) })

View File

@@ -38,11 +38,11 @@ type Round struct {
StartingTimestamp int64 StartingTimestamp int64
EndingTimestamp int64 EndingTimestamp int64
Stage Stage Stage Stage
Payments map[string]Payment TxRequests map[string]TxRequest
Txid string Txid string
UnsignedTx string UnsignedTx string
ForfeitTxs []string ForfeitTxs []string
CongestionTree tree.CongestionTree VtxoTree tree.VtxoTree
Connectors []string Connectors []string
ConnectorAddress string ConnectorAddress string
DustAmount uint64 DustAmount uint64
@@ -55,7 +55,7 @@ func NewRound(dustAmount uint64) *Round {
return &Round{ return &Round{
Id: uuid.New().String(), Id: uuid.New().String(),
DustAmount: dustAmount, DustAmount: dustAmount,
Payments: make(map[string]Payment), TxRequests: make(map[string]TxRequest),
changes: make([]RoundEvent, 0), changes: make([]RoundEvent, 0),
} }
} }
@@ -84,7 +84,7 @@ func (r *Round) On(event RoundEvent, replayed bool) {
r.StartingTimestamp = e.Timestamp r.StartingTimestamp = e.Timestamp
case RoundFinalizationStarted: case RoundFinalizationStarted:
r.Stage.Code = FinalizationStage r.Stage.Code = FinalizationStage
r.CongestionTree = e.CongestionTree r.VtxoTree = e.VtxoTree
r.Connectors = append([]string{}, e.Connectors...) r.Connectors = append([]string{}, e.Connectors...)
r.ConnectorAddress = e.ConnectorAddress r.ConnectorAddress = e.ConnectorAddress
r.UnsignedTx = e.RoundTx r.UnsignedTx = e.RoundTx
@@ -96,12 +96,12 @@ func (r *Round) On(event RoundEvent, replayed bool) {
case RoundFailed: case RoundFailed:
r.Stage.Failed = true r.Stage.Failed = true
r.EndingTimestamp = e.Timestamp r.EndingTimestamp = e.Timestamp
case PaymentsRegistered: case TxRequestsRegistered:
if r.Payments == nil { if r.TxRequests == nil {
r.Payments = make(map[string]Payment) r.TxRequests = make(map[string]TxRequest)
} }
for _, p := range e.Payments { for _, p := range e.TxRequests {
r.Payments[p.Id] = p r.TxRequests[p.Id] = p
} }
} }
@@ -113,7 +113,7 @@ func (r *Round) On(event RoundEvent, replayed bool) {
func (r *Round) StartRegistration() ([]RoundEvent, error) { func (r *Round) StartRegistration() ([]RoundEvent, error) {
empty := Stage{} empty := Stage{}
if r.Stage != empty { if r.Stage != empty {
return nil, fmt.Errorf("not in a valid stage to start payment registration") return nil, fmt.Errorf("not in a valid stage to start tx requests registration")
} }
event := RoundStarted{ event := RoundStarted{
@@ -125,45 +125,45 @@ func (r *Round) StartRegistration() ([]RoundEvent, error) {
return []RoundEvent{event}, nil return []RoundEvent{event}, nil
} }
func (r *Round) RegisterPayments(payments []Payment) ([]RoundEvent, error) { func (r *Round) RegisterTxRequests(txRequests []TxRequest) ([]RoundEvent, error) {
if r.Stage.Code != RegistrationStage || r.IsFailed() { if r.Stage.Code != RegistrationStage || r.IsFailed() {
return nil, fmt.Errorf("not in a valid stage to register payments") return nil, fmt.Errorf("not in a valid stage to register tx requests")
} }
if len(payments) <= 0 { if len(txRequests) <= 0 {
return nil, fmt.Errorf("missing payments to register") return nil, fmt.Errorf("missing tx requests to register")
} }
for _, p := range payments { for _, request := range txRequests {
if err := p.validate(false); err != nil { if err := request.validate(false); err != nil {
return nil, err return nil, err
} }
} }
event := PaymentsRegistered{ event := TxRequestsRegistered{
Id: r.Id, Id: r.Id,
Payments: payments, TxRequests: txRequests,
} }
r.raise(event) r.raise(event)
return []RoundEvent{event}, nil return []RoundEvent{event}, nil
} }
func (r *Round) StartFinalization(connectorAddress string, connectors []string, congestionTree tree.CongestionTree, poolTx string) ([]RoundEvent, error) { func (r *Round) StartFinalization(connectorAddress string, connectors []string, vtxoTree tree.VtxoTree, roundTx string) ([]RoundEvent, error) {
if len(poolTx) <= 0 { if len(roundTx) <= 0 {
return nil, fmt.Errorf("missing unsigned pool tx") return nil, fmt.Errorf("missing unsigned round tx")
} }
if r.Stage.Code != RegistrationStage || r.IsFailed() { if r.Stage.Code != RegistrationStage || r.IsFailed() {
return nil, fmt.Errorf("not in a valid stage to start payment finalization") return nil, fmt.Errorf("not in a valid stage to start finalization")
} }
if len(r.Payments) <= 0 { if len(r.TxRequests) <= 0 {
return nil, fmt.Errorf("no payments registered") return nil, fmt.Errorf("no tx requests registered")
} }
event := RoundFinalizationStarted{ event := RoundFinalizationStarted{
Id: r.Id, Id: r.Id,
CongestionTree: congestionTree, VtxoTree: vtxoTree,
Connectors: connectors, Connectors: connectors,
ConnectorAddress: connectorAddress, ConnectorAddress: connectorAddress,
RoundTx: poolTx, RoundTx: roundTx,
} }
r.raise(event) r.raise(event)
@@ -172,17 +172,17 @@ func (r *Round) StartFinalization(connectorAddress string, connectors []string,
func (r *Round) EndFinalization(forfeitTxs []string, txid string) ([]RoundEvent, error) { func (r *Round) EndFinalization(forfeitTxs []string, txid string) ([]RoundEvent, error) {
if len(forfeitTxs) <= 0 { if len(forfeitTxs) <= 0 {
for _, p := range r.Payments { for _, request := range r.TxRequests {
if len(p.Inputs) > 0 { if len(request.Inputs) > 0 {
return nil, fmt.Errorf("missing list of signed forfeit txs") return nil, fmt.Errorf("missing list of signed forfeit txs")
} }
} }
} }
if len(txid) <= 0 { if len(txid) <= 0 {
return nil, fmt.Errorf("missing pool txid") return nil, fmt.Errorf("missing round txid")
} }
if r.Stage.Code != FinalizationStage || r.IsFailed() { if r.Stage.Code != FinalizationStage || r.IsFailed() {
return nil, fmt.Errorf("not in a valid stage to end payment finalization") return nil, fmt.Errorf("not in a valid stage to end finalization")
} }
if r.Stage.Ended { if r.Stage.Ended {
return nil, fmt.Errorf("round already finalized") return nil, fmt.Errorf("round already finalized")
@@ -231,16 +231,16 @@ func (r *Round) IsFailed() bool {
func (r *Round) TotalInputAmount() uint64 { func (r *Round) TotalInputAmount() uint64 {
totInputs := 0 totInputs := 0
for _, p := range r.Payments { for _, request := range r.TxRequests {
totInputs += len(p.Inputs) totInputs += len(request.Inputs)
} }
return uint64(totInputs * int(r.DustAmount)) return uint64(totInputs * int(r.DustAmount))
} }
func (r *Round) TotalOutputAmount() uint64 { func (r *Round) TotalOutputAmount() uint64 {
tot := uint64(0) tot := uint64(0)
for _, p := range r.Payments { for _, request := range r.TxRequests {
tot += p.TotalOutputAmount() tot += request.TotalOutputAmount()
} }
return tot return tot
} }

View File

@@ -11,7 +11,7 @@ import (
var ( var (
dustAmount = uint64(450) dustAmount = uint64(450)
payments = []domain.Payment{ requests = []domain.TxRequest{
{ {
Id: "0", Id: "0",
Inputs: []domain.Vtxo{ Inputs: []domain.Vtxo{
@@ -20,21 +20,21 @@ var (
Txid: txid, Txid: txid,
VOut: 0, VOut: 0,
}, },
Pubkey: pubkey, PubKey: pubkey,
Amount: 2000, Amount: 2000,
}, },
}, },
Receivers: []domain.Receiver{ Receivers: []domain.Receiver{
{ {
Pubkey: pubkey, PubKey: pubkey,
Amount: 700, Amount: 700,
}, },
{ {
Pubkey: pubkey, PubKey: pubkey,
Amount: 700, Amount: 700,
}, },
{ {
Pubkey: pubkey, PubKey: pubkey,
Amount: 600, Amount: 600,
}, },
}, },
@@ -47,7 +47,7 @@ var (
Txid: txid, Txid: txid,
VOut: 0, VOut: 0,
}, },
Pubkey: pubkey, PubKey: pubkey,
Amount: 1000, Amount: 1000,
}, },
{ {
@@ -55,12 +55,12 @@ var (
Txid: txid, Txid: txid,
VOut: 0, VOut: 0,
}, },
Pubkey: pubkey, PubKey: pubkey,
Amount: 1000, Amount: 1000,
}, },
}, },
Receivers: []domain.Receiver{{ Receivers: []domain.Receiver{{
Pubkey: pubkey, PubKey: pubkey,
Amount: 2000, Amount: 2000,
}}, }},
}, },
@@ -68,7 +68,7 @@ var (
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA=" emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
emptyTx = "0200000000000000000000" emptyTx = "0200000000000000000000"
txid = "0000000000000000000000000000000000000000000000000000000000000000" txid = "0000000000000000000000000000000000000000000000000000000000000000"
congestionTree = tree.CongestionTree{ vtxoTree = tree.VtxoTree{
{ {
{ {
Txid: txid, Txid: txid,
@@ -113,13 +113,13 @@ var (
} }
connectors = []string{emptyPtx, emptyPtx, emptyPtx} connectors = []string{emptyPtx, emptyPtx, emptyPtx}
forfeitTxs = []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx} forfeitTxs = []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx}
poolTx = emptyTx roundTx = emptyTx
) )
func TestRound(t *testing.T) { func TestRound(t *testing.T) {
testStartRegistration(t) testStartRegistration(t)
testRegisterPayments(t) testRegisterTxRequests(t)
testStartFinalization(t) testStartFinalization(t)
@@ -165,7 +165,7 @@ func testStartRegistration(t *testing.T) {
Failed: true, Failed: true,
}, },
}, },
expectedErr: "not in a valid stage to start payment registration", expectedErr: "not in a valid stage to start tx requests registration",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -174,7 +174,7 @@ func testStartRegistration(t *testing.T) {
Code: domain.RegistrationStage, Code: domain.RegistrationStage,
}, },
}, },
expectedErr: "not in a valid stage to start payment registration", expectedErr: "not in a valid stage to start tx requests registration",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -183,7 +183,7 @@ func testStartRegistration(t *testing.T) {
Code: domain.FinalizationStage, Code: domain.FinalizationStage,
}, },
}, },
expectedErr: "not in a valid stage to start payment registration", expectedErr: "not in a valid stage to start tx requests registration",
}, },
} }
@@ -196,20 +196,20 @@ func testStartRegistration(t *testing.T) {
}) })
} }
func testRegisterPayments(t *testing.T) { func testRegisterTxRequests(t *testing.T) {
t.Run("register_payments", func(t *testing.T) { t.Run("register_tx_requests", func(t *testing.T) {
t.Run("valid", func(t *testing.T) { t.Run("valid", func(t *testing.T) {
round := domain.NewRound(dustAmount) round := domain.NewRound(dustAmount)
events, err := round.StartRegistration() events, err := round.StartRegistration()
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
events, err = round.RegisterPayments(payments) events, err = round.RegisterTxRequests(requests)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, events, 1) require.Len(t, events, 1)
require.Condition(t, func() bool { require.Condition(t, func() bool {
for _, payment := range payments { for _, request := range requests {
_, ok := round.Payments[payment.Id] _, ok := round.TxRequests[request.Id]
if !ok { if !ok {
return false return false
} }
@@ -217,16 +217,16 @@ func testRegisterPayments(t *testing.T) {
return true return true
}) })
event, ok := events[0].(domain.PaymentsRegistered) event, ok := events[0].(domain.TxRequestsRegistered)
require.True(t, ok) require.True(t, ok)
require.Equal(t, round.Id, event.Id) require.Equal(t, round.Id, event.Id)
require.Equal(t, payments, event.Payments) require.Equal(t, requests, event.TxRequests)
}) })
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
fixtures := []struct { fixtures := []struct {
round *domain.Round round *domain.Round
payments []domain.Payment requests []domain.TxRequest
expectedErr string expectedErr string
}{ }{
{ {
@@ -234,8 +234,8 @@ func testRegisterPayments(t *testing.T) {
Id: "id", Id: "id",
Stage: domain.Stage{}, Stage: domain.Stage{},
}, },
payments: payments, requests: requests,
expectedErr: "not in a valid stage to register payments", expectedErr: "not in a valid stage to register tx requests",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -245,8 +245,8 @@ func testRegisterPayments(t *testing.T) {
Failed: true, Failed: true,
}, },
}, },
payments: payments, requests: requests,
expectedErr: "not in a valid stage to register payments", expectedErr: "not in a valid stage to register tx requests",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -255,8 +255,8 @@ func testRegisterPayments(t *testing.T) {
Code: domain.FinalizationStage, Code: domain.FinalizationStage,
}, },
}, },
payments: payments, requests: requests,
expectedErr: "not in a valid stage to register payments", expectedErr: "not in a valid stage to register tx requests",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -265,13 +265,13 @@ func testRegisterPayments(t *testing.T) {
Code: domain.RegistrationStage, Code: domain.RegistrationStage,
}, },
}, },
payments: nil, requests: nil,
expectedErr: "missing payments to register", expectedErr: "missing tx requests to register",
}, },
} }
for _, f := range fixtures { for _, f := range fixtures {
events, err := f.round.RegisterPayments(f.payments) events, err := f.round.RegisterTxRequests(f.requests)
require.EqualError(t, err, f.expectedErr) require.EqualError(t, err, f.expectedErr)
require.Empty(t, events) require.Empty(t, events)
} }
@@ -287,11 +287,11 @@ func testStartFinalization(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
events, err = round.RegisterPayments(payments) events, err = round.RegisterTxRequests(requests)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
events, err = round.StartFinalization("", connectors, congestionTree, poolTx) events, err = round.StartFinalization("", connectors, vtxoTree, roundTx)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, events, 1) require.Len(t, events, 1)
require.True(t, round.IsStarted()) require.True(t, round.IsStarted())
@@ -302,20 +302,20 @@ func testStartFinalization(t *testing.T) {
require.True(t, ok) require.True(t, ok)
require.Equal(t, round.Id, event.Id) require.Equal(t, round.Id, event.Id)
require.Exactly(t, connectors, event.Connectors) require.Exactly(t, connectors, event.Connectors)
require.Exactly(t, congestionTree, event.CongestionTree) require.Exactly(t, vtxoTree, event.VtxoTree)
require.Exactly(t, poolTx, event.RoundTx) require.Exactly(t, roundTx, event.RoundTx)
}) })
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
paymentsById := map[string]domain.Payment{} requestsById := map[string]domain.TxRequest{}
for _, p := range payments { for _, p := range requests {
paymentsById[p.Id] = p requestsById[p.Id] = p
} }
fixtures := []struct { fixtures := []struct {
round *domain.Round round *domain.Round
connectors []string connectors []string
tree tree.CongestionTree tree tree.VtxoTree
poolTx string roundTx string
expectedErr string expectedErr string
}{ }{
{ {
@@ -324,12 +324,12 @@ func testStartFinalization(t *testing.T) {
Stage: domain.Stage{ Stage: domain.Stage{
Code: domain.RegistrationStage, Code: domain.RegistrationStage,
}, },
Payments: paymentsById, TxRequests: requestsById,
}, },
connectors: connectors, connectors: connectors,
tree: congestionTree, tree: vtxoTree,
poolTx: "", roundTx: "",
expectedErr: "missing unsigned pool tx", expectedErr: "missing unsigned round tx",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -337,12 +337,12 @@ func testStartFinalization(t *testing.T) {
Stage: domain.Stage{ Stage: domain.Stage{
Code: domain.RegistrationStage, Code: domain.RegistrationStage,
}, },
Payments: nil, TxRequests: nil,
}, },
connectors: connectors, connectors: connectors,
tree: congestionTree, tree: vtxoTree,
poolTx: poolTx, roundTx: roundTx,
expectedErr: "no payments registered", expectedErr: "no tx requests registered",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -350,12 +350,12 @@ func testStartFinalization(t *testing.T) {
Stage: domain.Stage{ Stage: domain.Stage{
Code: domain.UndefinedStage, Code: domain.UndefinedStage,
}, },
Payments: paymentsById, TxRequests: requestsById,
}, },
connectors: connectors, connectors: connectors,
tree: congestionTree, tree: vtxoTree,
poolTx: poolTx, roundTx: roundTx,
expectedErr: "not in a valid stage to start payment finalization", expectedErr: "not in a valid stage to start finalization",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -364,12 +364,12 @@ func testStartFinalization(t *testing.T) {
Code: domain.RegistrationStage, Code: domain.RegistrationStage,
Failed: true, Failed: true,
}, },
Payments: paymentsById, TxRequests: requestsById,
}, },
connectors: connectors, connectors: connectors,
tree: congestionTree, tree: vtxoTree,
poolTx: poolTx, roundTx: roundTx,
expectedErr: "not in a valid stage to start payment finalization", expectedErr: "not in a valid stage to start finalization",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -377,18 +377,18 @@ func testStartFinalization(t *testing.T) {
Stage: domain.Stage{ Stage: domain.Stage{
Code: domain.FinalizationStage, Code: domain.FinalizationStage,
}, },
Payments: paymentsById, TxRequests: requestsById,
}, },
connectors: connectors, connectors: connectors,
tree: congestionTree, tree: vtxoTree,
poolTx: poolTx, roundTx: roundTx,
expectedErr: "not in a valid stage to start payment finalization", expectedErr: "not in a valid stage to start finalization",
}, },
} }
for _, f := range fixtures { for _, f := range fixtures {
// TODO fix this // TODO fix this
events, err := f.round.StartFinalization("", f.connectors, f.tree, f.poolTx) events, err := f.round.StartFinalization("", f.connectors, f.tree, f.roundTx)
require.EqualError(t, err, f.expectedErr) require.EqualError(t, err, f.expectedErr)
require.Empty(t, events) require.Empty(t, events)
} }
@@ -404,11 +404,11 @@ func testEndFinalization(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
events, err = round.RegisterPayments(payments) events, err = round.RegisterTxRequests(requests)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
events, err = round.StartFinalization("", connectors, congestionTree, poolTx) events, err = round.StartFinalization("", connectors, vtxoTree, roundTx)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
@@ -428,9 +428,9 @@ func testEndFinalization(t *testing.T) {
}) })
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
paymentsById := map[string]domain.Payment{} requestsById := map[string]domain.TxRequest{}
for _, p := range payments { for _, p := range requests {
paymentsById[p.Id] = p requestsById[p.Id] = p
} }
fixtures := []struct { fixtures := []struct {
round *domain.Round round *domain.Round
@@ -444,7 +444,7 @@ func testEndFinalization(t *testing.T) {
Stage: domain.Stage{ Stage: domain.Stage{
Code: domain.FinalizationStage, Code: domain.FinalizationStage,
}, },
Payments: paymentsById, TxRequests: requestsById,
}, },
forfeitTxs: nil, forfeitTxs: nil,
txid: txid, txid: txid,
@@ -459,7 +459,7 @@ func testEndFinalization(t *testing.T) {
}, },
forfeitTxs: forfeitTxs, forfeitTxs: forfeitTxs,
txid: "", txid: "",
expectedErr: "missing pool txid", expectedErr: "missing round txid",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -467,7 +467,7 @@ func testEndFinalization(t *testing.T) {
}, },
forfeitTxs: forfeitTxs, forfeitTxs: forfeitTxs,
txid: txid, txid: txid,
expectedErr: "not in a valid stage to end payment finalization", expectedErr: "not in a valid stage to end finalization",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -478,7 +478,7 @@ func testEndFinalization(t *testing.T) {
}, },
forfeitTxs: forfeitTxs, forfeitTxs: forfeitTxs,
txid: txid, txid: txid,
expectedErr: "not in a valid stage to end payment finalization", expectedErr: "not in a valid stage to end finalization",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -490,7 +490,7 @@ func testEndFinalization(t *testing.T) {
}, },
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx}, forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
txid: txid, txid: txid,
expectedErr: "not in a valid stage to end payment finalization", expectedErr: "not in a valid stage to end finalization",
}, },
{ {
round: &domain.Round{ round: &domain.Round{
@@ -523,7 +523,7 @@ func testFail(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)
events, err = round.RegisterPayments(payments) events, err = round.RegisterTxRequests(requests)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, events) require.NotEmpty(t, events)

View File

@@ -27,15 +27,15 @@ type BoardingInput struct {
} }
type TxBuilder interface { type TxBuilder interface {
// BuildRoundTx builds a round tx for the given payments, boarding inputs // BuildRoundTx builds a round tx for the given tx requests, boarding inputs
// it selects coin from swept rounds and ASP wallet // it selects coin from swept rounds and server wallet
// returns the round partial tx, the vtxo tree and the set of connectors // returns the round partial tx, the vtxo tree and the set of connectors
BuildRoundTx( BuildRoundTx(
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, boardingInputs []BoardingInput, sweptRounds []domain.Round, serverPubkey *secp256k1.PublicKey, txRequests []domain.TxRequest, boardingInputs []BoardingInput, sweptRounds []domain.Round,
cosigners ...*secp256k1.PublicKey, cosigners ...*secp256k1.PublicKey,
) ( ) (
roundTx string, roundTx string,
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
connectorAddress string, connectorAddress string,
connectors []string, connectors []string,
err error, err error,
@@ -51,7 +51,7 @@ type TxBuilder interface {
FinalizeAndExtract(tx string) (txhex string, err error) FinalizeAndExtract(tx string) (txhex string, err error)
VerifyTapscriptPartialSigs(tx string) (valid bool, err error) VerifyTapscriptPartialSigs(tx string) (valid bool, err error)
// FindLeaves returns all the leaves txs that are reachable from the given outpoint // FindLeaves returns all the leaves txs that are reachable from the given outpoint
FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) (leaves []tree.Node, err error) FindLeaves(vtxoTree tree.VtxoTree, fromtxid string, vout uint32) (leaves []tree.Node, err error)
VerifyAndCombinePartialTx(dest string, src string) (string, error) VerifyAndCombinePartialTx(dest string, src string) (string, error)
GetTxID(tx string) (string, error) GetTxID(tx string) (string, error)
} }

View File

@@ -100,8 +100,8 @@ func deserializeEvent(buf []byte) (domain.RoundEvent, error) {
} }
} }
{ {
var event = domain.PaymentsRegistered{} var event = domain.TxRequestsRegistered{}
if err := json.Unmarshal(buf, &event); err == nil && len(event.Payments) > 0 { if err := json.Unmarshal(buf, &event); err == nil && len(event.TxRequests) > 0 {
return event, nil return event, nil
} }
} }

View File

@@ -91,7 +91,7 @@ func (r *vtxoRepository) GetVtxos(
func (r *vtxoRepository) GetVtxosForRound( func (r *vtxoRepository) GetVtxosForRound(
ctx context.Context, txid string, ctx context.Context, txid string,
) ([]domain.Vtxo, error) { ) ([]domain.Vtxo, error) {
query := badgerhold.Where("PoolTx").Eq(txid) query := badgerhold.Where("RoundTx").Eq(txid)
return r.findVtxos(ctx, query) return r.findVtxos(ctx, query)
} }
@@ -100,7 +100,7 @@ func (r *vtxoRepository) GetAllVtxos(
) ([]domain.Vtxo, []domain.Vtxo, error) { ) ([]domain.Vtxo, []domain.Vtxo, error) {
query := badgerhold.Where("Redeemed").Eq(false) query := badgerhold.Where("Redeemed").Eq(false)
if len(pubkey) > 0 { if len(pubkey) > 0 {
query = query.And("Pubkey").Eq(pubkey) query = query.And("PubKey").Eq(pubkey)
} }
vtxos, err := r.findVtxos(ctx, query) vtxos, err := r.findVtxos(ctx, query)
if err != nil { if err != nil {

View File

@@ -26,7 +26,7 @@ const (
pubkey2 = "33ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0" pubkey2 = "33ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0"
) )
var congestionTree = [][]tree.Node{ var vtxoTree = [][]tree.Node{
{ {
{ {
Txid: randomString(32), Txid: randomString(32),
@@ -149,7 +149,7 @@ func testRoundEventRepository(t *testing.T, svc ports.RepoManager) {
}, },
domain.RoundFinalizationStarted{ domain.RoundFinalizationStarted{
Id: "1ea610ff-bf3e-4068-9bfd-b6c3f553467e", Id: "1ea610ff-bf3e-4068-9bfd-b6c3f553467e",
CongestionTree: congestionTree, VtxoTree: vtxoTree,
Connectors: []string{emptyPtx, emptyPtx}, Connectors: []string{emptyPtx, emptyPtx},
RoundTx: emptyTx, RoundTx: emptyTx,
}, },
@@ -157,8 +157,8 @@ func testRoundEventRepository(t *testing.T, svc ports.RepoManager) {
handler: func(round *domain.Round) { handler: func(round *domain.Round) {
require.NotNil(t, round) require.NotNil(t, round)
require.Len(t, round.Events(), 2) require.Len(t, round.Events(), 2)
require.Len(t, round.CongestionTree, 3) require.Len(t, round.VtxoTree, 3)
require.Equal(t, round.CongestionTree.NumberOfNodes(), 7) require.Equal(t, round.VtxoTree.NumberOfNodes(), 7)
require.Len(t, round.Connectors, 2) require.Len(t, round.Connectors, 2)
}, },
}, },
@@ -171,7 +171,7 @@ func testRoundEventRepository(t *testing.T, svc ports.RepoManager) {
}, },
domain.RoundFinalizationStarted{ domain.RoundFinalizationStarted{
Id: "7578231e-428d-45ae-aaa4-e62c77ad5cec", Id: "7578231e-428d-45ae-aaa4-e62c77ad5cec",
CongestionTree: congestionTree, VtxoTree: vtxoTree,
Connectors: []string{emptyPtx, emptyPtx}, Connectors: []string{emptyPtx, emptyPtx},
RoundTx: emptyTx, RoundTx: emptyTx,
}, },
@@ -237,9 +237,9 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) {
require.Condition(t, roundsMatch(*round, *roundById)) require.Condition(t, roundsMatch(*round, *roundById))
newEvents := []domain.RoundEvent{ newEvents := []domain.RoundEvent{
domain.PaymentsRegistered{ domain.TxRequestsRegistered{
Id: roundId, Id: roundId,
Payments: []domain.Payment{ TxRequests: []domain.TxRequest{
{ {
Id: uuid.New().String(), Id: uuid.New().String(),
Inputs: []domain.Vtxo{ Inputs: []domain.Vtxo{
@@ -250,12 +250,12 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) {
}, },
RoundTxid: randomString(32), RoundTxid: randomString(32),
ExpireAt: 7980322, ExpireAt: 7980322,
Pubkey: randomString(32), PubKey: randomString(32),
Amount: 300, Amount: 300,
}, },
}, },
Receivers: []domain.Receiver{{ Receivers: []domain.Receiver{{
Pubkey: randomString(32), PubKey: randomString(32),
Amount: 300, Amount: 300,
}}, }},
}, },
@@ -270,17 +270,17 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) {
}, },
RoundTxid: randomString(32), RoundTxid: randomString(32),
ExpireAt: 7980322, ExpireAt: 7980322,
Pubkey: randomString(32), PubKey: randomString(32),
Amount: 600, Amount: 600,
}, },
}, },
Receivers: []domain.Receiver{ Receivers: []domain.Receiver{
{ {
Pubkey: randomString(32), PubKey: randomString(32),
Amount: 400, Amount: 400,
}, },
{ {
Pubkey: randomString(32), PubKey: randomString(32),
Amount: 200, Amount: 200,
}, },
}, },
@@ -289,15 +289,15 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) {
}, },
domain.RoundFinalizationStarted{ domain.RoundFinalizationStarted{
Id: roundId, Id: roundId,
CongestionTree: congestionTree, VtxoTree: vtxoTree,
Connectors: []string{emptyPtx, emptyPtx}, Connectors: []string{emptyPtx, emptyPtx},
RoundTx: emptyTx, RoundTx: emptyTx,
}, },
} }
events = append(events, newEvents...) events = append(events, newEvents...)
updatedRound := domain.NewRoundFromEvents(events) updatedRound := domain.NewRoundFromEvents(events)
for _, pay := range updatedRound.Payments { for _, request := range updatedRound.TxRequests {
err = svc.Vtxos().AddVtxos(ctx, pay.Inputs) err = svc.Vtxos().AddVtxos(ctx, request.Inputs)
require.NoError(t, err) require.NoError(t, err)
} }
@@ -346,7 +346,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
Txid: randomString(32), Txid: randomString(32),
VOut: 0, VOut: 0,
}, },
Pubkey: pubkey, PubKey: pubkey,
Amount: 1000, Amount: 1000,
}, },
{ {
@@ -354,7 +354,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
Txid: randomString(32), Txid: randomString(32),
VOut: 1, VOut: 1,
}, },
Pubkey: pubkey, PubKey: pubkey,
Amount: 2000, Amount: 2000,
}, },
} }
@@ -363,7 +363,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
Txid: randomString(32), Txid: randomString(32),
VOut: 1, VOut: 1,
}, },
Pubkey: pubkey2, PubKey: pubkey2,
Amount: 2000, Amount: 2000,
}) })
@@ -567,8 +567,8 @@ func roundsMatch(expected, got domain.Round) assert.Comparison {
return false return false
} }
for k, v := range expected.Payments { for k, v := range expected.TxRequests {
gotValue, ok := got.Payments[k] gotValue, ok := got.TxRequests[k]
if !ok { if !ok {
return false return false
} }
@@ -612,7 +612,7 @@ func roundsMatch(expected, got domain.Round) assert.Comparison {
} }
} }
if !reflect.DeepEqual(expected.CongestionTree, got.CongestionTree) { if !reflect.DeepEqual(expected.VtxoTree, got.VtxoTree) {
return false return false
} }

View File

@@ -1,6 +1,6 @@
DROP TABLE IF EXISTS round; DROP TABLE IF EXISTS round;
DROP TABLE IF EXISTS payment; DROP TABLE IF EXISTS tx_request;
DROP TABLE IF EXISTS receiver; DROP TABLE IF EXISTS receiver;

View File

@@ -13,19 +13,19 @@ CREATE TABLE IF NOT EXISTS round (
swept BOOLEAN NOT NULL swept BOOLEAN NOT NULL
); );
CREATE TABLE IF NOT EXISTS payment ( CREATE TABLE IF NOT EXISTS tx_request (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
round_id TEXT NOT NULL, round_id TEXT NOT NULL,
FOREIGN KEY (round_id) REFERENCES round(id) FOREIGN KEY (round_id) REFERENCES round(id)
); );
CREATE TABLE IF NOT EXISTS receiver ( CREATE TABLE IF NOT EXISTS receiver (
payment_id TEXT NOT NULL, request_id TEXT NOT NULL,
pubkey TEXT, pubkey TEXT,
onchain_address TEXT, onchain_address TEXT,
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
FOREIGN KEY (payment_id) REFERENCES payment(id), FOREIGN KEY (request_id) REFERENCES tx_request(id),
PRIMARY KEY (payment_id, pubkey, onchain_address) PRIMARY KEY (request_id, pubkey, onchain_address)
); );
CREATE TABLE IF NOT EXISTS tx ( CREATE TABLE IF NOT EXISTS tx (
@@ -46,17 +46,17 @@ CREATE TABLE IF NOT EXISTS vtxo (
vout INTEGER NOT NULL, vout INTEGER NOT NULL,
pubkey TEXT NOT NULL, pubkey TEXT NOT NULL,
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
pool_tx TEXT NOT NULL, round_tx TEXT NOT NULL,
spent_by TEXT NOT NULL, spent_by TEXT NOT NULL,
spent BOOLEAN NOT NULL, spent BOOLEAN NOT NULL,
redeemed BOOLEAN NOT NULL, redeemed BOOLEAN NOT NULL,
swept BOOLEAN NOT NULL, swept BOOLEAN NOT NULL,
expire_at INTEGER NOT NULL, expire_at INTEGER NOT NULL,
created_at INTEGER NOT NULL, created_at INTEGER NOT NULL,
payment_id TEXT, request_id TEXT,
redeem_tx TEXT, redeem_tx TEXT,
PRIMARY KEY (txid, vout), PRIMARY KEY (txid, vout),
FOREIGN KEY (payment_id) REFERENCES payment(id) FOREIGN KEY (request_id) REFERENCES tx_request(id)
); );
CREATE TABLE IF NOT EXISTS note ( CREATE TABLE IF NOT EXISTS note (
@@ -81,22 +81,22 @@ FROM entity
LEFT OUTER JOIN entity_vtxo LEFT OUTER JOIN entity_vtxo
ON entity.id=entity_vtxo.entity_id; ON entity.id=entity_vtxo.entity_id;
CREATE VIEW round_payment_vw AS SELECT payment.* CREATE VIEW round_request_vw AS SELECT tx_request.*
FROM round FROM round
LEFT OUTER JOIN payment LEFT OUTER JOIN tx_request
ON round.id=payment.round_id; ON round.id=tx_request.round_id;
CREATE VIEW round_tx_vw AS SELECT tx.* CREATE VIEW round_tx_vw AS SELECT tx.*
FROM round FROM round
LEFT OUTER JOIN tx LEFT OUTER JOIN tx
ON round.id=tx.round_id; ON round.id=tx.round_id;
CREATE VIEW payment_receiver_vw AS SELECT receiver.* CREATE VIEW request_receiver_vw AS SELECT receiver.*
FROM payment FROM tx_request
LEFT OUTER JOIN receiver LEFT OUTER JOIN receiver
ON payment.id=receiver.payment_id; ON tx_request.id=receiver.request_id;
CREATE VIEW payment_vtxo_vw AS SELECT vtxo.* CREATE VIEW request_vtxo_vw AS SELECT vtxo.*
FROM payment FROM tx_request
LEFT OUTER JOIN vtxo LEFT OUTER JOIN vtxo
ON payment.id=vtxo.payment_id; ON tx_request.id=vtxo.request_id;

View File

@@ -86,7 +86,7 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
return fmt.Errorf("failed to upsert round: %w", err) return fmt.Errorf("failed to upsert round: %w", err)
} }
if len(round.ForfeitTxs) > 0 || len(round.Connectors) > 0 || len(round.CongestionTree) > 0 { if len(round.ForfeitTxs) > 0 || len(round.Connectors) > 0 || len(round.VtxoTree) > 0 {
for pos, tx := range round.ForfeitTxs { for pos, tx := range round.ForfeitTxs {
if err := querierWithTx.UpsertTransaction( if err := querierWithTx.UpsertTransaction(
ctx, ctx,
@@ -115,7 +115,7 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
} }
} }
for level, levelTxs := range round.CongestionTree { for level, levelTxs := range round.VtxoTree {
for pos, tx := range levelTxs { for pos, tx := range levelTxs {
if err := querierWithTx.UpsertTransaction( if err := querierWithTx.UpsertTransaction(
ctx, ctx,
@@ -148,27 +148,27 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
} }
} }
if len(round.Payments) > 0 { if len(round.TxRequests) > 0 {
for _, payment := range round.Payments { for _, request := range round.TxRequests {
if err := querierWithTx.UpsertPayment( if err := querierWithTx.UpsertTxRequest(
ctx, ctx,
queries.UpsertPaymentParams{ queries.UpsertTxRequestParams{
ID: payment.Id, ID: request.Id,
RoundID: round.Id, RoundID: round.Id,
}, },
); err != nil { ); err != nil {
return fmt.Errorf("failed to upsert payment: %w", err) return fmt.Errorf("failed to upsert tx request: %w", err)
} }
for _, receiver := range payment.Receivers { for _, receiver := range request.Receivers {
if err := querierWithTx.UpsertReceiver( if err := querierWithTx.UpsertReceiver(
ctx, ctx,
queries.UpsertReceiverParams{ queries.UpsertReceiverParams{
PaymentID: payment.Id, RequestID: request.Id,
Amount: int64(receiver.Amount), Amount: int64(receiver.Amount),
Pubkey: sql.NullString{ Pubkey: sql.NullString{
String: receiver.Pubkey, String: receiver.PubKey,
Valid: len(receiver.Pubkey) > 0, Valid: len(receiver.PubKey) > 0,
}, },
OnchainAddress: sql.NullString{ OnchainAddress: sql.NullString{
String: receiver.OnchainAddress, String: receiver.OnchainAddress,
@@ -180,19 +180,19 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
} }
} }
for _, input := range payment.Inputs { for _, input := range request.Inputs {
if err := querierWithTx.UpdateVtxoPaymentId( if err := querierWithTx.UpdateVtxoRequestId(
ctx, ctx,
queries.UpdateVtxoPaymentIdParams{ queries.UpdateVtxoRequestIdParams{
PaymentID: sql.NullString{ RequestID: sql.NullString{
String: payment.Id, String: request.Id,
Valid: true, Valid: true,
}, },
Txid: input.Txid, Txid: input.Txid,
Vout: int64(input.VOut), Vout: int64(input.VOut),
}, },
); err != nil { ); err != nil {
return fmt.Errorf("failed to update vtxo payment id: %w", err) return fmt.Errorf("failed to update vtxo request id: %w", err)
} }
} }
} }
@@ -210,18 +210,18 @@ func (r *roundRepository) GetRoundWithId(ctx context.Context, id string) (*domai
return nil, err return nil, err
} }
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows)) rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows { for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{ rvs = append(rvs, combinedRow{
round: row.Round, round: row.Round,
payment: row.RoundPaymentVw, request: row.RoundRequestVw,
tx: row.RoundTxVw, tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw, receiver: row.RequestReceiverVw,
vtxo: row.PaymentVtxoVw, vtxo: row.RequestVtxoVw,
}) })
} }
rounds, err := readRoundRows(rvs) rounds, err := rowsToRounds(rvs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -239,18 +239,18 @@ func (r *roundRepository) GetRoundWithTxid(ctx context.Context, txid string) (*d
return nil, err return nil, err
} }
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows)) rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows { for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{ rvs = append(rvs, combinedRow{
round: row.Round, round: row.Round,
payment: row.RoundPaymentVw, request: row.RoundRequestVw,
tx: row.RoundTxVw, tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw, receiver: row.RequestReceiverVw,
vtxo: row.PaymentVtxoVw, vtxo: row.RequestVtxoVw,
}) })
} }
rounds, err := readRoundRows(rvs) rounds, err := rowsToRounds(rvs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -268,18 +268,18 @@ func (r *roundRepository) GetSweepableRounds(ctx context.Context) ([]domain.Roun
return nil, err return nil, err
} }
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows)) rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows { for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{ rvs = append(rvs, combinedRow{
round: row.Round, round: row.Round,
payment: row.RoundPaymentVw, request: row.RoundRequestVw,
tx: row.RoundTxVw, tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw, receiver: row.RequestReceiverVw,
vtxo: row.PaymentVtxoVw, vtxo: row.RequestVtxoVw,
}) })
} }
rounds, err := readRoundRows(rvs) rounds, err := rowsToRounds(rvs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -299,18 +299,18 @@ func (r *roundRepository) GetSweptRounds(ctx context.Context) ([]domain.Round, e
return nil, err return nil, err
} }
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows)) rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows { for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{ rvs = append(rvs, combinedRow{
round: row.Round, round: row.Round,
payment: row.RoundPaymentVw, request: row.RoundRequestVw,
tx: row.RoundTxVw, tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw, receiver: row.RequestReceiverVw,
vtxo: row.PaymentVtxoVw, vtxo: row.RequestVtxoVw,
}) })
} }
rounds, err := readRoundRows(rvs) rounds, err := rowsToRounds(rvs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -324,23 +324,23 @@ func (r *roundRepository) GetSweptRounds(ctx context.Context) ([]domain.Round, e
return res, nil return res, nil
} }
func rowToReceiver(row queries.PaymentReceiverVw) domain.Receiver { func rowToReceiver(row queries.RequestReceiverVw) domain.Receiver {
return domain.Receiver{ return domain.Receiver{
Amount: uint64(row.Amount.Int64), Amount: uint64(row.Amount.Int64),
Pubkey: row.Pubkey.String, PubKey: row.Pubkey.String,
OnchainAddress: row.OnchainAddress.String, OnchainAddress: row.OnchainAddress.String,
} }
} }
type roundPaymentTxReceiverVtxoRow struct { type combinedRow struct {
round queries.Round round queries.Round
payment queries.RoundPaymentVw request queries.RoundRequestVw
tx queries.RoundTxVw tx queries.RoundTxVw
receiver queries.PaymentReceiverVw receiver queries.RequestReceiverVw
vtxo queries.PaymentVtxoVw vtxo queries.RequestVtxoVw
} }
func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error) { func rowsToRounds(rows []combinedRow) ([]*domain.Round, error) {
rounds := make(map[string]*domain.Round) rounds := make(map[string]*domain.Round)
for _, v := range rows { for _, v := range rows {
@@ -364,35 +364,34 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
DustAmount: uint64(v.round.DustAmount), DustAmount: uint64(v.round.DustAmount),
Version: uint(v.round.Version), Version: uint(v.round.Version),
Swept: v.round.Swept, Swept: v.round.Swept,
Payments: make(map[string]domain.Payment), TxRequests: make(map[string]domain.TxRequest),
} }
} }
if v.payment.ID.Valid { if v.request.ID.Valid {
payment, ok := round.Payments[v.payment.ID.String] request, ok := round.TxRequests[v.request.ID.String]
if !ok { if !ok {
payment = domain.Payment{ request = domain.TxRequest{
Id: v.payment.ID.String, Id: v.request.ID.String,
Inputs: make([]domain.Vtxo, 0), Inputs: make([]domain.Vtxo, 0),
Receivers: make([]domain.Receiver, 0), Receivers: make([]domain.Receiver, 0),
} }
round.Payments[v.payment.ID.String] = payment round.TxRequests[v.request.ID.String] = request
} }
if v.vtxo.PaymentID.Valid { if v.vtxo.RequestID.Valid {
payment, ok = round.Payments[v.vtxo.PaymentID.String] request, ok = round.TxRequests[v.vtxo.RequestID.String]
if !ok { if !ok {
payment = domain.Payment{ request = domain.TxRequest{
Id: v.vtxo.PaymentID.String, Id: v.vtxo.RequestID.String,
Inputs: make([]domain.Vtxo, 0), Inputs: make([]domain.Vtxo, 0),
Receivers: make([]domain.Receiver, 0), Receivers: make([]domain.Receiver, 0),
} }
} }
vtxo := rowToPaymentVtxoVw(v.vtxo) vtxo := combinedRowToVtxo(v.vtxo)
found := false found := false
for _, v := range request.Inputs {
for _, v := range payment.Inputs {
if vtxo.Txid == v.Txid && vtxo.VOut == v.VOut { if vtxo.Txid == v.Txid && vtxo.VOut == v.VOut {
found = true found = true
break break
@@ -400,16 +399,16 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
} }
if !found { if !found {
payment.Inputs = append(payment.Inputs, rowToPaymentVtxoVw(v.vtxo)) request.Inputs = append(request.Inputs, combinedRowToVtxo(v.vtxo))
round.Payments[v.vtxo.PaymentID.String] = payment round.TxRequests[v.vtxo.RequestID.String] = request
} }
} }
if v.receiver.PaymentID.Valid { if v.receiver.RequestID.Valid {
payment, ok = round.Payments[v.receiver.PaymentID.String] request, ok = round.TxRequests[v.receiver.RequestID.String]
if !ok { if !ok {
payment = domain.Payment{ request = domain.TxRequest{
Id: v.receiver.PaymentID.String, Id: v.receiver.RequestID.String,
Inputs: make([]domain.Vtxo, 0), Inputs: make([]domain.Vtxo, 0),
Receivers: make([]domain.Receiver, 0), Receivers: make([]domain.Receiver, 0),
} }
@@ -418,17 +417,17 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
rcv := rowToReceiver(v.receiver) rcv := rowToReceiver(v.receiver)
found := false found := false
for _, rcv := range payment.Receivers { for _, rcv := range request.Receivers {
if (v.receiver.Pubkey.Valid || v.receiver.OnchainAddress.Valid) && v.receiver.Amount.Valid { if (v.receiver.Pubkey.Valid || v.receiver.OnchainAddress.Valid) && v.receiver.Amount.Valid {
if rcv.Pubkey == v.receiver.Pubkey.String && rcv.OnchainAddress == v.receiver.OnchainAddress.String && int64(rcv.Amount) == v.receiver.Amount.Int64 { if rcv.PubKey == v.receiver.Pubkey.String && rcv.OnchainAddress == v.receiver.OnchainAddress.String && int64(rcv.Amount) == v.receiver.Amount.Int64 {
found = true found = true
break break
} }
} }
} }
if !found { if !found {
payment.Receivers = append(payment.Receivers, rcv) request.Receivers = append(request.Receivers, rcv)
round.Payments[v.receiver.PaymentID.String] = payment round.TxRequests[v.receiver.RequestID.String] = request
} }
} }
} }
@@ -444,10 +443,10 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
round.Connectors[position.Int64] = v.tx.Tx.String round.Connectors[position.Int64] = v.tx.Tx.String
case "tree": case "tree":
level := v.tx.TreeLevel level := v.tx.TreeLevel
round.CongestionTree = extendArray(round.CongestionTree, int(level.Int64)) round.VtxoTree = extendArray(round.VtxoTree, int(level.Int64))
round.CongestionTree[int(level.Int64)] = extendArray(round.CongestionTree[int(level.Int64)], int(position.Int64)) round.VtxoTree[int(level.Int64)] = extendArray(round.VtxoTree[int(level.Int64)], int(position.Int64))
if round.CongestionTree[int(level.Int64)][position.Int64] == (tree.Node{}) { if round.VtxoTree[int(level.Int64)][position.Int64] == (tree.Node{}) {
round.CongestionTree[int(level.Int64)][position.Int64] = tree.Node{ round.VtxoTree[int(level.Int64)][position.Int64] = tree.Node{
Tx: v.tx.Tx.String, Tx: v.tx.Tx.String,
Txid: v.tx.Txid.String, Txid: v.tx.Txid.String,
ParentTxid: v.tx.ParentTxid.String, ParentTxid: v.tx.ParentTxid.String,
@@ -469,15 +468,15 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
return result, nil return result, nil
} }
func rowToPaymentVtxoVw(row queries.PaymentVtxoVw) domain.Vtxo { func combinedRowToVtxo(row queries.RequestVtxoVw) domain.Vtxo {
return domain.Vtxo{ return domain.Vtxo{
VtxoKey: domain.VtxoKey{ VtxoKey: domain.VtxoKey{
Txid: row.Txid.String, Txid: row.Txid.String,
VOut: uint32(row.Vout.Int64), VOut: uint32(row.Vout.Int64),
}, },
Amount: uint64(row.Amount.Int64), Amount: uint64(row.Amount.Int64),
Pubkey: row.Pubkey.String, PubKey: row.Pubkey.String,
RoundTxid: row.PoolTx.String, RoundTxid: row.RoundTx.String,
SpentBy: row.SpentBy.String, SpentBy: row.SpentBy.String,
Spent: row.Spent.Bool, Spent: row.Spent.Bool,
Redeemed: row.Redeemed.Bool, Redeemed: row.Redeemed.Bool,

View File

@@ -39,41 +39,36 @@ type Note struct {
ID int64 ID int64
} }
type Payment struct { type Receiver struct {
ID string RequestID string
RoundID string Pubkey sql.NullString
OnchainAddress sql.NullString
Amount int64
} }
type PaymentReceiverVw struct { type RequestReceiverVw struct {
PaymentID sql.NullString RequestID sql.NullString
Pubkey sql.NullString Pubkey sql.NullString
OnchainAddress sql.NullString OnchainAddress sql.NullString
Amount sql.NullInt64 Amount sql.NullInt64
} }
type PaymentVtxoVw struct { type RequestVtxoVw struct {
Txid sql.NullString Txid sql.NullString
Vout sql.NullInt64 Vout sql.NullInt64
Pubkey sql.NullString Pubkey sql.NullString
Amount sql.NullInt64 Amount sql.NullInt64
PoolTx sql.NullString RoundTx sql.NullString
SpentBy sql.NullString SpentBy sql.NullString
Spent sql.NullBool Spent sql.NullBool
Redeemed sql.NullBool Redeemed sql.NullBool
Swept sql.NullBool Swept sql.NullBool
ExpireAt sql.NullInt64 ExpireAt sql.NullInt64
CreatedAt sql.NullInt64 CreatedAt sql.NullInt64
PaymentID sql.NullString RequestID sql.NullString
RedeemTx sql.NullString RedeemTx sql.NullString
} }
type Receiver struct {
PaymentID string
Pubkey sql.NullString
OnchainAddress sql.NullString
Amount int64
}
type Round struct { type Round struct {
ID string ID string
StartingTimestamp int64 StartingTimestamp int64
@@ -89,7 +84,7 @@ type Round struct {
Swept bool Swept bool
} }
type RoundPaymentVw struct { type RoundRequestVw struct {
ID sql.NullString ID sql.NullString
RoundID sql.NullString RoundID sql.NullString
} }
@@ -118,18 +113,23 @@ type Tx struct {
IsLeaf sql.NullBool IsLeaf sql.NullBool
} }
type TxRequest struct {
ID string
RoundID string
}
type Vtxo struct { type Vtxo struct {
Txid string Txid string
Vout int64 Vout int64
Pubkey string Pubkey string
Amount int64 Amount int64
PoolTx string RoundTx string
SpentBy string SpentBy string
Spent bool Spent bool
Redeemed bool Redeemed bool
Swept bool Swept bool
ExpireAt int64 ExpireAt int64
CreatedAt int64 CreatedAt int64
PaymentID sql.NullString RequestID sql.NullString
RedeemTx sql.NullString RedeemTx sql.NullString
} }

View File

@@ -191,7 +191,7 @@ func (q *Queries) SelectEntitiesByVtxo(ctx context.Context, arg SelectEntitiesBy
} }
const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.round_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.request_id, vtxo.redeem_tx FROM vtxo
WHERE redeemed = false WHERE redeemed = false
` `
@@ -213,14 +213,14 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
&i.Vtxo.Vout, &i.Vtxo.Vout,
&i.Vtxo.Pubkey, &i.Vtxo.Pubkey,
&i.Vtxo.Amount, &i.Vtxo.Amount,
&i.Vtxo.PoolTx, &i.Vtxo.RoundTx,
&i.Vtxo.SpentBy, &i.Vtxo.SpentBy,
&i.Vtxo.Spent, &i.Vtxo.Spent,
&i.Vtxo.Redeemed, &i.Vtxo.Redeemed,
&i.Vtxo.Swept, &i.Vtxo.Swept,
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt, &i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID, &i.Vtxo.RequestID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
@@ -237,7 +237,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
} }
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.round_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.request_id, vtxo.redeem_tx FROM vtxo
WHERE redeemed = false AND pubkey = ? WHERE redeemed = false AND pubkey = ?
` `
@@ -259,14 +259,14 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey s
&i.Vtxo.Vout, &i.Vtxo.Vout,
&i.Vtxo.Pubkey, &i.Vtxo.Pubkey,
&i.Vtxo.Amount, &i.Vtxo.Amount,
&i.Vtxo.PoolTx, &i.Vtxo.RoundTx,
&i.Vtxo.SpentBy, &i.Vtxo.SpentBy,
&i.Vtxo.Spent, &i.Vtxo.Spent,
&i.Vtxo.Redeemed, &i.Vtxo.Redeemed,
&i.Vtxo.Swept, &i.Vtxo.Swept,
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt, &i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID, &i.Vtxo.RequestID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
@@ -343,24 +343,24 @@ func (q *Queries) SelectRoundIdsInRange(ctx context.Context, arg SelectRoundIdsI
const selectRoundWithRoundId = `-- name: SelectRoundWithRoundId :many const selectRoundWithRoundId = `-- name: SelectRoundWithRoundId :many
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept, SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
round_payment_vw.id, round_payment_vw.round_id, round_request_vw.id, round_request_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, request_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx request_vtxo_vw.txid, request_vtxo_vw.vout, request_vtxo_vw.pubkey, request_vtxo_vw.amount, request_vtxo_vw.round_tx, request_vtxo_vw.spent_by, request_vtxo_vw.spent, request_vtxo_vw.redeemed, request_vtxo_vw.swept, request_vtxo_vw.expire_at, request_vtxo_vw.created_at, request_vtxo_vw.request_id, request_vtxo_vw.redeem_tx
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.id = ? WHERE round.id = ?
` `
type SelectRoundWithRoundIdRow struct { type SelectRoundWithRoundIdRow struct {
Round Round Round Round
RoundPaymentVw RoundPaymentVw RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw RequestReceiverVw RequestReceiverVw
PaymentVtxoVw PaymentVtxoVw RequestVtxoVw RequestVtxoVw
} }
func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]SelectRoundWithRoundIdRow, error) { func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]SelectRoundWithRoundIdRow, error) {
@@ -385,8 +385,8 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
&i.Round.DustAmount, &i.Round.DustAmount,
&i.Round.Version, &i.Round.Version,
&i.Round.Swept, &i.Round.Swept,
&i.RoundPaymentVw.ID, &i.RoundRequestVw.ID,
&i.RoundPaymentVw.RoundID, &i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID, &i.RoundTxVw.ID,
&i.RoundTxVw.Tx, &i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID, &i.RoundTxVw.RoundID,
@@ -396,23 +396,23 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
&i.RoundTxVw.TreeLevel, &i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid, &i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf, &i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID, &i.RequestReceiverVw.RequestID,
&i.PaymentReceiverVw.Pubkey, &i.RequestReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress, &i.RequestReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount, &i.RequestReceiverVw.Amount,
&i.PaymentVtxoVw.Txid, &i.RequestVtxoVw.Txid,
&i.PaymentVtxoVw.Vout, &i.RequestVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey, &i.RequestVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount, &i.RequestVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx, &i.RequestVtxoVw.RoundTx,
&i.PaymentVtxoVw.SpentBy, &i.RequestVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent, &i.RequestVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed, &i.RequestVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept, &i.RequestVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt, &i.RequestVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt, &i.RequestVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID, &i.RequestVtxoVw.RequestID,
&i.PaymentVtxoVw.RedeemTx, &i.RequestVtxoVw.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -429,24 +429,24 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
const selectRoundWithRoundTxId = `-- name: SelectRoundWithRoundTxId :many const selectRoundWithRoundTxId = `-- name: SelectRoundWithRoundTxId :many
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept, SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
round_payment_vw.id, round_payment_vw.round_id, round_request_vw.id, round_request_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, request_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx request_vtxo_vw.txid, request_vtxo_vw.vout, request_vtxo_vw.pubkey, request_vtxo_vw.amount, request_vtxo_vw.round_tx, request_vtxo_vw.spent_by, request_vtxo_vw.spent, request_vtxo_vw.redeemed, request_vtxo_vw.swept, request_vtxo_vw.expire_at, request_vtxo_vw.created_at, request_vtxo_vw.request_id, request_vtxo_vw.redeem_tx
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.txid = ? WHERE round.txid = ?
` `
type SelectRoundWithRoundTxIdRow struct { type SelectRoundWithRoundTxIdRow struct {
Round Round Round Round
RoundPaymentVw RoundPaymentVw RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw RequestReceiverVw RequestReceiverVw
PaymentVtxoVw PaymentVtxoVw RequestVtxoVw RequestVtxoVw
} }
func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]SelectRoundWithRoundTxIdRow, error) { func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]SelectRoundWithRoundTxIdRow, error) {
@@ -471,8 +471,8 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
&i.Round.DustAmount, &i.Round.DustAmount,
&i.Round.Version, &i.Round.Version,
&i.Round.Swept, &i.Round.Swept,
&i.RoundPaymentVw.ID, &i.RoundRequestVw.ID,
&i.RoundPaymentVw.RoundID, &i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID, &i.RoundTxVw.ID,
&i.RoundTxVw.Tx, &i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID, &i.RoundTxVw.RoundID,
@@ -482,23 +482,23 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
&i.RoundTxVw.TreeLevel, &i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid, &i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf, &i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID, &i.RequestReceiverVw.RequestID,
&i.PaymentReceiverVw.Pubkey, &i.RequestReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress, &i.RequestReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount, &i.RequestReceiverVw.Amount,
&i.PaymentVtxoVw.Txid, &i.RequestVtxoVw.Txid,
&i.PaymentVtxoVw.Vout, &i.RequestVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey, &i.RequestVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount, &i.RequestVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx, &i.RequestVtxoVw.RoundTx,
&i.PaymentVtxoVw.SpentBy, &i.RequestVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent, &i.RequestVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed, &i.RequestVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept, &i.RequestVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt, &i.RequestVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt, &i.RequestVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID, &i.RequestVtxoVw.RequestID,
&i.PaymentVtxoVw.RedeemTx, &i.RequestVtxoVw.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -515,24 +515,24 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
const selectSweepableRounds = `-- name: SelectSweepableRounds :many const selectSweepableRounds = `-- name: SelectSweepableRounds :many
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept, SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
round_payment_vw.id, round_payment_vw.round_id, round_request_vw.id, round_request_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, request_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx request_vtxo_vw.txid, request_vtxo_vw.vout, request_vtxo_vw.pubkey, request_vtxo_vw.amount, request_vtxo_vw.round_tx, request_vtxo_vw.spent_by, request_vtxo_vw.spent, request_vtxo_vw.redeemed, request_vtxo_vw.swept, request_vtxo_vw.expire_at, request_vtxo_vw.created_at, request_vtxo_vw.request_id, request_vtxo_vw.redeem_tx
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.swept = false AND round.ended = true AND round.failed = false WHERE round.swept = false AND round.ended = true AND round.failed = false
` `
type SelectSweepableRoundsRow struct { type SelectSweepableRoundsRow struct {
Round Round Round Round
RoundPaymentVw RoundPaymentVw RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw RequestReceiverVw RequestReceiverVw
PaymentVtxoVw PaymentVtxoVw RequestVtxoVw RequestVtxoVw
} }
func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableRoundsRow, error) { func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableRoundsRow, error) {
@@ -557,8 +557,8 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
&i.Round.DustAmount, &i.Round.DustAmount,
&i.Round.Version, &i.Round.Version,
&i.Round.Swept, &i.Round.Swept,
&i.RoundPaymentVw.ID, &i.RoundRequestVw.ID,
&i.RoundPaymentVw.RoundID, &i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID, &i.RoundTxVw.ID,
&i.RoundTxVw.Tx, &i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID, &i.RoundTxVw.RoundID,
@@ -568,23 +568,23 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
&i.RoundTxVw.TreeLevel, &i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid, &i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf, &i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID, &i.RequestReceiverVw.RequestID,
&i.PaymentReceiverVw.Pubkey, &i.RequestReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress, &i.RequestReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount, &i.RequestReceiverVw.Amount,
&i.PaymentVtxoVw.Txid, &i.RequestVtxoVw.Txid,
&i.PaymentVtxoVw.Vout, &i.RequestVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey, &i.RequestVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount, &i.RequestVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx, &i.RequestVtxoVw.RoundTx,
&i.PaymentVtxoVw.SpentBy, &i.RequestVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent, &i.RequestVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed, &i.RequestVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept, &i.RequestVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt, &i.RequestVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt, &i.RequestVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID, &i.RequestVtxoVw.RequestID,
&i.PaymentVtxoVw.RedeemTx, &i.RequestVtxoVw.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -600,7 +600,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
} }
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.round_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.request_id, vtxo.redeem_tx FROM vtxo
WHERE redeemed = false AND swept = false WHERE redeemed = false AND swept = false
` `
@@ -622,14 +622,14 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
&i.Vtxo.Vout, &i.Vtxo.Vout,
&i.Vtxo.Pubkey, &i.Vtxo.Pubkey,
&i.Vtxo.Amount, &i.Vtxo.Amount,
&i.Vtxo.PoolTx, &i.Vtxo.RoundTx,
&i.Vtxo.SpentBy, &i.Vtxo.SpentBy,
&i.Vtxo.Spent, &i.Vtxo.Spent,
&i.Vtxo.Redeemed, &i.Vtxo.Redeemed,
&i.Vtxo.Swept, &i.Vtxo.Swept,
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt, &i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID, &i.Vtxo.RequestID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
@@ -647,24 +647,24 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
const selectSweptRounds = `-- name: SelectSweptRounds :many const selectSweptRounds = `-- name: SelectSweptRounds :many
SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept, SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round.failed, round.stage_code, round.txid, round.unsigned_tx, round.connector_address, round.dust_amount, round.version, round.swept,
round_payment_vw.id, round_payment_vw.round_id, round_request_vw.id, round_request_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, request_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx request_vtxo_vw.txid, request_vtxo_vw.vout, request_vtxo_vw.pubkey, request_vtxo_vw.amount, request_vtxo_vw.round_tx, request_vtxo_vw.spent_by, request_vtxo_vw.spent, request_vtxo_vw.redeemed, request_vtxo_vw.swept, request_vtxo_vw.expire_at, request_vtxo_vw.created_at, request_vtxo_vw.request_id, request_vtxo_vw.redeem_tx
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.swept = true AND round.failed = false AND round.ended = true AND round.connector_address <> '' WHERE round.swept = true AND round.failed = false AND round.ended = true AND round.connector_address <> ''
` `
type SelectSweptRoundsRow struct { type SelectSweptRoundsRow struct {
Round Round Round Round
RoundPaymentVw RoundPaymentVw RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw RequestReceiverVw RequestReceiverVw
PaymentVtxoVw PaymentVtxoVw RequestVtxoVw RequestVtxoVw
} }
func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow, error) { func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow, error) {
@@ -689,8 +689,8 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
&i.Round.DustAmount, &i.Round.DustAmount,
&i.Round.Version, &i.Round.Version,
&i.Round.Swept, &i.Round.Swept,
&i.RoundPaymentVw.ID, &i.RoundRequestVw.ID,
&i.RoundPaymentVw.RoundID, &i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID, &i.RoundTxVw.ID,
&i.RoundTxVw.Tx, &i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID, &i.RoundTxVw.RoundID,
@@ -700,23 +700,23 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
&i.RoundTxVw.TreeLevel, &i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid, &i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf, &i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID, &i.RequestReceiverVw.RequestID,
&i.PaymentReceiverVw.Pubkey, &i.RequestReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress, &i.RequestReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount, &i.RequestReceiverVw.Amount,
&i.PaymentVtxoVw.Txid, &i.RequestVtxoVw.Txid,
&i.PaymentVtxoVw.Vout, &i.RequestVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey, &i.RequestVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount, &i.RequestVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx, &i.RequestVtxoVw.RoundTx,
&i.PaymentVtxoVw.SpentBy, &i.RequestVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent, &i.RequestVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed, &i.RequestVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept, &i.RequestVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt, &i.RequestVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt, &i.RequestVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID, &i.RequestVtxoVw.RequestID,
&i.PaymentVtxoVw.RedeemTx, &i.RequestVtxoVw.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -732,7 +732,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
} }
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.round_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.request_id, vtxo.redeem_tx FROM vtxo
WHERE txid = ? AND vout = ? WHERE txid = ? AND vout = ?
` `
@@ -753,50 +753,50 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
&i.Vtxo.Vout, &i.Vtxo.Vout,
&i.Vtxo.Pubkey, &i.Vtxo.Pubkey,
&i.Vtxo.Amount, &i.Vtxo.Amount,
&i.Vtxo.PoolTx, &i.Vtxo.RoundTx,
&i.Vtxo.SpentBy, &i.Vtxo.SpentBy,
&i.Vtxo.Spent, &i.Vtxo.Spent,
&i.Vtxo.Redeemed, &i.Vtxo.Redeemed,
&i.Vtxo.Swept, &i.Vtxo.Swept,
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt, &i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID, &i.Vtxo.RequestID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
) )
return i, err return i, err
} }
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many const selectVtxosByRoundTxid = `-- name: SelectVtxosByRoundTxid :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.round_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.created_at, vtxo.request_id, vtxo.redeem_tx FROM vtxo
WHERE pool_tx = ? WHERE round_tx = ?
` `
type SelectVtxosByPoolTxidRow struct { type SelectVtxosByRoundTxidRow struct {
Vtxo Vtxo Vtxo Vtxo
} }
func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]SelectVtxosByPoolTxidRow, error) { func (q *Queries) SelectVtxosByRoundTxid(ctx context.Context, roundTx string) ([]SelectVtxosByRoundTxidRow, error) {
rows, err := q.db.QueryContext(ctx, selectVtxosByPoolTxid, poolTx) rows, err := q.db.QueryContext(ctx, selectVtxosByRoundTxid, roundTx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []SelectVtxosByPoolTxidRow var items []SelectVtxosByRoundTxidRow
for rows.Next() { for rows.Next() {
var i SelectVtxosByPoolTxidRow var i SelectVtxosByRoundTxidRow
if err := rows.Scan( if err := rows.Scan(
&i.Vtxo.Txid, &i.Vtxo.Txid,
&i.Vtxo.Vout, &i.Vtxo.Vout,
&i.Vtxo.Pubkey, &i.Vtxo.Pubkey,
&i.Vtxo.Amount, &i.Vtxo.Amount,
&i.Vtxo.PoolTx, &i.Vtxo.RoundTx,
&i.Vtxo.SpentBy, &i.Vtxo.SpentBy,
&i.Vtxo.Spent, &i.Vtxo.Spent,
&i.Vtxo.Redeemed, &i.Vtxo.Redeemed,
&i.Vtxo.Swept, &i.Vtxo.Swept,
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt, &i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID, &i.Vtxo.RequestID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
); err != nil { ); err != nil {
return nil, err return nil, err
@@ -868,18 +868,18 @@ func (q *Queries) UpdateVtxoExpireAt(ctx context.Context, arg UpdateVtxoExpireAt
return err return err
} }
const updateVtxoPaymentId = `-- name: UpdateVtxoPaymentId :exec const updateVtxoRequestId = `-- name: UpdateVtxoRequestId :exec
UPDATE vtxo SET payment_id = ? WHERE txid = ? AND vout = ? UPDATE vtxo SET request_id = ? WHERE txid = ? AND vout = ?
` `
type UpdateVtxoPaymentIdParams struct { type UpdateVtxoRequestIdParams struct {
PaymentID sql.NullString RequestID sql.NullString
Txid string Txid string
Vout int64 Vout int64
} }
func (q *Queries) UpdateVtxoPaymentId(ctx context.Context, arg UpdateVtxoPaymentIdParams) error { func (q *Queries) UpdateVtxoRequestId(ctx context.Context, arg UpdateVtxoRequestIdParams) error {
_, err := q.db.ExecContext(ctx, updateVtxoPaymentId, arg.PaymentID, arg.Txid, arg.Vout) _, err := q.db.ExecContext(ctx, updateVtxoRequestId, arg.RequestID, arg.Txid, arg.Vout)
return err return err
} }
@@ -916,31 +916,16 @@ func (q *Queries) UpsertEntityVtxo(ctx context.Context, arg UpsertEntityVtxoPara
return err return err
} }
const upsertPayment = `-- name: UpsertPayment :exec
INSERT INTO payment (id, round_id) VALUES (?, ?)
ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id
`
type UpsertPaymentParams struct {
ID string
RoundID string
}
func (q *Queries) UpsertPayment(ctx context.Context, arg UpsertPaymentParams) error {
_, err := q.db.ExecContext(ctx, upsertPayment, arg.ID, arg.RoundID)
return err
}
const upsertReceiver = `-- name: UpsertReceiver :exec const upsertReceiver = `-- name: UpsertReceiver :exec
INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?) INSERT INTO receiver (request_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET ON CONFLICT(request_id, pubkey, onchain_address) DO UPDATE SET
amount = EXCLUDED.amount, amount = EXCLUDED.amount,
pubkey = EXCLUDED.pubkey, pubkey = EXCLUDED.pubkey,
onchain_address = EXCLUDED.onchain_address onchain_address = EXCLUDED.onchain_address
` `
type UpsertReceiverParams struct { type UpsertReceiverParams struct {
PaymentID string RequestID string
Pubkey sql.NullString Pubkey sql.NullString
OnchainAddress sql.NullString OnchainAddress sql.NullString
Amount int64 Amount int64
@@ -948,7 +933,7 @@ type UpsertReceiverParams struct {
func (q *Queries) UpsertReceiver(ctx context.Context, arg UpsertReceiverParams) error { func (q *Queries) UpsertReceiver(ctx context.Context, arg UpsertReceiverParams) error {
_, err := q.db.ExecContext(ctx, upsertReceiver, _, err := q.db.ExecContext(ctx, upsertReceiver,
arg.PaymentID, arg.RequestID,
arg.Pubkey, arg.Pubkey,
arg.OnchainAddress, arg.OnchainAddress,
arg.Amount, arg.Amount,
@@ -1057,12 +1042,27 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
return err return err
} }
const upsertTxRequest = `-- name: UpsertTxRequest :exec
INSERT INTO tx_request (id, round_id) VALUES (?, ?)
ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id
`
type UpsertTxRequestParams struct {
ID string
RoundID string
}
func (q *Queries) UpsertTxRequest(ctx context.Context, arg UpsertTxRequestParams) error {
_, err := q.db.ExecContext(ctx, upsertTxRequest, arg.ID, arg.RoundID)
return err
}
const upsertVtxo = `-- name: UpsertVtxo :exec const upsertVtxo = `-- name: UpsertVtxo :exec
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, created_at, redeem_tx) INSERT INTO vtxo (txid, vout, pubkey, amount, round_tx, spent_by, spent, redeemed, swept, expire_at, created_at, redeem_tx)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
pubkey = EXCLUDED.pubkey, pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount, amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx, round_tx = EXCLUDED.round_tx,
spent_by = EXCLUDED.spent_by, spent_by = EXCLUDED.spent_by,
spent = EXCLUDED.spent, spent = EXCLUDED.spent,
redeemed = EXCLUDED.redeemed, redeemed = EXCLUDED.redeemed,
@@ -1077,7 +1077,7 @@ type UpsertVtxoParams struct {
Vout int64 Vout int64
Pubkey string Pubkey string
Amount int64 Amount int64
PoolTx string RoundTx string
SpentBy string SpentBy string
Spent bool Spent bool
Redeemed bool Redeemed bool
@@ -1093,7 +1093,7 @@ func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
arg.Vout, arg.Vout,
arg.Pubkey, arg.Pubkey,
arg.Amount, arg.Amount,
arg.PoolTx, arg.RoundTx,
arg.SpentBy, arg.SpentBy,
arg.Spent, arg.Spent,
arg.Redeemed, arg.Redeemed,

View File

@@ -39,70 +39,70 @@ ON CONFLICT(id) DO UPDATE SET
version = EXCLUDED.version, version = EXCLUDED.version,
swept = EXCLUDED.swept; swept = EXCLUDED.swept;
-- name: UpsertPayment :exec -- name: UpsertTxRequest :exec
INSERT INTO payment (id, round_id) VALUES (?, ?) INSERT INTO tx_request (id, round_id) VALUES (?, ?)
ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id; ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id;
-- name: UpsertReceiver :exec -- name: UpsertReceiver :exec
INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?) INSERT INTO receiver (request_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET ON CONFLICT(request_id, pubkey, onchain_address) DO UPDATE SET
amount = EXCLUDED.amount, amount = EXCLUDED.amount,
pubkey = EXCLUDED.pubkey, pubkey = EXCLUDED.pubkey,
onchain_address = EXCLUDED.onchain_address; onchain_address = EXCLUDED.onchain_address;
-- name: UpdateVtxoPaymentId :exec -- name: UpdateVtxoRequestId :exec
UPDATE vtxo SET payment_id = ? WHERE txid = ? AND vout = ?; UPDATE vtxo SET request_id = ? WHERE txid = ? AND vout = ?;
-- name: SelectRoundWithRoundId :many -- name: SelectRoundWithRoundId :many
SELECT sqlc.embed(round), SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw), sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw), sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw), sqlc.embed(request_receiver_vw),
sqlc.embed(payment_vtxo_vw) sqlc.embed(request_vtxo_vw)
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.id = ?; WHERE round.id = ?;
-- name: SelectRoundWithRoundTxId :many -- name: SelectRoundWithRoundTxId :many
SELECT sqlc.embed(round), SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw), sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw), sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw), sqlc.embed(request_receiver_vw),
sqlc.embed(payment_vtxo_vw) sqlc.embed(request_vtxo_vw)
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.txid = ?; WHERE round.txid = ?;
-- name: SelectSweepableRounds :many -- name: SelectSweepableRounds :many
SELECT sqlc.embed(round), SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw), sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw), sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw), sqlc.embed(request_receiver_vw),
sqlc.embed(payment_vtxo_vw) sqlc.embed(request_vtxo_vw)
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.swept = false AND round.ended = true AND round.failed = false; WHERE round.swept = false AND round.ended = true AND round.failed = false;
-- name: SelectSweptRounds :many -- name: SelectSweptRounds :many
SELECT sqlc.embed(round), SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw), sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw), sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw), sqlc.embed(request_receiver_vw),
sqlc.embed(payment_vtxo_vw) sqlc.embed(request_vtxo_vw)
FROM round FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_request_vw ON round.id=round_request_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
LEFT OUTER JOIN payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.swept = true AND round.failed = false AND round.ended = true AND round.connector_address <> ''; WHERE round.swept = true AND round.failed = false AND round.ended = true AND round.connector_address <> '';
-- name: SelectRoundIdsInRange :many -- name: SelectRoundIdsInRange :many
@@ -112,11 +112,11 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
SELECT id FROM round; SELECT id FROM round;
-- name: UpsertVtxo :exec -- name: UpsertVtxo :exec
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, created_at, redeem_tx) INSERT INTO vtxo (txid, vout, pubkey, amount, round_tx, spent_by, spent, redeemed, swept, expire_at, created_at, redeem_tx)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
pubkey = EXCLUDED.pubkey, pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount, amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx, round_tx = EXCLUDED.round_tx,
spent_by = EXCLUDED.spent_by, spent_by = EXCLUDED.spent_by,
spent = EXCLUDED.spent, spent = EXCLUDED.spent,
redeemed = EXCLUDED.redeemed, redeemed = EXCLUDED.redeemed,
@@ -141,9 +141,9 @@ WHERE redeemed = false AND pubkey = ?;
SELECT sqlc.embed(vtxo) FROM vtxo SELECT sqlc.embed(vtxo) FROM vtxo
WHERE txid = ? AND vout = ?; WHERE txid = ? AND vout = ?;
-- name: SelectVtxosByPoolTxid :many -- name: SelectVtxosByRoundTxid :many
SELECT sqlc.embed(vtxo) FROM vtxo SELECT sqlc.embed(vtxo) FROM vtxo
WHERE pool_tx = ?; WHERE round_tx = ?;
-- name: MarkVtxoAsRedeemed :exec -- name: MarkVtxoAsRedeemed :exec
UPDATE vtxo SET redeemed = true WHERE txid = ? AND vout = ?; UPDATE vtxo SET redeemed = true WHERE txid = ? AND vout = ?;

View File

@@ -42,9 +42,9 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
ctx, queries.UpsertVtxoParams{ ctx, queries.UpsertVtxoParams{
Txid: vtxo.Txid, Txid: vtxo.Txid,
Vout: int64(vtxo.VOut), Vout: int64(vtxo.VOut),
Pubkey: vtxo.Pubkey, Pubkey: vtxo.PubKey,
Amount: int64(vtxo.Amount), Amount: int64(vtxo.Amount),
PoolTx: vtxo.RoundTxid, RoundTx: vtxo.RoundTxid,
SpentBy: vtxo.SpentBy, SpentBy: vtxo.SpentBy,
Spent: vtxo.Spent, Spent: vtxo.Spent,
Redeemed: vtxo.Redeemed, Redeemed: vtxo.Redeemed,
@@ -150,7 +150,7 @@ func (v *vxtoRepository) GetVtxos(ctx context.Context, outpoints []domain.VtxoKe
} }
func (v *vxtoRepository) GetVtxosForRound(ctx context.Context, txid string) ([]domain.Vtxo, error) { func (v *vxtoRepository) GetVtxosForRound(ctx context.Context, txid string) ([]domain.Vtxo, error) {
res, err := v.querier.SelectVtxosByPoolTxid(ctx, txid) res, err := v.querier.SelectVtxosByRoundTxid(ctx, txid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -251,8 +251,8 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
VOut: uint32(row.Vout), VOut: uint32(row.Vout),
}, },
Amount: uint64(row.Amount), Amount: uint64(row.Amount),
Pubkey: row.Pubkey, PubKey: row.Pubkey,
RoundTxid: row.PoolTx, RoundTxid: row.RoundTx,
SpentBy: row.SpentBy, SpentBy: row.SpentBy,
Spent: row.Spent, Spent: row.Spent,
Redeemed: row.Redeemed, Redeemed: row.Redeemed,

View File

@@ -315,41 +315,41 @@ func (b *txBuilder) VerifyForfeitTxs(vtxos []domain.Vtxo, connectors []string, f
} }
func (b *txBuilder) BuildRoundTx( func (b *txBuilder) BuildRoundTx(
aspPubkey *secp256k1.PublicKey, serverPubkey *secp256k1.PublicKey,
payments []domain.Payment, requests []domain.TxRequest,
boardingInputs []ports.BoardingInput, boardingInputs []ports.BoardingInput,
sweptRounds []domain.Round, sweptRounds []domain.Round,
_ ...*secp256k1.PublicKey, // cosigners are not used in the covenant _ ...*secp256k1.PublicKey, // cosigners are not used in the covenant
) (roundTx string, congestionTree tree.CongestionTree, connectorAddress string, connectors []string, err error) { ) (roundTx string, vtxoTree tree.VtxoTree, connectorAddress string, connectors []string, err error) {
// The creation of the tree and the pool tx are tightly coupled: // The creation of the tree and the round tx are tightly coupled:
// - building the tree requires knowing the shared outpoint (txid:vout) // - building the tree requires knowing the shared outpoint (txid:vout)
// - building the pool tx requires knowing the shared output script and amount // - building the round tx requires knowing the shared output script and amount
// The idea here is to first create all the data for the outputs of the txs // The idea here is to first create all the data for the outputs of the txs
// of the congestion tree to calculate the shared output script and amount. // of the vtxo tree to calculate the shared output script and amount.
// With these data the pool tx can be created, and once the shared utxo // With these data the round tx can be created, and once the shared utxo
// outpoint is obtained, the congestion tree can be finally created. // outpoint is obtained, the vtxo tree can be finally created.
// The factory function `treeFactoryFn` returned below holds all outputs data // The factory function `treeFactoryFn` returned below holds all outputs data
// generated in the process and takes the shared utxo outpoint as argument. // generated in the process and takes the shared utxo outpoint as argument.
// This is safe as the memory allocated for `craftCongestionTree` is freed // This is safe as the memory allocated for `BuildVtxoTree` is flushed
// only after `BuildPoolTx` returns. // only after `BuildRoundTx` returns.
var sharedOutputScript []byte var sharedOutputScript []byte
var sharedOutputAmount uint64 var sharedOutputAmount uint64
var treeFactoryFn tree.TreeFactory var treeFactoryFn tree.TreeFactory
if !isOnchainOnly(payments) { if !isOnchainOnly(requests) {
feeSatsPerNode, err := b.wallet.MinRelayFee(context.Background(), uint64(common.CovenantTreeTxSize)) feeSatsPerNode, err := b.wallet.MinRelayFee(context.Background(), uint64(common.CovenantTreeTxSize))
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
} }
vtxosLeaves, err := getOutputVtxosLeaves(payments) vtxosLeaves, err := getOutputVtxosLeaves(requests)
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
} }
treeFactoryFn, sharedOutputScript, sharedOutputAmount, err = tree.CraftCongestionTree( treeFactoryFn, sharedOutputScript, sharedOutputAmount, err = tree.BuildVtxoTree(
b.onchainNetwork().AssetID, aspPubkey, vtxosLeaves, feeSatsPerNode, b.roundLifetime, b.onchainNetwork().AssetID, serverPubkey, vtxosLeaves, feeSatsPerNode, b.roundLifetime,
) )
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
@@ -361,8 +361,8 @@ func (b *txBuilder) BuildRoundTx(
return return
} }
ptx, err := b.createPoolTx( ptx, err := b.createRoundTx(
sharedOutputAmount, sharedOutputScript, payments, boardingInputs, aspPubkey, connectorAddress, sweptRounds, sharedOutputAmount, sharedOutputScript, requests, boardingInputs, serverPubkey, connectorAddress, sweptRounds,
) )
if err != nil { if err != nil {
return return
@@ -374,7 +374,7 @@ func (b *txBuilder) BuildRoundTx(
} }
if treeFactoryFn != nil { if treeFactoryFn != nil {
congestionTree, err = treeFactoryFn(psetv2.InputArgs{ vtxoTree, err = treeFactoryFn(psetv2.InputArgs{
Txid: unsignedTx.TxHash().String(), Txid: unsignedTx.TxHash().String(),
TxIndex: 0, TxIndex: 0,
}) })
@@ -388,7 +388,7 @@ func (b *txBuilder) BuildRoundTx(
return return
} }
if countSpentVtxos(payments) <= 0 { if countSpentVtxos(requests) <= 0 {
return return
} }
@@ -402,7 +402,7 @@ func (b *txBuilder) BuildRoundTx(
return "", nil, "", nil, err return "", nil, "", nil, err
} }
connectorsPsets, err := b.createConnectors(roundTx, payments, connectorAddress, connectorAmount, connectorFeeAmount) connectorsPsets, err := b.createConnectors(roundTx, requests, connectorAddress, connectorAmount, connectorFeeAmount)
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
} }
@@ -477,7 +477,7 @@ func (b *txBuilder) verifyTapscriptPartialSigs(pset *psetv2.Pset) (bool, error)
utx, _ := pset.UnsignedTx() utx, _ := pset.UnsignedTx()
txid := utx.TxHash().String() txid := utx.TxHash().String()
aspPublicKey, err := b.wallet.GetPubkey(context.Background()) serverPubkey, err := b.wallet.GetPubkey(context.Background())
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -511,8 +511,8 @@ func (b *txBuilder) verifyTapscriptPartialSigs(pset *psetv2.Pset) (bool, error)
} }
} }
// we don't need to check if ASP signed // we don't need to check if server signed
keys[hex.EncodeToString(schnorr.SerializePubKey(aspPublicKey))] = true keys[hex.EncodeToString(schnorr.SerializePubKey(serverPubkey))] = true
rootHash := tapLeaf.ControlBlock.RootHash(tapLeaf.Script) rootHash := tapLeaf.ControlBlock.RootHash(tapLeaf.Script)
tapKeyFromControlBlock := taproot.ComputeTaprootOutputKey(tree.UnspendableKey(), rootHash[:]) tapKeyFromControlBlock := taproot.ComputeTaprootOutputKey(tree.UnspendableKey(), rootHash[:])
@@ -629,15 +629,15 @@ func (b *txBuilder) FinalizeAndExtract(tx string) (string, error) {
} }
func (b *txBuilder) FindLeaves( func (b *txBuilder) FindLeaves(
congestionTree tree.CongestionTree, vtxoTree tree.VtxoTree,
fromtxid string, fromtxid string,
fromvout uint32, fromvout uint32,
) ([]tree.Node, error) { ) ([]tree.Node, error) {
allLeaves := congestionTree.Leaves() allLeaves := vtxoTree.Leaves()
foundLeaves := make([]tree.Node, 0) foundLeaves := make([]tree.Node, 0)
for _, leaf := range allLeaves { for _, leaf := range allLeaves {
branch, err := congestionTree.Branch(leaf.Txid) branch, err := vtxoTree.Branch(leaf.Txid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -669,16 +669,16 @@ func (b *txBuilder) FindLeaves(
return foundLeaves, nil return foundLeaves, nil
} }
func (b *txBuilder) createPoolTx( func (b *txBuilder) createRoundTx(
sharedOutputAmount uint64, sharedOutputAmount uint64,
sharedOutputScript []byte, sharedOutputScript []byte,
payments []domain.Payment, requests []domain.TxRequest,
boardingInputs []ports.BoardingInput, boardingInputs []ports.BoardingInput,
aspPubKey *secp256k1.PublicKey, serverPubkey *secp256k1.PublicKey,
connectorAddress string, connectorAddress string,
sweptRounds []domain.Round, sweptRounds []domain.Round,
) (*psetv2.Pset, error) { ) (*psetv2.Pset, error) {
aspScript, err := p2wpkhScript(aspPubKey, b.onchainNetwork()) serverScript, err := p2wpkhScript(serverPubkey, b.onchainNetwork())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -698,7 +698,7 @@ func (b *txBuilder) createPoolTx(
return nil, err return nil, err
} }
nbOfInputs := countSpentVtxos(payments) nbOfInputs := countSpentVtxos(requests)
connectorsAmount := (dustAmount + connectorMinRelayFee) * nbOfInputs connectorsAmount := (dustAmount + connectorMinRelayFee) * nbOfInputs
if nbOfInputs > 1 { if nbOfInputs > 1 {
connectorsAmount -= connectorMinRelayFee connectorsAmount -= connectorMinRelayFee
@@ -725,7 +725,7 @@ func (b *txBuilder) createPoolTx(
}) })
} }
onchainOutputs, err := getOnchainOutputs(payments, b.onchainNetwork()) onchainOutputs, err := getOnchainOutputs(requests, b.onchainNetwork())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -760,7 +760,7 @@ func (b *txBuilder) createPoolTx(
outputs = append(outputs, psetv2.OutputArgs{ outputs = append(outputs, psetv2.OutputArgs{
Asset: b.onchainNetwork().AssetID, Asset: b.onchainNetwork().AssetID,
Amount: change, Amount: change,
Script: aspScript, Script: serverScript,
}) })
} }
} }
@@ -878,7 +878,7 @@ func (b *txBuilder) createPoolTx(
{ {
Asset: b.onchainNetwork().AssetID, Asset: b.onchainNetwork().AssetID,
Amount: change, Amount: change,
Script: aspScript, Script: serverScript,
}, },
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -904,7 +904,7 @@ func (b *txBuilder) createPoolTx(
{ {
Asset: b.onchainNetwork().AssetID, Asset: b.onchainNetwork().AssetID,
Amount: change, Amount: change,
Script: aspScript, Script: serverScript,
}, },
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -1009,24 +1009,24 @@ func (b *txBuilder) VerifyAndCombinePartialTx(dest string, src string) (string,
} }
func (b *txBuilder) createConnectors( func (b *txBuilder) createConnectors(
roundTx string, payments []domain.Payment, roundTx string, requests []domain.TxRequest,
connectorAddress string, connectorAddress string,
connectorAmount, feeAmount uint64, connectorAmount, feeAmount uint64,
) ([]*psetv2.Pset, error) { ) ([]*psetv2.Pset, error) {
txid, _ := getTxid(roundTx) txid, _ := getTxid(roundTx)
aspScript, err := address.ToOutputScript(connectorAddress) serverScript, err := address.ToOutputScript(connectorAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
connectorOutput := psetv2.OutputArgs{ connectorOutput := psetv2.OutputArgs{
Asset: b.onchainNetwork().AssetID, Asset: b.onchainNetwork().AssetID,
Script: aspScript, Script: serverScript,
Amount: connectorAmount, Amount: connectorAmount,
} }
numberOfConnectors := countSpentVtxos(payments) numberOfConnectors := countSpentVtxos(requests)
previousInput := psetv2.InputArgs{ previousInput := psetv2.InputArgs{
Txid: txid, Txid: txid,
@@ -1035,7 +1035,7 @@ func (b *txBuilder) createConnectors(
if numberOfConnectors == 1 { if numberOfConnectors == 1 {
outputs := []psetv2.OutputArgs{connectorOutput} outputs := []psetv2.OutputArgs{connectorOutput}
connectorTx, err := craftConnectorTx(previousInput, aspScript, outputs, feeAmount) connectorTx, err := craftConnectorTx(previousInput, serverScript, outputs, feeAmount)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1056,11 +1056,11 @@ func (b *txBuilder) createConnectors(
if totalConnectorAmount > 0 { if totalConnectorAmount > 0 {
outputs = append(outputs, psetv2.OutputArgs{ outputs = append(outputs, psetv2.OutputArgs{
Asset: b.onchainNetwork().AssetID, Asset: b.onchainNetwork().AssetID,
Script: aspScript, Script: serverScript,
Amount: totalConnectorAmount, Amount: totalConnectorAmount,
}) })
} }
connectorTx, err := craftConnectorTx(previousInput, aspScript, outputs, feeAmount) connectorTx, err := craftConnectorTx(previousInput, serverScript, outputs, feeAmount)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -53,30 +53,30 @@ func TestMain(m *testing.M) {
os.Exit(m.Run()) os.Exit(m.Run())
} }
func TestBuildPoolTx(t *testing.T) { func TestBuildRoundTx(t *testing.T) {
builder := txbuilder.NewTxBuilder( builder := txbuilder.NewTxBuilder(
wallet, common.Liquid, roundLifetime, boardingExitDelay, wallet, common.Liquid, roundLifetime, boardingExitDelay,
) )
fixtures, err := parsePoolTxFixtures() fixtures, err := parseRoundTxFixtures()
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, fixtures) require.NotEmpty(t, fixtures)
if len(fixtures.Valid) > 0 { if len(fixtures.Valid) > 0 {
t.Run("valid", func(t *testing.T) { t.Run("valid", func(t *testing.T) {
for _, f := range fixtures.Valid { for _, f := range fixtures.Valid {
poolTx, congestionTree, connAddr, _, err := builder.BuildRoundTx( roundTx, vtxoTree, connAddr, _, err := builder.BuildRoundTx(
pubkey, f.Payments, []ports.BoardingInput{}, []domain.Round{}, pubkey, f.Requests, []ports.BoardingInput{}, []domain.Round{},
) )
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, poolTx) require.NotEmpty(t, roundTx)
require.NotEmpty(t, congestionTree) require.NotEmpty(t, vtxoTree)
require.Equal(t, connectorAddress, connAddr) require.Equal(t, connectorAddress, connAddr)
require.Equal(t, f.ExpectedNumOfNodes, congestionTree.NumberOfNodes()) require.Equal(t, f.ExpectedNumOfNodes, vtxoTree.NumberOfNodes())
require.Len(t, congestionTree.Leaves(), f.ExpectedNumOfLeaves) require.Len(t, vtxoTree.Leaves(), f.ExpectedNumOfLeaves)
err = tree.ValidateCongestionTree( err = tree.ValidateVtxoTree(
congestionTree, poolTx, pubkey, roundLifetime, vtxoTree, roundTx, pubkey, roundLifetime,
) )
require.NoError(t, err) require.NoError(t, err)
} }
@@ -86,13 +86,13 @@ func TestBuildPoolTx(t *testing.T) {
if len(fixtures.Invalid) > 0 { if len(fixtures.Invalid) > 0 {
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
for _, f := range fixtures.Invalid { for _, f := range fixtures.Invalid {
poolTx, congestionTree, connAddr, _, err := builder.BuildRoundTx( roundTx, vtxoTree, connAddr, _, err := builder.BuildRoundTx(
pubkey, f.Payments, []ports.BoardingInput{}, []domain.Round{}, pubkey, f.Requests, []ports.BoardingInput{}, []domain.Round{},
) )
require.EqualError(t, err, f.ExpectedErr) require.EqualError(t, err, f.ExpectedErr)
require.Empty(t, poolTx) require.Empty(t, roundTx)
require.Empty(t, connAddr) require.Empty(t, connAddr)
require.Empty(t, congestionTree) require.Empty(t, vtxoTree)
} }
}) })
} }
@@ -117,19 +117,19 @@ func randomHex(len int) string {
return hex.EncodeToString(buf) return hex.EncodeToString(buf)
} }
type poolTxFixtures struct { type roundTxFixtures struct {
Valid []struct { Valid []struct {
Payments []domain.Payment Requests []domain.TxRequest
ExpectedNumOfNodes int ExpectedNumOfNodes int
ExpectedNumOfLeaves int ExpectedNumOfLeaves int
} }
Invalid []struct { Invalid []struct {
Payments []domain.Payment Requests []domain.TxRequest
ExpectedErr string ExpectedErr string
} }
} }
func parsePoolTxFixtures() (*poolTxFixtures, error) { func parseRoundTxFixtures() (*roundTxFixtures, error) {
file, err := os.ReadFile("testdata/fixtures.json") file, err := os.ReadFile("testdata/fixtures.json")
if err != nil { if err != nil {
return nil, err return nil, err
@@ -139,9 +139,9 @@ func parsePoolTxFixtures() (*poolTxFixtures, error) {
return nil, err return nil, err
} }
vv := v["buildPoolTx"].(map[string]interface{}) vv := v["buildRoundTx"].(map[string]interface{})
file, _ = json.Marshal(vv) file, _ = json.Marshal(vv)
var fixtures poolTxFixtures var fixtures roundTxFixtures
if err := json.Unmarshal(file, &fixtures); err != nil { if err := json.Unmarshal(file, &fixtures); err != nil {
return nil, err return nil, err
} }

View File

@@ -1,8 +1,8 @@
{ {
"buildPoolTx": { "buildRoundTx": {
"valid": [ "valid": [
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -26,7 +26,7 @@
"expectedNumOfLeaves": 1 "expectedNumOfLeaves": 1
}, },
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -54,7 +54,7 @@
"expectedNumOfLeaves": 2 "expectedNumOfLeaves": 2
}, },
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -126,7 +126,7 @@
"expectedNumOfLeaves": 6 "expectedNumOfLeaves": 6
}, },
{ {
"payments": [ "requests": [
{ {
"id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f", "id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f",
"inputs": [ "inputs": [
@@ -199,7 +199,7 @@
"buildForfeitTxs": { "buildForfeitTxs": {
"valid": [ "valid": [
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -230,8 +230,8 @@
] ]
} }
], ],
"poolTx": "cHNldP8BAgQCAAAAAQQBAQEFAQMBBgEDAfsEAgAAAAABDiDk7dXxh4KQzgLO8i1ABtaLCe4aPL12GVhN1E9zM1ePLwEPBAAAAAABEAT/////AAEDCOgDAAAAAAAAAQQWABSNnpy01UJqd99eTg2M1IpdKId11gf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaB/wEcHNldAgEAAAAAAABAwh4BQAAAAAAAAEEFgAUjZ6ctNVCanffXk4NjNSKXSiHddYH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAAAQMI9AEAAAAAAAABBAAH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAA", "roundTx": "cHNldP8BAgQCAAAAAQQBAQEFAQMBBgEDAfsEAgAAAAABDiDk7dXxh4KQzgLO8i1ABtaLCe4aPL12GVhN1E9zM1ePLwEPBAAAAAABEAT/////AAEDCOgDAAAAAAAAAQQWABSNnpy01UJqd99eTg2M1IpdKId11gf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaB/wEcHNldAgEAAAAAAABAwh4BQAAAAAAAAEEFgAUjZ6ctNVCanffXk4NjNSKXSiHddYH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAAAQMI9AEAAAAAAAABBAAH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAA",
"poolTxid": "7981fce656f266472cc742444527cb32a8bed8c90fed6d47adbfc4c8780d4d9a", "roundTxid": "7981fce656f266472cc742444527cb32a8bed8c90fed6d47adbfc4c8780d4d9a",
"expectedNumOfForfeitTxs": 4, "expectedNumOfForfeitTxs": 4,
"expectedNumOfConnectors": 1 "expectedNumOfConnectors": 1
} }

View File

@@ -17,8 +17,8 @@ import (
"github.com/vulpemventures/go-elements/transaction" "github.com/vulpemventures/go-elements/transaction"
) )
func p2wpkhScript(publicKey *secp256k1.PublicKey, net *network.Network) ([]byte, error) { func p2wpkhScript(pubkey *secp256k1.PublicKey, net *network.Network) ([]byte, error) {
payment := payment.FromPublicKey(publicKey, net, nil) payment := payment.FromPublicKey(pubkey, net, nil)
addr, err := payment.WitnessPubKeyHash() addr, err := payment.WitnessPubKeyHash()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -46,11 +46,11 @@ func getPsetId(pset *psetv2.Pset) (string, error) {
} }
func getOnchainOutputs( func getOnchainOutputs(
payments []domain.Payment, net *network.Network, requests []domain.TxRequest, net *network.Network,
) ([]psetv2.OutputArgs, error) { ) ([]psetv2.OutputArgs, error) {
outputs := make([]psetv2.OutputArgs, 0) outputs := make([]psetv2.OutputArgs, 0)
for _, payment := range payments { for _, request := range requests {
for _, receiver := range payment.Receivers { for _, receiver := range request.Receivers {
if receiver.IsOnchain() { if receiver.IsOnchain() {
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress) receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
if err != nil { if err != nil {
@@ -69,14 +69,14 @@ func getOnchainOutputs(
} }
func getOutputVtxosLeaves( func getOutputVtxosLeaves(
payments []domain.Payment, requests []domain.TxRequest,
) ([]tree.VtxoLeaf, error) { ) ([]tree.VtxoLeaf, error) {
receivers := make([]tree.VtxoLeaf, 0) receivers := make([]tree.VtxoLeaf, 0)
for _, payment := range payments { for _, request := range requests {
for _, receiver := range payment.Receivers { for _, receiver := range request.Receivers {
if !receiver.IsOnchain() { if !receiver.IsOnchain() {
receivers = append(receivers, tree.VtxoLeaf{ receivers = append(receivers, tree.VtxoLeaf{
Pubkey: receiver.Pubkey, PubKey: receiver.PubKey,
Amount: receiver.Amount, Amount: receiver.Amount,
}) })
} }
@@ -104,10 +104,10 @@ func toWitnessUtxo(in ports.TxInput) (*transaction.TxOutput, error) {
return transaction.NewTxOutput(assetBytes, valueBytes, scriptBytes), nil return transaction.NewTxOutput(assetBytes, valueBytes, scriptBytes), nil
} }
func countSpentVtxos(payments []domain.Payment) uint64 { func countSpentVtxos(requests []domain.TxRequest) uint64 {
var sum uint64 var sum uint64
for _, payment := range payments { for _, request := range requests {
sum += uint64(len(payment.Inputs)) sum += uint64(len(request.Inputs))
} }
return sum return sum
} }
@@ -144,9 +144,9 @@ func addInputs(
return nil return nil
} }
func isOnchainOnly(payments []domain.Payment) bool { func isOnchainOnly(requests []domain.TxRequest) bool {
for _, p := range payments { for _, request := range requests {
for _, r := range p.Receivers { for _, r := range request.Receivers {
if !r.IsOnchain() { if !r.IsOnchain() {
return false return false
} }

View File

@@ -58,7 +58,7 @@ func (b *txBuilder) VerifyTapscriptPartialSigs(tx string) (bool, error) {
func (b *txBuilder) verifyTapscriptPartialSigs(ptx *psbt.Packet) (bool, error) { func (b *txBuilder) verifyTapscriptPartialSigs(ptx *psbt.Packet) (bool, error) {
txid := ptx.UnsignedTx.TxID() txid := ptx.UnsignedTx.TxID()
aspPublicKey, err := b.wallet.GetPubkey(context.Background()) serverPubkey, err := b.wallet.GetPubkey(context.Background())
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -93,8 +93,8 @@ func (b *txBuilder) verifyTapscriptPartialSigs(ptx *psbt.Packet) (bool, error) {
} }
} }
// we don't need to check if ASP signed // we don't need to check if server signed
keys[hex.EncodeToString(schnorr.SerializePubKey(aspPublicKey))] = true keys[hex.EncodeToString(schnorr.SerializePubKey(serverPubkey))] = true
if len(tapLeaf.ControlBlock) == 0 { if len(tapLeaf.ControlBlock) == 0 {
return false, fmt.Errorf("missing control block for input %d", index) return false, fmt.Errorf("missing control block for input %d", index)
@@ -471,12 +471,12 @@ func (b *txBuilder) VerifyForfeitTxs(vtxos []domain.Vtxo, connectors []string, f
} }
func (b *txBuilder) BuildRoundTx( func (b *txBuilder) BuildRoundTx(
aspPubkey *secp256k1.PublicKey, serverPubkey *secp256k1.PublicKey,
payments []domain.Payment, requests []domain.TxRequest,
boardingInputs []ports.BoardingInput, boardingInputs []ports.BoardingInput,
sweptRounds []domain.Round, sweptRounds []domain.Round,
cosigners ...*secp256k1.PublicKey, cosigners ...*secp256k1.PublicKey,
) (roundTx string, congestionTree tree.CongestionTree, connectorAddress string, connectors []string, err error) { ) (roundTx string, vtxoTree tree.VtxoTree, connectorAddress string, connectors []string, err error) {
var sharedOutputScript []byte var sharedOutputScript []byte
var sharedOutputAmount int64 var sharedOutputAmount int64
@@ -484,7 +484,7 @@ func (b *txBuilder) BuildRoundTx(
return "", nil, "", nil, fmt.Errorf("missing cosigners") return "", nil, "", nil, fmt.Errorf("missing cosigners")
} }
receivers, err := getOutputVtxosLeaves(payments) receivers, err := getOutputVtxosLeaves(requests)
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
} }
@@ -494,9 +494,9 @@ func (b *txBuilder) BuildRoundTx(
return return
} }
if !isOnchainOnly(payments) { if !isOnchainOnly(requests) {
sharedOutputScript, sharedOutputAmount, err = bitcointree.CraftSharedOutput( sharedOutputScript, sharedOutputAmount, err = bitcointree.CraftSharedOutput(
cosigners, aspPubkey, receivers, feeAmount, b.roundLifetime, cosigners, serverPubkey, receivers, feeAmount, b.roundLifetime,
) )
if err != nil { if err != nil {
return return
@@ -509,7 +509,7 @@ func (b *txBuilder) BuildRoundTx(
} }
ptx, err := b.createRoundTx( ptx, err := b.createRoundTx(
sharedOutputAmount, sharedOutputScript, payments, boardingInputs, connectorAddress, sweptRounds, sharedOutputAmount, sharedOutputScript, requests, boardingInputs, connectorAddress, sweptRounds,
) )
if err != nil { if err != nil {
return return
@@ -520,21 +520,21 @@ func (b *txBuilder) BuildRoundTx(
return return
} }
if !isOnchainOnly(payments) { if !isOnchainOnly(requests) {
initialOutpoint := &wire.OutPoint{ initialOutpoint := &wire.OutPoint{
Hash: ptx.UnsignedTx.TxHash(), Hash: ptx.UnsignedTx.TxHash(),
Index: 0, Index: 0,
} }
congestionTree, err = bitcointree.CraftCongestionTree( vtxoTree, err = bitcointree.BuildVtxoTree(
initialOutpoint, cosigners, aspPubkey, receivers, feeAmount, b.roundLifetime, initialOutpoint, cosigners, serverPubkey, receivers, feeAmount, b.roundLifetime,
) )
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
} }
} }
if countSpentVtxos(payments) <= 0 { if countSpentVtxos(requests) <= 0 {
return return
} }
@@ -553,7 +553,7 @@ func (b *txBuilder) BuildRoundTx(
return "", nil, "", nil, err return "", nil, "", nil, err
} }
connectorsPsbts, err := b.createConnectors(roundTx, payments, connectorPkScript, minRelayFeeConnectorTx) connectorsPsbts, err := b.createConnectors(roundTx, requests, connectorPkScript, minRelayFeeConnectorTx)
if err != nil { if err != nil {
return "", nil, "", nil, err return "", nil, "", nil, err
} }
@@ -566,7 +566,7 @@ func (b *txBuilder) BuildRoundTx(
connectors = append(connectors, b64) connectors = append(connectors, b64)
} }
return roundTx, congestionTree, connectorAddress, connectors, nil return roundTx, vtxoTree, connectorAddress, connectors, nil
} }
func (b *txBuilder) GetSweepInput(node tree.Node) (lifetime int64, sweepInput ports.SweepInput, err error) { func (b *txBuilder) GetSweepInput(node tree.Node) (lifetime int64, sweepInput ports.SweepInput, err error) {
@@ -611,12 +611,12 @@ func (b *txBuilder) GetSweepInput(node tree.Node) (lifetime int64, sweepInput po
return lifetime, sweepInput, nil return lifetime, sweepInput, nil
} }
func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) ([]tree.Node, error) { func (b *txBuilder) FindLeaves(vtxoTree tree.VtxoTree, fromtxid string, vout uint32) ([]tree.Node, error) {
allLeaves := congestionTree.Leaves() allLeaves := vtxoTree.Leaves()
foundLeaves := make([]tree.Node, 0) foundLeaves := make([]tree.Node, 0)
for _, leaf := range allLeaves { for _, leaf := range allLeaves {
branch, err := congestionTree.Branch(leaf.Txid) branch, err := vtxoTree.Branch(leaf.Txid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -647,7 +647,7 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri
func (b *txBuilder) createRoundTx( func (b *txBuilder) createRoundTx(
sharedOutputAmount int64, sharedOutputAmount int64,
sharedOutputScript []byte, sharedOutputScript []byte,
payments []domain.Payment, requests []domain.TxRequest,
boardingInputs []ports.BoardingInput, boardingInputs []ports.BoardingInput,
connectorAddress string, connectorAddress string,
sweptRounds []domain.Round, sweptRounds []domain.Round,
@@ -674,7 +674,7 @@ func (b *txBuilder) createRoundTx(
connectorAmount := dustLimit connectorAmount := dustLimit
nbOfInputs := countSpentVtxos(payments) nbOfInputs := countSpentVtxos(requests)
connectorsAmount := (connectorAmount + connectorMinRelayFee) * nbOfInputs connectorsAmount := (connectorAmount + connectorMinRelayFee) * nbOfInputs
if nbOfInputs > 1 { if nbOfInputs > 1 {
connectorsAmount -= connectorMinRelayFee connectorsAmount -= connectorMinRelayFee
@@ -699,7 +699,7 @@ func (b *txBuilder) createRoundTx(
}) })
} }
onchainOutputs, err := getOnchainOutputs(payments, b.onchainNetwork()) onchainOutputs, err := getOnchainOutputs(requests, b.onchainNetwork())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1021,9 +1021,9 @@ func (b *txBuilder) VerifyAndCombinePartialTx(dest string, src string) (string,
} }
func (b *txBuilder) createConnectors( func (b *txBuilder) createConnectors(
poolTx string, payments []domain.Payment, connectorScript []byte, feeAmount uint64, roundTx string, requests []domain.TxRequest, connectorScript []byte, feeAmount uint64,
) ([]*psbt.Packet, error) { ) ([]*psbt.Packet, error) {
partialTx, err := psbt.NewFromRawBytes(strings.NewReader(poolTx), true) partialTx, err := psbt.NewFromRawBytes(strings.NewReader(roundTx), true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1038,7 +1038,7 @@ func (b *txBuilder) createConnectors(
Value: int64(connectorAmount), Value: int64(connectorAmount),
} }
numberOfConnectors := countSpentVtxos(payments) numberOfConnectors := countSpentVtxos(requests)
previousInput := &wire.OutPoint{ previousInput := &wire.OutPoint{
Hash: partialTx.UnsignedTx.TxHash(), Hash: partialTx.UnsignedTx.TxHash(),

View File

@@ -55,12 +55,12 @@ func TestMain(m *testing.M) {
os.Exit(m.Run()) os.Exit(m.Run())
} }
func TestBuildPoolTx(t *testing.T) { func TestBuildRoundTx(t *testing.T) {
builder := txbuilder.NewTxBuilder( builder := txbuilder.NewTxBuilder(
wallet, common.Bitcoin, roundLifetime, boardingExitDelay, wallet, common.Bitcoin, roundLifetime, boardingExitDelay,
) )
fixtures, err := parsePoolTxFixtures() fixtures, err := parseRoundTxFixtures()
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, fixtures) require.NotEmpty(t, fixtures)
@@ -68,25 +68,25 @@ func TestBuildPoolTx(t *testing.T) {
t.Run("valid", func(t *testing.T) { t.Run("valid", func(t *testing.T) {
for _, f := range fixtures.Valid { for _, f := range fixtures.Valid {
cosigners := make([]*secp256k1.PublicKey, 0) cosigners := make([]*secp256k1.PublicKey, 0)
for range f.Payments { for range f.Requests {
randKey, err := secp256k1.GeneratePrivateKey() randKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
cosigners = append(cosigners, randKey.PubKey()) cosigners = append(cosigners, randKey.PubKey())
} }
poolTx, congestionTree, connAddr, _, err := builder.BuildRoundTx( roundTx, vtxoTree, connAddr, _, err := builder.BuildRoundTx(
pubkey, f.Payments, []ports.BoardingInput{}, []domain.Round{}, cosigners..., pubkey, f.Requests, []ports.BoardingInput{}, []domain.Round{}, cosigners...,
) )
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, poolTx) require.NotEmpty(t, roundTx)
require.NotEmpty(t, congestionTree) require.NotEmpty(t, vtxoTree)
require.Equal(t, connectorAddress, connAddr) require.Equal(t, connectorAddress, connAddr)
require.Equal(t, f.ExpectedNumOfNodes, congestionTree.NumberOfNodes()) require.Equal(t, f.ExpectedNumOfNodes, vtxoTree.NumberOfNodes())
require.Len(t, congestionTree.Leaves(), f.ExpectedNumOfLeaves) require.Len(t, vtxoTree.Leaves(), f.ExpectedNumOfLeaves)
err = bitcointree.ValidateCongestionTree( err = bitcointree.ValidateVtxoTree(
congestionTree, poolTx, pubkey, roundLifetime, vtxoTree, roundTx, pubkey, roundLifetime,
) )
require.NoError(t, err) require.NoError(t, err)
} }
@@ -96,13 +96,13 @@ func TestBuildPoolTx(t *testing.T) {
if len(fixtures.Invalid) > 0 { if len(fixtures.Invalid) > 0 {
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
for _, f := range fixtures.Invalid { for _, f := range fixtures.Invalid {
poolTx, congestionTree, connAddr, _, err := builder.BuildRoundTx( roundTx, vtxoTree, connAddr, _, err := builder.BuildRoundTx(
pubkey, f.Payments, []ports.BoardingInput{}, []domain.Round{}, pubkey, f.Requests, []ports.BoardingInput{}, []domain.Round{},
) )
require.EqualError(t, err, f.ExpectedErr) require.EqualError(t, err, f.ExpectedErr)
require.Empty(t, poolTx) require.Empty(t, roundTx)
require.Empty(t, connAddr) require.Empty(t, connAddr)
require.Empty(t, congestionTree) require.Empty(t, vtxoTree)
} }
}) })
} }
@@ -127,19 +127,19 @@ func randomHex(len int) string {
return hex.EncodeToString(buf) return hex.EncodeToString(buf)
} }
type poolTxFixtures struct { type roundTxFixtures struct {
Valid []struct { Valid []struct {
Payments []domain.Payment Requests []domain.TxRequest
ExpectedNumOfNodes int ExpectedNumOfNodes int
ExpectedNumOfLeaves int ExpectedNumOfLeaves int
} }
Invalid []struct { Invalid []struct {
Payments []domain.Payment Requests []domain.TxRequest
ExpectedErr string ExpectedErr string
} }
} }
func parsePoolTxFixtures() (*poolTxFixtures, error) { func parseRoundTxFixtures() (*roundTxFixtures, error) {
file, err := os.ReadFile("testdata/fixtures.json") file, err := os.ReadFile("testdata/fixtures.json")
if err != nil { if err != nil {
return nil, err return nil, err
@@ -149,9 +149,9 @@ func parsePoolTxFixtures() (*poolTxFixtures, error) {
return nil, err return nil, err
} }
vv := v["buildPoolTx"].(map[string]interface{}) vv := v["buildRoundTx"].(map[string]interface{})
file, _ = json.Marshal(vv) file, _ = json.Marshal(vv)
var fixtures poolTxFixtures var fixtures roundTxFixtures
if err := json.Unmarshal(file, &fixtures); err != nil { if err := json.Unmarshal(file, &fixtures); err != nil {
return nil, err return nil, err
} }

View File

@@ -1,8 +1,8 @@
{ {
"buildPoolTx": { "buildRoundTx": {
"valid": [ "valid": [
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -26,7 +26,7 @@
"expectedNumOfLeaves": 1 "expectedNumOfLeaves": 1
}, },
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -54,7 +54,7 @@
"expectedNumOfLeaves": 2 "expectedNumOfLeaves": 2
}, },
{ {
"payments": [ "requests": [
{ {
"id": "0", "id": "0",
"inputs": [ "inputs": [
@@ -126,7 +126,7 @@
"expectedNumOfLeaves": 6 "expectedNumOfLeaves": 6
}, },
{ {
"payments": [ "requests": [
{ {
"id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f", "id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f",
"inputs": [ "inputs": [
@@ -199,7 +199,7 @@
"buildForfeitTxs": { "buildForfeitTxs": {
"valid": [ "valid": [
{ {
"payments": [ "requests": [
{ {
"id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f", "id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f",
"inputs": [ "inputs": [
@@ -263,8 +263,8 @@
] ]
} }
], ],
"poolTx": "cHNidP8BALICAAAAAnonOnsJBkHUUaKf/7fdS0/sVyBCgDPusYzGSZZiXPbtAAAAAAD/////VLtr81ZII3QJnXgrIwgcnbsq3aa4L3qdHOAn2evlFtEAAAAAAP////8CohIAAAAAAAAiUSBZarBUuSIHnlkuIoel9MmvexqTGK8jCZaRjt8L+Pb3s+gDAAAAAAAAIlEgI95L4kHEn2fAA+vysD+RIR4eD3AIQwc+FyCInJ8HivYAAAAAAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAAA", "roundTx": "cHNidP8BALICAAAAAnonOnsJBkHUUaKf/7fdS0/sVyBCgDPusYzGSZZiXPbtAAAAAAD/////VLtr81ZII3QJnXgrIwgcnbsq3aa4L3qdHOAn2evlFtEAAAAAAP////8CohIAAAAAAAAiUSBZarBUuSIHnlkuIoel9MmvexqTGK8jCZaRjt8L+Pb3s+gDAAAAAAAAIlEgI95L4kHEn2fAA+vysD+RIR4eD3AIQwc+FyCInJ8HivYAAAAAAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAAA",
"poolTxid": "7c0c10756cdb9ab8e605f1c82e25989761308cf4c60e6a6f42b72d46144c4ce0", "roundTxid": "7c0c10756cdb9ab8e605f1c82e25989761308cf4c60e6a6f42b72d46144c4ce0",
"expectedNumOfForfeitTxs": 25, "expectedNumOfForfeitTxs": 25,
"expectedNumOfConnectors": 4 "expectedNumOfConnectors": 4
} }

Some files were not shown because too many files have changed in this diff Show More