From bab526f655cc7a80bc785a236571d95cef4f0bb5 Mon Sep 17 00:00:00 2001 From: ffranr Date: Thu, 1 Dec 2022 17:30:00 +0000 Subject: [PATCH] cmd: add chain subcommand Chain subcommand includes the commands: getblock, getbestblock, and getblockhash. This commit removes conflicting neutrino cli commands. --- cmd/lncli/chainrpc_active.go | 219 ++++++++++++++++++++++++++++++++++ cmd/lncli/chainrpc_default.go | 11 ++ cmd/lncli/main.go | 1 + cmd/lncli/neutrino_active.go | 81 ------------- go.mod | 2 +- go.sum | 2 + 6 files changed, 234 insertions(+), 82 deletions(-) create mode 100644 cmd/lncli/chainrpc_active.go create mode 100644 cmd/lncli/chainrpc_default.go diff --git a/cmd/lncli/chainrpc_active.go b/cmd/lncli/chainrpc_active.go new file mode 100644 index 000000000..0d8b891ac --- /dev/null +++ b/cmd/lncli/chainrpc_active.go @@ -0,0 +1,219 @@ +//go:build chainrpc +// +build chainrpc + +package main + +import ( + "bytes" + "fmt" + "strconv" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/lnrpc/chainrpc" + "github.com/urfave/cli" +) + +// chainCommands will return the set of commands to enable for chainrpc builds. +func chainCommands() []cli.Command { + return []cli.Command{ + { + Name: "chain", + Category: "On-chain", + Usage: "Interact with the bitcoin blockchain.", + Subcommands: []cli.Command{ + getBlockCommand, + getBestBlockCommand, + getBlockHashCommand, + }, + }, + } +} + +func getChainClient(ctx *cli.Context) (chainrpc.ChainKitClient, func()) { + conn := getClientConn(ctx, false) + + cleanUp := func() { + conn.Close() + } + + return chainrpc.NewChainKitClient(conn), cleanUp +} + +var getBlockCommand = cli.Command{ + Name: "getblock", + Category: "On-chain", + Usage: "Get block by block hash.", + Description: "Returns a block given the corresponding block hash.", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "hash", + Usage: "the target block hash", + }, + cli.BoolFlag{ + Name: "verbose", + Usage: "print entire block as JSON", + }, + }, + Action: actionDecorator(getBlock), +} + +func getBlock(ctx *cli.Context) error { + ctxc := getContext() + + var ( + args = ctx.Args() + blockHashString string + ) + + verbose := false + if ctx.IsSet("verbose") { + verbose = true + } + + switch { + case ctx.IsSet("hash"): + blockHashString = ctx.String("hash") + + case args.Present(): + blockHashString = args.First() + + default: + return fmt.Errorf("hash argument missing") + } + + blockHash, err := chainhash.NewHashFromStr(blockHashString) + if err != nil { + return err + } + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + req := &chainrpc.GetBlockRequest{BlockHash: blockHash.CloneBytes()} + resp, err := client.GetBlock(ctxc, req) + if err != nil { + return err + } + + // Convert raw block bytes into wire.MsgBlock. + msgBlock := &wire.MsgBlock{} + blockReader := bytes.NewReader(resp.RawBlock) + err = msgBlock.Deserialize(blockReader) + if err != nil { + return err + } + + if verbose { + printJSON(msgBlock) + } else { + printJSON(msgBlock.Header) + } + + return nil +} + +var getBestBlockCommand = cli.Command{ + Name: "getbestblock", + Category: "On-chain", + Usage: "Get best block.", + Description: "Returns the latest block hash and height from the " + + "valid most-work chain.", + Action: actionDecorator(getBestBlock), +} + +func getBestBlock(ctx *cli.Context) error { + ctxc := getContext() + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + resp, err := client.GetBestBlock(ctxc, &chainrpc.GetBestBlockRequest{}) + if err != nil { + return err + } + + // Cast gRPC block hash bytes as chain hash type. + var blockHash chainhash.Hash + copy(blockHash[:], resp.BlockHash) + + printJSON(struct { + BlockHash chainhash.Hash `json:"block_hash"` + BlockHeight int32 `json:"block_height"` + }{ + BlockHash: blockHash, + BlockHeight: resp.BlockHeight, + }) + + return nil +} + +var getBlockHashCommand = cli.Command{ + Name: "getblockhash", + Category: "On-chain", + Usage: "Get block hash by block height.", + Description: "Returns the block hash from the best chain at a given " + + "height.", + Flags: []cli.Flag{ + cli.Int64Flag{ + Name: "height", + Usage: "target block height", + }, + }, + Action: actionDecorator(getBlockHash), +} + +func getBlockHash(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()+ctx.NumFlags() != 1 { + return cli.ShowCommandHelp(ctx, "getblockhash") + } + + var ( + args = ctx.Args() + blockHeight int64 + ) + + switch { + case ctx.IsSet("height"): + blockHeight = ctx.Int64("height") + + case args.Present(): + blockHeightString := args.First() + + // Convert block height positional argument from string to + // int64. + var err error + blockHeight, err = strconv.ParseInt(blockHeightString, 10, 64) + if err != nil { + return err + } + + default: + return fmt.Errorf("block height argument missing") + } + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + req := &chainrpc.GetBlockHashRequest{BlockHeight: blockHeight} + resp, err := client.GetBlockHash(ctxc, req) + if err != nil { + return err + } + + // Cast gRPC block hash bytes as chain hash type. + var blockHash chainhash.Hash + copy(blockHash[:], resp.BlockHash) + + printJSON(struct { + BlockHash chainhash.Hash `json:"block_hash"` + }{ + BlockHash: blockHash, + }) + + return nil +} diff --git a/cmd/lncli/chainrpc_default.go b/cmd/lncli/chainrpc_default.go new file mode 100644 index 000000000..fa1ea99e2 --- /dev/null +++ b/cmd/lncli/chainrpc_default.go @@ -0,0 +1,11 @@ +//go:build !chainrpc +// +build !chainrpc + +package main + +import "github.com/urfave/cli" + +// chainCommands will return nil for non-chainrpc builds. +func chainCommands() []cli.Command { + return nil +} diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go index 116a42b13..4783ec06d 100644 --- a/cmd/lncli/main.go +++ b/cmd/lncli/main.go @@ -500,6 +500,7 @@ func main() { app.Commands = append(app.Commands, wtclientCommands()...) app.Commands = append(app.Commands, devCommands()...) app.Commands = append(app.Commands, peersCommands()...) + app.Commands = append(app.Commands, chainCommands()...) if err := app.Run(os.Args); err != nil { fatal(err) diff --git a/cmd/lncli/neutrino_active.go b/cmd/lncli/neutrino_active.go index 093821a3d..4267738e1 100644 --- a/cmd/lncli/neutrino_active.go +++ b/cmd/lncli/neutrino_active.go @@ -4,8 +4,6 @@ package main import ( - "strconv" - "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc" "github.com/urfave/cli" ) @@ -193,42 +191,6 @@ func getBlockHeader(ctx *cli.Context) error { return nil } -var getBlockCommand = cli.Command{ - Name: "getblock", - Usage: "Get a block.", - Category: "Neutrino", - Description: "Returns a block with a particular block hash.", - ArgsUsage: "hash", - Action: actionDecorator(getBlock), -} - -func getBlock(ctx *cli.Context) error { - ctxc := getContext() - args := ctx.Args() - - // Display the command's help message if we do not have the expected - // number of arguments/flags. - if !args.Present() { - return cli.ShowCommandHelp(ctx, "getblock") - } - - client, cleanUp := getNeutrinoKitClient(ctx) - defer cleanUp() - - req := &neutrinorpc.GetBlockRequest{ - Hash: args.First(), - } - - resp, err := client.GetBlock(ctxc, req) - if err != nil { - return err - } - - printRespJSON(resp) - - return nil -} - var getCFilterCommand = cli.Command{ Name: "getcfilter", Usage: "Get a compact filter.", @@ -263,47 +225,6 @@ func getCFilter(ctx *cli.Context) error { return nil } -var getBlockHashCommand = cli.Command{ - Name: "getblockhash", - Usage: "Get a block hash.", - Category: "Neutrino", - Description: "Returns the header hash of a block at a given height.", - ArgsUsage: "height", - Action: actionDecorator(getBlockHash), -} - -func getBlockHash(ctx *cli.Context) error { - ctxc := getContext() - args := ctx.Args() - - // Display the command's help message if we do not have the expected - // number of arguments/flags. - if !args.Present() { - return cli.ShowCommandHelp(ctx, "getblockhash") - } - - client, cleanUp := getNeutrinoKitClient(ctx) - defer cleanUp() - - height, err := strconv.ParseInt(args.First(), 10, 32) - if err != nil { - return err - } - - req := &neutrinorpc.GetBlockHashRequest{ - Height: int32(height), - } - - resp, err := client.GetBlockHash(ctxc, req) - if err != nil { - return err - } - - printRespJSON(resp) - - return nil -} - // neutrinoCommands will return the set of commands to enable for neutrinorpc // builds. func neutrinoCommands() []cli.Command { @@ -318,10 +239,8 @@ func neutrinoCommands() []cli.Command { addPeerCommand, disconnectPeerCommand, isBannedCommand, - getBlockCommand, getBlockHeaderCommand, getCFilterCommand, - getBlockHashCommand, }, }, } diff --git a/go.mod b/go.mod index 689eb764b..048f0de24 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.2.2 github.com/btcsuite/btcd/btcutil v1.1.3 github.com/btcsuite/btcd/btcutil/psbt v1.1.5 - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/btcwallet v0.16.6-0.20221203002441-6c7480c8a46b github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 diff --git a/go.sum b/go.sum index 023e5fd69..1c4856482 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,8 @@ github.com/btcsuite/btcd/btcutil/psbt v1.1.5/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=