mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
multi: implement MuSig2 RPCs and remote signing
This commit is contained in:
parent
8fc99fba00
commit
f7275c7fc4
@ -10,6 +10,11 @@ import (
|
||||
// Signer implementations such as hardware wallets, hardware tokens, HSM's, or
|
||||
// simply a regular wallet.
|
||||
type Signer interface {
|
||||
// MuSig2Signer is an embedded interface to make sure all our signers
|
||||
// also support MuSig2 signing, so we can forward calls to a remote
|
||||
// signer as well.
|
||||
MuSig2Signer
|
||||
|
||||
// SignOutputRaw generates a signature for the passed transaction
|
||||
// according to the data within the passed SignDescriptor.
|
||||
//
|
||||
|
@ -2,16 +2,20 @@ package input
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -130,6 +134,50 @@ func (m *MockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
// MuSig2CreateSession creates a new MuSig2 signing session using the local
|
||||
// key identified by the key locator. The complete list of all public keys of
|
||||
// all signing parties must be provided, including the public key of the local
|
||||
// signing key. If nonces of other parties are already known, they can be
|
||||
// submitted as well to reduce the number of method calls necessary later on.
|
||||
func (m *MockSigner) MuSig2CreateSession(keychain.KeyLocator,
|
||||
[]*btcec.PublicKey, *MuSig2Tweaks,
|
||||
[][musig2.PubNonceSize]byte) (*MuSig2SessionInfo, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2RegisterNonces registers one or more public nonces of other signing
|
||||
// participants for a session identified by its ID. This method returns true
|
||||
// once we have all nonces for all other signing participants.
|
||||
func (m *MockSigner) MuSig2RegisterNonces(MuSig2SessionID,
|
||||
[][musig2.PubNonceSize]byte) (bool, error) {
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// MuSig2Sign creates a partial signature using the local signing key
|
||||
// that was specified when the session was created. This can only be
|
||||
// called when all public nonces of all participants are known and have
|
||||
// been registered with the session. If this node isn't responsible for
|
||||
// combining all the partial signatures, then the cleanup parameter
|
||||
// should be set, indicating that the session can be removed from memory
|
||||
// once the signature was produced.
|
||||
func (m *MockSigner) MuSig2Sign(MuSig2SessionID,
|
||||
[sha256.Size]byte, bool) (*musig2.PartialSignature, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineSig combines the given partial signature(s) with the
|
||||
// local one, if it already exists. Once a partial signature of all
|
||||
// participants is registered, the final signature will be combined and
|
||||
// returned.
|
||||
func (m *MockSigner) MuSig2CombineSig(MuSig2SessionID,
|
||||
[]*musig2.PartialSignature) (*schnorr.Signature, bool, error) {
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// findKey searches through all stored private keys and returns one
|
||||
// corresponding to the hashed pubkey if it can be found. The public key may
|
||||
// either correspond directly to the private key or to the private key with a
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -201,6 +201,176 @@ func local_request_Signer_DeriveSharedKey_0(ctx context.Context, marshaler runti
|
||||
|
||||
}
|
||||
|
||||
func request_Signer_MuSig2CombineKeys_0(ctx context.Context, marshaler runtime.Marshaler, client SignerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2CombineKeysRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.MuSig2CombineKeys(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Signer_MuSig2CombineKeys_0(ctx context.Context, marshaler runtime.Marshaler, server SignerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2CombineKeysRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.MuSig2CombineKeys(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Signer_MuSig2CreateSession_0(ctx context.Context, marshaler runtime.Marshaler, client SignerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2SessionRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.MuSig2CreateSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Signer_MuSig2CreateSession_0(ctx context.Context, marshaler runtime.Marshaler, server SignerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2SessionRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.MuSig2CreateSession(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Signer_MuSig2RegisterNonces_0(ctx context.Context, marshaler runtime.Marshaler, client SignerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2RegisterNoncesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.MuSig2RegisterNonces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Signer_MuSig2RegisterNonces_0(ctx context.Context, marshaler runtime.Marshaler, server SignerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2RegisterNoncesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.MuSig2RegisterNonces(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Signer_MuSig2Sign_0(ctx context.Context, marshaler runtime.Marshaler, client SignerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2SignRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.MuSig2Sign(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Signer_MuSig2Sign_0(ctx context.Context, marshaler runtime.Marshaler, server SignerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2SignRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.MuSig2Sign(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Signer_MuSig2CombineSig_0(ctx context.Context, marshaler runtime.Marshaler, client SignerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2CombineSigRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.MuSig2CombineSig(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Signer_MuSig2CombineSig_0(ctx context.Context, marshaler runtime.Marshaler, server SignerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MuSig2CombineSigRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.MuSig2CombineSig(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterSignerHandlerServer registers the http handlers for service Signer to "mux".
|
||||
// UnaryRPC :call SignerServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
@ -322,6 +492,121 @@ func RegisterSignerHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2CombineKeys_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)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/signrpc.Signer/MuSig2CombineKeys", runtime.WithHTTPPathPattern("/v2/signer/musig2/combinekeys"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Signer_MuSig2CombineKeys_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2CombineKeys_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2CreateSession_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)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/signrpc.Signer/MuSig2CreateSession", runtime.WithHTTPPathPattern("/v2/signer/musig2/createsession"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Signer_MuSig2CreateSession_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2CreateSession_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2RegisterNonces_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)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/signrpc.Signer/MuSig2RegisterNonces", runtime.WithHTTPPathPattern("/v2/signer/musig2/registernonces"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Signer_MuSig2RegisterNonces_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2RegisterNonces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2Sign_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)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/signrpc.Signer/MuSig2Sign", runtime.WithHTTPPathPattern("/v2/signer/musig2/sign"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Signer_MuSig2Sign_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2Sign_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2CombineSig_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)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/signrpc.Signer/MuSig2CombineSig", runtime.WithHTTPPathPattern("/v2/signer/musig2/combinesig"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Signer_MuSig2CombineSig_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2CombineSig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -463,6 +748,106 @@ func RegisterSignerHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2CombineKeys_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)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/signrpc.Signer/MuSig2CombineKeys", runtime.WithHTTPPathPattern("/v2/signer/musig2/combinekeys"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Signer_MuSig2CombineKeys_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2CombineKeys_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2CreateSession_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)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/signrpc.Signer/MuSig2CreateSession", runtime.WithHTTPPathPattern("/v2/signer/musig2/createsession"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Signer_MuSig2CreateSession_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2CreateSession_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2RegisterNonces_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)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/signrpc.Signer/MuSig2RegisterNonces", runtime.WithHTTPPathPattern("/v2/signer/musig2/registernonces"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Signer_MuSig2RegisterNonces_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2RegisterNonces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2Sign_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)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/signrpc.Signer/MuSig2Sign", runtime.WithHTTPPathPattern("/v2/signer/musig2/sign"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Signer_MuSig2Sign_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2Sign_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Signer_MuSig2CombineSig_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)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/signrpc.Signer/MuSig2CombineSig", runtime.WithHTTPPathPattern("/v2/signer/musig2/combinesig"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Signer_MuSig2CombineSig_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Signer_MuSig2CombineSig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -476,6 +861,16 @@ var (
|
||||
pattern_Signer_VerifyMessage_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "signer", "verifymessage"}, ""))
|
||||
|
||||
pattern_Signer_DeriveSharedKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "signer", "sharedkey"}, ""))
|
||||
|
||||
pattern_Signer_MuSig2CombineKeys_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "signer", "musig2", "combinekeys"}, ""))
|
||||
|
||||
pattern_Signer_MuSig2CreateSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "signer", "musig2", "createsession"}, ""))
|
||||
|
||||
pattern_Signer_MuSig2RegisterNonces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "signer", "musig2", "registernonces"}, ""))
|
||||
|
||||
pattern_Signer_MuSig2Sign_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "signer", "musig2", "sign"}, ""))
|
||||
|
||||
pattern_Signer_MuSig2CombineSig_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "signer", "musig2", "combinesig"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@ -488,4 +883,14 @@ var (
|
||||
forward_Signer_VerifyMessage_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Signer_DeriveSharedKey_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Signer_MuSig2CombineKeys_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Signer_MuSig2CreateSession_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Signer_MuSig2RegisterNonces_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Signer_MuSig2Sign_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Signer_MuSig2CombineSig_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
@ -147,4 +147,129 @@ func RegisterSignerJSONCallbacks(registry map[string]func(ctx context.Context,
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["signrpc.Signer.MuSig2CombineKeys"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &MuSig2CombineKeysRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewSignerClient(conn)
|
||||
resp, err := client.MuSig2CombineKeys(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["signrpc.Signer.MuSig2CreateSession"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &MuSig2SessionRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewSignerClient(conn)
|
||||
resp, err := client.MuSig2CreateSession(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["signrpc.Signer.MuSig2RegisterNonces"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &MuSig2RegisterNoncesRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewSignerClient(conn)
|
||||
resp, err := client.MuSig2RegisterNonces(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["signrpc.Signer.MuSig2Sign"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &MuSig2SignRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewSignerClient(conn)
|
||||
resp, err := client.MuSig2Sign(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["signrpc.Signer.MuSig2CombineSig"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &MuSig2CombineSigRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewSignerClient(conn)
|
||||
resp, err := client.MuSig2CombineSig(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,76 @@ service Signer {
|
||||
hashed with sha256, resulting in the final key length of 256bit.
|
||||
*/
|
||||
rpc DeriveSharedKey (SharedKeyRequest) returns (SharedKeyResponse);
|
||||
|
||||
/*
|
||||
MuSig2CombineKeys (experimental!) is a stateless helper RPC that can be used
|
||||
to calculate the combined MuSig2 public key from a list of all participating
|
||||
signers' public keys. This RPC is completely stateless and deterministic and
|
||||
does not create any signing session. It can be used to determine the Taproot
|
||||
public key that should be put in an on-chain output once all public keys are
|
||||
known. A signing session is only needed later when that output should be
|
||||
_spent_ again.
|
||||
|
||||
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
releases. Backward compatibility is not guaranteed!
|
||||
*/
|
||||
rpc MuSig2CombineKeys (MuSig2CombineKeysRequest)
|
||||
returns (MuSig2CombineKeysResponse);
|
||||
|
||||
/*
|
||||
MuSig2CreateSession (experimental!) creates a new MuSig2 signing session
|
||||
using the local key identified by the key locator. The complete list of all
|
||||
public keys of all signing parties must be provided, including the public
|
||||
key of the local signing key. If nonces of other parties are already known,
|
||||
they can be submitted as well to reduce the number of RPC calls necessary
|
||||
later on.
|
||||
|
||||
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
releases. Backward compatibility is not guaranteed!
|
||||
*/
|
||||
rpc MuSig2CreateSession (MuSig2SessionRequest)
|
||||
returns (MuSig2SessionResponse);
|
||||
|
||||
/*
|
||||
MuSig2RegisterNonces (experimental!) registers one or more public nonces of
|
||||
other signing participants for a session identified by its ID. This RPC can
|
||||
be called multiple times until all nonces are registered.
|
||||
|
||||
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
releases. Backward compatibility is not guaranteed!
|
||||
*/
|
||||
rpc MuSig2RegisterNonces (MuSig2RegisterNoncesRequest)
|
||||
returns (MuSig2RegisterNoncesResponse);
|
||||
|
||||
/*
|
||||
MuSig2Sign (experimental!) creates a partial signature using the local
|
||||
signing key that was specified when the session was created. This can only
|
||||
be called when all public nonces of all participants are known and have been
|
||||
registered with the session. If this node isn't responsible for combining
|
||||
all the partial signatures, then the cleanup flag should be set, indicating
|
||||
that the session can be removed from memory once the signature was produced.
|
||||
|
||||
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
releases. Backward compatibility is not guaranteed!
|
||||
*/
|
||||
rpc MuSig2Sign (MuSig2SignRequest) returns (MuSig2SignResponse);
|
||||
|
||||
/*
|
||||
MuSig2CombineSig (experimental!) combines the given partial signature(s)
|
||||
with the local one, if it already exists. Once a partial signature of all
|
||||
participants is registered, the final signature will be combined and
|
||||
returned.
|
||||
|
||||
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
releases. Backward compatibility is not guaranteed!
|
||||
*/
|
||||
rpc MuSig2CombineSig (MuSig2CombineSigRequest)
|
||||
returns (MuSig2CombineSigResponse);
|
||||
}
|
||||
|
||||
message KeyLocator {
|
||||
@ -265,3 +335,221 @@ message SharedKeyResponse {
|
||||
// The shared public key, hashed with sha256.
|
||||
bytes shared_key = 1;
|
||||
}
|
||||
|
||||
message TweakDesc {
|
||||
/*
|
||||
Tweak is the 32-byte value that will modify the public key.
|
||||
*/
|
||||
bytes tweak = 1;
|
||||
|
||||
/*
|
||||
Specifies if the target key should be converted to an x-only public key
|
||||
before tweaking. If true, then the public key will be mapped to an x-only
|
||||
key before the tweaking operation is applied.
|
||||
*/
|
||||
bool is_x_only = 2;
|
||||
}
|
||||
|
||||
message TaprootTweakDesc {
|
||||
/*
|
||||
The root hash of the tapscript tree if a script path is committed to. If
|
||||
the MuSig2 key put on chain doesn't also commit to a script path (BIP-0086
|
||||
key spend only), then this needs to be empty and the key_spend_only field
|
||||
below must be set to true. This is required because gRPC cannot
|
||||
differentiate between a zero-size byte slice and a nil byte slice (both
|
||||
would be serialized the same way). So the extra boolean is required.
|
||||
*/
|
||||
bytes script_root = 1;
|
||||
|
||||
/*
|
||||
Indicates that the above script_root is expected to be empty because this
|
||||
is a BIP-0086 key spend only commitment where only the internal key is
|
||||
committed to instead of also including a script root hash.
|
||||
*/
|
||||
bool key_spend_only = 2;
|
||||
}
|
||||
|
||||
message MuSig2CombineKeysRequest {
|
||||
/*
|
||||
A list of all public keys (serialized in 32-byte x-only format!)
|
||||
participating in the signing session. The list will always be sorted
|
||||
lexicographically internally. This must include the local key which is
|
||||
described by the above key_loc.
|
||||
*/
|
||||
repeated bytes all_signer_pubkeys = 1;
|
||||
|
||||
/*
|
||||
A series of optional generic tweaks to be applied to the the aggregated
|
||||
public key.
|
||||
*/
|
||||
repeated TweakDesc tweaks = 2;
|
||||
|
||||
/*
|
||||
An optional taproot specific tweak that must be specified if the MuSig2
|
||||
combined key will be used as the main taproot key of a taproot output
|
||||
on-chain.
|
||||
*/
|
||||
TaprootTweakDesc taproot_tweak = 3;
|
||||
}
|
||||
|
||||
message MuSig2CombineKeysResponse {
|
||||
/*
|
||||
The combined public key (in the 32-byte x-only format) with all tweaks
|
||||
applied to it. If a taproot tweak is specified, this corresponds to the
|
||||
taproot key that can be put into the on-chain output.
|
||||
*/
|
||||
bytes combined_key = 1;
|
||||
|
||||
/*
|
||||
The raw combined public key (in the 32-byte x-only format) before any tweaks
|
||||
are applied to it. If a taproot tweak is specified, this corresponds to the
|
||||
internal key that needs to be put into the witness if the script spend path
|
||||
is used.
|
||||
*/
|
||||
bytes taproot_internal_key = 2;
|
||||
}
|
||||
|
||||
message MuSig2SessionRequest {
|
||||
/*
|
||||
The key locator that identifies which key to use for signing.
|
||||
*/
|
||||
KeyLocator key_loc = 1;
|
||||
|
||||
/*
|
||||
A list of all public keys (serialized in 32-byte x-only format!)
|
||||
participating in the signing session. The list will always be sorted
|
||||
lexicographically internally. This must include the local key which is
|
||||
described by the above key_loc.
|
||||
*/
|
||||
repeated bytes all_signer_pubkeys = 2;
|
||||
|
||||
/*
|
||||
An optional list of all public nonces of other signing participants that
|
||||
might already be known.
|
||||
*/
|
||||
repeated bytes other_signer_public_nonces = 3;
|
||||
|
||||
/*
|
||||
A series of optional generic tweaks to be applied to the the aggregated
|
||||
public key.
|
||||
*/
|
||||
repeated TweakDesc tweaks = 4;
|
||||
|
||||
/*
|
||||
An optional taproot specific tweak that must be specified if the MuSig2
|
||||
combined key will be used as the main taproot key of a taproot output
|
||||
on-chain.
|
||||
*/
|
||||
TaprootTweakDesc taproot_tweak = 5;
|
||||
}
|
||||
|
||||
message MuSig2SessionResponse {
|
||||
/*
|
||||
The unique ID that represents this signing session. A session can be used
|
||||
for producing a signature a single time. If the signing fails for any
|
||||
reason, a new session with the same participants needs to be created.
|
||||
*/
|
||||
bytes session_id = 1;
|
||||
|
||||
/*
|
||||
The combined public key (in the 32-byte x-only format) with all tweaks
|
||||
applied to it. If a taproot tweak is specified, this corresponds to the
|
||||
taproot key that can be put into the on-chain output.
|
||||
*/
|
||||
bytes combined_key = 2;
|
||||
|
||||
/*
|
||||
The raw combined public key (in the 32-byte x-only format) before any tweaks
|
||||
are applied to it. If a taproot tweak is specified, this corresponds to the
|
||||
internal key that needs to be put into the witness if the script spend path
|
||||
is used.
|
||||
*/
|
||||
bytes taproot_internal_key = 3;
|
||||
|
||||
/*
|
||||
The two public nonces the local signer uses, combined into a single value
|
||||
of 66 bytes. Can be split into the two 33-byte points to get the individual
|
||||
nonces.
|
||||
*/
|
||||
bytes local_public_nonces = 4;
|
||||
|
||||
/*
|
||||
Indicates whether all nonces required to start the signing process are known
|
||||
now.
|
||||
*/
|
||||
bool have_all_nonces = 5;
|
||||
}
|
||||
|
||||
message MuSig2RegisterNoncesRequest {
|
||||
/*
|
||||
The unique ID of the signing session those nonces should be registered with.
|
||||
*/
|
||||
bytes session_id = 1;
|
||||
|
||||
/*
|
||||
A list of all public nonces of other signing participants that should be
|
||||
registered.
|
||||
*/
|
||||
repeated bytes other_signer_public_nonces = 3;
|
||||
}
|
||||
|
||||
message MuSig2RegisterNoncesResponse {
|
||||
/*
|
||||
Indicates whether all nonces required to start the signing process are known
|
||||
now.
|
||||
*/
|
||||
bool have_all_nonces = 1;
|
||||
}
|
||||
|
||||
message MuSig2SignRequest {
|
||||
/*
|
||||
The unique ID of the signing session to use for signing.
|
||||
*/
|
||||
bytes session_id = 1;
|
||||
|
||||
/*
|
||||
The 32-byte SHA256 digest of the message to sign.
|
||||
*/
|
||||
bytes message_digest = 2;
|
||||
|
||||
/*
|
||||
Cleanup indicates that after signing, the session state can be cleaned up,
|
||||
since another participant is going to be responsible for combining the
|
||||
partial signatures.
|
||||
*/
|
||||
bool cleanup = 3;
|
||||
}
|
||||
|
||||
message MuSig2SignResponse {
|
||||
/*
|
||||
The partial signature created by the local signer.
|
||||
*/
|
||||
bytes local_partial_signature = 1;
|
||||
}
|
||||
|
||||
message MuSig2CombineSigRequest {
|
||||
/*
|
||||
The unique ID of the signing session to combine the signatures for.
|
||||
*/
|
||||
bytes session_id = 1;
|
||||
|
||||
/*
|
||||
The list of all other participants' partial signatures to add to the current
|
||||
session.
|
||||
*/
|
||||
repeated bytes other_partial_signatures = 2;
|
||||
}
|
||||
|
||||
message MuSig2CombineSigResponse {
|
||||
/*
|
||||
Indicates whether all partial signatures required to create a final, full
|
||||
signature are known yet. If this is true, then the final_signature field is
|
||||
set, otherwise it is empty.
|
||||
*/
|
||||
bool have_all_signatures = 1;
|
||||
|
||||
/*
|
||||
The final, full signature that is valid for the combined public key.
|
||||
*/
|
||||
bytes final_signature = 2;
|
||||
}
|
@ -50,6 +50,176 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/signer/musig2/combinekeys": {
|
||||
"post": {
|
||||
"summary": "MuSig2CombineKeys (experimental!) is a stateless helper RPC that can be used\nto calculate the combined MuSig2 public key from a list of all participating\nsigners' public keys. This RPC is completely stateless and deterministic and\ndoes not create any signing session. It can be used to determine the Taproot\npublic key that should be put in an on-chain output once all public keys are\nknown. A signing session is only needed later when that output should be\n_spent_ again.",
|
||||
"description": "NOTE: The MuSig2 BIP is not final yet and therefore this API must be\nconsidered to be HIGHLY EXPERIMENTAL and subject to change in upcoming\nreleases. Backward compatibility is not guaranteed!",
|
||||
"operationId": "Signer_MuSig2CombineKeys",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2CombineKeysResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2CombineKeysRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Signer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/signer/musig2/combinesig": {
|
||||
"post": {
|
||||
"summary": "MuSig2CombineSig (experimental!) combines the given partial signature(s)\nwith the local one, if it already exists. Once a partial signature of all\nparticipants is registered, the final signature will be combined and\nreturned.",
|
||||
"description": "NOTE: The MuSig2 BIP is not final yet and therefore this API must be\nconsidered to be HIGHLY EXPERIMENTAL and subject to change in upcoming\nreleases. Backward compatibility is not guaranteed!",
|
||||
"operationId": "Signer_MuSig2CombineSig",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2CombineSigResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2CombineSigRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Signer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/signer/musig2/createsession": {
|
||||
"post": {
|
||||
"summary": "MuSig2CreateSession (experimental!) creates a new MuSig2 signing session\nusing the local key identified by the key locator. The complete list of all\npublic keys of all signing parties must be provided, including the public\nkey of the local signing key. If nonces of other parties are already known,\nthey can be submitted as well to reduce the number of RPC calls necessary\nlater on.",
|
||||
"description": "NOTE: The MuSig2 BIP is not final yet and therefore this API must be\nconsidered to be HIGHLY EXPERIMENTAL and subject to change in upcoming\nreleases. Backward compatibility is not guaranteed!",
|
||||
"operationId": "Signer_MuSig2CreateSession",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2SessionResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2SessionRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Signer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/signer/musig2/registernonces": {
|
||||
"post": {
|
||||
"summary": "MuSig2RegisterNonces (experimental!) registers one or more public nonces of\nother signing participants for a session identified by its ID. This RPC can\nbe called multiple times until all nonces are registered.",
|
||||
"description": "NOTE: The MuSig2 BIP is not final yet and therefore this API must be\nconsidered to be HIGHLY EXPERIMENTAL and subject to change in upcoming\nreleases. Backward compatibility is not guaranteed!",
|
||||
"operationId": "Signer_MuSig2RegisterNonces",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2RegisterNoncesResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2RegisterNoncesRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Signer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/signer/musig2/sign": {
|
||||
"post": {
|
||||
"summary": "MuSig2Sign (experimental!) creates a partial signature using the local\nsigning key that was specified when the session was created. This can only\nbe called when all public nonces of all participants are known and have been\nregistered with the session. If this node isn't responsible for combining\nall the partial signatures, then the cleanup flag should be set, indicating\nthat the session can be removed from memory once the signature was produced.",
|
||||
"description": "NOTE: The MuSig2 BIP is not final yet and therefore this API must be\nconsidered to be HIGHLY EXPERIMENTAL and subject to change in upcoming\nreleases. Backward compatibility is not guaranteed!",
|
||||
"operationId": "Signer_MuSig2Sign",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2SignResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/signrpcMuSig2SignRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Signer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/signer/sharedkey": {
|
||||
"post": {
|
||||
"summary": "DeriveSharedKey returns a shared secret key by performing Diffie-Hellman key\nderivation between the ephemeral public key in the request and the node's\nkey specified in the key_desc parameter. Either a key locator or a raw\npublic key is expected in the key_desc, if neither is supplied, defaults to\nthe node's identity private key:\nP_shared = privKeyNode * ephemeralPubkey\nThe resulting shared public key is serialized in the compressed format and\nhashed with sha256, resulting in the final key length of 256bit.",
|
||||
@ -276,6 +446,198 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2CombineKeysRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all_signer_pubkeys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "A list of all public keys (serialized in 32-byte x-only format!)\nparticipating in the signing session. The list will always be sorted\nlexicographically internally. This must include the local key which is\ndescribed by the above key_loc."
|
||||
},
|
||||
"tweaks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/signrpcTweakDesc"
|
||||
},
|
||||
"description": "A series of optional generic tweaks to be applied to the the aggregated\npublic key."
|
||||
},
|
||||
"taproot_tweak": {
|
||||
"$ref": "#/definitions/signrpcTaprootTweakDesc",
|
||||
"description": "An optional taproot specific tweak that must be specified if the MuSig2\ncombined key will be used as the main taproot key of a taproot output\non-chain."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2CombineKeysResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"combined_key": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The combined public key (in the 32-byte x-only format) with all tweaks\napplied to it. If a taproot tweak is specified, this corresponds to the\ntaproot key that can be put into the on-chain output."
|
||||
},
|
||||
"taproot_internal_key": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The raw combined public key (in the 32-byte x-only format) before any tweaks\nare applied to it. If a taproot tweak is specified, this corresponds to the\ninternal key that needs to be put into the witness if the script spend path\nis used."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2CombineSigRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session_id": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The unique ID of the signing session to combine the signatures for."
|
||||
},
|
||||
"other_partial_signatures": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "The list of all other participants' partial signatures to add to the current\nsession."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2CombineSigResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"have_all_signatures": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates whether all partial signatures required to create a final, full\nsignature are known yet. If this is true, then the final_signature field is\nset, otherwise it is empty."
|
||||
},
|
||||
"final_signature": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The final, full signature that is valid for the combined public key."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2RegisterNoncesRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session_id": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The unique ID of the signing session those nonces should be registered with."
|
||||
},
|
||||
"other_signer_public_nonces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "A list of all public nonces of other signing participants that should be\nregistered."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2RegisterNoncesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"have_all_nonces": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates whether all nonces required to start the signing process are known\nnow."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2SessionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key_loc": {
|
||||
"$ref": "#/definitions/signrpcKeyLocator",
|
||||
"description": "The key locator that identifies which key to use for signing."
|
||||
},
|
||||
"all_signer_pubkeys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "A list of all public keys (serialized in 32-byte x-only format!)\nparticipating in the signing session. The list will always be sorted\nlexicographically internally. This must include the local key which is\ndescribed by the above key_loc."
|
||||
},
|
||||
"other_signer_public_nonces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "An optional list of all public nonces of other signing participants that\nmight already be known."
|
||||
},
|
||||
"tweaks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/signrpcTweakDesc"
|
||||
},
|
||||
"description": "A series of optional generic tweaks to be applied to the the aggregated\npublic key."
|
||||
},
|
||||
"taproot_tweak": {
|
||||
"$ref": "#/definitions/signrpcTaprootTweakDesc",
|
||||
"description": "An optional taproot specific tweak that must be specified if the MuSig2\ncombined key will be used as the main taproot key of a taproot output\non-chain."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2SessionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session_id": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The unique ID that represents this signing session. A session can be used\nfor producing a signature a single time. If the signing fails for any\nreason, a new session with the same participants needs to be created."
|
||||
},
|
||||
"combined_key": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The combined public key (in the 32-byte x-only format) with all tweaks\napplied to it. If a taproot tweak is specified, this corresponds to the\ntaproot key that can be put into the on-chain output."
|
||||
},
|
||||
"taproot_internal_key": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The raw combined public key (in the 32-byte x-only format) before any tweaks\nare applied to it. If a taproot tweak is specified, this corresponds to the\ninternal key that needs to be put into the witness if the script spend path\nis used."
|
||||
},
|
||||
"local_public_nonces": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The two public nonces the local signer uses, combined into a single value\nof 66 bytes. Can be split into the two 33-byte points to get the individual\nnonces."
|
||||
},
|
||||
"have_all_nonces": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates whether all nonces required to start the signing process are known\nnow."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2SignRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session_id": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The unique ID of the signing session to use for signing."
|
||||
},
|
||||
"message_digest": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The 32-byte SHA256 digest of the message to sign."
|
||||
},
|
||||
"cleanup": {
|
||||
"type": "boolean",
|
||||
"description": "Cleanup indicates that after signing, the session state can be cleaned up,\nsince another participant is going to be responsible for combining the\npartial signatures."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcMuSig2SignResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"local_partial_signature": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The partial signature created by the local signer."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcSharedKeyRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -416,6 +778,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcTaprootTweakDesc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"script_root": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "The root hash of the tapscript tree if a script path is committed to. If\nthe MuSig2 key put on chain doesn't also commit to a script path (BIP-0086\nkey spend only), then this needs to be empty and the key_spend_only field\nbelow must be set to true. This is required because gRPC cannot\ndifferentiate between a zero-size byte slice and a nil byte slice (both\nwould be serialized the same way). So the extra boolean is required."
|
||||
},
|
||||
"key_spend_only": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates that the above script_root is expected to be empty because this\nis a BIP-0086 key spend only commitment where only the internal key is\ncommitted to instead of also including a script root hash."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcTweakDesc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tweak": {
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "Tweak is the 32-byte value that will modify the public key."
|
||||
},
|
||||
"is_x_only": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies if the target key should be converted to an x-only public key\nbefore tweaking. If true, then the public key will be mapped to an x-only\nkey before the tweaking operation is applied."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signrpcTxOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -18,3 +18,18 @@ http:
|
||||
- selector: signrpc.Signer.DeriveSharedKey
|
||||
post: "/v2/signer/sharedkey"
|
||||
body: "*"
|
||||
- selector: signrpc.Signer.MuSig2CombineKeys
|
||||
post: "/v2/signer/musig2/combinekeys"
|
||||
body: "*"
|
||||
- selector: signrpc.Signer.MuSig2CreateSession
|
||||
post: "/v2/signer/musig2/createsession"
|
||||
body: "*"
|
||||
- selector: signrpc.Signer.MuSig2RegisterNonces
|
||||
post: "/v2/signer/musig2/registernonces"
|
||||
body: "*"
|
||||
- selector: signrpc.Signer.MuSig2Sign
|
||||
post: "/v2/signer/musig2/sign"
|
||||
body: "*"
|
||||
- selector: signrpc.Signer.MuSig2CombineSig
|
||||
post: "/v2/signer/musig2/combinesig"
|
||||
body: "*"
|
||||
|
@ -64,6 +64,62 @@ type SignerClient interface {
|
||||
//The resulting shared public key is serialized in the compressed format and
|
||||
//hashed with sha256, resulting in the final key length of 256bit.
|
||||
DeriveSharedKey(ctx context.Context, in *SharedKeyRequest, opts ...grpc.CallOption) (*SharedKeyResponse, error)
|
||||
//
|
||||
//MuSig2CombineKeys (experimental!) is a stateless helper RPC that can be used
|
||||
//to calculate the combined MuSig2 public key from a list of all participating
|
||||
//signers' public keys. This RPC is completely stateless and deterministic and
|
||||
//does not create any signing session. It can be used to determine the Taproot
|
||||
//public key that should be put in an on-chain output once all public keys are
|
||||
//known. A signing session is only needed later when that output should be
|
||||
//_spent_ again.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2CombineKeys(ctx context.Context, in *MuSig2CombineKeysRequest, opts ...grpc.CallOption) (*MuSig2CombineKeysResponse, error)
|
||||
//
|
||||
//MuSig2CreateSession (experimental!) creates a new MuSig2 signing session
|
||||
//using the local key identified by the key locator. The complete list of all
|
||||
//public keys of all signing parties must be provided, including the public
|
||||
//key of the local signing key. If nonces of other parties are already known,
|
||||
//they can be submitted as well to reduce the number of RPC calls necessary
|
||||
//later on.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2CreateSession(ctx context.Context, in *MuSig2SessionRequest, opts ...grpc.CallOption) (*MuSig2SessionResponse, error)
|
||||
//
|
||||
//MuSig2RegisterNonces (experimental!) registers one or more public nonces of
|
||||
//other signing participants for a session identified by its ID. This RPC can
|
||||
//be called multiple times until all nonces are registered.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2RegisterNonces(ctx context.Context, in *MuSig2RegisterNoncesRequest, opts ...grpc.CallOption) (*MuSig2RegisterNoncesResponse, error)
|
||||
//
|
||||
//MuSig2Sign (experimental!) creates a partial signature using the local
|
||||
//signing key that was specified when the session was created. This can only
|
||||
//be called when all public nonces of all participants are known and have been
|
||||
//registered with the session. If this node isn't responsible for combining
|
||||
//all the partial signatures, then the cleanup flag should be set, indicating
|
||||
//that the session can be removed from memory once the signature was produced.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2Sign(ctx context.Context, in *MuSig2SignRequest, opts ...grpc.CallOption) (*MuSig2SignResponse, error)
|
||||
//
|
||||
//MuSig2CombineSig (experimental!) combines the given partial signature(s)
|
||||
//with the local one, if it already exists. Once a partial signature of all
|
||||
//participants is registered, the final signature will be combined and
|
||||
//returned.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2CombineSig(ctx context.Context, in *MuSig2CombineSigRequest, opts ...grpc.CallOption) (*MuSig2CombineSigResponse, error)
|
||||
}
|
||||
|
||||
type signerClient struct {
|
||||
@ -119,6 +175,51 @@ func (c *signerClient) DeriveSharedKey(ctx context.Context, in *SharedKeyRequest
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *signerClient) MuSig2CombineKeys(ctx context.Context, in *MuSig2CombineKeysRequest, opts ...grpc.CallOption) (*MuSig2CombineKeysResponse, error) {
|
||||
out := new(MuSig2CombineKeysResponse)
|
||||
err := c.cc.Invoke(ctx, "/signrpc.Signer/MuSig2CombineKeys", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *signerClient) MuSig2CreateSession(ctx context.Context, in *MuSig2SessionRequest, opts ...grpc.CallOption) (*MuSig2SessionResponse, error) {
|
||||
out := new(MuSig2SessionResponse)
|
||||
err := c.cc.Invoke(ctx, "/signrpc.Signer/MuSig2CreateSession", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *signerClient) MuSig2RegisterNonces(ctx context.Context, in *MuSig2RegisterNoncesRequest, opts ...grpc.CallOption) (*MuSig2RegisterNoncesResponse, error) {
|
||||
out := new(MuSig2RegisterNoncesResponse)
|
||||
err := c.cc.Invoke(ctx, "/signrpc.Signer/MuSig2RegisterNonces", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *signerClient) MuSig2Sign(ctx context.Context, in *MuSig2SignRequest, opts ...grpc.CallOption) (*MuSig2SignResponse, error) {
|
||||
out := new(MuSig2SignResponse)
|
||||
err := c.cc.Invoke(ctx, "/signrpc.Signer/MuSig2Sign", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *signerClient) MuSig2CombineSig(ctx context.Context, in *MuSig2CombineSigRequest, opts ...grpc.CallOption) (*MuSig2CombineSigResponse, error) {
|
||||
out := new(MuSig2CombineSigResponse)
|
||||
err := c.cc.Invoke(ctx, "/signrpc.Signer/MuSig2CombineSig", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SignerServer is the server API for Signer service.
|
||||
// All implementations must embed UnimplementedSignerServer
|
||||
// for forward compatibility
|
||||
@ -169,6 +270,62 @@ type SignerServer interface {
|
||||
//The resulting shared public key is serialized in the compressed format and
|
||||
//hashed with sha256, resulting in the final key length of 256bit.
|
||||
DeriveSharedKey(context.Context, *SharedKeyRequest) (*SharedKeyResponse, error)
|
||||
//
|
||||
//MuSig2CombineKeys (experimental!) is a stateless helper RPC that can be used
|
||||
//to calculate the combined MuSig2 public key from a list of all participating
|
||||
//signers' public keys. This RPC is completely stateless and deterministic and
|
||||
//does not create any signing session. It can be used to determine the Taproot
|
||||
//public key that should be put in an on-chain output once all public keys are
|
||||
//known. A signing session is only needed later when that output should be
|
||||
//_spent_ again.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2CombineKeys(context.Context, *MuSig2CombineKeysRequest) (*MuSig2CombineKeysResponse, error)
|
||||
//
|
||||
//MuSig2CreateSession (experimental!) creates a new MuSig2 signing session
|
||||
//using the local key identified by the key locator. The complete list of all
|
||||
//public keys of all signing parties must be provided, including the public
|
||||
//key of the local signing key. If nonces of other parties are already known,
|
||||
//they can be submitted as well to reduce the number of RPC calls necessary
|
||||
//later on.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2CreateSession(context.Context, *MuSig2SessionRequest) (*MuSig2SessionResponse, error)
|
||||
//
|
||||
//MuSig2RegisterNonces (experimental!) registers one or more public nonces of
|
||||
//other signing participants for a session identified by its ID. This RPC can
|
||||
//be called multiple times until all nonces are registered.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2RegisterNonces(context.Context, *MuSig2RegisterNoncesRequest) (*MuSig2RegisterNoncesResponse, error)
|
||||
//
|
||||
//MuSig2Sign (experimental!) creates a partial signature using the local
|
||||
//signing key that was specified when the session was created. This can only
|
||||
//be called when all public nonces of all participants are known and have been
|
||||
//registered with the session. If this node isn't responsible for combining
|
||||
//all the partial signatures, then the cleanup flag should be set, indicating
|
||||
//that the session can be removed from memory once the signature was produced.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2Sign(context.Context, *MuSig2SignRequest) (*MuSig2SignResponse, error)
|
||||
//
|
||||
//MuSig2CombineSig (experimental!) combines the given partial signature(s)
|
||||
//with the local one, if it already exists. Once a partial signature of all
|
||||
//participants is registered, the final signature will be combined and
|
||||
//returned.
|
||||
//
|
||||
//NOTE: The MuSig2 BIP is not final yet and therefore this API must be
|
||||
//considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
|
||||
//releases. Backward compatibility is not guaranteed!
|
||||
MuSig2CombineSig(context.Context, *MuSig2CombineSigRequest) (*MuSig2CombineSigResponse, error)
|
||||
mustEmbedUnimplementedSignerServer()
|
||||
}
|
||||
|
||||
@ -191,6 +348,21 @@ func (UnimplementedSignerServer) VerifyMessage(context.Context, *VerifyMessageRe
|
||||
func (UnimplementedSignerServer) DeriveSharedKey(context.Context, *SharedKeyRequest) (*SharedKeyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeriveSharedKey not implemented")
|
||||
}
|
||||
func (UnimplementedSignerServer) MuSig2CombineKeys(context.Context, *MuSig2CombineKeysRequest) (*MuSig2CombineKeysResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MuSig2CombineKeys not implemented")
|
||||
}
|
||||
func (UnimplementedSignerServer) MuSig2CreateSession(context.Context, *MuSig2SessionRequest) (*MuSig2SessionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MuSig2CreateSession not implemented")
|
||||
}
|
||||
func (UnimplementedSignerServer) MuSig2RegisterNonces(context.Context, *MuSig2RegisterNoncesRequest) (*MuSig2RegisterNoncesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MuSig2RegisterNonces not implemented")
|
||||
}
|
||||
func (UnimplementedSignerServer) MuSig2Sign(context.Context, *MuSig2SignRequest) (*MuSig2SignResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MuSig2Sign not implemented")
|
||||
}
|
||||
func (UnimplementedSignerServer) MuSig2CombineSig(context.Context, *MuSig2CombineSigRequest) (*MuSig2CombineSigResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method MuSig2CombineSig not implemented")
|
||||
}
|
||||
func (UnimplementedSignerServer) mustEmbedUnimplementedSignerServer() {}
|
||||
|
||||
// UnsafeSignerServer may be embedded to opt out of forward compatibility for this service.
|
||||
@ -294,6 +466,96 @@ func _Signer_DeriveSharedKey_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Signer_MuSig2CombineKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MuSig2CombineKeysRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SignerServer).MuSig2CombineKeys(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/signrpc.Signer/MuSig2CombineKeys",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SignerServer).MuSig2CombineKeys(ctx, req.(*MuSig2CombineKeysRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Signer_MuSig2CreateSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MuSig2SessionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SignerServer).MuSig2CreateSession(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/signrpc.Signer/MuSig2CreateSession",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SignerServer).MuSig2CreateSession(ctx, req.(*MuSig2SessionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Signer_MuSig2RegisterNonces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MuSig2RegisterNoncesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SignerServer).MuSig2RegisterNonces(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/signrpc.Signer/MuSig2RegisterNonces",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SignerServer).MuSig2RegisterNonces(ctx, req.(*MuSig2RegisterNoncesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Signer_MuSig2Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MuSig2SignRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SignerServer).MuSig2Sign(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/signrpc.Signer/MuSig2Sign",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SignerServer).MuSig2Sign(ctx, req.(*MuSig2SignRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Signer_MuSig2CombineSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MuSig2CombineSigRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SignerServer).MuSig2CombineSig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/signrpc.Signer/MuSig2CombineSig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SignerServer).MuSig2CombineSig(ctx, req.(*MuSig2CombineSigRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Signer_ServiceDesc is the grpc.ServiceDesc for Signer service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -321,6 +583,26 @@ var Signer_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "DeriveSharedKey",
|
||||
Handler: _Signer_DeriveSharedKey_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "MuSig2CombineKeys",
|
||||
Handler: _Signer_MuSig2CombineKeys_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "MuSig2CreateSession",
|
||||
Handler: _Signer_MuSig2CreateSession_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "MuSig2RegisterNonces",
|
||||
Handler: _Signer_MuSig2RegisterNonces_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "MuSig2Sign",
|
||||
Handler: _Signer_MuSig2Sign_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "MuSig2CombineSig",
|
||||
Handler: _Signer_MuSig2CombineSig_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "signrpc/signer.proto",
|
||||
|
@ -6,12 +6,15 @@ package signrpc
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
@ -69,6 +72,26 @@ var (
|
||||
Entity: "signer",
|
||||
Action: "generate",
|
||||
}},
|
||||
"/signrpc.Signer/MuSig2CombineKeys": {{
|
||||
Entity: "signer",
|
||||
Action: "read",
|
||||
}},
|
||||
"/signrpc.Signer/MuSig2CreateSession": {{
|
||||
Entity: "signer",
|
||||
Action: "generate",
|
||||
}},
|
||||
"/signrpc.Signer/MuSig2RegisterNonces": {{
|
||||
Entity: "signer",
|
||||
Action: "generate",
|
||||
}},
|
||||
"/signrpc.Signer/MuSig2Sign": {{
|
||||
Entity: "signer",
|
||||
Action: "generate",
|
||||
}},
|
||||
"/signrpc.Signer/MuSig2CombineSig": {{
|
||||
Entity: "signer",
|
||||
Action: "generate",
|
||||
}},
|
||||
}
|
||||
|
||||
// DefaultSignerMacFilename is the default name of the signer macaroon
|
||||
@ -673,6 +696,250 @@ func (s *Server) DeriveSharedKey(_ context.Context, in *SharedKeyRequest) (
|
||||
return &SharedKeyResponse{SharedKey: sharedKeyHash[:]}, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineKeys combines the given set of public keys into a single
|
||||
// combined MuSig2 combined public key, applying the given tweaks.
|
||||
func (s *Server) MuSig2CombineKeys(_ context.Context,
|
||||
in *MuSig2CombineKeysRequest) (*MuSig2CombineKeysResponse, error) {
|
||||
|
||||
// Parse the public keys of all signing participants. This must also
|
||||
// include our own, local key.
|
||||
allSignerPubKeys := make([]*btcec.PublicKey, len(in.AllSignerPubkeys))
|
||||
if len(in.AllSignerPubkeys) < 2 {
|
||||
return nil, fmt.Errorf("need at least two signing public keys")
|
||||
}
|
||||
|
||||
for idx, pubKeyBytes := range in.AllSignerPubkeys {
|
||||
pubKey, err := schnorr.ParsePubKey(pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing signer public "+
|
||||
"key %d: %v", idx, err)
|
||||
}
|
||||
allSignerPubKeys[idx] = pubKey
|
||||
}
|
||||
|
||||
// Are there any tweaks to apply to the combined public key?
|
||||
tweaks, err := UnmarshalTweaks(in.Tweaks, in.TaprootTweak)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling tweak options: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
// Combine the keys now without creating a session in memory.
|
||||
combinedKey, err := input.MuSig2CombineKeys(allSignerPubKeys, tweaks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error combining keys: %v", err)
|
||||
}
|
||||
|
||||
var internalKeyBytes []byte
|
||||
if combinedKey.PreTweakedKey != nil {
|
||||
internalKeyBytes = schnorr.SerializePubKey(
|
||||
combinedKey.PreTweakedKey,
|
||||
)
|
||||
}
|
||||
|
||||
return &MuSig2CombineKeysResponse{
|
||||
CombinedKey: schnorr.SerializePubKey(
|
||||
combinedKey.FinalKey,
|
||||
),
|
||||
TaprootInternalKey: internalKeyBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MuSig2CreateSession creates a new MuSig2 signing session using the local
|
||||
// key identified by the key locator. The complete list of all public keys of
|
||||
// all signing parties must be provided, including the public key of the local
|
||||
// signing key. If nonces of other parties are already known, they can be
|
||||
// submitted as well to reduce the number of RPC calls necessary later on.
|
||||
func (s *Server) MuSig2CreateSession(_ context.Context,
|
||||
in *MuSig2SessionRequest) (*MuSig2SessionResponse, error) {
|
||||
|
||||
// A key locator is always mandatory.
|
||||
if in.KeyLoc == nil {
|
||||
return nil, fmt.Errorf("missing key_loc")
|
||||
}
|
||||
keyLoc := keychain.KeyLocator{
|
||||
Family: keychain.KeyFamily(in.KeyLoc.KeyFamily),
|
||||
Index: uint32(in.KeyLoc.KeyIndex),
|
||||
}
|
||||
|
||||
// Parse the public keys of all signing participants. This must also
|
||||
// include our own, local key.
|
||||
allSignerPubKeys := make([]*btcec.PublicKey, len(in.AllSignerPubkeys))
|
||||
if len(in.AllSignerPubkeys) < 2 {
|
||||
return nil, fmt.Errorf("need at least two signing public keys")
|
||||
}
|
||||
|
||||
for idx, pubKeyBytes := range in.AllSignerPubkeys {
|
||||
pubKey, err := schnorr.ParsePubKey(pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing signer public "+
|
||||
"key %d: %v", idx, err)
|
||||
}
|
||||
allSignerPubKeys[idx] = pubKey
|
||||
}
|
||||
|
||||
// We participate a nonce ourselves, so we can't have more nonces than
|
||||
// the total number of participants minus ourselves.
|
||||
maxNonces := len(in.AllSignerPubkeys) - 1
|
||||
if len(in.OtherSignerPublicNonces) > maxNonces {
|
||||
return nil, fmt.Errorf("too many other signer public nonces, "+
|
||||
"got %d but expected a maximum of %d",
|
||||
len(in.OtherSignerPublicNonces), maxNonces)
|
||||
}
|
||||
|
||||
// Parse all other nonces we might already know.
|
||||
otherSignerNonces, err := parseMuSig2PublicNonces(
|
||||
in.OtherSignerPublicNonces, true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing other nonces: %v", err)
|
||||
}
|
||||
|
||||
// Are there any tweaks to apply to the combined public key?
|
||||
tweaks, err := UnmarshalTweaks(in.Tweaks, in.TaprootTweak)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling tweak options: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
// Register the session with the internal wallet/signer now.
|
||||
session, err := s.cfg.Signer.MuSig2CreateSession(
|
||||
keyLoc, allSignerPubKeys, tweaks, otherSignerNonces,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error registering session: %v", err)
|
||||
}
|
||||
|
||||
var internalKeyBytes []byte
|
||||
if session.TaprootTweak {
|
||||
internalKeyBytes = schnorr.SerializePubKey(
|
||||
session.TaprootInternalKey,
|
||||
)
|
||||
}
|
||||
|
||||
return &MuSig2SessionResponse{
|
||||
SessionId: session.SessionID[:],
|
||||
CombinedKey: schnorr.SerializePubKey(
|
||||
session.CombinedKey,
|
||||
),
|
||||
TaprootInternalKey: internalKeyBytes,
|
||||
LocalPublicNonces: session.PublicNonce[:],
|
||||
HaveAllNonces: session.HaveAllNonces,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MuSig2RegisterNonces registers one or more public nonces of other signing
|
||||
// participants for a session identified by its ID.
|
||||
func (s *Server) MuSig2RegisterNonces(_ context.Context,
|
||||
in *MuSig2RegisterNoncesRequest) (*MuSig2RegisterNoncesResponse, error) {
|
||||
|
||||
// Check session ID length.
|
||||
sessionID, err := parseMuSig2SessionID(in.SessionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing session ID: %v", err)
|
||||
}
|
||||
|
||||
// Parse the other signing participants' nonces. We can't validate the
|
||||
// number of nonces here because we don't have access to the session in
|
||||
// this context. But the signer will be able to make sure we don't
|
||||
// register more nonces than there are signers (which would mean
|
||||
// something is wrong in the signing setup). But we want at least a
|
||||
// single nonce for each call.
|
||||
otherSignerNonces, err := parseMuSig2PublicNonces(
|
||||
in.OtherSignerPublicNonces, false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing other nonces: %v", err)
|
||||
}
|
||||
|
||||
// Register the nonces now.
|
||||
haveAllNonces, err := s.cfg.Signer.MuSig2RegisterNonces(
|
||||
sessionID, otherSignerNonces,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error registering nonces: %v", err)
|
||||
}
|
||||
|
||||
return &MuSig2RegisterNoncesResponse{HaveAllNonces: haveAllNonces}, nil
|
||||
}
|
||||
|
||||
// MuSig2Sign creates a partial signature using the local signing key that was
|
||||
// specified when the session was created. This can only be called when all
|
||||
// public nonces of all participants are known and have been registered with
|
||||
// the session. If this node isn't responsible for combining all the partial
|
||||
// signatures, then the cleanup flag should be set, indicating that the session
|
||||
// can be removed from memory once the signature was produced.
|
||||
func (s *Server) MuSig2Sign(_ context.Context,
|
||||
in *MuSig2SignRequest) (*MuSig2SignResponse, error) {
|
||||
|
||||
// Check session ID length.
|
||||
sessionID, err := parseMuSig2SessionID(in.SessionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing session ID: %v", err)
|
||||
}
|
||||
|
||||
// Schnorr signatures only work reliably if the message is 32 bytes.
|
||||
msg := [sha256.Size]byte{}
|
||||
if len(in.MessageDigest) != sha256.Size {
|
||||
return nil, fmt.Errorf("invalid message digest size, got %d "+
|
||||
"but expected %d", len(in.MessageDigest), sha256.Size)
|
||||
}
|
||||
copy(msg[:], in.MessageDigest)
|
||||
|
||||
// Create our own partial signature with the local signing key.
|
||||
partialSig, err := s.cfg.Signer.MuSig2Sign(sessionID, msg, in.Cleanup)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error signing: %v", err)
|
||||
}
|
||||
|
||||
serializedPartialSig, err := input.SerializePartialSignature(partialSig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error serializing sig: %v", err)
|
||||
}
|
||||
|
||||
return &MuSig2SignResponse{
|
||||
LocalPartialSignature: serializedPartialSig[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineSig combines the given partial signature(s) with the local one,
|
||||
// if it already exists. Once a partial signature of all participants is
|
||||
// registered, the final signature will be combined and returned.
|
||||
func (s *Server) MuSig2CombineSig(_ context.Context,
|
||||
in *MuSig2CombineSigRequest) (*MuSig2CombineSigResponse, error) {
|
||||
|
||||
// Check session ID length.
|
||||
sessionID, err := parseMuSig2SessionID(in.SessionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing session ID: %v", err)
|
||||
}
|
||||
|
||||
// Parse all other signatures. This can be called multiple times, so we
|
||||
// can't really sanity check how many we already have vs. how many the
|
||||
// user supplied in this call.
|
||||
partialSigs, err := parseMuSig2PartialSignatures(
|
||||
in.OtherPartialSignatures,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing partial signatures: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
// Combine the signatures now, potentially getting the final, full
|
||||
// signature if we've already got all partial ones.
|
||||
finalSig, haveAllSigs, err := s.cfg.Signer.MuSig2CombineSig(
|
||||
sessionID, partialSigs,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error combining signatures: %v", err)
|
||||
}
|
||||
|
||||
return &MuSig2CombineSigResponse{
|
||||
HaveAllSignatures: haveAllSigs,
|
||||
FinalSignature: finalSig.Serialize(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parseRawKeyBytes checks that the provided raw public key is valid and returns
|
||||
// the public key. A nil public key is returned if the length of the rawKeyBytes
|
||||
// is zero.
|
||||
@ -695,3 +962,111 @@ func parseRawKeyBytes(rawKeyBytes []byte) (*btcec.PublicKey, error) {
|
||||
"specified")
|
||||
}
|
||||
}
|
||||
|
||||
// parseMuSig2SessionID parses a MuSig2 session ID from a raw byte slice.
|
||||
func parseMuSig2SessionID(rawID []byte) (input.MuSig2SessionID, error) {
|
||||
sessionID := input.MuSig2SessionID{}
|
||||
|
||||
// The session ID must be exact in its length.
|
||||
if len(rawID) != sha256.Size {
|
||||
return sessionID, fmt.Errorf("invalid session ID size, got "+
|
||||
"%d but expected %d", len(rawID), sha256.Size)
|
||||
}
|
||||
copy(sessionID[:], rawID)
|
||||
|
||||
return sessionID, nil
|
||||
}
|
||||
|
||||
// parseMuSig2PublicNonces sanity checks and parses the other signers' public
|
||||
// nonces.
|
||||
func parseMuSig2PublicNonces(pubNonces [][]byte,
|
||||
emptyAllowed bool) ([][musig2.PubNonceSize]byte, error) {
|
||||
|
||||
// For some calls the nonces are optional while for others it doesn't
|
||||
// make any sense to not specify them (for example for the explicit
|
||||
// nonce registration call there should be at least one nonce).
|
||||
if !emptyAllowed && len(pubNonces) == 0 {
|
||||
return nil, fmt.Errorf("at least one other signer public " +
|
||||
"nonce is required")
|
||||
}
|
||||
|
||||
// Parse all other nonces. This can be called multiple times, so we
|
||||
// can't really sanity check how many we already have vs. how many the
|
||||
// user supplied in this call.
|
||||
otherSignerNonces := make([][musig2.PubNonceSize]byte, len(pubNonces))
|
||||
for idx, otherNonceBytes := range pubNonces {
|
||||
if len(otherNonceBytes) != musig2.PubNonceSize {
|
||||
return nil, fmt.Errorf("invalid public nonce at "+
|
||||
"index %d: invalid length, got %d but "+
|
||||
"expected %d", idx, len(otherNonceBytes),
|
||||
musig2.PubNonceSize)
|
||||
}
|
||||
copy(otherSignerNonces[idx][:], otherNonceBytes)
|
||||
}
|
||||
|
||||
return otherSignerNonces, nil
|
||||
}
|
||||
|
||||
// parseMuSig2PartialSignatures sanity checks and parses the other signers'
|
||||
// partial signatures.
|
||||
func parseMuSig2PartialSignatures(
|
||||
partialSignatures [][]byte) ([]*musig2.PartialSignature, error) {
|
||||
|
||||
// We always want at least one partial signature.
|
||||
if len(partialSignatures) == 0 {
|
||||
return nil, fmt.Errorf("at least one partial signature is " +
|
||||
"required")
|
||||
}
|
||||
|
||||
parsedPartialSigs := make(
|
||||
[]*musig2.PartialSignature, len(partialSignatures),
|
||||
)
|
||||
for idx, otherPartialSigBytes := range partialSignatures {
|
||||
sig, err := input.DeserializePartialSignature(
|
||||
otherPartialSigBytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid partial signature at "+
|
||||
"index %d: %v", idx, err)
|
||||
}
|
||||
|
||||
parsedPartialSigs[idx] = sig
|
||||
}
|
||||
|
||||
return parsedPartialSigs, nil
|
||||
}
|
||||
|
||||
// UnmarshalTweaks parses the RPC tweak descriptions into their native
|
||||
// counterpart.
|
||||
func UnmarshalTweaks(rpcTweaks []*TweakDesc,
|
||||
taprootTweak *TaprootTweakDesc) (*input.MuSig2Tweaks, error) {
|
||||
|
||||
// Parse the generic tweaks first.
|
||||
tweaks := &input.MuSig2Tweaks{
|
||||
GenericTweaks: make([]musig2.KeyTweakDesc, len(rpcTweaks)),
|
||||
}
|
||||
for idx, rpcTweak := range rpcTweaks {
|
||||
if len(rpcTweak.Tweak) == 0 {
|
||||
return nil, fmt.Errorf("tweak cannot be empty")
|
||||
}
|
||||
|
||||
copy(tweaks.GenericTweaks[idx].Tweak[:], rpcTweak.Tweak)
|
||||
tweaks.GenericTweaks[idx].IsXOnly = rpcTweak.IsXOnly
|
||||
}
|
||||
|
||||
// Now parse the taproot specific tweak.
|
||||
if taprootTweak != nil {
|
||||
if taprootTweak.KeySpendOnly {
|
||||
tweaks.TaprootBIP0086Tweak = true
|
||||
} else {
|
||||
if len(taprootTweak.ScriptRoot) == 0 {
|
||||
return nil, fmt.Errorf("script root cannot " +
|
||||
"be empty for non-keyspend")
|
||||
}
|
||||
|
||||
tweaks.TaprootTweak = taprootTweak.ScriptRoot
|
||||
}
|
||||
}
|
||||
|
||||
return tweaks, nil
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
@ -47,6 +50,50 @@ func (d *DummySigner) ComputeInputScript(tx *wire.MsgTx,
|
||||
return &input.Script{}, nil
|
||||
}
|
||||
|
||||
// MuSig2CreateSession creates a new MuSig2 signing session using the local
|
||||
// key identified by the key locator. The complete list of all public keys of
|
||||
// all signing parties must be provided, including the public key of the local
|
||||
// signing key. If nonces of other parties are already known, they can be
|
||||
// submitted as well to reduce the number of method calls necessary later on.
|
||||
func (d *DummySigner) MuSig2CreateSession(keychain.KeyLocator,
|
||||
[]*btcec.PublicKey, *input.MuSig2Tweaks,
|
||||
[][musig2.PubNonceSize]byte) (*input.MuSig2SessionInfo, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2RegisterNonces registers one or more public nonces of other signing
|
||||
// participants for a session identified by its ID. This method returns true
|
||||
// once we have all nonces for all other signing participants.
|
||||
func (d *DummySigner) MuSig2RegisterNonces(input.MuSig2SessionID,
|
||||
[][musig2.PubNonceSize]byte) (bool, error) {
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// MuSig2Sign creates a partial signature using the local signing key
|
||||
// that was specified when the session was created. This can only be
|
||||
// called when all public nonces of all participants are known and have
|
||||
// been registered with the session. If this node isn't responsible for
|
||||
// combining all the partial signatures, then the cleanup parameter
|
||||
// should be set, indicating that the session can be removed from memory
|
||||
// once the signature was produced.
|
||||
func (d *DummySigner) MuSig2Sign(input.MuSig2SessionID,
|
||||
[sha256.Size]byte, bool) (*musig2.PartialSignature, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineSig combines the given partial signature(s) with the
|
||||
// local one, if it already exists. Once a partial signature of all
|
||||
// participants is registered, the final signature will be combined and
|
||||
// returned.
|
||||
func (d *DummySigner) MuSig2CombineSig(input.MuSig2SessionID,
|
||||
[]*musig2.PartialSignature) (*schnorr.Signature, bool, error) {
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// SingleSigner is an implementation of the Signer interface that signs
|
||||
// everything with a single private key.
|
||||
type SingleSigner struct {
|
||||
@ -136,3 +183,47 @@ func (s *SingleSigner) SignMessage(keyLoc keychain.KeyLocator,
|
||||
}
|
||||
return ecdsa.Sign(s.Privkey, digest), nil
|
||||
}
|
||||
|
||||
// MuSig2CreateSession creates a new MuSig2 signing session using the local
|
||||
// key identified by the key locator. The complete list of all public keys of
|
||||
// all signing parties must be provided, including the public key of the local
|
||||
// signing key. If nonces of other parties are already known, they can be
|
||||
// submitted as well to reduce the number of method calls necessary later on.
|
||||
func (s *SingleSigner) MuSig2CreateSession(keychain.KeyLocator,
|
||||
[]*btcec.PublicKey, *input.MuSig2Tweaks,
|
||||
[][musig2.PubNonceSize]byte) (*input.MuSig2SessionInfo, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2RegisterNonces registers one or more public nonces of other signing
|
||||
// participants for a session identified by its ID. This method returns true
|
||||
// once we have all nonces for all other signing participants.
|
||||
func (s *SingleSigner) MuSig2RegisterNonces(input.MuSig2SessionID,
|
||||
[][musig2.PubNonceSize]byte) (bool, error) {
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// MuSig2Sign creates a partial signature using the local signing key
|
||||
// that was specified when the session was created. This can only be
|
||||
// called when all public nonces of all participants are known and have
|
||||
// been registered with the session. If this node isn't responsible for
|
||||
// combining all the partial signatures, then the cleanup parameter
|
||||
// should be set, indicating that the session can be removed from memory
|
||||
// once the signature was produced.
|
||||
func (s *SingleSigner) MuSig2Sign(input.MuSig2SessionID,
|
||||
[sha256.Size]byte, bool) (*musig2.PartialSignature, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineSig combines the given partial signature(s) with the
|
||||
// local one, if it already exists. Once a partial signature of all
|
||||
// participants is registered, the final signature will be combined and
|
||||
// returned.
|
||||
func (s *SingleSigner) MuSig2CombineSig(input.MuSig2SessionID,
|
||||
[]*musig2.PartialSignature) (*schnorr.Signature, bool, error) {
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package rpcwallet
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -11,6 +12,8 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
@ -537,7 +540,7 @@ func (r *RPCKeyRing) SignOutputRaw(tx *wire.MsgTx,
|
||||
//
|
||||
// NOTE: This method will ignore any tweak parameters set within the
|
||||
// passed SignDescriptor as it assumes a set of typical script
|
||||
// templates (p2wkh, np2wkh, etc).
|
||||
// templates (p2wkh, np2wkh, BIP0086 p2tr, etc).
|
||||
//
|
||||
// NOTE: This method is part of the input.Signer interface.
|
||||
func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx,
|
||||
@ -571,6 +574,214 @@ func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MuSig2CreateSession creates a new MuSig2 signing session using the local
|
||||
// key identified by the key locator. The complete list of all public keys of
|
||||
// all signing parties must be provided, including the public key of the local
|
||||
// signing key. If nonces of other parties are already known, they can be
|
||||
// submitted as well to reduce the number of method calls necessary later on.
|
||||
func (r *RPCKeyRing) MuSig2CreateSession(keyLoc keychain.KeyLocator,
|
||||
pubKeys []*btcec.PublicKey, tweaks *input.MuSig2Tweaks,
|
||||
otherNonces [][musig2.PubNonceSize]byte) (*input.MuSig2SessionInfo,
|
||||
error) {
|
||||
|
||||
// We need to serialize all data for the RPC call. We can do that by
|
||||
// putting everything directly into the request struct.
|
||||
req := &signrpc.MuSig2SessionRequest{
|
||||
KeyLoc: &signrpc.KeyLocator{
|
||||
KeyFamily: int32(keyLoc.Family),
|
||||
KeyIndex: int32(keyLoc.Index),
|
||||
},
|
||||
AllSignerPubkeys: make([][]byte, len(pubKeys)),
|
||||
Tweaks: make(
|
||||
[]*signrpc.TweakDesc, len(tweaks.GenericTweaks),
|
||||
),
|
||||
OtherSignerPublicNonces: make([][]byte, len(otherNonces)),
|
||||
}
|
||||
for idx, pubKey := range pubKeys {
|
||||
req.AllSignerPubkeys[idx] = schnorr.SerializePubKey(pubKey)
|
||||
}
|
||||
for idx, genericTweak := range tweaks.GenericTweaks {
|
||||
req.Tweaks[idx] = &signrpc.TweakDesc{
|
||||
Tweak: genericTweak.Tweak[:],
|
||||
IsXOnly: genericTweak.IsXOnly,
|
||||
}
|
||||
}
|
||||
for idx, nonce := range otherNonces {
|
||||
req.OtherSignerPublicNonces[idx] = make([]byte, len(nonce))
|
||||
copy(req.OtherSignerPublicNonces[idx], nonce[:])
|
||||
}
|
||||
if tweaks.HasTaprootTweak() {
|
||||
req.TaprootTweak = &signrpc.TaprootTweakDesc{
|
||||
KeySpendOnly: tweaks.TaprootBIP0086Tweak,
|
||||
ScriptRoot: tweaks.TaprootTweak,
|
||||
}
|
||||
}
|
||||
|
||||
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
resp, err := r.signerClient.MuSig2CreateSession(ctxt, req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error creating MuSig2 session in remote "+
|
||||
"signer instance: %v", err)
|
||||
|
||||
// Log as critical as we should shut down if there is no signer.
|
||||
log.Criticalf("RPC signer error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// De-Serialize all the info back into our native struct.
|
||||
info := &input.MuSig2SessionInfo{
|
||||
TaprootTweak: tweaks.HasTaprootTweak(),
|
||||
HaveAllNonces: resp.HaveAllNonces,
|
||||
}
|
||||
copy(info.SessionID[:], resp.SessionId)
|
||||
copy(info.PublicNonce[:], resp.LocalPublicNonces)
|
||||
|
||||
info.CombinedKey, err = schnorr.ParsePubKey(resp.CombinedKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing combined key: %v", err)
|
||||
}
|
||||
|
||||
if tweaks.HasTaprootTweak() {
|
||||
info.TaprootInternalKey, err = schnorr.ParsePubKey(
|
||||
resp.TaprootInternalKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing internal key: %v",
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// MuSig2RegisterNonces registers one or more public nonces of other signing
|
||||
// participants for a session identified by its ID. This method returns true
|
||||
// once we have all nonces for all other signing participants.
|
||||
func (r *RPCKeyRing) MuSig2RegisterNonces(sessionID input.MuSig2SessionID,
|
||||
pubNonces [][musig2.PubNonceSize]byte) (bool, error) {
|
||||
|
||||
// We need to serialize all data for the RPC call. We can do that by
|
||||
// putting everything directly into the request struct.
|
||||
req := &signrpc.MuSig2RegisterNoncesRequest{
|
||||
SessionId: sessionID[:],
|
||||
OtherSignerPublicNonces: make([][]byte, len(pubNonces)),
|
||||
}
|
||||
for idx, nonce := range pubNonces {
|
||||
req.OtherSignerPublicNonces[idx] = make([]byte, len(nonce))
|
||||
copy(req.OtherSignerPublicNonces[idx], nonce[:])
|
||||
}
|
||||
|
||||
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
resp, err := r.signerClient.MuSig2RegisterNonces(ctxt, req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error registering MuSig2 nonces in remote "+
|
||||
"signer instance: %v", err)
|
||||
|
||||
// Log as critical as we should shut down if there is no signer.
|
||||
log.Criticalf("RPC signer error: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return resp.HaveAllNonces, nil
|
||||
}
|
||||
|
||||
// MuSig2Sign creates a partial signature using the local signing key
|
||||
// that was specified when the session was created. This can only be
|
||||
// called when all public nonces of all participants are known and have
|
||||
// been registered with the session. If this node isn't responsible for
|
||||
// combining all the partial signatures, then the cleanup parameter
|
||||
// should be set, indicating that the session can be removed from memory
|
||||
// once the signature was produced.
|
||||
func (r *RPCKeyRing) MuSig2Sign(sessionID input.MuSig2SessionID,
|
||||
msg [sha256.Size]byte, cleanUp bool) (*musig2.PartialSignature, error) {
|
||||
|
||||
// We need to serialize all data for the RPC call. We can do that by
|
||||
// putting everything directly into the request struct.
|
||||
req := &signrpc.MuSig2SignRequest{
|
||||
SessionId: sessionID[:],
|
||||
MessageDigest: msg[:],
|
||||
Cleanup: cleanUp,
|
||||
}
|
||||
|
||||
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
resp, err := r.signerClient.MuSig2Sign(ctxt, req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error signing MuSig2 session in remote "+
|
||||
"signer instance: %v", err)
|
||||
|
||||
// Log as critical as we should shut down if there is no signer.
|
||||
log.Criticalf("RPC signer error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
partialSig, err := input.DeserializePartialSignature(
|
||||
resp.LocalPartialSignature,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing partial signature from "+
|
||||
"remote signer: %v", err)
|
||||
}
|
||||
|
||||
return partialSig, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineSig combines the given partial signature(s) with the
|
||||
// local one, if it already exists. Once a partial signature of all
|
||||
// participants is registered, the final signature will be combined and
|
||||
// returned.
|
||||
func (r *RPCKeyRing) MuSig2CombineSig(sessionID input.MuSig2SessionID,
|
||||
partialSigs []*musig2.PartialSignature) (*schnorr.Signature, bool,
|
||||
error) {
|
||||
|
||||
// We need to serialize all data for the RPC call. We can do that by
|
||||
// putting everything directly into the request struct.
|
||||
req := &signrpc.MuSig2CombineSigRequest{
|
||||
SessionId: sessionID[:],
|
||||
OtherPartialSignatures: make([][]byte, len(partialSigs)),
|
||||
}
|
||||
for idx, partialSig := range partialSigs {
|
||||
rawSig, err := input.SerializePartialSignature(partialSig)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("error serializing "+
|
||||
"partial signature: %v", err)
|
||||
}
|
||||
req.OtherPartialSignatures[idx] = rawSig[:]
|
||||
}
|
||||
|
||||
ctxt, cancel := context.WithTimeout(context.Background(), r.rpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
resp, err := r.signerClient.MuSig2CombineSig(ctxt, req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error combining MuSig2 signatures in remote "+
|
||||
"signer instance: %v", err)
|
||||
|
||||
// Log as critical as we should shut down if there is no signer.
|
||||
log.Criticalf("RPC signer error: %v", err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// The final signature is only available when we have all the other
|
||||
// partial signatures from all participants.
|
||||
if !resp.HaveAllSignatures {
|
||||
return nil, resp.HaveAllSignatures, nil
|
||||
}
|
||||
|
||||
finalSig, err := schnorr.ParseSignature(resp.FinalSignature)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("error parsing final signature: "+
|
||||
"%v", err)
|
||||
}
|
||||
|
||||
return finalSig, resp.HaveAllSignatures, nil
|
||||
}
|
||||
|
||||
// remoteSign signs the input specified in signDesc of the given transaction tx
|
||||
// using the remote signing instance.
|
||||
func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
|
||||
|
@ -1,10 +1,13 @@
|
||||
package wtmock
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"sync"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
@ -61,6 +64,50 @@ func (s *MockSigner) ComputeInputScript(tx *wire.MsgTx,
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// MuSig2CreateSession creates a new MuSig2 signing session using the local
|
||||
// key identified by the key locator. The complete list of all public keys of
|
||||
// all signing parties must be provided, including the public key of the local
|
||||
// signing key. If nonces of other parties are already known, they can be
|
||||
// submitted as well to reduce the number of method calls necessary later on.
|
||||
func (s *MockSigner) MuSig2CreateSession(keychain.KeyLocator,
|
||||
[]*btcec.PublicKey, *input.MuSig2Tweaks,
|
||||
[][musig2.PubNonceSize]byte) (*input.MuSig2SessionInfo, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2RegisterNonces registers one or more public nonces of other signing
|
||||
// participants for a session identified by its ID. This method returns true
|
||||
// once we have all nonces for all other signing participants.
|
||||
func (s *MockSigner) MuSig2RegisterNonces(input.MuSig2SessionID,
|
||||
[][musig2.PubNonceSize]byte) (bool, error) {
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// MuSig2Sign creates a partial signature using the local signing key
|
||||
// that was specified when the session was created. This can only be
|
||||
// called when all public nonces of all participants are known and have
|
||||
// been registered with the session. If this node isn't responsible for
|
||||
// combining all the partial signatures, then the cleanup parameter
|
||||
// should be set, indicating that the session can be removed from memory
|
||||
// once the signature was produced.
|
||||
func (s *MockSigner) MuSig2Sign(input.MuSig2SessionID,
|
||||
[sha256.Size]byte, bool) (*musig2.PartialSignature, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MuSig2CombineSig combines the given partial signature(s) with the
|
||||
// local one, if it already exists. Once a partial signature of all
|
||||
// participants is registered, the final signature will be combined and
|
||||
// returned.
|
||||
func (s *MockSigner) MuSig2CombineSig(input.MuSig2SessionID,
|
||||
[]*musig2.PartialSignature) (*schnorr.Signature, bool, error) {
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// AddPrivKey records the passed privKey in the MockSigner's registry of keys it
|
||||
// can sign with in the future. A unique key locator is returned, allowing the
|
||||
// caller to sign with this key when presented via an input.SignDescriptor.
|
||||
|
Loading…
Reference in New Issue
Block a user