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
- [`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
- [`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.
- [`server`](./server/): `arkd` Ark Service Provider (ASP) - the always-on daemon
- [`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 server - the always-on daemon
## Development

View File

@@ -163,7 +163,7 @@
]
}
},
"/v1/round/ping/{paymentId}": {
"/v1/round/ping/{requestId}": {
"get": {
"operationId": "ArkService_Ping",
"responses": {
@@ -182,7 +182,8 @@
},
"parameters": [
{
"name": "paymentId",
"name": "requestId",
"description": "The id used to register inputs and ouptuts.",
"in": "path",
"required": true,
"type": "string"
@@ -770,7 +771,7 @@
"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": {
"type": "object"
@@ -821,18 +822,16 @@
"v1RegisterInputsForNextRoundResponse": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Mocks wabisabi's blinded credentials."
"requestId": {
"type": "string"
}
}
},
"v1RegisterOutputsForNextRoundRequest": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Mocks wabisabi's blinded credentials."
"requestId": {
"type": "string"
},
"outputs": {
"type": "array",
@@ -840,7 +839,7 @@
"type": "object",
"$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": {
"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) {
option (google.api.http) = {
@@ -58,10 +58,12 @@ service ArkService {
};
rpc Ping(PingRequest) returns (PingResponse) {
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) {
option (google.api.http) = {
post: "/v1/redeem-tx"
@@ -134,7 +136,7 @@ message GetBoardingAddressResponse {
}
}
/* In-Round Payment API messages */
/* In-Round Transaction API messages */
message RegisterInputsForNextRoundRequest {
repeated Input inputs = 1;
@@ -142,14 +144,12 @@ message RegisterInputsForNextRoundRequest {
repeated string notes = 3;
}
message RegisterInputsForNextRoundResponse {
// Mocks wabisabi's blinded credentials.
string id = 1;
string request_id = 1;
}
message RegisterOutputsForNextRoundRequest {
// Mocks wabisabi's blinded credentials.
string id = 1;
// List of receivers for a registered payment.
string request_id = 1;
// List of receivers for to convert to leaves in the next VTXO tree.
repeated Output outputs = 2;
}
message RegisterOutputsForNextRoundResponse {}
@@ -171,7 +171,7 @@ message SubmitTreeSignaturesResponse {}
message SubmitSignedForfeitTxsRequest {
// Forfeit txs signed by the user.
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;
}
message SubmitSignedForfeitTxsResponse {}
@@ -188,7 +188,8 @@ message GetEventStreamResponse {
}
message PingRequest {
string payment_id = 1;
// The id used to register inputs and ouptuts.
string request_id = 1;
}
message PingResponse {}
@@ -343,7 +344,7 @@ message RedeemTransaction {
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 {
string control_block = 1;
string script = 2;

View File

@@ -439,8 +439,7 @@ type RegisterInputsForNextRoundResponse struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Mocks wabisabi's blinded credentials.
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
}
func (x *RegisterInputsForNextRoundResponse) Reset() {
@@ -475,9 +474,9 @@ func (*RegisterInputsForNextRoundResponse) Descriptor() ([]byte, []int) {
return file_ark_v1_service_proto_rawDescGZIP(), []int{5}
}
func (x *RegisterInputsForNextRoundResponse) GetId() string {
func (x *RegisterInputsForNextRoundResponse) GetRequestId() string {
if x != nil {
return x.Id
return x.RequestId
}
return ""
}
@@ -487,9 +486,8 @@ type RegisterOutputsForNextRoundRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Mocks wabisabi's blinded credentials.
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// List of receivers for a registered payment.
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
// List of receivers for to convert to leaves in the next VTXO tree.
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}
}
func (x *RegisterOutputsForNextRoundRequest) GetId() string {
func (x *RegisterOutputsForNextRoundRequest) GetRequestId() string {
if x != nil {
return x.Id
return x.RequestId
}
return ""
}
@@ -786,7 +784,7 @@ type SubmitSignedForfeitTxsRequest struct {
// Forfeit txs signed by the user.
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"`
}
@@ -1040,7 +1038,8 @@ type PingRequest struct {
sizeCache protoimpl.SizeCache
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() {
@@ -1075,9 +1074,9 @@ func (*PingRequest) Descriptor() ([]byte, []int) {
return file_ark_v1_service_proto_rawDescGZIP(), []int{16}
}
func (x *PingRequest) GetPaymentId() string {
func (x *PingRequest) GetRequestId() string {
if x != nil {
return x.PaymentId
return x.RequestId
}
return ""
}
@@ -2658,7 +2657,7 @@ func (x *RedeemTransaction) GetSpendableVtxos() []*Vtxo {
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 {
state protoimpl.MessageState
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,
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,
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,
0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x22, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70,
0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x23, 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, 0x6d, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65,
0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a,
0x08, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b,
0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79,
0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65,
0x73, 0x22, 0x1a, 0x0a, 0x18, 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, 0x79, 0x0a,
0x1b, 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, 0x12, 0x19, 0x0a, 0x08,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12,
0x27, 0x0a, 0x0f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 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, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 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, 0x8e, 0x01, 0x0a, 0x1d, 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, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x69,
0x67, 0x6e, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f,
0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e,
0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x6e, 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, 0x22, 0x20, 0x0a, 0x1e, 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, 0x17, 0x0a, 0x15, 0x47,
0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0xa7, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e,
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,
0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x6d, 0x0a, 0x22, 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,
0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12,
0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x25, 0x0a, 0x23, 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, 0x6d, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f,
0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1f,
0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22,
0x1a, 0x0a, 0x18, 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, 0x79, 0x0a, 0x1b, 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, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f,
0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f,
0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a,
0x0f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 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,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 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, 0x8e, 0x01, 0x0a, 0x1d, 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, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e,
0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66,
0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
0x00, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x6e, 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, 0x22, 0x20, 0x0a, 0x1e, 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, 0x17, 0x0a, 0x15, 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, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 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,
0x73, 0x74, 0x22, 0xa7, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 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, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 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, 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,
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, 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 (

View File

@@ -233,14 +233,14 @@ func request_ArkService_Ping_0(ctx context.Context, marshaler runtime.Marshaler,
_ = err
)
val, ok = pathParams["payment_id"]
val, ok = pathParams["request_id"]
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 {
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))
@@ -259,14 +259,14 @@ func local_request_ArkService_Ping_0(ctx context.Context, marshaler runtime.Mars
_ = err
)
val, ok = pathParams["payment_id"]
val, ok = pathParams["request_id"]
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 {
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)
@@ -722,7 +722,7 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
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 {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
@@ -1119,7 +1119,7 @@ func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux,
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
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 {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
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_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"}, ""))

View File

@@ -97,8 +97,8 @@ var (
Usage: "optional private key to encrypt",
}
urlFlag = &cli.StringFlag{
Name: "asp-url",
Usage: "the url of the ASP to connect to",
Name: "server-url",
Usage: "the url of the Ark server to connect to",
Required: true,
}
receiversFlag = &cli.StringFlag{
@@ -150,7 +150,7 @@ var (
var (
initCommand = cli.Command{
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 {
return initArkSdk(ctx)
},
@@ -180,7 +180,7 @@ var (
}
settleCmd = cli.Command{
Name: "settle",
Usage: "Settle onboarding funds or oor payments",
Usage: "Settle onboarding or pending funds",
Action: func(ctx *cli.Context) error {
return settle(ctx)
},
@@ -196,7 +196,7 @@ var (
}
sendCommand = cli.Command{
Name: "send",
Usage: "Send funds onchain, offchain, or asynchronously",
Usage: "Send funds offchain",
Action: func(ctx *cli.Context) error {
return send(ctx)
},
@@ -243,7 +243,7 @@ func initArkSdk(ctx *cli.Context) error {
ctx.Context, arksdk.InitArgs{
ClientType: clientType,
WalletType: arksdk.SingleKeyWallet,
AspUrl: ctx.String(urlFlag.Name),
ServerUrl: ctx.String(urlFlag.Name),
Seed: ctx.String(privateKeyFlag.Name),
Password: string(password),
ExplorerURL: ctx.String(explorerFlag.Name),
@@ -258,8 +258,8 @@ func config(ctx *cli.Context) error {
}
cfg := map[string]interface{}{
"asp_url": cfgData.AspUrl,
"asp_pubkey": hex.EncodeToString(cfgData.AspPubkey.SerializeCompressed()),
"server_url": cfgData.ServerUrl,
"server_pubkey": hex.EncodeToString(cfgData.ServerPubKey.SerializeCompressed()),
"wallet_type": cfgData.WalletType,
"client_tyep": cfgData.ClientType,
"network": cfgData.Network.Name,
@@ -368,7 +368,7 @@ func send(ctx *cli.Context) error {
if isBitcoin {
return sendCovenantLess(ctx, receivers)
}
return sendCovenant(ctx.Context, receivers)
return sendCovenant(ctx, receivers)
}
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 {
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)
redeemTx, err := arkSdkClient.SendAsync(
ctx.Context, computeExpiration, receivers,
redeemTx, err := arkSdkClient.SendOffChain(
ctx.Context, computeExpiration, offchainReceivers,
)
if err != nil {
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()})
}
func sendCovenant(ctx context.Context, receivers []arksdk.Receiver) error {
func sendCovenant(ctx *cli.Context, receivers []arksdk.Receiver) error {
var onchainReceivers, offchainReceivers []arksdk.Receiver
for _, receiver := range receivers {
@@ -552,22 +570,21 @@ func sendCovenant(ctx context.Context, receivers []arksdk.Receiver) error {
}
if len(onchainReceivers) > 0 {
txID, err := arkSdkClient.SendOnChain(ctx, onchainReceivers)
txID, err := arkSdkClient.SendOnChain(ctx.Context, onchainReceivers)
if err != nil {
return err
}
return printJSON(map[string]interface{}{"txid": txID})
}
if len(offchainReceivers) > 0 {
txID, err := arkSdkClient.SendOffChain(ctx, false, offchainReceivers)
computeExpiration := ctx.Bool(enableExpiryCoinselectFlag.Name)
txid, err := arkSdkClient.SendOffChain(
ctx.Context, computeExpiration, offchainReceivers,
)
if err != nil {
return err
}
return printJSON(map[string]interface{}{"txid": txID})
}
return nil
return printJSON(map[string]interface{}{"txid": txid})
}
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
func CraftSharedOutput(
cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
cosigners []*secp256k1.PublicKey, server *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64,
) ([]byte, int64, error) {
aggregatedKey, _, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime,
cosigners, server, roundLifetime,
)
if err != nil {
return nil, 0, err
@@ -34,21 +34,21 @@ func CraftSharedOutput(
amount := root.getAmount() + int64(feeSatsPerNode)
scriptPubKey, err := common.P2TRScript(aggregatedKey.FinalKey)
scriptPubkey, err := common.P2TRScript(aggregatedKey.FinalKey)
if err != nil {
return nil, 0, err
}
return scriptPubKey, amount, err
return scriptPubkey, amount, err
}
// CraftCongestionTree creates all the tree's transactions
func CraftCongestionTree(
initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
// BuildVtxoTree creates all the tree's transactions
func BuildVtxoTree(
initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, server *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64,
) (tree.CongestionTree, error) {
) (tree.VtxoTree, error) {
aggregatedKey, sweepTapLeaf, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime,
cosigners, server, roundLifetime,
)
if err != nil {
return nil, err
@@ -59,7 +59,7 @@ func CraftCongestionTree(
return nil, err
}
congestionTree := make(tree.CongestionTree, 0)
vtxoTree := make(tree.VtxoTree, 0)
ins := []*wire.OutPoint{initialInput}
nodes := []node{root}
@@ -95,12 +95,12 @@ func CraftCongestionTree(
}
}
congestionTree = append(congestionTree, treeLevel)
vtxoTree = append(vtxoTree, treeLevel)
nodes = append([]node{}, nextNodes...)
ins = append([]*wire.OutPoint{}, nextInputsArgs...)
}
return congestionTree, nil
return vtxoTree, nil
}
type node interface {
@@ -252,7 +252,7 @@ func createRootNode(
nodes := make([]node, 0, len(receivers))
for _, r := range receivers {
pubkeyBytes, err := hex.DecodeString(r.Pubkey)
pubkeyBytes, err := hex.DecodeString(r.PubKey)
if err != nil {
return nil, err
}
@@ -280,10 +280,10 @@ func createRootNode(
}
func createAggregatedKeyWithSweep(
cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, roundLifetime int64,
cosigners []*secp256k1.PublicKey, server *secp256k1.PublicKey, roundLifetime int64,
) (*musig2.AggregateKey, *psbt.TaprootTapLeafScript, error) {
sweepClosure := &tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{aspPubkey}},
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{server}},
Seconds: uint(roundLifetime),
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,12 +8,12 @@ import (
"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)) })"
func ParseDefaultVtxoDescriptor(
descriptor string,
) (user, asp *secp256k1.PublicKey, timeout uint, err error) {
) (user, server *secp256k1.PublicKey, timeout uint, err error) {
desc, err := ParseTaprootDescriptor(descriptor)
if err != nil {
return nil, nil, 0, err
@@ -42,7 +42,7 @@ func ParseDefaultVtxoDescriptor(
return nil, nil, 0, err
}
asp, err = schnorr.ParsePubKey(keyBytes)
server, err = schnorr.ParsePubKey(keyBytes)
if err != nil {
return nil, nil, 0, err
}
@@ -70,7 +70,7 @@ func ParseDefaultVtxoDescriptor(
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")
}

View File

@@ -8,24 +8,24 @@ import (
"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 {
HRP string
Asp *secp256k1.PublicKey
Server *secp256k1.PublicKey
VtxoTapKey *secp256k1.PublicKey
}
// Encode converts the address to its bech32m string representation
func (a *Address) Encode() (string, error) {
if a.Asp == nil {
return "", fmt.Errorf("missing asp public key")
if a.Server == nil {
return "", fmt.Errorf("missing server public key")
}
if a.VtxoTapKey == nil {
return "", fmt.Errorf("missing vtxo tap public key")
}
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)
if err != nil {
@@ -52,19 +52,19 @@ func DecodeAddress(addr string) (*Address, error) {
return nil, err
}
aKey, err := schnorr.ParsePubKey(grp[:32])
serverKey, err := schnorr.ParsePubKey(grp[:32])
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %s", err)
}
vtxoKey, err := schnorr.ParsePubKey(grp[32:])
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{
HRP: prefix,
Asp: aKey,
Server: serverKey,
VtxoTapKey: vtxoKey,
}, nil
}

View File

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

View File

@@ -30,7 +30,7 @@ func ComputeForfeitTxFee(
feeRate chainfee.SatPerKVByte,
tapscript *waddrmgr.Tapscript,
witnessSize int,
aspScriptClass txscript.ScriptClass,
serverScriptClass txscript.ScriptClass,
) (uint64, error) {
txWeightEstimator := &input.TxWeightEstimator{}
@@ -40,7 +40,7 @@ func ComputeForfeitTxFee(
tapscript,
)
switch aspScriptClass {
switch serverScriptClass {
case txscript.PubKeyHashTy:
txWeightEstimator.AddP2PKHOutput()
case txscript.ScriptHashTy:
@@ -52,7 +52,7 @@ func ComputeForfeitTxFee(
case txscript.WitnessV1TaprootTy:
txWeightEstimator.AddP2TROutput()
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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import (
"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 {
Txid string
Tx string
@@ -14,28 +14,28 @@ type Node struct {
var (
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
type CongestionTree [][]Node
type VtxoTree [][]Node
// Root returns the root node of the congestion tree
func (c CongestionTree) Root() (Node, error) {
// Root returns the root node of the vtxo tree
func (c VtxoTree) Root() (Node, error) {
if len(c) <= 0 {
return Node{}, errors.New("empty congestion tree")
return Node{}, errors.New("empty vtxo tree")
}
if len(c[0]) <= 0 {
return Node{}, errors.New("empty congestion tree")
return Node{}, errors.New("empty vtxo tree")
}
return c[0][0], nil
}
// Leaves returns the leaves of the congestion tree (the vtxos txs)
func (c CongestionTree) Leaves() []Node {
// Leaves returns the leaves of the vtxo tree
func (c VtxoTree) Leaves() []Node {
leaves := c[len(c)-1]
for _, level := range c[:len(c)-1] {
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
func (c CongestionTree) Children(nodeTxid string) []Node {
func (c VtxoTree) Children(nodeTxid string) []Node {
var children []Node
for _, level := range c {
for _, node := range level {
@@ -62,8 +62,8 @@ func (c CongestionTree) Children(nodeTxid string) []Node {
return children
}
// NumberOfNodes returns the total number of pset in the congestion tree
func (c CongestionTree) NumberOfNodes() int {
// NumberOfNodes returns the total number of pset in the vtxo tree
func (c VtxoTree) NumberOfNodes() int {
var count int
for _, level := range c {
count += len(level)
@@ -71,8 +71,8 @@ func (c CongestionTree) NumberOfNodes() int {
return count
}
// Branch returns the branch of the given vtxo txid from root to leaf in the order of the congestion tree
func (c CongestionTree) Branch(vtxoTxid string) ([]Node, error) {
// Branch returns the branch of the given vtxo txid from root to leaf in the order of the vtxo tree
func (c VtxoTree) Branch(vtxoTxid string) ([]Node, error) {
branch := make([]Node, 0)
leaves := c.Leaves()
@@ -102,7 +102,7 @@ func (c CongestionTree) Branch(vtxoTxid string) ([]Node, error) {
return branch, nil
}
func (n Node) findParent(tree CongestionTree) (Node, error) {
func (n Node) findParent(tree VtxoTree) (Node, error) {
for _, level := range tree {
for _, node := range level {
if node.Txid == n.ParentTxid {

View File

@@ -8,13 +8,9 @@ import (
)
func BuildForfeitTxs(
connectorTx *psetv2.Pset,
vtxoInput psetv2.InputArgs,
vtxoAmount,
connectorAmount,
feeAmount uint64,
vtxoScript,
aspScript []byte,
connectorTx *psetv2.Pset, vtxoInput psetv2.InputArgs,
vtxoAmount, connectorAmount, feeAmount uint64,
vtxoScript, serverScript []byte,
) (forfeitTxs []*psetv2.Pset, err error) {
connectors, prevouts := getConnectorInputs(connectorTx, connectorAmount)
@@ -63,7 +59,7 @@ func BuildForfeitTxs(
{
Asset: asset,
Amount: vtxoAmount + connectorAmount - feeAmount,
Script: aspScript,
Script: serverScript,
},
{
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
for i := len(f.PubKeys) - 1; i >= 0; i-- {
pubKey := f.PubKeys[i]
sig, ok := signatures[hex.EncodeToString(schnorr.SerializePubKey(pubKey))]
pubkey := f.PubKeys[i]
sig, ok := signatures[hex.EncodeToString(schnorr.SerializePubKey(pubkey))]
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)
}

View File

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

View File

@@ -4,9 +4,9 @@ import (
"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 {
Pubkey string
PubKey string
Amount uint64
}

View File

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

View File

@@ -26,14 +26,14 @@ func ParseVtxoScript(scripts []string) (VtxoScript, error) {
return v, err
}
func NewDefaultVtxoScript(owner, asp *secp256k1.PublicKey, exitDelay uint) *TapscriptsVtxoScript {
func NewDefaultVtxoScript(owner, server *secp256k1.PublicKey, exitDelay uint) *TapscriptsVtxoScript {
return &TapscriptsVtxoScript{
[]Closure{
&CSVSigClosure{
MultisigClosure: MultisigClosure{PubKeys: []*secp256k1.PublicKey{owner}},
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
}
func (v *TapscriptsVtxoScript) Validate(asp *secp256k1.PublicKey, minExitDelay uint) error {
aspXonly := schnorr.SerializePubKey(asp)
func (v *TapscriptsVtxoScript) Validate(server *secp256k1.PublicKey, minExitDelay uint) error {
serverXonly := schnorr.SerializePubKey(server)
for _, forfeit := range v.ForfeitClosures() {
// must contain asp pubkey
// must contain server pubkey
found := false
for _, pubkey := range forfeit.PubKeys {
if bytes.Equal(schnorr.SerializePubKey(pubkey), aspXonly) {
if bytes.Equal(schnorr.SerializePubKey(pubkey), serverXonly) {
found = true
break
}
}
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.
VtxoScript abstracts the taproot complexity behind vtxo contracts.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -88,12 +88,12 @@ func (o *ArkServicePingOK) Code() int {
func (o *ArkServicePingOK) Error() string {
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 {
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 {
@@ -160,12 +160,12 @@ func (o *ArkServicePingDefault) Code() int {
func (o *ArkServicePingDefault) Error() string {
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 {
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 {

View File

@@ -12,7 +12,7 @@ import (
"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
type V1OwnershipProof struct {

View File

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

View File

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

View File

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

View File

@@ -91,7 +91,7 @@ func LoadCovenantlessClient(sdkStore types.Store) (ArkClient, error) {
}
clientSvc, err := getClient(
supportedClients, cfgData.ClientType, cfgData.AspUrl,
supportedClients, cfgData.ClientType, cfgData.ServerUrl,
)
if err != nil {
return nil, fmt.Errorf("failed to setup transport client: %s", err)
@@ -151,7 +151,7 @@ func LoadCovenantlessClientWithWallet(
}
clientSvc, err := getClient(
supportedClients, cfgData.ClientType, cfgData.AspUrl,
supportedClients, cfgData.ClientType, cfgData.ServerUrl,
)
if err != nil {
return nil, fmt.Errorf("failed to setup transport client: %s", err)
@@ -395,7 +395,7 @@ func (a *covenantlessArkClient) processTransactionEvent(
vtxosToInsert := make([]types.Vtxo, 0)
txsToInsert := make([]types.Transaction, 0)
for _, v := range event.Round.SpendableVtxos {
if v.Pubkey == pubkey {
if v.PubKey == pubkey {
vtxosToInsert = append(vtxosToInsert, types.Vtxo{
VtxoKey: types.VtxoKey{
Txid: v.Txid,
@@ -474,7 +474,7 @@ func (a *covenantlessArkClient) processTransactionEvent(
outputAmount := uint64(0)
for _, v := range event.Redeem.SpendableVtxos {
if v.Pubkey == pubkey {
if v.PubKey == pubkey {
vtxosToInsert = append(vtxosToInsert, types.Vtxo{
VtxoKey: types.VtxoKey{
Txid: v.Txid,
@@ -507,7 +507,7 @@ func (a *covenantlessArkClient) processTransactionEvent(
}
} else {
for _, v := range event.Redeem.SpendableVtxos {
if v.Pubkey == pubkey {
if v.PubKey == pubkey {
vtxosToInsert = append(vtxosToInsert, types.Vtxo{
VtxoKey: types.VtxoKey{
Txid: v.Txid,
@@ -708,13 +708,126 @@ func (a *covenantlessArkClient) SendOffChain(
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 {
if receiver.IsOnchain() {
return "", fmt.Errorf("invalid receiver address '%s': must be offchain", receiver.To())
isOnchain, _, err := utils.ParseBitcoinAddress(receiver.To(), netParams)
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) {
@@ -741,7 +854,7 @@ func (a *covenantlessArkClient) RedeemNotes(ctx context.Context, notes []string)
return "", err
}
paymentID, err := a.client.RegisterNotesForNextRound(
requestID, err := a.client.RegisterNotesForNextRound(
ctx, notes, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
)
if err != nil {
@@ -756,15 +869,15 @@ func (a *covenantlessArkClient) RedeemNotes(ctx context.Context, notes []string)
receiversOutput := []client.Output{output}
if err := a.client.RegisterOutputsForNextRound(
ctx, paymentID, receiversOutput,
ctx, requestID, receiversOutput,
); err != nil {
return "", err
}
log.Infof("payment registered with id: %s", paymentID)
log.Infof("payout registered with id: %s", requestID)
roundTxID, err := a.handleRoundStream(
ctx, paymentID, nil, nil, receiversOutput, roundEphemeralKey,
ctx, requestID, nil, nil, receiversOutput, roundEphemeralKey,
)
if err != nil {
return "", err
@@ -865,7 +978,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network)
vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil {
return "", err
}
@@ -929,7 +1042,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
return "", err
}
paymentID, err := a.client.RegisterInputsForNextRound(
requestID, err := a.client.RegisterInputsForNextRound(
ctx,
inputs,
hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
@@ -938,12 +1051,12 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
return "", err
}
if err := a.client.RegisterOutputsForNextRound(ctx, paymentID, receivers); err != nil {
if err := a.client.RegisterOutputsForNextRound(ctx, requestID, receivers); err != nil {
return "", err
}
roundTxID, err := a.handleRoundStream(
ctx, paymentID, selectedCoins, selectedBoardingCoins, receivers, roundEphemeralKey,
ctx, requestID, selectedCoins, selectedBoardingCoins, receivers, roundEphemeralKey,
)
if err != nil {
return "", err
@@ -952,132 +1065,6 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
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) {
return a.sendOffchain(ctx, false, nil)
}
@@ -1136,7 +1123,7 @@ func (a *covenantlessArkClient) SetNostrNotificationRecipient(ctx context.Contex
descriptorVtxos := make([]client.TapscriptsVtxo, 0)
for _, offchainAddr := range offchainAddrs {
for _, vtxo := range spendableVtxos {
vtxoAddr, err := vtxo.Address(a.AspPubkey, a.Network)
vtxoAddr, err := vtxo.Address(a.ServerPubKey, a.Network)
if err != nil {
return err
}
@@ -1362,7 +1349,7 @@ func (a *covenantlessArkClient) sendOffchain(
return "", fmt.Errorf("wallet is locked")
}
expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey)
expectedServerPubkey := schnorr.SerializePubKey(a.ServerPubKey)
outputs := make([]client.Output, 0)
sumOfReceivers := uint64(0)
@@ -1373,10 +1360,10 @@ func (a *covenantlessArkClient) sendOffchain(
return "", fmt.Errorf("invalid receiver address: %s", err)
}
rcvAspPubKey := schnorr.SerializePubKey(rcvAddr.Asp)
rcvServerPubkey := schnorr.SerializePubKey(rcvAddr.Server)
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 !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 {
@@ -1408,7 +1395,7 @@ func (a *covenantlessArkClient) sendOffchain(
for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network)
vtxoAddr, err := v.Address(a.ServerPubKey, a.Network)
if err != nil {
return "", err
}
@@ -1495,7 +1482,7 @@ func (a *covenantlessArkClient) sendOffchain(
return "", err
}
paymentID, err := a.client.RegisterInputsForNextRound(
requestID, err := a.client.RegisterInputsForNextRound(
ctx, inputs, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
)
if err != nil {
@@ -1503,15 +1490,15 @@ func (a *covenantlessArkClient) sendOffchain(
}
if err := a.client.RegisterOutputsForNextRound(
ctx, paymentID, outputs,
ctx, requestID, outputs,
); err != nil {
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(
ctx, paymentID, selectedCoins, selectedBoardingCoins, outputs, roundEphemeralKey,
ctx, requestID, selectedCoins, selectedBoardingCoins, outputs, roundEphemeralKey,
)
if err != nil {
return "", err
@@ -1594,7 +1581,7 @@ func (a *covenantlessArkClient) addInputs(
func (a *covenantlessArkClient) handleRoundStream(
ctx context.Context,
paymentID string,
requestID string,
vtxosToSign []client.TapscriptsVtxo,
boardingUtxos []types.Utxo,
receivers []client.Output,
@@ -1605,14 +1592,14 @@ func (a *covenantlessArkClient) handleRoundStream(
return "", err
}
eventsCh, close, err := a.client.GetEventStream(ctx, paymentID)
eventsCh, close, err := a.client.GetEventStream(ctx, requestID)
if err != nil {
return "", err
}
var pingStop func()
for pingStop == nil {
pingStop = a.ping(ctx, paymentID)
pingStop = a.ping(ctx, requestID)
}
defer func() {
@@ -1697,7 +1684,7 @@ func (a *covenantlessArkClient) handleRoundStream(
continue
}
log.Info("finalizing payment... ")
log.Info("submitting forfeit transactions... ")
if err := a.client.SubmitSignedForfeitTxs(ctx, signedForfeitTxs, signedRoundTx); err != nil {
return "", err
}
@@ -1715,7 +1702,7 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
ctx context.Context, ephemeralKey *secp256k1.PrivateKey, event client.RoundSigningStartedEvent,
) (signerSession bitcointree.SignerSession, err error) {
sweepClosure := tree.CSVSigClosure{
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{a.AspPubkey}},
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{a.ServerPubKey}},
Seconds: uint(a.RoundLifetime),
}
@@ -1740,7 +1727,7 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
ephemeralKey, sharedOutputValue, event.UnsignedTree, root.CloneBytes(),
)
if err = signerSession.SetKeys(event.CosignersPublicKeys); err != nil {
if err = signerSession.SetKeys(event.CosignersPubKeys); err != nil {
return
}
@@ -1749,9 +1736,9 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
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
}
@@ -1794,8 +1781,8 @@ func (a *covenantlessArkClient) handleRoundFinalization(
boardingUtxos []types.Utxo,
receivers []client.Output,
) ([]string, string, error) {
if err := a.validateCongestionTree(event, receivers); err != nil {
return nil, "", fmt.Errorf("failed to verify congestion tree: %s", err)
if err := a.validateVtxoTree(event, receivers); err != nil {
return nil, "", fmt.Errorf("failed to verify vtxo tree: %s", err)
}
var forfeits []string
@@ -1880,24 +1867,25 @@ func (a *covenantlessArkClient) handleRoundFinalization(
return forfeits, signedRoundTx, nil
}
func (a *covenantlessArkClient) validateCongestionTree(
func (a *covenantlessArkClient) validateVtxoTree(
event client.RoundFinalizationEvent, receivers []client.Output,
) error {
poolTx := event.Tx
ptx, err := psbt.NewFromRawBytes(strings.NewReader(poolTx), true)
roundTx := event.Tx
ptx, err := psbt.NewFromRawBytes(strings.NewReader(roundTx), true)
if err != nil {
return err
}
if !utils.IsOnchainOnly(receivers) {
if err := bitcointree.ValidateCongestionTree(
event.Tree, poolTx, a.Config.AspPubkey, a.RoundLifetime,
if err := bitcointree.ValidateVtxoTree(
event.Tree, roundTx, a.Config.ServerPubKey, a.RoundLifetime,
); err != nil {
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
// }
@@ -1907,15 +1895,13 @@ func (a *covenantlessArkClient) validateCongestionTree(
return err
}
log.Info("congestion tree validated")
return nil
}
func (a *covenantlessArkClient) validateReceivers(
ptx *psbt.Packet,
receivers []client.Output,
congestionTree tree.CongestionTree,
vtxoTree tree.VtxoTree,
) error {
netParams := utils.ToBitcoinNetwork(a.Network)
for _, receiver := range receivers {
@@ -1932,7 +1918,7 @@ func (a *covenantlessArkClient) validateReceivers(
}
} else {
if err := a.validateOffChainReceiver(
congestionTree, receiver,
vtxoTree, receiver,
); err != nil {
return err
}
@@ -1966,7 +1952,7 @@ func (a *covenantlessArkClient) validateOnChainReceiver(
}
func (a *covenantlessArkClient) validateOffChainReceiver(
congestionTree tree.CongestionTree,
vtxoTree tree.VtxoTree,
receiver client.Output,
) error {
found := false
@@ -1978,7 +1964,7 @@ func (a *covenantlessArkClient) validateOffChainReceiver(
vtxoTapKey := schnorr.SerializePubKey(rcvAddr.VtxoTapKey)
leaves := congestionTree.Leaves()
leaves := vtxoTree.Leaves()
for _, leaf := range leaves {
tx, err := psbt.NewFromRawBytes(strings.NewReader(leaf.Tx), true)
if err != nil {
@@ -2247,7 +2233,7 @@ func (a *covenantlessArkClient) coinSelectOnchain(
func (a *covenantlessArkClient) getRedeemBranches(
ctx context.Context, vtxos []client.Vtxo,
) (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)
for i := range vtxos {
@@ -2258,17 +2244,17 @@ func (a *covenantlessArkClient) getRedeemBranches(
continue
}
if _, ok := congestionTrees[vtxo.RoundTxid]; !ok {
if _, ok := vtxoTrees[vtxo.RoundTxid]; !ok {
round, err := a.client.GetRound(ctx, vtxo.RoundTxid)
if err != nil {
return nil, err
}
congestionTrees[vtxo.RoundTxid] = round.Tree
vtxoTrees[vtxo.RoundTxid] = round.Tree
}
redeemBranch, err := redemption.NewCovenantlessRedeemBranch(
a.explorer, congestionTrees[vtxo.RoundTxid], vtxo,
a.explorer, vtxoTrees[vtxo.RoundTxid], vtxo,
)
if err != nil {
return nil, err

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ import (
)
var (
aspUrl = "localhost:7070"
serverUrl = "localhost:7070"
clientType = arksdk.GrpcClient
password = "password"
walletType = arksdk.SingleKeyWallet
@@ -128,11 +128,11 @@ func main() {
fmt.Println("")
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.Info("payment completed out of round")
log.Info("transaction completed out of round")
if err := generateBlock(); err != nil {
log.Fatal(err)
@@ -188,7 +188,7 @@ func setupArkClient(wallet string) (arksdk.ArkClient, error) {
if err := client.Init(context.Background(), arksdk.InitArgs{
WalletType: walletType,
ClientType: clientType,
AspUrl: aspUrl,
ServerUrl: serverUrl,
Password: password,
WithTransactionFeed: true,
}); err != nil {

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,9 +23,9 @@ type CovenantlessRedeemBranch struct {
func NewCovenantlessRedeemBranch(
explorer explorer.Explorer,
congestionTree tree.CongestionTree, vtxo client.Vtxo,
vtxoTree tree.VtxoTree, vtxo client.Vtxo,
) (*CovenantlessRedeemBranch, error) {
_, seconds, err := findCovenantlessSweepClosure(congestionTree)
_, seconds, err := findCovenantlessSweepClosure(vtxoTree)
if err != nil {
return nil, err
}
@@ -35,7 +35,7 @@ func NewCovenantlessRedeemBranch(
return nil, err
}
nodes, err := congestionTree.Branch(vtxo.Txid)
nodes, err := vtxoTree.Branch(vtxo.Txid)
if err != nil {
return nil, err
}
@@ -155,9 +155,9 @@ func (r *CovenantlessRedeemBranch) OffchainPath() ([]*psbt.Packet, error) {
}
func findCovenantlessSweepClosure(
congestionTree tree.CongestionTree,
vtxoTree tree.VtxoTree,
) (*txscript.TapLeaf, uint, error) {
root, err := congestionTree.Root()
root, err := vtxoTree.Root()
if err != nil {
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 {
sd := &storeData{
AspUrl: data.AspUrl,
AspPubkey: hex.EncodeToString(data.AspPubkey.SerializeCompressed()),
ServerUrl: data.ServerUrl,
ServerPubKey: hex.EncodeToString(data.ServerPubKey.SerializeCompressed()),
WalletType: data.WalletType,
ClientType: data.ClientType,
Network: data.Network.Name,

View File

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

View File

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

View File

@@ -4,10 +4,6 @@ import (
"bytes"
"encoding/json"
"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"
"net/http"
"os"
@@ -18,6 +14,11 @@ import (
"testing"
"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"
)
@@ -51,7 +52,7 @@ func TestMain(m *testing.M) {
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 {
fmt.Printf("error initializing ark config: %s", err)
os.Exit(1)
@@ -139,9 +140,9 @@ func TestWasm(t *testing.T) {
amount := 1000
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...")
txID, err = settle(bobPage)
@@ -231,8 +232,8 @@ func initWallet(page playwright.Page) error {
const privateKey = "";
const password = "pass";
const explorerUrl = "";
const aspUrl = "http://localhost:7070";
return await init(walletType, clientType, aspUrl, privateKey, password, chain, explorerUrl);
const serverUrl = "http://localhost:7070";
return await init(walletType, clientType, serverUrl, privateKey, password, chain, explorerUrl);
} catch (err) {
console.error("Init error:", err);
throw err;
@@ -336,10 +337,10 @@ func settle(page playwright.Page) (string, error) {
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 () => {
try {
return await sendAsync(false, [{To:"%s", Amount:%d}]);
return await sendOffChain(false, [{To:"%s", Amount:%d}]);
} catch (err) {
console.error("Error:", err);
throw err;

View File

@@ -23,7 +23,7 @@ var (
type InitArgs struct {
ClientType string
WalletType string
AspUrl string
ServerUrl string
Seed string
Password string
ExplorerURL string
@@ -51,8 +51,8 @@ func (a InitArgs) validate() error {
)
}
if len(a.AspUrl) <= 0 {
return fmt.Errorf("missing asp url")
if len(a.ServerUrl) <= 0 {
return fmt.Errorf("missing server url")
}
if len(a.Password) <= 0 {
return fmt.Errorf("missing password")
@@ -63,7 +63,7 @@ func (a InitArgs) validate() error {
type InitWithWalletArgs struct {
ClientType string
Wallet wallet.WalletService
AspUrl string
ServerUrl string
Seed string
Password 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)
}
if len(a.AspUrl) <= 0 {
return fmt.Errorf("missing asp url")
if len(a.ServerUrl) <= 0 {
return fmt.Errorf("missing server url")
}
if len(a.Password) <= 0 {
return fmt.Errorf("missing password")

View File

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

View File

@@ -193,7 +193,7 @@ func (s *bitcoinWallet) SignTransaction(
)
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 {
if len(input.TaprootLeafScript) > 0 {
@@ -246,7 +246,7 @@ func (s *bitcoinWallet) SignTransaction(
return "", err
}
if !sig.Verify(preimage, s.walletData.Pubkey) {
if !sig.Verify(preimage, s.walletData.PubKey) {
return "", fmt.Errorf("signature verification failed")
}
@@ -308,8 +308,8 @@ func (w *bitcoinWallet) getAddress(
netParams := utils.ToBitcoinNetwork(data.Network)
defaultVtxoScript := bitcointree.NewDefaultVtxoScript(
w.walletData.Pubkey,
data.AspPubkey,
w.walletData.PubKey,
data.ServerPubKey,
uint(data.UnilateralExitDelay),
)
@@ -320,13 +320,13 @@ func (w *bitcoinWallet) getAddress(
offchainAddress := &common.Address{
HRP: data.Network.Addr,
Asp: data.AspPubkey,
Server: data.ServerPubKey,
VtxoTapKey: vtxoTapKey,
}
boardingVtxoScript := bitcointree.NewDefaultVtxoScript(
w.walletData.Pubkey,
data.AspPubkey,
w.walletData.PubKey,
data.ServerPubKey,
uint(data.UnilateralExitDelay*2),
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -89,11 +89,11 @@ func (a *adminService) GetRoundDetails(ctx context.Context, roundId string) (*Ro
OutputsVtxos: []string{},
}
for _, payment := range round.Payments {
for _, request := range round.TxRequests {
// 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() {
roundDetails.TotalExitAmount += receiver.Amount
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
}
for _, input := range payment.Inputs {
for _, input := range request.Inputs {
roundDetails.InputsVtxos = append(roundDetails.InputsVtxos, input.Txid)
}
}
@@ -134,7 +134,7 @@ func (a *adminService) GetScheduledSweeps(ctx context.Context) ([]ScheduledSweep
for _, round := range sweepableRounds {
sweepable, err := findSweepableOutputs(
ctx, a.walletSvc, a.txBuilder, a.sweeperTimeUnit, round.CongestionTree,
ctx, a.walletSvc, a.txBuilder, a.sweeperTimeUnit, round.VtxoTree,
)
if err != nil {
return nil, err

View File

@@ -44,7 +44,7 @@ type covenantService struct {
scanner ports.BlockchainScanner
sweeper *sweeper
paymentRequests *paymentsMap
txRequests *txRequestsQueue
forfeitTxs *forfeitTxsMap
eventsCh chan domain.RoundEvent
@@ -94,7 +94,7 @@ func NewCovenantService(
builder: builder,
scanner: scanner,
sweeper: newSweeper(walletSvc, repoManager, builder, scheduler, notificationPrefix),
paymentRequests: newPaymentsMap(),
txRequests: newTxRequestsQueue(),
forfeitTxs: newForfeitTxsMap(builder),
eventsCh: make(chan domain.RoundEvent),
transactionEventsCh: make(chan TransactionEvent),
@@ -287,14 +287,14 @@ func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input)
vtxosInputs = append(vtxosInputs, vtxo)
}
payment, err := domain.NewPayment(vtxosInputs)
request, err := domain.NewTxRequest(vtxosInputs)
if err != nil {
return "", err
}
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil {
if err := s.txRequests.push(*request, boardingInputs); err != nil {
return "", err
}
return payment.Id, nil
return request.Id, nil
}
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 {
// Check credentials
payment, ok := s.paymentRequests.view(creds)
request, ok := s.txRequests.view(creds)
if !ok {
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 s.paymentRequests.update(*payment)
return s.txRequests.update(*request)
}
func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) error {
return s.paymentRequests.updatePingTimestamp(id)
func (s *covenantService) UpdateTxRequestStatus(_ context.Context, id string) error {
return s.txRequests.updatePingTimestamp(id)
}
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)
}
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")
}
@@ -414,8 +414,8 @@ func (s *covenantService) GetTransactionEventsChannel(ctx context.Context) <-cha
return s.transactionEventsCh
}
func (s *covenantService) GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.Round, error) {
return s.repoManager.Rounds().GetRoundWithTxid(ctx, poolTxid)
func (s *covenantService) GetRoundByTxid(ctx context.Context, roundTxid string) (*domain.Round, error) {
return s.repoManager.Rounds().GetRoundWithTxid(ctx, roundTxid)
}
func (s *covenantService) GetCurrentRound(ctx context.Context) (*domain.Round, error) {
@@ -472,11 +472,11 @@ func (s *covenantService) GetInfo(ctx context.Context) (*ServiceInfo, error) {
}, 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
// we should delete the associated payment
if err := s.paymentRequests.delete(paymentId); err != nil {
log.WithError(err).Warn("failed to delete payment")
// we should delete the associated tx request
if err := s.txRequests.delete(requestID); err != nil {
log.WithError(err).Warnf("failed to delete tx request %s", requestID)
}
return ErrTreeSigningNotRequired
@@ -554,22 +554,22 @@ func (s *covenantService) startFinalization() {
return
}
// TODO: understand how many payments must be popped from the queue and actually registered for the round
num := s.paymentRequests.len()
// TODO: understand how many tx requests must be popped from the queue and actually registered for the round
num := s.txRequests.len()
if num == 0 {
roundAborted = true
err := fmt.Errorf("no payments registered")
err := fmt.Errorf("no tx requests registered")
round.Fail(fmt.Errorf("round aborted: %s", err))
log.WithError(err).Debugf("round %s aborted", round.Id)
return
}
if num > paymentsThreshold {
num = paymentsThreshold
if num > txRequestsThreshold {
num = txRequestsThreshold
}
payments, boardingInputs, _, _ := s.paymentRequests.pop(num)
if _, err := round.RegisterPayments(payments); err != nil {
round.Fail(fmt.Errorf("failed to register payments: %s", err))
log.WithError(err).Warn("failed to register payments")
requests, boardingInputs, _, _ := s.txRequests.pop(num)
if _, err := round.RegisterTxRequests(requests); err != nil {
round.Fail(fmt.Errorf("failed to register tx requests: %s", err))
log.WithError(err).Warn("failed to register tx requests")
return
}
@@ -580,18 +580,18 @@ func (s *covenantService) startFinalization() {
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 {
round.Fail(fmt.Errorf("failed to create pool tx: %s", err))
log.WithError(err).Warn("failed to create pool tx")
round.Fail(fmt.Errorf("failed to create round tx: %s", err))
log.WithError(err).Warn("failed to create round tx")
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(
connectorAddress, connectors, tree, unsignedPoolTx,
connectorAddress, connectors, tree, unsignedRoundTx,
); err != nil {
round.Fail(fmt.Errorf("failed to start finalization: %s", err))
log.WithError(err).Warn("failed to start finalization")
@@ -670,8 +670,8 @@ func (s *covenantService) finalizeRound() {
txid, err := s.wallet.BroadcastTransaction(ctx, signedRoundTx)
if err != nil {
log.Debugf("failed to broadcast round tx: %s", signedRoundTx)
changes = round.Fail(fmt.Errorf("failed to broadcast pool tx: %s", err))
log.WithError(err).Warn("failed to broadcast pool tx")
changes = round.Fail(fmt.Errorf("failed to broadcast round tx: %s", err))
log.WithError(err).Warn("failed to broadcast round tx")
return
}
@@ -682,7 +682,7 @@ func (s *covenantService) finalizeRound() {
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() {
@@ -906,7 +906,7 @@ func (s *covenantService) updateVtxoSet(round *domain.Round) {
ctx := context.Background()
repo := s.repoManager.Vtxos()
spentVtxos := getSpentVtxos(round.Payments)
spentVtxos := getSpentVtxos(round.TxRequests)
if len(spentVtxos) > 0 {
for {
if err := repo.SpendVtxos(ctx, spentVtxos, round.Txid); err != nil {
@@ -970,8 +970,8 @@ func (s *covenantService) updateVtxoSet(round *domain.Round) {
}
}
s.transactionEventsCh <- RoundTransactionEvent{
RoundTxID: round.Txid,
SpentVtxos: getSpentVtxos(round.Payments),
RoundTxid: round.Txid,
SpentVtxos: getSpentVtxos(round.TxRequests),
SpendableVtxos: s.getNewVtxos(round),
ClaimedBoardingInputs: boardingInputs,
}
@@ -984,7 +984,7 @@ func (s *covenantService) propagateEvents(round *domain.Round) {
case domain.RoundFinalizationStarted:
ev := domain.RoundFinalizationStarted{
Id: e.Id,
CongestionTree: e.CongestionTree,
VtxoTree: e.VtxoTree,
Connectors: e.Connectors,
RoundTx: e.RoundTx,
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)
if err := s.sweeper.schedule(
expirationTime, round.Txid, round.CongestionTree,
expirationTime, round.Txid, round.VtxoTree,
); err != nil {
log.WithError(err).Warn("failed to schedule sweep tx")
}
}
func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo {
if len(round.CongestionTree) <= 0 {
if len(round.VtxoTree) <= 0 {
return nil
}
createdAt := time.Now().Unix()
leaves := round.CongestionTree.Leaves()
leaves := round.VtxoTree.Leaves()
vtxos := make([]domain.Vtxo, 0)
for _, node := range leaves {
tx, _ := psetv2.NewPsetFromBase64(node.Tx)
@@ -1038,7 +1038,7 @@ func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo {
vtxos = append(vtxos, domain.Vtxo{
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
Pubkey: vtxoPubkey,
PubKey: vtxoPubkey,
Amount: uint64(out.Value),
RoundTxid: round.Txid,
CreatedAt: createdAt,
@@ -1113,7 +1113,7 @@ func (s *covenantService) restoreWatchingVtxos() error {
func (s *covenantService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string, error) {
indexedScripts := make(map[string]struct{})
for _, vtxo := range vtxos {
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey)
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.PubKey)
if err != nil {
return nil, err
}

View File

@@ -46,7 +46,7 @@ type covenantlessService struct {
scanner ports.BlockchainScanner
sweeper *sweeper
paymentRequests *paymentsMap
txRequests *txRequestsQueue
forfeitTxs *forfeitTxsMap
eventsCh chan domain.RoundEvent
@@ -99,7 +99,7 @@ func NewCovenantlessService(
builder: builder,
scanner: scanner,
sweeper: newSweeper(walletSvc, repoManager, builder, scheduler, noteUriPrefix),
paymentRequests: newPaymentsMap(),
txRequests: newTxRequestsQueue(),
forfeitTxs: newForfeitTxsMap(builder),
eventsCh: make(chan domain.RoundEvent),
transactionEventsCh: make(chan TransactionEvent),
@@ -260,37 +260,37 @@ func (s *covenantlessService) SubmitRedeemTx(
}
// 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 {
if !bytes.Equal(sig.XOnlyPubKey, aspXOnlyPubKey) {
if !bytes.Equal(sig.XOnlyPubKey, serverXOnlyPubkey) {
parsed, err := schnorr.ParsePubKey(sig.XOnlyPubKey)
if err != nil {
return "", fmt.Errorf("failed to parse pubkey: %s", err)
}
userPubKey = parsed
userPubkey = parsed
break
}
}
if userPubKey == nil {
if userPubkey == nil {
return "", fmt.Errorf("redeem transaction is not signed")
}
vtxoPublicKeyBytes, err := hex.DecodeString(vtxo.Pubkey)
vtxoPubkeyBuf, err := hex.DecodeString(vtxo.PubKey)
if err != nil {
return "", fmt.Errorf("failed to decode vtxo pubkey: %s", err)
}
vtxoTapKey, err := schnorr.ParsePubKey(vtxoPublicKeyBytes)
vtxoPubkey, err := schnorr.ParsePubKey(vtxoPubkeyBuf)
if err != nil {
return "", fmt.Errorf("failed to parse vtxo pubkey: %s", err)
}
// verify witness utxo
pkscript, err := common.P2TRScript(vtxoTapKey)
pkscript, err := common.P2TRScript(vtxoPubkey)
if err != nil {
return "", fmt.Errorf("failed to get pkscript: %s", err)
}
@@ -378,8 +378,15 @@ func (s *covenantlessService) SubmitRedeemTx(
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 {
vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:])
if err != nil {
@@ -393,24 +400,15 @@ func (s *covenantlessService) SubmitRedeemTx(
Txid: redeemTxid,
VOut: uint32(outIndex),
},
Pubkey: vtxoPubkey,
PubKey: vtxoPubkey,
Amount: uint64(out.Value),
ExpireAt: expiration,
RoundTxid: roundTxid,
RedeemTx: redeemTx,
RedeemTx: signedRedeemTx,
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 {
return "", fmt.Errorf("failed to add vtxos: %s", err)
}
@@ -429,7 +427,7 @@ func (s *covenantlessService) SubmitRedeemTx(
go func() {
s.transactionEventsCh <- RedeemTransactionEvent{
AsyncTxID: redeemTxid,
RedeemTxid: redeemTxid,
SpentVtxos: spentVtxoKeys,
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 {
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 {
return "", fmt.Errorf("failed to push payment: %s", err)
if err := s.txRequests.pushWithNotes(*request, notes); err != nil {
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) {
@@ -600,14 +598,14 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp
vtxosInputs = append(vtxosInputs, vtxo)
}
payment, err := domain.NewPayment(vtxosInputs)
request, err := domain.NewTxRequest(vtxosInputs)
if err != nil {
return "", err
}
if err := s.paymentRequests.push(*payment, boardingInputs); err != nil {
if err := s.txRequests.push(*request, boardingInputs); err != nil {
return "", err
}
return payment.Id, nil
return request.Id, nil
}
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)
}
expectedScriptPubKey, err := common.P2TRScript(tapKey)
expectedScriptPubkey, err := common.P2TRScript(tapKey)
if err != nil {
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")
}
@@ -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 {
// Check credentials
payment, ok := s.paymentRequests.view(creds)
request, ok := s.txRequests.view(creds)
if !ok {
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 s.paymentRequests.update(*payment)
return s.txRequests.update(*request)
}
func (s *covenantlessService) UpdatePaymentStatus(_ context.Context, id string) error {
return s.paymentRequests.updatePingTimestamp(id)
func (s *covenantlessService) UpdateTxRequestStatus(_ context.Context, id string) error {
return s.txRequests.updatePingTimestamp(id)
}
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)
}
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")
}
@@ -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)
if err != nil {
return fmt.Errorf("failed to decode hex pubkey: %s", err)
}
ephemeralPublicKey, err := secp256k1.ParsePubKey(pubkeyBytes)
ephemeralPubkey, err := secp256k1.ParsePubKey(pubkeyBytes)
if err != nil {
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(
@@ -853,7 +851,7 @@ func (s *covenantlessService) RegisterCosignerNonces(
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() {
session.nonceDoneC <- struct{}{}
}()
@@ -884,7 +882,7 @@ func (s *covenantlessService) RegisterCosignerSignatures(
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() {
session.sigDoneC <- struct{}{}
}()
@@ -992,31 +990,31 @@ func (s *covenantlessService) startFinalization() {
return
}
// TODO: understand how many payments must be popped from the queue and actually registered for the round
num := s.paymentRequests.len()
// TODO: understand how many tx requests must be popped from the queue and actually registered for the round
num := s.txRequests.len()
if num == 0 {
roundAborted = true
err := fmt.Errorf("no payments registered")
err := fmt.Errorf("no tx requests registered")
round.Fail(fmt.Errorf("round aborted: %s", err))
log.WithError(err).Debugf("round %s aborted", round.Id)
return
}
if num > paymentsThreshold {
num = paymentsThreshold
if num > txRequestsThreshold {
num = txRequestsThreshold
}
payments, boardingInputs, cosigners, paymentsNotes := s.paymentRequests.pop(num)
if len(payments) > len(cosigners) {
err := fmt.Errorf("missing ephemeral key for payments")
requests, boardingInputs, cosigners, redeeemedNotes := s.txRequests.pop(num)
if len(requests) > len(cosigners) {
err := fmt.Errorf("missing ephemeral key for tx requests")
round.Fail(fmt.Errorf("round aborted: %s", err))
log.WithError(err).Debugf("round %s aborted", round.Id)
return
}
notes = paymentsNotes
notes = redeeemedNotes
if _, err := round.RegisterPayments(payments); err != nil {
round.Fail(fmt.Errorf("failed to register payments: %s", err))
log.WithError(err).Warn("failed to register payments")
if _, err := round.RegisterTxRequests(requests); err != nil {
round.Fail(fmt.Errorf("failed to register tx requests: %s", err))
log.WithError(err).Warn("failed to register tx requests")
return
}
@@ -1038,7 +1036,7 @@ func (s *covenantlessService) startFinalization() {
unsignedRoundTx, vtxoTree, connectorAddress, connectors, err := s.builder.BuildRoundTx(
s.pubkey,
payments,
requests,
boardingInputs,
sweptRounds,
cosigners...,
@@ -1050,10 +1048,10 @@ func (s *covenantlessService) startFinalization() {
}
log.Debugf("round tx created for round %s", round.Id)
s.forfeitTxs.init(connectors, payments)
s.forfeitTxs.init(connectors, requests)
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))
s.treeSigningSessions[round.Id] = signingSession
@@ -1094,11 +1092,11 @@ func (s *covenantlessService) startFinalization() {
return
}
aspSignerSession := bitcointree.NewTreeSignerSession(
serverSignerSession := bitcointree.NewTreeSignerSession(
ephemeralKey, sharedOutputAmount, vtxoTree, root.CloneBytes(),
)
nonces, err := aspSignerSession.GetNonces()
nonces, err := serverSignerSession.GetNonces()
if err != nil {
round.Fail(fmt.Errorf("failed to get nonces: %s", err))
log.WithError(err).Warn("failed to get nonces")
@@ -1142,33 +1140,33 @@ func (s *covenantlessService) startFinalization() {
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))
log.WithError(err).Warn("failed to set keys")
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))
log.WithError(err).Warn("failed to set aggregated nonces")
return
}
// sign the tree as ASP
aspTreeSigs, err := aspSignerSession.Sign()
// sign the tree as server
serverTreeSigs, err := serverSignerSession.Sign()
if err != nil {
round.Fail(fmt.Errorf("failed to sign tree: %s", err))
log.WithError(err).Warn("failed to sign tree")
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))
log.WithError(err).Warn("failed to add signature")
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)
@@ -1199,7 +1197,7 @@ func (s *covenantlessService) startFinalization() {
return
}
log.Debugf("congestion tree signed for round %s", round.Id)
log.Debugf("vtxo tree signed for round %s", round.Id)
vtxoTree = signedTree
}
@@ -1216,11 +1214,11 @@ func (s *covenantlessService) startFinalization() {
}
func (s *covenantlessService) propagateRoundSigningStartedEvent(
unsignedCongestionTree tree.CongestionTree, cosigners []*secp256k1.PublicKey,
unsignedVtxoTree tree.VtxoTree, cosigners []*secp256k1.PublicKey,
) {
ev := RoundSigningStarted{
Id: s.currentRound.Id,
UnsignedVtxoTree: unsignedCongestionTree,
UnsignedVtxoTree: unsignedVtxoTree,
Cosigners: cosigners,
UnsignedRoundTx: s.currentRound.UnsignedTx,
}
@@ -1311,7 +1309,7 @@ func (s *covenantlessService) finalizeRound(notes []note.Note) {
txid, err := s.wallet.BroadcastTransaction(ctx, signedRoundTx)
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
}
@@ -1331,14 +1329,14 @@ func (s *covenantlessService) finalizeRound(notes []note.Note) {
go func() {
s.transactionEventsCh <- RoundTransactionEvent{
RoundTxID: round.Txid,
SpentVtxos: getSpentVtxos(round.Payments),
RoundTxid: round.Txid,
SpentVtxos: getSpentVtxos(round.TxRequests),
SpendableVtxos: s.getNewVtxos(round),
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() {
@@ -1491,7 +1489,7 @@ func (s *covenantlessService) updateVtxoSet(round *domain.Round) {
ctx := context.Background()
repo := s.repoManager.Vtxos()
spentVtxos := getSpentVtxos(round.Payments)
spentVtxos := getSpentVtxos(round.TxRequests)
if len(spentVtxos) > 0 {
for {
if err := repo.SpendVtxos(ctx, spentVtxos, round.Txid); err != nil {
@@ -1544,7 +1542,7 @@ func (s *covenantlessService) propagateEvents(round *domain.Round) {
case domain.RoundFinalizationStarted:
ev := domain.RoundFinalizationStarted{
Id: e.Id,
CongestionTree: e.CongestionTree,
VtxoTree: e.VtxoTree,
Connectors: e.Connectors,
RoundTx: e.RoundTx,
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)
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")
}
}
func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo {
if len(round.CongestionTree) <= 0 {
if len(round.VtxoTree) <= 0 {
return nil
}
createdAt := time.Now().Unix()
leaves := round.CongestionTree.Leaves()
leaves := round.VtxoTree.Leaves()
vtxos := make([]domain.Vtxo, 0)
for _, node := range leaves {
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))
vtxos = append(vtxos, domain.Vtxo{
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
Pubkey: vtxoPubkey,
PubKey: vtxoPubkey,
Amount: uint64(out.Value),
RoundTxid: round.Txid,
CreatedAt: createdAt,
@@ -1672,7 +1669,7 @@ func (s *covenantlessService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string
indexedScripts := make(map[string]struct{})
for _, vtxo := range vtxos {
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey)
vtxoTapKeyBytes, err := hex.DecodeString(vtxo.PubKey)
if err != nil {
return nil, err
}
@@ -1731,7 +1728,7 @@ func (s *covenantlessService) reactToFraud(ctx context.Context, vtxo domain.Vtxo
if err != nil {
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{
{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)
}
asyncPayVtxo := vtxos[0]
if asyncPayVtxo.Redeemed { // redeem tx is already onchain
storedVtxo := vtxos[0]
if storedVtxo.Redeemed { // redeem tx is already onchain
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 {
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
* 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
*/
package application
@@ -17,7 +17,7 @@ import (
// signer should react to this event by generating a musig2 nonce for each transaction in the tree
type RoundSigningStarted struct {
Id string
UnsignedVtxoTree tree.CongestionTree
UnsignedVtxoTree tree.VtxoTree
Cosigners []*secp256k1.PublicKey
UnsignedRoundTx string
}

View File

@@ -2,10 +2,10 @@ package application
import "fmt"
type errPaymentNotFound struct {
type errTxRequestNotFound struct {
id string
}
func (e errPaymentNotFound) Error() string {
return fmt.Sprintf("payment %s not found", e.id)
func (e errTxRequestNotFound) Error() string {
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)
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")
}

View File

@@ -17,7 +17,7 @@ import (
// sweeper is an unexported service running while the main application service is started
// 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
// 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 {
wallet ports.WalletService
repoManager ports.RepoManager
@@ -58,7 +58,7 @@ func (s *sweeper) start() error {
}
for _, round := range allRounds {
task := s.createTask(round.Txid, round.CongestionTree)
task := s.createTask(round.Txid, round.VtxoTree)
task()
}
@@ -78,14 +78,14 @@ func (s *sweeper) removeTask(treeRootTxid string) {
// schedule set up a task to be executed once at the given timestamp
func (s *sweeper) schedule(
expirationTimestamp int64, roundTxid string, congestionTree tree.CongestionTree,
expirationTimestamp int64, roundTxid string, vtxoTree tree.VtxoTree,
) error {
if len(congestionTree) <= 0 { // skip
log.Debugf("skipping sweep scheduling (round tx %s), empty congestion tree", roundTxid)
if len(vtxoTree) <= 0 { // skip
log.Debugf("skipping sweep scheduling (round tx %s), empty vtxo tree", roundTxid)
return nil
}
root, err := congestionTree.Root()
root, err := vtxoTree.Root()
if err != nil {
return err
}
@@ -94,7 +94,7 @@ func (s *sweeper) schedule(
return nil
}
task := s.createTask(roundTxid, congestionTree)
task := s.createTask(roundTxid, vtxoTree)
var fancyTime string
if s.scheduler.Unit() == ports.UnixTime {
@@ -112,7 +112,7 @@ func (s *sweeper) schedule(
s.scheduledTasks[root.Txid] = struct{}{}
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")
}
@@ -120,14 +120,14 @@ func (s *sweeper) schedule(
}
// 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
func (s *sweeper) createTask(
roundTxid string, congestionTree tree.CongestionTree,
roundTxid string, vtxoTree tree.VtxoTree,
) func() {
return func() {
ctx := context.Background()
root, err := congestionTree.Root()
root, err := vtxoTree.Root()
if err != nil {
log.WithError(err).Error("error while getting root node")
return
@@ -139,17 +139,17 @@ func (s *sweeper) createTask(
sweepInputs := make([]ports.SweepInput, 0)
vtxoKeys := make([]domain.VtxoKey, 0) // vtxos associated to the sweep inputs
// inspect the congestion tree to find onchain shared outputs
sharedOutputs, err := findSweepableOutputs(ctx, s.wallet, s.builder, s.scheduler.Unit(), congestionTree)
// inspect the vtxo tree to find onchain shared outputs
sharedOutputs, err := findSweepableOutputs(ctx, s.wallet, s.builder, s.scheduler.Unit(), vtxoTree)
if err != nil {
log.WithError(err).Error("error while inspecting congestion tree")
log.WithError(err).Error("error while inspecting vtxo tree")
return
}
for expiredAt, inputs := range sharedOutputs {
// if the shared outputs are not expired, schedule a sweep task for it
if s.scheduler.AfterNow(expiredAt) {
subtrees, err := computeSubTrees(congestionTree, inputs)
subtrees, err := computeSubTrees(vtxoTree, inputs)
if err != nil {
log.WithError(err).Error("error while computing subtrees")
continue
@@ -185,7 +185,7 @@ func (s *sweeper) createTask(
}
} else {
// 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 {
log.WithError(err).Error("error while finding vtxos leaves")
continue
@@ -301,7 +301,7 @@ func (s *sweeper) createTask(
}
func (s *sweeper) updateVtxoExpirationTime(
tree tree.CongestionTree,
tree tree.VtxoTree,
expirationTime int64,
) error {
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) {
subTrees := make(map[string]tree.CongestionTree, 0)
func computeSubTrees(vtxoTree tree.VtxoTree, inputs []ports.SweepInput) ([]tree.VtxoTree, error) {
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
for _, input := range inputs {
subTree, err := computeSubTree(congestionTree, input.GetHash().String())
subTree, err := computeSubTree(vtxoTree, input.GetHash().String())
if err != nil {
log.WithError(err).Error("error while finding sub tree")
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
filteredSubTrees := make([]tree.CongestionTree, 0)
filteredSubTrees := make([]tree.VtxoTree, 0)
for i, subTree := range subTrees {
notIncludedInOtherTrees := true
@@ -426,19 +426,19 @@ func computeSubTrees(congestionTree tree.CongestionTree, inputs []ports.SweepInp
return filteredSubTrees, nil
}
func computeSubTree(congestionTree tree.CongestionTree, newRoot string) (tree.CongestionTree, error) {
for _, level := range congestionTree {
func computeSubTree(vtxoTree tree.VtxoTree, newRoot string) (tree.VtxoTree, error) {
for _, level := range vtxoTree {
for _, node := range level {
if node.Txid == newRoot || node.ParentTxid == newRoot {
newTree := make(tree.CongestionTree, 0)
newTree := make(tree.VtxoTree, 0)
newTree = append(newTree, []tree.Node{node})
children := congestionTree.Children(node.Txid)
children := vtxoTree.Children(node.Txid)
for len(children) > 0 {
newTree = append(newTree, children)
newChildren := make([]tree.Node, 0)
for _, child := range children {
newChildren = append(newChildren, congestionTree.Children(child.Txid)...)
newChildren = append(newChildren, vtxoTree.Children(child.Txid)...)
}
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")
}
func containsTree(tr0 tree.CongestionTree, tr1 tree.CongestionTree) (bool, error) {
func containsTree(tr0 tree.VtxoTree, tr1 tree.VtxoTree) (bool, error) {
tr1Root, err := tr1.Root()
if err != nil {
return false, err
@@ -468,7 +468,7 @@ func containsTree(tr0 tree.CongestionTree, tr1 tree.CongestionTree) (bool, error
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) {
if !leaf.Leaf {
return nil, fmt.Errorf("node is not a leaf")

View File

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

View File

@@ -16,32 +16,32 @@ import (
"github.com/nbd-wtf/go-nostr/nip19"
)
type timedPayment struct {
domain.Payment
type timedTxRequest struct {
domain.TxRequest
boardingInputs []ports.BoardingInput
notes []note.Note
timestamp time.Time
pingTimestamp time.Time
}
type paymentsMap struct {
type txRequestsQueue struct {
lock *sync.RWMutex
payments map[string]*timedPayment
requests map[string]*timedTxRequest
ephemeralKeys map[string]*secp256k1.PublicKey
}
func newPaymentsMap() *paymentsMap {
paymentsById := make(map[string]*timedPayment)
func newTxRequestsQueue() *txRequestsQueue {
requestsById := make(map[string]*timedTxRequest)
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()
defer m.lock.RUnlock()
count := int64(0)
for _, p := range m.payments {
for _, p := range m.requests {
if len(p.Receivers) > 0 {
count++
}
@@ -49,154 +49,154 @@ func (m *paymentsMap) len() int64 {
return count
}
func (m *paymentsMap) delete(id string) error {
func (m *txRequestsQueue) delete(id string) error {
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.payments[id]; !ok {
return errPaymentNotFound{id}
if _, ok := m.requests[id]; !ok {
return errTxRequestNotFound{id}
}
delete(m.payments, id)
delete(m.requests, id)
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()
defer m.lock.Unlock()
if _, ok := m.payments[payment.Id]; ok {
return fmt.Errorf("duplicated payment %s", payment.Id)
if _, ok := m.requests[request.Id]; ok {
return fmt.Errorf("duplicated tx request %s", request.Id)
}
for _, note := range notes {
for _, payment := range m.payments {
for _, pNote := range payment.notes {
if note.ID == pNote.ID {
for _, txRequest := range m.requests {
for _, rNote := range txRequest.notes {
if note.ID == rNote.ID {
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
}
func (m *paymentsMap) push(
payment domain.Payment,
func (m *txRequestsQueue) push(
request domain.TxRequest,
boardingInputs []ports.BoardingInput,
) error {
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.payments[payment.Id]; ok {
return fmt.Errorf("duplicated payment %s", payment.Id)
if _, ok := m.requests[request.Id]; ok {
return fmt.Errorf("duplicated tx request %s", request.Id)
}
for _, input := range payment.Inputs {
for _, pay := range m.payments {
for _, input := range request.Inputs {
for _, pay := range m.requests {
for _, pInput := range pay.Inputs {
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 _, pay := range m.payments {
for _, pBoardingInput := range pay.boardingInputs {
for _, request := range m.requests {
for _, pBoardingInput := range request.boardingInputs {
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
}
func (m *paymentsMap) pushEphemeralKey(paymentId string, pubkey *secp256k1.PublicKey) error {
func (m *txRequestsQueue) pushEphemeralKey(requestID string, pubkey *secp256k1.PublicKey) error {
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.payments[paymentId]; !ok {
return fmt.Errorf("payment %s not found, cannot register signing ephemeral public key", paymentId)
if _, ok := m.requests[requestID]; !ok {
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
}
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()
defer m.lock.Unlock()
paymentsByTime := make([]timedPayment, 0, len(m.payments))
for _, p := range m.payments {
// Skip payments without registered receivers.
requestsByTime := make([]timedTxRequest, 0, len(m.requests))
for _, p := range m.requests {
// Skip tx requests without registered receivers.
if len(p.Receivers) <= 0 {
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 {
continue
}
paymentsByTime = append(paymentsByTime, *p)
requestsByTime = append(requestsByTime, *p)
}
sort.SliceStable(paymentsByTime, func(i, j int) bool {
return paymentsByTime[i].timestamp.Before(paymentsByTime[j].timestamp)
sort.SliceStable(requestsByTime, func(i, j int) bool {
return requestsByTime[i].timestamp.Before(requestsByTime[j].timestamp)
})
if num < 0 || num > int64(len(paymentsByTime)) {
num = int64(len(paymentsByTime))
if num < 0 || num > int64(len(requestsByTime)) {
num = int64(len(requestsByTime))
}
payments := make([]domain.Payment, 0, num)
requests := make([]domain.TxRequest, 0, num)
boardingInputs := make([]ports.BoardingInput, 0)
cosigners := make([]*secp256k1.PublicKey, 0, num)
notes := make([]note.Note, 0)
for _, p := range paymentsByTime[:num] {
for _, p := range requestsByTime[:num] {
boardingInputs = append(boardingInputs, p.boardingInputs...)
payments = append(payments, p.Payment)
if pubkey, ok := m.ephemeralKeys[p.Payment.Id]; ok {
requests = append(requests, p.TxRequest)
if pubkey, ok := m.ephemeralKeys[p.TxRequest.Id]; ok {
cosigners = append(cosigners, pubkey)
delete(m.ephemeralKeys, p.Payment.Id)
delete(m.ephemeralKeys, p.TxRequest.Id)
}
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()
defer m.lock.Unlock()
p, ok := m.payments[payment.Id]
r, ok := m.requests[request.Id]
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
sumOfInputs := uint64(0)
for _, input := range payment.Inputs {
for _, input := range request.Inputs {
sumOfInputs += input.Amount
}
for _, boardingInput := range p.boardingInputs {
for _, boardingInput := range r.boardingInputs {
sumOfInputs += boardingInput.Amount
}
for _, note := range p.notes {
for _, note := range r.notes {
sumOfInputs += uint64(note.Value)
}
// sum outputs = receivers VTXOs
sumOfOutputs := uint64(0)
for _, receiver := range payment.Receivers {
for _, receiver := range request.Receivers {
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)
}
p.Payment = payment
r.TxRequest = request
return nil
}
func (m *paymentsMap) updatePingTimestamp(id string) error {
func (m *txRequestsQueue) updatePingTimestamp(id string) error {
m.lock.Lock()
defer m.lock.Unlock()
payment, ok := m.payments[id]
request, ok := m.requests[id]
if !ok {
return errPaymentNotFound{id}
return errTxRequestNotFound{id}
}
payment.pingTimestamp = time.Now()
request.pingTimestamp = time.Now()
return nil
}
func (m *paymentsMap) view(id string) (*domain.Payment, bool) {
func (m *txRequestsQueue) view(id string) (*domain.TxRequest, bool) {
m.lock.RLock()
defer m.lock.RUnlock()
payment, ok := m.payments[id]
request, ok := m.requests[id]
if !ok {
return nil, false
}
return &domain.Payment{
Id: payment.Id,
Inputs: payment.Inputs,
Receivers: payment.Receivers,
return &domain.TxRequest{
Id: request.Id,
Inputs: request.Inputs,
Receivers: request.Receivers,
}, true
}
@@ -251,10 +251,10 @@ func newForfeitTxsMap(txBuilder ports.TxBuilder) *forfeitTxsMap {
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)
for _, payment := range payments {
vtxosToSign = append(vtxosToSign, payment.Inputs...)
for _, request := range requests {
vtxosToSign = append(vtxosToSign, request.Inputs...)
}
m.lock.Lock()
@@ -318,18 +318,18 @@ func (m *forfeitTxsMap) pop() ([]string, error) {
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
func findSweepableOutputs(
ctx context.Context,
walletSvc ports.WalletService,
txbuilder ports.TxBuilder,
schedulerUnit ports.TimeUnit,
congestionTree tree.CongestionTree,
vtxoTree tree.VtxoTree,
) (map[int64][]ports.SweepInput, error) {
sweepableOutputs := make(map[int64][]ports.SweepInput)
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 {
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
// We will return the error below, but are we going to schedule the tasks for the "children roots"?
if !node.Leaf {
children := congestionTree.Children(node.Txid)
children := vtxoTree.Children(node.Txid)
newNodesToCheck = append(newNodesToCheck, children...)
}
continue
@@ -393,10 +393,10 @@ func findSweepableOutputs(
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)
for _, p := range payments {
for _, vtxo := range p.Inputs {
for _, request := range requests {
for _, vtxo := range request.Inputs {
vtxos = append(vtxos, vtxo.VtxoKey)
}
}
@@ -460,12 +460,12 @@ func nip19toNostrProfile(nostrRecipient string, defaultRelays []string) (string,
nprofileRecipient = nostrRecipient
case "npub":
recipientPublicKey, ok := result.(string)
recipientPubkey, ok := result.(string)
if !ok {
return "", fmt.Errorf("invalid NIP-19 result: %v", result)
}
nprofileRecipient, err = nip19.EncodeProfile(recipientPublicKey, defaultRelays)
nprofileRecipient, err = nip19.EncodeProfile(recipientPubkey, defaultRelays)
if err != nil {
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 RoundFinalized) IsEvent() {}
func (r RoundFailed) IsEvent() {}
func (r PaymentsRegistered) IsEvent() {}
func (r TxRequestsRegistered) IsEvent() {}
type RoundStarted struct {
Id string
@@ -19,7 +19,7 @@ type RoundStarted struct {
type RoundFinalizationStarted struct {
Id string
CongestionTree tree.CongestionTree // BTC: signed
VtxoTree tree.VtxoTree
Connectors []string
ConnectorAddress string
RoundTx string
@@ -39,7 +39,7 @@ type RoundFailed struct {
Timestamp int64
}
type PaymentsRegistered struct {
type TxRequestsRegistered struct {
Id string
Payments []Payment
TxRequests []TxRequest
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,15 +27,15 @@ type BoardingInput struct {
}
type TxBuilder interface {
// BuildRoundTx builds a round tx for the given payments, boarding inputs
// it selects coin from swept rounds and ASP wallet
// BuildRoundTx builds a round tx for the given tx requests, boarding inputs
// it selects coin from swept rounds and server wallet
// returns the round partial tx, the vtxo tree and the set of connectors
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,
) (
roundTx string,
congestionTree tree.CongestionTree,
vtxoTree tree.VtxoTree,
connectorAddress string,
connectors []string,
err error,
@@ -51,7 +51,7 @@ type TxBuilder interface {
FinalizeAndExtract(tx string) (txhex string, err error)
VerifyTapscriptPartialSigs(tx string) (valid bool, err error)
// 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)
GetTxID(tx string) (string, error)
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,19 +13,19 @@ CREATE TABLE IF NOT EXISTS round (
swept BOOLEAN NOT NULL
);
CREATE TABLE IF NOT EXISTS payment (
CREATE TABLE IF NOT EXISTS tx_request (
id TEXT PRIMARY KEY,
round_id TEXT NOT NULL,
FOREIGN KEY (round_id) REFERENCES round(id)
);
CREATE TABLE IF NOT EXISTS receiver (
payment_id TEXT NOT NULL,
request_id TEXT NOT NULL,
pubkey TEXT,
onchain_address TEXT,
amount INTEGER NOT NULL,
FOREIGN KEY (payment_id) REFERENCES payment(id),
PRIMARY KEY (payment_id, pubkey, onchain_address)
FOREIGN KEY (request_id) REFERENCES tx_request(id),
PRIMARY KEY (request_id, pubkey, onchain_address)
);
CREATE TABLE IF NOT EXISTS tx (
@@ -46,17 +46,17 @@ CREATE TABLE IF NOT EXISTS vtxo (
vout INTEGER NOT NULL,
pubkey TEXT NOT NULL,
amount INTEGER NOT NULL,
pool_tx TEXT NOT NULL,
round_tx TEXT NOT NULL,
spent_by TEXT NOT NULL,
spent BOOLEAN NOT NULL,
redeemed BOOLEAN NOT NULL,
swept BOOLEAN NOT NULL,
expire_at INTEGER NOT NULL,
created_at INTEGER NOT NULL,
payment_id TEXT,
request_id TEXT,
redeem_tx TEXT,
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 (
@@ -81,22 +81,22 @@ FROM entity
LEFT OUTER JOIN entity_vtxo
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
LEFT OUTER JOIN payment
ON round.id=payment.round_id;
LEFT OUTER JOIN tx_request
ON round.id=tx_request.round_id;
CREATE VIEW round_tx_vw AS SELECT tx.*
FROM round
LEFT OUTER JOIN tx
ON round.id=tx.round_id;
CREATE VIEW payment_receiver_vw AS SELECT receiver.*
FROM payment
CREATE VIEW request_receiver_vw AS SELECT receiver.*
FROM tx_request
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.*
FROM payment
CREATE VIEW request_vtxo_vw AS SELECT vtxo.*
FROM tx_request
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)
}
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 {
if err := querierWithTx.UpsertTransaction(
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 {
if err := querierWithTx.UpsertTransaction(
ctx,
@@ -148,27 +148,27 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
}
}
if len(round.Payments) > 0 {
for _, payment := range round.Payments {
if err := querierWithTx.UpsertPayment(
if len(round.TxRequests) > 0 {
for _, request := range round.TxRequests {
if err := querierWithTx.UpsertTxRequest(
ctx,
queries.UpsertPaymentParams{
ID: payment.Id,
queries.UpsertTxRequestParams{
ID: request.Id,
RoundID: round.Id,
},
); 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(
ctx,
queries.UpsertReceiverParams{
PaymentID: payment.Id,
RequestID: request.Id,
Amount: int64(receiver.Amount),
Pubkey: sql.NullString{
String: receiver.Pubkey,
Valid: len(receiver.Pubkey) > 0,
String: receiver.PubKey,
Valid: len(receiver.PubKey) > 0,
},
OnchainAddress: sql.NullString{
String: receiver.OnchainAddress,
@@ -180,19 +180,19 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou
}
}
for _, input := range payment.Inputs {
if err := querierWithTx.UpdateVtxoPaymentId(
for _, input := range request.Inputs {
if err := querierWithTx.UpdateVtxoRequestId(
ctx,
queries.UpdateVtxoPaymentIdParams{
PaymentID: sql.NullString{
String: payment.Id,
queries.UpdateVtxoRequestIdParams{
RequestID: sql.NullString{
String: request.Id,
Valid: true,
},
Txid: input.Txid,
Vout: int64(input.VOut),
},
); 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
}
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows))
rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{
rvs = append(rvs, combinedRow{
round: row.Round,
payment: row.RoundPaymentVw,
request: row.RoundRequestVw,
tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw,
vtxo: row.PaymentVtxoVw,
receiver: row.RequestReceiverVw,
vtxo: row.RequestVtxoVw,
})
}
rounds, err := readRoundRows(rvs)
rounds, err := rowsToRounds(rvs)
if err != nil {
return nil, err
}
@@ -239,18 +239,18 @@ func (r *roundRepository) GetRoundWithTxid(ctx context.Context, txid string) (*d
return nil, err
}
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows))
rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{
rvs = append(rvs, combinedRow{
round: row.Round,
payment: row.RoundPaymentVw,
request: row.RoundRequestVw,
tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw,
vtxo: row.PaymentVtxoVw,
receiver: row.RequestReceiverVw,
vtxo: row.RequestVtxoVw,
})
}
rounds, err := readRoundRows(rvs)
rounds, err := rowsToRounds(rvs)
if err != nil {
return nil, err
}
@@ -268,18 +268,18 @@ func (r *roundRepository) GetSweepableRounds(ctx context.Context) ([]domain.Roun
return nil, err
}
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows))
rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{
rvs = append(rvs, combinedRow{
round: row.Round,
payment: row.RoundPaymentVw,
request: row.RoundRequestVw,
tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw,
vtxo: row.PaymentVtxoVw,
receiver: row.RequestReceiverVw,
vtxo: row.RequestVtxoVw,
})
}
rounds, err := readRoundRows(rvs)
rounds, err := rowsToRounds(rvs)
if err != nil {
return nil, err
}
@@ -299,18 +299,18 @@ func (r *roundRepository) GetSweptRounds(ctx context.Context) ([]domain.Round, e
return nil, err
}
rvs := make([]roundPaymentTxReceiverVtxoRow, 0, len(rows))
rvs := make([]combinedRow, 0, len(rows))
for _, row := range rows {
rvs = append(rvs, roundPaymentTxReceiverVtxoRow{
rvs = append(rvs, combinedRow{
round: row.Round,
payment: row.RoundPaymentVw,
request: row.RoundRequestVw,
tx: row.RoundTxVw,
receiver: row.PaymentReceiverVw,
vtxo: row.PaymentVtxoVw,
receiver: row.RequestReceiverVw,
vtxo: row.RequestVtxoVw,
})
}
rounds, err := readRoundRows(rvs)
rounds, err := rowsToRounds(rvs)
if err != nil {
return nil, err
}
@@ -324,23 +324,23 @@ func (r *roundRepository) GetSweptRounds(ctx context.Context) ([]domain.Round, e
return res, nil
}
func rowToReceiver(row queries.PaymentReceiverVw) domain.Receiver {
func rowToReceiver(row queries.RequestReceiverVw) domain.Receiver {
return domain.Receiver{
Amount: uint64(row.Amount.Int64),
Pubkey: row.Pubkey.String,
PubKey: row.Pubkey.String,
OnchainAddress: row.OnchainAddress.String,
}
}
type roundPaymentTxReceiverVtxoRow struct {
type combinedRow struct {
round queries.Round
payment queries.RoundPaymentVw
request queries.RoundRequestVw
tx queries.RoundTxVw
receiver queries.PaymentReceiverVw
vtxo queries.PaymentVtxoVw
receiver queries.RequestReceiverVw
vtxo queries.RequestVtxoVw
}
func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error) {
func rowsToRounds(rows []combinedRow) ([]*domain.Round, error) {
rounds := make(map[string]*domain.Round)
for _, v := range rows {
@@ -364,35 +364,34 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
DustAmount: uint64(v.round.DustAmount),
Version: uint(v.round.Version),
Swept: v.round.Swept,
Payments: make(map[string]domain.Payment),
TxRequests: make(map[string]domain.TxRequest),
}
}
if v.payment.ID.Valid {
payment, ok := round.Payments[v.payment.ID.String]
if v.request.ID.Valid {
request, ok := round.TxRequests[v.request.ID.String]
if !ok {
payment = domain.Payment{
Id: v.payment.ID.String,
request = domain.TxRequest{
Id: v.request.ID.String,
Inputs: make([]domain.Vtxo, 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 {
payment, ok = round.Payments[v.vtxo.PaymentID.String]
if v.vtxo.RequestID.Valid {
request, ok = round.TxRequests[v.vtxo.RequestID.String]
if !ok {
payment = domain.Payment{
Id: v.vtxo.PaymentID.String,
request = domain.TxRequest{
Id: v.vtxo.RequestID.String,
Inputs: make([]domain.Vtxo, 0),
Receivers: make([]domain.Receiver, 0),
}
}
vtxo := rowToPaymentVtxoVw(v.vtxo)
vtxo := combinedRowToVtxo(v.vtxo)
found := false
for _, v := range payment.Inputs {
for _, v := range request.Inputs {
if vtxo.Txid == v.Txid && vtxo.VOut == v.VOut {
found = true
break
@@ -400,16 +399,16 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
}
if !found {
payment.Inputs = append(payment.Inputs, rowToPaymentVtxoVw(v.vtxo))
round.Payments[v.vtxo.PaymentID.String] = payment
request.Inputs = append(request.Inputs, combinedRowToVtxo(v.vtxo))
round.TxRequests[v.vtxo.RequestID.String] = request
}
}
if v.receiver.PaymentID.Valid {
payment, ok = round.Payments[v.receiver.PaymentID.String]
if v.receiver.RequestID.Valid {
request, ok = round.TxRequests[v.receiver.RequestID.String]
if !ok {
payment = domain.Payment{
Id: v.receiver.PaymentID.String,
request = domain.TxRequest{
Id: v.receiver.RequestID.String,
Inputs: make([]domain.Vtxo, 0),
Receivers: make([]domain.Receiver, 0),
}
@@ -418,17 +417,17 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
rcv := rowToReceiver(v.receiver)
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 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
break
}
}
}
if !found {
payment.Receivers = append(payment.Receivers, rcv)
round.Payments[v.receiver.PaymentID.String] = payment
request.Receivers = append(request.Receivers, rcv)
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
case "tree":
level := v.tx.TreeLevel
round.CongestionTree = extendArray(round.CongestionTree, int(level.Int64))
round.CongestionTree[int(level.Int64)] = extendArray(round.CongestionTree[int(level.Int64)], int(position.Int64))
if round.CongestionTree[int(level.Int64)][position.Int64] == (tree.Node{}) {
round.CongestionTree[int(level.Int64)][position.Int64] = tree.Node{
round.VtxoTree = extendArray(round.VtxoTree, int(level.Int64))
round.VtxoTree[int(level.Int64)] = extendArray(round.VtxoTree[int(level.Int64)], int(position.Int64))
if round.VtxoTree[int(level.Int64)][position.Int64] == (tree.Node{}) {
round.VtxoTree[int(level.Int64)][position.Int64] = tree.Node{
Tx: v.tx.Tx.String,
Txid: v.tx.Txid.String,
ParentTxid: v.tx.ParentTxid.String,
@@ -469,15 +468,15 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error
return result, nil
}
func rowToPaymentVtxoVw(row queries.PaymentVtxoVw) domain.Vtxo {
func combinedRowToVtxo(row queries.RequestVtxoVw) domain.Vtxo {
return domain.Vtxo{
VtxoKey: domain.VtxoKey{
Txid: row.Txid.String,
VOut: uint32(row.Vout.Int64),
},
Amount: uint64(row.Amount.Int64),
Pubkey: row.Pubkey.String,
RoundTxid: row.PoolTx.String,
PubKey: row.Pubkey.String,
RoundTxid: row.RoundTx.String,
SpentBy: row.SpentBy.String,
Spent: row.Spent.Bool,
Redeemed: row.Redeemed.Bool,

View File

@@ -39,41 +39,36 @@ type Note struct {
ID int64
}
type Payment struct {
ID string
RoundID string
type Receiver struct {
RequestID string
Pubkey sql.NullString
OnchainAddress sql.NullString
Amount int64
}
type PaymentReceiverVw struct {
PaymentID sql.NullString
type RequestReceiverVw struct {
RequestID sql.NullString
Pubkey sql.NullString
OnchainAddress sql.NullString
Amount sql.NullInt64
}
type PaymentVtxoVw struct {
type RequestVtxoVw struct {
Txid sql.NullString
Vout sql.NullInt64
Pubkey sql.NullString
Amount sql.NullInt64
PoolTx sql.NullString
RoundTx sql.NullString
SpentBy sql.NullString
Spent sql.NullBool
Redeemed sql.NullBool
Swept sql.NullBool
ExpireAt sql.NullInt64
CreatedAt sql.NullInt64
PaymentID sql.NullString
RequestID sql.NullString
RedeemTx sql.NullString
}
type Receiver struct {
PaymentID string
Pubkey sql.NullString
OnchainAddress sql.NullString
Amount int64
}
type Round struct {
ID string
StartingTimestamp int64
@@ -89,7 +84,7 @@ type Round struct {
Swept bool
}
type RoundPaymentVw struct {
type RoundRequestVw struct {
ID sql.NullString
RoundID sql.NullString
}
@@ -118,18 +113,23 @@ type Tx struct {
IsLeaf sql.NullBool
}
type TxRequest struct {
ID string
RoundID string
}
type Vtxo struct {
Txid string
Vout int64
Pubkey string
Amount int64
PoolTx string
RoundTx string
SpentBy string
Spent bool
Redeemed bool
Swept bool
ExpireAt int64
CreatedAt int64
PaymentID sql.NullString
RequestID 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
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
`
@@ -213,14 +213,14 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
&i.Vtxo.Vout,
&i.Vtxo.Pubkey,
&i.Vtxo.Amount,
&i.Vtxo.PoolTx,
&i.Vtxo.RoundTx,
&i.Vtxo.SpentBy,
&i.Vtxo.Spent,
&i.Vtxo.Redeemed,
&i.Vtxo.Swept,
&i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RequestID,
&i.Vtxo.RedeemTx,
); err != nil {
return nil, err
@@ -237,7 +237,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
}
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 = ?
`
@@ -259,14 +259,14 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey s
&i.Vtxo.Vout,
&i.Vtxo.Pubkey,
&i.Vtxo.Amount,
&i.Vtxo.PoolTx,
&i.Vtxo.RoundTx,
&i.Vtxo.SpentBy,
&i.Vtxo.Spent,
&i.Vtxo.Redeemed,
&i.Vtxo.Swept,
&i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RequestID,
&i.Vtxo.RedeemTx,
); err != nil {
return nil, err
@@ -343,24 +343,24 @@ func (q *Queries) SelectRoundIdsInRange(ctx context.Context, arg SelectRoundIdsI
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,
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,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_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_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
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
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.id = ?
`
type SelectRoundWithRoundIdRow struct {
Round Round
RoundPaymentVw RoundPaymentVw
RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw
PaymentVtxoVw PaymentVtxoVw
RequestReceiverVw RequestReceiverVw
RequestVtxoVw RequestVtxoVw
}
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.Version,
&i.Round.Swept,
&i.RoundPaymentVw.ID,
&i.RoundPaymentVw.RoundID,
&i.RoundRequestVw.ID,
&i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID,
&i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID,
@@ -396,23 +396,23 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
&i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID,
&i.PaymentReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount,
&i.PaymentVtxoVw.Txid,
&i.PaymentVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx,
&i.PaymentVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.RequestReceiverVw.RequestID,
&i.RequestReceiverVw.Pubkey,
&i.RequestReceiverVw.OnchainAddress,
&i.RequestReceiverVw.Amount,
&i.RequestVtxoVw.Txid,
&i.RequestVtxoVw.Vout,
&i.RequestVtxoVw.Pubkey,
&i.RequestVtxoVw.Amount,
&i.RequestVtxoVw.RoundTx,
&i.RequestVtxoVw.SpentBy,
&i.RequestVtxoVw.Spent,
&i.RequestVtxoVw.Redeemed,
&i.RequestVtxoVw.Swept,
&i.RequestVtxoVw.ExpireAt,
&i.RequestVtxoVw.CreatedAt,
&i.RequestVtxoVw.RequestID,
&i.RequestVtxoVw.RedeemTx,
); err != nil {
return nil, err
}
@@ -429,24 +429,24 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
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,
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,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_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_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
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
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.txid = ?
`
type SelectRoundWithRoundTxIdRow struct {
Round Round
RoundPaymentVw RoundPaymentVw
RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw
PaymentVtxoVw PaymentVtxoVw
RequestReceiverVw RequestReceiverVw
RequestVtxoVw RequestVtxoVw
}
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.Version,
&i.Round.Swept,
&i.RoundPaymentVw.ID,
&i.RoundPaymentVw.RoundID,
&i.RoundRequestVw.ID,
&i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID,
&i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID,
@@ -482,23 +482,23 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
&i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID,
&i.PaymentReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount,
&i.PaymentVtxoVw.Txid,
&i.PaymentVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx,
&i.PaymentVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.RequestReceiverVw.RequestID,
&i.RequestReceiverVw.Pubkey,
&i.RequestReceiverVw.OnchainAddress,
&i.RequestReceiverVw.Amount,
&i.RequestVtxoVw.Txid,
&i.RequestVtxoVw.Vout,
&i.RequestVtxoVw.Pubkey,
&i.RequestVtxoVw.Amount,
&i.RequestVtxoVw.RoundTx,
&i.RequestVtxoVw.SpentBy,
&i.RequestVtxoVw.Spent,
&i.RequestVtxoVw.Redeemed,
&i.RequestVtxoVw.Swept,
&i.RequestVtxoVw.ExpireAt,
&i.RequestVtxoVw.CreatedAt,
&i.RequestVtxoVw.RequestID,
&i.RequestVtxoVw.RedeemTx,
); err != nil {
return nil, err
}
@@ -515,24 +515,24 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
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,
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,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_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_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
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
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_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
`
type SelectSweepableRoundsRow struct {
Round Round
RoundPaymentVw RoundPaymentVw
RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw
PaymentVtxoVw PaymentVtxoVw
RequestReceiverVw RequestReceiverVw
RequestVtxoVw RequestVtxoVw
}
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.Version,
&i.Round.Swept,
&i.RoundPaymentVw.ID,
&i.RoundPaymentVw.RoundID,
&i.RoundRequestVw.ID,
&i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID,
&i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID,
@@ -568,23 +568,23 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
&i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID,
&i.PaymentReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount,
&i.PaymentVtxoVw.Txid,
&i.PaymentVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx,
&i.PaymentVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.RequestReceiverVw.RequestID,
&i.RequestReceiverVw.Pubkey,
&i.RequestReceiverVw.OnchainAddress,
&i.RequestReceiverVw.Amount,
&i.RequestVtxoVw.Txid,
&i.RequestVtxoVw.Vout,
&i.RequestVtxoVw.Pubkey,
&i.RequestVtxoVw.Amount,
&i.RequestVtxoVw.RoundTx,
&i.RequestVtxoVw.SpentBy,
&i.RequestVtxoVw.Spent,
&i.RequestVtxoVw.Redeemed,
&i.RequestVtxoVw.Swept,
&i.RequestVtxoVw.ExpireAt,
&i.RequestVtxoVw.CreatedAt,
&i.RequestVtxoVw.RequestID,
&i.RequestVtxoVw.RedeemTx,
); err != nil {
return nil, err
}
@@ -600,7 +600,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
}
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
`
@@ -622,14 +622,14 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
&i.Vtxo.Vout,
&i.Vtxo.Pubkey,
&i.Vtxo.Amount,
&i.Vtxo.PoolTx,
&i.Vtxo.RoundTx,
&i.Vtxo.SpentBy,
&i.Vtxo.Spent,
&i.Vtxo.Redeemed,
&i.Vtxo.Swept,
&i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RequestID,
&i.Vtxo.RedeemTx,
); err != nil {
return nil, err
@@ -647,24 +647,24 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
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,
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,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_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_receiver_vw.request_id, request_receiver_vw.pubkey, request_receiver_vw.onchain_address, request_receiver_vw.amount,
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
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_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 <> ''
`
type SelectSweptRoundsRow struct {
Round Round
RoundPaymentVw RoundPaymentVw
RoundRequestVw RoundRequestVw
RoundTxVw RoundTxVw
PaymentReceiverVw PaymentReceiverVw
PaymentVtxoVw PaymentVtxoVw
RequestReceiverVw RequestReceiverVw
RequestVtxoVw RequestVtxoVw
}
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.Version,
&i.Round.Swept,
&i.RoundPaymentVw.ID,
&i.RoundPaymentVw.RoundID,
&i.RoundRequestVw.ID,
&i.RoundRequestVw.RoundID,
&i.RoundTxVw.ID,
&i.RoundTxVw.Tx,
&i.RoundTxVw.RoundID,
@@ -700,23 +700,23 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
&i.RoundTxVw.TreeLevel,
&i.RoundTxVw.ParentTxid,
&i.RoundTxVw.IsLeaf,
&i.PaymentReceiverVw.PaymentID,
&i.PaymentReceiverVw.Pubkey,
&i.PaymentReceiverVw.OnchainAddress,
&i.PaymentReceiverVw.Amount,
&i.PaymentVtxoVw.Txid,
&i.PaymentVtxoVw.Vout,
&i.PaymentVtxoVw.Pubkey,
&i.PaymentVtxoVw.Amount,
&i.PaymentVtxoVw.PoolTx,
&i.PaymentVtxoVw.SpentBy,
&i.PaymentVtxoVw.Spent,
&i.PaymentVtxoVw.Redeemed,
&i.PaymentVtxoVw.Swept,
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.CreatedAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.RequestReceiverVw.RequestID,
&i.RequestReceiverVw.Pubkey,
&i.RequestReceiverVw.OnchainAddress,
&i.RequestReceiverVw.Amount,
&i.RequestVtxoVw.Txid,
&i.RequestVtxoVw.Vout,
&i.RequestVtxoVw.Pubkey,
&i.RequestVtxoVw.Amount,
&i.RequestVtxoVw.RoundTx,
&i.RequestVtxoVw.SpentBy,
&i.RequestVtxoVw.Spent,
&i.RequestVtxoVw.Redeemed,
&i.RequestVtxoVw.Swept,
&i.RequestVtxoVw.ExpireAt,
&i.RequestVtxoVw.CreatedAt,
&i.RequestVtxoVw.RequestID,
&i.RequestVtxoVw.RedeemTx,
); err != nil {
return nil, err
}
@@ -732,7 +732,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
}
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 = ?
`
@@ -753,50 +753,50 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
&i.Vtxo.Vout,
&i.Vtxo.Pubkey,
&i.Vtxo.Amount,
&i.Vtxo.PoolTx,
&i.Vtxo.RoundTx,
&i.Vtxo.SpentBy,
&i.Vtxo.Spent,
&i.Vtxo.Redeemed,
&i.Vtxo.Swept,
&i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RequestID,
&i.Vtxo.RedeemTx,
)
return i, err
}
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :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
WHERE pool_tx = ?
const selectVtxosByRoundTxid = `-- name: SelectVtxosByRoundTxid :many
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 round_tx = ?
`
type SelectVtxosByPoolTxidRow struct {
type SelectVtxosByRoundTxidRow struct {
Vtxo Vtxo
}
func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]SelectVtxosByPoolTxidRow, error) {
rows, err := q.db.QueryContext(ctx, selectVtxosByPoolTxid, poolTx)
func (q *Queries) SelectVtxosByRoundTxid(ctx context.Context, roundTx string) ([]SelectVtxosByRoundTxidRow, error) {
rows, err := q.db.QueryContext(ctx, selectVtxosByRoundTxid, roundTx)
if err != nil {
return nil, err
}
defer rows.Close()
var items []SelectVtxosByPoolTxidRow
var items []SelectVtxosByRoundTxidRow
for rows.Next() {
var i SelectVtxosByPoolTxidRow
var i SelectVtxosByRoundTxidRow
if err := rows.Scan(
&i.Vtxo.Txid,
&i.Vtxo.Vout,
&i.Vtxo.Pubkey,
&i.Vtxo.Amount,
&i.Vtxo.PoolTx,
&i.Vtxo.RoundTx,
&i.Vtxo.SpentBy,
&i.Vtxo.Spent,
&i.Vtxo.Redeemed,
&i.Vtxo.Swept,
&i.Vtxo.ExpireAt,
&i.Vtxo.CreatedAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RequestID,
&i.Vtxo.RedeemTx,
); err != nil {
return nil, err
@@ -868,18 +868,18 @@ func (q *Queries) UpdateVtxoExpireAt(ctx context.Context, arg UpdateVtxoExpireAt
return err
}
const updateVtxoPaymentId = `-- name: UpdateVtxoPaymentId :exec
UPDATE vtxo SET payment_id = ? WHERE txid = ? AND vout = ?
const updateVtxoRequestId = `-- name: UpdateVtxoRequestId :exec
UPDATE vtxo SET request_id = ? WHERE txid = ? AND vout = ?
`
type UpdateVtxoPaymentIdParams struct {
PaymentID sql.NullString
type UpdateVtxoRequestIdParams struct {
RequestID sql.NullString
Txid string
Vout int64
}
func (q *Queries) UpdateVtxoPaymentId(ctx context.Context, arg UpdateVtxoPaymentIdParams) error {
_, err := q.db.ExecContext(ctx, updateVtxoPaymentId, arg.PaymentID, arg.Txid, arg.Vout)
func (q *Queries) UpdateVtxoRequestId(ctx context.Context, arg UpdateVtxoRequestIdParams) error {
_, err := q.db.ExecContext(ctx, updateVtxoRequestId, arg.RequestID, arg.Txid, arg.Vout)
return err
}
@@ -916,31 +916,16 @@ func (q *Queries) UpsertEntityVtxo(ctx context.Context, arg UpsertEntityVtxoPara
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
INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET
INSERT INTO receiver (request_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
ON CONFLICT(request_id, pubkey, onchain_address) DO UPDATE SET
amount = EXCLUDED.amount,
pubkey = EXCLUDED.pubkey,
onchain_address = EXCLUDED.onchain_address
`
type UpsertReceiverParams struct {
PaymentID string
RequestID string
Pubkey sql.NullString
OnchainAddress sql.NullString
Amount int64
@@ -948,7 +933,7 @@ type UpsertReceiverParams struct {
func (q *Queries) UpsertReceiver(ctx context.Context, arg UpsertReceiverParams) error {
_, err := q.db.ExecContext(ctx, upsertReceiver,
arg.PaymentID,
arg.RequestID,
arg.Pubkey,
arg.OnchainAddress,
arg.Amount,
@@ -1057,12 +1042,27 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
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
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
pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx,
round_tx = EXCLUDED.round_tx,
spent_by = EXCLUDED.spent_by,
spent = EXCLUDED.spent,
redeemed = EXCLUDED.redeemed,
@@ -1077,7 +1077,7 @@ type UpsertVtxoParams struct {
Vout int64
Pubkey string
Amount int64
PoolTx string
RoundTx string
SpentBy string
Spent bool
Redeemed bool
@@ -1093,7 +1093,7 @@ func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
arg.Vout,
arg.Pubkey,
arg.Amount,
arg.PoolTx,
arg.RoundTx,
arg.SpentBy,
arg.Spent,
arg.Redeemed,

View File

@@ -39,70 +39,70 @@ ON CONFLICT(id) DO UPDATE SET
version = EXCLUDED.version,
swept = EXCLUDED.swept;
-- name: UpsertPayment :exec
INSERT INTO payment (id, round_id) VALUES (?, ?)
-- name: UpsertTxRequest :exec
INSERT INTO tx_request (id, round_id) VALUES (?, ?)
ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id;
-- name: UpsertReceiver :exec
INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET
INSERT INTO receiver (request_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?)
ON CONFLICT(request_id, pubkey, onchain_address) DO UPDATE SET
amount = EXCLUDED.amount,
pubkey = EXCLUDED.pubkey,
onchain_address = EXCLUDED.onchain_address;
-- name: UpdateVtxoPaymentId :exec
UPDATE vtxo SET payment_id = ? WHERE txid = ? AND vout = ?;
-- name: UpdateVtxoRequestId :exec
UPDATE vtxo SET request_id = ? WHERE txid = ? AND vout = ?;
-- name: SelectRoundWithRoundId :many
SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw),
sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw),
sqlc.embed(payment_vtxo_vw)
sqlc.embed(request_receiver_vw),
sqlc.embed(request_vtxo_vw)
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.id = ?;
-- name: SelectRoundWithRoundTxId :many
SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw),
sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw),
sqlc.embed(payment_vtxo_vw)
sqlc.embed(request_receiver_vw),
sqlc.embed(request_vtxo_vw)
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_id
LEFT OUTER JOIN request_vtxo_vw ON round_request_vw.id=request_vtxo_vw.request_id
WHERE round.txid = ?;
-- name: SelectSweepableRounds :many
SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw),
sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw),
sqlc.embed(payment_vtxo_vw)
sqlc.embed(request_receiver_vw),
sqlc.embed(request_vtxo_vw)
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_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;
-- name: SelectSweptRounds :many
SELECT sqlc.embed(round),
sqlc.embed(round_payment_vw),
sqlc.embed(round_request_vw),
sqlc.embed(round_tx_vw),
sqlc.embed(payment_receiver_vw),
sqlc.embed(payment_vtxo_vw)
sqlc.embed(request_receiver_vw),
sqlc.embed(request_vtxo_vw)
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 payment_receiver_vw ON round_payment_vw.id=payment_receiver_vw.payment_id
LEFT OUTER JOIN payment_vtxo_vw ON round_payment_vw.id=payment_vtxo_vw.payment_id
LEFT OUTER JOIN request_receiver_vw ON round_request_vw.id=request_receiver_vw.request_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 <> '';
-- name: SelectRoundIdsInRange :many
@@ -112,11 +112,11 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
SELECT id FROM round;
-- 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
pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx,
round_tx = EXCLUDED.round_tx,
spent_by = EXCLUDED.spent_by,
spent = EXCLUDED.spent,
redeemed = EXCLUDED.redeemed,
@@ -141,9 +141,9 @@ WHERE redeemed = false AND pubkey = ?;
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE txid = ? AND vout = ?;
-- name: SelectVtxosByPoolTxid :many
-- name: SelectVtxosByRoundTxid :many
SELECT sqlc.embed(vtxo) FROM vtxo
WHERE pool_tx = ?;
WHERE round_tx = ?;
-- name: MarkVtxoAsRedeemed :exec
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{
Txid: vtxo.Txid,
Vout: int64(vtxo.VOut),
Pubkey: vtxo.Pubkey,
Pubkey: vtxo.PubKey,
Amount: int64(vtxo.Amount),
PoolTx: vtxo.RoundTxid,
RoundTx: vtxo.RoundTxid,
SpentBy: vtxo.SpentBy,
Spent: vtxo.Spent,
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) {
res, err := v.querier.SelectVtxosByPoolTxid(ctx, txid)
res, err := v.querier.SelectVtxosByRoundTxid(ctx, txid)
if err != nil {
return nil, err
}
@@ -251,8 +251,8 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
VOut: uint32(row.Vout),
},
Amount: uint64(row.Amount),
Pubkey: row.Pubkey,
RoundTxid: row.PoolTx,
PubKey: row.Pubkey,
RoundTxid: row.RoundTx,
SpentBy: row.SpentBy,
Spent: row.Spent,
Redeemed: row.Redeemed,

View File

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

View File

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

View File

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

View File

@@ -17,8 +17,8 @@ import (
"github.com/vulpemventures/go-elements/transaction"
)
func p2wpkhScript(publicKey *secp256k1.PublicKey, net *network.Network) ([]byte, error) {
payment := payment.FromPublicKey(publicKey, net, nil)
func p2wpkhScript(pubkey *secp256k1.PublicKey, net *network.Network) ([]byte, error) {
payment := payment.FromPublicKey(pubkey, net, nil)
addr, err := payment.WitnessPubKeyHash()
if err != nil {
return nil, err
@@ -46,11 +46,11 @@ func getPsetId(pset *psetv2.Pset) (string, error) {
}
func getOnchainOutputs(
payments []domain.Payment, net *network.Network,
requests []domain.TxRequest, net *network.Network,
) ([]psetv2.OutputArgs, error) {
outputs := make([]psetv2.OutputArgs, 0)
for _, payment := range payments {
for _, receiver := range payment.Receivers {
for _, request := range requests {
for _, receiver := range request.Receivers {
if receiver.IsOnchain() {
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
if err != nil {
@@ -69,14 +69,14 @@ func getOnchainOutputs(
}
func getOutputVtxosLeaves(
payments []domain.Payment,
requests []domain.TxRequest,
) ([]tree.VtxoLeaf, error) {
receivers := make([]tree.VtxoLeaf, 0)
for _, payment := range payments {
for _, receiver := range payment.Receivers {
for _, request := range requests {
for _, receiver := range request.Receivers {
if !receiver.IsOnchain() {
receivers = append(receivers, tree.VtxoLeaf{
Pubkey: receiver.Pubkey,
PubKey: receiver.PubKey,
Amount: receiver.Amount,
})
}
@@ -104,10 +104,10 @@ func toWitnessUtxo(in ports.TxInput) (*transaction.TxOutput, error) {
return transaction.NewTxOutput(assetBytes, valueBytes, scriptBytes), nil
}
func countSpentVtxos(payments []domain.Payment) uint64 {
func countSpentVtxos(requests []domain.TxRequest) uint64 {
var sum uint64
for _, payment := range payments {
sum += uint64(len(payment.Inputs))
for _, request := range requests {
sum += uint64(len(request.Inputs))
}
return sum
}
@@ -144,9 +144,9 @@ func addInputs(
return nil
}
func isOnchainOnly(payments []domain.Payment) bool {
for _, p := range payments {
for _, r := range p.Receivers {
func isOnchainOnly(requests []domain.TxRequest) bool {
for _, request := range requests {
for _, r := range request.Receivers {
if !r.IsOnchain() {
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) {
txid := ptx.UnsignedTx.TxID()
aspPublicKey, err := b.wallet.GetPubkey(context.Background())
serverPubkey, err := b.wallet.GetPubkey(context.Background())
if err != nil {
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
keys[hex.EncodeToString(schnorr.SerializePubKey(aspPublicKey))] = true
// we don't need to check if server signed
keys[hex.EncodeToString(schnorr.SerializePubKey(serverPubkey))] = true
if len(tapLeaf.ControlBlock) == 0 {
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(
aspPubkey *secp256k1.PublicKey,
payments []domain.Payment,
serverPubkey *secp256k1.PublicKey,
requests []domain.TxRequest,
boardingInputs []ports.BoardingInput,
sweptRounds []domain.Round,
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 sharedOutputAmount int64
@@ -484,7 +484,7 @@ func (b *txBuilder) BuildRoundTx(
return "", nil, "", nil, fmt.Errorf("missing cosigners")
}
receivers, err := getOutputVtxosLeaves(payments)
receivers, err := getOutputVtxosLeaves(requests)
if err != nil {
return "", nil, "", nil, err
}
@@ -494,9 +494,9 @@ func (b *txBuilder) BuildRoundTx(
return
}
if !isOnchainOnly(payments) {
if !isOnchainOnly(requests) {
sharedOutputScript, sharedOutputAmount, err = bitcointree.CraftSharedOutput(
cosigners, aspPubkey, receivers, feeAmount, b.roundLifetime,
cosigners, serverPubkey, receivers, feeAmount, b.roundLifetime,
)
if err != nil {
return
@@ -509,7 +509,7 @@ func (b *txBuilder) BuildRoundTx(
}
ptx, err := b.createRoundTx(
sharedOutputAmount, sharedOutputScript, payments, boardingInputs, connectorAddress, sweptRounds,
sharedOutputAmount, sharedOutputScript, requests, boardingInputs, connectorAddress, sweptRounds,
)
if err != nil {
return
@@ -520,21 +520,21 @@ func (b *txBuilder) BuildRoundTx(
return
}
if !isOnchainOnly(payments) {
if !isOnchainOnly(requests) {
initialOutpoint := &wire.OutPoint{
Hash: ptx.UnsignedTx.TxHash(),
Index: 0,
}
congestionTree, err = bitcointree.CraftCongestionTree(
initialOutpoint, cosigners, aspPubkey, receivers, feeAmount, b.roundLifetime,
vtxoTree, err = bitcointree.BuildVtxoTree(
initialOutpoint, cosigners, serverPubkey, receivers, feeAmount, b.roundLifetime,
)
if err != nil {
return "", nil, "", nil, err
}
}
if countSpentVtxos(payments) <= 0 {
if countSpentVtxos(requests) <= 0 {
return
}
@@ -553,7 +553,7 @@ func (b *txBuilder) BuildRoundTx(
return "", nil, "", nil, err
}
connectorsPsbts, err := b.createConnectors(roundTx, payments, connectorPkScript, minRelayFeeConnectorTx)
connectorsPsbts, err := b.createConnectors(roundTx, requests, connectorPkScript, minRelayFeeConnectorTx)
if err != nil {
return "", nil, "", nil, err
}
@@ -566,7 +566,7 @@ func (b *txBuilder) BuildRoundTx(
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) {
@@ -611,12 +611,12 @@ func (b *txBuilder) GetSweepInput(node tree.Node) (lifetime int64, sweepInput po
return lifetime, sweepInput, nil
}
func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) ([]tree.Node, error) {
allLeaves := congestionTree.Leaves()
func (b *txBuilder) FindLeaves(vtxoTree tree.VtxoTree, fromtxid string, vout uint32) ([]tree.Node, error) {
allLeaves := vtxoTree.Leaves()
foundLeaves := make([]tree.Node, 0)
for _, leaf := range allLeaves {
branch, err := congestionTree.Branch(leaf.Txid)
branch, err := vtxoTree.Branch(leaf.Txid)
if err != nil {
return nil, err
}
@@ -647,7 +647,7 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri
func (b *txBuilder) createRoundTx(
sharedOutputAmount int64,
sharedOutputScript []byte,
payments []domain.Payment,
requests []domain.TxRequest,
boardingInputs []ports.BoardingInput,
connectorAddress string,
sweptRounds []domain.Round,
@@ -674,7 +674,7 @@ func (b *txBuilder) createRoundTx(
connectorAmount := dustLimit
nbOfInputs := countSpentVtxos(payments)
nbOfInputs := countSpentVtxos(requests)
connectorsAmount := (connectorAmount + connectorMinRelayFee) * nbOfInputs
if nbOfInputs > 1 {
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 {
return nil, err
}
@@ -1021,9 +1021,9 @@ func (b *txBuilder) VerifyAndCombinePartialTx(dest string, src string) (string,
}
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) {
partialTx, err := psbt.NewFromRawBytes(strings.NewReader(poolTx), true)
partialTx, err := psbt.NewFromRawBytes(strings.NewReader(roundTx), true)
if err != nil {
return nil, err
}
@@ -1038,7 +1038,7 @@ func (b *txBuilder) createConnectors(
Value: int64(connectorAmount),
}
numberOfConnectors := countSpentVtxos(payments)
numberOfConnectors := countSpentVtxos(requests)
previousInput := &wire.OutPoint{
Hash: partialTx.UnsignedTx.TxHash(),

View File

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

View File

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

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