rpcserver: add REST workarounds, implement new RPC calls

This commit adds a few workarounds in order to concurrently support the
REST proxy as well as the regular gRPC interface. Additionally,
concrete support for the following RPC calls has been added:
GetTransactions, SubscriptTransactions, SubscribeInvoices, and
NewWitnessAddress.
This commit is contained in:
Olaoluwa Osuntokun 2016-10-15 14:41:11 -07:00
parent 566cd86a1d
commit 7b953c9c61
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
5 changed files with 145 additions and 8 deletions

@ -219,7 +219,7 @@ func WriteMessage(w io.Writer, msg Message, pver uint32, btcnet wire.BitcoinNet)
return 0, nil return 0, nil
} }
// Write the head first. // Write the header first.
n, err := w.Write(hw.Bytes()) n, err := w.Write(hw.Bytes())
totalBytes += n totalBytes += n
if err != nil { if err != nil {

@ -1155,6 +1155,9 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
p.Disconnect() p.Disconnect()
return return
} }
// TODO(roasbeef): add pre-image to DB in order to swipe
// repeated r-values
case *lnwire.CommitSignature: case *lnwire.CommitSignature:
// We just received a new update to our local commitment chain, // We just received a new update to our local commitment chain,
// validate this new commitment, closing the link if invalid. // validate this new commitment, closing the link if invalid.

@ -167,7 +167,22 @@ func (r *rpcServer) NewAddress(ctx context.Context,
return &lnrpc.NewAddressResponse{Address: addr.String()}, nil return &lnrpc.NewAddressResponse{Address: addr.String()}, nil
} }
// NewWitnessAddress returns a new native witness address under the control of
// the local wallet.
func (r *rpcServer) NewWitnessAddress(ctx context.Context,
in *lnrpc.NewWitnessAddressRequest) (*lnrpc.NewAddressResponse, error) {
addr, err := r.server.lnwallet.NewAddress(lnwallet.WitnessPubKey, false)
if err != nil {
return nil, err
}
rpcsLog.Infof("[newaddress] addr=%v", addr.String())
return &lnrpc.NewAddressResponse{Address: addr.String()}, nil
}
// ConnectPeer attempts to establish a connection to a remote peer. // ConnectPeer attempts to establish a connection to a remote peer.
// TODO(roasbeef): also return pubkey and/or identity hash?
func (r *rpcServer) ConnectPeer(ctx context.Context, func (r *rpcServer) ConnectPeer(ctx context.Context,
in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) { in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) {
@ -674,13 +689,29 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
func (r *rpcServer) LookupInvoice(ctx context.Context, func (r *rpcServer) LookupInvoice(ctx context.Context,
req *lnrpc.PaymentHash) (*lnrpc.Invoice, error) { req *lnrpc.PaymentHash) (*lnrpc.Invoice, error) {
if len(req.RHash) != 32 { var (
return nil, fmt.Errorf("payment hash must be exactly "+ payHash [32]byte
"32 bytes, is instead %v", len(req.RHash)) rHash []byte
err error
)
// If the RHash as a raw string was provided, then decode that and use
// that directly. Otherwise, we use the raw bytes provided.
if req.RHashStr != "" {
rHash, err = hex.DecodeString(req.RHashStr)
if err != nil {
return nil, err
}
} else {
rHash = req.RHash
} }
var payHash [32]byte // Ensure that the payment hash is *exactly* 32-bytes.
copy(payHash[:], req.RHash) if len(rHash) != 0 && len(rHash) != 32 {
return nil, fmt.Errorf("payment hash must be exactly "+
"32 bytes, is instead %v", len(rHash))
}
copy(payHash[:], rHash)
rpcsLog.Tracef("[lookupinvoice] searching for invoice %x", payHash[:]) rpcsLog.Tracef("[lookupinvoice] searching for invoice %x", payHash[:])
@ -731,6 +762,109 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
}, nil }, nil
} }
// SubscribeInvoices returns a uni-directional stream (sever -> client) for
// notifying the client of newly added/settled invoices.
func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
updateStream lnrpc.Lightning_SubscribeInvoicesServer) error {
invoiceClient := r.server.invoices.SubscribeNotifications()
defer invoiceClient.Cancel()
for {
select {
// TODO(roasbeef): include newly added invoices?
case settledInvoice := <-invoiceClient.SettledInvoices:
invoice := &lnrpc.Invoice{
Memo: string(settledInvoice.Memo[:]),
Receipt: settledInvoice.Receipt[:],
RPreimage: settledInvoice.Terms.PaymentPreimage[:],
Value: int64(settledInvoice.Terms.Value),
Settled: settledInvoice.Terms.Settled,
}
if err := updateStream.Send(invoice); err != nil {
return err
}
case <-r.quit:
return nil
}
}
return nil
}
// SubscribeTransactions creates a uni-directional stream (server -> client) in
// which any newly discovered transactions relevant to the wallet are sent
// over.
func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
updateStream lnrpc.Lightning_SubscribeTransactionsServer) error {
txClient, err := r.server.lnwallet.SubscribeTransactions()
if err != nil {
return err
}
defer txClient.Cancel()
for {
select {
case tx := <-txClient.ConfirmedTransactions():
detail := &lnrpc.Transaction{
TxHash: tx.Hash.String(),
Amount: tx.Value.ToBTC(),
NumConfirmations: tx.NumConfirmations,
BlockHash: tx.BlockHash.String(),
TimeStamp: tx.Timestamp,
TotalFees: tx.TotalFees,
}
if err := updateStream.Send(detail); err != nil {
return err
}
case tx := <-txClient.UnconfirmedTransactions():
detail := &lnrpc.Transaction{
TxHash: tx.Hash.String(),
Amount: tx.Value.ToBTC(),
TimeStamp: tx.Timestamp,
TotalFees: tx.TotalFees,
}
if err := updateStream.Send(detail); err != nil {
return err
}
case <-r.quit:
return nil
}
}
return nil
}
// GetTransactions returns a list of describing all the known transactions
// relevant to the wallet.
func (r *rpcServer) GetTransactions(context.Context,
*lnrpc.GetTransactionsRequest) (*lnrpc.TransactionDetails, error) {
transactions, err := r.server.lnwallet.ListTransactionDetails()
if err != nil {
return nil, err
}
txDetails := &lnrpc.TransactionDetails{
Transactions: make([]*lnrpc.Transaction, len(transactions)),
}
for i, tx := range transactions {
txDetails.Transactions[i] = &lnrpc.Transaction{
TxHash: tx.Hash.String(),
Amount: tx.Value.ToBTC(),
NumConfirmations: tx.NumConfirmations,
BlockHash: tx.BlockHash.String(),
TimeStamp: tx.Timestamp,
TotalFees: tx.TotalFees,
}
}
return txDetails, nil
}
// ShowRoutingTable returns a table-formatted dump of the known routing
// topology from the PoV of the source node.
func (r *rpcServer) ShowRoutingTable(ctx context.Context, func (r *rpcServer) ShowRoutingTable(ctx context.Context,
in *lnrpc.ShowRoutingTableRequest) (*lnrpc.ShowRoutingTableResponse, error) { in *lnrpc.ShowRoutingTableRequest) (*lnrpc.ShowRoutingTableResponse, error) {
@ -738,7 +872,7 @@ func (r *rpcServer) ShowRoutingTable(ctx context.Context,
rtCopy := r.server.routingMgr.GetRTCopy() rtCopy := r.server.routingMgr.GetRTCopy()
channels := make([]*lnrpc.RoutingTableLink, 0) var channels []*lnrpc.RoutingTableLink
for _, channel := range rtCopy.AllChannels() { for _, channel := range rtCopy.AllChannels() {
channels = append(channels, channels = append(channels,
&lnrpc.RoutingTableLink{ &lnrpc.RoutingTableLink{

@ -48,7 +48,6 @@ type server struct {
bio lnwallet.BlockChainIO bio lnwallet.BlockChainIO
lnwallet *lnwallet.LightningWallet lnwallet *lnwallet.LightningWallet
// TODO(roasbeef): add to constructor
fundingMgr *fundingManager fundingMgr *fundingManager
chanDB *channeldb.DB chanDB *channeldb.DB

@ -134,6 +134,7 @@ out:
midStageOutputs <- immatureUtxo midStageOutputs <- immatureUtxo
}() }()
} }
// TODO(roasbeef): rename to preschool and kindergarden
case midUtxo := <-midStageOutputs: case midUtxo := <-midStageOutputs:
// The transaction creating the output has been // The transaction creating the output has been
// created, so we move it from early stage to // created, so we move it from early stage to