mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 06:21:40 +01:00
Merge pull request #7231 from ziggie1984/sign_verify_message_with_addr
Sign/Verify messages and signatures for single addresses
This commit is contained in:
commit
9187d4679b
15 changed files with 2091 additions and 653 deletions
|
@ -52,6 +52,8 @@ var (
|
||||||
Usage: "Interact with wallet addresses.",
|
Usage: "Interact with wallet addresses.",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
listAddressesCommand,
|
listAddressesCommand,
|
||||||
|
signMessageWithAddrCommand,
|
||||||
|
verifyMessageWithAddrCommand,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1193,6 +1195,200 @@ func listAddresses(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var signMessageWithAddrCommand = cli.Command{
|
||||||
|
Name: "signmessage",
|
||||||
|
Usage: "Sign a message with the private key of the provided " +
|
||||||
|
"address.",
|
||||||
|
ArgsUsage: "address msg",
|
||||||
|
Description: `
|
||||||
|
Sign a message with the private key of the specified address, and
|
||||||
|
return the signature. Signing is solely done in the ECDSA compact
|
||||||
|
signature format. This is also done when signing with a P2TR address
|
||||||
|
meaning that the private key of the P2TR address (internal key) is used
|
||||||
|
to sign the provided message with the ECDSA format. Only addresses are
|
||||||
|
accepted which are owned by the internal lnd wallet.
|
||||||
|
`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "address",
|
||||||
|
Usage: "specify the address which private key " +
|
||||||
|
"will be used to sign the message",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "msg",
|
||||||
|
Usage: "the message to sign for",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: actionDecorator(signMessageWithAddr),
|
||||||
|
}
|
||||||
|
|
||||||
|
func signMessageWithAddr(ctx *cli.Context) error {
|
||||||
|
ctxc := getContext()
|
||||||
|
|
||||||
|
// Display the command's help message if we do not have the expected
|
||||||
|
// number of arguments/flags.
|
||||||
|
if ctx.NArg() > 2 || ctx.NumFlags() > 2 {
|
||||||
|
return cli.ShowCommandHelp(ctx, "signmessagewithaddr")
|
||||||
|
}
|
||||||
|
|
||||||
|
walletClient, cleanUp := getWalletClient(ctx)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
var (
|
||||||
|
args = ctx.Args()
|
||||||
|
addr string
|
||||||
|
msg []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet("address"):
|
||||||
|
addr = ctx.String("address")
|
||||||
|
|
||||||
|
case ctx.Args().Present():
|
||||||
|
addr = args.First()
|
||||||
|
args = args.Tail()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("address argument missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet("msg"):
|
||||||
|
msg = []byte(ctx.String("msg"))
|
||||||
|
|
||||||
|
case ctx.Args().Present():
|
||||||
|
msg = []byte(args.First())
|
||||||
|
args = args.Tail()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("msg argument missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := walletClient.SignMessageWithAddr(
|
||||||
|
ctxc,
|
||||||
|
&walletrpc.SignMessageWithAddrRequest{
|
||||||
|
Msg: msg,
|
||||||
|
Addr: addr,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRespJSON(resp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var verifyMessageWithAddrCommand = cli.Command{
|
||||||
|
Name: "verifymessage",
|
||||||
|
Usage: "Verify a message signed with the private key of the " +
|
||||||
|
"provided address.",
|
||||||
|
ArgsUsage: "address sig msg",
|
||||||
|
Description: `
|
||||||
|
Verify a message signed with the signature of the public key
|
||||||
|
of the provided address. The signature must be in compact ECDSA format
|
||||||
|
The verification is independent whether the address belongs to the
|
||||||
|
wallet or not. This is achieved by only accepting ECDSA compacted
|
||||||
|
signatures. When verifying a signature with a taproot address, the
|
||||||
|
signature still has to be in the ECDSA compact format and no tapscript
|
||||||
|
has to be included in the P2TR address.
|
||||||
|
Supports address types P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
|
||||||
|
Besides whether the signature is valid or not, the recoverd public key
|
||||||
|
of the compact ECDSA signature is returned.
|
||||||
|
`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "address",
|
||||||
|
Usage: "specify the address which corresponding" +
|
||||||
|
"public key will be used",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "sig",
|
||||||
|
Usage: "the base64 encoded compact signature " +
|
||||||
|
"of the message",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "msg",
|
||||||
|
Usage: "the message to sign",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: actionDecorator(verifyMessageWithAddr),
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyMessageWithAddr(ctx *cli.Context) error {
|
||||||
|
ctxc := getContext()
|
||||||
|
|
||||||
|
// Display the command's help message if we do not have the expected
|
||||||
|
// number of arguments/flags.
|
||||||
|
if ctx.NArg() > 3 || ctx.NumFlags() > 3 {
|
||||||
|
return cli.ShowCommandHelp(ctx, "signmessagewithaddr")
|
||||||
|
}
|
||||||
|
|
||||||
|
walletClient, cleanUp := getWalletClient(ctx)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
var (
|
||||||
|
args = ctx.Args()
|
||||||
|
addr string
|
||||||
|
sig string
|
||||||
|
msg []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet("address"):
|
||||||
|
addr = ctx.String("address")
|
||||||
|
|
||||||
|
case args.Present():
|
||||||
|
addr = args.First()
|
||||||
|
args = args.Tail()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("address argument missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet("sig"):
|
||||||
|
sig = ctx.String("sig")
|
||||||
|
|
||||||
|
case ctx.Args().Present():
|
||||||
|
sig = args.First()
|
||||||
|
args = args.Tail()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("sig argument missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet("msg"):
|
||||||
|
msg = []byte(ctx.String("msg"))
|
||||||
|
|
||||||
|
case ctx.Args().Present():
|
||||||
|
msg = []byte(args.First())
|
||||||
|
args = args.Tail()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("msg argument missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := walletClient.VerifyMessageWithAddr(
|
||||||
|
ctxc,
|
||||||
|
&walletrpc.VerifyMessageWithAddrRequest{
|
||||||
|
Msg: msg,
|
||||||
|
Signature: sig,
|
||||||
|
Addr: addr,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRespJSON(resp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var importAccountCommand = cli.Command{
|
var importAccountCommand = cli.Command{
|
||||||
Name: "import",
|
Name: "import",
|
||||||
Usage: "Import an on-chain account into the wallet through its " +
|
Usage: "Import an on-chain account into the wallet through its " +
|
||||||
|
|
|
@ -97,6 +97,9 @@ current gossip sync query status.
|
||||||
the `dev` tag, and configured to [opt into overriding a specific message
|
the `dev` tag, and configured to [opt into overriding a specific message
|
||||||
type](https://github.com/lightningnetwork/lnd/pull/7153)
|
type](https://github.com/lightningnetwork/lnd/pull/7153)
|
||||||
|
|
||||||
|
* [Sign/Verify messages and signatures for single
|
||||||
|
addresses](https://github.com/lightningnetwork/lnd/pull/7231).
|
||||||
|
|
||||||
## Wallet
|
## Wallet
|
||||||
|
|
||||||
* [Allows Taproot public keys and tap scripts to be imported as watch-only
|
* [Allows Taproot public keys and tap scripts to be imported as watch-only
|
||||||
|
@ -285,6 +288,9 @@ in the lnwire package](https://github.com/lightningnetwork/lnd/pull/7303)
|
||||||
link is falling behind, which is now fixed by [retrying the enable
|
link is falling behind, which is now fixed by [retrying the enable
|
||||||
request](https://github.com/lightningnetwork/lnd/pull/7157).
|
request](https://github.com/lightningnetwork/lnd/pull/7157).
|
||||||
|
|
||||||
|
* [Sign/Verify messages and signatures for single
|
||||||
|
addresses](https://github.com/lightningnetwork/lnd/pull/7231).
|
||||||
|
|
||||||
## Code Health
|
## Code Health
|
||||||
|
|
||||||
* [test: use `T.TempDir` to create temporary test
|
* [test: use `T.TempDir` to create temporary test
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -362,6 +362,74 @@ func local_request_WalletKit_ListAddresses_0(ctx context.Context, marshaler runt
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func request_WalletKit_SignMessageWithAddr_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq SignMessageWithAddrRequest
|
||||||
|
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.SignMessageWithAddr(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_WalletKit_SignMessageWithAddr_0(ctx context.Context, marshaler runtime.Marshaler, server WalletKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq SignMessageWithAddrRequest
|
||||||
|
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.SignMessageWithAddr(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func request_WalletKit_VerifyMessageWithAddr_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq VerifyMessageWithAddrRequest
|
||||||
|
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.VerifyMessageWithAddr(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_WalletKit_VerifyMessageWithAddr_0(ctx context.Context, marshaler runtime.Marshaler, server WalletKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq VerifyMessageWithAddrRequest
|
||||||
|
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.VerifyMessageWithAddr(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func request_WalletKit_ImportAccount_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
func request_WalletKit_ImportAccount_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
var protoReq ImportAccountRequest
|
var protoReq ImportAccountRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
|
@ -1044,6 +1112,52 @@ func RegisterWalletKitHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WalletKit_SignMessageWithAddr_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, "/walletrpc.WalletKit/SignMessageWithAddr", runtime.WithHTTPPathPattern("/v2/wallet/address/signmessage"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_WalletKit_SignMessageWithAddr_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_WalletKit_SignMessageWithAddr_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WalletKit_VerifyMessageWithAddr_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, "/walletrpc.WalletKit/VerifyMessageWithAddr", runtime.WithHTTPPathPattern("/v2/wallet/address/verifymessage"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_WalletKit_VerifyMessageWithAddr_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_WalletKit_VerifyMessageWithAddr_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("POST", pattern_WalletKit_ImportAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("POST", pattern_WalletKit_ImportAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -1584,6 +1698,46 @@ func RegisterWalletKitHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WalletKit_SignMessageWithAddr_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, "/walletrpc.WalletKit/SignMessageWithAddr", runtime.WithHTTPPathPattern("/v2/wallet/address/signmessage"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_WalletKit_SignMessageWithAddr_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_WalletKit_SignMessageWithAddr_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WalletKit_VerifyMessageWithAddr_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, "/walletrpc.WalletKit/VerifyMessageWithAddr", runtime.WithHTTPPathPattern("/v2/wallet/address/verifymessage"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_WalletKit_VerifyMessageWithAddr_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_WalletKit_VerifyMessageWithAddr_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("POST", pattern_WalletKit_ImportAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("POST", pattern_WalletKit_ImportAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -1868,6 +2022,10 @@ var (
|
||||||
|
|
||||||
pattern_WalletKit_ListAddresses_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "addresses"}, ""))
|
pattern_WalletKit_ListAddresses_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "addresses"}, ""))
|
||||||
|
|
||||||
|
pattern_WalletKit_SignMessageWithAddr_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "address", "signmessage"}, ""))
|
||||||
|
|
||||||
|
pattern_WalletKit_VerifyMessageWithAddr_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "address", "verifymessage"}, ""))
|
||||||
|
|
||||||
pattern_WalletKit_ImportAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "accounts", "import"}, ""))
|
pattern_WalletKit_ImportAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "accounts", "import"}, ""))
|
||||||
|
|
||||||
pattern_WalletKit_ImportPublicKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "key", "import"}, ""))
|
pattern_WalletKit_ImportPublicKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "key", "import"}, ""))
|
||||||
|
@ -1916,6 +2074,10 @@ var (
|
||||||
|
|
||||||
forward_WalletKit_ListAddresses_0 = runtime.ForwardResponseMessage
|
forward_WalletKit_ListAddresses_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_WalletKit_SignMessageWithAddr_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_WalletKit_VerifyMessageWithAddr_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_WalletKit_ImportAccount_0 = runtime.ForwardResponseMessage
|
forward_WalletKit_ImportAccount_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_WalletKit_ImportPublicKey_0 = runtime.ForwardResponseMessage
|
forward_WalletKit_ImportPublicKey_0 = runtime.ForwardResponseMessage
|
||||||
|
|
|
@ -272,6 +272,56 @@ func RegisterWalletKitJSONCallbacks(registry map[string]func(ctx context.Context
|
||||||
callback(string(respBytes), nil)
|
callback(string(respBytes), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registry["walletrpc.WalletKit.SignMessageWithAddr"] = func(ctx context.Context,
|
||||||
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
req := &SignMessageWithAddrRequest{}
|
||||||
|
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWalletKitClient(conn)
|
||||||
|
resp, err := client.SignMessageWithAddr(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respBytes, err := marshaler.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(string(respBytes), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
registry["walletrpc.WalletKit.VerifyMessageWithAddr"] = func(ctx context.Context,
|
||||||
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
req := &VerifyMessageWithAddrRequest{}
|
||||||
|
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWalletKitClient(conn)
|
||||||
|
resp, err := client.VerifyMessageWithAddr(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respBytes, err := marshaler.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(string(respBytes), nil)
|
||||||
|
}
|
||||||
|
|
||||||
registry["walletrpc.WalletKit.ImportAccount"] = func(ctx context.Context,
|
registry["walletrpc.WalletKit.ImportAccount"] = func(ctx context.Context,
|
||||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,47 @@ service WalletKit {
|
||||||
*/
|
*/
|
||||||
rpc ListAddresses (ListAddressesRequest) returns (ListAddressesResponse);
|
rpc ListAddresses (ListAddressesRequest) returns (ListAddressesResponse);
|
||||||
|
|
||||||
|
/*
|
||||||
|
SignMessageWithAddr returns the compact signature (base64 encoded) created
|
||||||
|
with the private key of the provided address. This requires the address
|
||||||
|
to be solely based on a public key lock (no scripts). Obviously the internal
|
||||||
|
lnd wallet has to possess the private key of the address otherwise
|
||||||
|
an error is returned.
|
||||||
|
|
||||||
|
This method aims to provide full compatibility with the bitcoin-core and
|
||||||
|
btcd implementation. Bitcoin-core's algorithm is not specified in a
|
||||||
|
BIP and only applicable for legacy addresses. This method enhances the
|
||||||
|
signing for additional address types: P2WKH, NP2WKH, P2TR.
|
||||||
|
For P2TR addresses this represents a special case. ECDSA is used to create
|
||||||
|
a compact signature which makes the public key of the signature recoverable.
|
||||||
|
*/
|
||||||
|
rpc SignMessageWithAddr (SignMessageWithAddrRequest)
|
||||||
|
returns (SignMessageWithAddrResponse);
|
||||||
|
|
||||||
|
/*
|
||||||
|
VerifyMessageWithAddr returns the validity and the recovered public key of
|
||||||
|
the provided compact signature (base64 encoded). The verification is
|
||||||
|
twofold. First the validity of the signature itself is checked and then
|
||||||
|
it is verified that the recovered public key of the signature equals
|
||||||
|
the public key of the provided address. There is no dependence on the
|
||||||
|
private key of the address therefore also external addresses are allowed
|
||||||
|
to verify signatures.
|
||||||
|
Supported address types are P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
|
||||||
|
This method is the counterpart of the related signing method
|
||||||
|
(SignMessageWithAddr) and aims to provide full compatibility to
|
||||||
|
bitcoin-core's implementation. Although bitcoin-core/btcd only provide
|
||||||
|
this functionality for legacy addresses this function enhances it to
|
||||||
|
the address types: P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
|
||||||
|
The verification for P2TR addresses is a special case and requires the
|
||||||
|
ECDSA compact signature to compare the reovered public key to the internal
|
||||||
|
taproot key. The compact ECDSA signature format was used because there
|
||||||
|
are still no known compact signature schemes for schnorr signatures.
|
||||||
|
*/
|
||||||
|
rpc VerifyMessageWithAddr (VerifyMessageWithAddrRequest)
|
||||||
|
returns (VerifyMessageWithAddrResponse);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ImportAccount imports an account backed by an account extended public key.
|
ImportAccount imports an account backed by an account extended public key.
|
||||||
The master key fingerprint denotes the fingerprint of the root key
|
The master key fingerprint denotes the fingerprint of the root key
|
||||||
|
@ -497,6 +538,43 @@ message ListAddressesResponse {
|
||||||
repeated AccountWithAddresses account_with_addresses = 1;
|
repeated AccountWithAddresses account_with_addresses = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message SignMessageWithAddrRequest {
|
||||||
|
// The message to be signed. When using REST, this field must be encoded as
|
||||||
|
// base64.
|
||||||
|
bytes msg = 1;
|
||||||
|
|
||||||
|
// The address which will be used to look up the private key and sign the
|
||||||
|
// corresponding message.
|
||||||
|
string addr = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignMessageWithAddrResponse {
|
||||||
|
// The compact ECDSA signature for the given message encoded in base64.
|
||||||
|
string signature = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VerifyMessageWithAddrRequest {
|
||||||
|
// The message to be signed. When using REST, this field must be encoded as
|
||||||
|
// base64.
|
||||||
|
bytes msg = 1;
|
||||||
|
|
||||||
|
// The compact ECDSA signature to be verified over the given message
|
||||||
|
// ecoded in base64.
|
||||||
|
string signature = 2;
|
||||||
|
|
||||||
|
// The address which will be used to look up the public key and verify the
|
||||||
|
// the signature.
|
||||||
|
string addr = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VerifyMessageWithAddrResponse {
|
||||||
|
// Whether the signature was valid over the given message.
|
||||||
|
bool valid = 1;
|
||||||
|
|
||||||
|
// The pubkey recovered from the signature.
|
||||||
|
bytes pubkey = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message ImportAccountRequest {
|
message ImportAccountRequest {
|
||||||
// A name to identify the account with.
|
// A name to identify the account with.
|
||||||
string name = 1;
|
string name = 1;
|
||||||
|
|
|
@ -130,6 +130,74 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v2/wallet/address/signmessage": {
|
||||||
|
"post": {
|
||||||
|
"summary": "SignMessageWithAddr returns the compact signature (base64 encoded) created\nwith the private key of the provided address. This requires the address\nto be solely based on a public key lock (no scripts). Obviously the internal\nlnd wallet has to possess the private key of the address otherwise\nan error is returned.",
|
||||||
|
"description": "This method aims to provide full compatibility with the bitcoin-core and\nbtcd implementation. Bitcoin-core's algorithm is not specified in a\nBIP and only applicable for legacy addresses. This method enhances the\nsigning for additional address types: P2WKH, NP2WKH, P2TR.\nFor P2TR addresses this represents a special case. ECDSA is used to create\na compact signature which makes the public key of the signature recoverable.",
|
||||||
|
"operationId": "WalletKit_SignMessageWithAddr",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/walletrpcSignMessageWithAddrResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "An unexpected error response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rpcStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/walletrpcSignMessageWithAddrRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"WalletKit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v2/wallet/address/verifymessage": {
|
||||||
|
"post": {
|
||||||
|
"summary": "VerifyMessageWithAddr returns the validity and the recovered public key of\nthe provided compact signature (base64 encoded). The verification is\ntwofold. First the validity of the signature itself is checked and then\nit is verified that the recovered public key of the signature equals\nthe public key of the provided address. There is no dependence on the\nprivate key of the address therefore also external addresses are allowed\nto verify signatures.\nSupported address types are P2PKH, P2WKH, NP2WKH, P2TR.",
|
||||||
|
"description": "This method is the counterpart of the related signing method\n(SignMessageWithAddr) and aims to provide full compatibility to\nbitcoin-core's implementation. Although bitcoin-core/btcd only provide\nthis functionality for legacy addresses this function enhances it to\nthe address types: P2PKH, P2WKH, NP2WKH, P2TR.\n\nThe verification for P2TR addresses is a special case and requires the\nECDSA compact signature to compare the reovered public key to the internal\ntaproot key. The compact ECDSA signature format was used because there\nare still no known compact signature schemes for schnorr signatures.",
|
||||||
|
"operationId": "WalletKit_VerifyMessageWithAddr",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/walletrpcVerifyMessageWithAddrResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "An unexpected error response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rpcStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/walletrpcVerifyMessageWithAddrRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"WalletKit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v2/wallet/addresses": {
|
"/v2/wallet/addresses": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "ListAddresses retrieves all the addresses along with their balance. An\naccount name filter can be provided to filter through all of the\nwallet accounts and return the addresses of only those matching.",
|
"summary": "ListAddresses retrieves all the addresses along with their balance. An\naccount name filter can be provided to filter through all of the\nwallet accounts and return the addresses of only those matching.",
|
||||||
|
@ -1714,6 +1782,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"walletrpcSignMessageWithAddrRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte",
|
||||||
|
"description": "The message to be signed. When using REST, this field must be encoded as\nbase64."
|
||||||
|
},
|
||||||
|
"addr": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The address which will be used to look up the private key and sign the\ncorresponding message."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"walletrpcSignMessageWithAddrResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"signature": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The compact ECDSA signature for the given message encoded in base64."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"walletrpcSignPsbtRequest": {
|
"walletrpcSignPsbtRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -1846,6 +1937,38 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"walletrpcVerifyMessageWithAddrRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte",
|
||||||
|
"description": "The message to be signed. When using REST, this field must be encoded as\nbase64."
|
||||||
|
},
|
||||||
|
"signature": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The compact ECDSA signature to be verified over the given message\necoded in base64."
|
||||||
|
},
|
||||||
|
"addr": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The address which will be used to look up the public key and verify the\nthe signature."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"walletrpcVerifyMessageWithAddrResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"valid": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the signature was valid over the given message."
|
||||||
|
},
|
||||||
|
"pubkey": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte",
|
||||||
|
"description": "The pubkey recovered from the signature."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"walletrpcWitnessType": {
|
"walletrpcWitnessType": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|
|
@ -59,9 +59,15 @@ http:
|
||||||
- selector: walletrpc.WalletKit.ListAccounts
|
- selector: walletrpc.WalletKit.ListAccounts
|
||||||
get: "/v2/wallet/accounts"
|
get: "/v2/wallet/accounts"
|
||||||
- selector: walletrpc.WalletKit.RequiredReserve
|
- selector: walletrpc.WalletKit.RequiredReserve
|
||||||
get: "/v2/wallet/reserve"
|
get: "/v2/wallet/reserve"
|
||||||
- selector: walletrpc.WalletKit.ListAddresses
|
- selector: walletrpc.WalletKit.ListAddresses
|
||||||
get: "/v2/wallet/addresses"
|
get: "/v2/wallet/addresses"
|
||||||
- selector: walletrpc.WalletKit.ImportAccount
|
- selector: walletrpc.WalletKit.ImportAccount
|
||||||
post: "/v2/wallet/accounts/import"
|
post: "/v2/wallet/accounts/import"
|
||||||
body: "*"
|
body: "*"
|
||||||
|
- selector: walletrpc.WalletKit.SignMessageWithAddr
|
||||||
|
post: "/v2/wallet/address/signmessage"
|
||||||
|
body: "*"
|
||||||
|
- selector: walletrpc.WalletKit.VerifyMessageWithAddr
|
||||||
|
post: "/v2/wallet/address/verifymessage"
|
||||||
|
body: "*"
|
||||||
|
|
|
@ -57,6 +57,39 @@ type WalletKitClient interface {
|
||||||
// account name filter can be provided to filter through all of the
|
// account name filter can be provided to filter through all of the
|
||||||
// wallet accounts and return the addresses of only those matching.
|
// wallet accounts and return the addresses of only those matching.
|
||||||
ListAddresses(ctx context.Context, in *ListAddressesRequest, opts ...grpc.CallOption) (*ListAddressesResponse, error)
|
ListAddresses(ctx context.Context, in *ListAddressesRequest, opts ...grpc.CallOption) (*ListAddressesResponse, error)
|
||||||
|
// SignMessageWithAddr returns the compact signature (base64 encoded) created
|
||||||
|
// with the private key of the provided address. This requires the address
|
||||||
|
// to be solely based on a public key lock (no scripts). Obviously the internal
|
||||||
|
// lnd wallet has to possess the private key of the address otherwise
|
||||||
|
// an error is returned.
|
||||||
|
//
|
||||||
|
// This method aims to provide full compatibility with the bitcoin-core and
|
||||||
|
// btcd implementation. Bitcoin-core's algorithm is not specified in a
|
||||||
|
// BIP and only applicable for legacy addresses. This method enhances the
|
||||||
|
// signing for additional address types: P2WKH, NP2WKH, P2TR.
|
||||||
|
// For P2TR addresses this represents a special case. ECDSA is used to create
|
||||||
|
// a compact signature which makes the public key of the signature recoverable.
|
||||||
|
SignMessageWithAddr(ctx context.Context, in *SignMessageWithAddrRequest, opts ...grpc.CallOption) (*SignMessageWithAddrResponse, error)
|
||||||
|
// VerifyMessageWithAddr returns the validity and the recovered public key of
|
||||||
|
// the provided compact signature (base64 encoded). The verification is
|
||||||
|
// twofold. First the validity of the signature itself is checked and then
|
||||||
|
// it is verified that the recovered public key of the signature equals
|
||||||
|
// the public key of the provided address. There is no dependence on the
|
||||||
|
// private key of the address therefore also external addresses are allowed
|
||||||
|
// to verify signatures.
|
||||||
|
// Supported address types are P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
//
|
||||||
|
// This method is the counterpart of the related signing method
|
||||||
|
// (SignMessageWithAddr) and aims to provide full compatibility to
|
||||||
|
// bitcoin-core's implementation. Although bitcoin-core/btcd only provide
|
||||||
|
// this functionality for legacy addresses this function enhances it to
|
||||||
|
// the address types: P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
//
|
||||||
|
// The verification for P2TR addresses is a special case and requires the
|
||||||
|
// ECDSA compact signature to compare the reovered public key to the internal
|
||||||
|
// taproot key. The compact ECDSA signature format was used because there
|
||||||
|
// are still no known compact signature schemes for schnorr signatures.
|
||||||
|
VerifyMessageWithAddr(ctx context.Context, in *VerifyMessageWithAddrRequest, opts ...grpc.CallOption) (*VerifyMessageWithAddrResponse, error)
|
||||||
// ImportAccount imports an account backed by an account extended public key.
|
// ImportAccount imports an account backed by an account extended public key.
|
||||||
// The master key fingerprint denotes the fingerprint of the root key
|
// The master key fingerprint denotes the fingerprint of the root key
|
||||||
// corresponding to the account public key (also known as the key with
|
// corresponding to the account public key (also known as the key with
|
||||||
|
@ -303,6 +336,24 @@ func (c *walletKitClient) ListAddresses(ctx context.Context, in *ListAddressesRe
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *walletKitClient) SignMessageWithAddr(ctx context.Context, in *SignMessageWithAddrRequest, opts ...grpc.CallOption) (*SignMessageWithAddrResponse, error) {
|
||||||
|
out := new(SignMessageWithAddrResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/SignMessageWithAddr", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *walletKitClient) VerifyMessageWithAddr(ctx context.Context, in *VerifyMessageWithAddrRequest, opts ...grpc.CallOption) (*VerifyMessageWithAddrResponse, error) {
|
||||||
|
out := new(VerifyMessageWithAddrResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/VerifyMessageWithAddr", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *walletKitClient) ImportAccount(ctx context.Context, in *ImportAccountRequest, opts ...grpc.CallOption) (*ImportAccountResponse, error) {
|
func (c *walletKitClient) ImportAccount(ctx context.Context, in *ImportAccountRequest, opts ...grpc.CallOption) (*ImportAccountResponse, error) {
|
||||||
out := new(ImportAccountResponse)
|
out := new(ImportAccountResponse)
|
||||||
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/ImportAccount", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/ImportAccount", in, out, opts...)
|
||||||
|
@ -462,6 +513,39 @@ type WalletKitServer interface {
|
||||||
// account name filter can be provided to filter through all of the
|
// account name filter can be provided to filter through all of the
|
||||||
// wallet accounts and return the addresses of only those matching.
|
// wallet accounts and return the addresses of only those matching.
|
||||||
ListAddresses(context.Context, *ListAddressesRequest) (*ListAddressesResponse, error)
|
ListAddresses(context.Context, *ListAddressesRequest) (*ListAddressesResponse, error)
|
||||||
|
// SignMessageWithAddr returns the compact signature (base64 encoded) created
|
||||||
|
// with the private key of the provided address. This requires the address
|
||||||
|
// to be solely based on a public key lock (no scripts). Obviously the internal
|
||||||
|
// lnd wallet has to possess the private key of the address otherwise
|
||||||
|
// an error is returned.
|
||||||
|
//
|
||||||
|
// This method aims to provide full compatibility with the bitcoin-core and
|
||||||
|
// btcd implementation. Bitcoin-core's algorithm is not specified in a
|
||||||
|
// BIP and only applicable for legacy addresses. This method enhances the
|
||||||
|
// signing for additional address types: P2WKH, NP2WKH, P2TR.
|
||||||
|
// For P2TR addresses this represents a special case. ECDSA is used to create
|
||||||
|
// a compact signature which makes the public key of the signature recoverable.
|
||||||
|
SignMessageWithAddr(context.Context, *SignMessageWithAddrRequest) (*SignMessageWithAddrResponse, error)
|
||||||
|
// VerifyMessageWithAddr returns the validity and the recovered public key of
|
||||||
|
// the provided compact signature (base64 encoded). The verification is
|
||||||
|
// twofold. First the validity of the signature itself is checked and then
|
||||||
|
// it is verified that the recovered public key of the signature equals
|
||||||
|
// the public key of the provided address. There is no dependence on the
|
||||||
|
// private key of the address therefore also external addresses are allowed
|
||||||
|
// to verify signatures.
|
||||||
|
// Supported address types are P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
//
|
||||||
|
// This method is the counterpart of the related signing method
|
||||||
|
// (SignMessageWithAddr) and aims to provide full compatibility to
|
||||||
|
// bitcoin-core's implementation. Although bitcoin-core/btcd only provide
|
||||||
|
// this functionality for legacy addresses this function enhances it to
|
||||||
|
// the address types: P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
//
|
||||||
|
// The verification for P2TR addresses is a special case and requires the
|
||||||
|
// ECDSA compact signature to compare the reovered public key to the internal
|
||||||
|
// taproot key. The compact ECDSA signature format was used because there
|
||||||
|
// are still no known compact signature schemes for schnorr signatures.
|
||||||
|
VerifyMessageWithAddr(context.Context, *VerifyMessageWithAddrRequest) (*VerifyMessageWithAddrResponse, error)
|
||||||
// ImportAccount imports an account backed by an account extended public key.
|
// ImportAccount imports an account backed by an account extended public key.
|
||||||
// The master key fingerprint denotes the fingerprint of the root key
|
// The master key fingerprint denotes the fingerprint of the root key
|
||||||
// corresponding to the account public key (also known as the key with
|
// corresponding to the account public key (also known as the key with
|
||||||
|
@ -645,6 +729,12 @@ func (UnimplementedWalletKitServer) RequiredReserve(context.Context, *RequiredRe
|
||||||
func (UnimplementedWalletKitServer) ListAddresses(context.Context, *ListAddressesRequest) (*ListAddressesResponse, error) {
|
func (UnimplementedWalletKitServer) ListAddresses(context.Context, *ListAddressesRequest) (*ListAddressesResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListAddresses not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListAddresses not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedWalletKitServer) SignMessageWithAddr(context.Context, *SignMessageWithAddrRequest) (*SignMessageWithAddrResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SignMessageWithAddr not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedWalletKitServer) VerifyMessageWithAddr(context.Context, *VerifyMessageWithAddrRequest) (*VerifyMessageWithAddrResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method VerifyMessageWithAddr not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedWalletKitServer) ImportAccount(context.Context, *ImportAccountRequest) (*ImportAccountResponse, error) {
|
func (UnimplementedWalletKitServer) ImportAccount(context.Context, *ImportAccountRequest) (*ImportAccountResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ImportAccount not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ImportAccount not implemented")
|
||||||
}
|
}
|
||||||
|
@ -877,6 +967,42 @@ func _WalletKit_ListAddresses_Handler(srv interface{}, ctx context.Context, dec
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _WalletKit_SignMessageWithAddr_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SignMessageWithAddrRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(WalletKitServer).SignMessageWithAddr(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/walletrpc.WalletKit/SignMessageWithAddr",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(WalletKitServer).SignMessageWithAddr(ctx, req.(*SignMessageWithAddrRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _WalletKit_VerifyMessageWithAddr_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(VerifyMessageWithAddrRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(WalletKitServer).VerifyMessageWithAddr(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/walletrpc.WalletKit/VerifyMessageWithAddr",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(WalletKitServer).VerifyMessageWithAddr(ctx, req.(*VerifyMessageWithAddrRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _WalletKit_ImportAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _WalletKit_ImportAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(ImportAccountRequest)
|
in := new(ImportAccountRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
|
@ -1158,6 +1284,14 @@ var WalletKit_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "ListAddresses",
|
MethodName: "ListAddresses",
|
||||||
Handler: _WalletKit_ListAddresses_Handler,
|
Handler: _WalletKit_ListAddresses_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SignMessageWithAddr",
|
||||||
|
Handler: _WalletKit_SignMessageWithAddr_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "VerifyMessageWithAddr",
|
||||||
|
Handler: _WalletKit_VerifyMessageWithAddr_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "ImportAccount",
|
MethodName: "ImportAccount",
|
||||||
Handler: _WalletKit_ImportAccount_Handler,
|
Handler: _WalletKit_ImportAccount_Handler,
|
||||||
|
|
|
@ -6,6 +6,7 @@ package walletrpc
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -17,6 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"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"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||||
|
@ -126,6 +128,14 @@ var (
|
||||||
Entity: "onchain",
|
Entity: "onchain",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
}},
|
}},
|
||||||
|
"/walletrpc.WalletKit/SignMessageWithAddr": {{
|
||||||
|
Entity: "onchain",
|
||||||
|
Action: "write",
|
||||||
|
}},
|
||||||
|
"/walletrpc.WalletKit/VerifyMessageWithAddr": {{
|
||||||
|
Entity: "onchain",
|
||||||
|
Action: "write",
|
||||||
|
}},
|
||||||
"/walletrpc.WalletKit/FundPsbt": {{
|
"/walletrpc.WalletKit/FundPsbt": {{
|
||||||
Entity: "onchain",
|
Entity: "onchain",
|
||||||
Action: "write",
|
Action: "write",
|
||||||
|
@ -1625,6 +1635,182 @@ func parseAddrType(addrType AddressType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// msgSignaturePrefix is a prefix used to prevent inadvertently signing a
|
||||||
|
// transaction or a signature. It is prepended in front of the message and
|
||||||
|
// follows the same standard as bitcoin core and btcd.
|
||||||
|
const msgSignaturePrefix = "Bitcoin Signed Message:\n"
|
||||||
|
|
||||||
|
// SignMessageWithAddr signs a message with the private key of the provided
|
||||||
|
// address. The address needs to belong to the lnd wallet.
|
||||||
|
func (w *WalletKit) SignMessageWithAddr(ctx context.Context,
|
||||||
|
req *SignMessageWithAddrRequest) (*SignMessageWithAddrResponse, error) {
|
||||||
|
|
||||||
|
addr, err := btcutil.DecodeAddress(req.Addr, w.cfg.ChainParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !addr.IsForNet(w.cfg.ChainParams) {
|
||||||
|
return nil, fmt.Errorf("encoded address is for "+
|
||||||
|
"the wrong network %s", req.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch address infos from own wallet and check whether it belongs
|
||||||
|
// to the lnd wallet.
|
||||||
|
managedAddr, err := w.cfg.Wallet.AddressInfo(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("address could not be found in the "+
|
||||||
|
"wallet database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifying by checking the interface type that the wallet knows about
|
||||||
|
// the public and private keys so it can sign the message with the
|
||||||
|
// private key of this address.
|
||||||
|
pubKey, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("private key to address is unknown")
|
||||||
|
}
|
||||||
|
|
||||||
|
digest, err := doubleHashMessage(msgSignaturePrefix, string(req.Msg))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// For all address types (P2WKH, NP2WKH,P2TR) the ECDSA compact signing
|
||||||
|
// algorithm is used. For P2TR addresses this represents a special case.
|
||||||
|
// ECDSA is used to create a compact signature which makes the public
|
||||||
|
// key of the signature recoverable. For Schnorr no known compact
|
||||||
|
// signing algorithm exists yet.
|
||||||
|
privKey, err := pubKey.PrivKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("no private key could be "+
|
||||||
|
"fetched from wallet database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigBytes, err := ecdsa.SignCompact(privKey, digest, pubKey.Compressed())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitcoin signatures are base64 encoded (being compatible with
|
||||||
|
// bitcoin-core and btcd).
|
||||||
|
sig := base64.StdEncoding.EncodeToString(sigBytes)
|
||||||
|
|
||||||
|
return &SignMessageWithAddrResponse{
|
||||||
|
Signature: sig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyMessageWithAddr verifies a signature on a message with a provided
|
||||||
|
// address, it checks both the validity of the signature itself and then
|
||||||
|
// verifies whether the signature corresponds to the public key of the
|
||||||
|
// provided address. There is no dependence on the private key of the address
|
||||||
|
// therefore also external addresses are allowed to verify signatures.
|
||||||
|
// Supported address types are P2PKH, P2WKH, NP2WKH, P2TR.
|
||||||
|
func (w *WalletKit) VerifyMessageWithAddr(ctx context.Context,
|
||||||
|
req *VerifyMessageWithAddrRequest) (*VerifyMessageWithAddrResponse,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
sig, err := base64.StdEncoding.DecodeString(req.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("malformed base64 encoding of "+
|
||||||
|
"the signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
digest, err := doubleHashMessage(msgSignaturePrefix, string(req.Msg))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, wasCompressed, err := ecdsa.RecoverCompact(sig, digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to recover public key "+
|
||||||
|
"from compact signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializedPubkey []byte
|
||||||
|
if wasCompressed {
|
||||||
|
serializedPubkey = pk.SerializeCompressed()
|
||||||
|
} else {
|
||||||
|
serializedPubkey = pk.SerializeUncompressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := btcutil.DecodeAddress(req.Addr, w.cfg.ChainParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !addr.IsForNet(w.cfg.ChainParams) {
|
||||||
|
return nil, fmt.Errorf("encoded address is for"+
|
||||||
|
"the wrong network %s", req.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
address btcutil.Address
|
||||||
|
pubKeyHash = btcutil.Hash160(serializedPubkey)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure the address is one of the supported types.
|
||||||
|
switch addr.(type) {
|
||||||
|
case *btcutil.AddressPubKeyHash:
|
||||||
|
address, err = btcutil.NewAddressPubKeyHash(
|
||||||
|
pubKeyHash, w.cfg.ChainParams,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *btcutil.AddressWitnessPubKeyHash:
|
||||||
|
address, err = btcutil.NewAddressWitnessPubKeyHash(
|
||||||
|
pubKeyHash, w.cfg.ChainParams,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *btcutil.AddressScriptHash:
|
||||||
|
// Check if address is a Nested P2WKH (NP2WKH).
|
||||||
|
address, err = btcutil.NewAddressWitnessPubKeyHash(
|
||||||
|
pubKeyHash, w.cfg.ChainParams,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
witnessScript, err := txscript.PayToAddrScript(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
address, err = btcutil.NewAddressScriptHashFromHash(
|
||||||
|
btcutil.Hash160(witnessScript), w.cfg.ChainParams,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *btcutil.AddressTaproot:
|
||||||
|
// Only addresses without a tapscript are allowed because
|
||||||
|
// the verification is using the internal key.
|
||||||
|
tapKey := txscript.ComputeTaprootKeyNoScript(pk)
|
||||||
|
address, err = btcutil.NewAddressTaproot(
|
||||||
|
schnorr.SerializePubKey(tapKey),
|
||||||
|
w.cfg.ChainParams,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported address type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &VerifyMessageWithAddrResponse{
|
||||||
|
Valid: req.Addr == address.EncodeAddress(),
|
||||||
|
Pubkey: serializedPubkey,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ImportAccount imports an account backed by an account extended public key.
|
// ImportAccount imports an account backed by an account extended public key.
|
||||||
// The master key fingerprint denotes the fingerprint of the root key
|
// The master key fingerprint denotes the fingerprint of the root key
|
||||||
// corresponding to the account public key (also known as the key with
|
// corresponding to the account public key (also known as the key with
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package walletrpc
|
package walletrpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,3 +77,22 @@ func parseDerivationPath(path string) ([]uint32, error) {
|
||||||
}
|
}
|
||||||
return indices, nil
|
return indices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// doubleHashMessage creates the double hash (sha256) of a message
|
||||||
|
// prepended with a specified prefix.
|
||||||
|
func doubleHashMessage(prefix string, msg string) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := wire.WriteVarString(&buf, 0, prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = wire.WriteVarString(&buf, 0, msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := chainhash.DoubleHashB(buf.Bytes())
|
||||||
|
|
||||||
|
return digest, nil
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ import (
|
||||||
// WalletKitClient related RPCs.
|
// WalletKitClient related RPCs.
|
||||||
// =====================
|
// =====================
|
||||||
|
|
||||||
|
type (
|
||||||
|
SignReq *walletrpc.SignMessageWithAddrResponse
|
||||||
|
VerifyResp *walletrpc.VerifyMessageWithAddrResponse
|
||||||
|
)
|
||||||
|
|
||||||
// FinalizePsbt makes a RPC call to node's ListUnspent and asserts.
|
// FinalizePsbt makes a RPC call to node's ListUnspent and asserts.
|
||||||
func (h *HarnessRPC) ListUnspent(
|
func (h *HarnessRPC) ListUnspent(
|
||||||
req *walletrpc.ListUnspentRequest) *walletrpc.ListUnspentResponse {
|
req *walletrpc.ListUnspentRequest) *walletrpc.ListUnspentResponse {
|
||||||
|
@ -125,6 +130,33 @@ func (h *HarnessRPC) ListAddresses(
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignMessageWithAddr makes a RPC call to the SignMessageWithAddr and asserts.
|
||||||
|
func (h *HarnessRPC) SignMessageWithAddr(
|
||||||
|
req *walletrpc.SignMessageWithAddrRequest) SignReq {
|
||||||
|
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
key, err := h.WalletKit.SignMessageWithAddr(ctxt, req)
|
||||||
|
h.NoError(err, "SignMessageWithAddr")
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyMessageWithAddr makes a RPC call to
|
||||||
|
// the VerifyMessageWithAddr and asserts.
|
||||||
|
func (h *HarnessRPC) VerifyMessageWithAddr(
|
||||||
|
req *walletrpc.VerifyMessageWithAddrRequest) VerifyResp {
|
||||||
|
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
key, err := h.WalletKit.VerifyMessageWithAddr(ctxt, req)
|
||||||
|
h.NoError(err, "VerifyMessageWithAddr")
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
// ListSweeps makes a ListSweeps RPC call to the node's WalletKit client.
|
// ListSweeps makes a ListSweeps RPC call to the node's WalletKit client.
|
||||||
func (h *HarnessRPC) ListSweeps(verbose bool) *walletrpc.ListSweepsResponse {
|
func (h *HarnessRPC) ListSweeps(verbose bool) *walletrpc.ListSweepsResponse {
|
||||||
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
|
|
@ -501,4 +501,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
Name: "custom message",
|
Name: "custom message",
|
||||||
TestFunc: testCustomMessage,
|
TestFunc: testCustomMessage,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "sign verify message with addr",
|
||||||
|
TestFunc: testSignVerifyMessageWithAddr,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1073,3 +1073,120 @@ func assertChannelConstraintsEqual(ht *lntemp.HarnessTest,
|
||||||
require.Equal(ht, want.MaxAcceptedHtlcs, got.MaxAcceptedHtlcs,
|
require.Equal(ht, want.MaxAcceptedHtlcs, got.MaxAcceptedHtlcs,
|
||||||
"MaxAcceptedHtlcs mismatched")
|
"MaxAcceptedHtlcs mismatched")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testSignVerifyMessageWithAddr tests signing and also verifying a signature
|
||||||
|
// on a message with a provided address.
|
||||||
|
func testSignVerifyMessageWithAddr(ht *lntemp.HarnessTest) {
|
||||||
|
// Using different nodes to sign the message and verify the signature.
|
||||||
|
alice, bob := ht.Alice, ht.Bob
|
||||||
|
|
||||||
|
// Test an lnd wallet created P2WKH address.
|
||||||
|
respAddr := alice.RPC.NewAddress(&lnrpc.NewAddressRequest{
|
||||||
|
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH,
|
||||||
|
})
|
||||||
|
|
||||||
|
aliceMsg := []byte("alice msg")
|
||||||
|
|
||||||
|
respSig := alice.RPC.SignMessageWithAddr(
|
||||||
|
&walletrpc.SignMessageWithAddrRequest{
|
||||||
|
Msg: aliceMsg,
|
||||||
|
Addr: respAddr.Address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
respValid := bob.RPC.VerifyMessageWithAddr(
|
||||||
|
&walletrpc.VerifyMessageWithAddrRequest{
|
||||||
|
Msg: aliceMsg,
|
||||||
|
Signature: respSig.Signature,
|
||||||
|
Addr: respAddr.Address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.True(ht, respValid.Valid, "alice's signature didn't validate")
|
||||||
|
|
||||||
|
// Test an lnd wallet created NP2WKH address.
|
||||||
|
respAddr = alice.RPC.NewAddress(&lnrpc.NewAddressRequest{
|
||||||
|
Type: lnrpc.AddressType_NESTED_PUBKEY_HASH,
|
||||||
|
})
|
||||||
|
|
||||||
|
respSig = alice.RPC.SignMessageWithAddr(
|
||||||
|
&walletrpc.SignMessageWithAddrRequest{
|
||||||
|
Msg: aliceMsg,
|
||||||
|
Addr: respAddr.Address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
respValid = bob.RPC.VerifyMessageWithAddr(
|
||||||
|
&walletrpc.VerifyMessageWithAddrRequest{
|
||||||
|
Msg: aliceMsg,
|
||||||
|
Signature: respSig.Signature,
|
||||||
|
Addr: respAddr.Address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.True(ht, respValid.Valid, "alice's signature didn't validate")
|
||||||
|
|
||||||
|
// Test an lnd wallet created P2TR address.
|
||||||
|
respAddr = alice.RPC.NewAddress(&lnrpc.NewAddressRequest{
|
||||||
|
Type: lnrpc.AddressType_TAPROOT_PUBKEY,
|
||||||
|
})
|
||||||
|
|
||||||
|
respSig = alice.RPC.SignMessageWithAddr(
|
||||||
|
&walletrpc.SignMessageWithAddrRequest{
|
||||||
|
Msg: aliceMsg,
|
||||||
|
Addr: respAddr.Address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
respValid = bob.RPC.VerifyMessageWithAddr(
|
||||||
|
&walletrpc.VerifyMessageWithAddrRequest{
|
||||||
|
Msg: aliceMsg,
|
||||||
|
Signature: respSig.Signature,
|
||||||
|
Addr: respAddr.Address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.True(ht, respValid.Valid, "alice's signature didn't validate")
|
||||||
|
|
||||||
|
// Test verifying a signature with an external P2PKH address.
|
||||||
|
// P2PKH address type is not supported by the lnd wallet therefore
|
||||||
|
// using an external source (bitcoin-core) for address and
|
||||||
|
// signature creation.
|
||||||
|
externalMsg := []byte("external msg")
|
||||||
|
externalAddr := "msS5c4VihSiJ64QzvMMEmWh6rYBnuWo2xH"
|
||||||
|
|
||||||
|
// Base64 encoded signature created with bitcoin-core regtest.
|
||||||
|
externalSig := "H5DqqM7Cc8xZnYBr7j3gD4XD+AuQsim9Un/IxBrrhBA7I9//" +
|
||||||
|
"3exuQRg+u7HpwG65yobPsew6RMUteyuxyNkLF5E="
|
||||||
|
|
||||||
|
respValid = alice.RPC.VerifyMessageWithAddr(
|
||||||
|
&walletrpc.VerifyMessageWithAddrRequest{
|
||||||
|
Msg: externalMsg,
|
||||||
|
Signature: externalSig,
|
||||||
|
Addr: externalAddr,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.True(ht, respValid.Valid, "external signature didn't validate")
|
||||||
|
|
||||||
|
// Test verifying a signature with a different address which
|
||||||
|
// initially was used to create the following signature.
|
||||||
|
// externalAddr is a valid legacy P2PKH bitcoin address created
|
||||||
|
// with bitcoin-core.
|
||||||
|
externalAddr = "mugbg8CqFe9CbdrYjFTkMhmL3JxuEXkNbY"
|
||||||
|
|
||||||
|
// Base64 encoded signature created with bitcoin-core regtest but with
|
||||||
|
// the address msS5c4VihSiJ64QzvMMEmWh6rYBnuWo2xH.
|
||||||
|
externalSig = "H5DqqM7Cc8xZnYBr7j3gD4XD+AuQsim9Un/IxBrrhBA7I9//" +
|
||||||
|
"3exuQRg+u7HpwG65yobPsew6RMUteyuxyNkLF5E="
|
||||||
|
|
||||||
|
respValid = alice.RPC.VerifyMessageWithAddr(
|
||||||
|
&walletrpc.VerifyMessageWithAddrRequest{
|
||||||
|
Msg: externalMsg,
|
||||||
|
Signature: externalSig,
|
||||||
|
Addr: externalAddr,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.False(ht, respValid.Valid, "external signature did validate")
|
||||||
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// waddrmgrNamespaceKey is the namespace key that the waddrmgr state is
|
// waddrmgrNamespaceKey is the namespace key that the waddrmgr state is
|
||||||
// stored within the top-level waleltdb buckets of btcwallet.
|
// stored within the top-level walletdb buckets of btcwallet.
|
||||||
waddrmgrNamespaceKey = []byte("waddrmgr")
|
waddrmgrNamespaceKey = []byte("waddrmgr")
|
||||||
|
|
||||||
// wtxmgrNamespaceKey is the namespace key that the wtxmgr state is
|
// wtxmgrNamespaceKey is the namespace key that the wtxmgr state is
|
||||||
|
|
Loading…
Add table
Reference in a new issue