With this change, transactions created via craftSweepTx will be
standard. Previously, p2wsh/p2pkh scripts passed in via SendCoins would
be weighted as p2wpkh scripts. With a feerate of 1 sat/vbyte,
transactions returned would be non-standard. Luckily, the critical
sweeper subsystem only used p2wpkh scripts so this only affected
callers from the rpcserver.
Also added is an integration test that fails if SendCoins manages
to generate a non-standard transaction. All script types are now
accounted for in getWeightEstimate, which now errors if an unknown
script type is passed in.
This removes the requirement that the zero-conf channel acceptor
flow use anchors. Also adds a fail-early check for minimum depth
zero in the non zero conf case. It would fail later, but it makes
more sense to fail immediately when receiving AcceptChannel.
This commit modifies the netann subsystem to use the peer's alias
for ChannelUpdates where appropriate (i.e. in case we are sending
the alias to the peer). It also modifies the loadActiveChannels
function in the peer package to handle upgrading a channel when the
scid-alias feature bit is turned on.
AddInvoice,AddHoldInvoice now issue invoices that include our
peer's aliases. Some extra sanity checks are included to ensure we
don't leak our confirmed SCID for a private channel.
feature-bit channels
This allows opening zero-conf chan-type, scid-alias chan-type, and
scid-alias feature-bit channels. scid-alias chan-type channels are
required to be private. Two paths are available for opening a zero-conf
channel:
* explicit chan-type negotiation
* LDK carve-out where chan-types are not used, LND is on the
receiving end, and a ChannelAcceptor is used to enable zero-conf
When a zero-conf channel is negotiated, the funding manager:
* sends a FundingLocked with an alias
* waits for a FundingLocked from the remote peer
* calls addToRouterGraph to persist the channel using our alias in
the graph. The peer's alias is used to send them a ChannelUpdate.
* wait for six confirmations. If public, the alias edge in the
graph is deleted and replaced (not atomically) with the confirmed
edge. Our policy is also read-and-replaced, but the counterparty's
policy won't exist until they send it to us.
When a scid-alias-feature channel is negotiated, the funding manager:
* sends a FundingLocked with an alias:
* calls addToRouterGraph, sends ChannelUpdate with the confirmed SCID
since it exists.
* when six confirmations occurs, the edge is deleted and re-inserted
since the peer may have sent us an alias ChannelUpdate that we are
storing in the graph.
Since it is possible for a user to toggle the scid-alias-feature-bit
to on while channels exist in the funding manager, care has been taken
to ensure that an alias is ALWAYS sent in the funding_locked message
if this happens.
This intent of this change is to prevent privacy leaks when routing
with aliases and also to allow routing when using an alias. The
aliases are our aliases.
Introduces are two maps:
* aliasToReal:
This is an N->1 mapping for a channel. The keys are the set of
aliases and the value is the confirmed, on-chain SCID.
* baseIndex:
This is also an N->1 mapping for a channel. The keys are the set
of aliases and the value is the "base" SCID (whatever is in the
OpenChannel.ShortChannelID field). There is also a base->base
mapping, so not all keys are aliases.
The above maps are populated when a link is added to the switch and
when the channel has confirmed on-chain. The maps are not removed
from if the link is removed, but this is fine since forwarding won't
occur.
* getLinkByMapping
This function is introduced to adhere to the spec requirements that
using the confirmed SCID of a private, scid-alias-feature-bit
channel does not work. Lnd implements a stricter version of the spec
and disallows this behavior if the feature-bit was negotiated, rather
than just the channel type. The old, privacy-leak behavior is
preserved.
The spec also requires that if we must fail back an HTLC, the
ChannelUpdate must use the SCID of whatever was in the onion, to avoid
a privacy leak. This is also done by passing in the relevant SCID to
the mailbox and link. Lnd will also cancel back on the "incoming" side
if the InterceptableSwitch was used or if the link failed to decrypt
the onion. In this case, we are cautious and replace the SCID if an
alias exists.
This allows the router to determine what is and isn't an alias from
lnd's definition of an alias. Any ChannelAnnouncement that has an
alias ShortChannelID field is not verified on-chain. To prevent a
DoS vector from existing, the gossiper ensures that only the local
lnd node can send its ChannelAnnouncements to the router with an
alias ShortChannelID.
An OptionalMsgField has been added that allows outside subsystems
to provide a short channel id we should insert into a ChannelUpdate
that we then sign and send to our peer.
When the gossiper receives a ChannelUpdate, it will query the
alias manager by the passed-in FindBaseByAlias function to determine
if the short channel id in the ChannelUpdate points to a known
channel. If this lookup returns an error, we'll fallback to using
the original id in the ChannelUpdate when querying the router.
The lookup and potential fallback must occur in order to properly
lock the multimutex, query the correct router channels, and rate
limit the correct short channel id. An unfortunate side effect of
receiving ChannelUpdates from our peer that reference on of our
aliases rather than the real SCID is that we must store this policy.
Yet it is not broadcast-able. Care has been taken to ensure the
gossiper does not broadcast *any* ChannelUpdate with an alias SCID.
The cachedNetworkMsg uses the new processedNetworkMsg struct. This
is necessary so that delete-and-reinsert in the funding manager
doesn't process a ChannelUpdate twice and end up in a deadlock since
the err chan is no longer being used.
This introduces the a store for managing all things alias-related.
There are two maps:
* baseToSet:
This stores the "base" short channel id as the key. The value is
the set of all aliases issued for this channel. The "base" SCID is
whatever is stored in the OpenChannel's ShortChannelID member. For
everything other than zero-conf channels, this is the confirmed SCID.
For zero-conf channels, this is the very first alias assigned. This is
used mostly by the Switch to retrieve a set of aliases and determine
if it is safe to forward.
* aliasToBase:
This stores the reverse mapping of baseToSet. Each key is an alias
SCID and the value is the "base" SCID. This is exclusively used by
the gossiper to determine if an alias in a ChannelUpdate our peer
sends actually references a channel we know of.
The functions make use of the above two maps:
* AddLocalAlias:
This persists the {alias, base} pair in the database. The baseToSet
map is populated. The aliasToBase is optionally populated depending on
where this function is called from. Upgrade cases, where the
scid-alias feature bit is toggled and channels already exist, will
not persist to the gossip map. This is mainly to simplify the tangle
of logic that would otherwise occur.
* GetAliases:
This fetches the set of aliases by using the passed-in base SCID. This
is used in the Switch and other places where the alias set is needed.
* FindBaseSCID:
This fetches the base given an alias. This is used in the gossiper to
determine validity of a peer's ChannelUpdate that contains an alias.
* DeleteSixConfs:
This removes the aliasToBase map entry for the given "base". This is
used when the gossiper mappings are no longer needed, i.e. when the
channel has six confirmations and is public.
* PutPeerAlias:
Stores the peer's alias.
* GetPeerAlias:
Fetches the peer's alias.
* RequestAlias:
Generates an alias for us in the range 16000000:0:0 and
16250000:16777215:65535
This extends the Reservation arguments to include whether a pending
channel open has negotiated the zero-conf channel type, the scid-alias
channel type, and/or the scid-alias feature bit. The result of those
negotiates are stored in the OpenChannel's ChanType. The arguments to
NewChannelReservation have also been simplified.
This introduces a BigSize migration that is used to expand the width
of the ChannelStatus and ChannelType fields. Three channel "types"
are added - ZeroConfBit, ScidAliasChanBit, and ScidAliasFeatureBit.
ScidAliasChanBit denotes that the scid-alias channel type was
negotiated for the channel. ScidAliasFeatureBit denotes that the
scid-alias feature bit was negotiated during the *lifetime* of the
channel. Several helper functions on the OpenChannel struct are
exposed to aid callers from different packages.
The RefreshShortChanID has been renamed to Refresh.
A new function BroadcastHeight is used to guard access to the
mutable FundingBroadcastHeight member. This prevents data races.
This allows the zero-conf and scid-alias feature bits to be toggled
using the config. The feature bits are off by default to protect users
from accidentally incurring the risk of a zero-conf channel.
This defines the zero-conf feature bit, the scid-alias feature bit,
the zero-conf channel type, and the scid-alias channel type. It also
defines the dependency "tree" that exists for the feature bits.
The scid-alias feature bit signals that the node requires an alias
short channel id to be sent in funding_locked. The scid-alias channel
type requires that the channel is private, in addition to some other
forwarding-related privacy measures.
With this, extra calls to RemoveLink will wait for the link to
fully stop. This is accomplished by a map that stores a single stop
channel that callers to RemoveLink will listen on. This map is not
consulted when the Switch is shutting down and calls Stop on each
individual link. Though that could be added in the future, it is
not necessary.
Without this, calls to DisconnectPeer would bypass the
peerTerminationWatcher and allow subsequent connect requests to
go through before the peer's links were fully shut down. This could
lead to force closes.
This prevents a panic where an insert is attempted on a nil map via
updateActiveHTLCs. This panic would occur if the channel arbitrator
was in a buggy state, possibly introduced by power loss or via
SIGKILL.
This can cause an intermittent panic otherwise if bbolt remaps itself
via munmap and mmap. From bbolt's documentation:
* Byte slices returned from Bolt are only valid during a transaction.
Once the transaction has been committed or rolled back then the memory
they point to can be reused by a new page or can be unmapped from
virtual memory and you'll see an unexpected fault address panic when
accessing it.
This commit fixes OOO notifications between backlog and non-backlog
events by having the non-backlog goroutines wait on a chan that
signals that backlog processing is complete.
This commit also replaces usage of the canceled atomic variable with
the cancelChan to signal that delivery of an event should no longer
occur. Atomics do not make perfect "sequence" points as the atomic
may be checked too early and the end-result of delivering to a stopped
ntfnQueue is the same. Using the cancelChan ensures that we do not
hang on sending to ntfnQueue.
Prior to this change, if SubscribeSingleInvoice or
SubscribeNotifications was called, it was possible that a state
change would never be delivered to the client. The sequence of
events would be:
- delivery of backlog events with invoice in the Open state
- invoice goes to the Accepted state, no client to notify
- client added to map
This is fixed by adding the client to the map first. However, with
this change alone it then becomes possible for notifications to be
delivered out of order. This is addressed in a following commit.
This fixes a nil-pointer-dereference that would occur if this was
called for a settled AMP invoice. Terms.PaymentPreimage is always
false for AMP invoices.
This allows Switch-initiated payments to be failed back if they don't
make it into a commitment. Prior to this commit, a Switch-initiated
HTLC could get "lost" meaning the circuit wouldn't get deleted except
if conditions were "right" and the network result store would never
be made aware of the HTLC's fate. Switch-initiated HTLC's are now
passed to the link's mailbox to ensure they can be failed back.
This change also special-cases the ErrDuplicateKeystone error from
OpenCircuits(...) so that callers of updateCommitTx() in the link
don't send an Error to the peer if they encounter the keystone error.
With the first async change, the keystone error should now always
be recoverable.
On startup, we'll check whether we have the coop close chan status
and have already broadcasted a coop close txn, and then make a
decision on whether to restart the process based on that.
The main idea is that NotifyContractUpdate adds the ContractUpdate to
a map called unmerged. It is populated in Start by shallow-copying the
activeHTLCs map values (htlcSet). The htlcSets underlying maps are not
copied, and so unmerged will just contain pointers to them. This should
be fine since unmerged will not modify them. At the call-sites of
activeHTLCs, it is updated to include the unmerged sets. This happens
with a mutex and should not cause any data race, even though it is
copying the underlying map pointers. No persistence should be
necessary since on restart, activeHTLCs and unmerged will just be
populated again.
The counter-party shouldn't be doing this anyways as they would be
giving away a preimage for free. Them doing this would bork their
own channel due to open circuits not getting trimmed on startup.
Removing this faulty behavior also makes it easier to reason about
the circuit logic.
This also changes the chain_watcher and breacharbiter handoff. The
new logic ensures that the channel is only marked as pending closed
when the channel arbitrator has persisted the resolutions and commit
set.
This commit makes SendHTLC (we are the source) evaluate the dust
threshold of the outgoing channel against the default threshold of
500K satoshis. If the threshold is exceeded by adding this HTLC, we
fail backwards. It also makes handlePacketForward (we are forwarding)
evaluate the dust threshold of the incoming channel and the outgoing
channel and fails backwards if either channel's dust sum exceeds the
default threshold.
This commit extends the Mailbox interface with the SetDustClosure,
SetFeeRate, and DustPackets methods. This enables the mailbox to
report the dust exposure to the Switch when the Switch decides whether
to forward a dust packet. The dust is counted from the time an Add is
introduced via AddPacket until it is removed via AckPacket. This can
lead to some packets being counted twice before they are signed for,
but this is a trade-off between accuracy and simplicity.
It over-estimates the local or remote commitment's dust sum by
counting all updates in both updateLogs that are dust using the
trimmed-to-dust mechanism if applicable. The over-estimation is done
because ensuring an accurate counting is a trade-off between code
simplicity and accuracy.
When Alice receives a CommitSig near the end of the test, the test
assumed that she wouldn't reply with a RevokeAndAck message before
shutdownAssert was called. This led to a test flake. Instead, only
call shutdownAssert after we've received the RevokeAndAck.
This is necessary and is implied by BOLT#02. Both ChannelReserve
parameters should be above both DustLimit parameters. Otherwise,
it is possible for one side to have nothing at stake.
This commit updates call-sites to use the proper dust limits for
various script types. This also updates the default dust limit used
in the funding flow to be 354 satoshis instead of 573 satoshis.
Adds a new Brontide struct method tryLinkShutdown that attempts to
fetch the target link and calls ShutdownIfChannelClean on it. This
allows the coop close process to guarantee atomicity of the underlying
channel state. Also removes the UnregisterChannel method from the
chancloser's config as the link is shut down before the chancloser
is created.
This allows a caller to ensure to optimistically shut down the link
if the channel is clean. If the channel is not clean, an error is
returned and the link continues functioning as normal. The caller
should also call RemoveLink to ensure that the link isn't seen as
usable within the switch.
Adds a method to the LightningChannel struct called IsChannelClean
that returns a boolean telling the caller whether the channel state
is clean or not. Clean in this case means there are no lingering
updates to be signed for, no HTLC's active on either sides commitment
transaction, and no pending commitments on either side. This can be
used for dynamic commitments or during a strict cooperative close
process that ensures atomicity of the channel.
This commit fixes a buggy scenario where:
- a spend of the desired outpoint occurs
- RegisterSpend is called, not immediately notifying
- caller performs a historical dispatch, calling UpdateSpendDetails
- caller is notified on the Spend channel
- re-org occurs
- caller is not notified of the re-org
We fix this by correctly populating the spendsByHeight map when
dispatchSpendDetails is called. This mirrors the confirmation case.
After the error is received on the filter update errChan, update the
EndHeight if we're performing a historical scan. If a block was mined
after the call to RegisterConf/RegisterSpend but before the filter was
updated, then the block would not have the filter applied. This means
that a block containing the desired conf/spend parameters would be
undetected. Fix this by ensuring the historical scan also includes this
height, as it would previously not be included.
Moves filter handling logic for connecting blocks to the
connectFilteredBlock method. The disconnect logic now lives in
disconnectFilteredBlock. After updating the filter, drainChainUpdates
is called which will drain everything from the chainUpdates chan and
apply all updates by calling either connectFilteredBlock or
disconnectFilteredBlock. This will allow callers to update their
EndHeight if performing a historical dispatch, as blocks up to this
height may not have had the filter applied.
ConcurrentQueue has internal structures so if a filterUpdate exists
in it, the idea of draining the queue may not work reliably. The
update may exist in the ConcurrentQueue but may not be available via
ChanOut() when we're ready to drain the ConcurrentQueue. Fix this by
using a regular buffered chan, which will either have the update or
not have the update. Its size is set to 100 as our tests may generate
quite a bit of updates.
This mirrors the logic for the spend case. This prevents a double
dispatch scenario when combined with a later commit. Otherwise, the
confirmation would linger in the buffer which is not ideal.
This commit allows the peer to be tested without relying on a raw
htlcswitch.Switch pointer. This is accomplished by using a messageSwitch
interface and adding the CreateAndAddLink method to the
htlcswitch.Switch.