lnd: implement the sendcoins RPC request
This commit implements the “send coins” RPC request which was introduced at both the lnrpc and command line level in a prior commit. A small refactoring has taken place w.r.t to sendmany+sendcoins in order to eliminate some code duplication.
This commit is contained in:
parent
e391cf088e
commit
f3a6f8ffe6
@ -164,6 +164,10 @@ func TestRevocationKeyDerivation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeWitnessTestCase is a helper function used within test cases involving
|
||||||
|
// the validity of a crafted witness. This function is a wrapper function which
|
||||||
|
// allows constructing table-driven tests. In the case of an error while
|
||||||
|
// constructing the witness, the test fails fataly.
|
||||||
func makeWitnessTestCase(t *testing.T, f func() (wire.TxWitness, error)) func() wire.TxWitness {
|
func makeWitnessTestCase(t *testing.T, f func() (wire.TxWitness, error)) func() wire.TxWitness {
|
||||||
return func() wire.TxWitness {
|
return func() wire.TxWitness {
|
||||||
witness, err := f()
|
witness, err := f()
|
||||||
@ -190,6 +194,8 @@ func makeWitnessTestCase(t *testing.T, f func() (wire.TxWitness, error)) func()
|
|||||||
// * invalid sequence for CSV
|
// * invalid sequence for CSV
|
||||||
// * valid lock-time+sequence, valid sig
|
// * valid lock-time+sequence, valid sig
|
||||||
func TestHTLCSenderSpendValidation(t *testing.T) {
|
func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||||
|
// TODO(roasbeef): eliminate duplication with other HTLC tests.
|
||||||
|
|
||||||
// We generate a fake output, and the coresponding txin. This output
|
// We generate a fake output, and the coresponding txin. This output
|
||||||
// doesn't need to exist, as we'll only be validating spending from the
|
// doesn't need to exist, as we'll only be validating spending from the
|
||||||
// transaction that references this.
|
// transaction that references this.
|
||||||
@ -199,13 +205,15 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
|
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
|
||||||
|
|
||||||
|
// Generate a payment and revocation pre-image to be used below.
|
||||||
revokePreimage := testHdSeed[:]
|
revokePreimage := testHdSeed[:]
|
||||||
revokeHash := fastsha256.Sum256(revokePreimage)
|
revokeHash := fastsha256.Sum256(revokePreimage)
|
||||||
|
|
||||||
paymentPreimage := revokeHash
|
paymentPreimage := revokeHash
|
||||||
paymentPreimage[0] ^= 1
|
paymentPreimage[0] ^= 1
|
||||||
paymentHash := fastsha256.Sum256(paymentPreimage[:])
|
paymentHash := fastsha256.Sum256(paymentPreimage[:])
|
||||||
|
|
||||||
|
// We'll also need some tests keys for alice and bob, and meta-data of
|
||||||
|
// the HTLC output.
|
||||||
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
testWalletPrivKey)
|
testWalletPrivKey)
|
||||||
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
@ -214,6 +222,7 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
|||||||
cltvTimeout := uint32(8)
|
cltvTimeout := uint32(8)
|
||||||
csvTimeout := uint32(5)
|
csvTimeout := uint32(5)
|
||||||
|
|
||||||
|
// Generate the raw HTLC redemption scripts, and its p2wsh counterpart.
|
||||||
htlcScript, err := senderHTLCScript(cltvTimeout, csvTimeout,
|
htlcScript, err := senderHTLCScript(cltvTimeout, csvTimeout,
|
||||||
aliceKeyPub, bobKeyPub, revokeHash[:], paymentHash[:])
|
aliceKeyPub, bobKeyPub, revokeHash[:], paymentHash[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -318,6 +327,8 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
|||||||
t.Fatalf("unable to create engine: %v", err)
|
t.Fatalf("unable to create engine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This buffer will trace execution of the Script, only dumping
|
||||||
|
// out to stdout in the case that a test fails.
|
||||||
var debugBuf bytes.Buffer
|
var debugBuf bytes.Buffer
|
||||||
|
|
||||||
done := false
|
done := false
|
||||||
@ -366,13 +377,15 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
|
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
|
||||||
|
|
||||||
|
// Generate a payment and revocation pre-image to be used below.
|
||||||
revokePreimage := testHdSeed[:]
|
revokePreimage := testHdSeed[:]
|
||||||
revokeHash := fastsha256.Sum256(revokePreimage)
|
revokeHash := fastsha256.Sum256(revokePreimage)
|
||||||
|
|
||||||
paymentPreimage := revokeHash
|
paymentPreimage := revokeHash
|
||||||
paymentPreimage[0] ^= 1
|
paymentPreimage[0] ^= 1
|
||||||
paymentHash := fastsha256.Sum256(paymentPreimage[:])
|
paymentHash := fastsha256.Sum256(paymentPreimage[:])
|
||||||
|
|
||||||
|
// We'll also need some tests keys for alice and bob, and meta-data of
|
||||||
|
// the HTLC output.
|
||||||
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
testWalletPrivKey)
|
testWalletPrivKey)
|
||||||
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||||
@ -381,6 +394,7 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
|||||||
cltvTimeout := uint32(8)
|
cltvTimeout := uint32(8)
|
||||||
csvTimeout := uint32(5)
|
csvTimeout := uint32(5)
|
||||||
|
|
||||||
|
// Generate the raw HTLC redemption scripts, and its p2wsh counterpart.
|
||||||
htlcScript, err := receiverHTLCScript(cltvTimeout, csvTimeout,
|
htlcScript, err := receiverHTLCScript(cltvTimeout, csvTimeout,
|
||||||
aliceKeyPub, bobKeyPub, revokeHash[:], paymentHash[:])
|
aliceKeyPub, bobKeyPub, revokeHash[:], paymentHash[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -486,6 +500,8 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
|||||||
t.Fatalf("unable to create engine: %v", err)
|
t.Fatalf("unable to create engine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This buffer will trace execution of the Script, only dumping
|
||||||
|
// out to stdout in the case that a test fails.
|
||||||
var debugBuf bytes.Buffer
|
var debugBuf bytes.Buffer
|
||||||
|
|
||||||
done := false
|
done := false
|
||||||
|
76
rpcserver.go
76
rpcserver.go
@ -62,13 +62,13 @@ func (r *rpcServer) Stop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMany handles a request for a transaction create multiple specified
|
// addrPairsToOutputs converts a map describing a set of outputs to be created,
|
||||||
// outputs in parallel.
|
// the outputs themselves. The passed map pairs up an address, to a desired
|
||||||
func (r *rpcServer) SendMany(ctx context.Context,
|
// output value amount. Each address is converted to its corresponding pkScript
|
||||||
in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) {
|
// to be used within the constructed output(s).
|
||||||
|
func addrPairsToOutputs(addrPairs map[string]int64) ([]*wire.TxOut, error) {
|
||||||
outputs := make([]*wire.TxOut, 0, len(in.AddrToAmount))
|
outputs := make([]*wire.TxOut, 0, len(addrPairs))
|
||||||
for addr, amt := range in.AddrToAmount {
|
for addr, amt := range addrPairs {
|
||||||
addr, err := btcutil.DecodeAddress(addr, activeNetParams)
|
addr, err := btcutil.DecodeAddress(addr, activeNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -82,14 +82,50 @@ func (r *rpcServer) SendMany(ctx context.Context,
|
|||||||
outputs = append(outputs, wire.NewTxOut(amt, pkscript))
|
outputs = append(outputs, wire.NewTxOut(amt, pkscript))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instruct the wallet to create an transaction paying to the specified
|
return outputs, nil
|
||||||
// outputs, selecting any coins with at least one confirmation.
|
}
|
||||||
txid, err := r.server.lnwallet.SendOutputs(outputs, defaultAccount, 1)
|
|
||||||
|
// sendCoinsOnChain makes an on-chain transaction in or to send coins to one or
|
||||||
|
// more addresses specified in the passed payment map. The payment map maps an
|
||||||
|
// address to a specified output value to be sent to that address.
|
||||||
|
func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64) (*wire.ShaHash, error) {
|
||||||
|
outputs, err := addrPairsToOutputs(paymentMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcsLog.Infof("Generated txid: %v", txid.String())
|
return r.server.lnwallet.SendOutputs(outputs, defaultAccount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCoins executes a request to send coins to a particular address. Unlike
|
||||||
|
// SendMany, this RPC call only allows creating a single output at a time.
|
||||||
|
func (r *rpcServer) SendCoins(ctx context.Context,
|
||||||
|
in *lnrpc.SendCoinsRequest) (*lnrpc.SendCoinsResponse, error) {
|
||||||
|
|
||||||
|
rpcsLog.Infof("[sendcoins] addr=%v, amt=%v", in.Addr, btcutil.Amount(in.Amount))
|
||||||
|
|
||||||
|
paymentMap := map[string]int64{in.Addr: in.Amount}
|
||||||
|
txid, err := r.sendCoinsOnChain(paymentMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcsLog.Infof("[sendcoins] spend generated txid: %v", txid.String())
|
||||||
|
|
||||||
|
return &lnrpc.SendCoinsResponse{Txid: txid.String()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMany handles a request for a transaction create multiple specified
|
||||||
|
// outputs in parallel.
|
||||||
|
func (r *rpcServer) SendMany(ctx context.Context,
|
||||||
|
in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) {
|
||||||
|
|
||||||
|
txid, err := r.sendCoinsOnChain(in.AddrToAmount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcsLog.Infof("[sendmany] spend generated txid: %v", txid.String())
|
||||||
|
|
||||||
return &lnrpc.SendManyResponse{Txid: txid.String()}, nil
|
return &lnrpc.SendManyResponse{Txid: txid.String()}, nil
|
||||||
}
|
}
|
||||||
@ -119,7 +155,7 @@ func (r *rpcServer) NewAddress(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcsLog.Infof("Generated new address: %v", addr.String())
|
rpcsLog.Infof("[newaddress] addr=%v", addr.String())
|
||||||
return &lnrpc.NewAddressResponse{Address: addr.String()}, nil
|
return &lnrpc.NewAddressResponse{Address: addr.String()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +168,7 @@ func (r *rpcServer) ConnectPeer(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
idAtHost := fmt.Sprintf("%v@%v", in.Addr.PubKeyHash, in.Addr.Host)
|
idAtHost := fmt.Sprintf("%v@%v", in.Addr.PubKeyHash, in.Addr.Host)
|
||||||
rpcsLog.Debugf("Attempting to connect to peer %v", idAtHost)
|
rpcsLog.Debugf("[connectpeer] peer=%v", idAtHost)
|
||||||
|
|
||||||
peerAddr, err := lndc.LnAddrFromString(idAtHost)
|
peerAddr, err := lndc.LnAddrFromString(idAtHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -155,7 +191,7 @@ func (r *rpcServer) ConnectPeer(ctx context.Context,
|
|||||||
func (r *rpcServer) OpenChannel(ctx context.Context,
|
func (r *rpcServer) OpenChannel(ctx context.Context,
|
||||||
in *lnrpc.OpenChannelRequest) (*lnrpc.OpenChannelResponse, error) {
|
in *lnrpc.OpenChannelRequest) (*lnrpc.OpenChannelResponse, error) {
|
||||||
|
|
||||||
rpcsLog.Tracef("Recieved request to openchannel to peerid(%v) "+
|
rpcsLog.Tracef("[openchannel] request to peerid(%v) "+
|
||||||
"allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId,
|
"allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId,
|
||||||
in.LocalFundingAmount, in.RemoteFundingAmount, in.NumConfs)
|
in.LocalFundingAmount, in.RemoteFundingAmount, in.NumConfs)
|
||||||
|
|
||||||
@ -171,7 +207,7 @@ func (r *rpcServer) OpenChannel(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcsLog.Tracef("Opened channel with peerid(%v), ChannelPoint(%v)",
|
rpcsLog.Tracef("[openchannel] success peerid(%v), ChannelPoint(%v)",
|
||||||
in.TargetPeerId, resp)
|
in.TargetPeerId, resp)
|
||||||
|
|
||||||
return &lnrpc.OpenChannelResponse{
|
return &lnrpc.OpenChannelResponse{
|
||||||
@ -191,12 +227,12 @@ func (r *rpcServer) CloseChannel(ctx context.Context,
|
|||||||
index := in.ChannelPoint.OutputIndex
|
index := in.ChannelPoint.OutputIndex
|
||||||
txid, err := wire.NewShaHash(in.ChannelPoint.FundingTxid)
|
txid, err := wire.NewShaHash(in.ChannelPoint.FundingTxid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("(closechannel) invalid txid: %v", err)
|
rpcsLog.Errorf("[closechannel] invalid txid: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
targetChannelPoint := wire.NewOutPoint(txid, index)
|
targetChannelPoint := wire.NewOutPoint(txid, index)
|
||||||
|
|
||||||
rpcsLog.Tracef("Recieved closechannel request for ChannelPoint(%v)",
|
rpcsLog.Tracef("[closechannel] request for ChannelPoint(%v)",
|
||||||
targetChannelPoint)
|
targetChannelPoint)
|
||||||
|
|
||||||
resp, err := r.server.CloseChannel(targetChannelPoint)
|
resp, err := r.server.CloseChannel(targetChannelPoint)
|
||||||
@ -213,7 +249,7 @@ func (r *rpcServer) CloseChannel(ctx context.Context,
|
|||||||
func (r *rpcServer) ListPeers(ctx context.Context,
|
func (r *rpcServer) ListPeers(ctx context.Context,
|
||||||
in *lnrpc.ListPeersRequest) (*lnrpc.ListPeersResponse, error) {
|
in *lnrpc.ListPeersRequest) (*lnrpc.ListPeersResponse, error) {
|
||||||
|
|
||||||
rpcsLog.Tracef("recieved listpeers request")
|
rpcsLog.Tracef("[listpeers] request")
|
||||||
|
|
||||||
serverPeers := r.server.Peers()
|
serverPeers := r.server.Peers()
|
||||||
resp := &lnrpc.ListPeersResponse{
|
resp := &lnrpc.ListPeersResponse{
|
||||||
@ -250,7 +286,7 @@ func (r *rpcServer) ListPeers(ctx context.Context,
|
|||||||
resp.Peers = append(resp.Peers, peer)
|
resp.Peers = append(resp.Peers, peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcsLog.Tracef("listpeers yielded %v peers", serverPeers)
|
rpcsLog.Debugf("[listpeers] yielded %v peers", serverPeers)
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
@ -288,7 +324,7 @@ func (r *rpcServer) WalletBalance(ctx context.Context,
|
|||||||
balance = outputSum.ToBTC()
|
balance = outputSum.ToBTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcsLog.Debugf("walletbalance query response: %v", balance)
|
rpcsLog.Debugf("[walletbalance] balance=%v", balance)
|
||||||
|
|
||||||
return &lnrpc.WalletBalanceResponse{balance}, nil
|
return &lnrpc.WalletBalanceResponse{balance}, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user