2015-12-30 03:31:03 +01:00
package main
import (
2015-12-30 03:58:58 +01:00
"bytes"
2016-07-13 02:47:24 +02:00
"encoding/hex"
2015-12-30 03:31:03 +01:00
"encoding/json"
2016-04-25 05:27:19 +02:00
"fmt"
2016-07-08 00:35:06 +02:00
"io"
2017-01-24 05:32:17 +01:00
"io/ioutil"
2017-01-25 03:07:15 +01:00
"math"
2015-12-30 03:58:58 +01:00
"os"
2017-01-24 05:32:17 +01:00
"os/exec"
"strconv"
2016-06-21 21:35:07 +02:00
"strings"
2016-12-27 06:52:15 +01:00
2017-01-24 05:32:17 +01:00
"github.com/awalterschulze/gographviz"
2017-01-29 23:56:31 +01:00
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
2016-01-16 19:45:54 +01:00
"github.com/lightningnetwork/lnd/lnrpc"
2017-01-05 22:56:27 +01:00
"github.com/roasbeef/btcd/chaincfg/chainhash"
2017-01-24 05:32:17 +01:00
"github.com/roasbeef/btcutil"
2016-07-26 19:42:35 +02:00
"github.com/urfave/cli"
2017-10-12 11:42:06 +02:00
"golang.org/x/crypto/ssh/terminal"
2015-12-30 03:31:03 +01:00
"golang.org/x/net/context"
2017-11-06 23:34:49 +01:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2015-12-30 03:31:03 +01:00
)
2016-06-21 21:35:07 +02:00
// TODO(roasbeef): cli logic for supporting both positional and unix style
// arguments.
2017-11-23 20:40:14 +01:00
// TODO(roasbeef): expose all fee conf targets
2017-02-23 20:56:47 +01:00
func printJSON ( resp interface { } ) {
2015-12-30 03:58:58 +01:00
b , err := json . Marshal ( resp )
if err != nil {
fatal ( err )
}
var out bytes . Buffer
json . Indent ( & out , b , "" , "\t" )
2017-03-16 20:06:12 +01:00
out . WriteString ( "\n" )
2015-12-30 03:58:58 +01:00
out . WriteTo ( os . Stdout )
}
2017-02-23 20:56:47 +01:00
func printRespJSON ( resp proto . Message ) {
2017-01-29 23:56:31 +01:00
jsonMarshaler := & jsonpb . Marshaler {
EmitDefaults : true ,
Indent : " " ,
}
jsonStr , err := jsonMarshaler . MarshalToString ( resp )
if err != nil {
fmt . Println ( "unable to decode response: " , err )
return
}
fmt . Println ( jsonStr )
}
2017-11-06 23:34:49 +01:00
// actionDecorator is used to add additional information and error handling
// to command actions.
func actionDecorator ( f func ( * cli . Context ) error ) func ( * cli . Context ) error {
return func ( c * cli . Context ) error {
if err := f ( c ) ; err != nil {
// lnd might be active, but not possible to contact
// using RPC if the wallet is encrypted. If we get
// error code Unimplemented, it means that lnd is
// running, but the RPC server is not active yet (only
// WalletUnlocker server active) and most likely this
// is because of an encrypted wallet.
s , ok := status . FromError ( err )
if ok && s . Code ( ) == codes . Unimplemented {
return fmt . Errorf ( "Wallet is encrypted. " +
"Please unlock using 'lncli unlock', " +
"or set password using 'lncli create'" +
" if this is the first time starting " +
"lnd." )
}
return err
}
return nil
}
}
2017-02-24 14:32:33 +01:00
var newAddressCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "newaddress" ,
Usage : "generates a new address." ,
ArgsUsage : "address-type" ,
2017-11-23 20:40:14 +01:00
Description : `
Generate a wallet new address . Address - types has to be one of :
- p2wkh : Push to witness key hash
- np2wkh : Push to nested witness key hash
- p2pkh : Push to public key hash ( can ' t be used to fund channels ) ` ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( newAddress ) ,
2015-12-30 03:31:03 +01:00
}
2016-06-29 22:01:08 +02:00
func newAddress ( ctx * cli . Context ) error {
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2015-12-30 03:31:03 +01:00
2017-03-02 23:23:16 +01:00
stringAddrType := ctx . Args ( ) . First ( )
2016-04-25 05:27:19 +02:00
// Map the string encoded address type, to the concrete typed address
// type enum. An unrecognized address type will result in an error.
var addrType lnrpc . NewAddressRequest_AddressType
switch stringAddrType { // TODO(roasbeef): make them ints on the cli?
case "p2wkh" :
addrType = lnrpc . NewAddressRequest_WITNESS_PUBKEY_HASH
case "np2wkh" :
addrType = lnrpc . NewAddressRequest_NESTED_PUBKEY_HASH
case "p2pkh" :
addrType = lnrpc . NewAddressRequest_PUBKEY_HASH
default :
2016-06-29 22:01:08 +02:00
return fmt . Errorf ( "invalid address type %v, support address type " +
"are: p2wkh, np2wkh, p2pkh" , stringAddrType )
2016-04-25 05:27:19 +02:00
}
2015-12-30 03:31:03 +01:00
ctxb := context . Background ( )
2016-04-25 05:27:19 +02:00
addr , err := client . NewAddress ( ctxb , & lnrpc . NewAddressRequest {
Type : addrType ,
} )
2015-12-30 03:31:03 +01:00
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2015-12-30 03:31:03 +01:00
}
2017-02-23 20:56:47 +01:00
printRespJSON ( addr )
2016-06-29 22:01:08 +02:00
return nil
2015-12-30 03:31:03 +01:00
}
2017-02-24 14:32:33 +01:00
var sendCoinsCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "sendcoins" ,
Usage : "send bitcoin on-chain to an address" ,
ArgsUsage : "addr amt" ,
2017-11-23 20:40:14 +01:00
Description : `
Send amt coins in satoshis to the BASE58 encoded bitcoin address addr .
Fees used when sending the transaction can be specified via the -- conf_target , or
-- sat_per_byte optional flags .
Positional arguments and flags can be used interchangeably but not at the same time !
` ,
2016-06-29 20:29:21 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "addr" ,
2017-03-02 23:23:16 +01:00
Usage : "the BASE58 encoded bitcoin address to send coins to on-chain" ,
2016-06-29 20:29:21 +02:00
} ,
// TODO(roasbeef): switch to BTC on command line? int may not be sufficient
2017-03-02 23:23:16 +01:00
cli . Int64Flag {
2016-06-29 20:29:21 +02:00
Name : "amt" ,
Usage : "the number of bitcoin denominated in satoshis to send" ,
} ,
2017-11-23 20:40:14 +01:00
cli . Int64Flag {
Name : "conf_target" ,
Usage : "(optional) the number of blocks that the " +
"transaction *should* confirm in, will be " +
"used for fee estimation" ,
} ,
cli . Int64Flag {
Name : "sat_per_byte" ,
Usage : "(optional) a manual fee expressed in " +
"sat/byte that should be used when crafting " +
"the transaction" ,
} ,
2016-06-29 20:29:21 +02:00
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( sendCoins ) ,
2016-06-29 20:29:21 +02:00
}
2016-06-29 22:01:08 +02:00
func sendCoins ( ctx * cli . Context ) error {
2017-03-02 23:23:16 +01:00
var (
addr string
amt int64
err error
)
args := ctx . Args ( )
if ctx . NArg ( ) == 0 && ctx . NumFlags ( ) == 0 {
cli . ShowCommandHelp ( ctx , "sendcoins" )
return nil
}
2017-11-23 20:40:14 +01:00
if ctx . IsSet ( "conf_target" ) && ctx . IsSet ( "sat_per_byte" ) {
return fmt . Errorf ( "either conf_target or sat_per_byte should be " +
"set, but not both" )
}
2017-03-02 23:23:16 +01:00
switch {
case ctx . IsSet ( "addr" ) :
addr = ctx . String ( "addr" )
case args . Present ( ) :
addr = args . First ( )
args = args . Tail ( )
default :
return fmt . Errorf ( "Address argument missing" )
}
switch {
case ctx . IsSet ( "amt" ) :
amt = ctx . Int64 ( "amt" )
case args . Present ( ) :
amt , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
default :
return fmt . Errorf ( "Amount argument missing" )
}
if err != nil {
return fmt . Errorf ( "unable to decode amount: %v" , err )
}
2016-06-29 20:29:21 +02:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-06-29 20:29:21 +02:00
req := & lnrpc . SendCoinsRequest {
2017-11-23 20:40:14 +01:00
Addr : addr ,
Amount : amt ,
TargetConf : int32 ( ctx . Int64 ( "conf_target" ) ) ,
SatPerByte : ctx . Int64 ( "sat_per_byte" ) ,
2016-06-29 20:29:21 +02:00
}
txid , err := client . SendCoins ( ctxb , req )
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2016-06-29 20:29:21 +02:00
}
2017-02-23 20:56:47 +01:00
printRespJSON ( txid )
2016-06-29 22:01:08 +02:00
return nil
2016-06-29 20:29:21 +02:00
}
2017-02-24 14:32:33 +01:00
var sendManyCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "sendmany" ,
Usage : "send bitcoin on-chain to multiple addresses." ,
2017-11-23 20:40:14 +01:00
ArgsUsage : "send-json-string [--conf_target=N] [--sat_per_byte=P]" ,
Description : `
Create and broadcast a transaction paying the specified amount ( s ) to the passed address ( es ) .
The send - json - string ' param decodes addresses and the amount to send
respectively in the following format :
' { "ExampleAddr" : NumCoinsInSatoshis , "SecondAddr" : NumCoins } '
` ,
Flags : [ ] cli . Flag {
cli . Int64Flag {
Name : "conf_target" ,
Usage : "(optional) the number of blocks that the transaction *should* " +
"confirm in, will be used for fee estimation" ,
} ,
cli . Int64Flag {
Name : "sat_per_byte" ,
Usage : "(optional) a manual fee expressed in sat/byte that should be " +
"used when crafting the transaction" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( sendMany ) ,
2015-12-30 03:31:03 +01:00
}
2016-06-29 22:01:08 +02:00
func sendMany ( ctx * cli . Context ) error {
2015-12-30 03:31:03 +01:00
var amountToAddr map [ string ] int64
2017-03-02 23:23:16 +01:00
jsonMap := ctx . Args ( ) . First ( )
2015-12-30 03:31:03 +01:00
if err := json . Unmarshal ( [ ] byte ( jsonMap ) , & amountToAddr ) ; err != nil {
2016-06-29 22:01:08 +02:00
return err
2015-12-30 03:31:03 +01:00
}
2017-11-23 20:40:14 +01:00
if ctx . IsSet ( "conf_target" ) && ctx . IsSet ( "sat_per_byte" ) {
return fmt . Errorf ( "either conf_target or sat_per_byte should be " +
"set, but not both" )
}
2015-12-30 03:31:03 +01:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2015-12-30 03:31:03 +01:00
2017-02-23 20:09:34 +01:00
txid , err := client . SendMany ( ctxb , & lnrpc . SendManyRequest {
AddrToAmount : amountToAddr ,
2017-11-23 20:40:14 +01:00
TargetConf : int32 ( ctx . Int64 ( "conf_target" ) ) ,
SatPerByte : ctx . Int64 ( "sat_per_byte" ) ,
2017-02-23 20:09:34 +01:00
} )
2015-12-30 03:31:03 +01:00
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2015-12-30 03:31:03 +01:00
}
2017-02-23 20:56:47 +01:00
printRespJSON ( txid )
2016-06-29 22:01:08 +02:00
return nil
2015-12-30 03:31:03 +01:00
}
2016-01-17 04:10:29 +01:00
2017-02-24 14:32:33 +01:00
var connectCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "connect" ,
Usage : "connect to a remote lnd peer" ,
ArgsUsage : "<pubkey>@host" ,
2017-01-10 04:09:45 +01:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : "perm" ,
2017-03-02 23:23:16 +01:00
Usage : "If set, the daemon will attempt to persistently " +
"connect to the target peer.\n" +
" If not, the call will be synchronous." ,
2017-01-10 04:09:45 +01:00
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( connectPeer ) ,
2016-01-17 04:10:29 +01:00
}
2016-06-29 22:01:08 +02:00
func connectPeer ( ctx * cli . Context ) error {
2016-01-17 04:10:29 +01:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-01-17 04:10:29 +01:00
2017-03-02 23:23:16 +01:00
targetAddress := ctx . Args ( ) . First ( )
2016-06-21 21:35:07 +02:00
splitAddr := strings . Split ( targetAddress , "@" )
2016-07-17 02:43:27 +02:00
if len ( splitAddr ) != 2 {
2016-10-28 04:42:47 +02:00
return fmt . Errorf ( "target address expected in format: " +
"pubkey@host:port" )
2016-07-17 02:43:27 +02:00
}
2016-06-21 21:35:07 +02:00
addr := & lnrpc . LightningAddress {
2016-10-28 04:42:47 +02:00
Pubkey : splitAddr [ 0 ] ,
Host : splitAddr [ 1 ] ,
2016-06-21 21:35:07 +02:00
}
2017-01-10 04:09:45 +01:00
req := & lnrpc . ConnectPeerRequest {
Addr : addr ,
Perm : ctx . Bool ( "perm" ) ,
}
2016-01-17 04:10:29 +01:00
lnid , err := client . ConnectPeer ( ctxb , req )
2017-05-05 14:05:30 +02:00
if err != nil {
return err
}
printRespJSON ( lnid )
return nil
}
var disconnectCommand = cli . Command {
Name : "disconnect" ,
Usage : "disconnect a remote lnd peer identified by public key" ,
ArgsUsage : "<pubkey>" ,
2017-05-06 00:54:25 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "node_key" ,
Usage : "The hex-encoded compressed public key of the peer " +
"to disconnect from" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( disconnectPeer ) ,
2017-05-05 14:05:30 +02:00
}
func disconnectPeer ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2017-05-06 00:54:25 +02:00
var pubKey string
switch {
case ctx . IsSet ( "node_key" ) :
pubKey = ctx . String ( "node_key" )
case ctx . Args ( ) . Present ( ) :
pubKey = ctx . Args ( ) . First ( )
default :
return fmt . Errorf ( "must specify target public key" )
2017-05-05 14:05:30 +02:00
}
req := & lnrpc . DisconnectPeerRequest {
PubKey : pubKey ,
}
lnid , err := client . DisconnectPeer ( ctxb , req )
2016-01-17 04:10:29 +01:00
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2016-01-17 04:10:29 +01:00
}
2017-02-23 20:56:47 +01:00
printRespJSON ( lnid )
2016-06-29 22:01:08 +02:00
return nil
2016-01-17 04:10:29 +01:00
}
2016-06-21 21:35:07 +02:00
2017-03-02 23:23:16 +01:00
// TODO(roasbeef): change default number of confirmations
2017-02-24 14:32:33 +01:00
var openChannelCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "openchannel" ,
Usage : "Open a channel to an existing peer." ,
2017-11-23 20:40:14 +01:00
Description : `
Attempt to open a new channel to an existing peer with the key node - key
optionally blocking until the channel is ' open ' .
The channel will be initialized with local - amt satoshis local and push - amt
satoshis for the remote node . Once the channel is open , a channelPoint ( txid : vout )
of the funding output is returned .
One can manually set the fee to be used for the funding transaction via either
the -- conf_target or -- sat_per_byte arguments . This is optional .
NOTE : peer_id and node_key are mutually exclusive , only one should be used , not both . ` ,
2017-07-30 23:23:29 +02:00
ArgsUsage : "node-key local-amt push-amt" ,
2016-06-21 21:35:07 +02:00
Flags : [ ] cli . Flag {
cli . IntFlag {
Name : "peer_id" ,
2016-09-14 00:36:27 +02:00
Usage : "the relative id of the peer to open a channel with" ,
} ,
cli . StringFlag {
2016-10-28 04:42:47 +02:00
Name : "node_key" ,
Usage : "the identity public key of the target peer " +
"serialized in compressed format" ,
2016-06-21 21:35:07 +02:00
} ,
cli . IntFlag {
Name : "local_amt" ,
Usage : "the number of satoshis the wallet should commit to the channel" ,
} ,
cli . IntFlag {
2017-01-10 04:06:07 +01:00
Name : "push_amt" ,
Usage : "the number of satoshis to push to the remote " +
"side as part of the initial commitment state" ,
2016-06-21 21:35:07 +02:00
} ,
2016-07-08 00:35:06 +02:00
cli . BoolFlag {
Name : "block" ,
Usage : "block and wait until the channel is fully open" ,
} ,
2017-11-23 20:40:14 +01:00
cli . Int64Flag {
Name : "conf_target" ,
Usage : "(optional) the number of blocks that the " +
"transaction *should* confirm in, will be " +
"used for fee estimation" ,
} ,
cli . Int64Flag {
Name : "sat_per_byte" ,
Usage : "(optional) a manual fee expressed in " +
"sat/byte that should be used when crafting " +
"the transaction" ,
} ,
2016-06-21 21:35:07 +02:00
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( openChannel ) ,
2016-06-21 21:35:07 +02:00
}
2016-06-29 22:01:08 +02:00
func openChannel ( ctx * cli . Context ) error {
2016-06-21 21:35:07 +02:00
// TODO(roasbeef): add deadline to context
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2017-03-03 20:33:16 +01:00
2017-03-02 23:23:16 +01:00
args := ctx . Args ( )
var err error
// Show command help if no arguments provided
if ctx . NArg ( ) == 0 && ctx . NumFlags ( ) == 0 {
cli . ShowCommandHelp ( ctx , "openchannel" )
return nil
}
2016-06-21 21:35:07 +02:00
2017-03-02 23:23:16 +01:00
if ctx . IsSet ( "peer_id" ) && ctx . IsSet ( "node_key" ) {
2016-09-14 00:36:27 +02:00
return fmt . Errorf ( "both peer_id and lightning_id cannot be set " +
"at the same time, only one can be specified" )
}
2017-11-23 20:40:14 +01:00
req := & lnrpc . OpenChannelRequest {
TargetConf : int32 ( ctx . Int64 ( "conf_target" ) ) ,
SatPerByte : ctx . Int64 ( "sat_per_byte" ) ,
}
2016-06-21 21:35:07 +02:00
2017-03-02 23:23:16 +01:00
switch {
case ctx . IsSet ( "peer_id" ) :
2016-09-14 00:36:27 +02:00
req . TargetPeerId = int32 ( ctx . Int ( "peer_id" ) )
2017-03-02 23:23:16 +01:00
case ctx . IsSet ( "node_key" ) :
2016-10-28 04:42:47 +02:00
nodePubHex , err := hex . DecodeString ( ctx . String ( "node_key" ) )
2016-09-14 00:36:27 +02:00
if err != nil {
2017-03-02 23:23:16 +01:00
return fmt . Errorf ( "unable to decode node public key: %v" , err )
2016-09-14 00:36:27 +02:00
}
2016-10-28 04:42:47 +02:00
req . NodePubkey = nodePubHex
2017-03-02 23:23:16 +01:00
case args . Present ( ) :
nodePubHex , err := hex . DecodeString ( args . First ( ) )
if err != nil {
return fmt . Errorf ( "unable to decode node public key: %v" , err )
}
args = args . Tail ( )
req . NodePubkey = nodePubHex
default :
2017-03-03 20:33:16 +01:00
return fmt . Errorf ( "node id argument missing" )
2017-03-02 23:23:16 +01:00
}
switch {
case ctx . IsSet ( "local_amt" ) :
req . LocalFundingAmount = int64 ( ctx . Int ( "local_amt" ) )
case args . Present ( ) :
req . LocalFundingAmount , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
if err != nil {
return fmt . Errorf ( "unable to decode local amt: %v" , err )
}
args = args . Tail ( )
default :
return fmt . Errorf ( "local amt argument missing" )
}
2017-03-03 20:33:16 +01:00
if ctx . IsSet ( "push_amt" ) {
2017-03-02 23:23:16 +01:00
req . PushSat = int64 ( ctx . Int ( "push_amt" ) )
2017-03-03 20:33:16 +01:00
} else if args . Present ( ) {
2017-03-02 23:23:16 +01:00
req . PushSat , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
if err != nil {
return fmt . Errorf ( "unable to decode push amt: %v" , err )
}
2016-09-14 00:36:27 +02:00
}
2016-07-08 00:35:06 +02:00
stream , err := client . OpenChannel ( ctxb , req )
2016-06-21 21:35:07 +02:00
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2016-06-21 21:35:07 +02:00
}
2016-07-08 00:35:06 +02:00
for {
resp , err := stream . Recv ( )
if err == io . EOF {
return nil
} else if err != nil {
return err
}
2016-08-31 01:54:49 +02:00
switch update := resp . Update . ( type ) {
2017-02-08 04:36:15 +01:00
case * lnrpc . OpenStatusUpdate_ChanPending :
txid , err := chainhash . NewHash ( update . ChanPending . Txid )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printJSON ( struct {
2017-02-08 04:36:15 +01:00
FundingTxid string ` json:"funding_txid" `
} {
FundingTxid : txid . String ( ) ,
} ,
)
if ! ctx . Bool ( "block" ) {
return nil
}
2016-08-31 01:54:49 +02:00
case * lnrpc . OpenStatusUpdate_ChanOpen :
channelPoint := update . ChanOpen . ChannelPoint
2017-01-05 22:56:27 +01:00
txid , err := chainhash . NewHash ( channelPoint . FundingTxid )
2016-08-31 01:54:49 +02:00
if err != nil {
return err
}
index := channelPoint . OutputIndex
2017-02-23 20:56:47 +01:00
printJSON ( struct {
2016-08-31 01:54:49 +02:00
ChannelPoint string ` json:"channel_point" `
} {
ChannelPoint : fmt . Sprintf ( "%v:%v" , txid , index ) ,
} ,
)
2016-07-08 00:35:06 +02:00
}
2016-06-21 21:35:07 +02:00
}
}
// TODO(roasbeef): also allow short relative channel ID.
2017-02-23 20:56:47 +01:00
2017-02-24 14:32:33 +01:00
var closeChannelCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "closechannel" ,
Usage : "Close an existing channel." ,
2017-11-23 20:40:14 +01:00
Description : `
Close an existing channel . The channel can be closed either cooperatively ,
or unilaterally ( -- force ) .
A unilateral channel closure means that the latest commitment
transaction will be broadcast to the network . As a result , any settled
funds will be time locked for a few blocks before they can be swept int
lnd ' s wallet .
In the case of a cooperative closure , One can manually set the fee to
be used for the closing transaction via either the -- conf_target or
-- sat_per_byte arguments . This will be the starting value used during
fee negotiation . This is optional . ` ,
2017-03-02 23:23:16 +01:00
ArgsUsage : "funding_txid [output_index [time_limit]]" ,
2016-06-21 21:35:07 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "funding_txid" ,
Usage : "the txid of the channel's funding transaction" ,
} ,
cli . IntFlag {
Name : "output_index" ,
Usage : "the output index for the funding output of the funding " +
"transaction" ,
} ,
cli . StringFlag {
Name : "time_limit" ,
Usage : "a relative deadline afterwhich the attempt should be " +
2017-04-27 06:38:53 +02:00
"abandoned" ,
2016-06-21 21:35:07 +02:00
} ,
cli . BoolFlag {
Name : "force" ,
2016-09-12 21:28:28 +02:00
Usage : "after the time limit has passed, attempt an " +
2016-06-21 21:35:07 +02:00
"uncooperative closure" ,
} ,
2016-07-08 00:35:06 +02:00
cli . BoolFlag {
Name : "block" ,
Usage : "block until the channel is closed" ,
} ,
2017-11-23 20:40:14 +01:00
cli . Int64Flag {
Name : "conf_target" ,
Usage : "(optional) the number of blocks that the " +
"transaction *should* confirm in, will be " +
"used for fee estimation" ,
} ,
cli . Int64Flag {
Name : "sat_per_byte" ,
Usage : "(optional) a manual fee expressed in " +
"sat/byte that should be used when crafting " +
"the transaction" ,
} ,
2016-06-21 21:35:07 +02:00
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( closeChannel ) ,
2016-06-21 21:35:07 +02:00
}
2016-06-29 22:01:08 +02:00
func closeChannel ( ctx * cli . Context ) error {
2016-06-21 21:35:07 +02:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-06-21 21:35:07 +02:00
2017-03-02 23:23:16 +01:00
args := ctx . Args ( )
var (
txid string
err error
)
// Show command help if no arguments provieded
if ctx . NArg ( ) == 0 && ctx . NumFlags ( ) == 0 {
cli . ShowCommandHelp ( ctx , "closeChannel" )
return nil
2016-06-21 21:35:07 +02:00
}
2016-09-12 21:28:28 +02:00
// TODO(roasbeef): implement time deadline within server
2016-06-21 21:35:07 +02:00
req := & lnrpc . CloseChannelRequest {
2017-03-02 23:23:16 +01:00
ChannelPoint : & lnrpc . ChannelPoint { } ,
Force : ctx . Bool ( "force" ) ,
2017-11-23 20:40:14 +01:00
TargetConf : int32 ( ctx . Int64 ( "conf_target" ) ) ,
SatPerByte : ctx . Int64 ( "sat_per_byte" ) ,
2017-03-02 23:23:16 +01:00
}
switch {
case ctx . IsSet ( "funding_txid" ) :
txid = ctx . String ( "funding_txid" )
case args . Present ( ) :
txid = args . First ( )
args = args . Tail ( )
default :
return fmt . Errorf ( "funding txid argument missing" )
}
txidhash , err := chainhash . NewHashFromStr ( txid )
if err != nil {
return err
}
req . ChannelPoint . FundingTxid = txidhash [ : ]
switch {
case ctx . IsSet ( "output_index" ) :
req . ChannelPoint . OutputIndex = uint32 ( ctx . Int ( "output_index" ) )
case args . Present ( ) :
index , err := strconv . ParseInt ( args . First ( ) , 10 , 32 )
if err != nil {
return fmt . Errorf ( "unable to decode output index: %v" , err )
}
req . ChannelPoint . OutputIndex = uint32 ( index )
default :
req . ChannelPoint . OutputIndex = 0
2016-06-21 21:35:07 +02:00
}
2016-07-08 00:35:06 +02:00
stream , err := client . CloseChannel ( ctxb , req )
2016-06-21 21:35:07 +02:00
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2016-06-21 21:35:07 +02:00
}
2016-07-08 00:35:06 +02:00
for {
resp , err := stream . Recv ( )
if err == io . EOF {
return nil
} else if err != nil {
return err
}
2016-08-31 01:54:49 +02:00
switch update := resp . Update . ( type ) {
2017-02-08 04:36:15 +01:00
case * lnrpc . CloseStatusUpdate_ClosePending :
closingHash := update . ClosePending . Txid
txid , err := chainhash . NewHash ( closingHash )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printJSON ( struct {
2017-02-08 04:36:15 +01:00
ClosingTXID string ` json:"closing_txid" `
} {
ClosingTXID : txid . String ( ) ,
} )
if ! ctx . Bool ( "block" ) {
return nil
}
2016-08-31 01:54:49 +02:00
case * lnrpc . CloseStatusUpdate_ChanClose :
closingHash := update . ChanClose . ClosingTxid
2017-01-05 22:56:27 +01:00
txid , err := chainhash . NewHash ( closingHash )
2016-08-31 01:54:49 +02:00
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printJSON ( struct {
2016-08-31 01:54:49 +02:00
ClosingTXID string ` json:"closing_txid" `
} {
ClosingTXID : txid . String ( ) ,
} )
}
2016-07-08 00:35:06 +02:00
}
2016-06-21 21:35:07 +02:00
}
2017-02-24 14:32:33 +01:00
var listPeersCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "listpeers" ,
Usage : "List all active, currently connected peers." ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( listPeers ) ,
2016-06-21 21:35:07 +02:00
}
2016-06-29 22:01:08 +02:00
func listPeers ( ctx * cli . Context ) error {
2016-06-21 21:35:07 +02:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-06-21 21:35:07 +02:00
req := & lnrpc . ListPeersRequest { }
resp , err := client . ListPeers ( ctxb , req )
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2016-06-21 21:35:07 +02:00
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2016-06-29 22:01:08 +02:00
return nil
2016-06-21 21:35:07 +02:00
}
2017-10-12 11:42:06 +02:00
var createCommand = cli . Command {
Name : "create" ,
Usage : "used to set the wallet password at lnd startup" ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( create ) ,
2017-10-12 11:42:06 +02:00
}
func create ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getWalletUnlockerClient ( ctx )
defer cleanUp ( )
fmt . Printf ( "Input wallet password: " )
pw1 , err := terminal . ReadPassword ( 0 )
if err != nil {
return err
}
fmt . Println ( )
fmt . Printf ( "Confirm wallet password: " )
pw2 , err := terminal . ReadPassword ( 0 )
if err != nil {
return err
}
fmt . Println ( )
if ! bytes . Equal ( pw1 , pw2 ) {
return fmt . Errorf ( "passwords don't match" )
}
req := & lnrpc . CreateWalletRequest {
Password : pw1 ,
}
_ , err = client . CreateWallet ( ctxb , req )
if err != nil {
return err
}
return nil
}
var unlockCommand = cli . Command {
Name : "unlock" ,
Usage : "unlock encrypted wallet at lnd startup" ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( unlock ) ,
2017-10-12 11:42:06 +02:00
}
func unlock ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getWalletUnlockerClient ( ctx )
defer cleanUp ( )
fmt . Printf ( "Input wallet password: " )
pw , err := terminal . ReadPassword ( 0 )
if err != nil {
return err
}
fmt . Println ( )
req := & lnrpc . UnlockWalletRequest {
Password : pw ,
}
_ , err = client . UnlockWallet ( ctxb , req )
if err != nil {
return err
}
return nil
}
2017-02-24 14:32:33 +01:00
var walletBalanceCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "walletbalance" ,
Usage : "compute and display the wallet's current balance" ,
2016-06-21 21:35:07 +02:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : "witness_only" ,
Usage : "if only witness outputs should be considered when " +
"calculating the wallet's balance" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( walletBalance ) ,
2016-06-21 21:35:07 +02:00
}
2016-06-29 22:01:08 +02:00
func walletBalance ( ctx * cli . Context ) error {
2016-06-21 21:35:07 +02:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-06-21 21:35:07 +02:00
req := & lnrpc . WalletBalanceRequest {
WitnessOnly : ctx . Bool ( "witness_only" ) ,
}
resp , err := client . WalletBalance ( ctxb , req )
if err != nil {
2016-06-29 22:01:08 +02:00
return err
2016-06-21 21:35:07 +02:00
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2016-06-29 22:01:08 +02:00
return nil
2016-06-21 21:35:07 +02:00
}
2016-07-06 03:58:41 +02:00
2017-02-24 14:32:33 +01:00
var channelBalanceCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "channelbalance" ,
Usage : "returns the sum of the total available channel balance across all open channels" ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( channelBalance ) ,
2016-09-15 20:59:51 +02:00
}
func channelBalance ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-09-15 20:59:51 +02:00
req := & lnrpc . ChannelBalanceRequest { }
resp , err := client . ChannelBalance ( ctxb , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2016-09-15 20:59:51 +02:00
return nil
}
2017-02-24 14:32:33 +01:00
var getInfoCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "getinfo" ,
Usage : "returns basic information related to the active daemon" ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( getInfo ) ,
2016-07-06 03:58:41 +02:00
}
func getInfo ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-07-06 03:58:41 +02:00
req := & lnrpc . GetInfoRequest { }
resp , err := client . GetInfo ( ctxb , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2016-07-06 03:58:41 +02:00
return nil
}
2016-07-08 00:35:58 +02:00
2017-02-24 14:32:33 +01:00
var pendingChannelsCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "pendingchannels" ,
Usage : "display information pertaining to pending channels" ,
2016-07-08 00:35:58 +02:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : "open, o" ,
Usage : "display the status of new pending channels" ,
} ,
cli . BoolFlag {
Name : "close, c" ,
Usage : "display the status of channels being closed" ,
} ,
cli . BoolFlag {
Name : "all, a" ,
Usage : "display the status of channels in the " +
"process of being opened or closed" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( pendingChannels ) ,
2016-07-08 00:35:58 +02:00
}
func pendingChannels ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-07-08 00:35:58 +02:00
2017-05-05 01:13:50 +02:00
req := & lnrpc . PendingChannelRequest { }
2016-07-08 00:35:58 +02:00
resp , err := client . PendingChannels ( ctxb , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2016-07-08 00:35:58 +02:00
return nil
}
2016-07-13 02:47:24 +02:00
2017-02-24 14:32:33 +01:00
var listChannelsCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "listchannels" ,
Usage : "list all open channels" ,
2016-09-26 05:04:58 +02:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : "active_only, a" ,
Usage : "only list channels which are currently active" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( listChannels ) ,
2016-09-26 05:04:58 +02:00
}
func listChannels ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-09-26 05:04:58 +02:00
req := & lnrpc . ListChannelsRequest { }
resp , err := client . ListChannels ( ctxb , req )
if err != nil {
return err
}
2017-01-17 22:39:30 +01:00
// TODO(roasbeef): defer close the client for the all
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2016-09-26 05:04:58 +02:00
return nil
}
2017-02-24 14:32:33 +01:00
var sendPaymentCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "sendpayment" ,
Usage : "send a payment over lightning" ,
ArgsUsage : "(destination amount payment_hash " +
"| --pay_req=[payment request])" ,
2016-07-13 02:47:24 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
2016-09-21 01:19:15 +02:00
Name : "dest, d" ,
Usage : "the compressed identity pubkey of the " +
"payment recipient" ,
2016-07-13 02:47:24 +02:00
} ,
2017-03-02 23:23:16 +01:00
cli . Int64Flag {
2016-07-13 02:47:24 +02:00
Name : "amt, a" ,
Usage : "number of satoshis to send" ,
} ,
cli . StringFlag {
Name : "payment_hash, r" ,
Usage : "the hash to use within the payment's HTLC" ,
} ,
2016-09-21 01:14:45 +02:00
cli . BoolFlag {
Name : "debug_send" ,
Usage : "use the debug rHash when sending the HTLC" ,
} ,
2017-01-03 00:38:00 +01:00
cli . StringFlag {
Name : "pay_req" ,
2017-10-28 00:39:54 +02:00
Usage : "a zpay32 encoded payment request to fulfill" ,
2016-07-13 02:47:24 +02:00
} ,
} ,
2017-02-24 14:32:33 +01:00
Action : sendPayment ,
2016-07-13 02:47:24 +02:00
}
2017-02-24 14:32:33 +01:00
func sendPayment ( ctx * cli . Context ) error {
2017-03-02 23:23:16 +01:00
// Show command help if no arguments provieded
if ctx . NArg ( ) == 0 && ctx . NumFlags ( ) == 0 {
cli . ShowCommandHelp ( ctx , "sendpayment" )
return nil
}
2017-01-03 00:38:00 +01:00
var req * lnrpc . SendRequest
2017-03-02 23:23:16 +01:00
if ctx . IsSet ( "pay_req" ) {
2017-01-03 00:38:00 +01:00
req = & lnrpc . SendRequest {
PaymentRequest : ctx . String ( "pay_req" ) ,
}
} else {
2017-03-02 23:23:16 +01:00
args := ctx . Args ( )
var (
destNode [ ] byte
err error
amount int64
)
switch {
case ctx . IsSet ( "dest" ) :
destNode , err = hex . DecodeString ( ctx . String ( "dest" ) )
case args . Present ( ) :
destNode , err = hex . DecodeString ( args . First ( ) )
args = args . Tail ( )
default :
return fmt . Errorf ( "destination txid argument missing" )
}
2016-09-21 01:14:45 +02:00
if err != nil {
return err
}
2017-03-02 23:23:16 +01:00
2017-01-03 00:38:00 +01:00
if len ( destNode ) != 33 {
return fmt . Errorf ( "dest node pubkey must be exactly 33 bytes, is " +
"instead: %v" , len ( destNode ) )
}
2017-03-02 23:23:16 +01:00
if ctx . IsSet ( "amt" ) {
amount = ctx . Int64 ( "amt" )
} else if args . Present ( ) {
amount , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
args = args . Tail ( )
if err != nil {
return fmt . Errorf ( "unable to decode payment amount: %v" , err )
}
}
2017-01-03 00:38:00 +01:00
req = & lnrpc . SendRequest {
Dest : destNode ,
2017-03-02 23:23:16 +01:00
Amt : amount ,
2017-01-03 00:38:00 +01:00
}
2017-03-02 23:23:16 +01:00
if ctx . Bool ( "debug_send" ) && ( ctx . IsSet ( "payment_hash" ) || args . Present ( ) ) {
return fmt . Errorf ( "do not provide a payment hash with debug send" )
} else if ! ctx . Bool ( "debug_send" ) {
var rHash [ ] byte
switch {
case ctx . IsSet ( "payment_hash" ) :
rHash , err = hex . DecodeString ( ctx . String ( "payment_hash" ) )
case args . Present ( ) :
rHash , err = hex . DecodeString ( args . First ( ) )
default :
return fmt . Errorf ( "payment hash argument missing" )
}
2017-01-03 00:38:00 +01:00
if err != nil {
return err
}
if len ( rHash ) != 32 {
return fmt . Errorf ( "payment hash must be exactly 32 " +
"bytes, is instead %v" , len ( rHash ) )
}
req . PaymentHash = rHash
2016-09-21 01:14:45 +02:00
}
2016-07-13 02:47:24 +02:00
}
2017-10-28 00:39:54 +02:00
return sendPaymentRequest ( ctx , req )
}
func sendPaymentRequest ( ctx * cli . Context , req * lnrpc . SendRequest ) error {
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-07-13 02:47:24 +02:00
paymentStream , err := client . SendPayment ( context . Background ( ) )
if err != nil {
return err
}
if err := paymentStream . Send ( req ) ; err != nil {
return err
}
resp , err := paymentStream . Recv ( )
if err != nil {
return err
}
2016-07-22 01:18:42 +02:00
paymentStream . CloseSend ( )
2017-02-23 20:56:47 +01:00
printJSON ( struct {
2017-07-05 00:55:26 +02:00
E string ` json:"payment_error" `
2017-02-21 08:58:17 +01:00
P string ` json:"payment_preimage" `
R * lnrpc . Route ` json:"payment_route" `
} {
2017-07-05 00:55:26 +02:00
E : resp . PaymentError ,
2017-02-21 08:58:17 +01:00
P : hex . EncodeToString ( resp . PaymentPreimage ) ,
R : resp . PaymentRoute ,
} )
2016-07-13 02:47:24 +02:00
return nil
}
2016-07-15 13:02:59 +02:00
2017-10-28 00:39:54 +02:00
var payInvoiceCommand = cli . Command {
Name : "payinvoice" ,
Usage : "pay an invoice over lightning" ,
ArgsUsage : "pay_req" ,
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "pay_req" ,
Usage : "a zpay32 encoded payment request to fulfill" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( payInvoice ) ,
2017-10-28 00:39:54 +02:00
}
func payInvoice ( ctx * cli . Context ) error {
args := ctx . Args ( )
var payReq string
switch {
case ctx . IsSet ( "pay_req" ) :
payReq = ctx . String ( "pay_req" )
case args . Present ( ) :
payReq = args . First ( )
default :
return fmt . Errorf ( "pay_req argument missing" )
}
req := & lnrpc . SendRequest {
PaymentRequest : payReq ,
}
return sendPaymentRequest ( ctx , req )
}
2017-02-24 14:32:33 +01:00
var addInvoiceCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "addinvoice" ,
Usage : "add a new invoice." ,
2017-11-23 20:40:14 +01:00
Description : `
Add a new invoice , expressing intent for a future payment .
The value of the invoice in satoshis is necessary for the creation ,
the remaining parameters are optional . ` ,
2017-03-02 23:23:16 +01:00
ArgsUsage : "value preimage" ,
2016-09-19 21:05:54 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
2017-09-05 18:11:04 +02:00
Name : "memo" ,
Usage : "a description of the payment to attach along " +
"with the invoice (default=\"\")" ,
2016-09-19 21:05:54 +02:00
} ,
cli . StringFlag {
Name : "receipt" ,
Usage : "an optional cryptographic receipt of payment" ,
} ,
cli . StringFlag {
2017-09-05 18:11:04 +02:00
Name : "preimage" ,
Usage : "the hex-encoded preimage (32 byte) which will " +
"allow settling an incoming HTLC payable to this " +
"preimage. If not set, a random preimage will be " +
"created." ,
2016-09-19 21:05:54 +02:00
} ,
2017-03-02 23:23:16 +01:00
cli . Int64Flag {
2016-09-19 21:05:54 +02:00
Name : "value" ,
Usage : "the value of this invoice in satoshis" ,
} ,
2017-09-05 18:11:04 +02:00
cli . StringFlag {
Name : "description_hash" ,
Usage : "SHA-256 hash of the description of the payment. " +
"Used if the purpose of payment cannot naturally " +
"fit within the memo. If provided this will be " +
"used instead of the description(memo) field in " +
"the encoded invoice." ,
} ,
cli . StringFlag {
Name : "fallback_addr" ,
Usage : "fallback on-chain address that can be used in " +
"case the lightning payment fails" ,
} ,
cli . Int64Flag {
Name : "expiry" ,
Usage : "the invoice's expiry time in seconds. If not " +
"specified an expiry of 3600 seconds (1 hour) " +
"is implied." ,
} ,
2016-09-19 21:05:54 +02:00
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( addInvoice ) ,
2016-09-19 21:05:54 +02:00
}
func addInvoice ( ctx * cli . Context ) error {
2017-03-02 23:23:16 +01:00
var (
preimage [ ] byte
2017-09-05 18:11:04 +02:00
descHash [ ] byte
2017-03-02 23:23:16 +01:00
receipt [ ] byte
value int64
err error
)
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-09-19 21:05:54 +02:00
2017-03-02 23:23:16 +01:00
args := ctx . Args ( )
switch {
case ctx . IsSet ( "value" ) :
value = ctx . Int64 ( "value" )
case args . Present ( ) :
value , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
args = args . Tail ( )
if err != nil {
return fmt . Errorf ( "unable to decode value argument: %v" , err )
}
default :
return fmt . Errorf ( "value argument missing" )
}
switch {
case ctx . IsSet ( "preimage" ) :
preimage , err = hex . DecodeString ( ctx . String ( "preimage" ) )
case args . Present ( ) :
preimage , err = hex . DecodeString ( args . First ( ) )
}
2016-09-19 21:05:54 +02:00
if err != nil {
return fmt . Errorf ( "unable to parse preimage: %v" , err )
}
2017-09-05 18:11:04 +02:00
descHash , err = hex . DecodeString ( ctx . String ( "description_hash" ) )
if err != nil {
return fmt . Errorf ( "unable to parse description_hash: %v" , err )
}
2017-03-02 23:23:16 +01:00
receipt , err = hex . DecodeString ( ctx . String ( "receipt" ) )
2016-09-19 21:05:54 +02:00
if err != nil {
return fmt . Errorf ( "unable to parse receipt: %v" , err )
}
invoice := & lnrpc . Invoice {
2017-09-05 18:11:04 +02:00
Memo : ctx . String ( "memo" ) ,
Receipt : receipt ,
RPreimage : preimage ,
Value : value ,
DescriptionHash : descHash ,
FallbackAddr : ctx . String ( "fallback_addr" ) ,
Expiry : ctx . Int64 ( "expiry" ) ,
2016-09-19 21:05:54 +02:00
}
resp , err := client . AddInvoice ( context . Background ( ) , invoice )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printJSON ( struct {
2017-01-03 00:37:10 +01:00
RHash string ` json:"r_hash" `
PayReq string ` json:"pay_req" `
2016-09-19 21:05:54 +02:00
} {
2017-01-03 00:37:10 +01:00
RHash : hex . EncodeToString ( resp . RHash ) ,
PayReq : resp . PaymentRequest ,
2016-09-19 21:05:54 +02:00
} )
return nil
}
2017-02-24 14:32:33 +01:00
var lookupInvoiceCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "lookupinvoice" ,
Usage : "Lookup an existing invoice by its payment hash." ,
ArgsUsage : "rhash" ,
2016-09-19 21:05:54 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "rhash" ,
2017-03-02 23:23:16 +01:00
Usage : "the 32 byte payment hash of the invoice to query for, the hash " +
2016-09-19 21:05:54 +02:00
"should be a hex-encoded string" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( lookupInvoice ) ,
2016-09-19 21:05:54 +02:00
}
func lookupInvoice ( ctx * cli . Context ) error {
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-09-19 21:05:54 +02:00
2017-03-02 23:23:16 +01:00
var (
rHash [ ] byte
err error
)
switch {
case ctx . IsSet ( "rhash" ) :
rHash , err = hex . DecodeString ( ctx . String ( "rhash" ) )
case ctx . Args ( ) . Present ( ) :
rHash , err = hex . DecodeString ( ctx . Args ( ) . First ( ) )
default :
return fmt . Errorf ( "rhash argument missing" )
}
2016-09-19 21:05:54 +02:00
if err != nil {
2017-03-02 23:23:16 +01:00
return fmt . Errorf ( "unable to decode rhash argument: %v" , err )
2016-09-19 21:05:54 +02:00
}
req := & lnrpc . PaymentHash {
RHash : rHash ,
}
invoice , err := client . LookupInvoice ( context . Background ( ) , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( invoice )
2016-09-19 21:05:54 +02:00
return nil
}
2017-02-24 14:32:33 +01:00
var listInvoicesCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "listinvoices" ,
Usage : "List all invoices currently stored." ,
2016-09-19 21:05:54 +02:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : "pending_only" ,
Usage : "toggles if all invoices should be returned, or only " +
"those that are currently unsettled" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( listInvoices ) ,
2016-09-19 21:05:54 +02:00
}
func listInvoices ( ctx * cli . Context ) error {
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-09-19 21:05:54 +02:00
pendingOnly := true
if ! ctx . Bool ( "pending_only" ) {
pendingOnly = false
}
req := & lnrpc . ListInvoiceRequest {
PendingOnly : pendingOnly ,
}
invoices , err := client . ListInvoices ( context . Background ( ) , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( invoices )
2016-09-19 21:05:54 +02:00
return nil
}
2017-02-24 14:32:33 +01:00
var describeGraphCommand = cli . Command {
2016-12-27 06:52:15 +01:00
Name : "describegraph" ,
Description : "prints a human readable version of the known channel " +
"graph from the PoV of the node" ,
2017-03-02 23:23:16 +01:00
Usage : "describe the network graph" ,
2017-01-24 05:32:17 +01:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
2017-01-30 00:38:04 +01:00
Name : "render" ,
2017-03-02 23:23:16 +01:00
Usage : "If set, then an image of graph will be generated and displayed. The generated image is stored within the current directory with a file name of 'graph.svg'" ,
2017-01-24 05:32:17 +01:00
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( describeGraph ) ,
2016-08-31 01:54:49 +02:00
}
2016-08-20 22:49:35 +02:00
2016-12-27 06:52:15 +01:00
func describeGraph ( ctx * cli . Context ) error {
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-08-21 16:46:54 +02:00
2016-12-27 06:52:15 +01:00
req := & lnrpc . ChannelGraphRequest { }
2016-12-28 00:25:43 +01:00
2016-12-27 06:52:15 +01:00
graph , err := client . DescribeGraph ( context . Background ( ) , req )
2016-08-21 16:46:54 +02:00
if err != nil {
return err
}
2017-01-24 05:32:17 +01:00
// If the draw flag is on, then we'll use the 'dot' command to create a
// visualization of the graph itself.
2017-01-30 00:38:04 +01:00
if ctx . Bool ( "render" ) {
2017-01-24 05:32:17 +01:00
return drawChannelGraph ( graph )
}
2017-02-23 20:56:47 +01:00
printRespJSON ( graph )
2016-08-21 16:46:54 +02:00
return nil
}
2017-01-25 03:07:15 +01:00
// normalizeFunc is a factory function which returns a function that normalizes
// the capacity of of edges within the graph. The value of the returned
// function can be used to either plot the capacities, or to use a weight in a
// rendering of the graph.
func normalizeFunc ( edges [ ] * lnrpc . ChannelEdge , scaleFactor float64 ) func ( int64 ) float64 {
var (
min float64 = math . MaxInt64
max float64
)
for _ , edge := range edges {
// In order to obtain saner values, we reduce the capacity of a
// channel to it's base 2 logarithm.
z := math . Log2 ( float64 ( edge . Capacity ) )
if z < min {
min = z
}
if z > max {
max = z
}
}
return func ( x int64 ) float64 {
y := math . Log2 ( float64 ( x ) )
2017-01-26 03:27:27 +01:00
// TODO(roasbeef): results in min being zero
2017-03-09 05:44:32 +01:00
return ( y - min ) / ( max - min ) * scaleFactor
2017-01-25 03:07:15 +01:00
}
}
2017-01-24 05:32:17 +01:00
func drawChannelGraph ( graph * lnrpc . ChannelGraph ) error {
// First we'll create a temporary file that we'll write the compiled
// string that describes our graph in the dot format to.
tempDotFile , err := ioutil . TempFile ( "" , "" )
if err != nil {
return err
}
defer os . Remove ( tempDotFile . Name ( ) )
// Next, we'll create (or re-create) the file that the final graph
// image will be written to.
2017-01-25 03:07:15 +01:00
imageFile , err := os . Create ( "graph.svg" )
2017-01-24 05:32:17 +01:00
if err != nil {
return err
}
// With our temporary files set up, we'll initialize the graphviz
// object that we'll use to draw our graph.
graphName := "LightningNetwork"
graphCanvas := gographviz . NewGraph ( )
graphCanvas . SetName ( graphName )
2017-01-25 03:07:15 +01:00
graphCanvas . SetDir ( false )
2017-01-24 05:32:17 +01:00
2017-01-26 03:27:27 +01:00
const numKeyChars = 10
truncateStr := func ( k string , n uint ) string {
return k [ : n ]
}
2017-01-24 05:32:17 +01:00
// For each node within the graph, we'll add a new vertex to the graph.
for _ , node := range graph . Nodes {
// Rather than using the entire hex-encoded string, we'll only
// use the first 10 characters. We also add a prefix of "Z" as
// graphviz is unable to parse the compressed pubkey as a
// non-integer.
//
// TODO(roasbeef): should be able to get around this?
2017-01-26 03:27:27 +01:00
nodeID := fmt . Sprintf ( ` "%v" ` , truncateStr ( node . PubKey , numKeyChars ) )
2017-01-24 05:32:17 +01:00
graphCanvas . AddNode ( graphName , nodeID , gographviz . Attrs { } )
}
2017-01-25 03:07:15 +01:00
normalize := normalizeFunc ( graph . Edges , 3 )
2017-01-24 05:32:17 +01:00
// Similarly, for each edge we'll add an edge between the corresponding
// nodes added to the graph above.
for _ , edge := range graph . Edges {
// Once again, we add a 'Z' prefix so we're compliant with the
// dot grammar.
2017-01-26 03:27:27 +01:00
src := fmt . Sprintf ( ` "%v" ` , truncateStr ( edge . Node1Pub , numKeyChars ) )
dest := fmt . Sprintf ( ` "%v" ` , truncateStr ( edge . Node2Pub , numKeyChars ) )
2017-01-24 05:32:17 +01:00
// The weight for our edge will be the total capacity of the
// channel, in BTC.
2017-01-25 03:07:15 +01:00
// TODO(roasbeef): can also factor in the edges time-lock delta
// and fee information
2017-01-24 05:32:17 +01:00
amt := btcutil . Amount ( edge . Capacity ) . ToBTC ( )
edgeWeight := strconv . FormatFloat ( amt , 'f' , - 1 , 64 )
// The label for each edge will simply be a truncated version
// of it's channel ID.
2017-01-26 03:27:27 +01:00
chanIDStr := strconv . FormatUint ( edge . ChannelId , 10 )
edgeLabel := fmt . Sprintf ( ` "cid:%v" ` , truncateStr ( chanIDStr , 7 ) )
2017-01-24 05:32:17 +01:00
2017-01-25 03:07:15 +01:00
// We'll also use a normalized version of the channels'
// capacity in satoshis in order to modulate the "thickness" of
// the line that creates the edge within the graph.
normalizedCapacity := normalize ( edge . Capacity )
edgeThickness := strconv . FormatFloat ( normalizedCapacity , 'f' , - 1 , 64 )
2017-01-26 03:27:27 +01:00
// TODO(roasbeef): color code based on percentile capacity
2017-01-25 03:07:15 +01:00
graphCanvas . AddEdge ( src , dest , false , gographviz . Attrs {
"penwidth" : edgeThickness ,
"weight" : edgeWeight ,
"label" : edgeLabel ,
} )
2017-01-24 05:32:17 +01:00
}
// With the declarative generation of the graph complete, we now write
// the dot-string description of the graph
graphDotString := graphCanvas . String ( )
if _ , err := tempDotFile . WriteString ( graphDotString ) ; err != nil {
return err
}
if err := tempDotFile . Sync ( ) ; err != nil {
return err
}
2017-01-26 03:29:24 +01:00
var errBuffer bytes . Buffer
2017-01-24 05:32:17 +01:00
// Once our dot file has been written to disk, we can use the dot
// command itself to generate the drawn rendering of the graph
// described.
2017-01-25 03:07:15 +01:00
drawCmd := exec . Command ( "dot" , "-T" + "svg" , "-o" + imageFile . Name ( ) ,
2017-01-24 05:32:17 +01:00
tempDotFile . Name ( ) )
2017-01-26 03:29:24 +01:00
drawCmd . Stderr = & errBuffer
2017-01-24 05:32:17 +01:00
if err := drawCmd . Run ( ) ; err != nil {
2017-01-26 03:29:24 +01:00
fmt . Println ( "error rendering graph: " , errBuffer . String ( ) )
fmt . Println ( "dot: " , graphDotString )
2017-01-24 05:32:17 +01:00
return err
}
2017-01-26 03:29:24 +01:00
errBuffer . Reset ( )
2017-01-24 05:32:17 +01:00
// Finally, we'll open the drawn graph to display to the user.
openCmd := exec . Command ( "open" , imageFile . Name ( ) )
2017-01-26 03:29:24 +01:00
openCmd . Stderr = & errBuffer
2017-01-24 05:32:17 +01:00
if err := openCmd . Run ( ) ; err != nil {
2017-01-26 03:29:24 +01:00
fmt . Println ( "error opening rendered graph image: " ,
errBuffer . String ( ) )
2017-01-24 05:32:17 +01:00
return err
}
return nil
}
2017-02-24 14:32:33 +01:00
var listPaymentsCommand = cli . Command {
2017-03-02 23:23:16 +01:00
Name : "listpayments" ,
Usage : "list all outgoing payments" ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( listPayments ) ,
2016-12-05 12:59:36 +01:00
}
func listPayments ( ctx * cli . Context ) error {
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-12-05 12:59:36 +01:00
req := & lnrpc . ListPaymentsRequest { }
payments , err := client . ListPayments ( context . Background ( ) , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( payments )
2016-12-28 00:45:10 +01:00
return nil
}
2017-02-24 14:32:33 +01:00
var getChanInfoCommand = cli . Command {
2016-12-28 00:45:10 +01:00
Name : "getchaninfo" ,
2017-03-02 23:23:16 +01:00
Usage : "get the state of a channel" ,
2016-12-28 00:45:10 +01:00
Description : "prints out the latest authenticated state for a " +
"particular channel" ,
2017-03-02 23:23:16 +01:00
ArgsUsage : "chan_id" ,
2016-12-28 00:45:10 +01:00
Flags : [ ] cli . Flag {
2017-03-02 23:23:16 +01:00
cli . Int64Flag {
2016-12-28 00:45:10 +01:00
Name : "chan_id" ,
Usage : "the 8-byte compact channel ID to query for" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( getChanInfo ) ,
2016-12-28 00:45:10 +01:00
}
func getChanInfo ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-12-28 00:45:10 +01:00
2017-03-02 23:23:16 +01:00
var (
2017-03-09 05:44:32 +01:00
chanID int64
err error
2017-03-02 23:23:16 +01:00
)
switch {
case ctx . IsSet ( "chan_id" ) :
2017-03-09 05:44:32 +01:00
chanID = ctx . Int64 ( "chan_id" )
2017-03-02 23:23:16 +01:00
case ctx . Args ( ) . Present ( ) :
2017-03-09 05:44:32 +01:00
chanID , err = strconv . ParseInt ( ctx . Args ( ) . First ( ) , 10 , 64 )
2017-03-02 23:23:16 +01:00
default :
return fmt . Errorf ( "chan_id argument missing" )
}
2016-12-28 00:45:10 +01:00
req := & lnrpc . ChanInfoRequest {
2017-03-09 05:44:32 +01:00
ChanId : uint64 ( chanID ) ,
2016-12-28 00:45:10 +01:00
}
chanInfo , err := client . GetChanInfo ( ctxb , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( chanInfo )
2016-12-28 00:45:10 +01:00
return nil
}
2017-02-24 14:32:33 +01:00
var getNodeInfoCommand = cli . Command {
2016-12-28 00:45:10 +01:00
Name : "getnodeinfo" ,
2017-03-02 23:23:16 +01:00
Usage : "Get information on a specific node." ,
2016-12-28 00:45:10 +01:00
Description : "prints out the latest authenticated node state for an " +
"advertised node" ,
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "pub_key" ,
Usage : "the 33-byte hex-encoded compressed public of the target " +
"node" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( getNodeInfo ) ,
2016-12-28 00:45:10 +01:00
}
func getNodeInfo ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-12-28 00:45:10 +01:00
2017-04-13 20:55:17 +02:00
args := ctx . Args ( )
var pubKey string
switch {
case ctx . IsSet ( "pub_key" ) :
pubKey = ctx . String ( "pub_key" )
case args . Present ( ) :
pubKey = args . First ( )
default :
2017-03-02 23:23:16 +01:00
return fmt . Errorf ( "pub_key argument missing" )
}
2016-12-28 00:45:10 +01:00
req := & lnrpc . NodeInfoRequest {
2017-04-13 20:55:17 +02:00
PubKey : pubKey ,
2016-12-28 00:45:10 +01:00
}
nodeInfo , err := client . GetNodeInfo ( ctxb , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( nodeInfo )
2016-12-28 00:45:10 +01:00
return nil
}
2017-03-21 03:01:57 +01:00
var queryRoutesCommand = cli . Command {
Name : "queryroutes" ,
2017-03-02 23:23:16 +01:00
Usage : "Query a route to a destination." ,
Description : "Queries the channel router for a potential path to the destination that has sufficient flow for the amount including fees" ,
ArgsUsage : "dest amt" ,
2016-12-28 00:45:10 +01:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "dest" ,
Usage : "the 33-byte hex-encoded public key for the payment " +
"destination" ,
} ,
2017-03-02 23:23:16 +01:00
cli . Int64Flag {
2016-12-28 00:45:10 +01:00
Name : "amt" ,
Usage : "the amount to send expressed in satoshis" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( queryRoutes ) ,
2016-12-28 00:45:10 +01:00
}
2017-03-21 03:01:57 +01:00
func queryRoutes ( ctx * cli . Context ) error {
2016-12-28 00:45:10 +01:00
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-12-28 00:45:10 +01:00
2017-03-02 23:23:16 +01:00
var (
dest string
amt int64
err error
)
args := ctx . Args ( )
switch {
case ctx . IsSet ( "dest" ) :
dest = ctx . String ( "dest" )
case args . Present ( ) :
dest = args . First ( )
args = args . Tail ( )
default :
return fmt . Errorf ( "dest argument missing" )
}
switch {
case ctx . IsSet ( "amt" ) :
amt = ctx . Int64 ( "amt" )
case args . Present ( ) :
amt , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
if err != nil {
return fmt . Errorf ( "unable to decode amt argument: %v" , err )
}
default :
return fmt . Errorf ( "amt argument missing" )
}
2017-03-21 03:01:57 +01:00
req := & lnrpc . QueryRoutesRequest {
2017-03-02 23:23:16 +01:00
PubKey : dest ,
Amt : amt ,
2016-12-28 00:45:10 +01:00
}
2017-03-21 03:01:57 +01:00
route , err := client . QueryRoutes ( ctxb , req )
2016-12-28 00:45:10 +01:00
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( route )
2016-12-28 00:45:10 +01:00
return nil
}
2017-02-24 14:32:33 +01:00
var getNetworkInfoCommand = cli . Command {
2016-12-28 00:45:10 +01:00
Name : "getnetworkinfo" ,
Usage : "getnetworkinfo" ,
Description : "returns a set of statistics pertaining to the known channel " +
"graph" ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( getNetworkInfo ) ,
2016-12-28 00:45:10 +01:00
}
func getNetworkInfo ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2016-12-28 00:45:10 +01:00
req := & lnrpc . NetworkInfoRequest { }
netInfo , err := client . GetNetworkInfo ( ctxb , req )
if err != nil {
return err
}
2016-12-05 12:59:36 +01:00
2017-02-23 20:56:47 +01:00
printRespJSON ( netInfo )
2016-12-05 12:59:36 +01:00
return nil
2016-12-27 06:52:15 +01:00
}
2017-01-15 03:19:02 +01:00
2017-02-24 14:32:33 +01:00
var debugLevelCommand = cli . Command {
2017-11-23 20:40:14 +01:00
Name : "debuglevel" ,
Usage : "Set the debug level." ,
Description : ` Logging level for all subsystems { trace , debug , info , warn , error , critical }
You may also specify < subsystem >= < level > , < subsystem2 >= < level > , ... to set the log level for individual subsystems
Use show to list available subsystems ` ,
2017-01-15 03:19:02 +01:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : "show" ,
Usage : "if true, then the list of available sub-systems will be printed out" ,
} ,
cli . StringFlag {
Name : "level" ,
2017-04-13 20:55:17 +02:00
Usage : "the level specification to target either a coarse logging level, or granular set of specific sub-systems with logging levels for each" ,
2017-01-15 03:19:02 +01:00
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( debugLevel ) ,
2017-01-15 03:19:02 +01:00
}
func debugLevel ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2017-01-15 03:19:02 +01:00
req := & lnrpc . DebugLevelRequest {
Show : ctx . Bool ( "show" ) ,
LevelSpec : ctx . String ( "level" ) ,
}
resp , err := client . DebugLevel ( ctxb , req )
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2017-01-15 03:19:02 +01:00
return nil
}
2017-01-17 22:39:30 +01:00
2017-02-24 14:32:33 +01:00
var decodePayReqComamnd = cli . Command {
2017-01-17 22:39:30 +01:00
Name : "decodepayreq" ,
2017-03-02 23:23:16 +01:00
Usage : "Decode a payment request." ,
2017-01-17 22:39:30 +01:00
Description : "Decode the passed payment request revealing the destination, payment hash and value of the payment request" ,
2017-03-02 23:23:16 +01:00
ArgsUsage : "pay_req" ,
2017-01-17 22:39:30 +01:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "pay_req" ,
2017-09-05 18:11:04 +02:00
Usage : "the bech32 encoded payment request" ,
2017-01-17 22:39:30 +01:00
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( decodePayReq ) ,
2017-01-17 22:39:30 +01:00
}
func decodePayReq ( ctx * cli . Context ) error {
ctxb := context . Background ( )
2017-01-29 23:51:30 +01:00
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
2017-01-17 22:39:30 +01:00
2017-03-02 23:23:16 +01:00
var payreq string
switch {
case ctx . IsSet ( "pay_req" ) :
payreq = ctx . String ( "pay_req" )
case ctx . Args ( ) . Present ( ) :
payreq = ctx . Args ( ) . First ( )
default :
return fmt . Errorf ( "pay_req argument missing" )
2017-01-17 22:39:30 +01:00
}
2017-01-29 23:56:31 +01:00
resp , err := client . DecodePayReq ( ctxb , & lnrpc . PayReqString {
2017-03-02 23:23:16 +01:00
PayReq : payreq ,
2017-01-29 23:56:31 +01:00
} )
2017-01-17 22:39:30 +01:00
if err != nil {
return err
}
2017-02-23 20:56:47 +01:00
printRespJSON ( resp )
2017-01-17 22:39:30 +01:00
return nil
}
2017-03-04 09:23:04 +01:00
2017-03-09 05:44:32 +01:00
var listChainTxnsCommand = cli . Command {
2017-03-04 09:23:04 +01:00
Name : "listchaintxns" ,
Usage : "List transactions from the wallet." ,
Description : "List all transactions an address of the wallet was involved in." ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( listChainTxns ) ,
2017-03-04 09:23:04 +01:00
}
func listChainTxns ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
resp , err := client . GetTransactions ( ctxb , & lnrpc . GetTransactionsRequest { } )
if err != nil {
return err
}
2017-03-09 05:44:32 +01:00
printRespJSON ( resp )
2017-03-04 09:23:04 +01:00
return nil
}
2017-05-11 23:55:56 +02:00
var stopCommand = cli . Command {
2017-11-23 20:40:14 +01:00
Name : "stop" ,
Usage : "Stop and shutdown the daemon." ,
Description : `
Gracefully stop all daemon subsystems before stopping the daemon itself .
This is equivalent to stopping it using CTRL - C . ` ,
Action : actionDecorator ( stopDaemon ) ,
2017-05-11 23:55:56 +02:00
}
func stopDaemon ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
_ , err := client . StopDaemon ( ctxb , & lnrpc . StopRequest { } )
if err != nil {
return err
}
return nil
}
2017-04-20 04:33:09 +02:00
var signMessageCommand = cli . Command {
Name : "signmessage" ,
Usage : "sign a message with the node's private key" ,
ArgsUsage : "msg" ,
2017-11-23 20:40:14 +01:00
Description : `
Sign msg with the resident node ' s private key .
Returns the signature as a zbase32 string .
Positional arguments and flags can be used interchangeably but not at the same time ! ` ,
2017-04-20 04:33:09 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "msg" ,
Usage : "the message to sign" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( signMessage ) ,
2017-04-20 04:33:09 +02:00
}
func signMessage ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
var msg [ ] byte
switch {
case ctx . IsSet ( "msg" ) :
msg = [ ] byte ( ctx . String ( "msg" ) )
case ctx . Args ( ) . Present ( ) :
msg = [ ] byte ( ctx . Args ( ) . First ( ) )
default :
return fmt . Errorf ( "msg argument missing" )
}
resp , err := client . SignMessage ( ctxb , & lnrpc . SignMessageRequest { Msg : msg } )
if err != nil {
return err
}
printRespJSON ( resp )
return nil
}
var verifyMessageCommand = cli . Command {
Name : "verifymessage" ,
Usage : "verify a message signed with the signature" ,
ArgsUsage : "msg signature" ,
2017-11-23 20:40:14 +01:00
Description : `
Verify that the message was signed with a properly - formed signature
The signature must be zbase32 encoded and signed with the private key of
an active node in the resident node ' s channel database .
Positional arguments and flags can be used interchangeably but not at the same time ! ` ,
2017-04-20 04:33:09 +02:00
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "msg" ,
Usage : "the message to verify" ,
} ,
cli . StringFlag {
2017-04-29 13:44:29 +02:00
Name : "sig" ,
2017-04-20 04:33:09 +02:00
Usage : "the zbase32 encoded signature of the message" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( verifyMessage ) ,
2017-04-20 04:33:09 +02:00
}
func verifyMessage ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
var (
2017-04-29 13:44:29 +02:00
msg [ ] byte
sig string
2017-04-20 04:33:09 +02:00
)
args := ctx . Args ( )
switch {
case ctx . IsSet ( "msg" ) :
msg = [ ] byte ( ctx . String ( "msg" ) )
case args . Present ( ) :
msg = [ ] byte ( ctx . Args ( ) . First ( ) )
args = args . Tail ( )
default :
return fmt . Errorf ( "msg argument missing" )
}
switch {
2017-04-29 13:44:29 +02:00
case ctx . IsSet ( "sig" ) :
sig = ctx . String ( "sig" )
2017-04-20 04:33:09 +02:00
case args . Present ( ) :
2017-04-29 13:44:29 +02:00
sig = args . First ( )
2017-04-20 04:33:09 +02:00
default :
return fmt . Errorf ( "signature argument missing" )
}
2017-04-29 13:44:29 +02:00
req := & lnrpc . VerifyMessageRequest { Msg : msg , Signature : sig }
2017-04-20 04:33:09 +02:00
resp , err := client . VerifyMessage ( ctxb , req )
if err != nil {
return err
}
printRespJSON ( resp )
return nil
}
2017-08-22 09:29:08 +02:00
var feeReportCommand = cli . Command {
Name : "feereport" ,
Usage : "display the current fee policies of all active channels" ,
2017-11-23 20:40:14 +01:00
Description : `
Returns the current fee policies of all active channels .
Fee policies can be updated using the updateFees command . ` ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( feeReport ) ,
2017-08-22 09:29:08 +02:00
}
func feeReport ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
req := & lnrpc . FeeReportRequest { }
resp , err := client . FeeReport ( ctxb , req )
if err != nil {
return err
}
printRespJSON ( resp )
return nil
}
var updateFeesCommand = cli . Command {
Name : "updatefees" ,
Usage : "update the fee policy for all channels, or a single channel" ,
ArgsUsage : "base_fee_msat fee_rate [channel_point]" ,
2017-11-23 20:40:14 +01:00
Description : `
Updates the fee policy for all channels , or just a particular channel
identified by it ' s channel point . The fee update will be committed , and
broadcast to the rest of the network within the next batch .
Channel points are encoded as : funding_txid : output_index ` ,
2017-08-22 09:29:08 +02:00
Flags : [ ] cli . Flag {
cli . Int64Flag {
Name : "base_fee_msat" ,
Usage : "the base fee in milli-satoshis that will " +
"be charged for each forwarded HTLC, regardless " +
"of payment size" ,
} ,
cli . StringFlag {
Name : "fee_rate" ,
Usage : "the fee rate that will be charged " +
"proportionally based on the value of each " +
"forwarded HTLC, the lowest possible rate is 0.000001" ,
} ,
cli . StringFlag {
Name : "chan_point" ,
Usage : "The channel whose fee policy should be " +
"updated, if nil the policies for all channels " +
"will be updated. Takes the form of: txid:output_index" ,
} ,
} ,
2017-11-06 23:34:49 +01:00
Action : actionDecorator ( updateFees ) ,
2017-08-22 09:29:08 +02:00
}
func updateFees ( ctx * cli . Context ) error {
ctxb := context . Background ( )
client , cleanUp := getClient ( ctx )
defer cleanUp ( )
var (
baseFee int64
feeRate float64
err error
)
args := ctx . Args ( )
switch {
case ctx . IsSet ( "base_fee_msat" ) :
baseFee = ctx . Int64 ( "base_fee_msat" )
case args . Present ( ) :
baseFee , err = strconv . ParseInt ( args . First ( ) , 10 , 64 )
if err != nil {
return fmt . Errorf ( "unable to decode base_fee_msat: %v" , err )
}
args = args . Tail ( )
default :
return fmt . Errorf ( "base_fee_msat argument missing" )
}
switch {
case ctx . IsSet ( "fee_rate" ) :
feeRate = ctx . Float64 ( "fee_rate" )
case args . Present ( ) :
feeRate , err = strconv . ParseFloat ( args . First ( ) , 64 )
if err != nil {
return fmt . Errorf ( "unable to decode fee_rate: %v" , err )
}
args = args . Tail ( )
default :
return fmt . Errorf ( "fee_rate argument missing" )
}
var (
chanPoint * lnrpc . ChannelPoint
chanPointStr string
)
switch {
case ctx . IsSet ( "chan_point" ) :
chanPointStr = ctx . String ( "chan_point" )
case args . Present ( ) :
chanPointStr = args . First ( )
}
if chanPointStr != "" {
split := strings . Split ( chanPointStr , ":" )
if len ( split ) != 2 {
return fmt . Errorf ( "expecting chan_point to be in format of: " +
"txid:index" )
}
txHash , err := chainhash . NewHashFromStr ( split [ 0 ] )
if err != nil {
return err
}
index , err := strconv . ParseInt ( split [ 1 ] , 10 , 32 )
if err != nil {
return fmt . Errorf ( "unable to decode output index: %v" , err )
}
chanPoint = & lnrpc . ChannelPoint {
FundingTxid : txHash [ : ] ,
OutputIndex : uint32 ( index ) ,
}
}
req := & lnrpc . FeeUpdateRequest {
BaseFeeMsat : baseFee ,
FeeRate : feeRate ,
}
if chanPoint != nil {
req . Scope = & lnrpc . FeeUpdateRequest_ChanPoint {
ChanPoint : chanPoint ,
}
} else {
req . Scope = & lnrpc . FeeUpdateRequest_Global {
Global : true ,
}
}
resp , err := client . UpdateFees ( ctxb , req )
if err != nil {
return err
}
printRespJSON ( resp )
return nil
}