Merge pull request #784 from halseth/protocol-errors
Wire protocol errors
This commit is contained in:
commit
1c5f1885d9
@ -689,21 +689,40 @@ func (f *fundingManager) CancelPeerReservations(nodePub [33]byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// failFundingFlow will fail the active funding flow with the target peer,
|
// failFundingFlow will fail the active funding flow with the target peer,
|
||||||
// identified by its unique temporary channel ID. This method is send an error
|
// identified by its unique temporary channel ID. This method will send an
|
||||||
// to the remote peer, and also remove the reservation from our set of pending
|
// error to the remote peer, and also remove the reservation from our set of
|
||||||
// reservations.
|
// pending reservations.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): if peer disconnects, and haven't yet broadcast funding
|
// TODO(roasbeef): if peer disconnects, and haven't yet broadcast funding
|
||||||
// transaction, then all reservations should be cleared.
|
// transaction, then all reservations should be cleared.
|
||||||
func (f *fundingManager) failFundingFlow(peer *btcec.PublicKey,
|
func (f *fundingManager) failFundingFlow(peer *btcec.PublicKey,
|
||||||
tempChanID [32]byte, msg []byte) {
|
tempChanID [32]byte, fundingErr error) {
|
||||||
|
|
||||||
|
// We only send the exact error if it is part of out whitelisted set of
|
||||||
|
// errors (lnwire.ErrorCode or lnwallet.ReservationError).
|
||||||
|
var msg lnwire.ErrorData
|
||||||
|
switch e := fundingErr.(type) {
|
||||||
|
|
||||||
|
// Let the actual error message be sent to the remote.
|
||||||
|
case lnwallet.ReservationError:
|
||||||
|
msg = lnwire.ErrorData(e.Error())
|
||||||
|
|
||||||
|
// Send the status code.
|
||||||
|
case lnwire.ErrorCode:
|
||||||
|
msg = lnwire.ErrorData{byte(e)}
|
||||||
|
|
||||||
|
// We just send a generic error.
|
||||||
|
default:
|
||||||
|
msg = lnwire.ErrorData("funding failed due to internal error")
|
||||||
|
}
|
||||||
|
|
||||||
errMsg := &lnwire.Error{
|
errMsg := &lnwire.Error{
|
||||||
ChanID: tempChanID,
|
ChanID: tempChanID,
|
||||||
Data: msg,
|
Data: msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
fndgLog.Errorf("Failing funding flow: %v", spew.Sdump(errMsg))
|
fndgLog.Errorf("Failing funding flow: %v (%v)", fundingErr,
|
||||||
|
spew.Sdump(errMsg))
|
||||||
|
|
||||||
if _, err := f.cancelReservationCtx(peer, tempChanID); err != nil {
|
if _, err := f.cancelReservationCtx(peer, tempChanID); err != nil {
|
||||||
fndgLog.Errorf("unable to cancel reservation: %v", err)
|
fndgLog.Errorf("unable to cancel reservation: %v", err)
|
||||||
@ -819,8 +838,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
if len(f.activeReservations[peerIDKey]) >= cfg.MaxPendingChannels {
|
if len(f.activeReservations[peerIDKey]) >= cfg.MaxPendingChannels {
|
||||||
f.failFundingFlow(
|
f.failFundingFlow(
|
||||||
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
||||||
lnwire.ErrorData{byte(lnwire.ErrMaxPendingChannels)},
|
lnwire.ErrMaxPendingChannels)
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,8 +852,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
}
|
}
|
||||||
f.failFundingFlow(
|
f.failFundingFlow(
|
||||||
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
||||||
lnwire.ErrorData{byte(lnwire.ErrSynchronizingChain)},
|
lnwire.ErrSynchronizingChain)
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -844,8 +861,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
if msg.FundingAmount > maxFundingAmount {
|
if msg.FundingAmount > maxFundingAmount {
|
||||||
f.failFundingFlow(
|
f.failFundingFlow(
|
||||||
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
||||||
lnwire.ErrorData{byte(lnwire.ErrChanTooLarge)},
|
lnwire.ErrChanTooLarge)
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,7 +887,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("Unable to initialize reservation: %v", err)
|
fndgLog.Errorf("Unable to initialize reservation: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
msg.PendingChannelID, []byte(err.Error()))
|
msg.PendingChannelID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -890,10 +906,9 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
msg.MaxValueInFlight, msg.HtlcMinimum, msg.ChannelReserve,
|
msg.MaxValueInFlight, msg.HtlcMinimum, msg.ChannelReserve,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.failFundingFlow(
|
fndgLog.Errorf("Unaccaptable channel constraints: %v", err)
|
||||||
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
[]byte(fmt.Sprintf("Unacceptable channel "+
|
fmsg.msg.PendingChannelID, err,
|
||||||
"constraints: %v", err)),
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -951,9 +966,8 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
err = reservation.ProcessSingleContribution(remoteContribution)
|
err = reservation.ProcessSingleContribution(remoteContribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("unable to add contribution reservation: %v", err)
|
fndgLog.Errorf("unable to add contribution reservation: %v", err)
|
||||||
// TODO(roasbeef): verify only sending sane info over
|
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
msg.PendingChannelID, []byte(err.Error()))
|
msg.PendingChannelID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -985,7 +999,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("unable to send funding response to peer: %v", err)
|
fndgLog.Errorf("unable to send funding response to peer: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
msg.PendingChannelID, []byte(err.Error()))
|
msg.PendingChannelID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1028,11 +1042,9 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
|
|||||||
msg.MaxValueInFlight, msg.HtlcMinimum, msg.ChannelReserve,
|
msg.MaxValueInFlight, msg.HtlcMinimum, msg.ChannelReserve,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.failFundingFlow(
|
fndgLog.Warnf("Unacceptable channel constraints: %v", err)
|
||||||
fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
[]byte(fmt.Sprintf("Unacceptable channel "+
|
fmsg.msg.PendingChannelID, err)
|
||||||
"constraints: %v", err)),
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,7 +1082,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
|
|||||||
fndgLog.Errorf("Unable to process contribution from %v: %v",
|
fndgLog.Errorf("Unable to process contribution from %v: %v",
|
||||||
fmsg.peerAddress.IdentityKey, err)
|
fmsg.peerAddress.IdentityKey, err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
msg.PendingChannelID, []byte(err.Error()))
|
msg.PendingChannelID, err)
|
||||||
resCtx.err <- err
|
resCtx.err <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1115,7 +1127,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("Unable to parse signature: %v", err)
|
fndgLog.Errorf("Unable to parse signature: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
msg.PendingChannelID, []byte(err.Error()))
|
msg.PendingChannelID, err)
|
||||||
resCtx.err <- err
|
resCtx.err <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1123,7 +1135,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("Unable to send funding complete message: %v", err)
|
fndgLog.Errorf("Unable to send funding complete message: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
msg.PendingChannelID, []byte(err.Error()))
|
msg.PendingChannelID, err)
|
||||||
resCtx.err <- err
|
resCtx.err <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1177,7 +1189,7 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
|
|||||||
// TODO(roasbeef): better error logging: peerID, channelID, etc.
|
// TODO(roasbeef): better error logging: peerID, channelID, etc.
|
||||||
fndgLog.Errorf("unable to complete single reservation: %v", err)
|
fndgLog.Errorf("unable to complete single reservation: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
pendingChanID, []byte(err.Error()))
|
pendingChanID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1218,7 +1230,7 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("unable to parse signature: %v", err)
|
fndgLog.Errorf("unable to parse signature: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
pendingChanID, []byte(err.Error()))
|
pendingChanID, err)
|
||||||
deleteFromDatabase()
|
deleteFromDatabase()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1230,7 +1242,7 @@ func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
|
|||||||
if err := f.cfg.SendToPeer(peerKey, fundingSigned); err != nil {
|
if err := f.cfg.SendToPeer(peerKey, fundingSigned); err != nil {
|
||||||
fndgLog.Errorf("unable to send FundingSigned message: %v", err)
|
fndgLog.Errorf("unable to send FundingSigned message: %v", err)
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
pendingChanID, []byte(err.Error()))
|
pendingChanID, err)
|
||||||
deleteFromDatabase()
|
deleteFromDatabase()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1332,11 +1344,12 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
|
|||||||
delete(f.signedReservations, fmsg.msg.ChanID)
|
delete(f.signedReservations, fmsg.msg.ChanID)
|
||||||
f.resMtx.Unlock()
|
f.resMtx.Unlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
err := fmt.Sprintf("Unable to find signed reservation for "+
|
err := fmt.Errorf("Unable to find signed reservation for "+
|
||||||
"chan_id=%x", fmsg.msg.ChanID)
|
"chan_id=%x", fmsg.msg.ChanID)
|
||||||
fndgLog.Warnf(err)
|
fndgLog.Warnf(err.Error())
|
||||||
|
// TODO: add ErrChanNotFound?
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
pendingChanID, []byte(err))
|
pendingChanID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1346,8 +1359,9 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Warnf("Unable to find reservation (peerID:%v, chanID:%x)",
|
fndgLog.Warnf("Unable to find reservation (peerID:%v, chanID:%x)",
|
||||||
peerKey, pendingChanID[:])
|
peerKey, pendingChanID[:])
|
||||||
|
// TODO: add ErrChanNotFound?
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
pendingChanID, []byte(err.Error()))
|
pendingChanID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1369,7 +1383,7 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
|
|||||||
fndgLog.Errorf("Unable to complete reservation sign complete: %v", err)
|
fndgLog.Errorf("Unable to complete reservation sign complete: %v", err)
|
||||||
resCtx.err <- err
|
resCtx.err <- err
|
||||||
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
f.failFundingFlow(fmsg.peerAddress.IdentityKey,
|
||||||
pendingChanID, []byte(err.Error()))
|
pendingChanID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
lnwallet/errors.go
Normal file
108
lnwallet/errors.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package lnwallet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/roasbeef/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReservationError wraps certain errors returned during channel reservation
|
||||||
|
// that can be sent across the wire to the remote peer. Errors not being
|
||||||
|
// ReservationErrors will not be sent to the remote in case of a failed channel
|
||||||
|
// reservation, as they may contain private information.
|
||||||
|
type ReservationError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure ReservationError implements the error
|
||||||
|
// interface.
|
||||||
|
var _ error = (*ReservationError)(nil)
|
||||||
|
|
||||||
|
// ErrZeroCapacity returns an error indicating the funder attempted to put zero
|
||||||
|
// funds into the channel.
|
||||||
|
func ErrZeroCapacity() ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
errors.New("zero channel funds"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrChainMismatch returns an error indicating that the initiator tried to
|
||||||
|
// open a channel for an unknown chain.
|
||||||
|
func ErrChainMismatch(knownChain,
|
||||||
|
unknownChain *chainhash.Hash) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("Unknown chain=%v. Supported chain=%v",
|
||||||
|
unknownChain, knownChain),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrFunderBalanceDust returns an error indicating the initial balance of the
|
||||||
|
// funder is considered dust at the current commitment fee.
|
||||||
|
func ErrFunderBalanceDust(commitFee, funderBalance,
|
||||||
|
minBalance int64) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("Funder balance too small (%v) with fee=%v sat, "+
|
||||||
|
"minimum=%v sat required", funderBalance,
|
||||||
|
commitFee, minBalance),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCsvDelayTooLarge returns an error indicating that the CSV delay was to
|
||||||
|
// large to be accepted, along with the current max.
|
||||||
|
func ErrCsvDelayTooLarge(remoteDelay, maxDelay uint16) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("CSV delay too large: %v, max is %v",
|
||||||
|
remoteDelay, maxDelay),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrChanReserveTooLarge returns an error indicating that the chan reserve the
|
||||||
|
// remote is requiring, is too large to be accepted.
|
||||||
|
func ErrChanReserveTooLarge(reserve,
|
||||||
|
maxReserve btcutil.Amount) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("Channel reserve is too large: %v sat, max "+
|
||||||
|
"is %v sat", int64(reserve), int64(maxReserve)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMinHtlcTooLarge returns an error indicating that the MinHTLC value the
|
||||||
|
// remote required is too large to be accepted.
|
||||||
|
func ErrMinHtlcTooLarge(minHtlc,
|
||||||
|
maxMinHtlc lnwire.MilliSatoshi) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("Minimum HTLC value is too large: %v, max is %v",
|
||||||
|
minHtlc, maxMinHtlc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMaxHtlcNumTooLarge returns an error indicating that the 'max HTLCs in
|
||||||
|
// flight' value the remote required is too large to be accepted.
|
||||||
|
func ErrMaxHtlcNumTooLarge(maxHtlc, maxMaxHtlc uint16) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("maxHtlcs is too large: %d, max is %d",
|
||||||
|
maxHtlc, maxMaxHtlc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMaxHtlcNumTooSmall returns an error indicating that the 'max HTLCs in
|
||||||
|
// flight' value the remote required is too small to be accepted.
|
||||||
|
func ErrMaxHtlcNumTooSmall(maxHtlc, minMaxHtlc uint16) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("maxHtlcs is too small: %d, min is %d",
|
||||||
|
maxHtlc, minMaxHtlc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMaxValueInFlightTooSmall returns an error indicating that the 'max HTLC
|
||||||
|
// value in flight' the remote required is too small to be accepted.
|
||||||
|
func ErrMaxValueInFlightTooSmall(maxValInFlight,
|
||||||
|
minMaxValInFlight lnwire.MilliSatoshi) ReservationError {
|
||||||
|
return ReservationError{
|
||||||
|
fmt.Errorf("maxValueInFlight too small: %v, min is %v",
|
||||||
|
maxValInFlight, minMaxValInFlight),
|
||||||
|
}
|
||||||
|
}
|
@ -581,7 +581,7 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness,
|
|||||||
t.Fatalf("initialization should have failed due to " +
|
t.Fatalf("initialization should have failed due to " +
|
||||||
"insufficient local amount")
|
"insufficient local amount")
|
||||||
|
|
||||||
case !strings.Contains(err.Error(), "local output is too small"):
|
case !strings.Contains(err.Error(), "Funder balance too small"):
|
||||||
t.Fatalf("incorrect error: %v", err)
|
t.Fatalf("incorrect error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package lnwallet
|
package lnwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -186,9 +185,11 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount,
|
|||||||
//
|
//
|
||||||
// TODO(roasbeef): reject if 30% goes to fees? dust channel
|
// TODO(roasbeef): reject if 30% goes to fees? dust channel
|
||||||
if initiator && ourBalance.ToSatoshis() <= 2*DefaultDustLimit() {
|
if initiator && ourBalance.ToSatoshis() <= 2*DefaultDustLimit() {
|
||||||
return nil, fmt.Errorf("unable to init reservation, with "+
|
return nil, ErrFunderBalanceDust(
|
||||||
"fee=%v sat/kw, local output is too small: %v sat",
|
int64(commitFee),
|
||||||
int64(commitFee), int64(ourBalance.ToSatoshis()))
|
int64(ourBalance.ToSatoshis()),
|
||||||
|
int64(2*DefaultDustLimit()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next we'll set the channel type based on what we can ascertain about
|
// Next we'll set the channel type based on what we can ascertain about
|
||||||
@ -282,8 +283,9 @@ func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16,
|
|||||||
|
|
||||||
// Fail if we consider csvDelay excessively large.
|
// Fail if we consider csvDelay excessively large.
|
||||||
// TODO(halseth): find a more scientific choice of value.
|
// TODO(halseth): find a more scientific choice of value.
|
||||||
if csvDelay > 10000 {
|
const maxDelay = 10000
|
||||||
return fmt.Errorf("csvDelay is too large: %d", csvDelay)
|
if csvDelay > maxDelay {
|
||||||
|
return ErrCsvDelayTooLarge(csvDelay, maxDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if we consider the channel reserve to be too large.
|
// Fail if we consider the channel reserve to be too large.
|
||||||
@ -291,8 +293,7 @@ func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16,
|
|||||||
// channel capacity.
|
// channel capacity.
|
||||||
maxChanReserve := r.partialState.Capacity / 5
|
maxChanReserve := r.partialState.Capacity / 5
|
||||||
if chanReserve > maxChanReserve {
|
if chanReserve > maxChanReserve {
|
||||||
return fmt.Errorf("chanReserve is too large: %g",
|
return ErrChanReserveTooLarge(chanReserve, maxChanReserve)
|
||||||
chanReserve.ToBTC())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if the minimum HTLC value is too large. If this is
|
// Fail if the minimum HTLC value is too large. If this is
|
||||||
@ -302,29 +303,28 @@ func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16,
|
|||||||
// it wants.
|
// it wants.
|
||||||
// TODO(halseth): set a reasonable/dynamic value.
|
// TODO(halseth): set a reasonable/dynamic value.
|
||||||
if minHtlc > maxValueInFlight {
|
if minHtlc > maxValueInFlight {
|
||||||
return fmt.Errorf("minimum HTLC value is too large: %g",
|
return ErrMinHtlcTooLarge(minHtlc, maxValueInFlight)
|
||||||
r.ourContribution.MinHTLC.ToBTC())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if maxHtlcs is above the maximum allowed number of 483.
|
// Fail if maxHtlcs is above the maximum allowed number of 483.
|
||||||
// This number is specified in BOLT-02.
|
// This number is specified in BOLT-02.
|
||||||
if maxHtlcs > uint16(MaxHTLCNumber/2) {
|
if maxHtlcs > uint16(MaxHTLCNumber/2) {
|
||||||
return fmt.Errorf("maxHtlcs is too large: %d", maxHtlcs)
|
return ErrMaxHtlcNumTooLarge(maxHtlcs, uint16(MaxHTLCNumber/2))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if we consider maxHtlcs too small. If this is too small
|
// Fail if we consider maxHtlcs too small. If this is too small
|
||||||
// we cannot offer many HTLCs to the remote.
|
// we cannot offer many HTLCs to the remote.
|
||||||
const minNumHtlc = 5
|
const minNumHtlc = 5
|
||||||
if maxHtlcs < minNumHtlc {
|
if maxHtlcs < minNumHtlc {
|
||||||
return fmt.Errorf("maxHtlcs is too small: %d", maxHtlcs)
|
return ErrMaxHtlcNumTooSmall(maxHtlcs, minNumHtlc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if we consider maxValueInFlight too small. We currently
|
// Fail if we consider maxValueInFlight too small. We currently
|
||||||
// require the remote to at least allow minNumHtlc * minHtlc
|
// require the remote to at least allow minNumHtlc * minHtlc
|
||||||
// in flight.
|
// in flight.
|
||||||
if maxValueInFlight < minNumHtlc*minHtlc {
|
if maxValueInFlight < minNumHtlc*minHtlc {
|
||||||
return fmt.Errorf("maxValueInFlight is too small: %g",
|
return ErrMaxValueInFlightTooSmall(maxValueInFlight,
|
||||||
maxValueInFlight.ToBTC())
|
minNumHtlc*minHtlc)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ourContribution.ChannelConfig.CsvDelay = csvDelay
|
r.ourContribution.ChannelConfig.CsvDelay = csvDelay
|
||||||
|
@ -479,8 +479,8 @@ func (l *LightningWallet) InitChannelReservation(
|
|||||||
func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg) {
|
func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg) {
|
||||||
// It isn't possible to create a channel with zero funds committed.
|
// It isn't possible to create a channel with zero funds committed.
|
||||||
if req.fundingAmount+req.capacity == 0 {
|
if req.fundingAmount+req.capacity == 0 {
|
||||||
req.err <- fmt.Errorf("cannot have channel with zero " +
|
err := ErrZeroCapacity()
|
||||||
"satoshis funded")
|
req.err <- err
|
||||||
req.resp <- nil
|
req.resp <- nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -488,9 +488,9 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
|||||||
// If the funding request is for a different chain than the one the
|
// If the funding request is for a different chain than the one the
|
||||||
// wallet is aware of, then we'll reject the request.
|
// wallet is aware of, then we'll reject the request.
|
||||||
if !bytes.Equal(l.Cfg.NetParams.GenesisHash[:], req.chainHash[:]) {
|
if !bytes.Equal(l.Cfg.NetParams.GenesisHash[:], req.chainHash[:]) {
|
||||||
req.err <- fmt.Errorf("unable to create channel reservation "+
|
err := ErrChainMismatch(l.Cfg.NetParams.GenesisHash,
|
||||||
"for chain=%v, wallet is on chain=%v",
|
req.chainHash)
|
||||||
req.chainHash, l.Cfg.NetParams.GenesisHash)
|
req.err <- err
|
||||||
req.resp <- nil
|
req.resp <- nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,13 @@ func (e ErrorCode) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error returns the human redable version of the target ErrorCode.
|
||||||
|
//
|
||||||
|
// Satisfies the Error interface.
|
||||||
|
func (e ErrorCode) Error() string {
|
||||||
|
return e.String()
|
||||||
|
}
|
||||||
|
|
||||||
// ErrorData is a set of bytes associated with a particular sent error. A
|
// ErrorData is a set of bytes associated with a particular sent error. A
|
||||||
// receiving node SHOULD only print out data verbatim if the string is composed
|
// receiving node SHOULD only print out data verbatim if the string is composed
|
||||||
// solely of printable ASCII characters. For reference, the printable character
|
// solely of printable ASCII characters. For reference, the printable character
|
||||||
|
Loading…
Reference in New Issue
Block a user