mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
Make the round participants sign the vtxo tree (#271)
* [proto] add APIs to send and receive musig2 signing data * [common] add serialization functions for nonces and signatures * [application] implements tree signing * fix: remove old debug logs * [proto] cleaning * [common] fix musig2.go * [application] fixes and logs * [interface] fix: stop forwarding 2 times the events * [client] add musig2 support + sign the tree when joining a round * [interface] add new APIs into permissions.go * [application][proto] rework PingResponse (return all events type) * [common] split SetKeys into 2 distinct methods * [client] fixes according to musig2.go changes * [sdk] support tree signing + new PingResponse * [sdk] fixes * [application] revert event channel type * [application] use domain.RoundEvent as lastEvent type * [application] remove IsCovenantLess * comments * [application] revert roundAborted changes * [interface] remove bitcointree dependencie
This commit is contained in:
@@ -261,6 +261,70 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/tree/nonces": {
|
||||
"post": {
|
||||
"operationId": "ArkService_SendTreeNonces",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1SendTreeNoncesResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1SendTreeNoncesRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/tree/signatures": {
|
||||
"post": {
|
||||
"operationId": "ArkService_SendTreeSignatures",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1SendTreeSignaturesResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1SendTreeSignaturesRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/ping/{paymentId}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_Ping",
|
||||
@@ -323,7 +387,6 @@
|
||||
},
|
||||
"/v1/round/{txid}": {
|
||||
"get": {
|
||||
"summary": "TODO BTC: signTree rpc",
|
||||
"operationId": "ArkService_GetRound",
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -502,14 +565,19 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roundFinalization": {
|
||||
"$ref": "#/definitions/v1RoundFinalizationEvent",
|
||||
"title": "TODO: BTC add \"signTree\" event"
|
||||
"$ref": "#/definitions/v1RoundFinalizationEvent"
|
||||
},
|
||||
"roundFinalized": {
|
||||
"$ref": "#/definitions/v1RoundFinalizedEvent"
|
||||
},
|
||||
"roundFailed": {
|
||||
"$ref": "#/definitions/v1RoundFailed"
|
||||
},
|
||||
"roundSigning": {
|
||||
"$ref": "#/definitions/v1RoundSigningEvent"
|
||||
},
|
||||
"roundSigningNoncesGenerated": {
|
||||
"$ref": "#/definitions/v1RoundSigningNoncesGeneratedEvent"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -649,14 +717,20 @@
|
||||
"v1PingResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"forfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"roundFinalization": {
|
||||
"$ref": "#/definitions/v1RoundFinalizationEvent"
|
||||
},
|
||||
"roundFinalized": {
|
||||
"$ref": "#/definitions/v1RoundFinalizedEvent"
|
||||
},
|
||||
"roundFailed": {
|
||||
"$ref": "#/definitions/v1RoundFailed"
|
||||
},
|
||||
"roundSigning": {
|
||||
"$ref": "#/definitions/v1RoundSigningEvent"
|
||||
},
|
||||
"roundSigningNoncesGenerated": {
|
||||
"$ref": "#/definitions/v1RoundSigningNoncesGeneratedEvent"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -669,6 +743,9 @@
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Input"
|
||||
}
|
||||
},
|
||||
"ephemeralPubkey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -766,6 +843,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundSigningEvent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"cosignersPubkeys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"unsignedTree": {
|
||||
"$ref": "#/definitions/v1Tree"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundSigningNoncesGeneratedEvent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"treeNonces": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundStage": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -777,6 +882,40 @@
|
||||
],
|
||||
"default": "ROUND_STAGE_UNSPECIFIED"
|
||||
},
|
||||
"v1SendTreeNoncesRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roundId": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"treeNonces": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SendTreeNoncesResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1SendTreeSignaturesRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roundId": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"treeSignatures": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SendTreeSignaturesResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1Tree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -17,13 +17,24 @@ service ArkService {
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
rpc SendTreeNonces(SendTreeNoncesRequest) returns (SendTreeNoncesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/tree/nonces"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc SendTreeSignatures(SendTreeSignaturesRequest) returns (SendTreeSignaturesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/tree/signatures"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc FinalizePayment(FinalizePaymentRequest) returns (FinalizePaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/finalize"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
// TODO BTC: signTree rpc
|
||||
rpc GetRound(GetRoundRequest) returns (GetRoundResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/round/{txid}"
|
||||
@@ -91,6 +102,7 @@ message CompletePaymentResponse {}
|
||||
|
||||
message RegisterPaymentRequest {
|
||||
repeated Input inputs = 1;
|
||||
optional string ephemeral_pubkey = 2;
|
||||
}
|
||||
message RegisterPaymentResponse {
|
||||
// Mocks wabisabi's credentials.
|
||||
@@ -128,10 +140,11 @@ message GetRoundByIdResponse {
|
||||
message GetEventStreamRequest {}
|
||||
message GetEventStreamResponse {
|
||||
oneof event {
|
||||
// TODO: BTC add "signTree" event
|
||||
RoundFinalizationEvent round_finalization = 1;
|
||||
RoundFinalizedEvent round_finalized = 2;
|
||||
RoundFailed round_failed = 3;
|
||||
RoundSigningEvent round_signing = 4;
|
||||
RoundSigningNoncesGeneratedEvent round_signing_nonces_generated = 5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,8 +152,13 @@ message PingRequest {
|
||||
string payment_id = 1;
|
||||
}
|
||||
message PingResponse {
|
||||
repeated string forfeit_txs = 1;
|
||||
RoundFinalizationEvent event = 2;
|
||||
oneof event {
|
||||
RoundFinalizationEvent round_finalization = 1;
|
||||
RoundFinalizedEvent round_finalized = 2;
|
||||
RoundFailed round_failed = 3;
|
||||
RoundSigningEvent round_signing = 4;
|
||||
RoundSigningNoncesGeneratedEvent round_signing_nonces_generated = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message ListVtxosRequest {
|
||||
@@ -189,6 +207,17 @@ message RoundFailed {
|
||||
string reason = 2;
|
||||
}
|
||||
|
||||
message RoundSigningEvent {
|
||||
string id = 1;
|
||||
repeated string cosigners_pubkeys = 2;
|
||||
Tree unsigned_tree = 3;
|
||||
}
|
||||
|
||||
message RoundSigningNoncesGeneratedEvent {
|
||||
string id = 1;
|
||||
string tree_nonces = 2;
|
||||
}
|
||||
|
||||
// TYPES
|
||||
|
||||
enum RoundStage {
|
||||
@@ -251,4 +280,20 @@ message Vtxo {
|
||||
message PendingPayment {
|
||||
string redeem_tx = 1;
|
||||
repeated string unconditional_forfeit_txs =2;
|
||||
}
|
||||
}
|
||||
|
||||
message SendTreeNoncesRequest {
|
||||
string round_id = 1;
|
||||
string public_key = 2;
|
||||
string tree_nonces = 3;
|
||||
}
|
||||
|
||||
message SendTreeNoncesResponse {}
|
||||
|
||||
message SendTreeSignaturesRequest {
|
||||
string round_id = 1;
|
||||
string public_key = 2;
|
||||
string tree_signatures = 3;
|
||||
}
|
||||
|
||||
message SendTreeSignaturesResponse {}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -83,6 +83,58 @@ func local_request_ArkService_ClaimPayment_0(ctx context.Context, marshaler runt
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_SendTreeNonces_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SendTreeNoncesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.SendTreeNonces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_SendTreeNonces_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SendTreeNoncesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.SendTreeNonces(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_SendTreeSignatures_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SendTreeSignaturesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.SendTreeSignatures(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_SendTreeSignatures_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SendTreeSignaturesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.SendTreeSignatures(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_FinalizePayment_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq FinalizePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
@@ -486,6 +538,56 @@ func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_SendTreeNonces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/SendTreeNonces", runtime.WithHTTPPathPattern("/v1/payment/tree/nonces"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_SendTreeNonces_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_SendTreeNonces_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_SendTreeSignatures_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/SendTreeSignatures", runtime.WithHTTPPathPattern("/v1/payment/tree/signatures"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_SendTreeSignatures_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_SendTreeSignatures_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_FinalizePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -803,6 +905,50 @@ func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_SendTreeNonces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/SendTreeNonces", runtime.WithHTTPPathPattern("/v1/payment/tree/nonces"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_SendTreeNonces_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_SendTreeNonces_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_SendTreeSignatures_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/SendTreeSignatures", runtime.WithHTTPPathPattern("/v1/payment/tree/signatures"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_SendTreeSignatures_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_SendTreeSignatures_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_FinalizePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -1031,6 +1177,10 @@ var (
|
||||
|
||||
pattern_ArkService_ClaimPayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "payment", "claim"}, ""))
|
||||
|
||||
pattern_ArkService_SendTreeNonces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "payment", "tree", "nonces"}, ""))
|
||||
|
||||
pattern_ArkService_SendTreeSignatures_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "payment", "tree", "signatures"}, ""))
|
||||
|
||||
pattern_ArkService_FinalizePayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "payment", "finalize"}, ""))
|
||||
|
||||
pattern_ArkService_GetRound_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "round", "txid"}, ""))
|
||||
@@ -1057,6 +1207,10 @@ var (
|
||||
|
||||
forward_ArkService_ClaimPayment_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_SendTreeNonces_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_SendTreeSignatures_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_FinalizePayment_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_GetRound_0 = runtime.ForwardResponseMessage
|
||||
|
||||
@@ -20,8 +20,9 @@ const _ = grpc.SupportPackageIsVersion7
|
||||
type ArkServiceClient interface {
|
||||
RegisterPayment(ctx context.Context, in *RegisterPaymentRequest, opts ...grpc.CallOption) (*RegisterPaymentResponse, error)
|
||||
ClaimPayment(ctx context.Context, in *ClaimPaymentRequest, opts ...grpc.CallOption) (*ClaimPaymentResponse, error)
|
||||
SendTreeNonces(ctx context.Context, in *SendTreeNoncesRequest, opts ...grpc.CallOption) (*SendTreeNoncesResponse, error)
|
||||
SendTreeSignatures(ctx context.Context, in *SendTreeSignaturesRequest, opts ...grpc.CallOption) (*SendTreeSignaturesResponse, error)
|
||||
FinalizePayment(ctx context.Context, in *FinalizePaymentRequest, opts ...grpc.CallOption) (*FinalizePaymentResponse, error)
|
||||
// TODO BTC: signTree rpc
|
||||
GetRound(ctx context.Context, in *GetRoundRequest, opts ...grpc.CallOption) (*GetRoundResponse, error)
|
||||
GetRoundById(ctx context.Context, in *GetRoundByIdRequest, opts ...grpc.CallOption) (*GetRoundByIdResponse, error)
|
||||
GetEventStream(ctx context.Context, in *GetEventStreamRequest, opts ...grpc.CallOption) (ArkService_GetEventStreamClient, error)
|
||||
@@ -59,6 +60,24 @@ func (c *arkServiceClient) ClaimPayment(ctx context.Context, in *ClaimPaymentReq
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) SendTreeNonces(ctx context.Context, in *SendTreeNoncesRequest, opts ...grpc.CallOption) (*SendTreeNoncesResponse, error) {
|
||||
out := new(SendTreeNoncesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/SendTreeNonces", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) SendTreeSignatures(ctx context.Context, in *SendTreeSignaturesRequest, opts ...grpc.CallOption) (*SendTreeSignaturesResponse, error) {
|
||||
out := new(SendTreeSignaturesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/SendTreeSignatures", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) FinalizePayment(ctx context.Context, in *FinalizePaymentRequest, opts ...grpc.CallOption) (*FinalizePaymentResponse, error) {
|
||||
out := new(FinalizePaymentResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/FinalizePayment", in, out, opts...)
|
||||
@@ -178,8 +197,9 @@ func (c *arkServiceClient) CompletePayment(ctx context.Context, in *CompletePaym
|
||||
type ArkServiceServer interface {
|
||||
RegisterPayment(context.Context, *RegisterPaymentRequest) (*RegisterPaymentResponse, error)
|
||||
ClaimPayment(context.Context, *ClaimPaymentRequest) (*ClaimPaymentResponse, error)
|
||||
SendTreeNonces(context.Context, *SendTreeNoncesRequest) (*SendTreeNoncesResponse, error)
|
||||
SendTreeSignatures(context.Context, *SendTreeSignaturesRequest) (*SendTreeSignaturesResponse, error)
|
||||
FinalizePayment(context.Context, *FinalizePaymentRequest) (*FinalizePaymentResponse, error)
|
||||
// TODO BTC: signTree rpc
|
||||
GetRound(context.Context, *GetRoundRequest) (*GetRoundResponse, error)
|
||||
GetRoundById(context.Context, *GetRoundByIdRequest) (*GetRoundByIdResponse, error)
|
||||
GetEventStream(*GetEventStreamRequest, ArkService_GetEventStreamServer) error
|
||||
@@ -201,6 +221,12 @@ func (UnimplementedArkServiceServer) RegisterPayment(context.Context, *RegisterP
|
||||
func (UnimplementedArkServiceServer) ClaimPayment(context.Context, *ClaimPaymentRequest) (*ClaimPaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ClaimPayment not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) SendTreeNonces(context.Context, *SendTreeNoncesRequest) (*SendTreeNoncesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SendTreeNonces not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) SendTreeSignatures(context.Context, *SendTreeSignaturesRequest) (*SendTreeSignaturesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SendTreeSignatures not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) FinalizePayment(context.Context, *FinalizePaymentRequest) (*FinalizePaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method FinalizePayment not implemented")
|
||||
}
|
||||
@@ -279,6 +305,42 @@ func _ArkService_ClaimPayment_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_SendTreeNonces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendTreeNoncesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).SendTreeNonces(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/SendTreeNonces",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).SendTreeNonces(ctx, req.(*SendTreeNoncesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_SendTreeSignatures_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendTreeSignaturesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).SendTreeSignatures(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/SendTreeSignatures",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).SendTreeSignatures(ctx, req.(*SendTreeSignaturesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_FinalizePayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FinalizePaymentRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -477,6 +539,14 @@ var ArkService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ClaimPayment",
|
||||
Handler: _ArkService_ClaimPayment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SendTreeNonces",
|
||||
Handler: _ArkService_SendTreeNonces_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SendTreeSignatures",
|
||||
Handler: _ArkService_SendTreeSignatures_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "FinalizePayment",
|
||||
Handler: _ArkService_FinalizePayment_Handler,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package covenantless
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/client/utils"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -69,8 +72,19 @@ func selfTransferAllPendingPayments(
|
||||
return err
|
||||
}
|
||||
|
||||
ephemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubkey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed())
|
||||
|
||||
registerResponse, err := client.RegisterPayment(
|
||||
ctx.Context, &arkv1.RegisterPaymentRequest{Inputs: inputs},
|
||||
ctx.Context,
|
||||
&arkv1.RegisterPaymentRequest{
|
||||
Inputs: inputs,
|
||||
EphemeralPubkey: &pubkey,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -86,7 +100,7 @@ func selfTransferAllPendingPayments(
|
||||
|
||||
poolTxID, err := handleRoundStream(
|
||||
ctx, client, registerResponse.GetId(),
|
||||
pendingVtxos, secKey, receiversOutput,
|
||||
pendingVtxos, secKey, receiversOutput, ephemeralKey,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,6 +3,7 @@ package covenantless
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/urfave/cli/v2"
|
||||
"google.golang.org/grpc"
|
||||
@@ -220,12 +222,15 @@ func castCongestionTree(congestionTree tree.CongestionTree) *arkv1.Tree {
|
||||
func handleRoundStream(
|
||||
ctx *cli.Context, client arkv1.ArkServiceClient, paymentID string,
|
||||
vtxosToSign []vtxo, secKey *secp256k1.PrivateKey, receivers []*arkv1.Output,
|
||||
ephemeralKey *secp256k1.PrivateKey,
|
||||
) (poolTxID string, err error) {
|
||||
stream, err := client.GetEventStream(ctx.Context, &arkv1.GetEventStreamRequest{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
myEphemeralPublicKey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed())
|
||||
|
||||
var pingStop func()
|
||||
pingReq := &arkv1.PingRequest{
|
||||
PaymentId: paymentID,
|
||||
@@ -236,6 +241,8 @@ func handleRoundStream(
|
||||
|
||||
defer pingStop()
|
||||
|
||||
var treeSignerSession bitcointree.SignerSession
|
||||
|
||||
for {
|
||||
event, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
@@ -250,6 +257,143 @@ func handleRoundStream(
|
||||
return "", fmt.Errorf("round failed: %s", e.GetReason())
|
||||
}
|
||||
|
||||
if e := event.GetRoundSigning(); e != nil {
|
||||
pingStop()
|
||||
fmt.Println("tree created, generating nonces...")
|
||||
|
||||
cosignersPubKeysHex := e.GetCosignersPubkeys()
|
||||
|
||||
cosigners := make([]*secp256k1.PublicKey, 0, len(cosignersPubKeysHex))
|
||||
|
||||
for _, pubkeyHex := range cosignersPubKeysHex {
|
||||
pubkeyBytes, err := hex.DecodeString(pubkeyHex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pubkey, err := secp256k1.ParsePubKey(pubkeyBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cosigners = append(cosigners, pubkey)
|
||||
}
|
||||
|
||||
congestionTree, err := toCongestionTree(e.GetUnsignedTree())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
minRelayFee, err := utils.GetMinRelayFee(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
aspPubkey, err := utils.GetAspPublicKey(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
lifetime, err := utils.GetRoundLifetime(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sweepClosure := bitcointree.CSVSigClosure{
|
||||
Pubkey: aspPubkey,
|
||||
Seconds: uint(lifetime),
|
||||
}
|
||||
|
||||
sweepTapLeaf, err := sweepClosure.Leaf()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sweepTapTree := txscript.AssembleTaprootScriptTree(*sweepTapLeaf)
|
||||
root := sweepTapTree.RootNode.TapHash()
|
||||
|
||||
treeSignerSession = bitcointree.NewTreeSignerSession(
|
||||
ephemeralKey, congestionTree, int64(minRelayFee), root.CloneBytes(),
|
||||
)
|
||||
|
||||
nonces, err := treeSignerSession.GetNonces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := treeSignerSession.SetKeys(cosigners); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var nonceBuffer bytes.Buffer
|
||||
|
||||
if err := nonces.Encode(&nonceBuffer); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
serializedNonces := hex.EncodeToString(nonceBuffer.Bytes())
|
||||
|
||||
if _, err := client.SendTreeNonces(ctx.Context, &arkv1.SendTreeNoncesRequest{
|
||||
RoundId: e.GetId(),
|
||||
PublicKey: myEphemeralPublicKey,
|
||||
TreeNonces: serializedNonces,
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Println("nonces sent")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if e := event.GetRoundSigningNoncesGenerated(); e != nil {
|
||||
pingStop()
|
||||
fmt.Println("nonces generated, signing the tree...")
|
||||
|
||||
if treeSignerSession == nil {
|
||||
return "", fmt.Errorf("tree signer session not set")
|
||||
}
|
||||
|
||||
combinedNoncesBytes, err := hex.DecodeString(e.GetTreeNonces())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
combinedNonces, err := bitcointree.DecodeNonces(bytes.NewReader(combinedNoncesBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := treeSignerSession.SetAggregatedNonces(combinedNonces); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sigs, err := treeSignerSession.Sign()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var sigBuffer bytes.Buffer
|
||||
|
||||
if err := sigs.Encode(&sigBuffer); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
serializedSigs := hex.EncodeToString(sigBuffer.Bytes())
|
||||
|
||||
if _, err := client.SendTreeSignatures(ctx.Context, &arkv1.SendTreeSignaturesRequest{
|
||||
RoundId: e.GetId(),
|
||||
TreeSignatures: serializedSigs,
|
||||
PublicKey: myEphemeralPublicKey,
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Println("tree signed")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if e := event.GetRoundFinalization(); e != nil {
|
||||
// stop pinging as soon as we receive some forfeit txs
|
||||
pingStop()
|
||||
|
||||
@@ -168,7 +168,11 @@ func (c *clArkBitcoinCLI) Onboard(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signer.SetKeys(cosigners, aggregatedNonces); err != nil {
|
||||
if err := signer.SetKeys(cosigners); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := signer.SetAggregatedNonces(aggregatedNonces); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package covenantless
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/client/utils"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -80,8 +82,16 @@ func collaborativeRedeem(
|
||||
return err
|
||||
}
|
||||
|
||||
ephemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubkey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed())
|
||||
|
||||
registerResponse, err := client.RegisterPayment(ctx.Context, &arkv1.RegisterPaymentRequest{
|
||||
Inputs: inputs,
|
||||
Inputs: inputs,
|
||||
EphemeralPubkey: &pubkey,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -102,6 +112,7 @@ func collaborativeRedeem(
|
||||
selectedCoins,
|
||||
secKey,
|
||||
receivers,
|
||||
ephemeralKey,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package bitcointree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
@@ -12,20 +14,45 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCongestionTreeNotSet = errors.New("congestion tree not set")
|
||||
ErrAggregateKeyNotSet = errors.New("aggregate key not set")
|
||||
|
||||
columnSeparator = byte('|')
|
||||
rowSeparator = byte('/')
|
||||
)
|
||||
|
||||
type TreeNonces [][][66]byte // public nonces
|
||||
type Musig2Nonce struct {
|
||||
PubNonce [66]byte
|
||||
}
|
||||
|
||||
func (n *Musig2Nonce) Encode(w io.Writer) error {
|
||||
_, err := w.Write(n.PubNonce[:])
|
||||
return err
|
||||
}
|
||||
|
||||
func (n *Musig2Nonce) Decode(r io.Reader) error {
|
||||
bytes := make([]byte, 66)
|
||||
_, err := r.Read(bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.PubNonce = [66]byte(bytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
type TreeNonces [][]*Musig2Nonce // public nonces
|
||||
type TreePartialSigs [][]*musig2.PartialSignature
|
||||
|
||||
type SignerSession interface {
|
||||
GetNonces() (TreeNonces, error) // generate of return cached nonce for this session
|
||||
SetKeys([]*btcec.PublicKey, TreeNonces) error // set the keys for this session (with the combined nonces)
|
||||
Sign() (TreePartialSigs, error) // sign the tree
|
||||
GetNonces() (TreeNonces, error) // generate tree nonces for this session
|
||||
SetKeys([]*btcec.PublicKey) error // set the cosigner public keys for this session
|
||||
SetAggregatedNonces(TreeNonces) error // set the aggregated nonces
|
||||
Sign() (TreePartialSigs, error) // sign the tree
|
||||
}
|
||||
|
||||
type CoordinatorSession interface {
|
||||
@@ -36,6 +63,34 @@ type CoordinatorSession interface {
|
||||
SignTree() (tree.CongestionTree, error)
|
||||
}
|
||||
|
||||
func (n TreeNonces) Encode(w io.Writer) error {
|
||||
matrix, err := encodeMatrix(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(matrix)
|
||||
return err
|
||||
}
|
||||
|
||||
func DecodeNonces(r io.Reader) (TreeNonces, error) {
|
||||
return decodeMatrix(func() *Musig2Nonce { return new(Musig2Nonce) }, r)
|
||||
}
|
||||
|
||||
func (s TreePartialSigs) Encode(w io.Writer) error {
|
||||
matrix, err := encodeMatrix(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(matrix)
|
||||
return err
|
||||
}
|
||||
|
||||
func DecodeSignatures(r io.Reader) (TreePartialSigs, error) {
|
||||
return decodeMatrix(func() *musig2.PartialSignature { return new(musig2.PartialSignature) }, r)
|
||||
}
|
||||
|
||||
func AggregateKeys(
|
||||
pubkeys []*btcec.PublicKey,
|
||||
scriptRoot []byte,
|
||||
@@ -100,62 +155,6 @@ func ValidateTreeSigs(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n TreeNonces) Decode(r io.Reader, matrixFormat []int) error {
|
||||
for i := 0; i < len(matrixFormat); i++ {
|
||||
for j := 0; j < matrixFormat[i]; j++ {
|
||||
// read 66 bytes
|
||||
nonce := make([]byte, 66)
|
||||
_, err := r.Read(nonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n[i][j] = [66]byte(nonce)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n TreeNonces) Encode(w io.Writer) error {
|
||||
for i := 0; i < len(n); i++ {
|
||||
for j := 0; j < len(n[i]); j++ {
|
||||
nonce := n[i][j][:]
|
||||
_, err := w.Write(nonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n TreePartialSigs) Decode(r io.Reader, matrixFormat []int) error {
|
||||
for i := 0; i < len(matrixFormat); i++ {
|
||||
for j := 0; j < matrixFormat[i]; j++ {
|
||||
sig := &musig2.PartialSignature{}
|
||||
if err := sig.Decode(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n TreePartialSigs) Encode(w io.Writer) error {
|
||||
for i := 0; i < len(n); i++ {
|
||||
for j := 0; j < len(n[i]); j++ {
|
||||
if err := n[i][j].Encode(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTreeSignerSession(
|
||||
signer *btcec.PrivateKey,
|
||||
congestionTree tree.CongestionTree,
|
||||
@@ -221,9 +220,9 @@ func (t *treeSignerSession) GetNonces() (TreeNonces, error) {
|
||||
nonces := make(TreeNonces, 0)
|
||||
|
||||
for _, level := range t.myNonces {
|
||||
levelNonces := make([][66]byte, 0)
|
||||
levelNonces := make([]*Musig2Nonce, 0)
|
||||
for _, nonce := range level {
|
||||
levelNonces = append(levelNonces, nonce.PubNonce)
|
||||
levelNonces = append(levelNonces, &Musig2Nonce{nonce.PubNonce})
|
||||
}
|
||||
nonces = append(nonces, levelNonces)
|
||||
}
|
||||
@@ -231,15 +230,11 @@ func (t *treeSignerSession) GetNonces() (TreeNonces, error) {
|
||||
return nonces, nil
|
||||
}
|
||||
|
||||
func (t *treeSignerSession) SetKeys(keys []*btcec.PublicKey, nonces TreeNonces) error {
|
||||
func (t *treeSignerSession) SetKeys(keys []*btcec.PublicKey) error {
|
||||
if t.keys != nil {
|
||||
return errors.New("keys already set")
|
||||
}
|
||||
|
||||
if t.aggregateNonces != nil {
|
||||
return errors.New("nonces already set")
|
||||
}
|
||||
|
||||
aggregateKey, err := AggregateKeys(keys, t.scriptRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -251,12 +246,20 @@ func (t *treeSignerSession) SetKeys(keys []*btcec.PublicKey, nonces TreeNonces)
|
||||
}
|
||||
|
||||
t.prevoutFetcher = prevoutFetcher
|
||||
t.aggregateNonces = nonces
|
||||
t.keys = keys
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *treeSignerSession) SetAggregatedNonces(nonces TreeNonces) error {
|
||||
if t.aggregateNonces != nil {
|
||||
return errors.New("nonces already set")
|
||||
}
|
||||
|
||||
t.aggregateNonces = nonces
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *treeSignerSession) Sign() (TreePartialSigs, error) {
|
||||
if t.tree == nil {
|
||||
return nil, ErrCongestionTreeNotSet
|
||||
@@ -313,7 +316,7 @@ func (t *treeSignerSession) signPartial(partialTx *psbt.Packet, posx int, posy i
|
||||
}
|
||||
|
||||
return musig2.Sign(
|
||||
myNonce.SecNonce, seckey, aggregatedNonce, t.keys, [32]byte(message),
|
||||
myNonce.SecNonce, seckey, aggregatedNonce.PubNonce, t.keys, [32]byte(message),
|
||||
musig2.WithSortedKeys(), musig2.WithTaprootSignTweak(t.scriptRoot),
|
||||
)
|
||||
}
|
||||
@@ -390,12 +393,11 @@ func (t *treeCoordinatorSession) AggregateNonces() (TreeNonces, error) {
|
||||
aggregatedNonces := make(TreeNonces, 0)
|
||||
|
||||
for i, level := range t.tree {
|
||||
levelNonces := make([][66]byte, 0)
|
||||
levelNonces := make([]*Musig2Nonce, 0)
|
||||
for j := range level {
|
||||
|
||||
nonces := make([][66]byte, 0)
|
||||
for _, n := range t.nonces {
|
||||
nonces = append(nonces, n[i][j])
|
||||
nonces = append(nonces, n[i][j].PubNonce)
|
||||
}
|
||||
|
||||
aggregatedNonce, err := musig2.AggregateNonces(nonces)
|
||||
@@ -403,7 +405,7 @@ func (t *treeCoordinatorSession) AggregateNonces() (TreeNonces, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
levelNonces = append(levelNonces, aggregatedNonce)
|
||||
levelNonces = append(levelNonces, &Musig2Nonce{aggregatedNonce})
|
||||
}
|
||||
|
||||
aggregatedNonces = append(aggregatedNonces, levelNonces)
|
||||
@@ -414,12 +416,17 @@ func (t *treeCoordinatorSession) AggregateNonces() (TreeNonces, error) {
|
||||
|
||||
// SignTree implements CoordinatorSession.
|
||||
func (t *treeCoordinatorSession) SignTree() (tree.CongestionTree, error) {
|
||||
var missingSigs int
|
||||
for _, sig := range t.sigs {
|
||||
if sig == nil {
|
||||
return nil, errors.New("signatures not set")
|
||||
missingSigs++
|
||||
}
|
||||
}
|
||||
|
||||
if missingSigs > 0 {
|
||||
return nil, fmt.Errorf("missing %d signature(s)", missingSigs)
|
||||
}
|
||||
|
||||
aggregatedKey, err := AggregateKeys(t.keys, t.scriptRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -432,9 +439,18 @@ func (t *treeCoordinatorSession) SignTree() (tree.CongestionTree, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var combinedNonce *secp256k1.PublicKey
|
||||
sigs := make([]*musig2.PartialSignature, 0)
|
||||
for _, sig := range t.sigs {
|
||||
sigs = append(sigs, sig[i][j])
|
||||
s := sig[i][j]
|
||||
if s.R != nil {
|
||||
combinedNonce = s.R
|
||||
}
|
||||
sigs = append(sigs, s)
|
||||
}
|
||||
|
||||
if combinedNonce == nil {
|
||||
return nil, errors.New("missing combined nonce")
|
||||
}
|
||||
|
||||
inputFetcher := t.prevoutFetcher(partialTx)
|
||||
@@ -448,7 +464,7 @@ func (t *treeCoordinatorSession) SignTree() (tree.CongestionTree, error) {
|
||||
)
|
||||
|
||||
combinedSig := musig2.CombineSigs(
|
||||
sigs[0].R, sigs,
|
||||
combinedNonce, sigs,
|
||||
musig2.WithTaprootTweakedCombine([32]byte(message), t.keys, t.scriptRoot, true),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -507,3 +523,71 @@ type treePrevOutFetcher struct {
|
||||
func (f *treePrevOutFetcher) FetchPrevOutput(wire.OutPoint) *wire.TxOut {
|
||||
return f.prevout
|
||||
}
|
||||
|
||||
type writable interface {
|
||||
Encode(w io.Writer) error
|
||||
}
|
||||
|
||||
type readable interface {
|
||||
Decode(r io.Reader) error
|
||||
}
|
||||
|
||||
// encodeMatrix encode a matrix of serializable objects into a byte stream
|
||||
func encodeMatrix[T writable](matrix [][]T) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, row := range matrix {
|
||||
for _, cell := range row {
|
||||
if err := buf.WriteByte(columnSeparator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := cell.Encode(&buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := buf.WriteByte(rowSeparator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// decodeMatrix decode a byte stream into a matrix of serializable objects
|
||||
func decodeMatrix[T readable](factory func() T, data io.Reader) ([][]T, error) {
|
||||
matrix := make([][]T, 0)
|
||||
row := make([]T, 0)
|
||||
|
||||
for {
|
||||
separator := make([]byte, 1)
|
||||
|
||||
if _, err := data.Read(separator); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := separator[0]
|
||||
|
||||
if b == rowSeparator {
|
||||
matrix = append(matrix, row)
|
||||
row = make([]T, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
cell := factory()
|
||||
|
||||
if err := cell.Decode(data); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
row = append(row, cell)
|
||||
}
|
||||
|
||||
return matrix, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package bitcointree_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -85,6 +87,31 @@ func TestRoundTripSignTree(t *testing.T) {
|
||||
aspNonces, err := aspSession.GetNonces()
|
||||
require.NoError(t, err)
|
||||
|
||||
s := bytes.NewBuffer(nil)
|
||||
|
||||
err = aspNonces[0][0].Encode(s)
|
||||
require.NoError(t, err)
|
||||
|
||||
bitcointreeNonce := new(bitcointree.Musig2Nonce)
|
||||
err = bitcointreeNonce.Decode(s)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, aspNonces[0][0], bitcointreeNonce)
|
||||
|
||||
var serializedNonces bytes.Buffer
|
||||
|
||||
err = aspNonces.Encode(&serializedNonces)
|
||||
require.NoError(t, err)
|
||||
|
||||
decodedNonces, err := bitcointree.DecodeNonces(&serializedNonces)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, nonces := range aspNonces {
|
||||
for j, nonce := range nonces {
|
||||
require.Equal(t, nonce.PubNonce, decodedNonces[i][j].PubNonce, fmt.Sprintf("matrix nonce not equal at index i: %d, j: %d", i, j))
|
||||
}
|
||||
}
|
||||
|
||||
err = aspCoordinator.AddNonce(alice.PubKey(), aliceNonces)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -101,18 +128,30 @@ func TestRoundTripSignTree(t *testing.T) {
|
||||
|
||||
err = aliceSession.SetKeys(
|
||||
cosigners,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = aliceSession.SetAggregatedNonces(
|
||||
aggregatedNonce,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = bobSession.SetKeys(
|
||||
cosigners,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = bobSession.SetAggregatedNonces(
|
||||
aggregatedNonce,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = aspSession.SetKeys(
|
||||
cosigners,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = aspSession.SetAggregatedNonces(
|
||||
aggregatedNonce,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@@ -126,6 +165,22 @@ func TestRoundTripSignTree(t *testing.T) {
|
||||
aspSig, err := aspSession.Sign()
|
||||
require.NoError(t, err)
|
||||
|
||||
// check that the sigs are serializable
|
||||
|
||||
serializedSigs := bytes.NewBuffer(nil)
|
||||
|
||||
err = aspSig.Encode(serializedSigs)
|
||||
require.NoError(t, err)
|
||||
|
||||
decodedSigs, err := bitcointree.DecodeSignatures(serializedSigs)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, sigs := range aspSig {
|
||||
for j, sig := range sigs {
|
||||
require.Equal(t, sig.S, decodedSigs[i][j].S, fmt.Sprintf("matrix sig not equal at index i: %d, j: %d", i, j))
|
||||
}
|
||||
}
|
||||
|
||||
// coordinator receives the signatures and combines them
|
||||
err = aspCoordinator.AddSig(alice.PubKey(), aliceSig)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## genrest: compiles rest client from stub with https://github.com/go-swagger/go-swagger
|
||||
genrest:
|
||||
@echo "Generating rest client from stub..."
|
||||
@swagger generate client -f ../../server/api-spec/openapi/swagger/ark/v1/service.swagger.json -t ./client/rest/service --client-package=arkservice
|
||||
@swagger generate client -f ../../api-spec/openapi/swagger/ark/v1/service.swagger.json -t ./client/rest/service --client-package=arkservice
|
||||
|
||||
## test: runs unit tests
|
||||
test:
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,7 +27,7 @@ type ASPClient interface {
|
||||
ctx context.Context, tx, userPubkey string, congestionTree tree.CongestionTree,
|
||||
) error
|
||||
RegisterPayment(
|
||||
ctx context.Context, inputs []VtxoKey,
|
||||
ctx context.Context, inputs []VtxoKey, ephemeralPublicKey string,
|
||||
) (string, error)
|
||||
ClaimPayment(
|
||||
ctx context.Context, paymentID string, outputs []Output,
|
||||
@@ -33,7 +35,7 @@ type ASPClient interface {
|
||||
GetEventStream(
|
||||
ctx context.Context, paymentID string,
|
||||
) (<-chan RoundEventChannel, error)
|
||||
Ping(ctx context.Context, paymentID string) (*RoundFinalizationEvent, error)
|
||||
Ping(ctx context.Context, paymentID string) (RoundEvent, error)
|
||||
FinalizePayment(
|
||||
ctx context.Context, signedForfeitTxs []string,
|
||||
) error
|
||||
@@ -43,6 +45,12 @@ type ASPClient interface {
|
||||
CompletePayment(
|
||||
ctx context.Context, signedRedeemTx string, signedUnconditionalForfeitTxs []string,
|
||||
) error
|
||||
SendTreeNonces(
|
||||
ctx context.Context, roundID, cosignerPubkey string, nonces bitcointree.TreeNonces,
|
||||
) error
|
||||
SendTreeSignatures(
|
||||
ctx context.Context, roundID, cosignerPubkey string, signatures bitcointree.TreePartialSigs,
|
||||
) error
|
||||
Close()
|
||||
}
|
||||
|
||||
@@ -139,3 +147,18 @@ type RoundFailedEvent struct {
|
||||
}
|
||||
|
||||
func (e RoundFailedEvent) isRoundEvent() {}
|
||||
|
||||
type RoundSigningStartedEvent struct {
|
||||
ID string
|
||||
UnsignedTree tree.CongestionTree
|
||||
CosignersPublicKeys []*secp256k1.PublicKey
|
||||
}
|
||||
|
||||
func (e RoundSigningStartedEvent) isRoundEvent() {}
|
||||
|
||||
type RoundSigningNoncesGeneratedEvent struct {
|
||||
ID string
|
||||
Nonces bitcointree.TreeNonces
|
||||
}
|
||||
|
||||
func (e RoundSigningNoncesGeneratedEvent) isRoundEvent() {}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package grpcclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
@@ -73,7 +77,13 @@ func (a *grpcClient) GetEventStream(
|
||||
return
|
||||
}
|
||||
|
||||
a.eventsCh <- client.RoundEventChannel{Event: event{resp}.toRoundEvent()}
|
||||
ev, err := event{resp}.toRoundEvent()
|
||||
if err != nil {
|
||||
a.eventsCh <- client.RoundEventChannel{Err: err}
|
||||
return
|
||||
}
|
||||
|
||||
a.eventsCh <- client.RoundEventChannel{Event: ev}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -146,11 +156,15 @@ func (a *grpcClient) Onboard(
|
||||
}
|
||||
|
||||
func (a *grpcClient) RegisterPayment(
|
||||
ctx context.Context, inputs []client.VtxoKey,
|
||||
ctx context.Context, inputs []client.VtxoKey, ephemeralPublicKey string,
|
||||
) (string, error) {
|
||||
req := &arkv1.RegisterPaymentRequest{
|
||||
Inputs: ins(inputs).toProto(),
|
||||
}
|
||||
if len(ephemeralPublicKey) > 0 {
|
||||
req.EphemeralPubkey = &ephemeralPublicKey
|
||||
}
|
||||
|
||||
resp, err := a.svc.RegisterPayment(ctx, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -171,7 +185,7 @@ func (a *grpcClient) ClaimPayment(
|
||||
|
||||
func (a *grpcClient) Ping(
|
||||
ctx context.Context, paymentID string,
|
||||
) (*client.RoundFinalizationEvent, error) {
|
||||
) (client.RoundEvent, error) {
|
||||
req := &arkv1.PingRequest{
|
||||
PaymentId: paymentID,
|
||||
}
|
||||
@@ -179,14 +193,8 @@ func (a *grpcClient) Ping(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
event := resp.GetEvent()
|
||||
return &client.RoundFinalizationEvent{
|
||||
ID: event.GetId(),
|
||||
Tx: event.GetPoolTx(),
|
||||
ForfeitTxs: event.GetForfeitTxs(),
|
||||
Tree: treeFromProto{event.GetCongestionTree()}.parse(),
|
||||
Connectors: event.GetConnectors(),
|
||||
}, nil
|
||||
|
||||
return event{resp}.toRoundEvent()
|
||||
}
|
||||
|
||||
func (a *grpcClient) FinalizePayment(
|
||||
@@ -252,6 +260,54 @@ func (a *grpcClient) GetRoundByID(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) SendTreeNonces(
|
||||
ctx context.Context, roundID, cosignerPubkey string, nonces bitcointree.TreeNonces,
|
||||
) error {
|
||||
var nonceBuffer bytes.Buffer
|
||||
|
||||
if err := nonces.Encode(&nonceBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedNonces := hex.EncodeToString(nonceBuffer.Bytes())
|
||||
|
||||
req := &arkv1.SendTreeNoncesRequest{
|
||||
RoundId: roundID,
|
||||
PublicKey: cosignerPubkey,
|
||||
TreeNonces: serializedNonces,
|
||||
}
|
||||
|
||||
if _, err := a.svc.SendTreeNonces(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *grpcClient) SendTreeSignatures(
|
||||
ctx context.Context, roundID, cosignerPubkey string, signatures bitcointree.TreePartialSigs,
|
||||
) error {
|
||||
var sigsBuffer bytes.Buffer
|
||||
|
||||
if err := signatures.Encode(&sigsBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedSigs := hex.EncodeToString(sigsBuffer.Bytes())
|
||||
|
||||
req := &arkv1.SendTreeSignaturesRequest{
|
||||
RoundId: roundID,
|
||||
PublicKey: cosignerPubkey,
|
||||
TreeSignatures: serializedSigs,
|
||||
}
|
||||
|
||||
if _, err := a.svc.SendTreeSignatures(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type out client.Output
|
||||
|
||||
func (o out) toProto() *arkv1.Output {
|
||||
@@ -271,16 +327,25 @@ func (o outs) toProto() []*arkv1.Output {
|
||||
return list
|
||||
}
|
||||
|
||||
type event struct {
|
||||
*arkv1.GetEventStreamResponse
|
||||
// wrapper for GetEventStreamResponse and PingResponse
|
||||
type eventResponse interface {
|
||||
GetRoundFailed() *arkv1.RoundFailed
|
||||
GetRoundFinalization() *arkv1.RoundFinalizationEvent
|
||||
GetRoundFinalized() *arkv1.RoundFinalizedEvent
|
||||
GetRoundSigning() *arkv1.RoundSigningEvent
|
||||
GetRoundSigningNoncesGenerated() *arkv1.RoundSigningNoncesGeneratedEvent
|
||||
}
|
||||
|
||||
func (e event) toRoundEvent() client.RoundEvent {
|
||||
type event struct {
|
||||
eventResponse
|
||||
}
|
||||
|
||||
func (e event) toRoundEvent() (client.RoundEvent, error) {
|
||||
if ee := e.GetRoundFailed(); ee != nil {
|
||||
return client.RoundFailedEvent{
|
||||
ID: ee.GetId(),
|
||||
Reason: ee.GetReason(),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
if ee := e.GetRoundFinalization(); ee != nil {
|
||||
tree := treeFromProto{ee.GetCongestionTree()}.parse()
|
||||
@@ -290,13 +355,49 @@ func (e event) toRoundEvent() client.RoundEvent {
|
||||
ForfeitTxs: ee.GetForfeitTxs(),
|
||||
Tree: tree,
|
||||
Connectors: ee.GetConnectors(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if ee := e.GetRoundFinalized(); ee != nil {
|
||||
return client.RoundFinalizedEvent{
|
||||
ID: ee.GetId(),
|
||||
Txid: ee.GetPoolTxid(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if ee := e.GetRoundSigning(); ee != nil {
|
||||
pubkeys := make([]*secp256k1.PublicKey, 0, len(ee.GetCosignersPubkeys()))
|
||||
for _, pubkey := range ee.GetCosignersPubkeys() {
|
||||
p, err := hex.DecodeString(pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pk, err := secp256k1.ParsePubKey(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubkeys = append(pubkeys, pk)
|
||||
}
|
||||
|
||||
return client.RoundSigningStartedEvent{
|
||||
ID: ee.GetId(),
|
||||
UnsignedTree: treeFromProto{ee.GetUnsignedTree()}.parse(),
|
||||
CosignersPublicKeys: pubkeys,
|
||||
}, nil
|
||||
}
|
||||
ee := e.GetRoundFinalized()
|
||||
return client.RoundFinalizedEvent{
|
||||
ID: ee.GetId(),
|
||||
Txid: ee.GetPoolTxid(),
|
||||
|
||||
if ee := e.GetRoundSigningNoncesGenerated(); ee != nil {
|
||||
nonces, err := bitcointree.DecodeNonces(hex.NewDecoder(strings.NewReader(ee.GetTreeNonces())))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.RoundSigningNoncesGeneratedEvent{
|
||||
ID: ee.GetId(),
|
||||
Nonces: nonces,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown event")
|
||||
}
|
||||
|
||||
type vtxo struct {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package restclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -9,16 +11,16 @@ import (
|
||||
"time"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/arkservice"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/arkservice/ark_service"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
type restClient struct {
|
||||
@@ -71,39 +73,7 @@ func (a *restClient) GetEventStream(
|
||||
|
||||
if event != nil {
|
||||
a.eventsCh <- client.RoundEventChannel{
|
||||
Event: *event,
|
||||
}
|
||||
|
||||
for {
|
||||
roundID := event.ID
|
||||
round, err := a.GetRoundByID(ctx, roundID)
|
||||
if err != nil {
|
||||
a.eventsCh <- client.RoundEventChannel{
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if round.Stage == client.RoundStageFinalized {
|
||||
a.eventsCh <- client.RoundEventChannel{
|
||||
Event: client.RoundFinalizedEvent{
|
||||
ID: roundID,
|
||||
Txid: getTxid(round.Tx),
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if round.Stage == client.RoundStageFailed {
|
||||
a.eventsCh <- client.RoundEventChannel{
|
||||
Event: client.RoundFailedEvent{
|
||||
ID: roundID,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
Event: event,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +256,7 @@ func (a *restClient) Onboard(
|
||||
}
|
||||
|
||||
func (a *restClient) RegisterPayment(
|
||||
ctx context.Context, inputs []client.VtxoKey,
|
||||
ctx context.Context, inputs []client.VtxoKey, ephemeralPublicKey string,
|
||||
) (string, error) {
|
||||
ins := make([]*models.V1Input, 0, len(inputs))
|
||||
for _, i := range inputs {
|
||||
@@ -295,11 +265,15 @@ func (a *restClient) RegisterPayment(
|
||||
Vout: int64(i.VOut),
|
||||
})
|
||||
}
|
||||
body := models.V1RegisterPaymentRequest{
|
||||
body := &models.V1RegisterPaymentRequest{
|
||||
Inputs: ins,
|
||||
}
|
||||
if len(ephemeralPublicKey) > 0 {
|
||||
body.EphemeralPubkey = ephemeralPublicKey
|
||||
}
|
||||
|
||||
resp, err := a.svc.ArkServiceRegisterPayment(
|
||||
ark_service.NewArkServiceRegisterPaymentParams().WithBody(&body),
|
||||
ark_service.NewArkServiceRegisterPaymentParams().WithBody(body),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -331,7 +305,7 @@ func (a *restClient) ClaimPayment(
|
||||
|
||||
func (a *restClient) Ping(
|
||||
ctx context.Context, paymentID string,
|
||||
) (*client.RoundFinalizationEvent, error) {
|
||||
) (client.RoundEvent, error) {
|
||||
r := ark_service.NewArkServicePingParams()
|
||||
r.SetPaymentID(paymentID)
|
||||
resp, err := a.svc.ArkServicePing(r)
|
||||
@@ -339,18 +313,65 @@ func (a *restClient) Ping(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var event *client.RoundFinalizationEvent
|
||||
if resp.Payload.Event != nil {
|
||||
event = &client.RoundFinalizationEvent{
|
||||
ID: resp.Payload.Event.ID,
|
||||
Tx: resp.Payload.Event.PoolTx,
|
||||
ForfeitTxs: resp.Payload.Event.ForfeitTxs,
|
||||
Tree: treeFromProto{resp.Payload.Event.CongestionTree}.parse(),
|
||||
Connectors: resp.Payload.Event.Connectors,
|
||||
}
|
||||
payload := resp.Payload
|
||||
|
||||
if e := payload.RoundFailed; e != nil {
|
||||
return client.RoundFailedEvent{
|
||||
ID: e.ID,
|
||||
Reason: e.Reason,
|
||||
}, nil
|
||||
}
|
||||
if e := payload.RoundFinalization; e != nil {
|
||||
tree := treeFromProto{e.CongestionTree}.parse()
|
||||
return client.RoundFinalizationEvent{
|
||||
ID: e.ID,
|
||||
Tx: e.PoolTx,
|
||||
ForfeitTxs: e.ForfeitTxs,
|
||||
Tree: tree,
|
||||
Connectors: e.Connectors,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return event, nil
|
||||
if e := payload.RoundFinalized; e != nil {
|
||||
return client.RoundFinalizedEvent{
|
||||
ID: e.ID,
|
||||
Txid: e.PoolTxid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if e := payload.RoundSigning; e != nil {
|
||||
pubkeys := make([]*secp256k1.PublicKey, 0, len(e.CosignersPubkeys))
|
||||
for _, pubkey := range e.CosignersPubkeys {
|
||||
p, err := hex.DecodeString(pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pk, err := secp256k1.ParsePubKey(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubkeys = append(pubkeys, pk)
|
||||
}
|
||||
|
||||
return client.RoundSigningStartedEvent{
|
||||
ID: e.ID,
|
||||
UnsignedTree: treeFromProto{e.UnsignedTree}.parse(),
|
||||
CosignersPublicKeys: pubkeys,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if e := payload.RoundSigningNoncesGenerated; e != nil {
|
||||
nonces, err := bitcointree.DecodeNonces(hex.NewDecoder(strings.NewReader(e.TreeNonces)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.RoundSigningNoncesGeneratedEvent{
|
||||
ID: e.ID,
|
||||
Nonces: nonces,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (a *restClient) FinalizePayment(
|
||||
@@ -454,6 +475,58 @@ func (a *restClient) GetRoundByID(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *restClient) SendTreeNonces(
|
||||
ctx context.Context, roundID, cosignerPubkey string, nonces bitcointree.TreeNonces,
|
||||
) error {
|
||||
var nonceBuffer bytes.Buffer
|
||||
|
||||
if err := nonces.Encode(&nonceBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedNonces := hex.EncodeToString(nonceBuffer.Bytes())
|
||||
|
||||
body := &models.V1SendTreeNoncesRequest{
|
||||
RoundID: roundID,
|
||||
PublicKey: cosignerPubkey,
|
||||
TreeNonces: serializedNonces,
|
||||
}
|
||||
|
||||
if _, err := a.svc.ArkServiceSendTreeNonces(
|
||||
ark_service.NewArkServiceSendTreeNoncesParams().WithBody(body),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *restClient) SendTreeSignatures(
|
||||
ctx context.Context, roundID, cosignerPubkey string, signatures bitcointree.TreePartialSigs,
|
||||
) error {
|
||||
var sigsBuffer bytes.Buffer
|
||||
|
||||
if err := signatures.Encode(&sigsBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedSigs := hex.EncodeToString(sigsBuffer.Bytes())
|
||||
|
||||
body := &models.V1SendTreeSignaturesRequest{
|
||||
RoundID: roundID,
|
||||
PublicKey: cosignerPubkey,
|
||||
TreeSignatures: serializedSigs,
|
||||
}
|
||||
|
||||
if _, err := a.svc.ArkServiceSendTreeSignatures(
|
||||
ark_service.NewArkServiceSendTreeSignaturesParams().WithBody(body),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newRestClient(
|
||||
serviceURL string,
|
||||
) (ark_service.ClientService, error) {
|
||||
@@ -551,13 +624,3 @@ func (t treeToProto) parse() *models.V1Tree {
|
||||
Levels: levels,
|
||||
}
|
||||
}
|
||||
|
||||
func getTxid(tx string) string {
|
||||
if ptx, _ := psetv2.NewPsetFromBase64(tx); ptx != nil {
|
||||
utx, _ := ptx.UnsignedTx()
|
||||
return utx.TxHash().String()
|
||||
}
|
||||
|
||||
ptx, _ := psbt.NewFromRawBytes(strings.NewReader(tx), true)
|
||||
return ptx.UnsignedTx.TxID()
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@ type ClientService interface {
|
||||
|
||||
ArkServiceRegisterPayment(params *ArkServiceRegisterPaymentParams, opts ...ClientOption) (*ArkServiceRegisterPaymentOK, error)
|
||||
|
||||
ArkServiceSendTreeNonces(params *ArkServiceSendTreeNoncesParams, opts ...ClientOption) (*ArkServiceSendTreeNoncesOK, error)
|
||||
|
||||
ArkServiceSendTreeSignatures(params *ArkServiceSendTreeSignaturesParams, opts ...ClientOption) (*ArkServiceSendTreeSignaturesOK, error)
|
||||
|
||||
SetTransport(transport runtime.ClientTransport)
|
||||
}
|
||||
|
||||
@@ -304,7 +308,7 @@ func (a *Client) ArkServiceGetInfo(params *ArkServiceGetInfoParams, opts ...Clie
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceGetRound ts o d o b t c sign tree rpc
|
||||
ArkServiceGetRound ark service get round API
|
||||
*/
|
||||
func (a *Client) ArkServiceGetRound(params *ArkServiceGetRoundParams, opts ...ClientOption) (*ArkServiceGetRoundOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
@@ -525,6 +529,80 @@ func (a *Client) ArkServiceRegisterPayment(params *ArkServiceRegisterPaymentPara
|
||||
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeNonces ark service send tree nonces API
|
||||
*/
|
||||
func (a *Client) ArkServiceSendTreeNonces(params *ArkServiceSendTreeNoncesParams, opts ...ClientOption) (*ArkServiceSendTreeNoncesOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
if params == nil {
|
||||
params = NewArkServiceSendTreeNoncesParams()
|
||||
}
|
||||
op := &runtime.ClientOperation{
|
||||
ID: "ArkService_SendTreeNonces",
|
||||
Method: "POST",
|
||||
PathPattern: "/v1/payment/tree/nonces",
|
||||
ProducesMediaTypes: []string{"application/json"},
|
||||
ConsumesMediaTypes: []string{"application/json"},
|
||||
Schemes: []string{"http"},
|
||||
Params: params,
|
||||
Reader: &ArkServiceSendTreeNoncesReader{formats: a.formats},
|
||||
Context: params.Context,
|
||||
Client: params.HTTPClient,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success, ok := result.(*ArkServiceSendTreeNoncesOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
unexpectedSuccess := result.(*ArkServiceSendTreeNoncesDefault)
|
||||
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeSignatures ark service send tree signatures API
|
||||
*/
|
||||
func (a *Client) ArkServiceSendTreeSignatures(params *ArkServiceSendTreeSignaturesParams, opts ...ClientOption) (*ArkServiceSendTreeSignaturesOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
if params == nil {
|
||||
params = NewArkServiceSendTreeSignaturesParams()
|
||||
}
|
||||
op := &runtime.ClientOperation{
|
||||
ID: "ArkService_SendTreeSignatures",
|
||||
Method: "POST",
|
||||
PathPattern: "/v1/payment/tree/signatures",
|
||||
ProducesMediaTypes: []string{"application/json"},
|
||||
ConsumesMediaTypes: []string{"application/json"},
|
||||
Schemes: []string{"http"},
|
||||
Params: params,
|
||||
Reader: &ArkServiceSendTreeSignaturesReader{formats: a.formats},
|
||||
Context: params.Context,
|
||||
Client: params.HTTPClient,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
|
||||
result, err := a.transport.Submit(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success, ok := result.(*ArkServiceSendTreeSignaturesOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
unexpectedSuccess := result.(*ArkServiceSendTreeSignaturesDefault)
|
||||
return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())
|
||||
}
|
||||
|
||||
// SetTransport changes the transport on the client
|
||||
func (a *Client) SetTransport(transport runtime.ClientTransport) {
|
||||
a.transport = transport
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
cr "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// NewArkServiceSendTreeNoncesParams creates a new ArkServiceSendTreeNoncesParams object,
|
||||
// with the default timeout for this client.
|
||||
//
|
||||
// Default values are not hydrated, since defaults are normally applied by the API server side.
|
||||
//
|
||||
// To enforce default values in parameter, use SetDefaults or WithDefaults.
|
||||
func NewArkServiceSendTreeNoncesParams() *ArkServiceSendTreeNoncesParams {
|
||||
return &ArkServiceSendTreeNoncesParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeNoncesParamsWithTimeout creates a new ArkServiceSendTreeNoncesParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewArkServiceSendTreeNoncesParamsWithTimeout(timeout time.Duration) *ArkServiceSendTreeNoncesParams {
|
||||
return &ArkServiceSendTreeNoncesParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeNoncesParamsWithContext creates a new ArkServiceSendTreeNoncesParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewArkServiceSendTreeNoncesParamsWithContext(ctx context.Context) *ArkServiceSendTreeNoncesParams {
|
||||
return &ArkServiceSendTreeNoncesParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeNoncesParamsWithHTTPClient creates a new ArkServiceSendTreeNoncesParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewArkServiceSendTreeNoncesParamsWithHTTPClient(client *http.Client) *ArkServiceSendTreeNoncesParams {
|
||||
return &ArkServiceSendTreeNoncesParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeNoncesParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the ark service send tree nonces operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type ArkServiceSendTreeNoncesParams struct {
|
||||
|
||||
// Body.
|
||||
Body *models.V1SendTreeNoncesRequest
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the ark service send tree nonces params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceSendTreeNoncesParams) WithDefaults() *ArkServiceSendTreeNoncesParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the ark service send tree nonces params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceSendTreeNoncesParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) WithTimeout(timeout time.Duration) *ArkServiceSendTreeNoncesParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) WithContext(ctx context.Context) *ArkServiceSendTreeNoncesParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) WithHTTPClient(client *http.Client) *ArkServiceSendTreeNoncesParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithBody adds the body to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) WithBody(body *models.V1SendTreeNoncesRequest) *ArkServiceSendTreeNoncesParams {
|
||||
o.SetBody(body)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBody adds the body to the ark service send tree nonces params
|
||||
func (o *ArkServiceSendTreeNoncesParams) SetBody(body *models.V1SendTreeNoncesRequest) {
|
||||
o.Body = body
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *ArkServiceSendTreeNoncesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
if o.Body != nil {
|
||||
if err := r.SetBodyParam(o.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// ArkServiceSendTreeNoncesReader is a Reader for the ArkServiceSendTreeNonces structure.
|
||||
type ArkServiceSendTreeNoncesReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *ArkServiceSendTreeNoncesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewArkServiceSendTreeNoncesOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
result := NewArkServiceSendTreeNoncesDefault(response.Code())
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Code()/100 == 2 {
|
||||
return result, nil
|
||||
}
|
||||
return nil, result
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeNoncesOK creates a ArkServiceSendTreeNoncesOK with default headers values
|
||||
func NewArkServiceSendTreeNoncesOK() *ArkServiceSendTreeNoncesOK {
|
||||
return &ArkServiceSendTreeNoncesOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeNoncesOK describes a response with status code 200, with default header values.
|
||||
|
||||
A successful response.
|
||||
*/
|
||||
type ArkServiceSendTreeNoncesOK struct {
|
||||
Payload models.V1SendTreeNoncesResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service send tree nonces o k response has a 2xx status code
|
||||
func (o *ArkServiceSendTreeNoncesOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service send tree nonces o k response has a 3xx status code
|
||||
func (o *ArkServiceSendTreeNoncesOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service send tree nonces o k response has a 4xx status code
|
||||
func (o *ArkServiceSendTreeNoncesOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service send tree nonces o k response has a 5xx status code
|
||||
func (o *ArkServiceSendTreeNoncesOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service send tree nonces o k response a status code equal to that given
|
||||
func (o *ArkServiceSendTreeNoncesOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service send tree nonces o k response
|
||||
func (o *ArkServiceSendTreeNoncesOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/nonces][%d] arkServiceSendTreeNoncesOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/nonces][%d] arkServiceSendTreeNoncesOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesOK) GetPayload() models.V1SendTreeNoncesResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeNoncesDefault creates a ArkServiceSendTreeNoncesDefault with default headers values
|
||||
func NewArkServiceSendTreeNoncesDefault(code int) *ArkServiceSendTreeNoncesDefault {
|
||||
return &ArkServiceSendTreeNoncesDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeNoncesDefault describes a response with status code -1, with default header values.
|
||||
|
||||
An unexpected error response.
|
||||
*/
|
||||
type ArkServiceSendTreeNoncesDefault struct {
|
||||
_statusCode int
|
||||
|
||||
Payload *models.RPCStatus
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service send tree nonces default response has a 2xx status code
|
||||
func (o *ArkServiceSendTreeNoncesDefault) IsSuccess() bool {
|
||||
return o._statusCode/100 == 2
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service send tree nonces default response has a 3xx status code
|
||||
func (o *ArkServiceSendTreeNoncesDefault) IsRedirect() bool {
|
||||
return o._statusCode/100 == 3
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service send tree nonces default response has a 4xx status code
|
||||
func (o *ArkServiceSendTreeNoncesDefault) IsClientError() bool {
|
||||
return o._statusCode/100 == 4
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service send tree nonces default response has a 5xx status code
|
||||
func (o *ArkServiceSendTreeNoncesDefault) IsServerError() bool {
|
||||
return o._statusCode/100 == 5
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service send tree nonces default response a status code equal to that given
|
||||
func (o *ArkServiceSendTreeNoncesDefault) IsCode(code int) bool {
|
||||
return o._statusCode == code
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service send tree nonces default response
|
||||
func (o *ArkServiceSendTreeNoncesDefault) Code() int {
|
||||
return o._statusCode
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesDefault) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/nonces][%d] ArkService_SendTreeNonces default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesDefault) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/nonces][%d] ArkService_SendTreeNonces default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesDefault) GetPayload() *models.RPCStatus {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeNoncesDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
o.Payload = new(models.RPCStatus)
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
cr "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// NewArkServiceSendTreeSignaturesParams creates a new ArkServiceSendTreeSignaturesParams object,
|
||||
// with the default timeout for this client.
|
||||
//
|
||||
// Default values are not hydrated, since defaults are normally applied by the API server side.
|
||||
//
|
||||
// To enforce default values in parameter, use SetDefaults or WithDefaults.
|
||||
func NewArkServiceSendTreeSignaturesParams() *ArkServiceSendTreeSignaturesParams {
|
||||
return &ArkServiceSendTreeSignaturesParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeSignaturesParamsWithTimeout creates a new ArkServiceSendTreeSignaturesParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewArkServiceSendTreeSignaturesParamsWithTimeout(timeout time.Duration) *ArkServiceSendTreeSignaturesParams {
|
||||
return &ArkServiceSendTreeSignaturesParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeSignaturesParamsWithContext creates a new ArkServiceSendTreeSignaturesParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewArkServiceSendTreeSignaturesParamsWithContext(ctx context.Context) *ArkServiceSendTreeSignaturesParams {
|
||||
return &ArkServiceSendTreeSignaturesParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeSignaturesParamsWithHTTPClient creates a new ArkServiceSendTreeSignaturesParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewArkServiceSendTreeSignaturesParamsWithHTTPClient(client *http.Client) *ArkServiceSendTreeSignaturesParams {
|
||||
return &ArkServiceSendTreeSignaturesParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeSignaturesParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the ark service send tree signatures operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type ArkServiceSendTreeSignaturesParams struct {
|
||||
|
||||
// Body.
|
||||
Body *models.V1SendTreeSignaturesRequest
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the ark service send tree signatures params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceSendTreeSignaturesParams) WithDefaults() *ArkServiceSendTreeSignaturesParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the ark service send tree signatures params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *ArkServiceSendTreeSignaturesParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) WithTimeout(timeout time.Duration) *ArkServiceSendTreeSignaturesParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) WithContext(ctx context.Context) *ArkServiceSendTreeSignaturesParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) WithHTTPClient(client *http.Client) *ArkServiceSendTreeSignaturesParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithBody adds the body to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) WithBody(body *models.V1SendTreeSignaturesRequest) *ArkServiceSendTreeSignaturesParams {
|
||||
o.SetBody(body)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBody adds the body to the ark service send tree signatures params
|
||||
func (o *ArkServiceSendTreeSignaturesParams) SetBody(body *models.V1SendTreeSignaturesRequest) {
|
||||
o.Body = body
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *ArkServiceSendTreeSignaturesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
if o.Body != nil {
|
||||
if err := r.SetBodyParam(o.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package ark_service
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client/rest/service/models"
|
||||
)
|
||||
|
||||
// ArkServiceSendTreeSignaturesReader is a Reader for the ArkServiceSendTreeSignatures structure.
|
||||
type ArkServiceSendTreeSignaturesReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *ArkServiceSendTreeSignaturesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewArkServiceSendTreeSignaturesOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
result := NewArkServiceSendTreeSignaturesDefault(response.Code())
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Code()/100 == 2 {
|
||||
return result, nil
|
||||
}
|
||||
return nil, result
|
||||
}
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeSignaturesOK creates a ArkServiceSendTreeSignaturesOK with default headers values
|
||||
func NewArkServiceSendTreeSignaturesOK() *ArkServiceSendTreeSignaturesOK {
|
||||
return &ArkServiceSendTreeSignaturesOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeSignaturesOK describes a response with status code 200, with default header values.
|
||||
|
||||
A successful response.
|
||||
*/
|
||||
type ArkServiceSendTreeSignaturesOK struct {
|
||||
Payload models.V1SendTreeSignaturesResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service send tree signatures o k response has a 2xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service send tree signatures o k response has a 3xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service send tree signatures o k response has a 4xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service send tree signatures o k response has a 5xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service send tree signatures o k response a status code equal to that given
|
||||
func (o *ArkServiceSendTreeSignaturesOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service send tree signatures o k response
|
||||
func (o *ArkServiceSendTreeSignaturesOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/signatures][%d] arkServiceSendTreeSignaturesOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/signatures][%d] arkServiceSendTreeSignaturesOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesOK) GetPayload() models.V1SendTreeSignaturesResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewArkServiceSendTreeSignaturesDefault creates a ArkServiceSendTreeSignaturesDefault with default headers values
|
||||
func NewArkServiceSendTreeSignaturesDefault(code int) *ArkServiceSendTreeSignaturesDefault {
|
||||
return &ArkServiceSendTreeSignaturesDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ArkServiceSendTreeSignaturesDefault describes a response with status code -1, with default header values.
|
||||
|
||||
An unexpected error response.
|
||||
*/
|
||||
type ArkServiceSendTreeSignaturesDefault struct {
|
||||
_statusCode int
|
||||
|
||||
Payload *models.RPCStatus
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this ark service send tree signatures default response has a 2xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) IsSuccess() bool {
|
||||
return o._statusCode/100 == 2
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this ark service send tree signatures default response has a 3xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) IsRedirect() bool {
|
||||
return o._statusCode/100 == 3
|
||||
}
|
||||
|
||||
// IsClientError returns true when this ark service send tree signatures default response has a 4xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) IsClientError() bool {
|
||||
return o._statusCode/100 == 4
|
||||
}
|
||||
|
||||
// IsServerError returns true when this ark service send tree signatures default response has a 5xx status code
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) IsServerError() bool {
|
||||
return o._statusCode/100 == 5
|
||||
}
|
||||
|
||||
// IsCode returns true when this ark service send tree signatures default response a status code equal to that given
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) IsCode(code int) bool {
|
||||
return o._statusCode == code
|
||||
}
|
||||
|
||||
// Code gets the status code for the ark service send tree signatures default response
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) Code() int {
|
||||
return o._statusCode
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/signatures][%d] ArkService_SendTreeSignatures default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[POST /v1/payment/tree/signatures][%d] ArkService_SendTreeSignatures default %s", o._statusCode, payload)
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) GetPayload() *models.RPCStatus {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *ArkServiceSendTreeSignaturesDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
o.Payload = new(models.RPCStatus)
|
||||
|
||||
// response payload
|
||||
if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -21,11 +21,17 @@ type V1GetEventStreamResponse struct {
|
||||
// round failed
|
||||
RoundFailed *V1RoundFailed `json:"roundFailed,omitempty"`
|
||||
|
||||
// TODO: BTC add "signTree" event
|
||||
// round finalization
|
||||
RoundFinalization *V1RoundFinalizationEvent `json:"roundFinalization,omitempty"`
|
||||
|
||||
// round finalized
|
||||
RoundFinalized *V1RoundFinalizedEvent `json:"roundFinalized,omitempty"`
|
||||
|
||||
// round signing
|
||||
RoundSigning *V1RoundSigningEvent `json:"roundSigning,omitempty"`
|
||||
|
||||
// round signing nonces generated
|
||||
RoundSigningNoncesGenerated *V1RoundSigningNoncesGeneratedEvent `json:"roundSigningNoncesGenerated,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 get event stream response
|
||||
@@ -44,6 +50,14 @@ func (m *V1GetEventStreamResponse) Validate(formats strfmt.Registry) error {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRoundSigning(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRoundSigningNoncesGenerated(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
@@ -107,6 +121,44 @@ func (m *V1GetEventStreamResponse) validateRoundFinalized(formats strfmt.Registr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1GetEventStreamResponse) validateRoundSigning(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundSigning) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.RoundSigning != nil {
|
||||
if err := m.RoundSigning.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigning")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigning")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1GetEventStreamResponse) validateRoundSigningNoncesGenerated(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundSigningNoncesGenerated) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.RoundSigningNoncesGenerated != nil {
|
||||
if err := m.RoundSigningNoncesGenerated.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigningNoncesGenerated")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigningNoncesGenerated")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this v1 get event stream response based on the context it is used
|
||||
func (m *V1GetEventStreamResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
@@ -123,6 +175,14 @@ func (m *V1GetEventStreamResponse) ContextValidate(ctx context.Context, formats
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateRoundSigning(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateRoundSigningNoncesGenerated(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
@@ -192,6 +252,48 @@ func (m *V1GetEventStreamResponse) contextValidateRoundFinalized(ctx context.Con
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1GetEventStreamResponse) contextValidateRoundSigning(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.RoundSigning != nil {
|
||||
|
||||
if swag.IsZero(m.RoundSigning) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.RoundSigning.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigning")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigning")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1GetEventStreamResponse) contextValidateRoundSigningNoncesGenerated(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.RoundSigningNoncesGenerated != nil {
|
||||
|
||||
if swag.IsZero(m.RoundSigningNoncesGenerated) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.RoundSigningNoncesGenerated.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigningNoncesGenerated")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigningNoncesGenerated")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1GetEventStreamResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
|
||||
@@ -18,18 +18,43 @@ import (
|
||||
// swagger:model v1PingResponse
|
||||
type V1PingResponse struct {
|
||||
|
||||
// event
|
||||
Event *V1RoundFinalizationEvent `json:"event,omitempty"`
|
||||
// round failed
|
||||
RoundFailed *V1RoundFailed `json:"roundFailed,omitempty"`
|
||||
|
||||
// forfeit txs
|
||||
ForfeitTxs []string `json:"forfeitTxs"`
|
||||
// round finalization
|
||||
RoundFinalization *V1RoundFinalizationEvent `json:"roundFinalization,omitempty"`
|
||||
|
||||
// round finalized
|
||||
RoundFinalized *V1RoundFinalizedEvent `json:"roundFinalized,omitempty"`
|
||||
|
||||
// round signing
|
||||
RoundSigning *V1RoundSigningEvent `json:"roundSigning,omitempty"`
|
||||
|
||||
// round signing nonces generated
|
||||
RoundSigningNoncesGenerated *V1RoundSigningNoncesGeneratedEvent `json:"roundSigningNoncesGenerated,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 ping response
|
||||
func (m *V1PingResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateEvent(formats); err != nil {
|
||||
if err := m.validateRoundFailed(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRoundFinalization(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRoundFinalized(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRoundSigning(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRoundSigningNoncesGenerated(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -39,17 +64,93 @@ func (m *V1PingResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) validateEvent(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Event) { // not required
|
||||
func (m *V1PingResponse) validateRoundFailed(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundFailed) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Event != nil {
|
||||
if err := m.Event.Validate(formats); err != nil {
|
||||
if m.RoundFailed != nil {
|
||||
if err := m.RoundFailed.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("event")
|
||||
return ve.ValidateName("roundFailed")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("event")
|
||||
return ce.ValidateName("roundFailed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) validateRoundFinalization(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundFinalization) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.RoundFinalization != nil {
|
||||
if err := m.RoundFinalization.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundFinalization")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundFinalization")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) validateRoundFinalized(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundFinalized) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.RoundFinalized != nil {
|
||||
if err := m.RoundFinalized.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundFinalized")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundFinalized")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) validateRoundSigning(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundSigning) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.RoundSigning != nil {
|
||||
if err := m.RoundSigning.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigning")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigning")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) validateRoundSigningNoncesGenerated(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.RoundSigningNoncesGenerated) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.RoundSigningNoncesGenerated != nil {
|
||||
if err := m.RoundSigningNoncesGenerated.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigningNoncesGenerated")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigningNoncesGenerated")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -62,7 +163,23 @@ func (m *V1PingResponse) validateEvent(formats strfmt.Registry) error {
|
||||
func (m *V1PingResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateEvent(ctx, formats); err != nil {
|
||||
if err := m.contextValidateRoundFailed(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateRoundFinalization(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateRoundFinalized(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateRoundSigning(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateRoundSigningNoncesGenerated(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -72,19 +189,103 @@ func (m *V1PingResponse) ContextValidate(ctx context.Context, formats strfmt.Reg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) contextValidateEvent(ctx context.Context, formats strfmt.Registry) error {
|
||||
func (m *V1PingResponse) contextValidateRoundFailed(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Event != nil {
|
||||
if m.RoundFailed != nil {
|
||||
|
||||
if swag.IsZero(m.Event) { // not required
|
||||
if swag.IsZero(m.RoundFailed) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Event.ContextValidate(ctx, formats); err != nil {
|
||||
if err := m.RoundFailed.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("event")
|
||||
return ve.ValidateName("roundFailed")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("event")
|
||||
return ce.ValidateName("roundFailed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) contextValidateRoundFinalization(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.RoundFinalization != nil {
|
||||
|
||||
if swag.IsZero(m.RoundFinalization) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.RoundFinalization.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundFinalization")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundFinalization")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) contextValidateRoundFinalized(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.RoundFinalized != nil {
|
||||
|
||||
if swag.IsZero(m.RoundFinalized) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.RoundFinalized.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundFinalized")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundFinalized")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) contextValidateRoundSigning(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.RoundSigning != nil {
|
||||
|
||||
if swag.IsZero(m.RoundSigning) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.RoundSigning.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigning")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigning")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1PingResponse) contextValidateRoundSigningNoncesGenerated(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.RoundSigningNoncesGenerated != nil {
|
||||
|
||||
if swag.IsZero(m.RoundSigningNoncesGenerated) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.RoundSigningNoncesGenerated.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("roundSigningNoncesGenerated")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("roundSigningNoncesGenerated")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ import (
|
||||
// swagger:model v1RegisterPaymentRequest
|
||||
type V1RegisterPaymentRequest struct {
|
||||
|
||||
// ephemeral pubkey
|
||||
EphemeralPubkey string `json:"ephemeralPubkey,omitempty"`
|
||||
|
||||
// inputs
|
||||
Inputs []*V1Input `json:"inputs"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// V1RoundSigningEvent v1 round signing event
|
||||
//
|
||||
// swagger:model v1RoundSigningEvent
|
||||
type V1RoundSigningEvent struct {
|
||||
|
||||
// cosigners pubkeys
|
||||
CosignersPubkeys []string `json:"cosignersPubkeys"`
|
||||
|
||||
// id
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// unsigned tree
|
||||
UnsignedTree *V1Tree `json:"unsignedTree,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 round signing event
|
||||
func (m *V1RoundSigningEvent) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateUnsignedTree(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1RoundSigningEvent) validateUnsignedTree(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.UnsignedTree) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.UnsignedTree != nil {
|
||||
if err := m.UnsignedTree.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("unsignedTree")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("unsignedTree")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this v1 round signing event based on the context it is used
|
||||
func (m *V1RoundSigningEvent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateUnsignedTree(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1RoundSigningEvent) contextValidateUnsignedTree(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.UnsignedTree != nil {
|
||||
|
||||
if swag.IsZero(m.UnsignedTree) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.UnsignedTree.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("unsignedTree")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("unsignedTree")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1RoundSigningEvent) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1RoundSigningEvent) UnmarshalBinary(b []byte) error {
|
||||
var res V1RoundSigningEvent
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1RoundSigningNoncesGeneratedEvent v1 round signing nonces generated event
|
||||
//
|
||||
// swagger:model v1RoundSigningNoncesGeneratedEvent
|
||||
type V1RoundSigningNoncesGeneratedEvent struct {
|
||||
|
||||
// id
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// tree nonces
|
||||
TreeNonces string `json:"treeNonces,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 round signing nonces generated event
|
||||
func (m *V1RoundSigningNoncesGeneratedEvent) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 round signing nonces generated event based on context it is used
|
||||
func (m *V1RoundSigningNoncesGeneratedEvent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1RoundSigningNoncesGeneratedEvent) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1RoundSigningNoncesGeneratedEvent) UnmarshalBinary(b []byte) error {
|
||||
var res V1RoundSigningNoncesGeneratedEvent
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1SendTreeNoncesRequest v1 send tree nonces request
|
||||
//
|
||||
// swagger:model v1SendTreeNoncesRequest
|
||||
type V1SendTreeNoncesRequest struct {
|
||||
|
||||
// public key
|
||||
PublicKey string `json:"publicKey,omitempty"`
|
||||
|
||||
// round Id
|
||||
RoundID string `json:"roundId,omitempty"`
|
||||
|
||||
// tree nonces
|
||||
TreeNonces string `json:"treeNonces,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 send tree nonces request
|
||||
func (m *V1SendTreeNoncesRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 send tree nonces request based on context it is used
|
||||
func (m *V1SendTreeNoncesRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1SendTreeNoncesRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1SendTreeNoncesRequest) UnmarshalBinary(b []byte) error {
|
||||
var res V1SendTreeNoncesRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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
|
||||
|
||||
// V1SendTreeNoncesResponse v1 send tree nonces response
|
||||
//
|
||||
// swagger:model v1SendTreeNoncesResponse
|
||||
type V1SendTreeNoncesResponse interface{}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1SendTreeSignaturesRequest v1 send tree signatures request
|
||||
//
|
||||
// swagger:model v1SendTreeSignaturesRequest
|
||||
type V1SendTreeSignaturesRequest struct {
|
||||
|
||||
// public key
|
||||
PublicKey string `json:"publicKey,omitempty"`
|
||||
|
||||
// round Id
|
||||
RoundID string `json:"roundId,omitempty"`
|
||||
|
||||
// tree signatures
|
||||
TreeSignatures string `json:"treeSignatures,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 send tree signatures request
|
||||
func (m *V1SendTreeSignaturesRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 send tree signatures request based on context it is used
|
||||
func (m *V1SendTreeSignaturesRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1SendTreeSignaturesRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1SendTreeSignaturesRequest) UnmarshalBinary(b []byte) error {
|
||||
var res V1SendTreeSignaturesRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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
|
||||
|
||||
// V1SendTreeSignaturesResponse v1 send tree signatures response
|
||||
//
|
||||
// swagger:model v1SendTreeSignaturesResponse
|
||||
type V1SendTreeSignaturesResponse interface{}
|
||||
@@ -528,7 +528,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
|
||||
})
|
||||
}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(ctx, inputs)
|
||||
paymentID, err := a.client.RegisterPayment(ctx, inputs, "") // ephemeralPublicKey is not required for covenant
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -796,7 +796,7 @@ func (a *covenantArkClient) sendOffchain(
|
||||
}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(
|
||||
ctx, inputs,
|
||||
ctx, inputs, "", // ephemeralPublicKey is not required for covenant
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -262,7 +262,11 @@ func (a *covenantlessArkClient) Onboard(
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := signer.SetKeys(cosigners, aggregatedNonces); err != nil {
|
||||
if err := signer.SetKeys(cosigners); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := signer.SetAggregatedNonces(aggregatedNonces); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -608,7 +612,16 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
})
|
||||
}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(ctx, inputs)
|
||||
roundEphemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(
|
||||
ctx,
|
||||
inputs,
|
||||
hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -618,7 +631,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
}
|
||||
|
||||
poolTxID, err := a.handleRoundStream(
|
||||
ctx, paymentID, selectedCoins, receivers,
|
||||
ctx, paymentID, selectedCoins, receivers, roundEphemeralKey,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -990,8 +1003,13 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
})
|
||||
}
|
||||
|
||||
roundEphemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(
|
||||
ctx, inputs,
|
||||
ctx, inputs, hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -1006,7 +1024,7 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
log.Infof("payment registered with id: %s", paymentID)
|
||||
|
||||
poolTxID, err := a.handleRoundStream(
|
||||
ctx, paymentID, selectedCoins, receiversOutput,
|
||||
ctx, paymentID, selectedCoins, receiversOutput, roundEphemeralKey,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -1144,7 +1162,10 @@ func (a *covenantlessArkClient) addVtxoInput(
|
||||
|
||||
func (a *covenantlessArkClient) handleRoundStream(
|
||||
ctx context.Context,
|
||||
paymentID string, vtxosToSign []client.Vtxo, receivers []client.Output,
|
||||
paymentID string,
|
||||
vtxosToSign []client.Vtxo,
|
||||
receivers []client.Output,
|
||||
roundEphemeralKey *secp256k1.PrivateKey,
|
||||
) (string, error) {
|
||||
eventsCh, err := a.client.GetEventStream(ctx, paymentID)
|
||||
if err != nil {
|
||||
@@ -1158,6 +1179,8 @@ func (a *covenantlessArkClient) handleRoundStream(
|
||||
|
||||
defer pingStop()
|
||||
|
||||
var signerSession bitcointree.SignerSession
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -1172,6 +1195,25 @@ func (a *covenantlessArkClient) handleRoundStream(
|
||||
return event.(client.RoundFinalizedEvent).Txid, nil
|
||||
case client.RoundFailedEvent:
|
||||
return "", fmt.Errorf("round failed: %s", event.(client.RoundFailedEvent).Reason)
|
||||
case client.RoundSigningStartedEvent:
|
||||
pingStop()
|
||||
log.Info("a round signing started")
|
||||
signerSession, err = a.handleRoundSigningStarted(
|
||||
ctx, roundEphemeralKey, event.(client.RoundSigningStartedEvent),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
continue
|
||||
case client.RoundSigningNoncesGeneratedEvent:
|
||||
pingStop()
|
||||
log.Info("round combined nonces generated")
|
||||
if err := a.handleRoundSigningNoncesGenerated(
|
||||
ctx, event.(client.RoundSigningNoncesGeneratedEvent), roundEphemeralKey, signerSession,
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
continue
|
||||
case client.RoundFinalizationEvent:
|
||||
pingStop()
|
||||
log.Info("a round finalization started")
|
||||
@@ -1200,6 +1242,73 @@ func (a *covenantlessArkClient) handleRoundStream(
|
||||
}
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) handleRoundSigningStarted(
|
||||
ctx context.Context, ephemeralKey *secp256k1.PrivateKey, event client.RoundSigningStartedEvent,
|
||||
) (signerSession bitcointree.SignerSession, err error) {
|
||||
sweepClosure := bitcointree.CSVSigClosure{
|
||||
Pubkey: a.AspPubkey,
|
||||
Seconds: uint(a.RoundLifetime),
|
||||
}
|
||||
|
||||
sweepTapLeaf, err := sweepClosure.Leaf()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sweepTapTree := txscript.AssembleTaprootScriptTree(*sweepTapLeaf)
|
||||
root := sweepTapTree.RootNode.TapHash()
|
||||
|
||||
signerSession = bitcointree.NewTreeSignerSession(
|
||||
ephemeralKey, event.UnsignedTree, int64(a.MinRelayFee), root.CloneBytes(),
|
||||
)
|
||||
|
||||
if err = signerSession.SetKeys(event.CosignersPublicKeys); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nonces, err := signerSession.GetNonces()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
myPubKey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed())
|
||||
|
||||
err = a.arkClient.client.SendTreeNonces(ctx, event.ID, myPubKey, nonces)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) handleRoundSigningNoncesGenerated(
|
||||
ctx context.Context,
|
||||
event client.RoundSigningNoncesGeneratedEvent,
|
||||
ephemeralKey *secp256k1.PrivateKey,
|
||||
signerSession bitcointree.SignerSession,
|
||||
) error {
|
||||
if signerSession == nil {
|
||||
return fmt.Errorf("tree signer session not set")
|
||||
}
|
||||
|
||||
if err := signerSession.SetAggregatedNonces(event.Nonces); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sigs, err := signerSession.Sign()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.arkClient.client.SendTreeSignatures(
|
||||
ctx,
|
||||
event.ID,
|
||||
hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed()),
|
||||
sigs,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *covenantlessArkClient) handleRoundFinalization(
|
||||
ctx context.Context, event client.RoundFinalizationEvent,
|
||||
vtxos []client.Vtxo, receivers []client.Output,
|
||||
@@ -1619,7 +1728,16 @@ func (a *covenantlessArkClient) selfTransferAllPendingPayments(
|
||||
|
||||
outputs := []client.Output{myself}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(ctx, inputs)
|
||||
roundEphemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
paymentID, err := a.client.RegisterPayment(
|
||||
ctx,
|
||||
inputs,
|
||||
hex.EncodeToString(roundEphemeralKey.PubKey().SerializeCompressed()),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -1629,7 +1747,7 @@ func (a *covenantlessArkClient) selfTransferAllPendingPayments(
|
||||
}
|
||||
|
||||
roundTxid, err := a.handleRoundStream(
|
||||
ctx, paymentID, pendingVtxos, outputs,
|
||||
ctx, paymentID, pendingVtxos, outputs, roundEphemeralKey,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -20,6 +20,10 @@ import (
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTreeSigningNotRequired = fmt.Errorf("tree signing is not required on this ark (covenant)")
|
||||
)
|
||||
|
||||
type covenantService struct {
|
||||
network common.Network
|
||||
pubkey *secp256k1.PublicKey
|
||||
@@ -40,6 +44,7 @@ type covenantService struct {
|
||||
eventsCh chan domain.RoundEvent
|
||||
onboardingCh chan onboarding
|
||||
|
||||
lastEvent domain.RoundEvent
|
||||
currentRound *domain.Round
|
||||
}
|
||||
|
||||
@@ -66,7 +71,7 @@ func NewCovenantService(
|
||||
network, pubkey,
|
||||
roundLifetime, roundInterval, unilateralExitDelay, minRelayFee,
|
||||
walletSvc, repoManager, builder, scanner, sweeper,
|
||||
paymentRequests, forfeitTxs, eventsCh, onboardingCh, nil,
|
||||
paymentRequests, forfeitTxs, eventsCh, onboardingCh, nil, nil,
|
||||
}
|
||||
repoManager.RegisterEventsHandler(
|
||||
func(round *domain.Round) {
|
||||
@@ -148,17 +153,17 @@ func (s *covenantService) ClaimVtxos(ctx context.Context, creds string, receiver
|
||||
return s.paymentRequests.update(*payment)
|
||||
}
|
||||
|
||||
func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) ([]string, *domain.Round, error) {
|
||||
func (s *covenantService) UpdatePaymentStatus(_ context.Context, id string) (domain.RoundEvent, error) {
|
||||
err := s.paymentRequests.updatePingTimestamp(id)
|
||||
if err != nil {
|
||||
if _, ok := err.(errPaymentNotFound); ok {
|
||||
return s.forfeitTxs.view(), s.currentRound, nil
|
||||
return s.lastEvent, nil
|
||||
}
|
||||
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
return s.lastEvent, nil
|
||||
}
|
||||
|
||||
func (s *covenantService) CompleteAsyncPayment(ctx context.Context, redeemTx string, unconditionalForfeitTxs []string) error {
|
||||
@@ -248,6 +253,24 @@ func (s *covenantService) Onboard(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantService) RegisterCosignerPubkey(ctx context.Context, paymentId string, _ string) error {
|
||||
// if the user sends an ephemeral pubkey, something is going wrong client-side
|
||||
// we should delete the associated payment
|
||||
if err := s.paymentRequests.delete(paymentId); err != nil {
|
||||
log.WithError(err).Warn("failed to delete payment")
|
||||
}
|
||||
|
||||
return ErrTreeSigningNotRequired
|
||||
}
|
||||
|
||||
func (s *covenantService) RegisterCosignerNonces(context.Context, string, *secp256k1.PublicKey, string) error {
|
||||
return ErrTreeSigningNotRequired
|
||||
}
|
||||
|
||||
func (s *covenantService) RegisterCosignerSignatures(context.Context, string, *secp256k1.PublicKey, string) error {
|
||||
return ErrTreeSigningNotRequired
|
||||
}
|
||||
|
||||
func (s *covenantService) start() {
|
||||
s.startRound()
|
||||
}
|
||||
@@ -256,6 +279,7 @@ func (s *covenantService) startRound() {
|
||||
round := domain.NewRound(dustAmount)
|
||||
//nolint:all
|
||||
round.StartRegistration()
|
||||
s.lastEvent = nil
|
||||
s.currentRound = round
|
||||
|
||||
defer func() {
|
||||
@@ -305,7 +329,7 @@ func (s *covenantService) startFinalization() {
|
||||
if num > paymentsThreshold {
|
||||
num = paymentsThreshold
|
||||
}
|
||||
payments := s.paymentRequests.pop(num)
|
||||
payments, _ := 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")
|
||||
@@ -670,14 +694,17 @@ func (s *covenantService) propagateEvents(round *domain.Round) {
|
||||
switch e := lastEvent.(type) {
|
||||
case domain.RoundFinalizationStarted:
|
||||
forfeitTxs := s.forfeitTxs.view()
|
||||
s.eventsCh <- domain.RoundFinalizationStarted{
|
||||
ev := domain.RoundFinalizationStarted{
|
||||
Id: e.Id,
|
||||
CongestionTree: e.CongestionTree,
|
||||
Connectors: e.Connectors,
|
||||
PoolTx: e.PoolTx,
|
||||
UnsignedForfeitTxs: forfeitTxs,
|
||||
}
|
||||
s.lastEvent = ev
|
||||
s.eventsCh <- ev
|
||||
case domain.RoundFinalized, domain.RoundFailed:
|
||||
s.lastEvent = e
|
||||
s.eventsCh <- e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,11 @@ type covenantlessService struct {
|
||||
eventsCh chan domain.RoundEvent
|
||||
onboardingCh chan onboarding
|
||||
|
||||
currentRound *domain.Round
|
||||
|
||||
asyncPaymentsCache map[domain.VtxoKey]struct {
|
||||
// cached data for the current round
|
||||
lastEvent domain.RoundEvent
|
||||
currentRound *domain.Round
|
||||
treeSigningSessions map[string]*musigSigningSession
|
||||
asyncPaymentsCache map[domain.VtxoKey]struct {
|
||||
receivers []domain.Receiver
|
||||
expireAt int64
|
||||
}
|
||||
@@ -89,6 +91,7 @@ func NewCovenantlessService(
|
||||
eventsCh: eventsCh,
|
||||
onboardingCh: onboardingCh,
|
||||
asyncPaymentsCache: asyncPaymentsCache,
|
||||
treeSigningSessions: make(map[string]*musigSigningSession),
|
||||
}
|
||||
|
||||
repoManager.RegisterEventsHandler(
|
||||
@@ -273,17 +276,17 @@ func (s *covenantlessService) ClaimVtxos(ctx context.Context, creds string, rece
|
||||
return s.paymentRequests.update(*payment)
|
||||
}
|
||||
|
||||
func (s *covenantlessService) UpdatePaymentStatus(_ context.Context, id string) ([]string, *domain.Round, error) {
|
||||
func (s *covenantlessService) UpdatePaymentStatus(_ context.Context, id string) (domain.RoundEvent, error) {
|
||||
err := s.paymentRequests.updatePingTimestamp(id)
|
||||
if err != nil {
|
||||
if _, ok := err.(errPaymentNotFound); ok {
|
||||
return s.forfeitTxs.view(), s.currentRound, nil
|
||||
return s.lastEvent, nil
|
||||
}
|
||||
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
return s.lastEvent, nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) SignVtxos(ctx context.Context, forfeitTxs []string) error {
|
||||
@@ -367,6 +370,78 @@ func (s *covenantlessService) Onboard(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) RegisterCosignerPubkey(ctx context.Context, paymentId string, pubkey string) error {
|
||||
pubkeyBytes, err := hex.DecodeString(pubkey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode hex pubkey: %s", err)
|
||||
}
|
||||
|
||||
ephemeralPublicKey, err := secp256k1.ParsePubKey(pubkeyBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse pubkey: %s", err)
|
||||
}
|
||||
|
||||
return s.paymentRequests.pushEphemeralKey(paymentId, ephemeralPublicKey)
|
||||
}
|
||||
|
||||
func (s *covenantlessService) RegisterCosignerNonces(
|
||||
ctx context.Context, roundID string, pubkey *secp256k1.PublicKey, encodedNonces string,
|
||||
) error {
|
||||
session, ok := s.treeSigningSessions[roundID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`signing session not found for round "%s"`, roundID)
|
||||
}
|
||||
|
||||
nonces, err := bitcointree.DecodeNonces(hex.NewDecoder(strings.NewReader(encodedNonces)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode nonces: %s", err)
|
||||
}
|
||||
|
||||
session.lock.Lock()
|
||||
defer session.lock.Unlock()
|
||||
|
||||
if _, ok := session.nonces[pubkey]; ok {
|
||||
return nil // skip if we already have nonces for this pubkey
|
||||
}
|
||||
|
||||
session.nonces[pubkey] = nonces
|
||||
|
||||
if len(session.nonces) == session.nbCosigners-1 { // exclude the ASP
|
||||
session.nonceDoneC <- struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) RegisterCosignerSignatures(
|
||||
ctx context.Context, roundID string, pubkey *secp256k1.PublicKey, encodedSignatures string,
|
||||
) error {
|
||||
session, ok := s.treeSigningSessions[roundID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`signing session not found for round "%s"`, roundID)
|
||||
}
|
||||
|
||||
signatures, err := bitcointree.DecodeSignatures(hex.NewDecoder(strings.NewReader(encodedSignatures)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode signatures: %s", err)
|
||||
}
|
||||
|
||||
session.lock.Lock()
|
||||
defer session.lock.Unlock()
|
||||
|
||||
if _, ok := session.signatures[pubkey]; ok {
|
||||
return nil // skip if we already have signatures for this pubkey
|
||||
}
|
||||
|
||||
session.signatures[pubkey] = signatures
|
||||
|
||||
if len(session.signatures) == session.nbCosigners-1 { // exclude the ASP
|
||||
session.sigDoneC <- struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *covenantlessService) start() {
|
||||
s.startRound()
|
||||
}
|
||||
@@ -375,6 +450,7 @@ func (s *covenantlessService) startRound() {
|
||||
round := domain.NewRound(dustAmount) // TODO dynamic dust amount?
|
||||
//nolint:all
|
||||
round.StartRegistration()
|
||||
s.lastEvent = nil
|
||||
s.currentRound = round
|
||||
|
||||
defer func() {
|
||||
@@ -389,8 +465,12 @@ func (s *covenantlessService) startFinalization() {
|
||||
ctx := context.Background()
|
||||
round := s.currentRound
|
||||
|
||||
roundRemainingDuration := time.Duration(s.roundInterval/2-1) * time.Second
|
||||
thirdOfRemainingDuration := time.Duration(roundRemainingDuration / 3)
|
||||
|
||||
var roundAborted bool
|
||||
defer func() {
|
||||
delete(s.treeSigningSessions, round.Id)
|
||||
if roundAborted {
|
||||
s.startRound()
|
||||
return
|
||||
@@ -404,7 +484,7 @@ func (s *covenantlessService) startFinalization() {
|
||||
s.startRound()
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration((s.roundInterval/2)-1) * time.Second)
|
||||
time.Sleep(thirdOfRemainingDuration)
|
||||
s.finalizeRound()
|
||||
}()
|
||||
|
||||
@@ -424,7 +504,14 @@ func (s *covenantlessService) startFinalization() {
|
||||
if num > paymentsThreshold {
|
||||
num = paymentsThreshold
|
||||
}
|
||||
payments := s.paymentRequests.pop(num)
|
||||
payments, 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))
|
||||
log.WithError(err).Debugf("round %s aborted", round.Id)
|
||||
return
|
||||
}
|
||||
|
||||
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")
|
||||
@@ -438,32 +525,16 @@ func (s *covenantlessService) startFinalization() {
|
||||
return
|
||||
}
|
||||
|
||||
cosigners := make([]*secp256k1.PrivateKey, 0)
|
||||
cosignersPubKeys := make([]*secp256k1.PublicKey, 0, len(cosigners))
|
||||
for range payments {
|
||||
// TODO sender should provide the ephemeral *public* key
|
||||
ephemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to generate ephemeral key: %s", err))
|
||||
log.WithError(err).Warn("failed to generate ephemeral key")
|
||||
return
|
||||
}
|
||||
|
||||
cosigners = append(cosigners, ephemeralKey)
|
||||
cosignersPubKeys = append(cosignersPubKeys, ephemeralKey.PubKey())
|
||||
}
|
||||
|
||||
aspSigningKey, err := secp256k1.GeneratePrivateKey()
|
||||
ephemeralKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to generate asp signing key: %s", err))
|
||||
log.WithError(err).Warn("failed to generate asp signing key")
|
||||
round.Fail(fmt.Errorf("failed to generate ephemeral key: %s", err))
|
||||
log.WithError(err).Warn("failed to generate ephemeral key")
|
||||
return
|
||||
}
|
||||
|
||||
cosigners = append(cosigners, aspSigningKey)
|
||||
cosignersPubKeys = append(cosignersPubKeys, aspSigningKey.PubKey())
|
||||
cosigners = append(cosigners, ephemeralKey.PubKey())
|
||||
|
||||
unsignedPoolTx, tree, connectorAddress, err := s.builder.BuildPoolTx(s.pubkey, payments, s.minRelayFee, sweptRounds, cosignersPubKeys...)
|
||||
unsignedPoolTx, tree, connectorAddress, err := s.builder.BuildPoolTx(s.pubkey, payments, s.minRelayFee, sweptRounds, cosigners...)
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to create pool tx: %s", err))
|
||||
log.WithError(err).Warn("failed to create pool tx")
|
||||
@@ -472,6 +543,16 @@ func (s *covenantlessService) startFinalization() {
|
||||
log.Debugf("pool tx created for round %s", round.Id)
|
||||
|
||||
if len(tree) > 0 {
|
||||
log.Debugf("signing congestion tree for round %s", round.Id)
|
||||
|
||||
signingSession := newMusigSigningSession(len(cosigners))
|
||||
s.treeSigningSessions[round.Id] = signingSession
|
||||
|
||||
log.Debugf("signing session created for round %s", round.Id)
|
||||
|
||||
// send back the unsigned tree & all cosigners pubkeys
|
||||
s.propagateRoundSigningStartedEvent(tree, cosigners)
|
||||
|
||||
sweepClosure := bitcointree.CSVSigClosure{
|
||||
Pubkey: s.pubkey,
|
||||
Seconds: uint(s.roundLifetime),
|
||||
@@ -485,37 +566,50 @@ func (s *covenantlessService) startFinalization() {
|
||||
sweepTapTree := txscript.AssembleTaprootScriptTree(*sweepTapLeaf)
|
||||
root := sweepTapTree.RootNode.TapHash()
|
||||
|
||||
coordinator, err := s.createTreeCoordinatorSession(tree, cosignersPubKeys, root)
|
||||
coordinator, err := s.createTreeCoordinatorSession(tree, cosigners, root)
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to create tree coordinator: %s", err))
|
||||
log.WithError(err).Warn("failed to create tree coordinator")
|
||||
return
|
||||
}
|
||||
|
||||
signers := make([]bitcointree.SignerSession, 0)
|
||||
aspSignerSession := bitcointree.NewTreeSignerSession(
|
||||
ephemeralKey, tree, int64(s.minRelayFee), root.CloneBytes(),
|
||||
)
|
||||
|
||||
for _, seckey := range cosigners {
|
||||
signer := bitcointree.NewTreeSignerSession(
|
||||
seckey, tree, int64(s.minRelayFee), root.CloneBytes(),
|
||||
)
|
||||
|
||||
// TODO nonces should be sent by the sender
|
||||
nonces, err := signer.GetNonces()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to get nonces: %s", err))
|
||||
log.WithError(err).Warn("failed to get nonces")
|
||||
return
|
||||
}
|
||||
|
||||
if err := coordinator.AddNonce(seckey.PubKey(), nonces); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to add nonce: %s", err))
|
||||
log.WithError(err).Warn("failed to add nonce")
|
||||
return
|
||||
}
|
||||
|
||||
signers = append(signers, signer)
|
||||
nonces, err := aspSignerSession.GetNonces()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to get nonces: %s", err))
|
||||
log.WithError(err).Warn("failed to get nonces")
|
||||
return
|
||||
}
|
||||
|
||||
if err := coordinator.AddNonce(ephemeralKey.PubKey(), nonces); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to add nonce: %s", err))
|
||||
log.WithError(err).Warn("failed to add nonce")
|
||||
return
|
||||
}
|
||||
|
||||
noncesTimer := time.NewTimer(thirdOfRemainingDuration)
|
||||
|
||||
select {
|
||||
case <-noncesTimer.C:
|
||||
round.Fail(fmt.Errorf("musig2 signing session timed out (nonce collection)"))
|
||||
log.Warn("musig2 signing session timed out (nonce collection)")
|
||||
return
|
||||
case <-signingSession.nonceDoneC:
|
||||
noncesTimer.Stop()
|
||||
for pubkey, nonce := range signingSession.nonces {
|
||||
if err := coordinator.AddNonce(pubkey, nonce); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to add nonce: %s", err))
|
||||
log.WithError(err).Warn("failed to add nonce")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("nonces collected for round %s", round.Id)
|
||||
|
||||
aggragatedNonces, err := coordinator.AggregateNonces()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to aggregate nonces: %s", err))
|
||||
@@ -523,36 +617,69 @@ func (s *covenantlessService) startFinalization() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO aggragated nonces and public keys should be sent back to signer
|
||||
// TODO signing should be done client-side (except for the ASP)
|
||||
for i, signer := range signers {
|
||||
if err := signer.SetKeys(cosignersPubKeys, aggragatedNonces); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to set keys: %s", err))
|
||||
log.WithError(err).Warn("failed to set keys")
|
||||
return
|
||||
}
|
||||
log.Debugf("nonces aggregated for round %s", round.Id)
|
||||
|
||||
sig, err := signer.Sign()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to sign: %s", err))
|
||||
log.WithError(err).Warn("failed to sign")
|
||||
return
|
||||
}
|
||||
s.propagateRoundSigningNoncesGeneratedEvent(aggragatedNonces)
|
||||
|
||||
if err := coordinator.AddSig(cosignersPubKeys[i], sig); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to add sig: %s", err))
|
||||
log.WithError(err).Warn("failed to add sig")
|
||||
return
|
||||
}
|
||||
if err := aspSignerSession.SetKeys(cosigners); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to set keys: %s", err))
|
||||
log.WithError(err).Warn("failed to set keys")
|
||||
return
|
||||
}
|
||||
|
||||
signedTree, err := coordinator.SignTree()
|
||||
if err := aspSignerSession.SetAggregatedNonces(aggragatedNonces); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to set aggregated nonces: %s", err))
|
||||
log.WithError(err).Warn("failed to set aggregated nonces")
|
||||
return
|
||||
}
|
||||
|
||||
// sign the tree as ASP
|
||||
aspTreeSigs, err := aspSignerSession.Sign()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to sign tree: %s", err))
|
||||
log.WithError(err).Warn("failed to sign tree")
|
||||
return
|
||||
}
|
||||
|
||||
if err := coordinator.AddSig(ephemeralKey.PubKey(), aspTreeSigs); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to add signature: %s", err))
|
||||
log.WithError(err).Warn("failed to add signature")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("ASP tree signed for round %s", round.Id)
|
||||
|
||||
signaturesTimer := time.NewTimer(thirdOfRemainingDuration)
|
||||
|
||||
log.Debugf("waiting for cosigners to sign the tree")
|
||||
|
||||
select {
|
||||
case <-signaturesTimer.C:
|
||||
round.Fail(fmt.Errorf("musig2 signing session timed out (signatures)"))
|
||||
log.Warn("musig2 signing session timed out (signatures)")
|
||||
return
|
||||
case <-signingSession.sigDoneC:
|
||||
signaturesTimer.Stop()
|
||||
for pubkey, sig := range signingSession.signatures {
|
||||
if err := coordinator.AddSig(pubkey, sig); err != nil {
|
||||
round.Fail(fmt.Errorf("failed to add signature: %s", err))
|
||||
log.WithError(err).Warn("failed to add signature")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("signatures collected for round %s", round.Id)
|
||||
|
||||
signedTree, err := coordinator.SignTree()
|
||||
if err != nil {
|
||||
round.Fail(fmt.Errorf("failed to aggragate tree signatures: %s", err))
|
||||
log.WithError(err).Warn("failed aggragate tree signatures")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("congestion tree signed for round %s", round.Id)
|
||||
|
||||
tree = signedTree
|
||||
}
|
||||
|
||||
@@ -578,6 +705,29 @@ func (s *covenantlessService) startFinalization() {
|
||||
log.Debugf("started finalization stage for round: %s", round.Id)
|
||||
}
|
||||
|
||||
func (s *covenantlessService) propagateRoundSigningStartedEvent(
|
||||
unsignedCongestionTree tree.CongestionTree, cosigners []*secp256k1.PublicKey,
|
||||
) {
|
||||
ev := RoundSigningStarted{
|
||||
Id: s.currentRound.Id,
|
||||
UnsignedVtxoTree: unsignedCongestionTree,
|
||||
Cosigners: cosigners,
|
||||
}
|
||||
|
||||
s.lastEvent = ev
|
||||
s.eventsCh <- ev
|
||||
}
|
||||
|
||||
func (s *covenantlessService) propagateRoundSigningNoncesGeneratedEvent(combinedNonces bitcointree.TreeNonces) {
|
||||
ev := RoundSigningNoncesGenerated{
|
||||
Id: s.currentRound.Id,
|
||||
Nonces: combinedNonces,
|
||||
}
|
||||
|
||||
s.lastEvent = ev
|
||||
s.eventsCh <- ev
|
||||
}
|
||||
|
||||
func (s *covenantlessService) createTreeCoordinatorSession(
|
||||
congestionTree tree.CongestionTree, cosigners []*secp256k1.PublicKey, root chainhash.Hash,
|
||||
) (bitcointree.CoordinatorSession, error) {
|
||||
@@ -900,14 +1050,17 @@ func (s *covenantlessService) propagateEvents(round *domain.Round) {
|
||||
switch e := lastEvent.(type) {
|
||||
case domain.RoundFinalizationStarted:
|
||||
forfeitTxs := s.forfeitTxs.view()
|
||||
s.eventsCh <- domain.RoundFinalizationStarted{
|
||||
ev := domain.RoundFinalizationStarted{
|
||||
Id: e.Id,
|
||||
CongestionTree: e.CongestionTree,
|
||||
Connectors: e.Connectors,
|
||||
PoolTx: e.PoolTx,
|
||||
UnsignedForfeitTxs: forfeitTxs,
|
||||
}
|
||||
s.lastEvent = ev
|
||||
s.eventsCh <- ev
|
||||
case domain.RoundFinalized, domain.RoundFailed:
|
||||
s.lastEvent = e
|
||||
s.eventsCh <- e
|
||||
}
|
||||
}
|
||||
@@ -1118,3 +1271,26 @@ func findForfeitTxBitcoin(
|
||||
|
||||
return "", fmt.Errorf("forfeit tx not found")
|
||||
}
|
||||
|
||||
// musigSigningSession holds the state of ephemeral nonces and signatures in order to coordinate the signing of the tree
|
||||
type musigSigningSession struct {
|
||||
lock sync.Mutex
|
||||
nbCosigners int
|
||||
nonces map[*secp256k1.PublicKey]bitcointree.TreeNonces
|
||||
nonceDoneC chan struct{}
|
||||
|
||||
signatures map[*secp256k1.PublicKey]bitcointree.TreePartialSigs
|
||||
sigDoneC chan struct{}
|
||||
}
|
||||
|
||||
func newMusigSigningSession(nbCosigners int) *musigSigningSession {
|
||||
return &musigSigningSession{
|
||||
nonces: make(map[*secp256k1.PublicKey]bitcointree.TreeNonces),
|
||||
nonceDoneC: make(chan struct{}),
|
||||
|
||||
signatures: make(map[*secp256k1.PublicKey]bitcointree.TreePartialSigs),
|
||||
sigDoneC: make(chan struct{}),
|
||||
lock: sync.Mutex{},
|
||||
nbCosigners: nbCosigners,
|
||||
}
|
||||
}
|
||||
|
||||
43
server/internal/core/application/covenantless_event.go
Normal file
43
server/internal/core/application/covenantless_event.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This package contains intermediary events that are used only by the covenantless version
|
||||
* they let to sign the congestion tree using musig2 algorithm
|
||||
* they are not included in domain because they don't mutate the Round state and should not be persisted
|
||||
*/
|
||||
package application
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
// signer should react to this event by generating a musig2 nonce for each transaction in the tree
|
||||
type RoundSigningStarted struct {
|
||||
Id string
|
||||
UnsignedVtxoTree tree.CongestionTree
|
||||
Cosigners []*secp256k1.PublicKey
|
||||
}
|
||||
|
||||
// signer should react to this event by partially signing the vtxo tree transactions
|
||||
// then, delete its ephemeral key
|
||||
type RoundSigningNoncesGenerated struct {
|
||||
Id string
|
||||
Nonces bitcointree.TreeNonces // aggregated nonces
|
||||
}
|
||||
|
||||
func (e RoundSigningNoncesGenerated) SerializeNonces() (string, error) {
|
||||
var serialized bytes.Buffer
|
||||
|
||||
if err := e.Nonces.Encode(&serialized); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(serialized.Bytes()), nil
|
||||
}
|
||||
|
||||
// implement domain.RoundEvent interface
|
||||
func (r RoundSigningStarted) IsEvent() {}
|
||||
func (r RoundSigningNoncesGenerated) IsEvent() {}
|
||||
@@ -149,7 +149,6 @@ func (s *sweeper) createTask(
|
||||
for _, input := range inputs {
|
||||
// sweepableVtxos related to the sweep input
|
||||
sweepableVtxos := make([]domain.VtxoKey, 0)
|
||||
fmt.Println("input", input.GetHash().String(), input.GetIndex())
|
||||
|
||||
// check if input is the vtxo itself
|
||||
vtxos, _ := s.repoManager.Vtxos().GetVtxos(
|
||||
|
||||
@@ -25,7 +25,7 @@ type Service interface {
|
||||
GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent
|
||||
UpdatePaymentStatus(
|
||||
ctx context.Context, paymentId string,
|
||||
) (unsignedForfeitTxs []string, currentRound *domain.Round, err error)
|
||||
) (lastEvent domain.RoundEvent, err error)
|
||||
ListVtxos(
|
||||
ctx context.Context, pubkey *secp256k1.PublicKey,
|
||||
) (spendableVtxos, spentVtxos []domain.Vtxo, err error)
|
||||
@@ -41,6 +41,16 @@ type Service interface {
|
||||
CompleteAsyncPayment(
|
||||
ctx context.Context, redeemTx string, unconditionalForfeitTxs []string,
|
||||
) error
|
||||
// Tree signing methods
|
||||
RegisterCosignerPubkey(ctx context.Context, paymentId string, ephemeralPublicKey string) error
|
||||
RegisterCosignerNonces(
|
||||
ctx context.Context, roundID string,
|
||||
pubkey *secp256k1.PublicKey, nonces string,
|
||||
) error
|
||||
RegisterCosignerSignatures(
|
||||
ctx context.Context, roundID string,
|
||||
pubkey *secp256k1.PublicKey, signatures string,
|
||||
) error
|
||||
}
|
||||
|
||||
type ServiceInfo struct {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"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"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -20,8 +21,9 @@ type timedPayment struct {
|
||||
}
|
||||
|
||||
type paymentsMap struct {
|
||||
lock *sync.RWMutex
|
||||
payments map[string]*timedPayment
|
||||
lock *sync.RWMutex
|
||||
payments map[string]*timedPayment
|
||||
ephemeralKeys map[string]*secp256k1.PublicKey
|
||||
}
|
||||
|
||||
func newPaymentsMap(payments []domain.Payment) *paymentsMap {
|
||||
@@ -30,7 +32,7 @@ func newPaymentsMap(payments []domain.Payment) *paymentsMap {
|
||||
paymentsById[p.Id] = &timedPayment{p, time.Now(), time.Time{}}
|
||||
}
|
||||
lock := &sync.RWMutex{}
|
||||
return &paymentsMap{lock, paymentsById}
|
||||
return &paymentsMap{lock, paymentsById, make(map[string]*secp256k1.PublicKey)}
|
||||
}
|
||||
|
||||
func (m *paymentsMap) len() int64 {
|
||||
@@ -46,6 +48,18 @@ func (m *paymentsMap) len() int64 {
|
||||
return count
|
||||
}
|
||||
|
||||
func (m *paymentsMap) delete(id string) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
if _, ok := m.payments[id]; !ok {
|
||||
return errPaymentNotFound{id}
|
||||
}
|
||||
|
||||
delete(m.payments, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) push(payment domain.Payment) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
@@ -58,7 +72,19 @@ func (m *paymentsMap) push(payment domain.Payment) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) pop(num int64) []domain.Payment {
|
||||
func (m *paymentsMap) pushEphemeralKey(paymentId string, pubkey *secp256k1.PublicKey) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
if _, ok := m.payments[paymentId]; !ok {
|
||||
return fmt.Errorf("payment %s not found, cannot register signing ephemeral public key", paymentId)
|
||||
}
|
||||
|
||||
m.ephemeralKeys[paymentId] = pubkey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) pop(num int64) ([]domain.Payment, []*secp256k1.PublicKey) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
@@ -83,11 +109,16 @@ func (m *paymentsMap) pop(num int64) []domain.Payment {
|
||||
}
|
||||
|
||||
payments := make([]domain.Payment, 0, num)
|
||||
cosigners := make([]*secp256k1.PublicKey, 0, num)
|
||||
for _, p := range paymentsByTime[:num] {
|
||||
payments = append(payments, p.Payment)
|
||||
if pubkey, ok := m.ephemeralKeys[p.Payment.Id]; ok {
|
||||
cosigners = append(cosigners, pubkey)
|
||||
delete(m.ephemeralKeys, p.Payment.Id)
|
||||
}
|
||||
delete(m.payments, p.Id)
|
||||
}
|
||||
return payments
|
||||
return payments, cosigners
|
||||
}
|
||||
|
||||
func (m *paymentsMap) update(payment domain.Payment) error {
|
||||
|
||||
@@ -3,14 +3,14 @@ package domain
|
||||
import "github.com/ark-network/ark/common/tree"
|
||||
|
||||
type RoundEvent interface {
|
||||
isEvent()
|
||||
IsEvent()
|
||||
}
|
||||
|
||||
func (r RoundStarted) isEvent() {}
|
||||
func (r RoundFinalizationStarted) isEvent() {}
|
||||
func (r RoundFinalized) isEvent() {}
|
||||
func (r RoundFailed) isEvent() {}
|
||||
func (r PaymentsRegistered) isEvent() {}
|
||||
func (r RoundStarted) IsEvent() {}
|
||||
func (r RoundFinalizationStarted) IsEvent() {}
|
||||
func (r RoundFinalized) IsEvent() {}
|
||||
func (r RoundFailed) IsEvent() {}
|
||||
func (r PaymentsRegistered) IsEvent() {}
|
||||
|
||||
type RoundStarted struct {
|
||||
Id string
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/ark-network/ark/server/internal/core/domain"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -118,25 +119,77 @@ func (h *handler) Ping(ctx context.Context, req *arkv1.PingRequest) (*arkv1.Ping
|
||||
return nil, status.Error(codes.InvalidArgument, "missing payment id")
|
||||
}
|
||||
|
||||
forfeits, round, err := h.svc.UpdatePaymentStatus(ctx, req.GetPaymentId())
|
||||
lastEvent, err := h.svc.UpdatePaymentStatus(ctx, req.GetPaymentId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var event *arkv1.RoundFinalizationEvent
|
||||
if round != nil {
|
||||
event = &arkv1.RoundFinalizationEvent{
|
||||
Id: round.Id,
|
||||
PoolTx: round.UnsignedTx,
|
||||
ForfeitTxs: forfeits,
|
||||
CongestionTree: castCongestionTree(round.CongestionTree),
|
||||
Connectors: round.Connectors,
|
||||
var resp *arkv1.PingResponse
|
||||
|
||||
switch e := lastEvent.(type) {
|
||||
case domain.RoundFinalizationStarted:
|
||||
resp = &arkv1.PingResponse{
|
||||
Event: &arkv1.PingResponse_RoundFinalization{
|
||||
RoundFinalization: &arkv1.RoundFinalizationEvent{
|
||||
Id: e.Id,
|
||||
PoolTx: e.PoolTx,
|
||||
CongestionTree: castCongestionTree(e.CongestionTree),
|
||||
ForfeitTxs: e.UnsignedForfeitTxs,
|
||||
Connectors: e.Connectors,
|
||||
},
|
||||
},
|
||||
}
|
||||
case domain.RoundFinalized:
|
||||
resp = &arkv1.PingResponse{
|
||||
Event: &arkv1.PingResponse_RoundFinalized{
|
||||
RoundFinalized: &arkv1.RoundFinalizedEvent{
|
||||
Id: e.Id,
|
||||
PoolTxid: e.Txid,
|
||||
},
|
||||
},
|
||||
}
|
||||
case domain.RoundFailed:
|
||||
resp = &arkv1.PingResponse{
|
||||
Event: &arkv1.PingResponse_RoundFailed{
|
||||
RoundFailed: &arkv1.RoundFailed{
|
||||
Id: e.Id,
|
||||
Reason: e.Err,
|
||||
},
|
||||
},
|
||||
}
|
||||
case application.RoundSigningStarted:
|
||||
cosignersKeys := make([]string, 0, len(e.Cosigners))
|
||||
for _, key := range e.Cosigners {
|
||||
cosignersKeys = append(cosignersKeys, hex.EncodeToString(key.SerializeCompressed()))
|
||||
}
|
||||
|
||||
resp = &arkv1.PingResponse{
|
||||
Event: &arkv1.PingResponse_RoundSigning{
|
||||
RoundSigning: &arkv1.RoundSigningEvent{
|
||||
Id: e.Id,
|
||||
CosignersPubkeys: cosignersKeys,
|
||||
UnsignedTree: castCongestionTree(e.UnsignedVtxoTree),
|
||||
},
|
||||
},
|
||||
}
|
||||
case application.RoundSigningNoncesGenerated:
|
||||
serialized, err := e.SerializeNonces()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to serialize nonces")
|
||||
return nil, status.Error(codes.Internal, "failed to serialize nonces")
|
||||
}
|
||||
|
||||
resp = &arkv1.PingResponse{
|
||||
Event: &arkv1.PingResponse_RoundSigningNoncesGenerated{
|
||||
RoundSigningNoncesGenerated: &arkv1.RoundSigningNoncesGeneratedEvent{
|
||||
Id: e.Id,
|
||||
TreeNonces: serialized,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return &arkv1.PingResponse{
|
||||
ForfeitTxs: forfeits,
|
||||
Event: event,
|
||||
}, nil
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (h *handler) RegisterPayment(ctx context.Context, req *arkv1.RegisterPaymentRequest) (*arkv1.RegisterPaymentResponse, error) {
|
||||
@@ -150,6 +203,13 @@ func (h *handler) RegisterPayment(ctx context.Context, req *arkv1.RegisterPaymen
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubkey := req.GetEphemeralPubkey()
|
||||
if len(pubkey) > 0 {
|
||||
if err := h.svc.RegisterCosignerPubkey(ctx, id, pubkey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &arkv1.RegisterPaymentResponse{
|
||||
Id: id,
|
||||
}, nil
|
||||
@@ -267,14 +327,6 @@ func (h *handler) GetEventStream(_ *arkv1.GetEventStreamRequest, stream arkv1.Ar
|
||||
if err := stream.Send(ev); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch ev.Event.(type) {
|
||||
case *arkv1.GetEventStreamResponse_RoundFinalized, *arkv1.GetEventStreamResponse_RoundFailed:
|
||||
if err := stream.Send(ev); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,6 +364,74 @@ func (h *handler) GetInfo(ctx context.Context, req *arkv1.GetInfoRequest) (*arkv
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) SendTreeNonces(ctx context.Context, req *arkv1.SendTreeNoncesRequest) (*arkv1.SendTreeNoncesResponse, error) {
|
||||
pubkey := req.GetPublicKey()
|
||||
encodedNonces := req.GetTreeNonces()
|
||||
roundID := req.GetRoundId()
|
||||
|
||||
if len(pubkey) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing cosigner public key")
|
||||
}
|
||||
|
||||
if len(encodedNonces) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing tree nonces")
|
||||
}
|
||||
|
||||
if len(roundID) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing round id")
|
||||
}
|
||||
|
||||
pubkeyBytes, err := hex.DecodeString(pubkey)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid cosigner public key")
|
||||
}
|
||||
|
||||
cosignerPublicKey, err := secp256k1.ParsePubKey(pubkeyBytes)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid cosigner public key")
|
||||
}
|
||||
|
||||
if err := h.svc.RegisterCosignerNonces(ctx, roundID, cosignerPublicKey, encodedNonces); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.SendTreeNoncesResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) SendTreeSignatures(ctx context.Context, req *arkv1.SendTreeSignaturesRequest) (*arkv1.SendTreeSignaturesResponse, error) {
|
||||
roundID := req.GetRoundId()
|
||||
pubkey := req.GetPublicKey()
|
||||
encodedSignatures := req.GetTreeSignatures()
|
||||
|
||||
if len(pubkey) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing cosigner public key")
|
||||
}
|
||||
|
||||
if len(encodedSignatures) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing tree signatures")
|
||||
}
|
||||
|
||||
if len(roundID) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing round id")
|
||||
}
|
||||
|
||||
pubkeyBytes, err := hex.DecodeString(pubkey)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid cosigner public key")
|
||||
}
|
||||
|
||||
cosignerPublicKey, err := secp256k1.ParsePubKey(pubkeyBytes)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid cosigner public key")
|
||||
}
|
||||
|
||||
if err := h.svc.RegisterCosignerSignatures(ctx, roundID, cosignerPublicKey, encodedSignatures); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.SendTreeSignaturesResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) pushListener(l *listener) {
|
||||
h.listenersLock.Lock()
|
||||
defer h.listenersLock.Unlock()
|
||||
@@ -368,6 +488,36 @@ func (h *handler) listenToEvents() {
|
||||
},
|
||||
},
|
||||
}
|
||||
case application.RoundSigningStarted:
|
||||
cosignersKeys := make([]string, 0, len(e.Cosigners))
|
||||
for _, key := range e.Cosigners {
|
||||
cosignersKeys = append(cosignersKeys, hex.EncodeToString(key.SerializeCompressed()))
|
||||
}
|
||||
|
||||
ev = &arkv1.GetEventStreamResponse{
|
||||
Event: &arkv1.GetEventStreamResponse_RoundSigning{
|
||||
RoundSigning: &arkv1.RoundSigningEvent{
|
||||
Id: e.Id,
|
||||
CosignersPubkeys: cosignersKeys,
|
||||
UnsignedTree: castCongestionTree(e.UnsignedVtxoTree),
|
||||
},
|
||||
},
|
||||
}
|
||||
case application.RoundSigningNoncesGenerated:
|
||||
serialized, err := e.SerializeNonces()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to serialize nonces")
|
||||
continue
|
||||
}
|
||||
|
||||
ev = &arkv1.GetEventStreamResponse{
|
||||
Event: &arkv1.GetEventStreamResponse_RoundSigningNoncesGenerated{
|
||||
RoundSigningNoncesGenerated: &arkv1.RoundSigningNoncesGeneratedEvent{
|
||||
Id: e.Id,
|
||||
TreeNonces: serialized,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ev != nil {
|
||||
|
||||
@@ -161,6 +161,14 @@ func Whitelist() map[string][]bakery.Op {
|
||||
Entity: EntityHealth,
|
||||
Action: "read",
|
||||
}},
|
||||
fmt.Sprintf("/%s/SendTreeNonces", arkv1.ArkService_ServiceDesc.ServiceName): {{
|
||||
Entity: EntityArk,
|
||||
Action: "write",
|
||||
}},
|
||||
fmt.Sprintf("/%s/SendTreeSignatures", arkv1.ArkService_ServiceDesc.ServiceName): {{
|
||||
Entity: EntityArk,
|
||||
Action: "write",
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user