diff --git a/api-spec/openapi/swagger/ark/v1/service.swagger.json b/api-spec/openapi/swagger/ark/v1/service.swagger.json index 36775ae..9966c68 100644 --- a/api-spec/openapi/swagger/ark/v1/service.swagger.json +++ b/api-spec/openapi/swagger/ark/v1/service.swagger.json @@ -506,6 +506,17 @@ } } }, + "v1AsyncPaymentInput": { + "type": "object", + "properties": { + "input": { + "$ref": "#/definitions/v1Input" + }, + "forfeitLeafHash": { + "type": "string" + } + } + }, "v1CompletePaymentRequest": { "type": "object", "properties": { @@ -524,7 +535,7 @@ "type": "array", "items": { "type": "object", - "$ref": "#/definitions/v1Input" + "$ref": "#/definitions/v1AsyncPaymentInput" } }, "outputs": { @@ -717,9 +728,6 @@ "type": "string", "format": "uint64", "description": "Amount to send in satoshis." - }, - "descriptor": { - "type": "string" } } }, @@ -1046,9 +1054,6 @@ "outpoint": { "$ref": "#/definitions/v1Outpoint" }, - "descriptor": { - "type": "string" - }, "spent": { "type": "boolean" }, @@ -1074,6 +1079,9 @@ "amount": { "type": "string", "format": "uint64" + }, + "pubkey": { + "type": "string" } } } diff --git a/api-spec/protobuf/ark/v1/service.proto b/api-spec/protobuf/ark/v1/service.proto index fae41ef..b924eb6 100755 --- a/api-spec/protobuf/ark/v1/service.proto +++ b/api-spec/protobuf/ark/v1/service.proto @@ -187,8 +187,13 @@ message PingResponse { /* Async Payment API messages */ +message AsyncPaymentInput { + Input input = 1; + string forfeit_leaf_hash = 2; +} + message CreatePaymentRequest { - repeated Input inputs = 1; + repeated AsyncPaymentInput inputs = 1; repeated Output outputs = 2; } message CreatePaymentResponse { @@ -290,7 +295,6 @@ message Input { message Output { string address = 1; // onchain or off-chain uint64 amount = 2; // Amount to send in satoshis. - string descriptor = 3; } message Tree { @@ -309,18 +313,17 @@ message Node { message Vtxo { Outpoint outpoint = 1; - string descriptor = 2; - bool spent = 3; - string round_txid = 4; - string spent_by = 5; - int64 expire_at = 6; - bool swept = 7; - bool pending = 8; - string redeem_tx = 9; - uint64 amount = 10; + bool spent = 2; + string round_txid = 3; + string spent_by = 4; + int64 expire_at = 5; + bool swept = 6; + bool pending = 7; + string redeem_tx = 8; + uint64 amount = 9; + string pubkey = 10; } - message GetTransactionsStreamRequest {} message GetTransactionsStreamResponse { oneof tx { diff --git a/api-spec/protobuf/gen/ark/v1/service.pb.go b/api-spec/protobuf/gen/ark/v1/service.pb.go index 9e7bbc9..10e6dcc 100644 --- a/api-spec/protobuf/gen/ark/v1/service.pb.go +++ b/api-spec/protobuf/gen/ark/v1/service.pb.go @@ -1153,19 +1153,74 @@ func (*PingResponse_RoundSigning) isPingResponse_Event() {} func (*PingResponse_RoundSigningNoncesGenerated) isPingResponse_Event() {} +type AsyncPaymentInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Input *Input `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"` + ForfeitLeafHash string `protobuf:"bytes,2,opt,name=forfeit_leaf_hash,json=forfeitLeafHash,proto3" json:"forfeit_leaf_hash,omitempty"` +} + +func (x *AsyncPaymentInput) Reset() { + *x = AsyncPaymentInput{} + if protoimpl.UnsafeEnabled { + mi := &file_ark_v1_service_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AsyncPaymentInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AsyncPaymentInput) ProtoMessage() {} + +func (x *AsyncPaymentInput) ProtoReflect() protoreflect.Message { + mi := &file_ark_v1_service_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AsyncPaymentInput.ProtoReflect.Descriptor instead. +func (*AsyncPaymentInput) Descriptor() ([]byte, []int) { + return file_ark_v1_service_proto_rawDescGZIP(), []int{18} +} + +func (x *AsyncPaymentInput) GetInput() *Input { + if x != nil { + return x.Input + } + return nil +} + +func (x *AsyncPaymentInput) GetForfeitLeafHash() string { + if x != nil { + return x.ForfeitLeafHash + } + return "" +} + type CreatePaymentRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Inputs []*Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"` - Outputs []*Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"` + Inputs []*AsyncPaymentInput `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"` + Outputs []*Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"` } func (x *CreatePaymentRequest) Reset() { *x = CreatePaymentRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[18] + mi := &file_ark_v1_service_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1178,7 +1233,7 @@ func (x *CreatePaymentRequest) String() string { func (*CreatePaymentRequest) ProtoMessage() {} func (x *CreatePaymentRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[18] + mi := &file_ark_v1_service_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1191,10 +1246,10 @@ func (x *CreatePaymentRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreatePaymentRequest.ProtoReflect.Descriptor instead. func (*CreatePaymentRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{18} + return file_ark_v1_service_proto_rawDescGZIP(), []int{19} } -func (x *CreatePaymentRequest) GetInputs() []*Input { +func (x *CreatePaymentRequest) GetInputs() []*AsyncPaymentInput { if x != nil { return x.Inputs } @@ -1219,7 +1274,7 @@ type CreatePaymentResponse struct { func (x *CreatePaymentResponse) Reset() { *x = CreatePaymentResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[19] + mi := &file_ark_v1_service_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1232,7 +1287,7 @@ func (x *CreatePaymentResponse) String() string { func (*CreatePaymentResponse) ProtoMessage() {} func (x *CreatePaymentResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[19] + mi := &file_ark_v1_service_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1245,7 +1300,7 @@ func (x *CreatePaymentResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreatePaymentResponse.ProtoReflect.Descriptor instead. func (*CreatePaymentResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{19} + return file_ark_v1_service_proto_rawDescGZIP(), []int{20} } func (x *CreatePaymentResponse) GetSignedRedeemTx() string { @@ -1266,7 +1321,7 @@ type CompletePaymentRequest struct { func (x *CompletePaymentRequest) Reset() { *x = CompletePaymentRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[20] + mi := &file_ark_v1_service_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1279,7 +1334,7 @@ func (x *CompletePaymentRequest) String() string { func (*CompletePaymentRequest) ProtoMessage() {} func (x *CompletePaymentRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[20] + mi := &file_ark_v1_service_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1292,7 +1347,7 @@ func (x *CompletePaymentRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CompletePaymentRequest.ProtoReflect.Descriptor instead. func (*CompletePaymentRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{20} + return file_ark_v1_service_proto_rawDescGZIP(), []int{21} } func (x *CompletePaymentRequest) GetSignedRedeemTx() string { @@ -1311,7 +1366,7 @@ type CompletePaymentResponse struct { func (x *CompletePaymentResponse) Reset() { *x = CompletePaymentResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[21] + mi := &file_ark_v1_service_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1324,7 +1379,7 @@ func (x *CompletePaymentResponse) String() string { func (*CompletePaymentResponse) ProtoMessage() {} func (x *CompletePaymentResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[21] + mi := &file_ark_v1_service_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1337,7 +1392,7 @@ func (x *CompletePaymentResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CompletePaymentResponse.ProtoReflect.Descriptor instead. func (*CompletePaymentResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{21} + return file_ark_v1_service_proto_rawDescGZIP(), []int{22} } type GetRoundRequest struct { @@ -1351,7 +1406,7 @@ type GetRoundRequest struct { func (x *GetRoundRequest) Reset() { *x = GetRoundRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[22] + mi := &file_ark_v1_service_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1364,7 +1419,7 @@ func (x *GetRoundRequest) String() string { func (*GetRoundRequest) ProtoMessage() {} func (x *GetRoundRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[22] + mi := &file_ark_v1_service_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1377,7 +1432,7 @@ func (x *GetRoundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoundRequest.ProtoReflect.Descriptor instead. func (*GetRoundRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{22} + return file_ark_v1_service_proto_rawDescGZIP(), []int{23} } func (x *GetRoundRequest) GetTxid() string { @@ -1398,7 +1453,7 @@ type GetRoundResponse struct { func (x *GetRoundResponse) Reset() { *x = GetRoundResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[23] + mi := &file_ark_v1_service_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1411,7 +1466,7 @@ func (x *GetRoundResponse) String() string { func (*GetRoundResponse) ProtoMessage() {} func (x *GetRoundResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[23] + mi := &file_ark_v1_service_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1424,7 +1479,7 @@ func (x *GetRoundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoundResponse.ProtoReflect.Descriptor instead. func (*GetRoundResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{23} + return file_ark_v1_service_proto_rawDescGZIP(), []int{24} } func (x *GetRoundResponse) GetRound() *Round { @@ -1445,7 +1500,7 @@ type GetRoundByIdRequest struct { func (x *GetRoundByIdRequest) Reset() { *x = GetRoundByIdRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[24] + mi := &file_ark_v1_service_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1458,7 +1513,7 @@ func (x *GetRoundByIdRequest) String() string { func (*GetRoundByIdRequest) ProtoMessage() {} func (x *GetRoundByIdRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[24] + mi := &file_ark_v1_service_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1471,7 +1526,7 @@ func (x *GetRoundByIdRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoundByIdRequest.ProtoReflect.Descriptor instead. func (*GetRoundByIdRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{24} + return file_ark_v1_service_proto_rawDescGZIP(), []int{25} } func (x *GetRoundByIdRequest) GetId() string { @@ -1492,7 +1547,7 @@ type GetRoundByIdResponse struct { func (x *GetRoundByIdResponse) Reset() { *x = GetRoundByIdResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[25] + mi := &file_ark_v1_service_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1505,7 +1560,7 @@ func (x *GetRoundByIdResponse) String() string { func (*GetRoundByIdResponse) ProtoMessage() {} func (x *GetRoundByIdResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[25] + mi := &file_ark_v1_service_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1518,7 +1573,7 @@ func (x *GetRoundByIdResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoundByIdResponse.ProtoReflect.Descriptor instead. func (*GetRoundByIdResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{25} + return file_ark_v1_service_proto_rawDescGZIP(), []int{26} } func (x *GetRoundByIdResponse) GetRound() *Round { @@ -1539,7 +1594,7 @@ type ListVtxosRequest struct { func (x *ListVtxosRequest) Reset() { *x = ListVtxosRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[26] + mi := &file_ark_v1_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1552,7 +1607,7 @@ func (x *ListVtxosRequest) String() string { func (*ListVtxosRequest) ProtoMessage() {} func (x *ListVtxosRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[26] + mi := &file_ark_v1_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1565,7 +1620,7 @@ func (x *ListVtxosRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListVtxosRequest.ProtoReflect.Descriptor instead. func (*ListVtxosRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{26} + return file_ark_v1_service_proto_rawDescGZIP(), []int{27} } func (x *ListVtxosRequest) GetAddress() string { @@ -1587,7 +1642,7 @@ type ListVtxosResponse struct { func (x *ListVtxosResponse) Reset() { *x = ListVtxosResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[27] + mi := &file_ark_v1_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1600,7 +1655,7 @@ func (x *ListVtxosResponse) String() string { func (*ListVtxosResponse) ProtoMessage() {} func (x *ListVtxosResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[27] + mi := &file_ark_v1_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1613,7 +1668,7 @@ func (x *ListVtxosResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListVtxosResponse.ProtoReflect.Descriptor instead. func (*ListVtxosResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{27} + return file_ark_v1_service_proto_rawDescGZIP(), []int{28} } func (x *ListVtxosResponse) GetSpendableVtxos() []*Vtxo { @@ -1645,7 +1700,7 @@ type RoundFinalizationEvent struct { func (x *RoundFinalizationEvent) Reset() { *x = RoundFinalizationEvent{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[28] + mi := &file_ark_v1_service_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1658,7 +1713,7 @@ func (x *RoundFinalizationEvent) String() string { func (*RoundFinalizationEvent) ProtoMessage() {} func (x *RoundFinalizationEvent) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[28] + mi := &file_ark_v1_service_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1671,7 +1726,7 @@ func (x *RoundFinalizationEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundFinalizationEvent.ProtoReflect.Descriptor instead. func (*RoundFinalizationEvent) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{28} + return file_ark_v1_service_proto_rawDescGZIP(), []int{29} } func (x *RoundFinalizationEvent) GetId() string { @@ -1721,7 +1776,7 @@ type RoundFinalizedEvent struct { func (x *RoundFinalizedEvent) Reset() { *x = RoundFinalizedEvent{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[29] + mi := &file_ark_v1_service_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1734,7 +1789,7 @@ func (x *RoundFinalizedEvent) String() string { func (*RoundFinalizedEvent) ProtoMessage() {} func (x *RoundFinalizedEvent) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[29] + mi := &file_ark_v1_service_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1747,7 +1802,7 @@ func (x *RoundFinalizedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundFinalizedEvent.ProtoReflect.Descriptor instead. func (*RoundFinalizedEvent) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{29} + return file_ark_v1_service_proto_rawDescGZIP(), []int{30} } func (x *RoundFinalizedEvent) GetId() string { @@ -1776,7 +1831,7 @@ type RoundFailed struct { func (x *RoundFailed) Reset() { *x = RoundFailed{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[30] + mi := &file_ark_v1_service_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1789,7 +1844,7 @@ func (x *RoundFailed) String() string { func (*RoundFailed) ProtoMessage() {} func (x *RoundFailed) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[30] + mi := &file_ark_v1_service_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1802,7 +1857,7 @@ func (x *RoundFailed) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundFailed.ProtoReflect.Descriptor instead. func (*RoundFailed) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{30} + return file_ark_v1_service_proto_rawDescGZIP(), []int{31} } func (x *RoundFailed) GetId() string { @@ -1833,7 +1888,7 @@ type RoundSigningEvent struct { func (x *RoundSigningEvent) Reset() { *x = RoundSigningEvent{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[31] + mi := &file_ark_v1_service_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1846,7 +1901,7 @@ func (x *RoundSigningEvent) String() string { func (*RoundSigningEvent) ProtoMessage() {} func (x *RoundSigningEvent) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[31] + mi := &file_ark_v1_service_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1859,7 +1914,7 @@ func (x *RoundSigningEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundSigningEvent.ProtoReflect.Descriptor instead. func (*RoundSigningEvent) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{31} + return file_ark_v1_service_proto_rawDescGZIP(), []int{32} } func (x *RoundSigningEvent) GetId() string { @@ -1902,7 +1957,7 @@ type RoundSigningNoncesGeneratedEvent struct { func (x *RoundSigningNoncesGeneratedEvent) Reset() { *x = RoundSigningNoncesGeneratedEvent{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[32] + mi := &file_ark_v1_service_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1915,7 +1970,7 @@ func (x *RoundSigningNoncesGeneratedEvent) String() string { func (*RoundSigningNoncesGeneratedEvent) ProtoMessage() {} func (x *RoundSigningNoncesGeneratedEvent) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[32] + mi := &file_ark_v1_service_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1928,7 +1983,7 @@ func (x *RoundSigningNoncesGeneratedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundSigningNoncesGeneratedEvent.ProtoReflect.Descriptor instead. func (*RoundSigningNoncesGeneratedEvent) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{32} + return file_ark_v1_service_proto_rawDescGZIP(), []int{33} } func (x *RoundSigningNoncesGeneratedEvent) GetId() string { @@ -1963,7 +2018,7 @@ type Round struct { func (x *Round) Reset() { *x = Round{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[33] + mi := &file_ark_v1_service_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1976,7 +2031,7 @@ func (x *Round) String() string { func (*Round) ProtoMessage() {} func (x *Round) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[33] + mi := &file_ark_v1_service_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1989,7 +2044,7 @@ func (x *Round) ProtoReflect() protoreflect.Message { // Deprecated: Use Round.ProtoReflect.Descriptor instead. func (*Round) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{33} + return file_ark_v1_service_proto_rawDescGZIP(), []int{34} } func (x *Round) GetId() string { @@ -2060,7 +2115,7 @@ type Outpoint struct { func (x *Outpoint) Reset() { *x = Outpoint{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[34] + mi := &file_ark_v1_service_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2073,7 +2128,7 @@ func (x *Outpoint) String() string { func (*Outpoint) ProtoMessage() {} func (x *Outpoint) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[34] + mi := &file_ark_v1_service_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2086,7 +2141,7 @@ func (x *Outpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use Outpoint.ProtoReflect.Descriptor instead. func (*Outpoint) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{34} + return file_ark_v1_service_proto_rawDescGZIP(), []int{35} } func (x *Outpoint) GetTxid() string { @@ -2115,7 +2170,7 @@ type Input struct { func (x *Input) Reset() { *x = Input{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[35] + mi := &file_ark_v1_service_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2128,7 +2183,7 @@ func (x *Input) String() string { func (*Input) ProtoMessage() {} func (x *Input) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[35] + mi := &file_ark_v1_service_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2141,7 +2196,7 @@ func (x *Input) ProtoReflect() protoreflect.Message { // Deprecated: Use Input.ProtoReflect.Descriptor instead. func (*Input) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{35} + return file_ark_v1_service_proto_rawDescGZIP(), []int{36} } func (x *Input) GetOutpoint() *Outpoint { @@ -2163,15 +2218,14 @@ type Output struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // onchain or off-chain - Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` // Amount to send in satoshis. - Descriptor_ string `protobuf:"bytes,3,opt,name=descriptor,proto3" json:"descriptor,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // onchain or off-chain + Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` // Amount to send in satoshis. } func (x *Output) Reset() { *x = Output{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[36] + mi := &file_ark_v1_service_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2184,7 +2238,7 @@ func (x *Output) String() string { func (*Output) ProtoMessage() {} func (x *Output) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[36] + mi := &file_ark_v1_service_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2197,7 +2251,7 @@ func (x *Output) ProtoReflect() protoreflect.Message { // Deprecated: Use Output.ProtoReflect.Descriptor instead. func (*Output) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{36} + return file_ark_v1_service_proto_rawDescGZIP(), []int{37} } func (x *Output) GetAddress() string { @@ -2214,13 +2268,6 @@ func (x *Output) GetAmount() uint64 { return 0 } -func (x *Output) GetDescriptor_() string { - if x != nil { - return x.Descriptor_ - } - return "" -} - type Tree struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2232,7 +2279,7 @@ type Tree struct { func (x *Tree) Reset() { *x = Tree{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[37] + mi := &file_ark_v1_service_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2245,7 +2292,7 @@ func (x *Tree) String() string { func (*Tree) ProtoMessage() {} func (x *Tree) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[37] + mi := &file_ark_v1_service_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2258,7 +2305,7 @@ func (x *Tree) ProtoReflect() protoreflect.Message { // Deprecated: Use Tree.ProtoReflect.Descriptor instead. func (*Tree) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{37} + return file_ark_v1_service_proto_rawDescGZIP(), []int{38} } func (x *Tree) GetLevels() []*TreeLevel { @@ -2279,7 +2326,7 @@ type TreeLevel struct { func (x *TreeLevel) Reset() { *x = TreeLevel{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[38] + mi := &file_ark_v1_service_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2292,7 +2339,7 @@ func (x *TreeLevel) String() string { func (*TreeLevel) ProtoMessage() {} func (x *TreeLevel) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[38] + mi := &file_ark_v1_service_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2305,7 +2352,7 @@ func (x *TreeLevel) ProtoReflect() protoreflect.Message { // Deprecated: Use TreeLevel.ProtoReflect.Descriptor instead. func (*TreeLevel) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{38} + return file_ark_v1_service_proto_rawDescGZIP(), []int{39} } func (x *TreeLevel) GetNodes() []*Node { @@ -2328,7 +2375,7 @@ type Node struct { func (x *Node) Reset() { *x = Node{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[39] + mi := &file_ark_v1_service_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2341,7 +2388,7 @@ func (x *Node) String() string { func (*Node) ProtoMessage() {} func (x *Node) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[39] + mi := &file_ark_v1_service_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2354,7 +2401,7 @@ func (x *Node) ProtoReflect() protoreflect.Message { // Deprecated: Use Node.ProtoReflect.Descriptor instead. func (*Node) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{39} + return file_ark_v1_service_proto_rawDescGZIP(), []int{40} } func (x *Node) GetTxid() string { @@ -2383,22 +2430,22 @@ type Vtxo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Outpoint *Outpoint `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` - Descriptor_ string `protobuf:"bytes,2,opt,name=descriptor,proto3" json:"descriptor,omitempty"` - Spent bool `protobuf:"varint,3,opt,name=spent,proto3" json:"spent,omitempty"` - RoundTxid string `protobuf:"bytes,4,opt,name=round_txid,json=roundTxid,proto3" json:"round_txid,omitempty"` - SpentBy string `protobuf:"bytes,5,opt,name=spent_by,json=spentBy,proto3" json:"spent_by,omitempty"` - ExpireAt int64 `protobuf:"varint,6,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` - Swept bool `protobuf:"varint,7,opt,name=swept,proto3" json:"swept,omitempty"` - Pending bool `protobuf:"varint,8,opt,name=pending,proto3" json:"pending,omitempty"` - RedeemTx string `protobuf:"bytes,9,opt,name=redeem_tx,json=redeemTx,proto3" json:"redeem_tx,omitempty"` - Amount uint64 `protobuf:"varint,10,opt,name=amount,proto3" json:"amount,omitempty"` + Outpoint *Outpoint `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` + Spent bool `protobuf:"varint,2,opt,name=spent,proto3" json:"spent,omitempty"` + RoundTxid string `protobuf:"bytes,3,opt,name=round_txid,json=roundTxid,proto3" json:"round_txid,omitempty"` + SpentBy string `protobuf:"bytes,4,opt,name=spent_by,json=spentBy,proto3" json:"spent_by,omitempty"` + ExpireAt int64 `protobuf:"varint,5,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` + Swept bool `protobuf:"varint,6,opt,name=swept,proto3" json:"swept,omitempty"` + Pending bool `protobuf:"varint,7,opt,name=pending,proto3" json:"pending,omitempty"` + RedeemTx string `protobuf:"bytes,8,opt,name=redeem_tx,json=redeemTx,proto3" json:"redeem_tx,omitempty"` + Amount uint64 `protobuf:"varint,9,opt,name=amount,proto3" json:"amount,omitempty"` + Pubkey string `protobuf:"bytes,10,opt,name=pubkey,proto3" json:"pubkey,omitempty"` } func (x *Vtxo) Reset() { *x = Vtxo{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[40] + mi := &file_ark_v1_service_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2411,7 +2458,7 @@ func (x *Vtxo) String() string { func (*Vtxo) ProtoMessage() {} func (x *Vtxo) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[40] + mi := &file_ark_v1_service_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2424,7 +2471,7 @@ func (x *Vtxo) ProtoReflect() protoreflect.Message { // Deprecated: Use Vtxo.ProtoReflect.Descriptor instead. func (*Vtxo) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{40} + return file_ark_v1_service_proto_rawDescGZIP(), []int{41} } func (x *Vtxo) GetOutpoint() *Outpoint { @@ -2434,13 +2481,6 @@ func (x *Vtxo) GetOutpoint() *Outpoint { return nil } -func (x *Vtxo) GetDescriptor_() string { - if x != nil { - return x.Descriptor_ - } - return "" -} - func (x *Vtxo) GetSpent() bool { if x != nil { return x.Spent @@ -2497,6 +2537,13 @@ func (x *Vtxo) GetAmount() uint64 { return 0 } +func (x *Vtxo) GetPubkey() string { + if x != nil { + return x.Pubkey + } + return "" +} + type GetTransactionsStreamRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2506,7 +2553,7 @@ type GetTransactionsStreamRequest struct { func (x *GetTransactionsStreamRequest) Reset() { *x = GetTransactionsStreamRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[41] + mi := &file_ark_v1_service_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2519,7 +2566,7 @@ func (x *GetTransactionsStreamRequest) String() string { func (*GetTransactionsStreamRequest) ProtoMessage() {} func (x *GetTransactionsStreamRequest) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[41] + mi := &file_ark_v1_service_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2532,7 +2579,7 @@ func (x *GetTransactionsStreamRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTransactionsStreamRequest.ProtoReflect.Descriptor instead. func (*GetTransactionsStreamRequest) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{41} + return file_ark_v1_service_proto_rawDescGZIP(), []int{42} } type GetTransactionsStreamResponse struct { @@ -2550,7 +2597,7 @@ type GetTransactionsStreamResponse struct { func (x *GetTransactionsStreamResponse) Reset() { *x = GetTransactionsStreamResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[42] + mi := &file_ark_v1_service_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2563,7 +2610,7 @@ func (x *GetTransactionsStreamResponse) String() string { func (*GetTransactionsStreamResponse) ProtoMessage() {} func (x *GetTransactionsStreamResponse) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[42] + mi := &file_ark_v1_service_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2576,7 +2623,7 @@ func (x *GetTransactionsStreamResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTransactionsStreamResponse.ProtoReflect.Descriptor instead. func (*GetTransactionsStreamResponse) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{42} + return file_ark_v1_service_proto_rawDescGZIP(), []int{43} } func (m *GetTransactionsStreamResponse) GetTx() isGetTransactionsStreamResponse_Tx { @@ -2630,7 +2677,7 @@ type RoundTransaction struct { func (x *RoundTransaction) Reset() { *x = RoundTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[43] + mi := &file_ark_v1_service_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2643,7 +2690,7 @@ func (x *RoundTransaction) String() string { func (*RoundTransaction) ProtoMessage() {} func (x *RoundTransaction) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[43] + mi := &file_ark_v1_service_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2656,7 +2703,7 @@ func (x *RoundTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use RoundTransaction.ProtoReflect.Descriptor instead. func (*RoundTransaction) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{43} + return file_ark_v1_service_proto_rawDescGZIP(), []int{44} } func (x *RoundTransaction) GetTxid() string { @@ -2700,7 +2747,7 @@ type RedeemTransaction struct { func (x *RedeemTransaction) Reset() { *x = RedeemTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_ark_v1_service_proto_msgTypes[44] + mi := &file_ark_v1_service_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2713,7 +2760,7 @@ func (x *RedeemTransaction) String() string { func (*RedeemTransaction) ProtoMessage() {} func (x *RedeemTransaction) ProtoReflect() protoreflect.Message { - mi := &file_ark_v1_service_proto_msgTypes[44] + mi := &file_ark_v1_service_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2726,7 +2773,7 @@ func (x *RedeemTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use RedeemTransaction.ProtoReflect.Descriptor instead. func (*RedeemTransaction) Descriptor() ([]byte, []int) { - return file_ark_v1_service_proto_rawDescGZIP(), []int{44} + return file_ark_v1_service_proto_rawDescGZIP(), []int{45} } func (x *RedeemTransaction) GetTxid() string { @@ -2898,307 +2945,311 @@ var file_ark_v1_service_proto_rawDesc = []byte{ 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, 0x67, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x11, 0x41, 0x73, 0x79, 0x6e, 0x63, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x05, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x6c, 0x65, 0x61, + 0x66, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x66, 0x6f, + 0x72, 0x66, 0x65, 0x69, 0x74, 0x4c, 0x65, 0x61, 0x66, 0x48, 0x61, 0x73, 0x68, 0x22, 0x73, 0x0a, + 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x73, 0x79, 0x6e, 0x63, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x73, 0x22, 0x41, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x64, + 0x65, 0x65, 0x6d, 0x54, 0x78, 0x22, 0x42, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x25, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, - 0x22, 0x41, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x64, 0x65, 0x65, - 0x6d, 0x54, 0x78, 0x22, 0x42, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, - 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, - 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, - 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x10, 0x47, 0x65, 0x74, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, - 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x05, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x22, 0x25, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, - 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x14, 0x47, 0x65, 0x74, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, - 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, - 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x22, 0x79, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, - 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, - 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, - 0x12, 0x2d, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, - 0x74, 0x78, 0x6f, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x22, - 0xbb, 0x01, 0x0a, 0x16, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, - 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, 0x09, 0x76, 0x74, 0x78, 0x6f, 0x5f, 0x74, 0x72, - 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, 0x76, 0x74, 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, - 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, - 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x66, 0x65, - 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6d, 0x69, - 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x44, 0x0a, - 0x13, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, - 0x78, 0x69, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xb8, 0x01, 0x0a, 0x11, 0x52, - 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 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, + 0x28, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, + 0x5f, 0x74, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x05, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x25, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, + 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x14, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, + 0x64, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, + 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x79, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0f, 0x73, + 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, + 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, + 0x6f, 0x73, 0x12, 0x2d, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, + 0x73, 0x22, 0xbb, 0x01, 0x0a, 0x16, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, 0x09, 0x76, 0x74, 0x78, 0x6f, 0x5f, - 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, + 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, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, 0x74, 0x78, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, - 0x54, 0x78, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x22, 0x32, 0x0a, - 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x76, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x76, 0x6f, 0x75, - 0x74, 0x22, 0x55, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, - 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, - 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 0x5a, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x22, 0x31, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12, 0x29, 0x0a, 0x06, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, - 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x22, 0x2f, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x78, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x74, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0xa6, 0x02, 0x0a, 0x04, 0x56, 0x74, 0x78, 0x6f, 0x12, 0x2c, - 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x70, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x70, 0x65, - 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x69, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x69, - 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x12, 0x1b, 0x0a, 0x09, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x65, - 0x70, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x77, 0x65, 0x70, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x64, - 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1e, - 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, - 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, - 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, - 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04, 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, - 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, - 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, - 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, - 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, - 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, - 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, - 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, - 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, - 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, - 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, - 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, - 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, - 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, - 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, - 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, - 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, - 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb, 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, - 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, - 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, - 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, - 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 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, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, - 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, - 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, - 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, - 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, - 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, - 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, - 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, - 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, - 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, - 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, - 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x65, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x5f, + 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, + 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, + 0x44, 0x0a, 0x13, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, + 0x74, 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x54, 0x78, 0x69, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xb8, 0x01, 0x0a, + 0x11, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x5f, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, + 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x12, + 0x3a, 0x0a, 0x12, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x76, 0x74, 0x78, 0x6f, + 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x10, 0x75, 0x6e, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x56, 0x74, 0x78, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x75, + 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x22, 0x53, 0x0a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x72, 0x65, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xf0, 0x01, 0x0a, + 0x05, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x12, 0x29, 0x0a, 0x09, 0x76, 0x74, 0x78, + 0x6f, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x08, 0x76, 0x74, 0x78, 0x6f, + 0x54, 0x72, 0x65, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x5f, + 0x74, 0x78, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x66, 0x65, + 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x22, + 0x32, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x76, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x76, + 0x6f, 0x75, 0x74, 0x22, 0x55, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x08, + 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x22, 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, 0x9e, 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, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72, + 0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 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, 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, - 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, - 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, - 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, - 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06, + 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, + 0x6d, 0x42, 0x04, 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e, + 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, + 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, + 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, + 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c, + 0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, + 0x74, 0x78, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c, + 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, + 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, + 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, + 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, + 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, + 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, + 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, + 0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, + 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, + 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, + 0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, + 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, + 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, + 0x04, 0x32, 0xeb, 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, + 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, + 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, + 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, + 0x9c, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, + 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, + 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d, + 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, + 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, + 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, + 0x0a, 0x14, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01, + 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, + 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, + 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, + 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, + 0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, + 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, + 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, + 0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, + 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, + 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x42, + 0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, + 0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, + 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, + 0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, + 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3214,7 +3265,7 @@ func file_ark_v1_service_proto_rawDescGZIP() []byte { } var file_ark_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_ark_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 45) +var file_ark_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 46) var file_ark_v1_service_proto_goTypes = []interface{}{ (RoundStage)(0), // 0: ark.v1.RoundStage (*GetInfoRequest)(nil), // 1: ark.v1.GetInfoRequest @@ -3235,103 +3286,105 @@ var file_ark_v1_service_proto_goTypes = []interface{}{ (*GetEventStreamResponse)(nil), // 16: ark.v1.GetEventStreamResponse (*PingRequest)(nil), // 17: ark.v1.PingRequest (*PingResponse)(nil), // 18: ark.v1.PingResponse - (*CreatePaymentRequest)(nil), // 19: ark.v1.CreatePaymentRequest - (*CreatePaymentResponse)(nil), // 20: ark.v1.CreatePaymentResponse - (*CompletePaymentRequest)(nil), // 21: ark.v1.CompletePaymentRequest - (*CompletePaymentResponse)(nil), // 22: ark.v1.CompletePaymentResponse - (*GetRoundRequest)(nil), // 23: ark.v1.GetRoundRequest - (*GetRoundResponse)(nil), // 24: ark.v1.GetRoundResponse - (*GetRoundByIdRequest)(nil), // 25: ark.v1.GetRoundByIdRequest - (*GetRoundByIdResponse)(nil), // 26: ark.v1.GetRoundByIdResponse - (*ListVtxosRequest)(nil), // 27: ark.v1.ListVtxosRequest - (*ListVtxosResponse)(nil), // 28: ark.v1.ListVtxosResponse - (*RoundFinalizationEvent)(nil), // 29: ark.v1.RoundFinalizationEvent - (*RoundFinalizedEvent)(nil), // 30: ark.v1.RoundFinalizedEvent - (*RoundFailed)(nil), // 31: ark.v1.RoundFailed - (*RoundSigningEvent)(nil), // 32: ark.v1.RoundSigningEvent - (*RoundSigningNoncesGeneratedEvent)(nil), // 33: ark.v1.RoundSigningNoncesGeneratedEvent - (*Round)(nil), // 34: ark.v1.Round - (*Outpoint)(nil), // 35: ark.v1.Outpoint - (*Input)(nil), // 36: ark.v1.Input - (*Output)(nil), // 37: ark.v1.Output - (*Tree)(nil), // 38: ark.v1.Tree - (*TreeLevel)(nil), // 39: ark.v1.TreeLevel - (*Node)(nil), // 40: ark.v1.Node - (*Vtxo)(nil), // 41: ark.v1.Vtxo - (*GetTransactionsStreamRequest)(nil), // 42: ark.v1.GetTransactionsStreamRequest - (*GetTransactionsStreamResponse)(nil), // 43: ark.v1.GetTransactionsStreamResponse - (*RoundTransaction)(nil), // 44: ark.v1.RoundTransaction - (*RedeemTransaction)(nil), // 45: ark.v1.RedeemTransaction + (*AsyncPaymentInput)(nil), // 19: ark.v1.AsyncPaymentInput + (*CreatePaymentRequest)(nil), // 20: ark.v1.CreatePaymentRequest + (*CreatePaymentResponse)(nil), // 21: ark.v1.CreatePaymentResponse + (*CompletePaymentRequest)(nil), // 22: ark.v1.CompletePaymentRequest + (*CompletePaymentResponse)(nil), // 23: ark.v1.CompletePaymentResponse + (*GetRoundRequest)(nil), // 24: ark.v1.GetRoundRequest + (*GetRoundResponse)(nil), // 25: ark.v1.GetRoundResponse + (*GetRoundByIdRequest)(nil), // 26: ark.v1.GetRoundByIdRequest + (*GetRoundByIdResponse)(nil), // 27: ark.v1.GetRoundByIdResponse + (*ListVtxosRequest)(nil), // 28: ark.v1.ListVtxosRequest + (*ListVtxosResponse)(nil), // 29: ark.v1.ListVtxosResponse + (*RoundFinalizationEvent)(nil), // 30: ark.v1.RoundFinalizationEvent + (*RoundFinalizedEvent)(nil), // 31: ark.v1.RoundFinalizedEvent + (*RoundFailed)(nil), // 32: ark.v1.RoundFailed + (*RoundSigningEvent)(nil), // 33: ark.v1.RoundSigningEvent + (*RoundSigningNoncesGeneratedEvent)(nil), // 34: ark.v1.RoundSigningNoncesGeneratedEvent + (*Round)(nil), // 35: ark.v1.Round + (*Outpoint)(nil), // 36: ark.v1.Outpoint + (*Input)(nil), // 37: ark.v1.Input + (*Output)(nil), // 38: ark.v1.Output + (*Tree)(nil), // 39: ark.v1.Tree + (*TreeLevel)(nil), // 40: ark.v1.TreeLevel + (*Node)(nil), // 41: ark.v1.Node + (*Vtxo)(nil), // 42: ark.v1.Vtxo + (*GetTransactionsStreamRequest)(nil), // 43: ark.v1.GetTransactionsStreamRequest + (*GetTransactionsStreamResponse)(nil), // 44: ark.v1.GetTransactionsStreamResponse + (*RoundTransaction)(nil), // 45: ark.v1.RoundTransaction + (*RedeemTransaction)(nil), // 46: ark.v1.RedeemTransaction } var file_ark_v1_service_proto_depIdxs = []int32{ - 36, // 0: ark.v1.RegisterInputsForNextRoundRequest.inputs:type_name -> ark.v1.Input - 37, // 1: ark.v1.RegisterOutputsForNextRoundRequest.outputs:type_name -> ark.v1.Output - 29, // 2: ark.v1.GetEventStreamResponse.round_finalization:type_name -> ark.v1.RoundFinalizationEvent - 30, // 3: ark.v1.GetEventStreamResponse.round_finalized:type_name -> ark.v1.RoundFinalizedEvent - 31, // 4: ark.v1.GetEventStreamResponse.round_failed:type_name -> ark.v1.RoundFailed - 32, // 5: ark.v1.GetEventStreamResponse.round_signing:type_name -> ark.v1.RoundSigningEvent - 33, // 6: ark.v1.GetEventStreamResponse.round_signing_nonces_generated:type_name -> ark.v1.RoundSigningNoncesGeneratedEvent - 29, // 7: ark.v1.PingResponse.round_finalization:type_name -> ark.v1.RoundFinalizationEvent - 30, // 8: ark.v1.PingResponse.round_finalized:type_name -> ark.v1.RoundFinalizedEvent - 31, // 9: ark.v1.PingResponse.round_failed:type_name -> ark.v1.RoundFailed - 32, // 10: ark.v1.PingResponse.round_signing:type_name -> ark.v1.RoundSigningEvent - 33, // 11: ark.v1.PingResponse.round_signing_nonces_generated:type_name -> ark.v1.RoundSigningNoncesGeneratedEvent - 36, // 12: ark.v1.CreatePaymentRequest.inputs:type_name -> ark.v1.Input - 37, // 13: ark.v1.CreatePaymentRequest.outputs:type_name -> ark.v1.Output - 34, // 14: ark.v1.GetRoundResponse.round:type_name -> ark.v1.Round - 34, // 15: ark.v1.GetRoundByIdResponse.round:type_name -> ark.v1.Round - 41, // 16: ark.v1.ListVtxosResponse.spendable_vtxos:type_name -> ark.v1.Vtxo - 41, // 17: ark.v1.ListVtxosResponse.spent_vtxos:type_name -> ark.v1.Vtxo - 38, // 18: ark.v1.RoundFinalizationEvent.vtxo_tree:type_name -> ark.v1.Tree - 38, // 19: ark.v1.RoundSigningEvent.unsigned_vtxo_tree:type_name -> ark.v1.Tree - 38, // 20: ark.v1.Round.vtxo_tree:type_name -> ark.v1.Tree - 0, // 21: ark.v1.Round.stage:type_name -> ark.v1.RoundStage - 35, // 22: ark.v1.Input.outpoint:type_name -> ark.v1.Outpoint - 39, // 23: ark.v1.Tree.levels:type_name -> ark.v1.TreeLevel - 40, // 24: ark.v1.TreeLevel.nodes:type_name -> ark.v1.Node - 35, // 25: ark.v1.Vtxo.outpoint:type_name -> ark.v1.Outpoint - 44, // 26: ark.v1.GetTransactionsStreamResponse.round:type_name -> ark.v1.RoundTransaction - 45, // 27: ark.v1.GetTransactionsStreamResponse.redeem:type_name -> ark.v1.RedeemTransaction - 35, // 28: ark.v1.RoundTransaction.spent_vtxos:type_name -> ark.v1.Outpoint - 41, // 29: ark.v1.RoundTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo - 35, // 30: ark.v1.RoundTransaction.claimed_boarding_utxos:type_name -> ark.v1.Outpoint - 35, // 31: ark.v1.RedeemTransaction.spent_vtxos:type_name -> ark.v1.Outpoint - 41, // 32: ark.v1.RedeemTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo - 1, // 33: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest - 3, // 34: ark.v1.ArkService.GetBoardingAddress:input_type -> ark.v1.GetBoardingAddressRequest - 5, // 35: ark.v1.ArkService.RegisterInputsForNextRound:input_type -> ark.v1.RegisterInputsForNextRoundRequest - 7, // 36: ark.v1.ArkService.RegisterOutputsForNextRound:input_type -> ark.v1.RegisterOutputsForNextRoundRequest - 9, // 37: ark.v1.ArkService.SubmitTreeNonces:input_type -> ark.v1.SubmitTreeNoncesRequest - 11, // 38: ark.v1.ArkService.SubmitTreeSignatures:input_type -> ark.v1.SubmitTreeSignaturesRequest - 13, // 39: ark.v1.ArkService.SubmitSignedForfeitTxs:input_type -> ark.v1.SubmitSignedForfeitTxsRequest - 15, // 40: ark.v1.ArkService.GetEventStream:input_type -> ark.v1.GetEventStreamRequest - 17, // 41: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest - 19, // 42: ark.v1.ArkService.CreatePayment:input_type -> ark.v1.CreatePaymentRequest - 21, // 43: ark.v1.ArkService.CompletePayment:input_type -> ark.v1.CompletePaymentRequest - 23, // 44: ark.v1.ArkService.GetRound:input_type -> ark.v1.GetRoundRequest - 25, // 45: ark.v1.ArkService.GetRoundById:input_type -> ark.v1.GetRoundByIdRequest - 27, // 46: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest - 42, // 47: ark.v1.ArkService.GetTransactionsStream:input_type -> ark.v1.GetTransactionsStreamRequest - 2, // 48: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse - 4, // 49: ark.v1.ArkService.GetBoardingAddress:output_type -> ark.v1.GetBoardingAddressResponse - 6, // 50: ark.v1.ArkService.RegisterInputsForNextRound:output_type -> ark.v1.RegisterInputsForNextRoundResponse - 8, // 51: ark.v1.ArkService.RegisterOutputsForNextRound:output_type -> ark.v1.RegisterOutputsForNextRoundResponse - 10, // 52: ark.v1.ArkService.SubmitTreeNonces:output_type -> ark.v1.SubmitTreeNoncesResponse - 12, // 53: ark.v1.ArkService.SubmitTreeSignatures:output_type -> ark.v1.SubmitTreeSignaturesResponse - 14, // 54: ark.v1.ArkService.SubmitSignedForfeitTxs:output_type -> ark.v1.SubmitSignedForfeitTxsResponse - 16, // 55: ark.v1.ArkService.GetEventStream:output_type -> ark.v1.GetEventStreamResponse - 18, // 56: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse - 20, // 57: ark.v1.ArkService.CreatePayment:output_type -> ark.v1.CreatePaymentResponse - 22, // 58: ark.v1.ArkService.CompletePayment:output_type -> ark.v1.CompletePaymentResponse - 24, // 59: ark.v1.ArkService.GetRound:output_type -> ark.v1.GetRoundResponse - 26, // 60: ark.v1.ArkService.GetRoundById:output_type -> ark.v1.GetRoundByIdResponse - 28, // 61: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse - 43, // 62: ark.v1.ArkService.GetTransactionsStream:output_type -> ark.v1.GetTransactionsStreamResponse - 48, // [48:63] is the sub-list for method output_type - 33, // [33:48] is the sub-list for method input_type - 33, // [33:33] is the sub-list for extension type_name - 33, // [33:33] is the sub-list for extension extendee - 0, // [0:33] is the sub-list for field type_name + 37, // 0: ark.v1.RegisterInputsForNextRoundRequest.inputs:type_name -> ark.v1.Input + 38, // 1: ark.v1.RegisterOutputsForNextRoundRequest.outputs:type_name -> ark.v1.Output + 30, // 2: ark.v1.GetEventStreamResponse.round_finalization:type_name -> ark.v1.RoundFinalizationEvent + 31, // 3: ark.v1.GetEventStreamResponse.round_finalized:type_name -> ark.v1.RoundFinalizedEvent + 32, // 4: ark.v1.GetEventStreamResponse.round_failed:type_name -> ark.v1.RoundFailed + 33, // 5: ark.v1.GetEventStreamResponse.round_signing:type_name -> ark.v1.RoundSigningEvent + 34, // 6: ark.v1.GetEventStreamResponse.round_signing_nonces_generated:type_name -> ark.v1.RoundSigningNoncesGeneratedEvent + 30, // 7: ark.v1.PingResponse.round_finalization:type_name -> ark.v1.RoundFinalizationEvent + 31, // 8: ark.v1.PingResponse.round_finalized:type_name -> ark.v1.RoundFinalizedEvent + 32, // 9: ark.v1.PingResponse.round_failed:type_name -> ark.v1.RoundFailed + 33, // 10: ark.v1.PingResponse.round_signing:type_name -> ark.v1.RoundSigningEvent + 34, // 11: ark.v1.PingResponse.round_signing_nonces_generated:type_name -> ark.v1.RoundSigningNoncesGeneratedEvent + 37, // 12: ark.v1.AsyncPaymentInput.input:type_name -> ark.v1.Input + 19, // 13: ark.v1.CreatePaymentRequest.inputs:type_name -> ark.v1.AsyncPaymentInput + 38, // 14: ark.v1.CreatePaymentRequest.outputs:type_name -> ark.v1.Output + 35, // 15: ark.v1.GetRoundResponse.round:type_name -> ark.v1.Round + 35, // 16: ark.v1.GetRoundByIdResponse.round:type_name -> ark.v1.Round + 42, // 17: ark.v1.ListVtxosResponse.spendable_vtxos:type_name -> ark.v1.Vtxo + 42, // 18: ark.v1.ListVtxosResponse.spent_vtxos:type_name -> ark.v1.Vtxo + 39, // 19: ark.v1.RoundFinalizationEvent.vtxo_tree:type_name -> ark.v1.Tree + 39, // 20: ark.v1.RoundSigningEvent.unsigned_vtxo_tree:type_name -> ark.v1.Tree + 39, // 21: ark.v1.Round.vtxo_tree:type_name -> ark.v1.Tree + 0, // 22: ark.v1.Round.stage:type_name -> ark.v1.RoundStage + 36, // 23: ark.v1.Input.outpoint:type_name -> ark.v1.Outpoint + 40, // 24: ark.v1.Tree.levels:type_name -> ark.v1.TreeLevel + 41, // 25: ark.v1.TreeLevel.nodes:type_name -> ark.v1.Node + 36, // 26: ark.v1.Vtxo.outpoint:type_name -> ark.v1.Outpoint + 45, // 27: ark.v1.GetTransactionsStreamResponse.round:type_name -> ark.v1.RoundTransaction + 46, // 28: ark.v1.GetTransactionsStreamResponse.redeem:type_name -> ark.v1.RedeemTransaction + 36, // 29: ark.v1.RoundTransaction.spent_vtxos:type_name -> ark.v1.Outpoint + 42, // 30: ark.v1.RoundTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo + 36, // 31: ark.v1.RoundTransaction.claimed_boarding_utxos:type_name -> ark.v1.Outpoint + 36, // 32: ark.v1.RedeemTransaction.spent_vtxos:type_name -> ark.v1.Outpoint + 42, // 33: ark.v1.RedeemTransaction.spendable_vtxos:type_name -> ark.v1.Vtxo + 1, // 34: ark.v1.ArkService.GetInfo:input_type -> ark.v1.GetInfoRequest + 3, // 35: ark.v1.ArkService.GetBoardingAddress:input_type -> ark.v1.GetBoardingAddressRequest + 5, // 36: ark.v1.ArkService.RegisterInputsForNextRound:input_type -> ark.v1.RegisterInputsForNextRoundRequest + 7, // 37: ark.v1.ArkService.RegisterOutputsForNextRound:input_type -> ark.v1.RegisterOutputsForNextRoundRequest + 9, // 38: ark.v1.ArkService.SubmitTreeNonces:input_type -> ark.v1.SubmitTreeNoncesRequest + 11, // 39: ark.v1.ArkService.SubmitTreeSignatures:input_type -> ark.v1.SubmitTreeSignaturesRequest + 13, // 40: ark.v1.ArkService.SubmitSignedForfeitTxs:input_type -> ark.v1.SubmitSignedForfeitTxsRequest + 15, // 41: ark.v1.ArkService.GetEventStream:input_type -> ark.v1.GetEventStreamRequest + 17, // 42: ark.v1.ArkService.Ping:input_type -> ark.v1.PingRequest + 20, // 43: ark.v1.ArkService.CreatePayment:input_type -> ark.v1.CreatePaymentRequest + 22, // 44: ark.v1.ArkService.CompletePayment:input_type -> ark.v1.CompletePaymentRequest + 24, // 45: ark.v1.ArkService.GetRound:input_type -> ark.v1.GetRoundRequest + 26, // 46: ark.v1.ArkService.GetRoundById:input_type -> ark.v1.GetRoundByIdRequest + 28, // 47: ark.v1.ArkService.ListVtxos:input_type -> ark.v1.ListVtxosRequest + 43, // 48: ark.v1.ArkService.GetTransactionsStream:input_type -> ark.v1.GetTransactionsStreamRequest + 2, // 49: ark.v1.ArkService.GetInfo:output_type -> ark.v1.GetInfoResponse + 4, // 50: ark.v1.ArkService.GetBoardingAddress:output_type -> ark.v1.GetBoardingAddressResponse + 6, // 51: ark.v1.ArkService.RegisterInputsForNextRound:output_type -> ark.v1.RegisterInputsForNextRoundResponse + 8, // 52: ark.v1.ArkService.RegisterOutputsForNextRound:output_type -> ark.v1.RegisterOutputsForNextRoundResponse + 10, // 53: ark.v1.ArkService.SubmitTreeNonces:output_type -> ark.v1.SubmitTreeNoncesResponse + 12, // 54: ark.v1.ArkService.SubmitTreeSignatures:output_type -> ark.v1.SubmitTreeSignaturesResponse + 14, // 55: ark.v1.ArkService.SubmitSignedForfeitTxs:output_type -> ark.v1.SubmitSignedForfeitTxsResponse + 16, // 56: ark.v1.ArkService.GetEventStream:output_type -> ark.v1.GetEventStreamResponse + 18, // 57: ark.v1.ArkService.Ping:output_type -> ark.v1.PingResponse + 21, // 58: ark.v1.ArkService.CreatePayment:output_type -> ark.v1.CreatePaymentResponse + 23, // 59: ark.v1.ArkService.CompletePayment:output_type -> ark.v1.CompletePaymentResponse + 25, // 60: ark.v1.ArkService.GetRound:output_type -> ark.v1.GetRoundResponse + 27, // 61: ark.v1.ArkService.GetRoundById:output_type -> ark.v1.GetRoundByIdResponse + 29, // 62: ark.v1.ArkService.ListVtxos:output_type -> ark.v1.ListVtxosResponse + 44, // 63: ark.v1.ArkService.GetTransactionsStream:output_type -> ark.v1.GetTransactionsStreamResponse + 49, // [49:64] is the sub-list for method output_type + 34, // [34:49] is the sub-list for method input_type + 34, // [34:34] is the sub-list for extension type_name + 34, // [34:34] is the sub-list for extension extendee + 0, // [0:34] is the sub-list for field type_name } func init() { file_ark_v1_service_proto_init() } @@ -3557,7 +3610,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreatePaymentRequest); i { + switch v := v.(*AsyncPaymentInput); i { case 0: return &v.state case 1: @@ -3569,7 +3622,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreatePaymentResponse); i { + switch v := v.(*CreatePaymentRequest); i { case 0: return &v.state case 1: @@ -3581,7 +3634,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompletePaymentRequest); i { + switch v := v.(*CreatePaymentResponse); i { case 0: return &v.state case 1: @@ -3593,7 +3646,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompletePaymentResponse); i { + switch v := v.(*CompletePaymentRequest); i { case 0: return &v.state case 1: @@ -3605,7 +3658,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRoundRequest); i { + switch v := v.(*CompletePaymentResponse); i { case 0: return &v.state case 1: @@ -3617,7 +3670,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRoundResponse); i { + switch v := v.(*GetRoundRequest); i { case 0: return &v.state case 1: @@ -3629,7 +3682,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRoundByIdRequest); i { + switch v := v.(*GetRoundResponse); i { case 0: return &v.state case 1: @@ -3641,7 +3694,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRoundByIdResponse); i { + switch v := v.(*GetRoundByIdRequest); i { case 0: return &v.state case 1: @@ -3653,7 +3706,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListVtxosRequest); i { + switch v := v.(*GetRoundByIdResponse); i { case 0: return &v.state case 1: @@ -3665,7 +3718,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListVtxosResponse); i { + switch v := v.(*ListVtxosRequest); i { case 0: return &v.state case 1: @@ -3677,7 +3730,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoundFinalizationEvent); i { + switch v := v.(*ListVtxosResponse); i { case 0: return &v.state case 1: @@ -3689,7 +3742,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoundFinalizedEvent); i { + switch v := v.(*RoundFinalizationEvent); i { case 0: return &v.state case 1: @@ -3701,7 +3754,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoundFailed); i { + switch v := v.(*RoundFinalizedEvent); i { case 0: return &v.state case 1: @@ -3713,7 +3766,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoundSigningEvent); i { + switch v := v.(*RoundFailed); i { case 0: return &v.state case 1: @@ -3725,7 +3778,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoundSigningNoncesGeneratedEvent); i { + switch v := v.(*RoundSigningEvent); i { case 0: return &v.state case 1: @@ -3737,7 +3790,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Round); i { + switch v := v.(*RoundSigningNoncesGeneratedEvent); i { case 0: return &v.state case 1: @@ -3749,7 +3802,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Outpoint); i { + switch v := v.(*Round); i { case 0: return &v.state case 1: @@ -3761,7 +3814,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Input); i { + switch v := v.(*Outpoint); i { case 0: return &v.state case 1: @@ -3773,7 +3826,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Output); i { + switch v := v.(*Input); i { case 0: return &v.state case 1: @@ -3785,7 +3838,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Tree); i { + switch v := v.(*Output); i { case 0: return &v.state case 1: @@ -3797,7 +3850,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TreeLevel); i { + switch v := v.(*Tree); i { case 0: return &v.state case 1: @@ -3809,7 +3862,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Node); i { + switch v := v.(*TreeLevel); i { case 0: return &v.state case 1: @@ -3821,7 +3874,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Vtxo); i { + switch v := v.(*Node); i { case 0: return &v.state case 1: @@ -3833,7 +3886,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTransactionsStreamRequest); i { + switch v := v.(*Vtxo); i { case 0: return &v.state case 1: @@ -3845,7 +3898,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTransactionsStreamResponse); i { + switch v := v.(*GetTransactionsStreamRequest); i { case 0: return &v.state case 1: @@ -3857,7 +3910,7 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoundTransaction); i { + switch v := v.(*GetTransactionsStreamResponse); i { case 0: return &v.state case 1: @@ -3869,6 +3922,18 @@ func file_ark_v1_service_proto_init() { } } file_ark_v1_service_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoundTransaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ark_v1_service_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RedeemTransaction); i { case 0: return &v.state @@ -3897,7 +3962,7 @@ func file_ark_v1_service_proto_init() { (*PingResponse_RoundSigning)(nil), (*PingResponse_RoundSigningNoncesGenerated)(nil), } - file_ark_v1_service_proto_msgTypes[42].OneofWrappers = []interface{}{ + file_ark_v1_service_proto_msgTypes[43].OneofWrappers = []interface{}{ (*GetTransactionsStreamResponse_Round)(nil), (*GetTransactionsStreamResponse_Redeem)(nil), } @@ -3907,7 +3972,7 @@ func file_ark_v1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ark_v1_service_proto_rawDesc, NumEnums: 1, - NumMessages: 45, + NumMessages: 46, NumExtensions: 0, NumServices: 1, }, diff --git a/common/bitcointree/builder.go b/common/bitcointree/builder.go index 44327c2..e5a40e9 100644 --- a/common/bitcointree/builder.go +++ b/common/bitcointree/builder.go @@ -1,8 +1,10 @@ package bitcointree import ( + "encoding/hex" "fmt" + "github.com/ark-network/ark/common" "github.com/ark-network/ark/common/tree" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" @@ -15,7 +17,7 @@ import ( // CraftSharedOutput returns the taproot script and the amount of the initial root output func CraftSharedOutput( - cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []Receiver, + cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf, feeSatsPerNode uint64, roundLifetime int64, ) ([]byte, int64, error) { aggregatedKey, _, err := createAggregatedKeyWithSweep( @@ -32,7 +34,7 @@ func CraftSharedOutput( amount := root.getAmount() + int64(feeSatsPerNode) - scriptPubKey, err := taprootOutputScript(aggregatedKey.FinalKey) + scriptPubKey, err := common.P2TRScript(aggregatedKey.FinalKey) if err != nil { return nil, 0, err } @@ -42,7 +44,7 @@ func CraftSharedOutput( // CraftCongestionTree creates all the tree's transactions func CraftCongestionTree( - initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []Receiver, + initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf, feeSatsPerNode uint64, roundLifetime int64, ) (tree.CongestionTree, error) { aggregatedKey, sweepTapLeaf, err := createAggregatedKeyWithSweep( @@ -108,8 +110,8 @@ type node interface { } type leaf struct { - vtxoScript VtxoScript - amount int64 + amount int64 + pubkey *secp256k1.PublicKey } type branch struct { @@ -142,12 +144,7 @@ func (l *leaf) getAmount() int64 { } func (l *leaf) getOutputs() ([]*wire.TxOut, error) { - taprootKey, _, err := l.vtxoScript.TapTree() - if err != nil { - return nil, err - } - - script, err := taprootOutputScript(taprootKey) + script, err := common.P2TRScript(l.pubkey) if err != nil { return nil, err } @@ -161,7 +158,7 @@ func (l *leaf) getOutputs() ([]*wire.TxOut, error) { } func (b *branch) getOutputs() ([]*wire.TxOut, error) { - sharedOutputScript, err := taprootOutputScript(b.aggregatedKey.FinalKey) + sharedOutputScript, err := common.P2TRScript(b.aggregatedKey.FinalKey) if err != nil { return nil, err } @@ -246,7 +243,7 @@ func getTx( func createRootNode( aggregatedKey *musig2.AggregateKey, cosigners []*secp256k1.PublicKey, - receivers []Receiver, + receivers []tree.VtxoLeaf, feeSatsPerNode uint64, ) (root node, err error) { if len(receivers) == 0 { @@ -255,9 +252,19 @@ func createRootNode( nodes := make([]node, 0, len(receivers)) for _, r := range receivers { + pubkeyBytes, err := hex.DecodeString(r.Pubkey) + if err != nil { + return nil, err + } + + pubkey, err := schnorr.ParsePubKey(pubkeyBytes) + if err != nil { + return nil, err + } + leafNode := &leaf{ - vtxoScript: r.Script, - amount: int64(r.Amount), + amount: int64(r.Amount), + pubkey: pubkey, } nodes = append(nodes, leafNode) } @@ -339,9 +346,3 @@ func createUpperLevel(nodes []node, aggregatedKey *musig2.AggregateKey, cosigner } return pairs, nil } - -func taprootOutputScript(taprootKey *secp256k1.PublicKey) ([]byte, error) { - return txscript.NewScriptBuilder().AddOp(txscript.OP_1).AddData( - schnorr.SerializePubKey(taprootKey), - ).Script() -} diff --git a/common/bitcointree/musig2.go b/common/bitcointree/musig2.go index 9ca9a4b..4f73cf8 100644 --- a/common/bitcointree/musig2.go +++ b/common/bitcointree/musig2.go @@ -7,6 +7,7 @@ import ( "io" "strings" + "github.com/ark-network/ark/common" "github.com/ark-network/ark/common/tree" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" @@ -512,7 +513,7 @@ func prevOutFetcherFactory( func(partial *psbt.Packet) (txscript.PrevOutputFetcher, error), error, ) { - pkscript, err := taprootOutputScript(finalAggregatedKey) + pkscript, err := common.P2TRScript(finalAggregatedKey) if err != nil { return nil, err } diff --git a/common/bitcointree/musig2_test.go b/common/bitcointree/musig2_test.go index 2ba1859..b391f49 100644 --- a/common/bitcointree/musig2_test.go +++ b/common/bitcointree/musig2_test.go @@ -2,13 +2,13 @@ package bitcointree_test import ( "bytes" - "encoding/hex" "encoding/json" "fmt" "os" "testing" "github.com/ark-network/ark/common/bitcointree" + "github.com/ark-network/ark/common/tree" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" @@ -44,7 +44,7 @@ func TestRoundTripSignTree(t *testing.T) { _, sharedOutputAmount, err := bitcointree.CraftSharedOutput( cosigners, asp.PubKey(), - castReceivers(f.Receivers, asp.PubKey()), + castReceivers(f.Receivers), minRelayFee, lifetime, ) @@ -58,7 +58,7 @@ func TestRoundTripSignTree(t *testing.T) { }, cosigners, asp.PubKey(), - castReceivers(f.Receivers, asp.PubKey()), + castReceivers(f.Receivers), minRelayFee, lifetime, ) @@ -222,29 +222,11 @@ type receiverFixture struct { Pubkey string `json:"pubkey"` } -func (r receiverFixture) toVtxoScript(asp *secp256k1.PublicKey) bitcointree.VtxoScript { - bytesKey, err := hex.DecodeString(r.Pubkey) - if err != nil { - panic(err) - } - - pubkey, err := secp256k1.ParsePubKey(bytesKey) - if err != nil { - panic(err) - } - - return &bitcointree.DefaultVtxoScript{ - Owner: pubkey, - Asp: asp, - ExitDelay: exitDelay, - } -} - -func castReceivers(receivers []receiverFixture, asp *secp256k1.PublicKey) []bitcointree.Receiver { - receiversOut := make([]bitcointree.Receiver, 0, len(receivers)) +func castReceivers(receivers []receiverFixture) []tree.VtxoLeaf { + receiversOut := make([]tree.VtxoLeaf, 0, len(receivers)) for _, r := range receivers { - receiversOut = append(receiversOut, bitcointree.Receiver{ - Script: r.toVtxoScript(asp), + receiversOut = append(receiversOut, tree.VtxoLeaf{ + Pubkey: r.Pubkey, Amount: uint64(r.Amount), }) } diff --git a/common/bitcointree/testdata/musig2.json b/common/bitcointree/testdata/musig2.json index e0c230e..08efeed 100644 --- a/common/bitcointree/testdata/musig2.json +++ b/common/bitcointree/testdata/musig2.json @@ -4,7 +4,7 @@ { "receivers": [ { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1100 } ] @@ -12,11 +12,11 @@ { "receivers": [ { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1100 }, { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 8000 } ] @@ -24,23 +24,23 @@ { "receivers": [ { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1100 }, { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1100 }, { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1100 }, { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1000 }, { - "pubkey": "020000000000000000000000000000000000000000000000000000000000000002", + "pubkey": "0000000000000000000000000000000000000000000000000000000000000002", "amount": 1100 } ] diff --git a/common/bitcointree/type.go b/common/bitcointree/type.go deleted file mode 100644 index f023daa..0000000 --- a/common/bitcointree/type.go +++ /dev/null @@ -1,6 +0,0 @@ -package bitcointree - -type Receiver struct { - Script VtxoScript - Amount uint64 -} diff --git a/common/encoding.go b/common/encoding.go index caa6139..d719e63 100644 --- a/common/encoding.go +++ b/common/encoding.go @@ -3,63 +3,68 @@ package common import ( "fmt" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil/bech32" "github.com/decred/dcrd/dcrec/secp256k1/v4" ) -func EncodeAddress( - hrp string, userKey, aspKey *secp256k1.PublicKey, -) (addr string, err error) { - if userKey == nil { - err = fmt.Errorf("missing public key") - return +// Address represents an Ark address with HRP, ASP public key, and VTXO Taproot public key +type Address struct { + HRP string + Asp *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 aspKey == nil { - err = fmt.Errorf("missing asp public key") - return - } - if hrp != Liquid.Addr && hrp != LiquidTestNet.Addr { - err = fmt.Errorf("invalid prefix") - return + if a.VtxoTapKey == nil { + return "", fmt.Errorf("missing vtxo tap public key") } + combinedKey := append( - aspKey.SerializeCompressed(), userKey.SerializeCompressed()..., + schnorr.SerializePubKey(a.Asp), schnorr.SerializePubKey(a.VtxoTapKey)..., ) grp, err := bech32.ConvertBits(combinedKey, 8, 5, true) if err != nil { - return + return "", err } - addr, err = bech32.EncodeM(hrp, grp) - return + return bech32.EncodeM(a.HRP, grp) } -func DecodeAddress( - addr string, -) (hrp string, userKey, aspKey *secp256k1.PublicKey, err error) { +// DecodeAddress parses a bech32m encoded address string and returns an Address object +func DecodeAddress(addr string) (*Address, error) { + if len(addr) == 0 { + return nil, fmt.Errorf("address is empty") + } + prefix, buf, err := bech32.DecodeNoLimit(addr) if err != nil { - return + return nil, err } if prefix != Liquid.Addr && prefix != LiquidTestNet.Addr && prefix != LiquidRegTest.Addr { - err = fmt.Errorf("invalid prefix") - return + return nil, fmt.Errorf("invalid prefix") } grp, err := bech32.ConvertBits(buf, 5, 8, false) if err != nil { - return + return nil, err } - aKey, err := secp256k1.ParsePubKey(grp[:33]) + + aKey, err := schnorr.ParsePubKey(grp[:32]) if err != nil { - err = fmt.Errorf("failed to parse public key: %s", err) - return + return nil, fmt.Errorf("failed to parse public key: %s", err) } - uKey, err := secp256k1.ParsePubKey(grp[33:]) + + vtxoKey, err := schnorr.ParsePubKey(grp[32:]) if err != nil { - err = fmt.Errorf("failed to parse asp public key: %s", err) - return + return nil, fmt.Errorf("failed to parse asp public key: %s", err) } - hrp = prefix - userKey = uKey - aspKey = aKey - return + + return &Address{ + HRP: prefix, + Asp: aKey, + VtxoTapKey: vtxoKey, + }, nil } diff --git a/common/encoding_test.go b/common/encoding_test.go index 623e97c..b64e002 100644 --- a/common/encoding_test.go +++ b/common/encoding_test.go @@ -40,31 +40,29 @@ func TestAddressEncoding(t *testing.T) { t.Run("valid", func(t *testing.T) { for _, f := range fixtures.Address.Valid { - hrp, userKey, aspKey, err := common.DecodeAddress(f.Addr) + addr, err := common.DecodeAddress(f.Addr) require.NoError(t, err) - require.NotEmpty(t, hrp) - require.NotNil(t, userKey) - require.NotNil(t, aspKey) + require.NotEmpty(t, addr.HRP) + require.NotNil(t, addr.Asp) + require.NotNil(t, addr.VtxoTapKey) require.NoError(t, err) - require.Equal(t, f.ExpectedUserKey, hex.EncodeToString(userKey.SerializeCompressed())) + require.Equal(t, f.ExpectedUserKey, hex.EncodeToString(addr.VtxoTapKey.SerializeCompressed())) require.NoError(t, err) - require.Equal(t, f.ExpectedAspKey, hex.EncodeToString(aspKey.SerializeCompressed())) + require.Equal(t, f.ExpectedAspKey, hex.EncodeToString(addr.Asp.SerializeCompressed())) - addr, err := common.EncodeAddress(hrp, userKey, aspKey) + encoded, err := addr.Encode() require.NoError(t, err) - require.Equal(t, f.Addr, addr) + require.Equal(t, f.Addr, encoded) } }) t.Run("invalid", func(t *testing.T) { for _, f := range fixtures.Address.Invalid { - hrp, userKey, aspKey, err := common.DecodeAddress(f.Addr) + addr, err := common.DecodeAddress(f.Addr) require.EqualError(t, err, f.ExpectedError) - require.Empty(t, hrp) - require.Nil(t, userKey) - require.Nil(t, aspKey) + require.Nil(t, addr) } }) } diff --git a/common/fixtures/encoding.json b/common/fixtures/encoding.json index 8905a24..7502ebe 100644 --- a/common/fixtures/encoding.json +++ b/common/fixtures/encoding.json @@ -2,9 +2,9 @@ "address": { "valid": [ { - "addr": "ark1qgvdtj5ttpuhkldavhq8thtm5auyk0ec4dcmrfdgu0u5hgp9we22vqa7mdkrrulzu48law4zzvzz8k59hul0ayl2urt905we5wf6gee68sfrfj35", - "expectedUserKey": "03bedb6c31f3e2e54ffebaa2130423da85bf3efe93eae0d657d1d9a393a4673a3c", - "expectedAspKey": "0218d5ca8b58797b7dbd65c075dd7ba7784b3f38ab71b1a5a8e3f94ba0257654a6" + "addr": "tark1x0lm8hhr2wc6n6lyemtyh9rz8rg2ftpkfun46aca56kjg3ws0tsztfpuanaquxc6faedvjk3tax0575y6perapg3e95654pk8r4fjecs5fyd2", + "expectedUserKey": "0225a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", + "expectedAspKey": "0233ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0" } ], "invalid": [ diff --git a/common/tree/builder.go b/common/tree/builder.go index cdeeaba..61741f3 100644 --- a/common/tree/builder.go +++ b/common/tree/builder.go @@ -1,9 +1,9 @@ package tree import ( + "encoding/hex" "fmt" - "github.com/ark-network/ark/common" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" @@ -13,7 +13,7 @@ import ( ) func CraftCongestionTree( - asset string, aspPubkey *secp256k1.PublicKey, receivers []Receiver, + asset string, aspPubkey *secp256k1.PublicKey, receivers []VtxoLeaf, feeSatsPerNode uint64, roundLifetime int64, ) ( buildCongestionTree TreeFactory, @@ -41,9 +41,14 @@ func CraftCongestionTree( return } +type vtxoOutput struct { + pubkey *secp256k1.PublicKey + amount uint64 +} + type node struct { sweepKey *secp256k1.PublicKey - receivers []Receiver + receivers []vtxoOutput left *node right *node asset string @@ -61,7 +66,7 @@ func (n *node) isLeaf() bool { func (n *node) getAmount() uint64 { var amount uint64 for _, r := range n.receivers { - amount += r.Amount + amount += r.amount } if n.isLeaf() { @@ -107,7 +112,7 @@ func (n *node) getChildren() []*node { func (n *node) getOutputs() ([]psetv2.OutputArgs, error) { if n.isLeaf() { - taprootKey, _, err := n.getVtxoWitnessData() + taprootKey, err := n.getVtxoWitnessData() if err != nil { return nil, err } @@ -168,7 +173,7 @@ func (n *node) getWitnessData() ( } if n.isLeaf() { - taprootKey, _, err := n.getVtxoWitnessData() + taprootKey, err := n.getVtxoWitnessData() if err != nil { return nil, nil, err } @@ -241,15 +246,14 @@ func (n *node) getWitnessData() ( } func (n *node) getVtxoWitnessData() ( - *secp256k1.PublicKey, common.TaprootTree, error, + *secp256k1.PublicKey, error, ) { if !n.isLeaf() { - return nil, nil, fmt.Errorf("cannot call vtxoWitness on a non-leaf node") + return nil, fmt.Errorf("cannot call vtxoWitness on a non-leaf node") } receiver := n.receivers[0] - - return receiver.Script.TapTree() + return receiver.pubkey, nil } func (n *node) getTreeNode( @@ -373,7 +377,7 @@ func (n *node) createFinalCongestionTree() TreeFactory { } func createPartialCongestionTree( - asset string, aspPubkey *secp256k1.PublicKey, receivers []Receiver, + asset string, aspPubkey *secp256k1.PublicKey, receivers []VtxoLeaf, feeSatsPerNode uint64, roundLifetime int64, ) (root *node, err error) { if len(receivers) == 0 { @@ -382,9 +386,19 @@ func createPartialCongestionTree( nodes := make([]*node, 0, len(receivers)) for _, r := range receivers { + pubkeyBytes, err := hex.DecodeString(r.Pubkey) + if err != nil { + return nil, err + } + + pubkey, err := schnorr.ParsePubKey(pubkeyBytes) + if err != nil { + return nil, err + } + leafNode := &node{ sweepKey: aspPubkey, - receivers: []Receiver{r}, + receivers: []vtxoOutput{{pubkey, r.Amount}}, asset: asset, feeSats: feeSatsPerNode, roundLifetime: roundLifetime, diff --git a/common/tree/type.go b/common/tree/type.go index 502bd50..aee6f45 100644 --- a/common/tree/type.go +++ b/common/tree/type.go @@ -6,7 +6,7 @@ import ( type TreeFactory func(outpoint psetv2.InputArgs) (CongestionTree, error) -type Receiver struct { - Script VtxoScript +type VtxoLeaf struct { + Pubkey string Amount uint64 } diff --git a/pkg/client-sdk/client.go b/pkg/client-sdk/client.go index a26edfe..92ce9b5 100644 --- a/pkg/client-sdk/client.go +++ b/pkg/client-sdk/client.go @@ -94,7 +94,7 @@ func (a *arkClient) Receive(ctx context.Context) (string, string, error) { return "", "", err } - return offchainAddr, boardingAddr, nil + return offchainAddr.Address, boardingAddr.Address, nil } func (a *arkClient) GetTransactionEventChannel() chan types.TransactionEvent { diff --git a/pkg/client-sdk/client/client.go b/pkg/client-sdk/client/client.go index ce895de..d832ef7 100644 --- a/pkg/client-sdk/client/client.go +++ b/pkg/client-sdk/client/client.go @@ -2,10 +2,14 @@ package client import ( "context" + "encoding/hex" "time" + "github.com/ark-network/ark/common" "github.com/ark-network/ark/common/bitcointree" "github.com/ark-network/ark/common/tree" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -41,7 +45,7 @@ type ASPClient interface { ) (<-chan RoundEventChannel, func(), error) Ping(ctx context.Context, paymentID string) (RoundEvent, error) CreatePayment( - ctx context.Context, inputs []Input, outputs []Output, + ctx context.Context, inputs []AsyncPaymentInput, outputs []Output, ) (string, error) CompletePayment( ctx context.Context, signedRedeemTx string, @@ -79,21 +83,50 @@ type Input struct { Descriptor string } +type AsyncPaymentInput struct { + Input + ForfeitLeafHash chainhash.Hash +} + type Vtxo struct { Outpoint + Pubkey string + Amount uint64 + RoundTxid string + ExpiresAt *time.Time + RedeemTx string + Pending bool + SpentBy string +} + +func (v Vtxo) Address(asp *secp256k1.PublicKey, net common.Network) (string, error) { + pubkeyBytes, err := hex.DecodeString(v.Pubkey) + if err != nil { + return "", err + } + + pubkey, err := schnorr.ParsePubKey(pubkeyBytes) + if err != nil { + return "", err + } + + a := &common.Address{ + HRP: net.Addr, + Asp: asp, + VtxoTapKey: pubkey, + } + + return a.Encode() +} + +type DescriptorVtxo struct { + Vtxo Descriptor string - Amount uint64 - RoundTxid string - ExpiresAt *time.Time - RedeemTx string - Pending bool - SpentBy string } type Output struct { - Address string // onchain output address - Descriptor string // offchain vtxo descriptor - Amount uint64 + Address string // onchain or offchain address + Amount uint64 } type RoundStage int diff --git a/pkg/client-sdk/client/grpc/client.go b/pkg/client-sdk/client/grpc/client.go index 1628fa0..30e352f 100644 --- a/pkg/client-sdk/client/grpc/client.go +++ b/pkg/client-sdk/client/grpc/client.go @@ -238,10 +238,10 @@ func (a *grpcClient) Ping( } func (a *grpcClient) CreatePayment( - ctx context.Context, inputs []client.Input, outputs []client.Output, + ctx context.Context, inputs []client.AsyncPaymentInput, outputs []client.Output, ) (string, error) { req := &arkv1.CreatePaymentRequest{ - Inputs: ins(inputs).toProto(), + Inputs: asyncIns(inputs).toProto(), Outputs: outs(outputs).toProto(), } resp, err := a.svc.CreatePayment(ctx, req) @@ -404,13 +404,13 @@ func vtxosFromProto(protoVtxos []*arkv1.Vtxo) []client.Vtxo { Txid: v.Outpoint.Txid, VOut: v.Outpoint.Vout, }, - Descriptor: v.Descriptor_, - Amount: v.Amount, - RoundTxid: v.RoundTxid, - ExpiresAt: &expiresAt, - RedeemTx: v.RedeemTx, - Pending: v.Pending, - SpentBy: v.SpentBy, + Pubkey: v.Pubkey, + Amount: v.Amount, + RoundTxid: v.RoundTxid, + ExpiresAt: &expiresAt, + RedeemTx: v.RedeemTx, + Pending: v.Pending, + SpentBy: v.SpentBy, } } return vtxos diff --git a/pkg/client-sdk/client/grpc/types.go b/pkg/client-sdk/client/grpc/types.go index 6442adc..4b13dae 100644 --- a/pkg/client-sdk/client/grpc/types.go +++ b/pkg/client-sdk/client/grpc/types.go @@ -18,9 +18,8 @@ type out client.Output func (o out) toProto() *arkv1.Output { return &arkv1.Output{ - Address: o.Address, - Descriptor_: o.Descriptor, - Amount: o.Amount, + Address: o.Address, + Amount: o.Amount, } } @@ -123,13 +122,13 @@ func (v vtxo) toVtxo() client.Vtxo { Txid: v.GetOutpoint().GetTxid(), VOut: v.GetOutpoint().GetVout(), }, - Amount: v.GetAmount(), - RoundTxid: v.GetRoundTxid(), - ExpiresAt: expiresAt, - Pending: v.GetPending(), - RedeemTx: v.GetRedeemTx(), - SpentBy: v.GetSpentBy(), - Descriptor: v.GetDescriptor_(), + Amount: v.GetAmount(), + RoundTxid: v.GetRoundTxid(), + ExpiresAt: expiresAt, + Pending: v.GetPending(), + RedeemTx: v.GetRedeemTx(), + SpentBy: v.GetSpentBy(), + Pubkey: v.GetPubkey(), } } @@ -153,6 +152,23 @@ func toProtoInput(i client.Input) *arkv1.Input { } } +func toAsyncProtoInput(i client.AsyncPaymentInput) *arkv1.AsyncPaymentInput { + return &arkv1.AsyncPaymentInput{ + Input: toProtoInput(i.Input), + ForfeitLeafHash: i.ForfeitLeafHash.String(), + } +} + +type asyncIns []client.AsyncPaymentInput + +func (i asyncIns) toProto() []*arkv1.AsyncPaymentInput { + list := make([]*arkv1.AsyncPaymentInput, 0, len(i)) + for _, ii := range i { + list = append(list, toAsyncProtoInput(ii)) + } + return list +} + type ins []client.Input func (i ins) toProto() []*arkv1.Input { diff --git a/pkg/client-sdk/client/rest/client.go b/pkg/client-sdk/client/rest/client.go index bfdcaf8..73458c4 100644 --- a/pkg/client-sdk/client/rest/client.go +++ b/pkg/client-sdk/client/rest/client.go @@ -138,9 +138,8 @@ func (a *restClient) RegisterOutputsForNextRound( outs := make([]*models.V1Output, 0, len(outputs)) for _, o := range outputs { outs = append(outs, &models.V1Output{ - Address: o.Address, - Descriptor: o.Descriptor, - Amount: strconv.Itoa(int(o.Amount)), + Address: o.Address, + Amount: strconv.Itoa(int(o.Amount)), }) } body := models.V1RegisterOutputsForNextRoundRequest{ @@ -337,24 +336,26 @@ func (a *restClient) Ping( } func (a *restClient) CreatePayment( - ctx context.Context, inputs []client.Input, outputs []client.Output, + ctx context.Context, inputs []client.AsyncPaymentInput, outputs []client.Output, ) (string, error) { - ins := make([]*models.V1Input, 0, len(inputs)) + ins := make([]*models.V1AsyncPaymentInput, 0, len(inputs)) for _, i := range inputs { - ins = append(ins, &models.V1Input{ - Outpoint: &models.V1Outpoint{ - Txid: i.Txid, - Vout: int64(i.VOut), + ins = append(ins, &models.V1AsyncPaymentInput{ + Input: &models.V1Input{ + Outpoint: &models.V1Outpoint{ + Txid: i.Input.Txid, + Vout: int64(i.VOut), + }, + Descriptor: i.Input.Descriptor, }, - Descriptor: i.Descriptor, + ForfeitLeafHash: i.ForfeitLeafHash.String(), }) } outs := make([]*models.V1Output, 0, len(outputs)) for _, o := range outputs { outs = append(outs, &models.V1Output{ - Address: o.Address, - Amount: strconv.Itoa(int(o.Amount)), - Descriptor: o.Descriptor, + Address: o.Address, + Amount: strconv.Itoa(int(o.Amount)), }) } body := models.V1CreatePaymentRequest{ @@ -495,13 +496,13 @@ func (a *restClient) ListVtxos( Txid: v.Outpoint.Txid, VOut: uint32(v.Outpoint.Vout), }, - Amount: uint64(amount), - RoundTxid: v.RoundTxid, - ExpiresAt: expiresAt, - Pending: v.Pending, - RedeemTx: v.RedeemTx, - SpentBy: v.SpentBy, - Descriptor: v.Descriptor, + Amount: uint64(amount), + RoundTxid: v.RoundTxid, + ExpiresAt: expiresAt, + Pending: v.Pending, + RedeemTx: v.RedeemTx, + SpentBy: v.SpentBy, + Pubkey: v.Pubkey, }) } @@ -527,11 +528,11 @@ func (a *restClient) ListVtxos( Txid: v.Outpoint.Txid, VOut: uint32(v.Outpoint.Vout), }, - Amount: uint64(amount), - RoundTxid: v.RoundTxid, - ExpiresAt: expiresAt, - SpentBy: v.SpentBy, - Descriptor: v.Descriptor, + Amount: uint64(amount), + RoundTxid: v.RoundTxid, + ExpiresAt: expiresAt, + SpentBy: v.SpentBy, + Pubkey: v.Pubkey, }) } @@ -695,13 +696,13 @@ func vtxosFromRest(restVtxos []*models.V1Vtxo) []client.Vtxo { Txid: v.Outpoint.Txid, VOut: uint32(v.Outpoint.Vout), }, - Descriptor: v.Descriptor, - Amount: uint64(amount), - RoundTxid: v.RoundTxid, - ExpiresAt: expiresAt, - RedeemTx: v.RedeemTx, - Pending: v.Pending, - SpentBy: v.SpentBy, + Pubkey: v.Pubkey, + Amount: uint64(amount), + RoundTxid: v.RoundTxid, + ExpiresAt: expiresAt, + RedeemTx: v.RedeemTx, + Pending: v.Pending, + SpentBy: v.SpentBy, } } return vtxos diff --git a/pkg/client-sdk/client/rest/service/models/v1_async_payment_input.go b/pkg/client-sdk/client/rest/service/models/v1_async_payment_input.go new file mode 100644 index 0000000..8912ef8 --- /dev/null +++ b/pkg/client-sdk/client/rest/service/models/v1_async_payment_input.go @@ -0,0 +1,112 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// V1AsyncPaymentInput v1 async payment input +// +// swagger:model v1AsyncPaymentInput +type V1AsyncPaymentInput struct { + + // forfeit leaf hash + ForfeitLeafHash string `json:"forfeitLeafHash,omitempty"` + + // input + Input *V1Input `json:"input,omitempty"` +} + +// Validate validates this v1 async payment input +func (m *V1AsyncPaymentInput) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateInput(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1AsyncPaymentInput) validateInput(formats strfmt.Registry) error { + if swag.IsZero(m.Input) { // not required + return nil + } + + if m.Input != nil { + if err := m.Input.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("input") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("input") + } + return err + } + } + + return nil +} + +// ContextValidate validate this v1 async payment input based on the context it is used +func (m *V1AsyncPaymentInput) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateInput(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *V1AsyncPaymentInput) contextValidateInput(ctx context.Context, formats strfmt.Registry) error { + + if m.Input != nil { + + if swag.IsZero(m.Input) { // not required + return nil + } + + if err := m.Input.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("input") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("input") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *V1AsyncPaymentInput) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *V1AsyncPaymentInput) UnmarshalBinary(b []byte) error { + var res V1AsyncPaymentInput + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/client-sdk/client/rest/service/models/v1_create_payment_request.go b/pkg/client-sdk/client/rest/service/models/v1_create_payment_request.go index 72140d3..8587e80 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_create_payment_request.go +++ b/pkg/client-sdk/client/rest/service/models/v1_create_payment_request.go @@ -20,7 +20,7 @@ import ( type V1CreatePaymentRequest struct { // inputs - Inputs []*V1Input `json:"inputs"` + Inputs []*V1AsyncPaymentInput `json:"inputs"` // outputs Outputs []*V1Output `json:"outputs"` diff --git a/pkg/client-sdk/client/rest/service/models/v1_output.go b/pkg/client-sdk/client/rest/service/models/v1_output.go index be584cb..e7aeb19 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_output.go +++ b/pkg/client-sdk/client/rest/service/models/v1_output.go @@ -22,9 +22,6 @@ type V1Output struct { // Amount to send in satoshis. Amount string `json:"amount,omitempty"` - - // descriptor - Descriptor string `json:"descriptor,omitempty"` } // Validate validates this v1 output diff --git a/pkg/client-sdk/client/rest/service/models/v1_vtxo.go b/pkg/client-sdk/client/rest/service/models/v1_vtxo.go index 74e0f87..5bca48d 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_vtxo.go +++ b/pkg/client-sdk/client/rest/service/models/v1_vtxo.go @@ -21,9 +21,6 @@ type V1Vtxo struct { // amount Amount string `json:"amount,omitempty"` - // descriptor - Descriptor string `json:"descriptor,omitempty"` - // expire at ExpireAt string `json:"expireAt,omitempty"` @@ -33,6 +30,9 @@ type V1Vtxo struct { // pending Pending bool `json:"pending,omitempty"` + // pubkey + Pubkey string `json:"pubkey,omitempty"` + // redeem tx RedeemTx string `json:"redeemTx,omitempty"` diff --git a/pkg/client-sdk/covenant_client.go b/pkg/client-sdk/covenant_client.go index 3ad4543..5a0fd3c 100644 --- a/pkg/client-sdk/covenant_client.go +++ b/pkg/client-sdk/covenant_client.go @@ -260,7 +260,7 @@ func (a *covenantArkClient) ListVtxos( } for _, addr := range offchainAddrs { - spendable, spent, err := a.client.ListVtxos(ctx, addr) + spendable, spent, err := a.client.ListVtxos(ctx, addr.Address) if err != nil { return nil, nil, err } @@ -303,7 +303,7 @@ func (a *covenantArkClient) Balance( offchainBalance: balance, offchainBalanceByExpiration: amountByExpiration, } - }(offchainAddr) + }(offchainAddr.Address) getDelayedBalance := func(addr string) { defer wg.Done() @@ -323,8 +323,8 @@ func (a *covenantArkClient) Balance( } } - go getDelayedBalance(boardingAddr) - go getDelayedBalance(redeemAddr) + go getDelayedBalance(boardingAddr.Address) + go getDelayedBalance(redeemAddr.Address) } wg.Wait() @@ -455,7 +455,7 @@ func (a *covenantArkClient) UnilateralRedeem(ctx context.Context) error { vtxos := make([]client.Vtxo, 0) for _, offchainAddr := range offchainAddrs { - fetchedVtxos, _, err := a.client.ListVtxos(ctx, offchainAddr) + fetchedVtxos, _, err := a.client.ListVtxos(ctx, offchainAddr.Address) if err != nil { return err } @@ -549,13 +549,19 @@ func (a *covenantArkClient) CollaborativeRedeem( }, } - vtxos := make([]client.Vtxo, 0) + vtxos := make([]client.DescriptorVtxo, 0) for _, offchainAddr := range offchainAddrs { - spendableVtxos, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect) + spendableVtxos, err := a.getVtxos(ctx, offchainAddr.Address, withExpiryCoinselect) if err != nil { return "", err } - vtxos = append(vtxos, spendableVtxos...) + + for _, vtxo := range spendableVtxos { + vtxos = append(vtxos, client.DescriptorVtxo{ + Vtxo: vtxo, + Descriptor: offchainAddr.Descriptor, + }) + } } selectedCoins, changeAmount, err := utils.CoinSelect( @@ -571,14 +577,9 @@ func (a *covenantArkClient) CollaborativeRedeem( return "", err } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddr) - if err != nil { - return "", err - } - receivers = append(receivers, client.Output{ - Descriptor: desc, - Amount: changeAmount, + Address: offchainAddr.Address, + Amount: changeAmount, }) } @@ -621,12 +622,7 @@ func (a *covenantArkClient) SendAsync( } func (a *covenantArkClient) Claim(ctx context.Context) (string, error) { - myselfOffchain, _, err := a.wallet.NewAddress(ctx, false) - if err != nil { - return "", err - } - - _, mypubkey, _, err := common.DecodeAddress(myselfOffchain) + myselfOffchain, boardingAddr, err := a.wallet.NewAddress(ctx, false) if err != nil { return "", err } @@ -644,21 +640,16 @@ func (a *covenantArkClient) Claim(ctx context.Context) (string, error) { return "", fmt.Errorf("no funds to claim") } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(myselfOffchain) - if err != nil { - return "", err - } - receiver := client.Output{ - Descriptor: desc, - Amount: pendingBalance, + Address: myselfOffchain.Address, + Amount: pendingBalance, } return a.selfTransferAllPendingPayments( ctx, boardingUtxos, receiver, - hex.EncodeToString(mypubkey.SerializeCompressed()), + boardingAddr.Descriptor, ) } @@ -692,13 +683,13 @@ func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]explorer utxos := []explorer.Utxo{} for _, addr := range boardingAddrs { - txs, err := a.explorer.GetTxs(addr) + txs, err := a.explorer.GetTxs(addr.Address) if err != nil { continue } for _, tx := range txs { for i, vout := range tx.Vout { - if vout.Address == addr { + if vout.Address == addr.Address { createdAt := time.Time{} if tx.Status.Confirmed { createdAt = time.Unix(tx.Status.Blocktime, 0) @@ -718,43 +709,34 @@ func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]explorer } func (a *covenantArkClient) getClaimableBoardingUtxos(ctx context.Context) ([]explorer.Utxo, error) { - offchainAddrs, boardingAddrs, _, err := a.wallet.GetAddresses(ctx) + _, boardingAddrs, _, err := a.wallet.GetAddresses(ctx) if err != nil { return nil, err } claimable := make([]explorer.Utxo, 0) - now := time.Now() - - _, myPubkey, _, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return nil, err - } - - myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(myPubkey)) - descriptorStr := strings.ReplaceAll( - a.BoardingDescriptorTemplate, "USER", myPubkeyStr, - ) - - boardingScript, err := tree.ParseVtxoScript(descriptorStr) - if err != nil { - return nil, err - } - - var boardingTimeout uint - - if defaultVtxo, ok := boardingScript.(*tree.DefaultVtxoScript); ok { - boardingTimeout = defaultVtxo.ExitDelay - } else { - return nil, fmt.Errorf("unsupported boarding descriptor: %s", descriptorStr) - } for _, addr := range boardingAddrs { - boardingUtxos, err := a.explorer.GetUtxos(addr) + boardingScript, err := tree.ParseVtxoScript(addr.Descriptor) if err != nil { return nil, err } + var boardingTimeout uint + + if defaultVtxo, ok := boardingScript.(*tree.DefaultVtxoScript); ok { + boardingTimeout = defaultVtxo.ExitDelay + } else { + return nil, fmt.Errorf("unsupported boarding descriptor: %s", addr.Descriptor) + } + + boardingUtxos, err := a.explorer.GetUtxos(addr.Address) + if err != nil { + return nil, err + } + + now := time.Now() + for _, utxo := range boardingUtxos { u := utxo.ToUtxo(boardingTimeout) @@ -826,7 +808,7 @@ func (a *covenantArkClient) sendOnchain( return "", err } - changeScript, err := address.ToOutputScript(changeAddr) + changeScript, err := address.ToOutputScript(changeAddr.Address) if err != nil { return "", err } @@ -876,7 +858,7 @@ func (a *covenantArkClient) sendOnchain( return "", err } - changeScript, err := address.ToOutputScript(changeAddr) + changeScript, err := address.ToOutputScript(changeAddr.Address) if err != nil { return "", err } @@ -938,49 +920,47 @@ func (a *covenantArkClient) sendOffchain( return "", fmt.Errorf("no funds detected") } - _, _, aspPubKey, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return "", err - } + expectedAspPubkey := schnorr.SerializePubKey(a.AspPubkey) receiversOutput := make([]client.Output, 0) sumOfReceivers := uint64(0) for _, receiver := range receivers { - _, _, aspKey, err := common.DecodeAddress(receiver.To()) + rcvAddr, err := common.DecodeAddress(receiver.To()) if err != nil { return "", fmt.Errorf("invalid receiver address: %s", err) } - if !bytes.Equal( - aspPubKey.SerializeCompressed(), aspKey.SerializeCompressed(), - ) { - return "", fmt.Errorf("invalid receiver address '%s': must be associated with the connected service provider", receiver.To()) + rcvAspPubkey := schnorr.SerializePubKey(rcvAddr.Asp) + + if !bytes.Equal(rcvAspPubkey, expectedAspPubkey) { + 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) } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(receiver.To()) - if err != nil { - return "", err - } - receiversOutput = append(receiversOutput, client.Output{ - Descriptor: desc, - Amount: receiver.Amount(), + Address: receiver.To(), + Amount: receiver.Amount(), }) sumOfReceivers += receiver.Amount() } - vtxos := make([]client.Vtxo, 0) + vtxos := make([]client.DescriptorVtxo, 0) for _, offchainAddr := range offchainAddrs { - spendableVtxos, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect) + spendableVtxos, err := a.getVtxos(ctx, offchainAddr.Address, withExpiryCoinselect) if err != nil { return "", err } - vtxos = append(vtxos, spendableVtxos...) + + for _, vtxo := range spendableVtxos { + vtxos = append(vtxos, client.DescriptorVtxo{ + Vtxo: vtxo, + Descriptor: offchainAddr.Descriptor, + }) + } } selectedCoins, changeAmount, err := utils.CoinSelect( @@ -996,14 +976,9 @@ func (a *covenantArkClient) sendOffchain( return "", err } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddr) - if err != nil { - return "", err - } - changeReceiver := client.Output{ - Descriptor: desc, - Amount: changeAmount, + Address: offchainAddr.Address, + Amount: changeAmount, } receiversOutput = append(receiversOutput, changeReceiver) } @@ -1044,6 +1019,7 @@ func (a *covenantArkClient) sendOffchain( return poolTxID, nil } +// addInputs adds the inputs to the pset for send onchain func (a *covenantArkClient) addInputs( ctx context.Context, updater *psetv2.Updater, @@ -1055,11 +1031,21 @@ func (a *covenantArkClient) addInputs( return err } - _, userPubkey, aspPubkey, err := common.DecodeAddress(offchain) + vtxoScript, err := tree.ParseVtxoScript(offchain.Descriptor) if err != nil { return err } + var userPubkey, aspPubkey *secp256k1.PublicKey + + switch s := vtxoScript.(type) { + case *tree.DefaultVtxoScript: + userPubkey = s.Owner + aspPubkey = s.Asp + default: + return fmt.Errorf("unsupported vtxo script: %T", s) + } + for _, utxo := range utxos { sequence, err := utxo.Sequence() if err != nil { @@ -1126,7 +1112,7 @@ func (a *covenantArkClient) addInputs( func (a *covenantArkClient) handleRoundStream( ctx context.Context, paymentID string, - vtxosToSign []client.Vtxo, + vtxosToSign []client.DescriptorVtxo, boardingUtxos []explorer.Utxo, boardingDescriptor string, receivers []client.Output, @@ -1191,7 +1177,7 @@ func (a *covenantArkClient) handleRoundStream( func (a *covenantArkClient) handleRoundFinalization( ctx context.Context, event client.RoundFinalizationEvent, - vtxos []client.Vtxo, + vtxos []client.DescriptorVtxo, boardingUtxos []explorer.Utxo, boardingDescriptor string, receivers []client.Output, @@ -1200,18 +1186,8 @@ func (a *covenantArkClient) handleRoundFinalization( return } - offchainAddr, _, err := a.wallet.NewAddress(ctx, false) - if err != nil { - return - } - - _, myPubkey, _, err := common.DecodeAddress(offchainAddr) - if err != nil { - return - } - if len(vtxos) > 0 { - signedForfeits, err = a.createAndSignForfeits(ctx, vtxos, event.Connectors, event.MinRelayFeeRate, myPubkey) + signedForfeits, err = a.createAndSignForfeits(ctx, vtxos, event.Connectors, event.MinRelayFeeRate) if err != nil { return } @@ -1228,10 +1204,16 @@ func (a *covenantArkClient) handleRoundFinalization( return nil, "", err } - // add tapscript leaf - forfeitClosure := &tree.MultisigClosure{ - Pubkey: myPubkey, - AspPubkey: a.AspPubkey, + var forfeitClosure tree.Closure + + switch s := boardingVtxoScript.(type) { + case *tree.DefaultVtxoScript: + forfeitClosure = &tree.MultisigClosure{ + Pubkey: s.Owner, + AspPubkey: a.AspPubkey, + } + default: + return nil, "", fmt.Errorf("unsupported boarding descriptor: %s", boardingDescriptor) } forfeitLeaf, err := forfeitClosure.Leaf() @@ -1381,15 +1363,12 @@ func (a *covenantArkClient) validateOffChainReceiver( ) error { found := false - receiverVtxoScript, err := tree.ParseVtxoScript(receiver.Descriptor) + addr, err := common.DecodeAddress(receiver.Address) if err != nil { return err } - outputTapKey, _, err := receiverVtxoScript.TapTree() - if err != nil { - return err - } + vtxoTapKey := schnorr.SerializePubKey(addr.VtxoTapKey) leaves := congestionTree.Leaves() for _, leaf := range leaves { @@ -1402,7 +1381,7 @@ func (a *covenantArkClient) validateOffChainReceiver( if len(output.Script) == 0 { continue } - if bytes.Equal(output.Script[2:], schnorr.SerializePubKey(outputTapKey)) { + if bytes.Equal(output.Script[2:], vtxoTapKey) { if output.Value == receiver.Amount { found = true break @@ -1423,10 +1402,9 @@ func (a *covenantArkClient) validateOffChainReceiver( func (a *covenantArkClient) createAndSignForfeits( ctx context.Context, - vtxosToSign []client.Vtxo, + vtxosToSign []client.DescriptorVtxo, connectors []string, feeRate chainfee.SatPerKVByte, - myPubKey *secp256k1.PublicKey, ) ([]string, error) { signedForfeits := make([]string, 0) connectorsPsets := make([]*psetv2.Pset, 0, len(connectors)) @@ -1471,9 +1449,16 @@ func (a *covenantArkClient) createAndSignForfeits( TxIndex: vtxo.VOut, } - forfeitClosure := &tree.MultisigClosure{ - Pubkey: myPubKey, - AspPubkey: a.AspPubkey, + var forfeitClosure tree.Closure + + switch s := vtxoScript.(type) { + case *tree.DefaultVtxoScript: + forfeitClosure = &tree.MultisigClosure{ + Pubkey: s.Owner, + AspPubkey: a.AspPubkey, + } + default: + return nil, fmt.Errorf("unsupported vtxo script: %T", s) } forfeitLeaf, err := forfeitClosure.Leaf() @@ -1535,38 +1520,31 @@ func (a *covenantArkClient) createAndSignForfeits( func (a *covenantArkClient) coinSelectOnchain( ctx context.Context, targetAmount uint64, exclude []explorer.Utxo, ) ([]explorer.Utxo, uint64, error) { - offchainAddrs, boardingAddrs, redemptionAddrs, err := a.wallet.GetAddresses(ctx) + _, boardingAddrs, redemptionAddrs, err := a.wallet.GetAddresses(ctx) if err != nil { return nil, 0, err } - _, myPubkey, _, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return nil, 0, err - } - - myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(myPubkey)) - descriptorStr := strings.ReplaceAll( - a.BoardingDescriptorTemplate, "USER", myPubkeyStr, - ) - boardingScript, err := tree.ParseVtxoScript(descriptorStr) - if err != nil { - return nil, 0, err - } - - var boardingTimeout uint - - if defaultVtxo, ok := boardingScript.(*tree.DefaultVtxoScript); ok { - boardingTimeout = defaultVtxo.ExitDelay - } else { - return nil, 0, fmt.Errorf("unsupported boarding descriptor: %s", descriptorStr) - } - now := time.Now() fetchedUtxos := make([]explorer.Utxo, 0) for _, addr := range boardingAddrs { - utxos, err := a.explorer.GetUtxos(addr) + boardingDescriptor := addr.Descriptor + + boardingScript, err := tree.ParseVtxoScript(boardingDescriptor) + if err != nil { + return nil, 0, err + } + + var boardingTimeout uint + + if defaultVtxo, ok := boardingScript.(*tree.DefaultVtxoScript); ok { + boardingTimeout = defaultVtxo.ExitDelay + } else { + return nil, 0, fmt.Errorf("unsupported boarding descriptor: %s", boardingDescriptor) + } + + utxos, err := a.explorer.GetUtxos(addr.Address) if err != nil { return nil, 0, err } @@ -1602,7 +1580,7 @@ func (a *covenantArkClient) coinSelectOnchain( fetchedUtxos = make([]explorer.Utxo, 0) for _, addr := range redemptionAddrs { - utxos, err := a.explorer.GetUtxos(addr) + utxos, err := a.explorer.GetUtxos(addr.Address) if err != nil { return nil, 0, err } @@ -1731,14 +1709,10 @@ func (a *covenantArkClient) getVtxos( } func (a *covenantArkClient) selfTransferAllPendingPayments( - ctx context.Context, boardingUtxos []explorer.Utxo, myself client.Output, mypubkey string, + ctx context.Context, boardingUtxos []explorer.Utxo, myself client.Output, boardingDescriptor string, ) (string, error) { inputs := make([]client.Input, 0, len(boardingUtxos)) - boardingDescriptor := strings.ReplaceAll( - a.BoardingDescriptorTemplate, "USER", mypubkey[2:], - ) - for _, utxo := range boardingUtxos { inputs = append(inputs, client.Input{ Outpoint: client.Outpoint{ @@ -1761,7 +1735,7 @@ func (a *covenantArkClient) selfTransferAllPendingPayments( } roundTxid, err := a.handleRoundStream( - ctx, paymentID, make([]client.Vtxo, 0), boardingUtxos, boardingDescriptor, outputs, + ctx, paymentID, make([]client.DescriptorVtxo, 0), boardingUtxos, boardingDescriptor, outputs, ) if err != nil { return "", err @@ -1770,24 +1744,7 @@ func (a *covenantArkClient) selfTransferAllPendingPayments( return roundTxid, nil } -func (a *covenantArkClient) offchainAddressToDefaultVtxoDescriptor(addr string) (string, error) { - _, userPubKey, aspPubkey, err := common.DecodeAddress(addr) - if err != nil { - return "", err - } - - vtxoScript := tree.DefaultVtxoScript{ - Owner: userPubKey, - Asp: aspPubkey, - ExitDelay: uint(a.UnilateralExitDelay), - } - - return vtxoScript.ToDescriptor(), nil -} - -func (a *covenantArkClient) getBoardingTxs( - ctx context.Context, -) (transactions []types.Transaction) { +func (a *covenantArkClient) getBoardingTxs(ctx context.Context) (transactions []types.Transaction) { utxos, err := a.getClaimableBoardingUtxos(ctx) if err != nil { return nil diff --git a/pkg/client-sdk/covenantless_client.go b/pkg/client-sdk/covenantless_client.go index 6f9ebda..a8d06e7 100644 --- a/pkg/client-sdk/covenantless_client.go +++ b/pkg/client-sdk/covenantless_client.go @@ -223,12 +223,14 @@ func (a *covenantlessArkClient) listenForTransactions(ctx context.Context) { return } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddr) + addr, err := common.DecodeAddress(offchainAddr.Address) if err != nil { - log.WithError(err).Error("Failed to get descriptor for new address") + log.WithError(err).Error("Failed to decode address") return } + addrPubkey := hex.EncodeToString(schnorr.SerializePubKey(addr.VtxoTapKey)) + for { select { case event, ok := <-eventChan: @@ -253,7 +255,7 @@ func (a *covenantlessArkClient) listenForTransactions(ctx context.Context) { continue } - a.processTransactionEvent(desc, event, pendingBoardingTxsMap) + a.processTransactionEvent(addrPubkey, event, pendingBoardingTxsMap) case <-ctx.Done(): return } @@ -319,7 +321,7 @@ func (a *covenantlessArkClient) getBoardingPendingTransactions( } func (a *covenantlessArkClient) processTransactionEvent( - descriptor string, + pubkey string, event client.TransactionEvent, pendingBoardingTxsMap map[string]types.Transaction, ) { @@ -372,7 +374,7 @@ func (a *covenantlessArkClient) processTransactionEvent( vtxosToInsert := make([]types.Vtxo, 0) txsToInsert := make([]types.Transaction, 0) for _, v := range event.Round.SpendableVtxos { - if v.Descriptor == descriptor { + if v.Pubkey == pubkey { vtxosToInsert = append(vtxosToInsert, types.Vtxo{ VtxoKey: types.VtxoKey{ Txid: v.Txid, @@ -449,7 +451,7 @@ func (a *covenantlessArkClient) processTransactionEvent( outputAmount := uint64(0) for _, v := range event.Redeem.SpendableVtxos { - if v.Descriptor == descriptor { + if v.Pubkey == pubkey { vtxosToInsert = append(vtxosToInsert, types.Vtxo{ VtxoKey: types.VtxoKey{ Txid: v.Txid, @@ -482,7 +484,7 @@ func (a *covenantlessArkClient) processTransactionEvent( } } else { for _, v := range event.Redeem.SpendableVtxos { - if v.Descriptor == descriptor { + if v.Pubkey == pubkey { vtxosToInsert = append(vtxosToInsert, types.Vtxo{ VtxoKey: types.VtxoKey{ Txid: v.Txid, @@ -524,17 +526,25 @@ func (a *covenantlessArkClient) processTransactionEvent( func (a *covenantlessArkClient) ListVtxos( ctx context.Context, ) (spendableVtxos, spentVtxos []client.Vtxo, err error) { - offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx) + offchainAddrs, boardingAddrs, _, err := a.wallet.GetAddresses(ctx) if err != nil { return } - _, pubkey, _, err := common.DecodeAddress(offchainAddrs[0]) + boardingAddrScript, err := bitcointree.ParseVtxoScript(boardingAddrs[0].Descriptor) if err != nil { return } - myPubkey := schnorr.SerializePubKey(pubkey) + var myPubkey []byte + + if boardingScript, ok := boardingAddrScript.(*bitcointree.DefaultVtxoScript); ok { + myPubkey = schnorr.SerializePubKey(boardingScript.Owner) + } + + if myPubkey == nil { + return nil, nil, fmt.Errorf("invalid boarding address descriptor") + } // The ASP returns the vtxos sent to others via async payments as spendable // because they are actually revertable. Since we do not provide any revert @@ -544,22 +554,25 @@ func (a *covenantlessArkClient) ListVtxos( // The auxiliary variables below are used to make these checks in an // efficient way. for _, addr := range offchainAddrs { - spendable, spent, err := a.client.ListVtxos(ctx, addr) + spendable, spent, err := a.client.ListVtxos(ctx, addr.Address) if err != nil { return nil, nil, err } + + script, err := bitcointree.ParseVtxoScript(addr.Descriptor) + if err != nil { + return nil, nil, err + } + + reversibleVtxo, isReversible := script.(*bitcointree.ReversibleVtxoScript) + for _, v := range spendable { if !v.Pending { spendableVtxos = append(spendableVtxos, v) continue } - script, err := bitcointree.ParseVtxoScript(v.Descriptor) - if err != nil { - return nil, nil, err - } - reversibleVtxo, ok := script.(*bitcointree.ReversibleVtxoScript) - if !ok { + if !isReversible { spendableVtxos = append(spendableVtxos, v) continue } @@ -568,17 +581,13 @@ func (a *covenantlessArkClient) ListVtxos( spendableVtxos = append(spendableVtxos, v) } } - for _, v := range spent { - script, err := bitcointree.ParseVtxoScript(v.Descriptor) - if err != nil { - return nil, nil, err - } - reversibleVtxo, ok := script.(*bitcointree.ReversibleVtxoScript) - if !ok { + for _, v := range spent { + if !isReversible { spentVtxos = append(spentVtxos, v) continue } + if !bytes.Equal(schnorr.SerializePubKey(reversibleVtxo.Sender), myPubkey) { spentVtxos = append(spentVtxos, v) } @@ -602,14 +611,13 @@ func (a *covenantlessArkClient) Balance( chRes := make(chan balanceRes, nbWorkers*len(offchainAddrs)) for i := range offchainAddrs { - offchainAddr := offchainAddrs[i] boardingAddr := boardingAddrs[i] redeemAddr := redeemAddrs[i] - go func(addr string) { + go func() { defer wg.Done() balance, amountByExpiration, err := a.getOffchainBalance( - ctx, addr, computeVtxoExpiration, + ctx, computeVtxoExpiration, ) if err != nil { chRes <- balanceRes{err: err} @@ -620,7 +628,7 @@ func (a *covenantlessArkClient) Balance( offchainBalance: balance, offchainBalanceByExpiration: amountByExpiration, } - }(offchainAddr) + }() getDelayedBalance := func(addr string) { defer wg.Done() @@ -640,8 +648,8 @@ func (a *covenantlessArkClient) Balance( } } - go getDelayedBalance(boardingAddr) - go getDelayedBalance(redeemAddr) + go getDelayedBalance(boardingAddr.Address) + go getDelayedBalance(redeemAddr.Address) } wg.Wait() @@ -765,20 +773,11 @@ func (a *covenantlessArkClient) UnilateralRedeem(ctx context.Context) error { return fmt.Errorf("wallet is locked") } - offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx) + vtxos, _, err := a.getVtxos(ctx, false) if err != nil { return err } - vtxos := make([]client.Vtxo, 0) - for _, offchainAddr := range offchainAddrs { - spendableVtxos, _, err := a.getVtxos(ctx, offchainAddr, false) - if err != nil { - return err - } - vtxos = append(vtxos, spendableVtxos...) - } - totalVtxosAmount := uint64(0) for _, vtxo := range vtxos { totalVtxosAmount += vtxo.Amount @@ -853,13 +852,26 @@ func (a *covenantlessArkClient) CollaborativeRedeem( }, } - vtxos := make([]client.Vtxo, 0) + vtxos := make([]client.DescriptorVtxo, 0) + spendableVtxos, _, err := a.getVtxos(ctx, withExpiryCoinselect) + if err != nil { + return "", err + } + for _, offchainAddr := range offchainAddrs { - spendableVtxos, _, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect) - if err != nil { - return "", err + 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.DescriptorVtxo{ + Vtxo: v, + Descriptor: offchainAddr.Descriptor, + }) + } } - vtxos = append(vtxos, spendableVtxos...) } selectedCoins, changeAmount, err := utils.CoinSelect( @@ -875,14 +887,9 @@ func (a *covenantlessArkClient) CollaborativeRedeem( return "", err } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddr) - if err != nil { - return "", err - } - receivers = append(receivers, client.Output{ - Descriptor: desc, - Amount: changeAmount, + Address: offchainAddr.Address, + Amount: changeAmount, }) } @@ -950,59 +957,57 @@ func (a *covenantlessArkClient) SendAsync( return "", err } - _, _, aspPubKey, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return "", err - } + expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey) receiversOutput := make([]client.Output, 0) sumOfReceivers := uint64(0) for _, receiver := range receivers { - _, _, aspKey, err := common.DecodeAddress(receiver.To()) + rcvAddr, err := common.DecodeAddress(receiver.To()) if err != nil { return "", fmt.Errorf("invalid receiver address: %s", err) } - if !bytes.Equal( - aspPubKey.SerializeCompressed(), aspKey.SerializeCompressed(), - ) { - return "", fmt.Errorf("invalid receiver address '%s': must be associated with the connected service provider", receiver) + 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) } - isSelfTransfer := offchainAddrs[0] == receiver.To() - - var desc string - - // reversible vtxo does not make sense for self transfer - // if the receiver is the same as the sender, handle the output like the change - if !isSelfTransfer { - desc, err = a.offchainAddressToReversibleVtxoDescriptor(offchainAddrs[0], receiver.To()) - if err != nil { - return "", err - } - } else { - desc, err = a.offchainAddressToDefaultVtxoDescriptor(receiver.To()) - if err != nil { - return "", err - } - } - receiversOutput = append(receiversOutput, client.Output{ - Descriptor: desc, - Amount: receiver.Amount(), + Address: receiver.To(), + Amount: receiver.Amount(), }) sumOfReceivers += receiver.Amount() } - vtxos, _, err := a.getVtxos(ctx, offchainAddrs[0], withExpiryCoinselect) + vtxos := make([]client.DescriptorVtxo, 0) + + spendableVtxos, _, err := a.getVtxos(ctx, withExpiryCoinselect) 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.DescriptorVtxo{ + Vtxo: v, + Descriptor: offchainAddr.Descriptor, + }) + } + } + } + selectedCoins, changeAmount, err := utils.CoinSelect( vtxos, sumOfReceivers, a.Dust, withExpiryCoinselect, ) @@ -1011,27 +1016,52 @@ func (a *covenantlessArkClient) SendAsync( } if changeAmount > 0 { - changeDesc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddrs[0]) - if err != nil { - return "", err - } - changeReceiver := client.Output{ - Descriptor: changeDesc, - Amount: changeAmount, + Address: offchainAddrs[0].Address, + Amount: changeAmount, } receiversOutput = append(receiversOutput, changeReceiver) } - inputs := make([]client.Input, 0, len(selectedCoins)) + inputs := make([]client.AsyncPaymentInput, 0, len(selectedCoins)) for _, coin := range selectedCoins { - inputs = append(inputs, client.Input{ - Outpoint: client.Outpoint{ - Txid: coin.Txid, - VOut: coin.VOut, + vtxoScript, err := bitcointree.ParseVtxoScript(coin.Descriptor) + if err != nil { + return "", err + } + + var forfeitClosure bitcointree.Closure + + switch s := vtxoScript.(type) { + case *bitcointree.DefaultVtxoScript: + forfeitClosure = &bitcointree.MultisigClosure{ + Pubkey: s.Owner, + AspPubkey: s.Asp, + } + case *bitcointree.ReversibleVtxoScript: + forfeitClosure = &bitcointree.MultisigClosure{ + Pubkey: s.Owner, + AspPubkey: s.Asp, + } + default: + return "", fmt.Errorf("unsupported vtxo script: %T", s) + } + + forfeitLeaf, err := forfeitClosure.Leaf() + if err != nil { + return "", err + } + + inputs = append(inputs, client.AsyncPaymentInput{ + Input: client.Input{ + Outpoint: client.Outpoint{ + Txid: coin.Txid, + VOut: coin.VOut, + }, + Descriptor: coin.Descriptor, }, - Descriptor: coin.Descriptor, + ForfeitLeafHash: forfeitLeaf.TapHash(), }) } @@ -1057,19 +1087,22 @@ func (a *covenantlessArkClient) SendAsync( } func (a *covenantlessArkClient) Claim(ctx context.Context) (string, error) { - myselfOffchain, _, err := a.wallet.NewAddress(ctx, false) + myselfOffchain, boardingAddr, err := a.wallet.NewAddress(ctx, false) if err != nil { return "", err } - _, pendingVtxos, err := a.getVtxos(ctx, myselfOffchain, false) + _, pendingVtxos, err := a.getVtxos(ctx, false) if err != nil { return "", err } - _, mypubkey, _, err := common.DecodeAddress(myselfOffchain) - if err != nil { - return "", err + pendingVtxosWithDescriptor := make([]client.DescriptorVtxo, 0) + for _, vtxo := range pendingVtxos { + pendingVtxosWithDescriptor = append(pendingVtxosWithDescriptor, client.DescriptorVtxo{ + Vtxo: vtxo, + Descriptor: myselfOffchain.Descriptor, + }) } boardingUtxos, err := a.getClaimableBoardingUtxos(ctx) @@ -1088,22 +1121,17 @@ func (a *covenantlessArkClient) Claim(ctx context.Context) (string, error) { return "", nil } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(myselfOffchain) - if err != nil { - return "", err - } - receiver := client.Output{ - Descriptor: desc, - Amount: pendingBalance, + Address: myselfOffchain.Address, + Amount: pendingBalance, } return a.selfTransferAllPendingPayments( ctx, - pendingVtxos, + pendingVtxosWithDescriptor, boardingUtxos, receiver, - hex.EncodeToString(mypubkey.SerializeCompressed()), + boardingAddr.Descriptor, ) } @@ -1208,7 +1236,7 @@ func (a *covenantlessArkClient) sendOnchain( if err != nil { return "", err } - addr, _ := btcutil.DecodeAddress(changeAddr, &netParams) + addr, _ := btcutil.DecodeAddress(changeAddr.Address, &netParams) pkscript, err := txscript.PayToAddrScript(addr) if err != nil { @@ -1255,7 +1283,7 @@ func (a *covenantlessArkClient) sendOnchain( if err != nil { return "", err } - addr, _ := btcutil.DecodeAddress(changeAddr, &netParams) + addr, _ := btcutil.DecodeAddress(changeAddr.Address, &netParams) pkscript, err := txscript.PayToAddrScript(addr) if err != nil { @@ -1306,49 +1334,55 @@ func (a *covenantlessArkClient) sendOffchain( return "", fmt.Errorf("no funds detected") } - _, _, aspPubKey, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return "", err - } + expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey) receiversOutput := make([]client.Output, 0) sumOfReceivers := uint64(0) for _, receiver := range receivers { - _, _, aspKey, err := common.DecodeAddress(receiver.To()) + rcvAddr, err := common.DecodeAddress(receiver.To()) if err != nil { return "", fmt.Errorf("invalid receiver address: %s", err) } - if !bytes.Equal( - aspPubKey.SerializeCompressed(), aspKey.SerializeCompressed(), - ) { - return "", fmt.Errorf("invalid receiver address '%s': must be associated with the connected service provider", receiver.To()) + 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) } - desc, err := a.offchainAddressToDefaultVtxoDescriptor(receiver.To()) - if err != nil { - return "", err - } - receiversOutput = append(receiversOutput, client.Output{ - Descriptor: desc, - Amount: receiver.Amount(), + Address: receiver.To(), + Amount: receiver.Amount(), }) sumOfReceivers += receiver.Amount() } - vtxos := make([]client.Vtxo, 0) + vtxos := make([]client.DescriptorVtxo, 0) + + spendableVtxos, _, err := a.getVtxos(ctx, withExpiryCoinselect) + if err != nil { + return "", err + } + for _, offchainAddr := range offchainAddrs { - spendableVtxos, _, err := a.getVtxos(ctx, offchainAddr, withExpiryCoinselect) - if err != nil { - return "", err + 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.DescriptorVtxo{ + Vtxo: v, + Descriptor: offchainAddr.Descriptor, + }) + } } - vtxos = append(vtxos, spendableVtxos...) } selectedCoins, changeAmount, err := utils.CoinSelect( @@ -1363,15 +1397,9 @@ func (a *covenantlessArkClient) sendOffchain( if err != nil { return "", err } - - desc, err := a.offchainAddressToDefaultVtxoDescriptor(offchainAddr) - if err != nil { - return "", err - } - changeReceiver := client.Output{ - Descriptor: desc, - Amount: changeAmount, + Address: offchainAddr.Address, + Amount: changeAmount, } receiversOutput = append(receiversOutput, changeReceiver) } @@ -1428,11 +1456,21 @@ func (a *covenantlessArkClient) addInputs( return err } - _, userPubkey, aspPubkey, err := common.DecodeAddress(offchain) + vtxoScript, err := tree.ParseVtxoScript(offchain.Descriptor) if err != nil { return err } + var userPubkey, aspPubkey *secp256k1.PublicKey + + switch s := vtxoScript.(type) { + case *tree.DefaultVtxoScript: + userPubkey = s.Owner + aspPubkey = s.Asp + default: + return fmt.Errorf("unsupported vtxo script: %T", s) + } + for _, utxo := range utxos { previousHash, err := chainhash.NewHashFromStr(utxo.Txid) if err != nil { @@ -1495,7 +1533,7 @@ func (a *covenantlessArkClient) addInputs( func (a *covenantlessArkClient) handleRoundStream( ctx context.Context, paymentID string, - vtxosToSign []client.Vtxo, + vtxosToSign []client.DescriptorVtxo, boardingUtxos []explorer.Utxo, boardingDescriptor string, receivers []client.Output, @@ -1681,7 +1719,7 @@ func (a *covenantlessArkClient) handleRoundSigningNoncesGenerated( func (a *covenantlessArkClient) handleRoundFinalization( ctx context.Context, event client.RoundFinalizationEvent, - vtxos []client.Vtxo, + vtxos []client.DescriptorVtxo, boardingUtxos []explorer.Utxo, boardingDescriptor string, receivers []client.Output, @@ -1690,21 +1728,11 @@ func (a *covenantlessArkClient) handleRoundFinalization( return nil, "", fmt.Errorf("failed to verify congestion tree: %s", err) } - offchainAddr, _, err := a.wallet.NewAddress(ctx, false) - if err != nil { - return nil, "", err - } - - _, myPubkey, _, err := common.DecodeAddress(offchainAddr) - if err != nil { - return nil, "", err - } - var forfeits []string if len(vtxos) > 0 { signedForfeits, err := a.createAndSignForfeits( - ctx, vtxos, event.Connectors, event.MinRelayFeeRate, myPubkey, + ctx, vtxos, event.Connectors, event.MinRelayFeeRate, ) if err != nil { return nil, "", err @@ -1724,6 +1752,15 @@ func (a *covenantlessArkClient) handleRoundFinalization( return nil, "", err } + var myPubkey *secp256k1.PublicKey + + switch v := boardingVtxoScript.(type) { + case *bitcointree.DefaultVtxoScript: + myPubkey = v.Owner + default: + return nil, "", fmt.Errorf("unsupported boarding descriptor: %s", boardingDescriptor) + } + // add tapscript leaf forfeitClosure := &bitcointree.MultisigClosure{ Pubkey: myPubkey, @@ -1869,15 +1906,12 @@ func (a *covenantlessArkClient) validateOffChainReceiver( ) error { found := false - receiverVtxoScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor) + rcvAddr, err := common.DecodeAddress(receiver.Address) if err != nil { return err } - outputTapKey, _, err := receiverVtxoScript.TapTree() - if err != nil { - return err - } + vtxoTapKey := schnorr.SerializePubKey(rcvAddr.VtxoTapKey) leaves := congestionTree.Leaves() for _, leaf := range leaves { @@ -1891,9 +1925,7 @@ func (a *covenantlessArkClient) validateOffChainReceiver( continue } - if bytes.Equal( - output.PkScript[2:], schnorr.SerializePubKey(outputTapKey), - ) { + if bytes.Equal(output.PkScript[2:], vtxoTapKey) { if output.Value != int64(receiver.Amount) { continue } @@ -1919,10 +1951,9 @@ func (a *covenantlessArkClient) validateOffChainReceiver( func (a *covenantlessArkClient) createAndSignForfeits( ctx context.Context, - vtxosToSign []client.Vtxo, + vtxosToSign []client.DescriptorVtxo, connectors []string, feeRate chainfee.SatPerKVByte, - myPubkey *secp256k1.PublicKey, ) ([]string, error) { parsedForfeitAddr, err := btcutil.DecodeAddress(a.ForfeitAddress, nil) if err != nil { @@ -1982,9 +2013,21 @@ func (a *covenantlessArkClient) createAndSignForfeits( Index: vtxo.VOut, } - forfeitClosure := &bitcointree.MultisigClosure{ - Pubkey: myPubkey, - AspPubkey: a.AspPubkey, + var forfeitClosure bitcointree.Closure + + switch v := vtxoScript.(type) { + case *bitcointree.DefaultVtxoScript: + forfeitClosure = &bitcointree.MultisigClosure{ + Pubkey: v.Owner, + AspPubkey: a.AspPubkey, + } + case *bitcointree.ReversibleVtxoScript: + forfeitClosure = &bitcointree.MultisigClosure{ + Pubkey: v.Owner, + AspPubkey: a.AspPubkey, + } + default: + return nil, fmt.Errorf("unsupported vtxo script: %T", vtxoScript) } forfeitLeaf, err := forfeitClosure.Leaf() @@ -2040,39 +2083,28 @@ func (a *covenantlessArkClient) createAndSignForfeits( func (a *covenantlessArkClient) coinSelectOnchain( ctx context.Context, targetAmount uint64, exclude []explorer.Utxo, ) ([]explorer.Utxo, uint64, error) { - offchainAddrs, boardingAddrs, redemptionAddrs, err := a.wallet.GetAddresses(ctx) + _, boardingAddrs, redemptionAddrs, err := a.wallet.GetAddresses(ctx) if err != nil { return nil, 0, err } - _, myPubkey, _, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return nil, 0, err - } - - myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(myPubkey)) - descriptorStr := strings.ReplaceAll( - a.BoardingDescriptorTemplate, "USER", myPubkeyStr, - ) - - boardingScript, err := bitcointree.ParseVtxoScript(descriptorStr) - if err != nil { - return nil, 0, err - } - - var boardingTimeout uint - - if defaultVtxo, ok := boardingScript.(*bitcointree.DefaultVtxoScript); ok { - boardingTimeout = defaultVtxo.ExitDelay - } else { - return nil, 0, fmt.Errorf("unsupported boarding descriptor: %s", descriptorStr) - } - now := time.Now() fetchedUtxos := make([]explorer.Utxo, 0) for _, addr := range boardingAddrs { - utxos, err := a.explorer.GetUtxos(addr) + boardingScript, err := bitcointree.ParseVtxoScript(addr.Descriptor) + if err != nil { + return nil, 0, err + } + + var boardingTimeout uint + + if defaultVtxo, ok := boardingScript.(*bitcointree.DefaultVtxoScript); ok { + boardingTimeout = defaultVtxo.ExitDelay + } else { + return nil, 0, fmt.Errorf("unsupported boarding descriptor: %s", addr.Descriptor) + } + utxos, err := a.explorer.GetUtxos(addr.Address) if err != nil { return nil, 0, err } @@ -2108,7 +2140,7 @@ func (a *covenantlessArkClient) coinSelectOnchain( fetchedUtxos = make([]explorer.Utxo, 0) for _, addr := range redemptionAddrs { - utxos, err := a.explorer.GetUtxos(addr) + utxos, err := a.explorer.GetUtxos(addr.Address) if err != nil { return nil, 0, err } @@ -2185,11 +2217,11 @@ func (a *covenantlessArkClient) getRedeemBranches( // Currently, the returned balance is calculated from both spendable and // pending vtxos. func (a *covenantlessArkClient) getOffchainBalance( - ctx context.Context, addr string, computeVtxoExpiration bool, + ctx context.Context, computeVtxoExpiration bool, ) (uint64, map[int64]uint64, error) { amountByExpiration := make(map[int64]uint64, 0) - vtxos, _, err := a.getVtxos(ctx, addr, computeVtxoExpiration) + vtxos, _, err := a.getVtxos(ctx, computeVtxoExpiration) if err != nil { return 0, nil, err } @@ -2222,13 +2254,13 @@ func (a *covenantlessArkClient) getAllBoardingUtxos( utxos := []explorer.Utxo{} ignoreVtxos := make(map[string]struct{}, 0) for _, addr := range boardingAddrs { - txs, err := a.explorer.GetTxs(addr) + txs, err := a.explorer.GetTxs(addr.Address) if err != nil { return nil, nil, err } for _, tx := range txs { for i, vout := range tx.Vout { - if vout.Address == addr { + if vout.Address == addr.Address { spentStatuses, err := a.explorer.GetTxOutspends(tx.Txid) if err != nil { return nil, nil, err @@ -2255,39 +2287,29 @@ func (a *covenantlessArkClient) getAllBoardingUtxos( } func (a *covenantlessArkClient) getClaimableBoardingUtxos(ctx context.Context) ([]explorer.Utxo, error) { - offchainAddrs, boardingAddrs, _, err := a.wallet.GetAddresses(ctx) + _, boardingAddrs, _, err := a.wallet.GetAddresses(ctx) if err != nil { return nil, err } - _, myPubkey, _, err := common.DecodeAddress(offchainAddrs[0]) - if err != nil { - return nil, err - } - - myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(myPubkey)) - descriptorStr := strings.ReplaceAll( - a.BoardingDescriptorTemplate, "USER", myPubkeyStr, - ) - - boardingScript, err := bitcointree.ParseVtxoScript(descriptorStr) - if err != nil { - return nil, err - } - - var boardingTimeout uint - - if defaultVtxo, ok := boardingScript.(*bitcointree.DefaultVtxoScript); ok { - boardingTimeout = defaultVtxo.ExitDelay - } else { - return nil, fmt.Errorf("unsupported boarding descriptor: %s", descriptorStr) - } - claimable := make([]explorer.Utxo, 0) now := time.Now() for _, addr := range boardingAddrs { - boardingUtxos, err := a.explorer.GetUtxos(addr) + boardingScript, err := bitcointree.ParseVtxoScript(addr.Descriptor) + if err != nil { + return nil, err + } + + var boardingTimeout uint + + if defaultVtxo, ok := boardingScript.(*bitcointree.DefaultVtxoScript); ok { + boardingTimeout = defaultVtxo.ExitDelay + } else { + return nil, fmt.Errorf("unsupported boarding descriptor: %s", addr.Descriptor) + } + + boardingUtxos, err := a.explorer.GetUtxos(addr.Address) if err != nil { return nil, err } @@ -2306,7 +2328,7 @@ func (a *covenantlessArkClient) getClaimableBoardingUtxos(ctx context.Context) ( } func (a *covenantlessArkClient) getVtxos( - ctx context.Context, _ string, computeVtxoExpiration bool, + ctx context.Context, computeVtxoExpiration bool, ) ([]client.Vtxo, []client.Vtxo, error) { spendableVtxos, _, err := a.ListVtxos(ctx) if err != nil { @@ -2347,14 +2369,10 @@ func (a *covenantlessArkClient) getVtxos( } func (a *covenantlessArkClient) selfTransferAllPendingPayments( - ctx context.Context, pendingVtxos []client.Vtxo, boardingUtxos []explorer.Utxo, myself client.Output, mypubkey string, + ctx context.Context, pendingVtxos []client.DescriptorVtxo, boardingUtxos []explorer.Utxo, myself client.Output, boardingDescriptor string, ) (string, error) { inputs := make([]client.Input, 0, len(pendingVtxos)+len(boardingUtxos)) - boardingDescriptor := strings.ReplaceAll( - a.BoardingDescriptorTemplate, "USER", mypubkey[2:], - ) - for _, coin := range pendingVtxos { inputs = append(inputs, client.Input{ Outpoint: client.Outpoint{ @@ -2404,42 +2422,6 @@ func (a *covenantlessArkClient) selfTransferAllPendingPayments( return roundTxid, nil } -func (a *covenantlessArkClient) offchainAddressToReversibleVtxoDescriptor(myaddr string, receiveraddr string) (string, error) { - _, receiverPubkey, aspPubkey, err := common.DecodeAddress(receiveraddr) - if err != nil { - return "", err - } - - _, userPubKey, _, err := common.DecodeAddress(myaddr) - if err != nil { - return "", err - } - - vtxoScript := bitcointree.ReversibleVtxoScript{ - Owner: receiverPubkey, - Sender: userPubKey, - Asp: aspPubkey, - ExitDelay: uint(a.UnilateralExitDelay), - } - - return vtxoScript.ToDescriptor(), nil -} - -func (a *covenantlessArkClient) offchainAddressToDefaultVtxoDescriptor(addr string) (string, error) { - _, userPubKey, aspPubkey, err := common.DecodeAddress(addr) - if err != nil { - return "", err - } - - vtxoScript := bitcointree.DefaultVtxoScript{ - Owner: userPubKey, - Asp: aspPubkey, - ExitDelay: uint(a.UnilateralExitDelay), - } - - return vtxoScript.ToDescriptor(), nil -} - // getBoardingTxs builds the boarding tx history from onchain utxos: // - unspent utxo => pending boarding tx // - spent utxo => claimed boarding tx diff --git a/pkg/client-sdk/internal/utils/utils.go b/pkg/client-sdk/internal/utils/utils.go index d8504cc..4e4f50e 100644 --- a/pkg/client-sdk/internal/utils/utils.go +++ b/pkg/client-sdk/internal/utils/utils.go @@ -23,10 +23,10 @@ import ( ) func CoinSelect( - vtxos []client.Vtxo, amount, dust uint64, sortByExpirationTime bool, -) ([]client.Vtxo, uint64, error) { - selected := make([]client.Vtxo, 0) - notSelected := make([]client.Vtxo, 0) + vtxos []client.DescriptorVtxo, amount, dust uint64, sortByExpirationTime bool, +) ([]client.DescriptorVtxo, uint64, error) { + selected := make([]client.DescriptorVtxo, 0) + notSelected := make([]client.DescriptorVtxo, 0) selectedAmount := uint64(0) if sortByExpirationTime { diff --git a/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go b/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go index 0f0aef8..af287dd 100644 --- a/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go +++ b/pkg/client-sdk/wallet/singlekey/bitcoin_wallet.go @@ -43,41 +43,96 @@ func NewBitcoinWallet( func (w *bitcoinWallet) GetAddresses( ctx context.Context, -) ([]string, []string, []string, error) { - offchainAddr, boardingAddr, redemptionAddr, err := w.getAddress(ctx) +) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, []wallet.DescriptorAddress, error) { + offchainAddr, boardingAddr, err := w.getAddress(ctx) if err != nil { return nil, nil, nil, err } - offchainAddrs := []string{offchainAddr} - boardingAddrs := []string{boardingAddr} - redemptionAddrs := []string{redemptionAddr} + encodedOffchainAddr, err := offchainAddr.Address.Encode() + if err != nil { + return nil, nil, nil, err + } + + data, err := w.configStore.GetData(ctx) + if err != nil { + return nil, nil, nil, err + } + + netParams := utils.ToBitcoinNetwork(data.Network) + + redemptionAddr, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(offchainAddr.Address.VtxoTapKey), + &netParams, + ) + if err != nil { + return nil, nil, nil, err + } + + offchainAddrs := []wallet.DescriptorAddress{ + { + Descriptor: offchainAddr.Descriptor, + Address: encodedOffchainAddr, + }, + } + boardingAddrs := []wallet.DescriptorAddress{ + { + Descriptor: boardingAddr.Descriptor, + Address: boardingAddr.Address, + }, + } + redemptionAddrs := []wallet.DescriptorAddress{ + { + Descriptor: offchainAddr.Descriptor, + Address: redemptionAddr.EncodeAddress(), + }, + } return offchainAddrs, boardingAddrs, redemptionAddrs, nil } func (w *bitcoinWallet) NewAddress( ctx context.Context, _ bool, -) (string, string, error) { - offchainAddr, boardingAddr, _, err := w.getAddress(ctx) - if err != nil { - return "", "", err - } - return offchainAddr, boardingAddr, nil -} - -func (w *bitcoinWallet) NewAddresses( - ctx context.Context, _ bool, num int, -) ([]string, []string, error) { - offchainAddr, boardingAddr, _, err := w.getAddress(ctx) +) (*wallet.DescriptorAddress, *wallet.DescriptorAddress, error) { + offchainAddr, boardingAddr, err := w.getAddress(ctx) if err != nil { return nil, nil, err } - offchainAddrs := make([]string, 0, num) - boardingAddrs := make([]string, 0, num) + encodedOffchainAddr, err := offchainAddr.Address.Encode() + if err != nil { + return nil, nil, err + } + + return &wallet.DescriptorAddress{ + Descriptor: offchainAddr.Descriptor, + Address: encodedOffchainAddr, + }, boardingAddr, nil +} + +func (w *bitcoinWallet) NewAddresses( + ctx context.Context, _ bool, num int, +) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, error) { + offchainAddr, boardingAddr, err := w.getAddress(ctx) + if err != nil { + return nil, nil, err + } + + offchainAddrs := make([]wallet.DescriptorAddress, 0, num) + boardingAddrs := make([]wallet.DescriptorAddress, 0, num) for i := 0; i < num; i++ { - offchainAddrs = append(offchainAddrs, offchainAddr) - boardingAddrs = append(boardingAddrs, boardingAddr) + encodedOffchainAddr, err := offchainAddr.Address.Encode() + if err != nil { + return nil, nil, err + } + + offchainAddrs = append(offchainAddrs, wallet.DescriptorAddress{ + Descriptor: offchainAddr.Descriptor, + Address: encodedOffchainAddr, + }) + boardingAddrs = append(boardingAddrs, wallet.DescriptorAddress{ + Descriptor: boardingAddr.Descriptor, + Address: boardingAddr.Address, + }) } return offchainAddrs, boardingAddrs, nil } @@ -205,19 +260,21 @@ func (s *bitcoinWallet) SignTransaction( func (w *bitcoinWallet) getAddress( ctx context.Context, -) (string, string, string, error) { +) ( + *struct { + Address common.Address + Descriptor string + }, + *wallet.DescriptorAddress, + error, +) { if w.walletData == nil { - return "", "", "", fmt.Errorf("wallet not initialized") + return nil, nil, fmt.Errorf("wallet not initialized") } data, err := w.configStore.GetData(ctx) if err != nil { - return "", "", "", err - } - - offchainAddr, err := common.EncodeAddress(data.Network.Addr, w.walletData.Pubkey, data.AspPubkey) - if err != nil { - return "", "", "", err + return nil, nil, err } netParams := utils.ToBitcoinNetwork(data.Network) @@ -230,15 +287,13 @@ func (w *bitcoinWallet) getAddress( vtxoTapKey, _, err := defaultVtxoScript.TapTree() if err != nil { - return "", "", "", err + return nil, nil, err } - redemptionAddr, err := btcutil.NewAddressTaproot( - schnorr.SerializePubKey(vtxoTapKey), - &netParams, - ) - if err != nil { - return "", "", "", err + offchainAddress := &common.Address{ + HRP: data.Network.Addr, + Asp: data.AspPubkey, + VtxoTapKey: vtxoTapKey, } myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey)) @@ -248,12 +303,12 @@ func (w *bitcoinWallet) getAddress( boardingVtxoScript, err := bitcointree.ParseVtxoScript(descriptorStr) if err != nil { - return "", "", "", err + return nil, nil, err } boardingTapKey, _, err := boardingVtxoScript.TapTree() if err != nil { - return "", "", "", err + return nil, nil, err } boardingAddr, err := btcutil.NewAddressTaproot( @@ -261,8 +316,18 @@ func (w *bitcoinWallet) getAddress( &netParams, ) if err != nil { - return "", "", "", err + return nil, nil, err } - return offchainAddr, boardingAddr.EncodeAddress(), redemptionAddr.EncodeAddress(), nil + return &struct { + Address common.Address + Descriptor string + }{ + *offchainAddress, defaultVtxoScript.ToDescriptor(), + }, + &wallet.DescriptorAddress{ + Descriptor: descriptorStr, + Address: boardingAddr.EncodeAddress(), + }, + nil } diff --git a/pkg/client-sdk/wallet/singlekey/liquid_wallet.go b/pkg/client-sdk/wallet/singlekey/liquid_wallet.go index 316eb5f..8aee2d7 100644 --- a/pkg/client-sdk/wallet/singlekey/liquid_wallet.go +++ b/pkg/client-sdk/wallet/singlekey/liquid_wallet.go @@ -44,41 +44,103 @@ func NewLiquidWallet( func (w *liquidWallet) GetAddresses( ctx context.Context, -) ([]string, []string, []string, error) { - offchainAddr, boardingAddr, redemptionAddr, err := w.getAddress(ctx) +) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, []wallet.DescriptorAddress, error) { + offchainAddr, boardingAddr, err := w.getAddress(ctx) if err != nil { return nil, nil, nil, err } - offchainAddrs := []string{offchainAddr} - boardingAddrs := []string{boardingAddr} - redemptionAddrs := []string{redemptionAddr} + encodedOffchainAddr, err := offchainAddr.Address.Encode() + if err != nil { + return nil, nil, nil, err + } + + data, err := w.configStore.GetData(ctx) + if err != nil { + return nil, nil, nil, err + } + + liquidNet := utils.ToElementsNetwork(data.Network) + + vtxoP2TR, err := payment.FromTweakedKey(offchainAddr.Address.VtxoTapKey, &liquidNet, nil) + if err != nil { + return nil, nil, nil, err + } + + redemptionAddr, err := vtxoP2TR.TaprootAddress() + if err != nil { + return nil, nil, nil, err + } + + offchainAddrs := []wallet.DescriptorAddress{ + { + Descriptor: offchainAddr.Descriptor, + Address: encodedOffchainAddr, + }, + } + boardingAddrs := []wallet.DescriptorAddress{ + { + Descriptor: boardingAddr.Descriptor, + Address: boardingAddr.Address, + }, + } + + redemptionAddrs := []wallet.DescriptorAddress{ + { + Descriptor: offchainAddr.Descriptor, + Address: redemptionAddr, + }, + } + return offchainAddrs, boardingAddrs, redemptionAddrs, nil } func (w *liquidWallet) NewAddress( ctx context.Context, _ bool, -) (string, string, error) { - offchainAddr, boardingAddr, _, err := w.getAddress(ctx) - if err != nil { - return "", "", err - } - return offchainAddr, boardingAddr, nil -} - -func (w *liquidWallet) NewAddresses( - ctx context.Context, _ bool, num int, -) ([]string, []string, error) { - offchainAddr, boardingAddr, _, err := w.getAddress(ctx) +) (*wallet.DescriptorAddress, *wallet.DescriptorAddress, error) { + offchainAddr, boardingAddr, err := w.getAddress(ctx) if err != nil { return nil, nil, err } - offchainAddrs := make([]string, 0, num) - boardingAddrs := make([]string, 0, num) + encodedOffchainAddr, err := offchainAddr.Address.Encode() + if err != nil { + return nil, nil, err + } + + return &wallet.DescriptorAddress{ + Descriptor: offchainAddr.Descriptor, + Address: encodedOffchainAddr, + }, &wallet.DescriptorAddress{ + Descriptor: boardingAddr.Descriptor, + Address: boardingAddr.Address, + }, nil +} + +func (w *liquidWallet) NewAddresses( + ctx context.Context, _ bool, num int, +) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, error) { + offchainAddr, boardingAddr, err := w.getAddress(ctx) + if err != nil { + return nil, nil, err + } + + offchainAddrs := make([]wallet.DescriptorAddress, 0, num) + boardingAddrs := make([]wallet.DescriptorAddress, 0, num) for i := 0; i < num; i++ { - offchainAddrs = append(offchainAddrs, offchainAddr) - boardingAddrs = append(boardingAddrs, boardingAddr) + encodedOffchainAddr, err := offchainAddr.Address.Encode() + if err != nil { + return nil, nil, err + } + + offchainAddrs = append(offchainAddrs, wallet.DescriptorAddress{ + Descriptor: offchainAddr.Descriptor, + Address: encodedOffchainAddr, + }) + boardingAddrs = append(boardingAddrs, wallet.DescriptorAddress{ + Descriptor: boardingAddr.Descriptor, + Address: boardingAddr.Address, + }) } return offchainAddrs, boardingAddrs, nil } @@ -227,21 +289,21 @@ func (s *liquidWallet) SignTransaction( func (w *liquidWallet) getAddress( ctx context.Context, -) (string, string, string, error) { +) ( + *struct { + Address common.Address + Descriptor string + }, + *wallet.DescriptorAddress, + error, +) { if w.walletData == nil { - return "", "", "", fmt.Errorf("wallet not initialized") + return nil, nil, fmt.Errorf("wallet not initialized") } data, err := w.configStore.GetData(ctx) if err != nil { - return "", "", "", err - } - - offchainAddr, err := common.EncodeAddress( - data.Network.Addr, w.walletData.Pubkey, data.AspPubkey, - ) - if err != nil { - return "", "", "", err + return nil, nil, err } liquidNet := utils.ToElementsNetwork(data.Network) @@ -254,17 +316,13 @@ func (w *liquidWallet) getAddress( vtxoTapKey, _, err := vtxoScript.TapTree() if err != nil { - return "", "", "", err + return nil, nil, err } - vtxoP2tr, err := payment.FromTweakedKey(vtxoTapKey, &liquidNet, nil) - if err != nil { - return "", "", "", err - } - - redemptionAddr, err := vtxoP2tr.TaprootAddress() - if err != nil { - return "", "", "", err + offchainAddr := &common.Address{ + HRP: data.Network.Addr, + Asp: data.AspPubkey, + VtxoTapKey: vtxoTapKey, } myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey)) @@ -274,23 +332,32 @@ func (w *liquidWallet) getAddress( onboardingScript, err := tree.ParseVtxoScript(descriptorStr) if err != nil { - return "", "", "", err + return nil, nil, err } tapKey, _, err := onboardingScript.TapTree() if err != nil { - return "", "", "", err + return nil, nil, err } p2tr, err := payment.FromTweakedKey(tapKey, &liquidNet, nil) if err != nil { - return "", "", "", err + return nil, nil, err } boardingAddr, err := p2tr.TaprootAddress() if err != nil { - return "", "", "", err + return nil, nil, err } - return offchainAddr, boardingAddr, redemptionAddr, nil + return &struct { + Address common.Address + Descriptor string + }{ + Address: *offchainAddr, + Descriptor: vtxoScript.ToDescriptor(), + }, &wallet.DescriptorAddress{ + Descriptor: descriptorStr, + Address: boardingAddr, + }, nil } diff --git a/pkg/client-sdk/wallet/wallet.go b/pkg/client-sdk/wallet/wallet.go index 6d0319e..cb36d3c 100644 --- a/pkg/client-sdk/wallet/wallet.go +++ b/pkg/client-sdk/wallet/wallet.go @@ -10,6 +10,11 @@ const ( SingleKeyWallet = "singlekey" ) +type DescriptorAddress struct { + Descriptor string + Address string +} + type WalletService interface { GetType() string Create( @@ -20,13 +25,13 @@ type WalletService interface { IsLocked() bool GetAddresses( ctx context.Context, - ) (offchainAddresses, boardingAddresses, redemptionAddresses []string, err error) + ) (offchainAddresses, boardingAddresses, redemptionAddresses []DescriptorAddress, err error) NewAddress( ctx context.Context, change bool, - ) (offchainAddr, onchainAddr string, err error) + ) (offchainAddr, onchainAddr *DescriptorAddress, err error) NewAddresses( ctx context.Context, change bool, num int, - ) (offchainAddresses, onchainAddresses []string, err error) + ) (offchainAddresses, onchainAddresses []DescriptorAddress, err error) SignTransaction( ctx context.Context, explorerSvc explorer.Explorer, tx string, ) (signedTx string, err error) diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index abd8b2b..d1e5615 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -156,6 +156,7 @@ func (s *covenantService) GetBoardingAddress(ctx context.Context, userPubkey *se func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input) (string, error) { vtxosInputs := make([]domain.Vtxo, 0) boardingInputs := make([]ports.BoardingInput, 0) + descriptors := make(map[domain.VtxoKey]string) now := time.Now().Unix() @@ -218,13 +219,14 @@ func (s *covenantService) SpendVtxos(ctx context.Context, inputs []ports.Input) } vtxosInputs = append(vtxosInputs, vtxo) + descriptors[vtxo.VtxoKey] = input.Descriptor } payment, err := domain.NewPayment(vtxosInputs) if err != nil { return "", err } - if err := s.paymentRequests.push(*payment, boardingInputs); err != nil { + if err := s.paymentRequests.push(*payment, boardingInputs, descriptors); err != nil { return "", err } return payment.Id, nil @@ -324,7 +326,7 @@ func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx str return fmt.Errorf("unimplemented") } -func (s *covenantService) CreateAsyncPayment(ctx context.Context, inputs []ports.Input, receivers []domain.Receiver) (string, error) { +func (s *covenantService) CreateAsyncPayment(_ context.Context, _ []AsyncPaymentInput, _ []domain.Receiver) (string, error) { return "", fmt.Errorf("unimplemented") } @@ -345,9 +347,14 @@ func (s *covenantService) SignRoundTx(ctx context.Context, signedRoundTx string) return nil } -func (s *covenantService) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, []domain.Vtxo, error) { - pk := hex.EncodeToString(pubkey.SerializeCompressed()) - return s.repoManager.Vtxos().GetAllVtxos(ctx, pk) +func (s *covenantService) ListVtxos(ctx context.Context, address string) ([]domain.Vtxo, []domain.Vtxo, error) { + decodedAddress, err := common.DecodeAddress(address) + if err != nil { + return nil, nil, fmt.Errorf("failed to decode address: %s", err) + } + pubkey := hex.EncodeToString(schnorr.SerializePubKey(decodedAddress.VtxoTapKey)) + + return s.repoManager.Vtxos().GetAllVtxos(ctx, pubkey) } func (s *covenantService) GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent { @@ -482,7 +489,7 @@ func (s *covenantService) startFinalization() { if num > paymentsThreshold { num = paymentsThreshold } - payments, boardingInputs, _ := s.paymentRequests.pop(num) + payments, boardingInputs, descriptors, _ := 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") @@ -517,7 +524,7 @@ func (s *covenantService) startFinalization() { minRelayFeeRate := s.wallet.MinRelayFeeRate(ctx) if needForfeits { - connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedPoolTx, payments, minRelayFeeRate) + connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedPoolTx, payments, descriptors, minRelayFeeRate) if err != nil { round.Fail(fmt.Errorf("failed to create connectors and forfeit txs: %s", err)) log.WithError(err).Warn("failed to create connectors and forfeit txs") @@ -933,53 +940,20 @@ func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo { continue // skip fee outputs } - desc := "" - found := false - - for _, p := range round.Payments { - if found { - break - } - - for _, r := range p.Receivers { - if r.IsOnchain() { - continue - } - - vtxoScript, err := tree.ParseVtxoScript(r.Descriptor) - if err != nil { - log.WithError(err).Warn("failed to parse vtxo descriptor") - continue - } - - tapKey, _, err := vtxoScript.TapTree() - if err != nil { - log.WithError(err).Warn("failed to compute vtxo tap key") - continue - } - - script, err := common.P2TRScript(tapKey) - if err != nil { - log.WithError(err).Warn("failed to create vtxo scriptpubkey") - continue - } - - if bytes.Equal(script, out.Script) { - found = true - desc = r.Descriptor - break - } - } + vtxoTapKey, err := schnorr.ParsePubKey(out.Script[2:]) + if err != nil { + log.WithError(err).Warn("failed to parse vtxo tap key") + continue } - if found { - vtxos = append(vtxos, domain.Vtxo{ - VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)}, - Receiver: domain.Receiver{Descriptor: desc, Amount: uint64(out.Value)}, - RoundTxid: round.Txid, - }) - break - } + vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)) + + vtxos = append(vtxos, domain.Vtxo{ + VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)}, + Pubkey: vtxoPubkey, + Amount: uint64(out.Value), + RoundTxid: round.Txid, + }) } } return vtxos @@ -1050,17 +1024,17 @@ func (s *covenantService) restoreWatchingVtxos() error { func (s *covenantService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string, error) { indexedScripts := make(map[string]struct{}) for _, vtxo := range vtxos { - vtxoScript, err := tree.ParseVtxoScript(vtxo.Receiver.Descriptor) + vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey) if err != nil { return nil, err } - tapKey, _, err := vtxoScript.TapTree() + vtxoTapKey, err := schnorr.ParsePubKey(vtxoTapKeyBytes) if err != nil { return nil, err } - script, err := common.P2TRScript(tapKey) + script, err := common.P2TRScript(vtxoTapKey) if err != nil { return nil, err } diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index a0a736b..4a8693b 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -19,6 +19,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/decred/dcrd/dcrec/secp256k1/v4" @@ -177,37 +178,28 @@ func (s *covenantlessService) CompleteAsyncPayment( // verify that the vtxo is spendable - vtxo, err := vtxoRepo.GetVtxos(ctx, []domain.VtxoKey{{Txid: vtxoOutpoint.Hash.String(), VOut: vtxoOutpoint.Index}}) + vtxos, err := vtxoRepo.GetVtxos(ctx, []domain.VtxoKey{{Txid: vtxoOutpoint.Hash.String(), VOut: vtxoOutpoint.Index}}) if err != nil { return fmt.Errorf("failed to get vtxo: %s", err) } - if len(vtxo) == 0 { + if len(vtxos) == 0 { return fmt.Errorf("vtxo not found") } - if vtxo[0].Spent { + vtxo := vtxos[0] + if vtxo.Spent { return fmt.Errorf("vtxo already spent") } - if vtxo[0].Redeemed { + if vtxo.Redeemed { return fmt.Errorf("vtxo already redeemed") } - if vtxo[0].Swept { + if vtxo.Swept { return fmt.Errorf("vtxo already swept") } - vtxoScript, err := bitcointree.ParseVtxoScript(vtxo[0].Descriptor) - if err != nil { - return fmt.Errorf("failed to parse vtxo script: %s", err) - } - - vtxoTapKey, _, err := vtxoScript.TapTree() - if err != nil { - return fmt.Errorf("failed to get taproot key: %s", err) - } - // verify that the user signs a forfeit closure var userPubKey *secp256k1.PublicKey @@ -228,6 +220,16 @@ func (s *covenantlessService) CompleteAsyncPayment( return fmt.Errorf("redeem transaction is not signed") } + vtxoPublicKeyBytes, err := hex.DecodeString(vtxo.Pubkey) + if err != nil { + return fmt.Errorf("failed to decode vtxo pubkey: %s", err) + } + + vtxoTapKey, err := schnorr.ParsePubKey(vtxoPublicKeyBytes) + if err != nil { + return fmt.Errorf("failed to parse vtxo pubkey: %s", err) + } + // verify witness utxo pkscript, err := common.P2TRScript(vtxoTapKey) if err != nil { @@ -238,7 +240,7 @@ func (s *covenantlessService) CompleteAsyncPayment( return fmt.Errorf("witness utxo script mismatch") } - if input.WitnessUtxo.Value != int64(vtxo[0].Amount) { + if input.WitnessUtxo.Value != int64(vtxo.Amount) { return fmt.Errorf("witness utxo value mismatch") } } @@ -260,19 +262,23 @@ func (s *covenantlessService) CompleteAsyncPayment( vtxos := make([]domain.Vtxo, 0, len(asyncPayData.receivers)) for outIndex, out := range redeemPtx.UnsignedTx.TxOut { - desc := asyncPayData.receivers[outIndex].Descriptor - _, _, _, _, err := descriptor.ParseReversibleVtxoDescriptor(desc) - isPending := err == nil + vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:]) + if err != nil { + return fmt.Errorf("failed to parse vtxo taproot key: %s", err) + } + + vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)) + + // all pending except the last one + isPending := outIndex < len(asyncPayData.receivers)-1 vtxos = append(vtxos, domain.Vtxo{ VtxoKey: domain.VtxoKey{ Txid: redeemTxid, VOut: uint32(outIndex), }, - Receiver: domain.Receiver{ - Descriptor: desc, - Amount: uint64(out.Value), - }, + Pubkey: vtxoPubkey, + Amount: uint64(out.Value), ExpireAt: asyncPayData.expireAt, RedeemTx: redeemTx, Pending: isPending, @@ -309,11 +315,16 @@ func (s *covenantlessService) CompleteAsyncPayment( } func (s *covenantlessService) CreateAsyncPayment( - ctx context.Context, inputs []ports.Input, receivers []domain.Receiver, + ctx context.Context, inputs []AsyncPaymentInput, receivers []domain.Receiver, ) (string, error) { vtxosKeys := make([]domain.VtxoKey, 0, len(inputs)) + descriptors := make(map[domain.VtxoKey]string) + forfeitLeaves := make(map[domain.VtxoKey]chainhash.Hash) + for _, in := range inputs { vtxosKeys = append(vtxosKeys, in.VtxoKey) + descriptors[in.VtxoKey] = in.Descriptor + forfeitLeaves[in.VtxoKey] = in.ForfeitLeafHash } vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, vtxosKeys) @@ -351,7 +362,7 @@ func (s *covenantlessService) CreateAsyncPayment( } redeemTx, err := s.builder.BuildAsyncPaymentTransactions( - vtxosInputs, s.pubkey, receivers, + vtxosInputs, descriptors, forfeitLeaves, receivers, ) if err != nil { return "", fmt.Errorf("failed to build async payment txs: %s", err) @@ -404,7 +415,7 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp now := time.Now().Unix() boardingTxs := make(map[string]wire.MsgTx, 0) // txid -> txhex - + descriptors := make(map[domain.VtxoKey]string) for _, input := range inputs { vtxosResult, err := s.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{input.VtxoKey}) if err != nil || len(vtxosResult) == 0 { @@ -461,6 +472,8 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp return "", fmt.Errorf("input %s:%d already swept", vtxo.Txid, vtxo.VOut) } + descriptors[vtxo.VtxoKey] = input.Descriptor + vtxosInputs = append(vtxosInputs, vtxo) } @@ -468,7 +481,7 @@ func (s *covenantlessService) SpendVtxos(ctx context.Context, inputs []ports.Inp if err != nil { return "", err } - if err := s.paymentRequests.push(*payment, boardingInputs); err != nil { + if err := s.paymentRequests.push(*payment, boardingInputs, descriptors); err != nil { return "", err } return payment.Id, nil @@ -572,9 +585,14 @@ func (s *covenantlessService) SignRoundTx(ctx context.Context, signedRoundTx str return nil } -func (s *covenantlessService) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, []domain.Vtxo, error) { - pk := hex.EncodeToString(pubkey.SerializeCompressed()) - return s.repoManager.Vtxos().GetAllVtxos(ctx, pk) +func (s *covenantlessService) ListVtxos(ctx context.Context, address string) ([]domain.Vtxo, []domain.Vtxo, error) { + decodedAddress, err := common.DecodeAddress(address) + if err != nil { + return nil, nil, fmt.Errorf("failed to decode address: %s", err) + } + pubkey := hex.EncodeToString(schnorr.SerializePubKey(decodedAddress.VtxoTapKey)) + + return s.repoManager.Vtxos().GetAllVtxos(ctx, pubkey) } func (s *covenantlessService) GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent { @@ -771,7 +789,7 @@ func (s *covenantlessService) startFinalization() { if num > paymentsThreshold { num = paymentsThreshold } - payments, boardingInputs, cosigners := s.paymentRequests.pop(num) + payments, boardingInputs, descriptors, cosigners := s.paymentRequests.pop(num) if len(payments) > len(cosigners) { err := fmt.Errorf("missing ephemeral key for payments") round.Fail(fmt.Errorf("round aborted: %s", err)) @@ -973,7 +991,7 @@ func (s *covenantlessService) startFinalization() { minRelayFeeRate := s.wallet.MinRelayFeeRate(ctx) if needForfeits { - connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedRoundTx, payments, minRelayFeeRate) + connectors, forfeitTxs, err = s.builder.BuildForfeitTxs(unsignedRoundTx, payments, descriptors, minRelayFeeRate) if err != nil { round.Fail(fmt.Errorf("failed to create connectors and forfeit txs: %s", err)) log.WithError(err).Warn("failed to create connectors and forfeit txs") @@ -1332,53 +1350,18 @@ func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo { continue } for i, out := range tx.UnsignedTx.TxOut { - desc := "" - found := false - - for _, p := range round.Payments { - if found { - break - } - - for _, r := range p.Receivers { - if r.IsOnchain() { - continue - } - - vtxoScript, err := bitcointree.ParseVtxoScript(r.Descriptor) - if err != nil { - log.WithError(err).Warn("failed to parse vtxo descriptor") - continue - } - - tapKey, _, err := vtxoScript.TapTree() - if err != nil { - log.WithError(err).Warn("failed to compute vtxo tap key") - continue - } - - script, err := common.P2TRScript(tapKey) - if err != nil { - log.WithError(err).Warn("failed to create vtxo scriptpubkey") - continue - } - - if bytes.Equal(script, out.PkScript) { - found = true - desc = r.Descriptor - break - } - } + vtxoTapKey, err := schnorr.ParsePubKey(out.PkScript[2:]) + if err != nil { + log.WithError(err).Warn("failed to parse vtxo tap key") + continue } - if found { - vtxos = append(vtxos, domain.Vtxo{ - VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)}, - Receiver: domain.Receiver{Descriptor: desc, Amount: uint64(out.Value)}, - RoundTxid: round.Txid, - }) - break - } + vtxos = append(vtxos, domain.Vtxo{ + VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)}, + Pubkey: hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)), + Amount: uint64(out.Value), + RoundTxid: round.Txid, + }) } } return vtxos @@ -1450,17 +1433,17 @@ func (s *covenantlessService) extractVtxosScripts(vtxos []domain.Vtxo) ([]string indexedScripts := make(map[string]struct{}) for _, vtxo := range vtxos { - vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Receiver.Descriptor) + vtxoTapKeyBytes, err := hex.DecodeString(vtxo.Pubkey) if err != nil { return nil, err } - tapKey, _, err := vtxoScript.TapTree() + vtxoTapKey, err := schnorr.ParsePubKey(vtxoTapKeyBytes) if err != nil { return nil, err } - script, err := common.P2TRScript(tapKey) + script, err := common.P2TRScript(vtxoTapKey) if err != nil { return nil, err } diff --git a/server/internal/core/application/types.go b/server/internal/core/application/types.go index a833af5..9c0228d 100644 --- a/server/internal/core/application/types.go +++ b/server/internal/core/application/types.go @@ -5,6 +5,7 @@ import ( "github.com/ark-network/ark/server/internal/core/domain" "github.com/ark-network/ark/server/internal/core/ports" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/decred/dcrd/dcrec/secp256k1/v4" ) @@ -12,6 +13,11 @@ var ( paymentsThreshold = int64(128) ) +type AsyncPaymentInput struct { + ports.Input + ForfeitLeafHash chainhash.Hash +} + type Service interface { Start() error Stop() @@ -27,12 +33,12 @@ type Service interface { ctx context.Context, paymentId string, ) (lastEvent domain.RoundEvent, err error) ListVtxos( - ctx context.Context, pubkey *secp256k1.PublicKey, + ctx context.Context, address string, ) (spendableVtxos, spentVtxos []domain.Vtxo, err error) GetInfo(ctx context.Context) (*ServiceInfo, error) // Async payments CreateAsyncPayment( - ctx context.Context, inputs []ports.Input, receivers []domain.Receiver, + ctx context.Context, inputs []AsyncPaymentInput, receivers []domain.Receiver, ) (string, error) CompleteAsyncPayment( ctx context.Context, redeemTx string, diff --git a/server/internal/core/application/utils.go b/server/internal/core/application/utils.go index f797b40..d45c5d8 100644 --- a/server/internal/core/application/utils.go +++ b/server/internal/core/application/utils.go @@ -24,13 +24,14 @@ type timedPayment struct { type paymentsMap struct { lock *sync.RWMutex payments map[string]*timedPayment + descriptors map[domain.VtxoKey]string ephemeralKeys map[string]*secp256k1.PublicKey } func newPaymentsMap() *paymentsMap { paymentsById := make(map[string]*timedPayment) lock := &sync.RWMutex{} - return &paymentsMap{lock, paymentsById, make(map[string]*secp256k1.PublicKey)} + return &paymentsMap{lock, paymentsById, make(map[domain.VtxoKey]string), make(map[string]*secp256k1.PublicKey)} } func (m *paymentsMap) len() int64 { @@ -58,7 +59,11 @@ func (m *paymentsMap) delete(id string) error { return nil } -func (m *paymentsMap) push(payment domain.Payment, boardingInputs []ports.BoardingInput) error { +func (m *paymentsMap) push( + payment domain.Payment, + boardingInputs []ports.BoardingInput, + descriptors map[domain.VtxoKey]string, +) error { m.lock.Lock() defer m.lock.Unlock() @@ -86,6 +91,10 @@ func (m *paymentsMap) push(payment domain.Payment, boardingInputs []ports.Boardi } } + for key, desc := range descriptors { + m.descriptors[key] = desc + } + m.payments[payment.Id] = &timedPayment{payment, boardingInputs, time.Now(), time.Time{}} return nil } @@ -102,7 +111,7 @@ func (m *paymentsMap) pushEphemeralKey(paymentId string, pubkey *secp256k1.Publi return nil } -func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, []*secp256k1.PublicKey) { +func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, map[domain.VtxoKey]string, []*secp256k1.PublicKey) { m.lock.Lock() defer m.lock.Unlock() @@ -129,6 +138,7 @@ func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, [ payments := make([]domain.Payment, 0, num) boardingInputs := make([]ports.BoardingInput, 0) cosigners := make([]*secp256k1.PublicKey, 0, num) + descriptors := make(map[domain.VtxoKey]string) for _, p := range paymentsByTime[:num] { boardingInputs = append(boardingInputs, p.boardingInputs...) payments = append(payments, p.Payment) @@ -136,9 +146,15 @@ func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, [ cosigners = append(cosigners, pubkey) delete(m.ephemeralKeys, p.Payment.Id) } + for _, input := range payments { + for _, vtxo := range input.Inputs { + descriptors[vtxo.VtxoKey] = m.descriptors[vtxo.VtxoKey] + delete(m.descriptors, vtxo.VtxoKey) + } + } delete(m.payments, p.Id) } - return payments, boardingInputs, cosigners + return payments, boardingInputs, descriptors, cosigners } func (m *paymentsMap) update(payment domain.Payment) error { diff --git a/server/internal/core/domain/payment.go b/server/internal/core/domain/payment.go index 8005f20..1500692 100644 --- a/server/internal/core/domain/payment.go +++ b/server/internal/core/domain/payment.go @@ -68,9 +68,12 @@ func (p Payment) validate(ignoreOuts bool) error { return fmt.Errorf("missing outputs") } for _, r := range p.Receivers { - if len(r.OnchainAddress) <= 0 && len(r.Descriptor) <= 0 { + if len(r.OnchainAddress) <= 0 && len(r.Pubkey) <= 0 { return fmt.Errorf("missing receiver destination") } + if r.Amount == 0 { + return fmt.Errorf("missing receiver amount") + } } return nil } @@ -80,6 +83,10 @@ type VtxoKey struct { VOut uint32 } +func (k VtxoKey) String() string { + return fmt.Sprintf("%s:%d", k.Txid, k.VOut) +} + func (k VtxoKey) Hash() string { calcHash := func(buf []byte, hasher hash.Hash) []byte { _, _ = hasher.Write(buf) @@ -96,9 +103,9 @@ func (k VtxoKey) Hash() string { } type Receiver struct { - Descriptor string Amount uint64 - OnchainAddress string + OnchainAddress string // onchain + Pubkey string // offchain } func (r Receiver) IsOnchain() bool { @@ -107,7 +114,8 @@ func (r Receiver) IsOnchain() bool { type Vtxo struct { VtxoKey - Receiver + Amount uint64 + Pubkey string RoundTxid string SpentBy string // round txid or async redeem txid Spent bool diff --git a/server/internal/core/domain/payment_test.go b/server/internal/core/domain/payment_test.go index a2a07c8..1e4b8b5 100644 --- a/server/internal/core/domain/payment_test.go +++ b/server/internal/core/domain/payment_test.go @@ -1,22 +1,14 @@ package domain_test import ( - "fmt" "testing" - "github.com/ark-network/ark/common/descriptor" "github.com/ark-network/ark/server/internal/core/domain" "github.com/stretchr/testify/require" ) -var desc = fmt.Sprintf( - descriptor.DefaultVtxoDescriptorTemplate, - "030000000000000000000000000000000000000000000000000000000000000001", - "0000000000000000000000000000000000000000000000000000000000000001", - "0000000000000000000000000000000000000000000000000000000000000001", - 512, - "0000000000000000000000000000000000000000000000000000000000000001", -) +// x-only pubkey +const pubkey = "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967" var inputs = []domain.Vtxo{ { @@ -24,10 +16,8 @@ var inputs = []domain.Vtxo{ Txid: "0000000000000000000000000000000000000000000000000000000000000000", VOut: 0, }, - Receiver: domain.Receiver{ - Descriptor: desc, - Amount: 1000, - }, + Pubkey: pubkey, + Amount: 1000, }, } @@ -51,12 +41,12 @@ func TestPayment(t *testing.T) { err = payment.AddReceivers([]domain.Receiver{ { - Descriptor: desc, - Amount: 450, + Pubkey: pubkey, + Amount: 450, }, { - Descriptor: desc, - Amount: 550, + Pubkey: pubkey, + Amount: 550, }, }) require.NoError(t, err) diff --git a/server/internal/core/domain/round_test.go b/server/internal/core/domain/round_test.go index c44cb01..560e98b 100644 --- a/server/internal/core/domain/round_test.go +++ b/server/internal/core/domain/round_test.go @@ -20,24 +20,22 @@ var ( Txid: txid, VOut: 0, }, - Receiver: domain.Receiver{ - Descriptor: desc, - Amount: 2000, - }, + Pubkey: pubkey, + Amount: 2000, }, }, Receivers: []domain.Receiver{ { - Descriptor: desc, - Amount: 700, + Pubkey: pubkey, + Amount: 700, }, { - Descriptor: desc, - Amount: 700, + Pubkey: pubkey, + Amount: 700, }, { - Descriptor: desc, - Amount: 600, + Pubkey: pubkey, + Amount: 600, }, }, }, @@ -49,25 +47,21 @@ var ( Txid: txid, VOut: 0, }, - Receiver: domain.Receiver{ - Descriptor: desc, - Amount: 1000, - }, + Pubkey: pubkey, + Amount: 1000, }, { VtxoKey: domain.VtxoKey{ Txid: txid, VOut: 0, }, - Receiver: domain.Receiver{ - Descriptor: desc, - Amount: 1000, - }, + Pubkey: pubkey, + Amount: 1000, }, }, Receivers: []domain.Receiver{{ - Descriptor: desc, - Amount: 2000, + Pubkey: pubkey, + Amount: 2000, }}, }, } diff --git a/server/internal/core/ports/tx_builder.go b/server/internal/core/ports/tx_builder.go index 4b1bc10..d4fff63 100644 --- a/server/internal/core/ports/tx_builder.go +++ b/server/internal/core/ports/tx_builder.go @@ -32,7 +32,12 @@ type TxBuilder interface { aspPubkey *secp256k1.PublicKey, payments []domain.Payment, boardingInputs []BoardingInput, sweptRounds []domain.Round, cosigners ...*secp256k1.PublicKey, ) (roundTx string, congestionTree tree.CongestionTree, connectorAddress string, err error) - BuildForfeitTxs(poolTx string, payments []domain.Payment, minRelayFeeRate chainfee.SatPerKVByte) (connectors []string, forfeitTxs []string, err error) + BuildForfeitTxs( + roundTx string, + payments []domain.Payment, + descriptors map[domain.VtxoKey]string, + minRelayFeeRate chainfee.SatPerKVByte, + ) (connectors []string, forfeitTxs []string, err error) BuildSweepTx(inputs []SweepInput) (signedSweepTx string, err error) GetSweepInput(node tree.Node) (lifetime int64, sweepInput SweepInput, err error) FinalizeAndExtract(tx string) (txhex string, err error) @@ -41,7 +46,9 @@ type TxBuilder interface { FindLeaves(congestionTree tree.CongestionTree, fromtxid string, vout uint32) (leaves []tree.Node, err error) BuildAsyncPaymentTransactions( vtxosToSpend []domain.Vtxo, - aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver, + descriptors map[domain.VtxoKey]string, + forfeitsLeaves map[domain.VtxoKey]chainhash.Hash, + receivers []domain.Receiver, ) (string, error) VerifyAndCombinePartialTx(dest string, src string) (string, error) GetTxID(tx string) (string, error) diff --git a/server/internal/infrastructure/db/badger/vtxo_repo.go b/server/internal/infrastructure/db/badger/vtxo_repo.go index 3c6698c..2f51a0c 100644 --- a/server/internal/infrastructure/db/badger/vtxo_repo.go +++ b/server/internal/infrastructure/db/badger/vtxo_repo.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "path/filepath" - "regexp" "strings" "github.com/ark-network/ark/server/internal/core/domain" @@ -101,13 +100,7 @@ func (r *vtxoRepository) GetAllVtxos( ) ([]domain.Vtxo, []domain.Vtxo, error) { query := badgerhold.Where("Redeemed").Eq(false) if len(pubkey) > 0 { - if len(pubkey) == 66 { - pubkey = pubkey[2:] - } - - query = query.And("Descriptor").RegExp( - regexp.MustCompile(fmt.Sprintf(".*%s.*", pubkey)), - ) + query = query.And("Pubkey").Eq(pubkey) } vtxos, err := r.findVtxos(ctx, query) if err != nil { diff --git a/server/internal/infrastructure/db/service_test.go b/server/internal/infrastructure/db/service_test.go index 86d8feb..048c583 100644 --- a/server/internal/infrastructure/db/service_test.go +++ b/server/internal/infrastructure/db/service_test.go @@ -4,14 +4,12 @@ import ( "context" "crypto/rand" "encoding/hex" - "fmt" "os" "reflect" "sort" "testing" "time" - "github.com/ark-network/ark/common/descriptor" "github.com/ark-network/ark/common/tree" "github.com/ark-network/ark/server/internal/core/domain" "github.com/ark-network/ark/server/internal/core/ports" @@ -24,26 +22,8 @@ import ( const ( emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA=" emptyTx = "0200000000000000000000" - pubkey1 = "00000000000000000000000000000000000000000000000000000000000000001" - pubkey2 = "00000000000000000000000000000000000000000000000000000000000000002" -) - -var desc1 = fmt.Sprintf( - descriptor.DefaultVtxoDescriptorTemplate, - randomString(66), - pubkey1, - pubkey1, - 512, - pubkey1, -) - -var desc2 = fmt.Sprintf( - descriptor.DefaultVtxoDescriptorTemplate, - randomString(66), - pubkey2, - pubkey2, - 512, - pubkey2, + pubkey = "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967" + pubkey2 = "33ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0" ) var congestionTree = [][]tree.Node{ @@ -270,15 +250,13 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) { }, RoundTxid: randomString(32), ExpireAt: 7980322, - Receiver: domain.Receiver{ - Descriptor: randomString(120), - Amount: 300, - }, + Pubkey: randomString(32), + Amount: 300, }, }, Receivers: []domain.Receiver{{ - Descriptor: randomString(120), - Amount: 300, + Pubkey: randomString(32), + Amount: 300, }}, }, { @@ -292,20 +270,18 @@ func testRoundRepository(t *testing.T, svc ports.RepoManager) { }, RoundTxid: randomString(32), ExpireAt: 7980322, - Receiver: domain.Receiver{ - Descriptor: randomString(120), - Amount: 600, - }, + Pubkey: randomString(32), + Amount: 600, }, }, Receivers: []domain.Receiver{ { - Descriptor: randomString(120), - Amount: 400, + Pubkey: randomString(32), + Amount: 400, }, { - Descriptor: randomString(120), - Amount: 200, + Pubkey: randomString(32), + Amount: 200, }, }, }, @@ -370,20 +346,16 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) { Txid: randomString(32), VOut: 0, }, - Receiver: domain.Receiver{ - Descriptor: desc1, - Amount: 1000, - }, + Pubkey: pubkey, + Amount: 1000, }, { VtxoKey: domain.VtxoKey{ Txid: randomString(32), VOut: 1, }, - Receiver: domain.Receiver{ - Descriptor: desc1, - Amount: 2000, - }, + Pubkey: pubkey, + Amount: 2000, }, } newVtxos := append(userVtxos, domain.Vtxo{ @@ -391,10 +363,8 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) { Txid: randomString(32), VOut: 1, }, - Receiver: domain.Receiver{ - Descriptor: desc2, - Amount: 2000, - }, + Pubkey: pubkey2, + Amount: 2000, }) vtxoKeys := make([]domain.VtxoKey, 0, len(userVtxos)) @@ -406,7 +376,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) { require.Error(t, err) require.Empty(t, vtxos) - spendableVtxos, spentVtxos, err := svc.Vtxos().GetAllVtxos(ctx, pubkey1) + spendableVtxos, spentVtxos, err := svc.Vtxos().GetAllVtxos(ctx, pubkey) require.NoError(t, err) require.Empty(t, spendableVtxos) require.Empty(t, spentVtxos) @@ -423,7 +393,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) { require.NoError(t, err) require.Exactly(t, userVtxos, vtxos) - spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey1) + spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey) require.NoError(t, err) sortedVtxos := sortVtxos(userVtxos) @@ -449,7 +419,7 @@ func testVtxoRepository(t *testing.T, svc ports.RepoManager) { require.True(t, v.Spent) } - spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey1) + spendableVtxos, spentVtxos, err = svc.Vtxos().GetAllVtxos(ctx, pubkey) require.NoError(t, err) require.Exactly(t, vtxos[1:], spendableVtxos) require.Len(t, spentVtxos, len(vtxoKeys[:1])) diff --git a/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql b/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql index cc9078b..584d4a3 100644 --- a/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql +++ b/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql @@ -21,11 +21,11 @@ CREATE TABLE IF NOT EXISTS payment ( CREATE TABLE IF NOT EXISTS receiver ( payment_id TEXT NOT NULL, - pubkey TEXT NOT NULL, + pubkey TEXT, + onchain_address TEXT, amount INTEGER NOT NULL, - onchain_address TEXT NOT NULL, FOREIGN KEY (payment_id) REFERENCES payment(id), - PRIMARY KEY (payment_id, pubkey) + PRIMARY KEY (payment_id, pubkey, onchain_address) ); CREATE TABLE IF NOT EXISTS tx ( @@ -54,6 +54,7 @@ CREATE TABLE IF NOT EXISTS vtxo ( expire_at INTEGER NOT NULL, payment_id TEXT, redeem_tx TEXT, + pending BOOLEAN NOT NULL, PRIMARY KEY (txid, vout), FOREIGN KEY (payment_id) REFERENCES payment(id) ); diff --git a/server/internal/infrastructure/db/sqlite/migration/20240913142235_add-receiver-descriptor.down.sql b/server/internal/infrastructure/db/sqlite/migration/20240913142235_add-receiver-descriptor.down.sql deleted file mode 100644 index b4d7d7f..0000000 --- a/server/internal/infrastructure/db/sqlite/migration/20240913142235_add-receiver-descriptor.down.sql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE TABLE IF NOT EXISTS old_receiver ( - payment_id TEXT NOT NULL, - pubkey TEXT NOT NULL, - amount INTEGER NOT NULL, - onchain_address TEXT NOT NULL, - FOREIGN KEY (payment_id) REFERENCES payment(id), - PRIMARY KEY (payment_id, pubkey) -); - -INSERT INTO old_receiver SELECT * FROM receiver; - -DROP TABLE receiver; - -ALTER TABLE old_receiver RENAME TO receiver; - -ALTER TABLE vtxo DROP COLUMN descriptor; -ALTER TABLE vtxo ADD COLUMN pubkey TEXT NOT NULL; diff --git a/server/internal/infrastructure/db/sqlite/migration/20240913142235_add-receiver-descriptor.up.sql b/server/internal/infrastructure/db/sqlite/migration/20240913142235_add-receiver-descriptor.up.sql deleted file mode 100644 index 8e4dcc3..0000000 --- a/server/internal/infrastructure/db/sqlite/migration/20240913142235_add-receiver-descriptor.up.sql +++ /dev/null @@ -1,28 +0,0 @@ -CREATE TABLE IF NOT EXISTS new_receiver ( - payment_id TEXT NOT NULL, - descriptor TEXT NOT NULL, - amount INTEGER NOT NULL, - onchain_address TEXT NOT NULL, - FOREIGN KEY (payment_id) REFERENCES payment(id), - PRIMARY KEY (payment_id, descriptor) -); - -INSERT INTO new_receiver SELECT * FROM receiver; - -DROP VIEW payment_vtxo_vw; -DROP VIEW payment_receiver_vw; -DROP TABLE receiver; -ALTER TABLE new_receiver RENAME TO receiver; - -ALTER TABLE vtxo ADD COLUMN descriptor TEXT; -ALTER TABLE vtxo DROP COLUMN pubkey; - -CREATE VIEW payment_vtxo_vw AS SELECT vtxo.* -FROM payment -LEFT OUTER JOIN vtxo -ON payment.id=vtxo.payment_id; - -CREATE VIEW payment_receiver_vw AS SELECT receiver.* -FROM payment -LEFT OUTER JOIN receiver -ON payment.id=receiver.payment_id; \ No newline at end of file diff --git a/server/internal/infrastructure/db/sqlite/migration/20240919104541_add-pending-field-to-vtxo.down.sql b/server/internal/infrastructure/db/sqlite/migration/20240919104541_add-pending-field-to-vtxo.down.sql deleted file mode 100644 index e20ddd5..0000000 --- a/server/internal/infrastructure/db/sqlite/migration/20240919104541_add-pending-field-to-vtxo.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE vtxo DROP COLUMN pending; \ No newline at end of file diff --git a/server/internal/infrastructure/db/sqlite/migration/20240919104541_add-pending-field-to-vtxo.up.sql b/server/internal/infrastructure/db/sqlite/migration/20240919104541_add-pending-field-to-vtxo.up.sql deleted file mode 100644 index cb32b96..0000000 --- a/server/internal/infrastructure/db/sqlite/migration/20240919104541_add-pending-field-to-vtxo.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -ALTER TABLE vtxo ADD COLUMN pending BOOLEAN NOT NULL; - -DROP VIEW payment_vtxo_vw; -DROP VIEW payment_receiver_vw; - -CREATE VIEW payment_vtxo_vw AS SELECT vtxo.* -FROM payment -LEFT OUTER JOIN vtxo -ON payment.id=vtxo.payment_id; - -CREATE VIEW payment_receiver_vw AS SELECT receiver.* -FROM payment -LEFT OUTER JOIN receiver -ON payment.id=receiver.payment_id; \ No newline at end of file diff --git a/server/internal/infrastructure/db/sqlite/round_repo.go b/server/internal/infrastructure/db/sqlite/round_repo.go index 65908ce..0020b7a 100644 --- a/server/internal/infrastructure/db/sqlite/round_repo.go +++ b/server/internal/infrastructure/db/sqlite/round_repo.go @@ -164,10 +164,16 @@ func (r *roundRepository) AddOrUpdateRound(ctx context.Context, round domain.Rou if err := querierWithTx.UpsertReceiver( ctx, queries.UpsertReceiverParams{ - PaymentID: payment.Id, - Descriptor: receiver.Descriptor, - Amount: int64(receiver.Amount), - OnchainAddress: receiver.OnchainAddress, + PaymentID: payment.Id, + Amount: int64(receiver.Amount), + Pubkey: sql.NullString{ + String: receiver.Pubkey, + Valid: len(receiver.Pubkey) > 0, + }, + OnchainAddress: sql.NullString{ + String: receiver.OnchainAddress, + Valid: len(receiver.OnchainAddress) > 0, + }, }, ); err != nil { return fmt.Errorf("failed to upsert receiver: %w", err) @@ -320,8 +326,8 @@ func (r *roundRepository) GetSweptRounds(ctx context.Context) ([]domain.Round, e func rowToReceiver(row queries.PaymentReceiverVw) domain.Receiver { return domain.Receiver{ - Descriptor: row.Descriptor.String, Amount: uint64(row.Amount.Int64), + Pubkey: row.Pubkey.String, OnchainAddress: row.OnchainAddress.String, } } @@ -413,8 +419,8 @@ func readRoundRows(rows []roundPaymentTxReceiverVtxoRow) ([]*domain.Round, error found := false for _, rcv := range payment.Receivers { - if v.receiver.Descriptor.Valid && v.receiver.Amount.Valid { - if rcv.Descriptor == v.receiver.Descriptor.String && int64(rcv.Amount) == v.receiver.Amount.Int64 { + 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 { found = true break } @@ -469,10 +475,8 @@ func rowToPaymentVtxoVw(row queries.PaymentVtxoVw) domain.Vtxo { Txid: row.Txid.String, VOut: uint32(row.Vout.Int64), }, - Receiver: domain.Receiver{ - Descriptor: row.Descriptor.String, - Amount: uint64(row.Amount.Int64), - }, + Amount: uint64(row.Amount.Int64), + Pubkey: row.Pubkey.String, RoundTxid: row.PoolTx.String, SpentBy: row.SpentBy.String, Spent: row.Spent.Bool, diff --git a/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go b/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go index 392476c..03ae8a4 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go +++ b/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go @@ -15,32 +15,32 @@ type Payment struct { type PaymentReceiverVw struct { PaymentID sql.NullString - Descriptor sql.NullString - Amount sql.NullInt64 + Pubkey sql.NullString OnchainAddress sql.NullString + Amount sql.NullInt64 } type PaymentVtxoVw struct { - Txid sql.NullString - Vout sql.NullInt64 - Amount sql.NullInt64 - PoolTx sql.NullString - SpentBy sql.NullString - Spent sql.NullBool - Redeemed sql.NullBool - Swept sql.NullBool - ExpireAt sql.NullInt64 - PaymentID sql.NullString - RedeemTx sql.NullString - Descriptor sql.NullString - Pending sql.NullBool + Txid sql.NullString + Vout sql.NullInt64 + Pubkey sql.NullString + Amount sql.NullInt64 + PoolTx sql.NullString + SpentBy sql.NullString + Spent sql.NullBool + Redeemed sql.NullBool + Swept sql.NullBool + ExpireAt sql.NullInt64 + PaymentID sql.NullString + RedeemTx sql.NullString + Pending sql.NullBool } type Receiver struct { PaymentID string - Descriptor string + Pubkey sql.NullString + OnchainAddress sql.NullString Amount int64 - OnchainAddress string } type Round struct { @@ -88,17 +88,17 @@ type Tx struct { } type Vtxo struct { - Txid string - Vout int64 - Amount int64 - PoolTx string - SpentBy string - Spent bool - Redeemed bool - Swept bool - ExpireAt int64 - PaymentID sql.NullString - RedeemTx sql.NullString - Descriptor sql.NullString - Pending bool + Txid string + Vout int64 + Pubkey string + Amount int64 + PoolTx string + SpentBy string + Spent bool + Redeemed bool + Swept bool + ExpireAt int64 + PaymentID sql.NullString + RedeemTx sql.NullString + Pending bool } diff --git a/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go b/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go index 48dad1d..d8d4f16 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go +++ b/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go @@ -54,7 +54,7 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams } const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo +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.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo WHERE redeemed = false ` @@ -74,6 +74,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem if err := rows.Scan( &i.Vtxo.Txid, &i.Vtxo.Vout, + &i.Vtxo.Pubkey, &i.Vtxo.Amount, &i.Vtxo.PoolTx, &i.Vtxo.SpentBy, @@ -83,7 +84,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem &i.Vtxo.ExpireAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, - &i.Vtxo.Descriptor, &i.Vtxo.Pending, ); err != nil { return nil, err @@ -100,16 +100,16 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem } const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo -WHERE redeemed = false AND INSTR(descriptor, ?) > 0 +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.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo +WHERE redeemed = false AND pubkey = ? ` type SelectNotRedeemedVtxosWithPubkeyRow struct { Vtxo Vtxo } -func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) { - rows, err := q.db.QueryContext(ctx, selectNotRedeemedVtxosWithPubkey, instr) +func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey string) ([]SelectNotRedeemedVtxosWithPubkeyRow, error) { + rows, err := q.db.QueryContext(ctx, selectNotRedeemedVtxosWithPubkey, pubkey) if err != nil { return nil, err } @@ -120,6 +120,7 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr st if err := rows.Scan( &i.Vtxo.Txid, &i.Vtxo.Vout, + &i.Vtxo.Pubkey, &i.Vtxo.Amount, &i.Vtxo.PoolTx, &i.Vtxo.SpentBy, @@ -129,7 +130,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, instr st &i.Vtxo.ExpireAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, - &i.Vtxo.Descriptor, &i.Vtxo.Pending, ); err != nil { return nil, err @@ -208,8 +208,8 @@ 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_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.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending + 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -260,11 +260,12 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele &i.RoundTxVw.ParentTxid, &i.RoundTxVw.IsLeaf, &i.PaymentReceiverVw.PaymentID, - &i.PaymentReceiverVw.Descriptor, - &i.PaymentReceiverVw.Amount, + &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, @@ -274,7 +275,6 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele &i.PaymentVtxoVw.ExpireAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, - &i.PaymentVtxoVw.Descriptor, &i.PaymentVtxoVw.Pending, ); err != nil { return nil, err @@ -294,8 +294,8 @@ 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_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.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending + 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -346,11 +346,12 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([] &i.RoundTxVw.ParentTxid, &i.RoundTxVw.IsLeaf, &i.PaymentReceiverVw.PaymentID, - &i.PaymentReceiverVw.Descriptor, - &i.PaymentReceiverVw.Amount, + &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, @@ -360,7 +361,6 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([] &i.PaymentVtxoVw.ExpireAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, - &i.PaymentVtxoVw.Descriptor, &i.PaymentVtxoVw.Pending, ); err != nil { return nil, err @@ -380,8 +380,8 @@ 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_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.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending + 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -432,11 +432,12 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR &i.RoundTxVw.ParentTxid, &i.RoundTxVw.IsLeaf, &i.PaymentReceiverVw.PaymentID, - &i.PaymentReceiverVw.Descriptor, - &i.PaymentReceiverVw.Amount, + &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, @@ -446,7 +447,6 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR &i.PaymentVtxoVw.ExpireAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, - &i.PaymentVtxoVw.Descriptor, &i.PaymentVtxoVw.Pending, ); err != nil { return nil, err @@ -463,7 +463,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR } const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo +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.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo WHERE redeemed = false AND swept = false ` @@ -483,6 +483,7 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt if err := rows.Scan( &i.Vtxo.Txid, &i.Vtxo.Vout, + &i.Vtxo.Pubkey, &i.Vtxo.Amount, &i.Vtxo.PoolTx, &i.Vtxo.SpentBy, @@ -492,7 +493,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt &i.Vtxo.ExpireAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, - &i.Vtxo.Descriptor, &i.Vtxo.Pending, ); err != nil { return nil, err @@ -512,8 +512,8 @@ 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_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.descriptor, payment_receiver_vw.amount, payment_receiver_vw.onchain_address, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.descriptor, payment_vtxo_vw.pending + 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.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -564,11 +564,12 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow &i.RoundTxVw.ParentTxid, &i.RoundTxVw.IsLeaf, &i.PaymentReceiverVw.PaymentID, - &i.PaymentReceiverVw.Descriptor, - &i.PaymentReceiverVw.Amount, + &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, @@ -578,7 +579,6 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow &i.PaymentVtxoVw.ExpireAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, - &i.PaymentVtxoVw.Descriptor, &i.PaymentVtxoVw.Pending, ); err != nil { return nil, err @@ -595,7 +595,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow } const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo +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.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo WHERE txid = ? AND vout = ? ` @@ -614,6 +614,7 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp err := row.Scan( &i.Vtxo.Txid, &i.Vtxo.Vout, + &i.Vtxo.Pubkey, &i.Vtxo.Amount, &i.Vtxo.PoolTx, &i.Vtxo.SpentBy, @@ -623,14 +624,13 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp &i.Vtxo.ExpireAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, - &i.Vtxo.Descriptor, &i.Vtxo.Pending, ) return i, err } const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many -SELECT vtxo.txid, vtxo.vout, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.descriptor, vtxo.pending FROM vtxo +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.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo WHERE pool_tx = ? ` @@ -650,6 +650,7 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S if err := rows.Scan( &i.Vtxo.Txid, &i.Vtxo.Vout, + &i.Vtxo.Pubkey, &i.Vtxo.Amount, &i.Vtxo.PoolTx, &i.Vtxo.SpentBy, @@ -659,7 +660,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S &i.Vtxo.ExpireAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, - &i.Vtxo.Descriptor, &i.Vtxo.Pending, ); err != nil { return nil, err @@ -721,26 +721,26 @@ func (q *Queries) UpsertPayment(ctx context.Context, arg UpsertPaymentParams) er } const upsertReceiver = `-- name: UpsertReceiver :exec -INSERT INTO receiver (payment_id, descriptor, amount, onchain_address) VALUES (?, ?, ?, ?) -ON CONFLICT(payment_id, descriptor) DO UPDATE SET +INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?) +ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET amount = EXCLUDED.amount, - onchain_address = EXCLUDED.onchain_address, - descriptor = EXCLUDED.descriptor + pubkey = EXCLUDED.pubkey, + onchain_address = EXCLUDED.onchain_address ` type UpsertReceiverParams struct { PaymentID string - Descriptor string + Pubkey sql.NullString + OnchainAddress sql.NullString Amount int64 - OnchainAddress string } func (q *Queries) UpsertReceiver(ctx context.Context, arg UpsertReceiverParams) error { _, err := q.db.ExecContext(ctx, upsertReceiver, arg.PaymentID, - arg.Descriptor, - arg.Amount, + arg.Pubkey, arg.OnchainAddress, + arg.Amount, ) return err } @@ -847,9 +847,9 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa } const upsertVtxo = `-- name: UpsertVtxo :exec -INSERT INTO vtxo (txid, vout, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) +INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET - descriptor = EXCLUDED.descriptor, + pubkey = EXCLUDED.pubkey, amount = EXCLUDED.amount, pool_tx = EXCLUDED.pool_tx, spent_by = EXCLUDED.spent_by, @@ -862,25 +862,25 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE ` type UpsertVtxoParams struct { - Txid string - Vout int64 - Descriptor sql.NullString - Amount int64 - PoolTx string - SpentBy string - Spent bool - Redeemed bool - Swept bool - ExpireAt int64 - RedeemTx sql.NullString - Pending bool + Txid string + Vout int64 + Pubkey string + Amount int64 + PoolTx string + SpentBy string + Spent bool + Redeemed bool + Swept bool + ExpireAt int64 + RedeemTx sql.NullString + Pending bool } func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error { _, err := q.db.ExecContext(ctx, upsertVtxo, arg.Txid, arg.Vout, - arg.Descriptor, + arg.Pubkey, arg.Amount, arg.PoolTx, arg.SpentBy, diff --git a/server/internal/infrastructure/db/sqlite/sqlc/query.sql b/server/internal/infrastructure/db/sqlite/sqlc/query.sql index 92787de..05c4c56 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/query.sql +++ b/server/internal/infrastructure/db/sqlite/sqlc/query.sql @@ -44,11 +44,11 @@ INSERT INTO payment (id, round_id) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET round_id = EXCLUDED.round_id; -- name: UpsertReceiver :exec -INSERT INTO receiver (payment_id, descriptor, amount, onchain_address) VALUES (?, ?, ?, ?) -ON CONFLICT(payment_id, descriptor) DO UPDATE SET +INSERT INTO receiver (payment_id, pubkey, onchain_address, amount) VALUES (?, ?, ?, ?) +ON CONFLICT(payment_id, pubkey, onchain_address) DO UPDATE SET amount = EXCLUDED.amount, - onchain_address = EXCLUDED.onchain_address, - descriptor = EXCLUDED.descriptor; + pubkey = EXCLUDED.pubkey, + onchain_address = EXCLUDED.onchain_address; -- name: UpdateVtxoPaymentId :exec UPDATE vtxo SET payment_id = ? WHERE txid = ? AND vout = ?; @@ -112,9 +112,9 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?; SELECT id FROM round; -- name: UpsertVtxo :exec -INSERT INTO vtxo (txid, vout, descriptor, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) +INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET - descriptor = EXCLUDED.descriptor, + pubkey = EXCLUDED.pubkey, amount = EXCLUDED.amount, pool_tx = EXCLUDED.pool_tx, spent_by = EXCLUDED.spent_by, @@ -135,7 +135,7 @@ WHERE redeemed = false; -- name: SelectNotRedeemedVtxosWithPubkey :many SELECT sqlc.embed(vtxo) FROM vtxo -WHERE redeemed = false AND INSTR(descriptor, ?) > 0; +WHERE redeemed = false AND pubkey = ?; -- name: SelectVtxoByOutpoint :one SELECT sqlc.embed(vtxo) FROM vtxo diff --git a/server/internal/infrastructure/db/sqlite/vtxo_repo.go b/server/internal/infrastructure/db/sqlite/vtxo_repo.go index c0113c2..e47306a 100644 --- a/server/internal/infrastructure/db/sqlite/vtxo_repo.go +++ b/server/internal/infrastructure/db/sqlite/vtxo_repo.go @@ -37,20 +37,21 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro txBody := func(querierWithTx *queries.Queries) error { for i := range vtxos { vtxo := vtxos[i] + if err := querierWithTx.UpsertVtxo( ctx, queries.UpsertVtxoParams{ - Txid: vtxo.Txid, - Vout: int64(vtxo.VOut), - Descriptor: sql.NullString{String: vtxo.Descriptor, Valid: true}, - Amount: int64(vtxo.Amount), - PoolTx: vtxo.RoundTxid, - SpentBy: vtxo.SpentBy, - Spent: vtxo.Spent, - Redeemed: vtxo.Redeemed, - Swept: vtxo.Swept, - ExpireAt: vtxo.ExpireAt, - RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true}, - Pending: vtxo.Pending, + Txid: vtxo.Txid, + Vout: int64(vtxo.VOut), + Pubkey: vtxo.Pubkey, + Amount: int64(vtxo.Amount), + PoolTx: vtxo.RoundTxid, + SpentBy: vtxo.SpentBy, + Spent: vtxo.Spent, + Redeemed: vtxo.Redeemed, + Swept: vtxo.Swept, + ExpireAt: vtxo.ExpireAt, + RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true}, + Pending: vtxo.Pending, }, ); err != nil { return err @@ -81,10 +82,6 @@ func (v *vxtoRepository) GetAllVtxos(ctx context.Context, pubkey string) ([]doma var rows []queries.Vtxo if withPubkey { - if len(pubkey) == 66 { - pubkey = pubkey[2:] - } - res, err := v.querier.SelectNotRedeemedVtxosWithPubkey(ctx, pubkey) if err != nil { return nil, nil, err @@ -253,10 +250,8 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo { Txid: row.Txid, VOut: uint32(row.Vout), }, - Receiver: domain.Receiver{ - Descriptor: row.Descriptor.String, - Amount: uint64(row.Amount), - }, + Amount: uint64(row.Amount), + Pubkey: row.Pubkey, RoundTxid: row.PoolTx, SpentBy: row.SpentBy, Spent: row.Spent, diff --git a/server/internal/infrastructure/tx-builder/covenant/builder.go b/server/internal/infrastructure/tx-builder/covenant/builder.go index 58af969..3fa11c3 100644 --- a/server/internal/infrastructure/tx-builder/covenant/builder.go +++ b/server/internal/infrastructure/tx-builder/covenant/builder.go @@ -84,6 +84,7 @@ func (b *txBuilder) BuildSweepTx(inputs []ports.SweepInput) (signedSweepTx strin func (b *txBuilder) BuildForfeitTxs( poolTx string, payments []domain.Payment, + descriptors map[domain.VtxoKey]string, minRelayFeeRate chainfee.SatPerKVByte, ) (connectors []string, forfeitTxs []string, err error) { connectorAddress, err := b.getConnectorAddress(poolTx) @@ -106,7 +107,7 @@ func (b *txBuilder) BuildForfeitTxs( return nil, nil, err } - forfeitTxs, err = b.createForfeitTxs(payments, connectorTxs, connectorAmount, minRelayFeeRate) + forfeitTxs, err = b.createForfeitTxs(payments, descriptors, connectorTxs, connectorAmount, minRelayFeeRate) if err != nil { return nil, nil, err } @@ -147,13 +148,13 @@ func (b *txBuilder) BuildRoundTx( return "", nil, "", err } - receivers, err := getOffchainReceivers(payments) + vtxosLeaves, err := getOutputVtxosLeaves(payments) if err != nil { return "", nil, "", err } treeFactoryFn, sharedOutputScript, sharedOutputAmount, err = tree.CraftCongestionTree( - b.onchainNetwork().AssetID, aspPubkey, receivers, feeSatsPerNode, b.roundLifetime, + b.onchainNetwork().AssetID, aspPubkey, vtxosLeaves, feeSatsPerNode, b.roundLifetime, ) if err != nil { return "", nil, "", err @@ -362,7 +363,10 @@ func (b *txBuilder) FindLeaves( } func (b *txBuilder) BuildAsyncPaymentTransactions( - _ []domain.Vtxo, _ *secp256k1.PublicKey, _ []domain.Receiver, + _ []domain.Vtxo, + _ map[domain.VtxoKey]string, + _ map[domain.VtxoKey]chainhash.Hash, + _ []domain.Receiver, ) (string, error) { return "", fmt.Errorf("not implemented") } @@ -396,7 +400,6 @@ func (b *txBuilder) createPoolTx( return nil, err } - receivers := getOnchainReceivers(payments) nbOfInputs := countSpentVtxos(payments) connectorsAmount := (dustAmount + connectorMinRelayFee) * nbOfInputs if nbOfInputs > 1 { @@ -424,21 +427,17 @@ func (b *txBuilder) createPoolTx( }) } - for _, receiver := range receivers { - targetAmount += receiver.Amount - - receiverScript, err := address.ToOutputScript(receiver.OnchainAddress) - if err != nil { - return nil, err - } - - outputs = append(outputs, psetv2.OutputArgs{ - Asset: b.onchainNetwork().AssetID, - Amount: receiver.Amount, - Script: receiverScript, - }) + onchainOutputs, err := getOnchainOutputs(payments, b.onchainNetwork()) + if err != nil { + return nil, err } + for _, out := range onchainOutputs { + targetAmount += out.Amount + } + + outputs = append(outputs, onchainOutputs...) + for _, in := range boardingInputs { targetAmount -= in.Amount } @@ -786,6 +785,7 @@ func (b *txBuilder) createConnectors( func (b *txBuilder) createForfeitTxs( payments []domain.Payment, + descriptors map[domain.VtxoKey]string, connectors []*psetv2.Pset, connectorAmount uint64, minRelayFeeRate chainfee.SatPerKVByte, @@ -803,7 +803,12 @@ func (b *txBuilder) createForfeitTxs( forfeitTxs := make([]string, 0) for _, payment := range payments { for _, vtxo := range payment.Inputs { - offchainScript, err := tree.ParseVtxoScript(vtxo.Descriptor) + desc, ok := descriptors[vtxo.VtxoKey] + if !ok { + return nil, fmt.Errorf("descriptor not found for vtxo %s:%d", vtxo.VtxoKey.Txid, vtxo.VtxoKey.VOut) + } + + offchainScript, err := tree.ParseVtxoScript(desc) if err != nil { return nil, err } diff --git a/server/internal/infrastructure/tx-builder/covenant/builder_test.go b/server/internal/infrastructure/tx-builder/covenant/builder_test.go index 30aed55..125439b 100644 --- a/server/internal/infrastructure/tx-builder/covenant/builder_test.go +++ b/server/internal/infrastructure/tx-builder/covenant/builder_test.go @@ -113,7 +113,7 @@ func TestBuildForfeitTxs(t *testing.T) { t.Run("valid", func(t *testing.T) { for _, f := range fixtures.Valid { connectors, forfeitTxs, err := builder.BuildForfeitTxs( - f.PoolTx, f.Payments, minRelayFeeRate, + f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate, ) require.NoError(t, err) require.Len(t, connectors, f.ExpectedNumOfConnectors) @@ -151,7 +151,7 @@ func TestBuildForfeitTxs(t *testing.T) { t.Run("invalid", func(t *testing.T) { for _, f := range fixtures.Invalid { connectors, forfeitTxs, err := builder.BuildForfeitTxs( - f.PoolTx, f.Payments, minRelayFeeRate, + f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate, ) require.EqualError(t, err, f.ExpectedErr) require.Empty(t, connectors) @@ -215,6 +215,7 @@ func parsePoolTxFixtures() (*poolTxFixtures, error) { type forfeitTxsFixtures struct { Valid []struct { Payments []domain.Payment + Descriptors map[domain.VtxoKey]string ExpectedNumOfConnectors int ExpectedNumOfForfeitTxs int PoolTx string @@ -222,6 +223,7 @@ type forfeitTxsFixtures struct { } Invalid []struct { Payments []domain.Payment + Descriptors map[domain.VtxoKey]string ExpectedErr string PoolTx string } @@ -244,6 +246,42 @@ func parseForfeitTxsFixtures() (*forfeitTxsFixtures, error) { return nil, err } + valid := vv["valid"].([]interface{}) + for i, v := range valid { + val := v.(map[string]interface{}) + payments := val["payments"].([]interface{}) + descriptors := make(map[domain.VtxoKey]string) + for _, p := range payments { + inputs := p.(map[string]interface{})["inputs"].([]interface{}) + for _, in := range inputs { + inMap := in.(map[string]interface{}) + descriptors[domain.VtxoKey{ + Txid: inMap["txid"].(string), + VOut: uint32(inMap["vout"].(float64)), + }] = inMap["descriptor"].(string) + } + } + fixtures.Valid[i].Descriptors = descriptors + } + + invalid := vv["invalid"].([]interface{}) + for i, v := range invalid { + val := v.(map[string]interface{}) + payments := val["payments"].([]interface{}) + descriptors := make(map[domain.VtxoKey]string) + for _, p := range payments { + inputs := p.(map[string]interface{})["inputs"].([]interface{}) + for _, in := range inputs { + inMap := in.(map[string]interface{}) + descriptors[domain.VtxoKey{ + Txid: inMap["txid"].(string), + VOut: uint32(inMap["vout"].(float64)), + }] = inMap["descriptor"].(string) + } + } + fixtures.Invalid[i].Descriptors = descriptors + } + return &fixtures, nil } diff --git a/server/internal/infrastructure/tx-builder/covenant/testdata/fixtures.json b/server/internal/infrastructure/tx-builder/covenant/testdata/fixtures.json index 5221591..1f88af0 100644 --- a/server/internal/infrastructure/tx-builder/covenant/testdata/fixtures.json +++ b/server/internal/infrastructure/tx-builder/covenant/testdata/fixtures.json @@ -9,14 +9,14 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1100 } ] @@ -33,18 +33,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -61,18 +61,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -83,18 +83,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -105,18 +105,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -133,58 +133,58 @@ { "txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1000 }, { "txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1000 }, { "txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 500 }, { "txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60", "vout": 0, + "pubkey":"0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1000 }, { "txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 1000 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -206,25 +206,25 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 600 }, { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", - "signerPubkey": "020000000000000000000000000000000000000000000000000000000000000001", "amount": 500 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] diff --git a/server/internal/infrastructure/tx-builder/covenant/utils.go b/server/internal/infrastructure/tx-builder/covenant/utils.go index 990fd47..c962531 100644 --- a/server/internal/infrastructure/tx-builder/covenant/utils.go +++ b/server/internal/infrastructure/tx-builder/covenant/utils.go @@ -45,34 +45,38 @@ func getPsetId(pset *psetv2.Pset) (string, error) { return utx.TxHash().String(), nil } -func getOnchainReceivers( - payments []domain.Payment, -) []domain.Receiver { - receivers := make([]domain.Receiver, 0) +func getOnchainOutputs( + payments []domain.Payment, net *network.Network, +) ([]psetv2.OutputArgs, error) { + outputs := make([]psetv2.OutputArgs, 0) for _, payment := range payments { for _, receiver := range payment.Receivers { if receiver.IsOnchain() { - receivers = append(receivers, receiver) - } - } - } - return receivers -} - -func getOffchainReceivers( - payments []domain.Payment, -) ([]tree.Receiver, error) { - receivers := make([]tree.Receiver, 0) - for _, payment := range payments { - for _, receiver := range payment.Receivers { - if !receiver.IsOnchain() { - vtxoScript, err := tree.ParseVtxoScript(receiver.Descriptor) + receiverScript, err := address.ToOutputScript(receiver.OnchainAddress) if err != nil { return nil, err } - receivers = append(receivers, tree.Receiver{ - Script: vtxoScript, + outputs = append(outputs, psetv2.OutputArgs{ + Script: receiverScript, + Amount: receiver.Amount, + Asset: net.AssetID, + }) + } + } + } + return outputs, nil +} + +func getOutputVtxosLeaves( + payments []domain.Payment, +) ([]tree.VtxoLeaf, error) { + receivers := make([]tree.VtxoLeaf, 0) + for _, payment := range payments { + for _, receiver := range payment.Receivers { + if !receiver.IsOnchain() { + receivers = append(receivers, tree.VtxoLeaf{ + Pubkey: receiver.Pubkey, Amount: receiver.Amount, }) } diff --git a/server/internal/infrastructure/tx-builder/covenantless/builder.go b/server/internal/infrastructure/tx-builder/covenantless/builder.go index 3fc7a3d..db72326 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/builder.go +++ b/server/internal/infrastructure/tx-builder/covenantless/builder.go @@ -228,7 +228,10 @@ func (b *txBuilder) BuildSweepTx(inputs []ports.SweepInput) (signedSweepTx strin } func (b *txBuilder) BuildForfeitTxs( - poolTx string, payments []domain.Payment, minRelayFeeRate chainfee.SatPerKVByte, + poolTx string, + payments []domain.Payment, + descriptors map[domain.VtxoKey]string, + minRelayFeeRate chainfee.SatPerKVByte, ) (connectors []string, forfeitTxs []string, err error) { connectorPkScript, err := b.getConnectorPkScript(poolTx) if err != nil { @@ -245,7 +248,7 @@ func (b *txBuilder) BuildForfeitTxs( return nil, nil, err } - forfeitTxs, err = b.createForfeitTxs(payments, connectorTxs, minRelayFeeRate) + forfeitTxs, err = b.createForfeitTxs(payments, descriptors, connectorTxs, minRelayFeeRate) if err != nil { return nil, nil, err } @@ -271,7 +274,7 @@ func (b *txBuilder) BuildRoundTx( return "", nil, "", fmt.Errorf("missing cosigners") } - receivers, err := getOffchainReceivers(payments) + receivers, err := getOutputVtxosLeaves(payments) if err != nil { return "", nil, "", err } @@ -399,7 +402,10 @@ func (b *txBuilder) FindLeaves(congestionTree tree.CongestionTree, fromtxid stri } func (b *txBuilder) BuildAsyncPaymentTransactions( - vtxos []domain.Vtxo, aspPubKey *secp256k1.PublicKey, receivers []domain.Receiver, + vtxos []domain.Vtxo, + descriptors map[domain.VtxoKey]string, + forfeitsLeaves map[domain.VtxoKey]chainhash.Hash, + receivers []domain.Receiver, ) (string, error) { if len(vtxos) <= 0 { return "", fmt.Errorf("missing vtxos") @@ -412,6 +418,16 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( redeemTxWeightEstimator := &input.TxWeightEstimator{} for index, vtxo := range vtxos { + desc, ok := descriptors[vtxo.VtxoKey] + if !ok { + return "", fmt.Errorf("missing descriptor for vtxo %s", vtxo.VtxoKey) + } + + forfeitLeafHash, ok := forfeitsLeaves[vtxo.VtxoKey] + if !ok { + return "", fmt.Errorf("missing forfeit leaf hash for vtxo %s", vtxo.VtxoKey) + } + if vtxo.Spent || vtxo.Redeemed || vtxo.Swept { return "", fmt.Errorf("all vtxos must be unspent") } @@ -426,7 +442,7 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( Index: vtxo.VOut, } - vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor) + vtxoScript, err := bitcointree.ParseVtxoScript(desc) if err != nil { return "", err } @@ -446,41 +462,27 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( PkScript: vtxoOutputScript, } - if defaultVtxoScript, ok := vtxoScript.(*bitcointree.DefaultVtxoScript); ok { - forfeitLeaf := bitcointree.MultisigClosure{ - Pubkey: defaultVtxoScript.Owner, - AspPubkey: defaultVtxoScript.Asp, - } - - tapLeaf, err := forfeitLeaf.Leaf() - if err != nil { - return "", err - } - - leafProof, err := vtxoTree.GetTaprootMerkleProof(tapLeaf.TapHash()) - if err != nil { - return "", err - } - - tapscripts[index] = &psbt.TaprootTapLeafScript{ - ControlBlock: leafProof.ControlBlock, - Script: leafProof.Script, - LeafVersion: txscript.BaseLeafVersion, - } - - ctrlBlock, err := txscript.ParseControlBlock(leafProof.ControlBlock) - if err != nil { - return "", err - } - - redeemTxWeightEstimator.AddTapscriptInput(64*2, &waddrmgr.Tapscript{ - RevealedScript: leafProof.Script, - ControlBlock: ctrlBlock, - }) - } else { - return "", fmt.Errorf("vtxo %s:%d script is not default script, can't be async spent", vtxo.Txid, vtxo.VOut) + leafProof, err := vtxoTree.GetTaprootMerkleProof(forfeitLeafHash) + if err != nil { + return "", err } + tapscripts[index] = &psbt.TaprootTapLeafScript{ + ControlBlock: leafProof.ControlBlock, + Script: leafProof.Script, + LeafVersion: txscript.BaseLeafVersion, + } + + ctrlBlock, err := txscript.ParseControlBlock(leafProof.ControlBlock) + if err != nil { + return "", err + } + + redeemTxWeightEstimator.AddTapscriptInput(64*2+40, &waddrmgr.Tapscript{ + RevealedScript: leafProof.Script, + ControlBlock: ctrlBlock, + }) + ins = append(ins, vtxoOutpoint) } @@ -498,17 +500,21 @@ func (b *txBuilder) BuildAsyncPaymentTransactions( } for i, receiver := range receivers { - offchainScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor) + if receiver.IsOnchain() { + return "", fmt.Errorf("receiver %d is onchain", i) + } + + pubkeyBytes, err := hex.DecodeString(receiver.Pubkey) if err != nil { return "", err } - receiverVtxoTaprootKey, _, err := offchainScript.TapTree() + pubkey, err := schnorr.ParsePubKey(pubkeyBytes) if err != nil { return "", err } - newVtxoScript, err := common.P2TRScript(receiverVtxoTaprootKey) + newVtxoScript, err := common.P2TRScript(pubkey) if err != nil { return "", err } @@ -588,7 +594,6 @@ func (b *txBuilder) createRoundTx( connectorAmount := dustLimit - receivers := getOnchainReceivers(payments) nbOfInputs := countSpentVtxos(payments) connectorsAmount := (connectorAmount + connectorMinRelayFee) * nbOfInputs if nbOfInputs > 1 { @@ -614,25 +619,17 @@ func (b *txBuilder) createRoundTx( }) } - for _, receiver := range receivers { - targetAmount += receiver.Amount - - receiverAddr, err := btcutil.DecodeAddress(receiver.OnchainAddress, b.onchainNetwork()) - if err != nil { - return nil, err - } - - receiverScript, err := txscript.PayToAddrScript(receiverAddr) - if err != nil { - return nil, err - } - - outputs = append(outputs, &wire.TxOut{ - Value: int64(receiver.Amount), - PkScript: receiverScript, - }) + onchainOutputs, err := getOnchainOutputs(payments, b.onchainNetwork()) + if err != nil { + return nil, err } + for _, output := range onchainOutputs { + targetAmount += uint64(output.Value) + } + + outputs = append(outputs, onchainOutputs...) + for _, input := range boardingInputs { targetAmount -= input.Amount } @@ -1016,6 +1013,7 @@ func (b *txBuilder) minRelayFeeTreeTx() (uint64, error) { func (b *txBuilder) createForfeitTxs( payments []domain.Payment, + descriptors map[domain.VtxoKey]string, connectors []*psbt.Packet, minRelayFeeRate chainfee.SatPerKVByte, ) ([]string, error) { @@ -1042,7 +1040,12 @@ func (b *txBuilder) createForfeitTxs( forfeitTxs := make([]string, 0) for _, payment := range payments { for _, vtxo := range payment.Inputs { - offchainscript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor) + desc, ok := descriptors[vtxo.VtxoKey] + if !ok { + return nil, err + } + + offchainscript, err := bitcointree.ParseVtxoScript(desc) if err != nil { return nil, err } diff --git a/server/internal/infrastructure/tx-builder/covenantless/builder_test.go b/server/internal/infrastructure/tx-builder/covenantless/builder_test.go index 7411507..da312d6 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/builder_test.go +++ b/server/internal/infrastructure/tx-builder/covenantless/builder_test.go @@ -123,7 +123,7 @@ func TestBuildForfeitTxs(t *testing.T) { t.Run("valid", func(t *testing.T) { for _, f := range fixtures.Valid { connectors, forfeitTxs, err := builder.BuildForfeitTxs( - f.PoolTx, f.Payments, minRelayFeeRate, + f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate, ) require.NoError(t, err) require.Len(t, connectors, f.ExpectedNumOfConnectors) @@ -161,7 +161,7 @@ func TestBuildForfeitTxs(t *testing.T) { t.Run("invalid", func(t *testing.T) { for _, f := range fixtures.Invalid { connectors, forfeitTxs, err := builder.BuildForfeitTxs( - f.PoolTx, f.Payments, minRelayFeeRate, + f.PoolTx, f.Payments, f.Descriptors, minRelayFeeRate, ) require.EqualError(t, err, f.ExpectedErr) require.Empty(t, connectors) @@ -225,6 +225,7 @@ func parsePoolTxFixtures() (*poolTxFixtures, error) { type forfeitTxsFixtures struct { Valid []struct { Payments []domain.Payment + Descriptors map[domain.VtxoKey]string ExpectedNumOfConnectors int ExpectedNumOfForfeitTxs int PoolTx string @@ -232,6 +233,7 @@ type forfeitTxsFixtures struct { } Invalid []struct { Payments []domain.Payment + Descriptors map[domain.VtxoKey]string ExpectedErr string PoolTx string } @@ -254,5 +256,41 @@ func parseForfeitTxsFixtures() (*forfeitTxsFixtures, error) { return nil, err } + valid := vv["valid"].([]interface{}) + for i, v := range valid { + val := v.(map[string]interface{}) + payments := val["payments"].([]interface{}) + descriptors := make(map[domain.VtxoKey]string) + for _, p := range payments { + inputs := p.(map[string]interface{})["inputs"].([]interface{}) + for _, in := range inputs { + inMap := in.(map[string]interface{}) + descriptors[domain.VtxoKey{ + Txid: inMap["txid"].(string), + VOut: uint32(inMap["vout"].(float64)), + }] = inMap["descriptor"].(string) + } + } + fixtures.Valid[i].Descriptors = descriptors + } + + invalid := vv["invalid"].([]interface{}) + for i, v := range invalid { + val := v.(map[string]interface{}) + payments := val["payments"].([]interface{}) + descriptors := make(map[domain.VtxoKey]string) + for _, p := range payments { + inputs := p.(map[string]interface{})["inputs"].([]interface{}) + for _, in := range inputs { + inMap := in.(map[string]interface{}) + descriptors[domain.VtxoKey{ + Txid: inMap["txid"].(string), + VOut: uint32(inMap["vout"].(float64)), + }] = inMap["descriptor"].(string) + } + } + fixtures.Invalid[i].Descriptors = descriptors + } + return &fixtures, nil } diff --git a/server/internal/infrastructure/tx-builder/covenantless/testdata/fixtures.json b/server/internal/infrastructure/tx-builder/covenantless/testdata/fixtures.json index e3e5f00..30c4968 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/testdata/fixtures.json +++ b/server/internal/infrastructure/tx-builder/covenantless/testdata/fixtures.json @@ -9,13 +9,14 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1100 } ] @@ -32,17 +33,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -59,17 +61,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -80,17 +83,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -101,17 +105,18 @@ { "txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1100 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 600 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -128,54 +133,58 @@ { "txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 }, { "txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 }, { "txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 500 }, { "txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 }, { "txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5", - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -197,53 +206,58 @@ { "txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 }, { "txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 }, { "txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 500 }, { "txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60", "vout": 0, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 }, { "txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60", "vout": 1, + "pubkey": "0000000000000000000000000000000000000000000000000000000000000001", "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", "amount": 1000 } ], "receivers": [ { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 1000 }, { - "descriptor": "tr(020000000000000000000000000000000000000000000000000000000000000001,{ and(pk(0000000000000000000000000000000000000000000000000000000000000001), pk(0000000000000000000000000000000000000000000000000000000000000001)), and(older(512), pk(0000000000000000000000000000000000000000000000000000000000000001)) })", + "pubkey": "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967", "amount": 500 } ] @@ -252,7 +266,7 @@ "poolTx": "cHNidP8BALICAAAAAnonOnsJBkHUUaKf/7fdS0/sVyBCgDPusYzGSZZiXPbtAAAAAAD/////VLtr81ZII3QJnXgrIwgcnbsq3aa4L3qdHOAn2evlFtEAAAAAAP////8CohIAAAAAAAAiUSBZarBUuSIHnlkuIoel9MmvexqTGK8jCZaRjt8L+Pb3s+gDAAAAAAAAIlEgI95L4kHEn2fAA+vysD+RIR4eD3AIQwc+FyCInJ8HivYAAAAAAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAEBIOgDAAAAAAAAF6kU6p9IboLvs92Dpp/Zbj8BE3V9oDyHAAAA", "poolTxid": "7c0c10756cdb9ab8e605f1c82e25989761308cf4c60e6a6f42b72d46144c4ce0", "expectedNumOfForfeitTxs": 25, - "expectedNumOfConnectors": 4 + "expectedNumOfConnectors": 4 } ], "invalid": [] diff --git a/server/internal/infrastructure/tx-builder/covenantless/utils.go b/server/internal/infrastructure/tx-builder/covenantless/utils.go index aac6c1b..574a2f3 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/utils.go +++ b/server/internal/infrastructure/tx-builder/covenantless/utils.go @@ -1,47 +1,58 @@ package txbuilder import ( - "github.com/ark-network/ark/common/bitcointree" + "github.com/ark-network/ark/common/tree" "github.com/ark-network/ark/server/internal/core/domain" "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/decred/dcrd/dcrec/secp256k1/v4" ) -func getOnchainReceivers( - payments []domain.Payment, -) []domain.Receiver { - receivers := make([]domain.Receiver, 0) +func getOnchainOutputs( + payments []domain.Payment, network *chaincfg.Params, +) ([]*wire.TxOut, error) { + outputs := make([]*wire.TxOut, 0) for _, payment := range payments { for _, receiver := range payment.Receivers { if receiver.IsOnchain() { - receivers = append(receivers, receiver) - } - } - } - return receivers -} - -func getOffchainReceivers( - payments []domain.Payment, -) ([]bitcointree.Receiver, error) { - receivers := make([]bitcointree.Receiver, 0) - for _, payment := range payments { - for _, receiver := range payment.Receivers { - if !receiver.IsOnchain() { - vtxoScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor) + receiverAddr, err := btcutil.DecodeAddress(receiver.OnchainAddress, network) if err != nil { return nil, err } - receivers = append(receivers, bitcointree.Receiver{ - Script: vtxoScript, + receiverScript, err := txscript.PayToAddrScript(receiverAddr) + if err != nil { + return nil, err + } + + outputs = append(outputs, &wire.TxOut{ + Value: int64(receiver.Amount), + PkScript: receiverScript, + }) + } + } + } + return outputs, nil +} + +func getOutputVtxosLeaves( + payments []domain.Payment, +) ([]tree.VtxoLeaf, error) { + leaves := make([]tree.VtxoLeaf, 0) + for _, payment := range payments { + for _, receiver := range payment.Receivers { + if !receiver.IsOnchain() { + leaves = append(leaves, tree.VtxoLeaf{ + Pubkey: receiver.Pubkey, Amount: receiver.Amount, }) } } } - return receivers, nil + return leaves, nil } func countSpentVtxos(payments []domain.Payment) uint64 { diff --git a/server/internal/interface/grpc/handlers/arkservice.go b/server/internal/interface/grpc/handlers/arkservice.go index b0dd9a1..5e94a08 100644 --- a/server/internal/interface/grpc/handlers/arkservice.go +++ b/server/internal/interface/grpc/handlers/arkservice.go @@ -338,7 +338,7 @@ func (h *handler) Ping( func (h *handler) CreatePayment( ctx context.Context, req *arkv1.CreatePaymentRequest, ) (*arkv1.CreatePaymentResponse, error) { - inputs, err := parseInputs(req.GetInputs()) + inputs, err := parseAsyncPaymentInputs(req.GetInputs()) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -353,12 +353,12 @@ func (h *handler) CreatePayment( return nil, status.Error(codes.InvalidArgument, "output amount must be greater than 0") } - if len(receiver.OnchainAddress) > 0 { - return nil, status.Error(codes.InvalidArgument, "onchain address is not supported as async payment destination") + if len(receiver.OnchainAddress) <= 0 && len(receiver.Pubkey) <= 0 { + return nil, status.Error(codes.InvalidArgument, "missing address") } - if len(receiver.Descriptor) <= 0 { - return nil, status.Error(codes.InvalidArgument, "missing output descriptor") + if receiver.IsOnchain() { + return nil, status.Error(codes.InvalidArgument, "onchain outputs are not supported as async payment destination") } } @@ -462,12 +462,12 @@ func (h *handler) GetRoundById( func (h *handler) ListVtxos( ctx context.Context, req *arkv1.ListVtxosRequest, ) (*arkv1.ListVtxosResponse, error) { - _, userPubkey, _, err := parseAddress(req.GetAddress()) + _, err := parseAddress(req.GetAddress()) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } - spendableVtxos, spentVtxos, err := h.svc.ListVtxos(ctx, userPubkey) + spendableVtxos, spentVtxos, err := h.svc.ListVtxos(ctx, req.GetAddress()) if err != nil { return nil, err } diff --git a/server/internal/interface/grpc/handlers/parser.go b/server/internal/interface/grpc/handlers/parser.go index 2203843..b2c9981 100644 --- a/server/internal/interface/grpc/handlers/parser.go +++ b/server/internal/interface/grpc/handlers/parser.go @@ -1,25 +1,55 @@ package handlers import ( + "encoding/hex" "fmt" arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1" "github.com/ark-network/ark/common" "github.com/ark-network/ark/common/tree" + "github.com/ark-network/ark/server/internal/core/application" "github.com/ark-network/ark/server/internal/core/domain" "github.com/ark-network/ark/server/internal/core/ports" - "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" ) // From interface type to app type -func parseAddress(addr string) (string, *secp256k1.PublicKey, *secp256k1.PublicKey, error) { +func parseAddress(addr string) (*common.Address, error) { if len(addr) <= 0 { - return "", nil, nil, fmt.Errorf("missing address") + return nil, fmt.Errorf("missing address") } return common.DecodeAddress(addr) } +func parseAsyncPaymentInputs(ins []*arkv1.AsyncPaymentInput) ([]application.AsyncPaymentInput, error) { + if len(ins) <= 0 { + return nil, fmt.Errorf("missing inputs") + } + + inputs := make([]application.AsyncPaymentInput, 0, len(ins)) + for _, input := range ins { + forfeitLeafHash, err := chainhash.NewHashFromStr(input.GetForfeitLeafHash()) + if err != nil { + return nil, fmt.Errorf("invalid forfeit leaf hash: %s", err) + } + + inputs = append(inputs, application.AsyncPaymentInput{ + Input: ports.Input{ + VtxoKey: domain.VtxoKey{ + Txid: input.GetInput().GetOutpoint().GetTxid(), + VOut: input.GetInput().GetOutpoint().GetVout(), + }, + Descriptor: input.GetInput().GetDescriptor_(), + }, + ForfeitLeafHash: *forfeitLeafHash, + }) + } + + return inputs, nil +} + func parseInputs(ins []*arkv1.Input) ([]ports.Input, error) { if len(ins) <= 0 { return nil, fmt.Errorf("missing inputs") @@ -39,26 +69,43 @@ func parseInputs(ins []*arkv1.Input) ([]ports.Input, error) { return inputs, nil } +func parseReceiver(out *arkv1.Output) (domain.Receiver, error) { + decodedAddr, err := common.DecodeAddress(out.GetAddress()) + if err != nil { + // onchain address + return domain.Receiver{ + Amount: out.GetAmount(), + OnchainAddress: out.GetAddress(), + }, nil + } + + return domain.Receiver{ + Amount: out.GetAmount(), + Pubkey: hex.EncodeToString(schnorr.SerializePubKey(decodedAddr.VtxoTapKey)), + }, nil +} + func parseReceivers(outs []*arkv1.Output) ([]domain.Receiver, error) { receivers := make([]domain.Receiver, 0, len(outs)) for _, out := range outs { if out.GetAmount() == 0 { return nil, fmt.Errorf("missing output amount") } - if len(out.GetAddress()) <= 0 && len(out.GetDescriptor_()) <= 0 { + if len(out.GetAddress()) <= 0 { return nil, fmt.Errorf("missing output destination") } - receivers = append(receivers, domain.Receiver{ - Descriptor: out.GetDescriptor_(), - Amount: out.GetAmount(), - OnchainAddress: out.GetAddress(), - }) + rcv, err := parseReceiver(out) + if err != nil { + return nil, err + } + + receivers = append(receivers, rcv) } return receivers, nil } -// From app typeto interface type +// From app type to interface type type vtxoList []domain.Vtxo @@ -70,15 +117,15 @@ func (v vtxoList) toProto() []*arkv1.Vtxo { Txid: vv.Txid, Vout: vv.VOut, }, - Descriptor_: vv.Descriptor, - Amount: vv.Amount, - RoundTxid: vv.RoundTxid, - Spent: vv.Spent, - ExpireAt: vv.ExpireAt, - SpentBy: vv.SpentBy, - Swept: vv.Swept, - RedeemTx: vv.RedeemTx, - Pending: vv.Pending, + Amount: vv.Amount, + RoundTxid: vv.RoundTxid, + Spent: vv.Spent, + ExpireAt: vv.ExpireAt, + SpentBy: vv.SpentBy, + Swept: vv.Swept, + RedeemTx: vv.RedeemTx, + Pending: vv.Pending, + Pubkey: vv.Pubkey, }) }