Skip to content

Commit

Permalink
Merge branch 'master' into blockBasedGracePeriod
Browse files Browse the repository at this point in the history
  • Loading branch information
Crypt-iQ committed Sep 20, 2017
2 parents 55779fc + 1379488 commit fad92ce
Show file tree
Hide file tree
Showing 24 changed files with 2,477 additions and 94 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ service. The exported API's are not yet stable, so be warned: they may change
drastically in the near future.

An automatically generated set of documentation for the RPC APIs can be found
at [api.lightning.community](api.lightning.community). A set of developer
at [api.lightning.community](http://api.lightning.community). A set of developer
resources including talks, articles, and example applications can be found at:
[dev.lightning.community](dev.lightning.community).
[dev.lightning.community](http://dev.lightning.community).

Finally, we also have an active
[Slack](https://join.slack.com/t/lightningcommunity/shared_invite/MjI4OTg3MzQ4MjI2LTE1MDMxNzM1NTMtNjlmOGYzOTI1Ng)
Expand Down
2 changes: 1 addition & 1 deletion channeldb/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ func (c *ChannelGraph) ChannelID(chanPoint *wire.OutPoint) (uint64, error) {
return ErrGraphNoEdgesFound
}
chanIndex := edges.Bucket(channelPointBucket)
if edges == nil {
if chanIndex == nil {
return ErrGraphNoEdgesFound
}

Expand Down
53 changes: 31 additions & 22 deletions cmd/lncli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"gopkg.in/macaroon-bakery.v1/bakery/checkers"
"gopkg.in/macaroon.v1"

"github.com/lightningnetwork/lnd/lnrpc"
Expand Down Expand Up @@ -73,28 +71,35 @@ func getClientConn(ctx *cli.Context) *grpc.ClientConn {
fatal(err)
}

// We add a time-based constraint to prevent replay of the
// macaroon. It's good for 60 seconds by default to make up for
// any discrepancy between client and server clocks, but leaking
// the macaroon before it becomes invalid makes it possible for
// an attacker to reuse the macaroon. In addition, the validity
// time of the macaroon is extended by the time the server clock
// is behind the client clock, or shortened by the time the
// server clock is ahead of the client clock (or invalid
// altogether if, in the latter case, this time is more than 60
// seconds).
// TODO(aakselrod): add better anti-replay protection.
macaroonTimeout := time.Duration(ctx.GlobalInt64("macaroontimeout"))
requestTimeout := time.Now().Add(time.Second * macaroonTimeout)
timeCaveat := checkers.TimeBeforeCaveat(requestTimeout)
mac.AddFirstPartyCaveat(timeCaveat.Condition)
macConstraints := []macaroons.Constraint{
// We add a time-based constraint to prevent replay of the
// macaroon. It's good for 60 seconds by default to make up for
// any discrepancy between client and server clocks, but leaking
// the macaroon before it becomes invalid makes it possible for
// an attacker to reuse the macaroon. In addition, the validity
// time of the macaroon is extended by the time the server clock
// is behind the client clock, or shortened by the time the
// server clock is ahead of the client clock (or invalid
// altogether if, in the latter case, this time is more than 60
// seconds).
// TODO(aakselrod): add better anti-replay protection.
macaroons.TimeoutConstraint(ctx.GlobalInt64("macaroontimeout")),

// Lock macaroon down to a specific IP address.
macaroons.IPLockConstraint(ctx.GlobalString("macaroonip")),

// ... Add more constraints if needed.
}

// Apply constraints to the macaroon.
constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...)
if err != nil {
fatal(err)
}

// Now we append the macaroon credentials to the dial options.
opts = append(
opts,
grpc.WithPerRPCCredentials(
macaroons.NewMacaroonCredential(mac)),
)
cred := macaroons.NewMacaroonCredential(constrainedMac)
opts = append(opts, grpc.WithPerRPCCredentials(cred))
}

conn, err := grpc.Dial(ctx.GlobalString("rpcserver"), opts...)
Expand Down Expand Up @@ -135,6 +140,10 @@ func main() {
Value: 60,
Usage: "anti-replay macaroon validity time in seconds",
},
cli.StringFlag{
Name: "macaroonip",
Usage: "if set, lock macaroon to specific IP address",
},
}
app.Commands = []cli.Command{
newAddressCommand,
Expand Down
1 change: 1 addition & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ type config struct {
RPCPort int `long:"rpcport" description:"The port for the rpc server"`
RESTPort int `long:"restport" description:"The port for the REST server"`
DebugHTLC bool `long:"debughtlc" description:"Activate the debug htlc mode. With the debug HTLC mode, all payments sent use a pre-determined R-Hash. Additionally, all HTLCs sent to a node with the debug HTLC R-Hash are immediately settled in the next available state transition."`
HodlHTLC bool `long:"hodlhtlc" description:"Activate the hodl HTLC mode. With hodl HTLC mode, all incoming HTLCs will be accepted by the receiving node, but no attempt will be made to settle the payment with the sender."`
MaxPendingChannels int `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`

Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"`
Expand Down
1 change: 0 additions & 1 deletion docker/lnd/start-lnd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ NETWORK=$(set_default "$NETWORK" "simnet")
CHAIN=$(set_default "$CHAIN" "bitcoin")

lnd \
--datadir="/data" \
--logdir="/data" \
"--$CHAIN.rpccert"="/rpc/rpc.cert" \
"--$CHAIN.active" \
Expand Down
24 changes: 12 additions & 12 deletions fundingmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
return
}

fndgLog.Infof("Recv'd fundingResponse for pendingID(%x)", pendingChanID)
fndgLog.Infof("Recv'd fundingResponse for pendingID(%x)", pendingChanID[:])

// We'll also specify the responder's preference for the number of
// required confirmations, and also the set of channel constraints
Expand Down Expand Up @@ -930,7 +930,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
}

fndgLog.Infof("pendingChan(%x): remote party proposes num_confs=%v, "+
"csv_delay=%v", pendingChanID, msg.MinAcceptDepth, msg.CsvDelay)
"csv_delay=%v", pendingChanID[:], msg.MinAcceptDepth, msg.CsvDelay)
fndgLog.Debugf("Remote party accepted commitment constraints: %v",
spew.Sdump(remoteContribution.ChannelConfig.ChannelConstraints))

Expand Down Expand Up @@ -967,7 +967,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
f.resMtx.Unlock()

fndgLog.Infof("Generated ChannelPoint(%v) for pendingID(%x)", outPoint,
pendingChanID)
pendingChanID[:])

fundingCreated := &lnwire.FundingCreated{
PendingChannelID: pendingChanID,
Expand Down Expand Up @@ -1002,8 +1002,8 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {

resCtx, err := f.getReservationCtx(peerKey, pendingChanID)
if err != nil {
fndgLog.Warnf("can't find reservation (peerID:%v, chanID:%v)",
peerKey, pendingChanID)
fndgLog.Warnf("can't find reservation (peerID:%v, chanID:%x)",
peerKey, pendingChanID[:])
return
}

Expand All @@ -1014,7 +1014,7 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
// TODO(roasbeef): make case (p vs P) consistent throughout
fundingOut := fmsg.msg.FundingPoint
fndgLog.Infof("completing pendingID(%x) with ChannelPoint(%v)",
pendingChanID, fundingOut)
pendingChanID[:], fundingOut)

// With all the necessary data available, attempt to advance the
// funding workflow to the next stage. If this succeeds then the
Expand Down Expand Up @@ -1059,7 +1059,7 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
f.barrierMtx.Unlock()

fndgLog.Infof("sending signComplete for pendingID(%x) over ChannelPoint(%v)",
pendingChanID, fundingOut)
pendingChanID[:], fundingOut)

// With their signature for our version of the commitment transaction
// verified, we can now send over our signature to the remote peer.
Expand Down Expand Up @@ -1178,8 +1178,8 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
resCtx, err := f.getReservationCtx(fmsg.peerAddress.IdentityKey,
pendingChanID)
if err != nil {
fndgLog.Warnf("Unable to find reservation (peerID:%v, chanID:%v)",
peerKey, pendingChanID)
fndgLog.Warnf("Unable to find reservation (peerID:%v, chanID:%x)",
peerKey, pendingChanID[:])
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
pendingChanID, []byte(err.Error()))
return
Expand Down Expand Up @@ -1208,7 +1208,7 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
}

fndgLog.Infof("Finalizing pendingID(%x) over ChannelPoint(%v), "+
"waiting for channel open on-chain", pendingChanID, fundingPoint)
"waiting for channel open on-chain", pendingChanID[:], fundingPoint)

// Send an update to the upstream client that the negotiation process
// is over.
Expand Down Expand Up @@ -2043,7 +2043,7 @@ func (f *fundingManager) cancelReservationCtx(peerKey *btcec.PublicKey,
pendingChanID [32]byte) (*reservationWithCtx, error) {

fndgLog.Infof("Cancelling funding reservation for node_key=%x, "+
"chan_id=%x", peerKey.SerializeCompressed(), pendingChanID)
"chan_id=%x", peerKey.SerializeCompressed(), pendingChanID[:])

ctx, err := f.getReservationCtx(peerKey, pendingChanID)
if err != nil {
Expand Down Expand Up @@ -2085,7 +2085,7 @@ func (f *fundingManager) getReservationCtx(peerKey *btcec.PublicKey,

if !ok {
return nil, errors.Errorf("unknown channel (id: %x)",
pendingChanID)
pendingChanID[:])
}

return resCtx, nil
Expand Down
13 changes: 13 additions & 0 deletions htlcswitch/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ type ChannelLinkConfig struct {
// with the debug htlc R-Hash are immediately settled in the next
// available state transition.
DebugHTLC bool

// HodlHTLC should be active if you want this node to refrain from
// settling all incoming HTLCs with the sender if it finds itself to be
// the exit node.
// NOTE: HodlHTLC should be active in conjunction with DebugHTLC.
HodlHTLC bool
}

// channelLink is the service which drives a channel's commitment update
Expand Down Expand Up @@ -1176,6 +1182,13 @@ func (l *channelLink) processLockedInHtlcs(
continue
}

if l.cfg.DebugHTLC && l.cfg.HodlHTLC {
log.Warnf("hodl HTLC mode enabled, " +
"will not attempt to settle " +
"HTLC with sender")
continue
}

preimage := invoice.Terms.PaymentPreimage
logIndex, err := l.channel.SettleHTLC(preimage)
if err != nil {
Expand Down
158 changes: 158 additions & 0 deletions invoice/amountunits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package invoice

import (
"fmt"
"strconv"

"github.com/lightningnetwork/lnd/lnwire"
)

var (
// toMSat is a map from a unit to a function that converts an amount
// of that unit to millisatoshis.
toMSat = map[byte]func(uint64) (lnwire.MilliSatoshi, error){
'm': mBtcToMSat,
'u': uBtcToMSat,
'n': nBtcToMSat,
'p': pBtcToMSat,
}

// fromMSat is a map from a unit to a function that converts an amount
// in millisatoshis to an amount of that unit.
fromMSat = map[byte]func(lnwire.MilliSatoshi) (uint64, error){
'm': mSatToMBtc,
'u': mSatToUBtc,
'n': mSatToNBtc,
'p': mSatToPBtc,
}
)

// mBtcToMSat converts the given amount in milliBTC to millisatoshis.
func mBtcToMSat(m uint64) (lnwire.MilliSatoshi, error) {
return lnwire.MilliSatoshi(m) * 100000000, nil
}

// uBtcToMSat converts the given amount in microBTC to millisatoshis.
func uBtcToMSat(u uint64) (lnwire.MilliSatoshi, error) {
return lnwire.MilliSatoshi(u * 100000), nil
}

// nBtcToMSat converts the given amount in nanoBTC to millisatoshis.
func nBtcToMSat(n uint64) (lnwire.MilliSatoshi, error) {
return lnwire.MilliSatoshi(n * 100), nil
}

// pBtcToMSat converts the given amount in picoBTC to millisatoshis.
func pBtcToMSat(p uint64) (lnwire.MilliSatoshi, error) {
if p < 10 {
return 0, fmt.Errorf("minimum amount is 10p")
}
if p%10 != 0 {
return 0, fmt.Errorf("amount %d pBTC not expressible in msat",
p)
}
return lnwire.MilliSatoshi(p / 10), nil
}

// mSatToMBtc converts the given amount in millisatoshis to milliBTC.
func mSatToMBtc(msat lnwire.MilliSatoshi) (uint64, error) {
if msat%100000000 != 0 {
return 0, fmt.Errorf("%d msat not expressible "+
"in mBTC", msat)
}
return uint64(msat / 100000000), nil
}

// mSatToUBtc converts the given amount in millisatoshis to microBTC.
func mSatToUBtc(msat lnwire.MilliSatoshi) (uint64, error) {
if msat%100000 != 0 {
return 0, fmt.Errorf("%d msat not expressible "+
"in uBTC", msat)
}
return uint64(msat / 100000), nil
}

// mSatToNBtc converts the given amount in millisatoshis to nanoBTC.
func mSatToNBtc(msat lnwire.MilliSatoshi) (uint64, error) {
if msat%100 != 0 {
return 0, fmt.Errorf("%d msat not expressible in nBTC", msat)
}
return uint64(msat / 100), nil
}

// mSatToPBtc converts the given amount in milllisatoshis to picoBTC.
func mSatToPBtc(msat lnwire.MilliSatoshi) (uint64, error) {
return uint64(msat * 10), nil
}

// decodeAmount returns the amount encoded by the provided string in
// milllisatoshi.
func decodeAmount(amount string) (lnwire.MilliSatoshi, error) {
if len(amount) < 1 {
return 0, fmt.Errorf("amount must be non-empty")
}

// If last character is a digit, then the amount can just be
// interpreted as BTC.
char := amount[len(amount)-1]
digit := char - '0'
if digit >= 0 && digit <= 9 {
btc, err := strconv.ParseUint(amount, 10, 64)
if err != nil {
return 0, err
}
return lnwire.MilliSatoshi(btc) * mSatPerBtc, nil
}

// If not a digit, it must be part of the known units.
conv, ok := toMSat[char]
if !ok {
return 0, fmt.Errorf("unknown multiplier %c", char)
}

// Known unit.
num := amount[:len(amount)-1]
if len(num) < 1 {
return 0, fmt.Errorf("number must be non-empty")
}

am, err := strconv.ParseUint(num, 10, 64)
if err != nil {
return 0, err
}

return conv(am)
}

// encodeAmount encodes the provided millisatoshi amount using as few characters
// as possible.
func encodeAmount(msat lnwire.MilliSatoshi) (string, error) {
// If possible to express in BTC, that will always be the shortest
// representation.
if msat%mSatPerBtc == 0 {
return strconv.FormatInt(int64(msat/mSatPerBtc), 10), nil
}

// Should always be expressible in pico BTC.
pico, err := fromMSat['p'](msat)
if err != nil {
return "", fmt.Errorf("unable to express %d msat as pBTC: %v",
msat, err)
}
shortened := strconv.FormatUint(pico, 10) + "p"
for unit, conv := range fromMSat {
am, err := conv(msat)
if err != nil {
// Not expressible using this unit.
continue
}

// Save the shortest found representation.
str := strconv.FormatUint(am, 10) + string(unit)
if len(str) < len(shortened) {
shortened = str
}
}

return shortened, nil
}

0 comments on commit fad92ce

Please sign in to comment.