Merge pull request #1017 from halseth/contract-court-without-on-chain

Contract court acting on confirmed chain events
This commit is contained in:
Olaoluwa Osuntokun 2018-04-25 17:13:24 -07:00 committed by GitHub
commit 86fd9e361e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1722 additions and 840 deletions

View File

@ -696,6 +696,22 @@ func (b *breachArbiter) breachObserver(
brarLog.Debugf("Breach observer for ChannelPoint(%v) started ", brarLog.Debugf("Breach observer for ChannelPoint(%v) started ",
chanPoint) chanPoint)
gracefullyExit := func() {
// Launch a goroutine to cancel out this contract within the
// breachArbiter's main goroutine.
b.wg.Add(1)
go func() {
defer b.wg.Done()
select {
case b.settledContracts <- chanPoint:
case <-b.quit:
}
}()
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
}
select { select {
// A read from this channel indicates that the contract has been // A read from this channel indicates that the contract has been
// settled cooperatively so we exit as our duties are no longer needed. // settled cooperatively so we exit as our duties are no longer needed.
@ -704,36 +720,14 @@ func (b *breachArbiter) breachObserver(
// The channel has been closed cooperatively, so we're done here. // The channel has been closed cooperatively, so we're done here.
case <-chainEvents.CooperativeClosure: case <-chainEvents.CooperativeClosure:
// Launch a goroutine to cancel out this contract within the gracefullyExit()
// breachArbiter's main goroutine.
b.wg.Add(1)
go func() {
defer b.wg.Done()
select {
case b.settledContracts <- chanPoint:
case <-b.quit:
}
}()
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
// The channel has been closed by a normal means: force closing with // The channel has been closed by a normal means: force closing with
// the latest commitment transaction. // the latest commitment transaction.
case <-chainEvents.UnilateralClosure: case <-chainEvents.LocalUnilateralClosure:
// Launch a goroutine to cancel out this contract within the gracefullyExit()
// breachArbiter's main goroutine. case <-chainEvents.RemoteUnilateralClosure:
b.wg.Add(1) gracefullyExit()
go func() {
defer b.wg.Done()
select {
case b.settledContracts <- chanPoint:
case <-b.quit:
}
}()
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
// A read from this channel indicates that a channel breach has been // A read from this channel indicates that a channel breach has been
// detected! So we notify the main coordination goroutine with the // detected! So we notify the main coordination goroutine with the

View File

@ -951,11 +951,12 @@ func TestBreachHandoffSuccess(t *testing.T) {
// Instantiate a breach arbiter to handle the breach of alice's channel. // Instantiate a breach arbiter to handle the breach of alice's channel.
alicePoint := alice.ChannelPoint() alicePoint := alice.ChannelPoint()
spendEvents := contractcourt.ChainEventSubscription{ spendEvents := contractcourt.ChainEventSubscription{
UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
CooperativeClosure: make(chan struct{}, 1), LocalUnilateralClosure: make(chan *contractcourt.LocalUnilateralCloseInfo, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1), CooperativeClosure: make(chan struct{}, 1),
ProcessACK: make(chan error, 1), ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
ChanPoint: *alicePoint, ProcessACK: make(chan error, 1),
ChanPoint: *alicePoint,
Cancel: func() { Cancel: func() {
}, },
} }
@ -1039,11 +1040,12 @@ func TestBreachHandoffFail(t *testing.T) {
// Instantiate a breach arbiter to handle the breach of alice's channel. // Instantiate a breach arbiter to handle the breach of alice's channel.
alicePoint := alice.ChannelPoint() alicePoint := alice.ChannelPoint()
spendEvents := contractcourt.ChainEventSubscription{ spendEvents := contractcourt.ChainEventSubscription{
UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
CooperativeClosure: make(chan struct{}, 1), LocalUnilateralClosure: make(chan *contractcourt.LocalUnilateralCloseInfo, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1), CooperativeClosure: make(chan struct{}, 1),
ProcessACK: make(chan error, 1), ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
ChanPoint: *alicePoint, ProcessACK: make(chan error, 1),
ChanPoint: *alicePoint,
Cancel: func() { Cancel: func() {
}, },
} }

View File

@ -285,6 +285,38 @@ type ChannelCommitment struct {
// * lets just walk through // * lets just walk through
} }
// ChannelStatus is used to indicate whether an OpenChannel is in the default
// usable state, or a state where it shouldn't be used.
type ChannelStatus uint8
var (
// Default is the normal state of an open channel.
Default ChannelStatus = 0
// Borked indicates that the channel has entered an irreconcilable
// state, triggered by a state desynchronization or channel breach.
// Channels in this state should never be added to the htlc switch.
Borked ChannelStatus = 1
// CommitmentBroadcasted indicates that a commitment for this channel
// has been broadcasted.
CommitmentBroadcasted ChannelStatus = 2
)
// String returns a human-readable representation of the ChannelStatus.
func (c ChannelStatus) String() string {
switch c {
case Default:
return "Default"
case Borked:
return "Borked"
case CommitmentBroadcasted:
return "CommitmentBroadcasted"
default:
return "Unknown"
}
}
// OpenChannel encapsulates the persistent and dynamic state of an open channel // OpenChannel encapsulates the persistent and dynamic state of an open channel
// with a remote node. An open channel supports several options for on-disk // with a remote node. An open channel supports several options for on-disk
// serialization depending on the exact context. Full (upon channel creation) // serialization depending on the exact context. Full (upon channel creation)
@ -322,10 +354,9 @@ type OpenChannel struct {
// negotiate fees, or close the channel. // negotiate fees, or close the channel.
IsInitiator bool IsInitiator bool
// IsBorked indicates that the channel has entered an irreconcilable // ChanStatus is the current status of this channel. If it is not in
// state, triggered by a state desynchronization or channel breach. // the state Default, it should not be used for forwarding payments.
// Channels in this state should never be added to the htlc switch. ChanStatus ChannelStatus
IsBorked bool
// FundingBroadcastHeight is the height in which the funding // FundingBroadcastHeight is the height in which the funding
// transaction was broadcast. This value can be used by higher level // transaction was broadcast. This value can be used by higher level
@ -571,6 +602,20 @@ func (c *OpenChannel) MarkBorked() error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
return c.putChanStatus(Borked)
}
// MarkCommitmentBroadcasted marks the channel as a commitment transaction has
// been broadcast, either our own or the remote, and we should watch the chain
// for it to confirm before taking any further action.
func (c *OpenChannel) MarkCommitmentBroadcasted() error {
c.Lock()
defer c.Unlock()
return c.putChanStatus(CommitmentBroadcasted)
}
func (c *OpenChannel) putChanStatus(status ChannelStatus) error {
if err := c.Db.Update(func(tx *bolt.Tx) error { if err := c.Db.Update(func(tx *bolt.Tx) error {
chanBucket, err := updateChanBucket(tx, c.IdentityPub, chanBucket, err := updateChanBucket(tx, c.IdentityPub,
&c.FundingOutpoint, c.ChainHash) &c.FundingOutpoint, c.ChainHash)
@ -583,14 +628,15 @@ func (c *OpenChannel) MarkBorked() error {
return err return err
} }
channel.IsBorked = true channel.ChanStatus = status
return putOpenChannel(chanBucket, channel) return putOpenChannel(chanBucket, channel)
}); err != nil { }); err != nil {
return err return err
} }
c.IsBorked = true // Update the in-memory representation to keep it in sync with the DB.
c.ChanStatus = status
return nil return nil
} }
@ -1478,8 +1524,8 @@ func (c *OpenChannel) FindPreviousState(updateNum uint64) (*ChannelCommitment, e
} }
// ClosureType is an enum like structure that details exactly _how_ a channel // ClosureType is an enum like structure that details exactly _how_ a channel
// was closed. Three closure types are currently possible: cooperative, force, // was closed. Three closure types are currently possible: none, cooperative,
// and breach. // local force close, remote force close, and (remote) breach.
type ClosureType uint8 type ClosureType uint8
const ( const (
@ -1487,21 +1533,25 @@ const (
// cooperatively. This means that both channel peers were online and // cooperatively. This means that both channel peers were online and
// signed a new transaction paying out the settled balance of the // signed a new transaction paying out the settled balance of the
// contract. // contract.
CooperativeClose ClosureType = iota CooperativeClose ClosureType = 0
// ForceClose indicates that one peer unilaterally broadcast their // LocalForceClose indicates that we have unilaterally broadcast our
// current commitment state on-chain. // current commitment state on-chain.
ForceClose LocalForceClose ClosureType = 1
// BreachClose indicates that one peer attempted to broadcast a prior // RemoteForceClose indicates that the remote peer has unilaterally
// _revoked_ channel state. // broadcast their current commitment state on-chain.
BreachClose RemoteForceClose ClosureType = 4
// BreachClose indicates that the remote peer attempted to broadcast a
// prior _revoked_ channel state.
BreachClose ClosureType = 2
// FundingCanceled indicates that the channel never was fully opened // FundingCanceled indicates that the channel never was fully opened
// before it was marked as closed in the database. This can happen if // before it was marked as closed in the database. This can happen if
// we or the remote fail at some point during the opening workflow, or // we or the remote fail at some point during the opening workflow, or
// we timeout waiting for the funding transaction to be confirmed. // we timeout waiting for the funding transaction to be confirmed.
FundingCanceled FundingCanceled ClosureType = 3
) )
// ChannelCloseSummary contains the final state of a channel at the point it // ChannelCloseSummary contains the final state of a channel at the point it
@ -1549,8 +1599,9 @@ type ChannelCloseSummary struct {
// outstanding outgoing HTLC's at the time of channel closure. // outstanding outgoing HTLC's at the time of channel closure.
TimeLockedBalance btcutil.Amount TimeLockedBalance btcutil.Amount
// CloseType details exactly _how_ the channel was closed. Three // CloseType details exactly _how_ the channel was closed. Five closure
// closure types are possible: cooperative, force, and breach. // types are possible: cooperative, local force, remote force, breach
// and funding canceled.
CloseType ClosureType CloseType ClosureType
// IsPending indicates whether this channel is in the 'pending close' // IsPending indicates whether this channel is in the 'pending close'
@ -1804,7 +1855,7 @@ func putChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error {
if err := writeElements(&w, if err := writeElements(&w,
channel.ChanType, channel.ChainHash, channel.FundingOutpoint, channel.ChanType, channel.ChainHash, channel.FundingOutpoint,
channel.ShortChanID, channel.IsPending, channel.IsInitiator, channel.ShortChanID, channel.IsPending, channel.IsInitiator,
channel.IsBorked, channel.FundingBroadcastHeight, channel.ChanStatus, channel.FundingBroadcastHeight,
channel.NumConfsRequired, channel.ChannelFlags, channel.NumConfsRequired, channel.ChannelFlags,
channel.IdentityPub, channel.Capacity, channel.TotalMSatSent, channel.IdentityPub, channel.Capacity, channel.TotalMSatSent,
channel.TotalMSatReceived, channel.TotalMSatReceived,
@ -1912,7 +1963,7 @@ func fetchChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error {
if err := readElements(r, if err := readElements(r,
&channel.ChanType, &channel.ChainHash, &channel.FundingOutpoint, &channel.ChanType, &channel.ChainHash, &channel.FundingOutpoint,
&channel.ShortChanID, &channel.IsPending, &channel.IsInitiator, &channel.ShortChanID, &channel.IsPending, &channel.IsInitiator,
&channel.IsBorked, &channel.FundingBroadcastHeight, &channel.ChanStatus, &channel.FundingBroadcastHeight,
&channel.NumConfsRequired, &channel.ChannelFlags, &channel.NumConfsRequired, &channel.ChannelFlags,
&channel.IdentityPub, &channel.Capacity, &channel.TotalMSatSent, &channel.IdentityPub, &channel.Capacity, &channel.TotalMSatSent,
&channel.TotalMSatReceived, &channel.TotalMSatReceived,

View File

@ -606,7 +606,7 @@ func TestChannelStateTransition(t *testing.T) {
SettledBalance: btcutil.Amount(500), SettledBalance: btcutil.Amount(500),
TimeLockedBalance: btcutil.Amount(10000), TimeLockedBalance: btcutil.Amount(10000),
IsPending: false, IsPending: false,
CloseType: ForceClose, CloseType: RemoteForceClose,
} }
if err := updatedChannel[0].CloseChannel(closeSummary); err != nil { if err := updatedChannel[0].CloseChannel(closeSummary); err != nil {
t.Fatalf("unable to delete updated channel: %v", err) t.Fatalf("unable to delete updated channel: %v", err)
@ -770,7 +770,7 @@ func TestFetchClosedChannels(t *testing.T) {
Capacity: state.Capacity, Capacity: state.Capacity,
SettledBalance: state.LocalCommitment.LocalBalance.ToSatoshis(), SettledBalance: state.LocalCommitment.LocalBalance.ToSatoshis(),
TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000, TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000,
CloseType: ForceClose, CloseType: RemoteForceClose,
IsPending: true, IsPending: true,
} }
if err := state.CloseChannel(summary); err != nil { if err := state.CloseChannel(summary); err != nil {

View File

@ -148,10 +148,16 @@ func writeElement(w io.Writer, element interface{}) error {
return err return err
} }
case ChannelStatus:
if err := binary.Write(w, byteOrder, e); err != nil {
return err
}
case ClosureType: case ClosureType:
if err := binary.Write(w, byteOrder, e); err != nil { if err := binary.Write(w, byteOrder, e); err != nil {
return err return err
} }
case lnwire.FundingFlag: case lnwire.FundingFlag:
if err := binary.Write(w, byteOrder, e); err != nil { if err := binary.Write(w, byteOrder, e); err != nil {
return err return err
@ -321,10 +327,16 @@ func readElement(r io.Reader, element interface{}) error {
*e = msg *e = msg
case *ChannelStatus:
if err := binary.Read(r, byteOrder, e); err != nil {
return err
}
case *ClosureType: case *ClosureType:
if err := binary.Read(r, byteOrder, e); err != nil { if err := binary.Read(r, byteOrder, e); err != nil {
return err return err
} }
case *lnwire.FundingFlag: case *lnwire.FundingFlag:
if err := binary.Read(r, byteOrder, e); err != nil { if err := binary.Read(r, byteOrder, e); err != nil {
return err return err

View File

@ -312,23 +312,60 @@ func (d *DB) fetchNodeChannels(chainBucket *bolt.Bucket) ([]*OpenChannel, error)
} }
// FetchAllChannels attempts to retrieve all open channels currently stored // FetchAllChannels attempts to retrieve all open channels currently stored
// within the database. // within the database, including pending open, fully open and channels waiting
// for a closing transaction to confirm.
func (d *DB) FetchAllChannels() ([]*OpenChannel, error) { func (d *DB) FetchAllChannels() ([]*OpenChannel, error) {
return fetchChannels(d, false) var channels []*OpenChannel
// TODO(halseth): fetch all in one db tx.
openChannels, err := d.FetchAllOpenChannels()
if err != nil {
return nil, err
}
channels = append(channels, openChannels...)
pendingChannels, err := d.FetchPendingChannels()
if err != nil {
return nil, err
}
channels = append(channels, pendingChannels...)
waitingClose, err := d.FetchWaitingCloseChannels()
if err != nil {
return nil, err
}
channels = append(channels, waitingClose...)
return channels, nil
}
// FetchAllOpenChannels will return all channels that have the funding
// transaction confirmed, and is not waiting for a closing transaction to be
// confirmed.
func (d *DB) FetchAllOpenChannels() ([]*OpenChannel, error) {
return fetchChannels(d, false, false)
} }
// FetchPendingChannels will return channels that have completed the process of // FetchPendingChannels will return channels that have completed the process of
// generating and broadcasting funding transactions, but whose funding // generating and broadcasting funding transactions, but whose funding
// transactions have yet to be confirmed on the blockchain. // transactions have yet to be confirmed on the blockchain.
func (d *DB) FetchPendingChannels() ([]*OpenChannel, error) { func (d *DB) FetchPendingChannels() ([]*OpenChannel, error) {
return fetchChannels(d, true) return fetchChannels(d, true, false)
}
// FetchWaitingCloseChannels will return all channels that have been opened,
// but now is waiting for a closing transaction to be confirmed.
func (d *DB) FetchWaitingCloseChannels() ([]*OpenChannel, error) {
return fetchChannels(d, false, true)
} }
// fetchChannels attempts to retrieve channels currently stored in the // fetchChannels attempts to retrieve channels currently stored in the
// database. The pendingOnly parameter determines whether only pending channels // database. The pending parameter determines whether only pending channels
// will be returned. If no active channels exist within the network, then // will be returned, or only open channels will be returned. The waitingClose
// ErrNoActiveChannels is returned. // parameter determines wheter only channels waiting for a closing transaction
func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) { // to be confirmed should be returned. If no active channels exist within the
// network, then ErrNoActiveChannels is returned.
func fetchChannels(d *DB, pending, waitingClose bool) ([]*OpenChannel, error) {
var channels []*OpenChannel var channels []*OpenChannel
err := d.View(func(tx *bolt.Tx) error { err := d.View(func(tx *bolt.Tx) error {
@ -377,23 +414,36 @@ func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) {
"channel for chain_hash=%x, "+ "channel for chain_hash=%x, "+
"node_key=%x: %v", chainHash[:], k, err) "node_key=%x: %v", chainHash[:], k, err)
} }
// TODO(roasbeef): simplify for _, channel := range nodeChans {
if pendingOnly { if channel.IsPending != pending {
for _, channel := range nodeChans { continue
if channel.IsPending {
channels = append(channels, channel)
}
} }
} else {
channels = append(channels, nodeChans...) // If the channel is in any other state
// than Default, then it means it is
// waiting to be closed.
channelWaitingClose :=
channel.ChanStatus != Default
// Only include it if we requested
// channels with the same waitingClose
// status.
if channelWaitingClose != waitingClose {
continue
}
channels = append(channels, channel)
} }
return nil return nil
}) })
}) })
}) })
if err != nil {
return nil, err
}
return channels, err return channels, nil
} }
// FetchClosedChannels attempts to fetch all closed channels from the database. // FetchClosedChannels attempts to fetch all closed channels from the database.

View File

@ -112,13 +112,19 @@ const (
// so yet. // so yet.
StateBroadcastCommit ArbitratorState = 1 StateBroadcastCommit ArbitratorState = 1
// StateCommitmentBroadcasted is a state that indicates that the
// attendant has broadcasted the commitment transaction, and is now
// waiting for it to confirm.
StateCommitmentBroadcasted ArbitratorState = 6
// StateContractClosed is a state that indicates the contract has // StateContractClosed is a state that indicates the contract has
// already been "closed". At this point, we can now examine our active // already been "closed", meaning the commitment is confirmed on chain.
// contracts, in order to create the proper resolver for each one. // At this point, we can now examine our active contracts, in order to
// create the proper resolver for each one.
StateContractClosed ArbitratorState = 2 StateContractClosed ArbitratorState = 2
// StateWaitingFullResolution is a state that indicates that the // StateWaitingFullResolution is a state that indicates that the
// commitment transaction has been broadcast, and the attendant is now // commitment transaction has been confirmed, and the attendant is now
// waiting for all unresolved contracts to be fully resolved. // waiting for all unresolved contracts to be fully resolved.
StateWaitingFullResolution ArbitratorState = 3 StateWaitingFullResolution ArbitratorState = 3
@ -142,6 +148,9 @@ func (a ArbitratorState) String() string {
case StateBroadcastCommit: case StateBroadcastCommit:
return "StateBroadcastCommit" return "StateBroadcastCommit"
case StateCommitmentBroadcasted:
return "StateCommitmentBroadcasted"
case StateContractClosed: case StateContractClosed:
return "StateContractClosed" return "StateContractClosed"

View File

@ -231,14 +231,9 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel,
return chanMachine.ForceClose() return chanMachine.ForceClose()
}, },
CloseChannel: func(summary *channeldb.ChannelCloseSummary) error { MarkCommitmentBroadcasted: channel.MarkCommitmentBroadcasted,
log.Tracef("ChannelArbitrator(%v): closing "+ ChainArbitratorConfig: c.cfg,
"channel", chanPoint) ChainEvents: chanEvents,
return channel.CloseChannel(summary)
},
ChainArbitratorConfig: c.cfg,
ChainEvents: chanEvents,
} }
// The final component needed is an arbitrator log that the arbitrator // The final component needed is an arbitrator log that the arbitrator

View File

@ -16,6 +16,13 @@ import (
"github.com/roasbeef/btcutil" "github.com/roasbeef/btcutil"
) )
// LocalUnilateralCloseInfo encapsulates all the informnation we need to act
// on a local force close that gets confirmed.
type LocalUnilateralCloseInfo struct {
*chainntnfs.SpendDetail
*lnwallet.LocalForceCloseSummary
}
// ChainEventSubscription is a struct that houses a subscription to be notified // ChainEventSubscription is a struct that houses a subscription to be notified
// for any on-chain events related to a channel. There are three types of // for any on-chain events related to a channel. There are three types of
// possible on-chain events: a cooperative channel closure, a unilateral // possible on-chain events: a cooperative channel closure, a unilateral
@ -25,13 +32,16 @@ type ChainEventSubscription struct {
// ChanPoint is that channel that chain events will be dispatched for. // ChanPoint is that channel that chain events will be dispatched for.
ChanPoint wire.OutPoint ChanPoint wire.OutPoint
// UnilateralClosure is a channel that will be sent upon in the event that // RemoteUnilateralClosure is a channel that will be sent upon in the
// the remote party broadcasts their latest version of the commitment // event that the remote party's commitment transaction is confirmed.
// transaction. RemoteUnilateralClosure chan *lnwallet.UnilateralCloseSummary
UnilateralClosure chan *lnwallet.UnilateralCloseSummary
// CooperativeClosure is a signal that will be sent upon once a cooperative // LocalUnilateralClosure is a channel that will be sent upon in the
// channel closure has been detected. // event that our commitment transaction is confirmed.
LocalUnilateralClosure chan *LocalUnilateralCloseInfo
// CooperativeClosure is a signal that will be sent upon once a
// cooperative channel closure has been detected confirmed.
// //
// TODO(roasbeef): or something else // TODO(roasbeef): or something else
CooperativeClosure chan struct{} CooperativeClosure chan struct{}
@ -180,7 +190,7 @@ func (c *chainWatcher) Start() error {
} }
spendNtfn, err := c.notifier.RegisterSpendNtfn( spendNtfn, err := c.notifier.RegisterSpendNtfn(
fundingOut, heightHint, true, fundingOut, heightHint, false,
) )
if err != nil { if err != nil {
return err return err
@ -226,10 +236,11 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs
clientID, c.chanState.FundingOutpoint) clientID, c.chanState.FundingOutpoint)
sub := &ChainEventSubscription{ sub := &ChainEventSubscription{
ChanPoint: c.chanState.FundingOutpoint, ChanPoint: c.chanState.FundingOutpoint,
UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
CooperativeClosure: make(chan struct{}, 1), LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1), CooperativeClosure: make(chan struct{}, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
Cancel: func() { Cancel: func() {
c.Lock() c.Lock()
delete(c.clientSubscriptions, clientID) delete(c.clientSubscriptions, clientID)
@ -240,6 +251,30 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs
if syncDispatch { if syncDispatch {
sub.ProcessACK = make(chan error, 1) sub.ProcessACK = make(chan error, 1)
// If this client is syncDispatch, we cannot safely delete it
// from our list of clients. This is because of a potential
// race at shutdown, where the client shuts down and calls
// Cancel(). In this case we must make sure the ChainWatcher
// doesn't think it has successfully handed off a contract
// breach to the client. We start a goroutine that will send an
// error on the ProcessACK channel until the ChainWatcher is
// shutdown.
sub.Cancel = func() {
c.wg.Add(1)
go func() {
defer c.wg.Done()
err := fmt.Errorf("cancelled")
for {
select {
case sub.ProcessACK <- err:
case <-c.quit:
return
}
}
}()
}
} }
c.Lock() c.Lock()
@ -307,6 +342,13 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
&commitmentHash, &commitmentHash,
) )
if isOurCommitment { if isOurCommitment {
if err := c.dispatchLocalForceClose(
commitSpend, *localCommit,
); err != nil {
log.Errorf("unable to handle local"+
"close for chan_point=%v: %v",
c.chanState.FundingOutpoint, err)
}
return return
} }
@ -349,7 +391,7 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
// has a fail crash _after_ accepting the new state, // has a fail crash _after_ accepting the new state,
// but _before_ sending their signature to us. // but _before_ sending their signature to us.
case broadcastStateNum >= remoteStateNum: case broadcastStateNum >= remoteStateNum:
if err := c.dispatchRemoteClose( if err := c.dispatchRemoteForceClose(
commitSpend, *remoteCommit, commitSpend, *remoteCommit,
); err != nil { ); err != nil {
log.Errorf("unable to handle remote "+ log.Errorf("unable to handle remote "+
@ -500,12 +542,77 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet
} }
// dispatchRemoteClose processes a detected unilateral channel closure by the // dispatchLocalForceClose processes a unilateral close by us being confirmed.
func (c *chainWatcher) dispatchLocalForceClose(
commitSpend *chainntnfs.SpendDetail,
localCommit channeldb.ChannelCommitment) error {
log.Infof("Local unilateral close of ChannelPoint(%v) "+
"detected", c.chanState.FundingOutpoint)
forceClose, err := lnwallet.NewLocalForceCloseSummary(
c.chanState, c.signer, c.pCache, commitSpend.SpendingTx,
localCommit,
)
if err != nil {
return err
}
// As we've detected that the channel has been closed, immediately
// delete the state from disk, creating a close summary for future
// usage by related sub-systems.
chanSnapshot := forceClose.ChanSnapshot
closeSummary := &channeldb.ChannelCloseSummary{
ChanPoint: chanSnapshot.ChannelPoint,
ChainHash: chanSnapshot.ChainHash,
ClosingTXID: forceClose.CloseTx.TxHash(),
RemotePub: &chanSnapshot.RemoteIdentity,
Capacity: chanSnapshot.Capacity,
CloseType: channeldb.LocalForceClose,
IsPending: true,
ShortChanID: c.chanState.ShortChanID,
CloseHeight: uint32(commitSpend.SpendingHeight),
}
// If our commitment output isn't dust or we have active HTLC's on the
// commitment transaction, then we'll populate the balances on the
// close channel summary.
if forceClose.CommitResolution != nil {
closeSummary.SettledBalance = chanSnapshot.LocalBalance.ToSatoshis()
closeSummary.TimeLockedBalance = chanSnapshot.LocalBalance.ToSatoshis()
}
for _, htlc := range forceClose.HtlcResolutions.OutgoingHTLCs {
htlcValue := btcutil.Amount(htlc.SweepSignDesc.Output.Value)
closeSummary.TimeLockedBalance += htlcValue
}
err = c.chanState.CloseChannel(closeSummary)
if err != nil {
return fmt.Errorf("unable to delete channel state: %v", err)
}
// With the event processed, we'll now notify all subscribers of the
// event.
closeInfo := &LocalUnilateralCloseInfo{commitSpend, forceClose}
c.Lock()
for _, sub := range c.clientSubscriptions {
select {
case sub.LocalUnilateralClosure <- closeInfo:
case <-c.quit:
c.Unlock()
return fmt.Errorf("exiting")
}
}
c.Unlock()
return nil
}
// dispatchRemoteForceClose processes a detected unilateral channel closure by the
// remote party. This function will prepare a UnilateralCloseSummary which will // remote party. This function will prepare a UnilateralCloseSummary which will
// then be sent to any subscribers allowing them to resolve all our funds in // then be sent to any subscribers allowing them to resolve all our funds in
// the channel on chain. Once this close summary is prepared, all registered // the channel on chain. Once this close summary is prepared, all registered
// subscribers will receive a notification of this event. // subscribers will receive a notification of this event.
func (c *chainWatcher) dispatchRemoteClose(commitSpend *chainntnfs.SpendDetail, func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDetail,
remoteCommit channeldb.ChannelCommitment) error { remoteCommit channeldb.ChannelCommitment) error {
log.Infof("Unilateral close of ChannelPoint(%v) "+ log.Infof("Unilateral close of ChannelPoint(%v) "+
@ -538,7 +645,7 @@ func (c *chainWatcher) dispatchRemoteClose(commitSpend *chainntnfs.SpendDetail,
// * get ACK from the consumer of the ntfn before writing to disk? // * get ACK from the consumer of the ntfn before writing to disk?
// * no harm in repeated ntfns: at least once semantics // * no harm in repeated ntfns: at least once semantics
select { select {
case sub.UnilateralClosure <- uniClose: case sub.RemoteUnilateralClosure <- uniClose:
case <-c.quit: case <-c.quit:
c.Unlock() c.Unlock()
return fmt.Errorf("exiting") return fmt.Errorf("exiting")

View File

@ -10,9 +10,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
) )
const ( const (
@ -91,11 +89,9 @@ type ChannelArbitratorConfig struct {
// eventually resolve all outputs on chain. // eventually resolve all outputs on chain.
ForceCloseChan func() (*lnwallet.LocalForceCloseSummary, error) ForceCloseChan func() (*lnwallet.LocalForceCloseSummary, error)
// CloseChannel is a function closure that marks a channel under watch // MarkCommitmentBroadcasted should mark the channel as the commitment
// as "closing". In this phase, we will no longer accept any updates to // being broadcast, and we are waiting for the commitment to confirm.
// the channel as the commitment transaction has been broadcast, and MarkCommitmentBroadcasted func() error
// possibly fully confirmed.
CloseChannel func(*channeldb.ChannelCloseSummary) error
// MarkChannelResolved is a function closure that serves to mark a // MarkChannelResolved is a function closure that serves to mark a
// channel as "fully resolved". A channel itself can be considered // channel as "fully resolved". A channel itself can be considered
@ -239,7 +235,7 @@ func (c *ChannelArbitrator) Start() error {
log.Infof("ChannelArbitrator(%v): starting state=%v", c.cfg.ChanPoint, log.Infof("ChannelArbitrator(%v): starting state=%v", c.cfg.ChanPoint,
c.state) c.state)
bestHash, bestHeight, err := c.cfg.ChainIO.GetBestBlock() _, bestHeight, err := c.cfg.ChainIO.GetBestBlock()
if err != nil { if err != nil {
return err return err
} }
@ -248,7 +244,7 @@ func (c *ChannelArbitrator) Start() error {
// on-chain state, and our set of active contracts. // on-chain state, and our set of active contracts.
startingState := c.state startingState := c.state
nextState, _, err := c.advanceState( nextState, _, err := c.advanceState(
uint32(bestHeight), bestHash, chainTrigger, nil, uint32(bestHeight), chainTrigger, nil,
) )
if err != nil { if err != nil {
return err return err
@ -280,7 +276,7 @@ func (c *ChannelArbitrator) Start() error {
// TODO(roasbeef): cancel if breached // TODO(roasbeef): cancel if breached
c.wg.Add(1) c.wg.Add(1)
go c.channelAttendant(bestHeight, bestHash) go c.channelAttendant(bestHeight)
return nil return nil
} }
@ -320,14 +316,18 @@ const (
// being attached. // being attached.
chainTrigger transitionTrigger = iota chainTrigger transitionTrigger = iota
// remotePeerTrigger is a transition trigger driven by actions of the
// remote peer.
remotePeerTrigger
// userTrigger is a transition trigger driven by user action. Examples // userTrigger is a transition trigger driven by user action. Examples
// of such a trigger include a user requesting a forgive closure of the // of such a trigger include a user requesting a force closure of the
// channel. // channel.
userTrigger userTrigger
// remoteCloseTrigger is a transition trigger driven by the remote
// peer's commitment being confirmed.
remoteCloseTrigger
// localCloseTrigger is a transition trigger driven by our commitment
// being confirmed.
localCloseTrigger
) )
// String returns a human readable string describing the passed // String returns a human readable string describing the passed
@ -337,12 +337,15 @@ func (t transitionTrigger) String() string {
case chainTrigger: case chainTrigger:
return "chainTrigger" return "chainTrigger"
case remotePeerTrigger: case remoteCloseTrigger:
return "remotePeerTrigger" return "remoteCloseTrigger"
case userTrigger: case userTrigger:
return "userTrigger" return "userTrigger"
case localCloseTrigger:
return "localCloseTrigger"
default: default:
return "unknown trigger" return "unknown trigger"
} }
@ -352,7 +355,7 @@ func (t transitionTrigger) String() string {
// the appropriate state transition if necessary. The next state we transition // the appropriate state transition if necessary. The next state we transition
// to is returned, Additionally, if the next transition results in a commitment // to is returned, Additionally, if the next transition results in a commitment
// broadcast, the commitment transaction itself is returned. // broadcast, the commitment transaction itself is returned.
func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Hash, func (c *ChannelArbitrator) stateStep(triggerHeight uint32,
trigger transitionTrigger) (ArbitratorState, *wire.MsgTx, error) { trigger transitionTrigger) (ArbitratorState, *wire.MsgTx, error) {
var ( var (
@ -364,16 +367,15 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
// If we're in the default state, then we'll check our set of actions // If we're in the default state, then we'll check our set of actions
// to see if while we were down, conditions have changed. // to see if while we were down, conditions have changed.
case StateDefault: case StateDefault:
log.Debugf("ChannelArbitrator(%v): new block (height=%v, "+ log.Debugf("ChannelArbitrator(%v): new block (height=%v) "+
"hash=%v) examining active HTLC's", "examining active HTLC's", c.cfg.ChanPoint,
c.cfg.ChanPoint, bestHeight, bestHash) triggerHeight)
// As a new block has been connected to the end of the main // As a new block has been connected to the end of the main
// chain, we'll check to see if we need to make any on-chain // chain, we'll check to see if we need to make any on-chain
// claims on behalf of the channel contract that we're // claims on behalf of the channel contract that we're
// arbitrating for. // arbitrating for.
chainActions := c.checkChainActions(uint32(bestHeight), chainActions := c.checkChainActions(triggerHeight, trigger)
trigger)
// If there are no actions to be made, then we'll remain in the // If there are no actions to be made, then we'll remain in the
// default state. If this isn't a self initiated event (we're // default state. If this isn't a self initiated event (we're
@ -409,10 +411,16 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
case userTrigger: case userTrigger:
nextState = StateBroadcastCommit nextState = StateBroadcastCommit
// Otherwise, if this state advance was triggered by the remote // Otherwise, if this state advance was triggered by a
// peer, then we'll jump straight to the state where the // commitment being confirmed on chain, then we'll jump
// contract has already been closed. // straight to the state where the contract has already been
case remotePeerTrigger: // closed.
case localCloseTrigger:
log.Errorf("ChannelArbitrator(%v): unexpected local "+
"commitment confirmed while in StateDefault",
c.cfg.ChanPoint)
fallthrough
case remoteCloseTrigger:
nextState = StateContractClosed nextState = StateContractClosed
} }
@ -456,60 +464,38 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
} }
} }
// As we've have broadcast the commitment transaction, we send if err := c.cfg.MarkCommitmentBroadcasted(); err != nil {
// out commitment output for incubation, but only if it wasn't log.Errorf("ChannelArbitrator(%v): unable to "+
// trimmed. We'll need to wait for a CSV timeout before we can "mark commitment broadcasted: %v",
// reclaim the funds. c.cfg.ChanPoint, err)
if closeSummary.CommitResolution != nil {
log.Infof("ChannelArbitrator(%v): sending commit "+
"output for incubation", c.cfg.ChanPoint)
err = c.cfg.IncubateOutputs(
c.cfg.ChanPoint, closeSummary.CommitResolution,
nil, nil,
)
if err != nil {
// TODO(roasbeef): check for AlreadyExists errors
log.Errorf("unable to incubate commitment "+
"output: %v", err)
return StateError, closeTx, err
}
} }
contractRes := ContractResolutions{ // We go to the StateCommitmentBroadcasted state, where we'll
CommitHash: closeTx.TxHash(), // be waiting for the commitment to be confirmed.
CommitResolution: closeSummary.CommitResolution, nextState = StateCommitmentBroadcasted
HtlcResolutions: *closeSummary.HtlcResolutions,
// In this state we have broadcasted our own commitment, and will need
// to wait for a commitment (not necessarily the one we broadcasted!)
// to be confirmed.
case StateCommitmentBroadcasted:
switch trigger {
// We are waiting for a commitment to be confirmed, so any
// other trigger will be ignored.
case chainTrigger, userTrigger:
log.Infof("ChannelArbitrator(%v): noop state %v",
c.cfg.ChanPoint, trigger)
nextState = StateCommitmentBroadcasted
// If this state advance was triggered by any of the
// commitments being confirmed, then we'll jump to the state
// where the contract has been closed.
case localCloseTrigger, remoteCloseTrigger:
log.Infof("ChannelArbitrator(%v): state %v, "+
" going to StateContractClosed",
c.cfg.ChanPoint, trigger)
nextState = StateContractClosed
} }
// Now that the transaction has been broadcast, we can mark
// that it has been closed to outside sub-systems.
err = c.markContractClosed(
closeTx, closeSummary.ChanSnapshot, &contractRes,
bestHeight,
)
if err != nil {
log.Errorf("unable to close contract: %v", err)
return StateError, closeTx, err
}
// With the channel force closed, we'll now log our
// resolutions, then advance our state forward.
log.Infof("ChannelArbitrator(%v): logging contract "+
"resolutions: commit=%v, num_htlcs=%v",
c.cfg.ChanPoint,
closeSummary.CommitResolution != nil,
len(closeSummary.HtlcResolutions.IncomingHTLCs)+
len(closeSummary.HtlcResolutions.OutgoingHTLCs))
err = c.log.LogContractResolutions(&contractRes)
if err != nil {
log.Errorf("unable to write resolutions: %v", err)
return StateError, closeTx, err
}
nextState = StateContractClosed
// If we're in this state, then the contract has been fully closed to // If we're in this state, then the contract has been fully closed to
// outside sub-systems, so we'll process the prior set of on-chain // outside sub-systems, so we'll process the prior set of on-chain
// contract actions and launch a set of resolvers. // contract actions and launch a set of resolvers.
@ -539,12 +525,32 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
break break
} }
// If we've have broadcast the commitment transaction, we send
// our commitment output for incubation, but only if it wasn't
// trimmed. We'll need to wait for a CSV timeout before we can
// reclaim the funds.
commitRes := contractResolutions.CommitResolution
if commitRes != nil && commitRes.MaturityDelay > 0 {
log.Infof("ChannelArbitrator(%v): sending commit "+
"output for incubation", c.cfg.ChanPoint)
err = c.cfg.IncubateOutputs(
c.cfg.ChanPoint, commitRes,
nil, nil,
)
if err != nil {
// TODO(roasbeef): check for AlreadyExists errors
log.Errorf("unable to incubate commitment "+
"output: %v", err)
return StateError, closeTx, err
}
}
// Now that we know we'll need to act, we'll process the htlc // Now that we know we'll need to act, we'll process the htlc
// actions, wen create the structures we need to resolve all // actions, wen create the structures we need to resolve all
// outstanding contracts. // outstanding contracts.
htlcResolvers, pktsToSend, err := c.prepContractResolutions( htlcResolvers, pktsToSend, err := c.prepContractResolutions(
chainActions, contractResolutions, uint32(bestHeight), chainActions, contractResolutions, triggerHeight,
trigger,
) )
if err != nil { if err != nil {
log.Errorf("ChannelArbitrator(%v): unable to "+ log.Errorf("ChannelArbitrator(%v): unable to "+
@ -601,7 +607,7 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
nextState = StateFullyResolved nextState = StateFullyResolved
log.Infof("ChannelPoint(%v) has been fully resolved "+ log.Infof("ChannelPoint(%v) has been fully resolved "+
"on-chain at height=%v", c.cfg.ChanPoint, bestHeight) "on-chain at height=%v", c.cfg.ChanPoint, triggerHeight)
return nextState, closeTx, c.cfg.MarkChannelResolved() return nextState, closeTx, c.cfg.MarkChannelResolved()
} }
@ -622,25 +628,25 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
// redundant transition, meaning that the state transition is a noop. The final // redundant transition, meaning that the state transition is a noop. The final
// param is a callback that allows the caller to execute an arbitrary action // param is a callback that allows the caller to execute an arbitrary action
// after each state transition. // after each state transition.
func (c *ChannelArbitrator) advanceState(currentHeight uint32, func (c *ChannelArbitrator) advanceState(triggerHeight uint32,
bestHash *chainhash.Hash, trigger transitionTrigger, trigger transitionTrigger, stateCallback func(ArbitratorState) error) (
stateCallback func(ArbitratorState) error) (ArbitratorState, *wire.MsgTx, error) { ArbitratorState, *wire.MsgTx, error) {
var ( var (
priorState ArbitratorState priorState ArbitratorState
forceCloseTx *wire.MsgTx forceCloseTx *wire.MsgTx
) )
log.Tracef("ChannelArbitrator(%v): attempting state step with "+
"trigger=%v", c.cfg.ChanPoint, trigger)
// We'll continue to advance our state forward until the state we // We'll continue to advance our state forward until the state we
// transition to is that same state that we started at. // transition to is that same state that we started at.
for { for {
priorState = c.state priorState = c.state
log.Tracef("ChannelArbitrator(%v): attempting state step with "+
"trigger=%v from state=%v", c.cfg.ChanPoint, trigger,
priorState)
nextState, closeTx, err := c.stateStep( nextState, closeTx, err := c.stateStep(
currentHeight, bestHash, trigger, triggerHeight, trigger,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -926,7 +932,6 @@ func (c *ChannelArbitrator) checkChainActions(height uint32,
// are properly resolved. // are properly resolved.
func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap, func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap,
contractResolutions *ContractResolutions, height uint32, contractResolutions *ContractResolutions, height uint32,
trigger transitionTrigger,
) ([]ContractResolver, []ResolutionMsg, error) { ) ([]ContractResolver, []ResolutionMsg, error) {
// There may be a class of HTLC's which we can fail back immediately, // There may be a class of HTLC's which we can fail back immediately,
@ -1278,8 +1283,7 @@ func (c *ChannelArbitrator) UpdateContractSignals(newSignals *ContractSignals) {
// Nursery for incubation, and ultimate sweeping. // Nursery for incubation, and ultimate sweeping.
// //
// NOTE: This MUST be run as a goroutine. // NOTE: This MUST be run as a goroutine.
func (c *ChannelArbitrator) channelAttendant(bestHeight int32, func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
bestHash *chainhash.Hash) {
// TODO(roasbeef): tell top chain arb we're done // TODO(roasbeef): tell top chain arb we're done
defer c.wg.Done() defer c.wg.Done()
@ -1295,7 +1299,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
return return
} }
bestHeight = blockEpoch.Height bestHeight = blockEpoch.Height
bestHash = blockEpoch.Hash
// If we're not in the default state, then we can // If we're not in the default state, then we can
// ignore this signal as we're waiting for contract // ignore this signal as we're waiting for contract
@ -1307,7 +1310,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
// Now that a new block has arrived, we'll attempt to // Now that a new block has arrived, we'll attempt to
// advance our state forward. // advance our state forward.
nextState, _, err := c.advanceState( nextState, _, err := c.advanceState(
uint32(bestHeight), bestHash, chainTrigger, nil, uint32(bestHeight), chainTrigger, nil,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1352,20 +1355,67 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
}), }),
) )
// We've cooperatively closed the channel, so we're no longer // We've cooperatively closed the channel, so we're no longer
// needed. // needed.
case <-c.cfg.ChainEvents.CooperativeClosure: case <-c.cfg.ChainEvents.CooperativeClosure:
log.Infof("ChannelArbitrator(%v) closing due to co-op "+ log.Infof("ChannelArbitrator(%v) closing due to co-op "+
"closure", c.cfg.ChanPoint) "closure", c.cfg.ChanPoint)
return return
// We have broadcasted our commitment, and it is now confirmed
// on-chain.
case closeInfo := <-c.cfg.ChainEvents.LocalUnilateralClosure:
log.Infof("ChannelArbitrator(%v): local on-chain "+
"channel close", c.cfg.ChanPoint)
if c.state != StateCommitmentBroadcasted {
log.Errorf("ChannelArbitrator(%v): unexpected "+
"local on-chain channel close",
c.cfg.ChanPoint)
}
closeTx := closeInfo.CloseTx
contractRes := &ContractResolutions{
CommitHash: closeTx.TxHash(),
CommitResolution: closeInfo.CommitResolution,
HtlcResolutions: *closeInfo.HtlcResolutions,
}
// When processing a unilateral close event, we'll
// transition directly to the ContractClosed state.
// When the state machine reaches that state, we'll log
// out the set of resolutions.
stateCb := func(nextState ArbitratorState) error {
if nextState != StateContractClosed {
return nil
}
err := c.log.LogContractResolutions(
contractRes,
)
if err != nil {
return fmt.Errorf("unable to "+
"write resolutions: %v",
err)
}
return nil
}
// We'll now advance our state machine until it reaches
// a terminal state.
_, _, err := c.advanceState(
uint32(closeInfo.SpendingHeight),
localCloseTrigger, stateCb,
)
if err != nil {
log.Errorf("unable to advance state: %v", err)
}
// The remote party has broadcast the commitment on-chain. // The remote party has broadcast the commitment on-chain.
// We'll examine our state to determine if we need to act at // We'll examine our state to determine if we need to act at
// all. // all.
case uniClosure := <-c.cfg.ChainEvents.UnilateralClosure: case uniClosure := <-c.cfg.ChainEvents.RemoteUnilateralClosure:
if c.state != StateDefault {
continue
}
log.Infof("ChannelArbitrator(%v): remote party has "+ log.Infof("ChannelArbitrator(%v): remote party has "+
"closed channel out on-chain", c.cfg.ChanPoint) "closed channel out on-chain", c.cfg.ChanPoint)
@ -1378,17 +1428,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
CommitResolution: uniClosure.CommitResolution, CommitResolution: uniClosure.CommitResolution,
HtlcResolutions: *uniClosure.HtlcResolutions, HtlcResolutions: *uniClosure.HtlcResolutions,
} }
if contractRes.IsEmpty() {
log.Infof("ChannelArbitrator(%v): contract "+
"resolutions empty, exiting", c.cfg.ChanPoint)
err := c.cfg.MarkChannelResolved()
if err != nil {
log.Errorf("unable to resolve "+
"contract: %v", err)
}
return
}
// TODO(roasbeef): modify signal to also detect // TODO(roasbeef): modify signal to also detect
// cooperative closures? // cooperative closures?
@ -1399,19 +1438,21 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
// present on their commitment. // present on their commitment.
c.activeHTLCs = newHtlcSet(uniClosure.RemoteCommit.Htlcs) c.activeHTLCs = newHtlcSet(uniClosure.RemoteCommit.Htlcs)
// When processing a remote party initiated event, // When processing a unilateral close event, we'll
// we'll skip the BroadcastCommit state, and transition // transition directly to the ContractClosed state.
// directly to the ContractClosed state. As a result, // When the state machine reaches that state, we'll log
// we'll now manually log out set of resolutions. // out the set of resolutions.
stateCb := func(nextState ArbitratorState) error { stateCb := func(nextState ArbitratorState) error {
if nextState == StateContractClosed { if nextState != StateContractClosed {
err := c.log.LogContractResolutions( return nil
contractRes, }
)
if err != nil { err := c.log.LogContractResolutions(
return fmt.Errorf("unable to write "+ contractRes,
"resolutions: %v", err) )
} if err != nil {
return fmt.Errorf("unable to write "+
"resolutions: %v", err)
} }
return nil return nil
@ -1420,8 +1461,8 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
// We'll now advance our state machine until it reaches // We'll now advance our state machine until it reaches
// a terminal state. // a terminal state.
_, _, err := c.advanceState( _, _, err := c.advanceState(
uint32(bestHeight), bestHash, uint32(uniClosure.SpendingHeight),
remotePeerTrigger, stateCb, remoteCloseTrigger, stateCb,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1466,7 +1507,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
} }
nextState, closeTx, err := c.advanceState( nextState, closeTx, err := c.advanceState(
uint32(bestHeight), bestHash, userTrigger, nil, uint32(bestHeight), userTrigger, nil,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1490,6 +1531,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
log.Infof("ChannelArbitrator(%v): all "+ log.Infof("ChannelArbitrator(%v): all "+
"contracts resolved, exiting", "contracts resolved, exiting",
c.cfg.ChanPoint) c.cfg.ChanPoint)
return
} }
case <-c.quit: case <-c.quit:
@ -1497,39 +1539,3 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
} }
} }
} }
// markContractClosed marks a contract as "pending closed". After this state,
// upon restart, we'll no longer watch for updates to the set of contracts as
// the channel cannot be updated any longer.
func (c *ChannelArbitrator) markContractClosed(closeTx *wire.MsgTx,
chanSnapshot channeldb.ChannelSnapshot,
contractResolution *ContractResolutions,
closeHeight uint32) error {
// TODO(roasbeef): also need height info?
closeInfo := &channeldb.ChannelCloseSummary{
ChanPoint: chanSnapshot.ChannelPoint,
ChainHash: chanSnapshot.ChainHash,
ClosingTXID: closeTx.TxHash(),
RemotePub: &chanSnapshot.RemoteIdentity,
Capacity: chanSnapshot.Capacity,
CloseType: channeldb.ForceClose,
IsPending: true,
ShortChanID: c.cfg.ShortChanID,
CloseHeight: closeHeight,
}
// If our commitment output isn't dust or we have active HTLC's on the
// commitment transaction, then we'll populate the balances on the
// close channel summary.
if contractResolution.CommitResolution != nil {
closeInfo.SettledBalance = chanSnapshot.LocalBalance.ToSatoshis()
closeInfo.TimeLockedBalance = chanSnapshot.LocalBalance.ToSatoshis()
}
for _, htlc := range contractResolution.HtlcResolutions.OutgoingHTLCs {
htlcValue := btcutil.Amount(htlc.SweepSignDesc.Output.Value)
closeInfo.TimeLockedBalance += htlcValue
}
return c.cfg.CloseChannel(closeInfo)
}

View File

@ -1 +1,341 @@
package contractcourt package contractcourt
import (
"fmt"
"testing"
"time"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire"
)
type mockChainIO struct{}
func (*mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) {
return nil, 0, nil
}
func (*mockChainIO) GetUtxo(op *wire.OutPoint,
heightHint uint32) (*wire.TxOut, error) {
return nil, nil
}
func (*mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
return nil, nil
}
func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
return nil, nil
}
func createTestChannelArbitrator() (*ChannelArbitrator, chan struct{}, func(), error) {
blockEpoch := &chainntnfs.BlockEpochEvent{
Cancel: func() {},
}
chanPoint := wire.OutPoint{}
shortChanID := lnwire.ShortChannelID{}
chanEvents := &ChainEventSubscription{
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1),
CooperativeClosure: make(chan struct{}, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
}
chainIO := &mockChainIO{}
chainArbCfg := ChainArbitratorConfig{
ChainIO: chainIO,
PublishTx: func(*wire.MsgTx) error {
return nil
},
}
// We'll use the resolvedChan to synchronize on call to
// MarkChannelResolved.
resolvedChan := make(chan struct{}, 1)
// Next we'll create the matching configuration struct that contains
// all interfaces and methods the arbitrator needs to do its job.
arbCfg := ChannelArbitratorConfig{
ChanPoint: chanPoint,
ShortChanID: shortChanID,
BlockEpochs: blockEpoch,
MarkChannelResolved: func() error {
resolvedChan <- struct{}{}
return nil
},
ForceCloseChan: func() (*lnwallet.LocalForceCloseSummary, error) {
summary := &lnwallet.LocalForceCloseSummary{
CloseTx: &wire.MsgTx{},
HtlcResolutions: &lnwallet.HtlcResolutions{},
}
return summary, nil
},
MarkCommitmentBroadcasted: func() error {
return nil
},
ChainArbitratorConfig: chainArbCfg,
ChainEvents: chanEvents,
}
testLog, cleanUp, err := newTestBoltArbLog(
testChainHash, testChanPoint1,
)
if err != nil {
return nil, nil, nil, fmt.Errorf("unable to create test log: %v",
err)
}
return NewChannelArbitrator(arbCfg, nil, testLog),
resolvedChan, cleanUp, nil
}
// assertState checks that the ChannelArbitrator is in the state we expect it
// to be.
func assertState(t *testing.T, c *ChannelArbitrator, expected ArbitratorState) {
if c.state != expected {
t.Fatalf("expected state %v, was %v", expected, c.state)
}
}
// TestChannelArbitratorCooperativeClose tests that the ChannelArbitertor
// correctly does nothing in case a cooperative close is confirmed.
func TestChannelArbitratorCooperativeClose(t *testing.T) {
chanArb, _, cleanUp, err := createTestChannelArbitrator()
if err != nil {
t.Fatalf("unable to create ChannelArbitrator: %v", err)
}
defer cleanUp()
if err := chanArb.Start(); err != nil {
t.Fatalf("unable to start ChannelArbitrator: %v", err)
}
defer chanArb.Stop()
// It should start out in the default state.
assertState(t, chanArb, StateDefault)
// Cooperative close should do nothing.
// TODO: this will change?
chanArb.cfg.ChainEvents.CooperativeClosure <- struct{}{}
assertState(t, chanArb, StateDefault)
}
// TestChannelArbitratorRemoteForceClose checks that the ChannelArbitrotor goes
// through the expected states if a remote force close is observed in the
// chain.
func TestChannelArbitratorRemoteForceClose(t *testing.T) {
chanArb, resolved, cleanUp, err := createTestChannelArbitrator()
if err != nil {
t.Fatalf("unable to create ChannelArbitrator: %v", err)
}
defer cleanUp()
if err := chanArb.Start(); err != nil {
t.Fatalf("unable to start ChannelArbitrator: %v", err)
}
defer chanArb.Stop()
// It should start out in the default state.
assertState(t, chanArb, StateDefault)
// Send a remote force close event.
commitSpend := &chainntnfs.SpendDetail{
SpenderTxHash: &chainhash.Hash{},
}
uniClose := &lnwallet.UnilateralCloseSummary{
SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{},
}
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose
// It should mark the channel as resolved.
select {
case <-resolved:
// Expected.
case <-time.After(5 * time.Second):
t.Fatalf("contract was not resolved")
}
// TODO: intermediate states.
// We expect the ChannelArbitrator to end up in the the resolved state.
assertState(t, chanArb, StateFullyResolved)
}
// TestChannelArbitratorLocalForceClose tests that the ChannelArbitrator goes
// through the expected states in case we request it to force close the channel,
// and the local force close event is observed in chain.
func TestChannelArbitratorLocalForceClose(t *testing.T) {
chanArb, resolved, cleanUp, err := createTestChannelArbitrator()
if err != nil {
t.Fatalf("unable to create ChannelArbitrator: %v", err)
}
defer cleanUp()
if err := chanArb.Start(); err != nil {
t.Fatalf("unable to start ChannelArbitrator: %v", err)
}
defer chanArb.Stop()
// It should start out in the default state.
assertState(t, chanArb, StateDefault)
// We create a channel we can use to pause the ChannelArbitrator at the
// point where it broadcasts the close tx, and check its state.
stateChan := make(chan ArbitratorState)
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
// When the force close tx is being broadcasted, check that the
// state is correct at that point.
select {
case stateChan <- chanArb.state:
case <-chanArb.quit:
return fmt.Errorf("exiting")
}
return nil
}
errChan := make(chan error, 1)
respChan := make(chan *wire.MsgTx, 1)
// With the channel found, and the request crafted, we'll send over a
// force close request to the arbitrator that watches this channel.
chanArb.forceCloseReqs <- &forceCloseReq{
errResp: errChan,
closeTx: respChan,
}
// When it is broadcasting the force close, its state should be
// StateBroadcastCommit.
select {
case state := <-stateChan:
if state != StateBroadcastCommit {
t.Fatalf("state during PublishTx was %v", state)
}
case <-time.After(15 * time.Second):
t.Fatalf("did not get state update")
}
select {
case <-respChan:
case err := <-errChan:
t.Fatalf("error force closing channel: %v", err)
case <-time.After(15 * time.Second):
t.Fatalf("did not receive reponse")
}
// After broadcasting the close tx, it should be in state
// StateCommitmentBroadcasted.
assertState(t, chanArb, StateCommitmentBroadcasted)
// Now notify about the local force close getting confirmed.
chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{
&chainntnfs.SpendDetail{},
&lnwallet.LocalForceCloseSummary{
CloseTx: &wire.MsgTx{},
HtlcResolutions: &lnwallet.HtlcResolutions{},
},
}
// It should mark the channel as resolved.
select {
case <-resolved:
// Expected.
case <-time.After(5 * time.Second):
t.Fatalf("contract was not resolved")
}
// And end up in the StateFullyResolved state.
// TODO: intermediate states as well.
assertState(t, chanArb, StateFullyResolved)
}
// TestChannelArbitratorLocalForceCloseRemoteConfiremd tests that the
// ChannelArbitrator behaves as expected in the case where we request a local
// force close, but a remote commitment ends up being confirmed in chain.
func TestChannelArbitratorLocalForceCloseRemoteConfirmed(t *testing.T) {
chanArb, resolved, cleanUp, err := createTestChannelArbitrator()
if err != nil {
t.Fatalf("unable to create ChannelArbitrator: %v", err)
}
defer cleanUp()
if err := chanArb.Start(); err != nil {
t.Fatalf("unable to start ChannelArbitrator: %v", err)
}
defer chanArb.Stop()
// It should start out in the default state.
assertState(t, chanArb, StateDefault)
// Create a channel we can use to assert the state when it publishes
// the close tx.
stateChan := make(chan ArbitratorState)
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
// When the force close tx is being broadcasted, check that the
// state is correct at that point.
select {
case stateChan <- chanArb.state:
case <-chanArb.quit:
return fmt.Errorf("exiting")
}
return nil
}
errChan := make(chan error, 1)
respChan := make(chan *wire.MsgTx, 1)
// With the channel found, and the request crafted, we'll send over a
// force close request to the arbitrator that watches this channel.
chanArb.forceCloseReqs <- &forceCloseReq{
errResp: errChan,
closeTx: respChan,
}
// We expect it to be in state StateBroadcastCommit when publishing
// the force close.
select {
case state := <-stateChan:
if state != StateBroadcastCommit {
t.Fatalf("state during PublishTx was %v", state)
}
case <-time.After(15 * time.Second):
t.Fatalf("no state update received")
}
// Wait for a response to the force close.
select {
case <-respChan:
case err := <-errChan:
t.Fatalf("error force closing channel: %v", err)
case <-time.After(15 * time.Second):
t.Fatalf("no response received")
}
// The state should be StateCommitmentBroadcasted.
assertState(t, chanArb, StateCommitmentBroadcasted)
// Now notify about the _REMOTE_ commitment getting confirmed.
commitSpend := &chainntnfs.SpendDetail{
SpenderTxHash: &chainhash.Hash{},
}
uniClose := &lnwallet.UnilateralCloseSummary{
SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{},
}
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose
// It should resolve.
select {
case <-resolved:
// Expected.
case <-time.After(15 * time.Second):
t.Fatalf("contract was not resolved")
}
// And we expect it to end up in StateFullyResolved.
// TODO: intermediate states as well.
assertState(t, chanArb, StateFullyResolved)
}

View File

@ -350,16 +350,12 @@ func (cm *circuitMap) decodeCircuit(v []byte) (*PaymentCircuit, error) {
// channels. Therefore, it must be called before any links are created to avoid // channels. Therefore, it must be called before any links are created to avoid
// interfering with normal operation. // interfering with normal operation.
func (cm *circuitMap) trimAllOpenCircuits() error { func (cm *circuitMap) trimAllOpenCircuits() error {
activeChannels, err := cm.cfg.DB.FetchAllChannels() activeChannels, err := cm.cfg.DB.FetchAllOpenChannels()
if err != nil { if err != nil {
return err return err
} }
for _, activeChannel := range activeChannels { for _, activeChannel := range activeChannels {
if activeChannel.IsPending {
continue
}
chanID := activeChannel.ShortChanID chanID := activeChannel.ShortChanID
start := activeChannel.LocalCommitment.LocalHtlcIndex start := activeChannel.LocalCommitment.LocalHtlcIndex
if err := cm.TrimOpenCircuits(chanID, start); err != nil { if err := cm.TrimOpenCircuits(chanID, start); err != nil {

View File

@ -832,7 +832,7 @@ out:
// the contract as fully settled. Afterwards we can exit. // the contract as fully settled. Afterwards we can exit.
// //
// TODO(roasbeef): add force closure? also breach? // TODO(roasbeef): add force closure? also breach?
case <-l.cfg.ChainEvents.UnilateralClosure: case <-l.cfg.ChainEvents.RemoteUnilateralClosure:
log.Warnf("Remote peer has closed ChannelPoint(%v) on-chain", log.Warnf("Remote peer has closed ChannelPoint(%v) on-chain",
l.channel.ChannelPoint()) l.channel.ChannelPoint())

View File

@ -1513,16 +1513,12 @@ func (s *Switch) Start() error {
// forwarding packages and reforwards any Settle or Fail HTLCs found. This is // forwarding packages and reforwards any Settle or Fail HTLCs found. This is
// used to resurrect the switch's mailboxes after a restart. // used to resurrect the switch's mailboxes after a restart.
func (s *Switch) reforwardResponses() error { func (s *Switch) reforwardResponses() error {
activeChannels, err := s.cfg.DB.FetchAllChannels() activeChannels, err := s.cfg.DB.FetchAllOpenChannels()
if err != nil { if err != nil {
return err return err
} }
for _, activeChannel := range activeChannels { for _, activeChannel := range activeChannels {
if activeChannel.IsPending {
continue
}
shortChanID := activeChannel.ShortChanID shortChanID := activeChannel.ShortChanID
fwdPkgs, err := s.loadChannelFwdPkgs(shortChanID) fwdPkgs, err := s.loadChannelFwdPkgs(shortChanID)
if err != nil { if err != nil {

View File

@ -112,7 +112,7 @@ func assertTxInBlock(t *harnessTest, block *wire.MsgBlock, txid *chainhash.Hash)
} }
} }
t.Fatalf("funding tx was not included in block") t.Fatalf("tx was not included in block")
} }
// mineBlocks mine 'num' of blocks and check that blocks are present in // mineBlocks mine 'num' of blocks and check that blocks are present in
@ -1109,8 +1109,17 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
// Disconnect Alice-peer from Bob-peer without getting error // Disconnect Alice-peer from Bob-peer without getting error
// about existing channels. // about existing channels.
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil { var predErr error
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v", err) err = lntest.WaitPredicate(func() bool {
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil {
predErr = err
return false
}
return true
}, time.Second*15)
if err != nil {
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v",
predErr)
} }
// Check zero peer connections. // Check zero peer connections.
@ -1353,6 +1362,27 @@ func findForceClosedChannel(t *harnessTest,
return forceClose return forceClose
} }
// findWaitingCloseChannel searches a pending channel response for a particular
// channel, returning the waiting close channel upon success.
func findWaitingCloseChannel(t *harnessTest,
pendingChanResp *lnrpc.PendingChannelsResponse,
op *wire.OutPoint) *lnrpc.PendingChannelsResponse_WaitingCloseChannel {
var found bool
var waitingClose *lnrpc.PendingChannelsResponse_WaitingCloseChannel
for _, waitingClose = range pendingChanResp.WaitingCloseChannels {
if waitingClose.Channel.ChannelPoint == op.String() {
found = true
break
}
}
if !found {
t.Fatalf("channel not marked as waiting close")
}
return waitingClose
}
func assertCommitmentMaturity(t *harnessTest, func assertCommitmentMaturity(t *harnessTest,
forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel,
maturityHeight uint32, blocksTilMaturity int32) { maturityHeight uint32, blocksTilMaturity int32) {
@ -1394,6 +1424,18 @@ func assertNumForceClosedChannels(t *harnessTest,
} }
} }
// assertNumWaitingCloseChannels checks that a pending channel response has the
// expected number of channels waiting for closing tx to confirm.
func assertNumWaitingCloseChannels(t *harnessTest,
pendingChanResp *lnrpc.PendingChannelsResponse, expectedNumChans int) {
if len(pendingChanResp.WaitingCloseChannels) != expectedNumChans {
t.Fatalf("expected to find %d channels waiting closure, got %d",
expectedNumChans,
len(pendingChanResp.WaitingCloseChannels))
}
}
// assertPendingHtlcStageAndMaturity uniformly tests all pending htlc's // assertPendingHtlcStageAndMaturity uniformly tests all pending htlc's
// belonging to a force closed channel, testing for the expected stage number, // belonging to a force closed channel, testing for the expected stage number,
// blocks till maturity, and the maturity height. // blocks till maturity, and the maturity height.
@ -1574,13 +1616,13 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
} }
// Now that the channel has been force closed, it should show up in the // Now that the channel has been force closed, it should show up in the
// PendingChannels RPC under the force close section. // PendingChannels RPC under the waiting close section.
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest) pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) t.Fatalf("unable to query for pending channels: %v", err)
} }
assertNumForceClosedChannels(t, pendingChanResp, 1) assertNumWaitingCloseChannels(t, pendingChanResp, 1)
// Compute the outpoint of the channel, which we will use repeatedly to // Compute the outpoint of the channel, which we will use repeatedly to
// locate the pending channel information in the rpc responses. // locate the pending channel information in the rpc responses.
@ -1597,21 +1639,12 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
Index: chanPoint.OutputIndex, Index: chanPoint.OutputIndex,
} }
forceClose := findForceClosedChannel(t, pendingChanResp, &op) waitingClose := findWaitingCloseChannel(t, pendingChanResp, &op)
// Immediately after force closing, all of the funds should be in limbo, // Immediately after force closing, all of the funds should be in limbo.
// and the pending channels response should not indicate that any funds if waitingClose.LimboBalance == 0 {
// have been recovered.
if forceClose.LimboBalance == 0 {
t.Fatalf("all funds should still be in limbo") t.Fatalf("all funds should still be in limbo")
} }
if forceClose.RecoveredBalance != 0 {
t.Fatalf("no funds should yet be shown as recovered")
}
// The commitment transaction has not been confirmed, so we expect to
// see a maturity height and blocks til maturity of 0.
assertCommitmentMaturity(t, forceClose, 0, 0)
// The several restarts in this test are intended to ensure that when a // The several restarts in this test are intended to ensure that when a
// channel is force-closed, the UTXO nursery has persisted the state of // channel is force-closed, the UTXO nursery has persisted the state of
@ -1639,13 +1672,15 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
duration := time.Millisecond * 300 duration := time.Millisecond * 300
time.Sleep(duration) time.Sleep(duration)
// Now that the commitment has been confirmed, the channel should be
// marked as force closed.
pendingChanResp, err = net.Alice.PendingChannels(ctxb, pendingChansRequest) pendingChanResp, err = net.Alice.PendingChannels(ctxb, pendingChansRequest)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) t.Fatalf("unable to query for pending channels: %v", err)
} }
assertNumForceClosedChannels(t, pendingChanResp, 1) assertNumForceClosedChannels(t, pendingChanResp, 1)
forceClose = findForceClosedChannel(t, pendingChanResp, &op) forceClose := findForceClosedChannel(t, pendingChanResp, &op)
// Now that the channel has been force closed, it should now have the // Now that the channel has been force closed, it should now have the
// height and number of blocks to confirm populated. // height and number of blocks to confirm populated.
@ -3828,7 +3863,7 @@ func waitForNTxsInMempool(miner *rpcclient.Client, n int,
for { for {
select { select {
case <-breakTimeout: case <-breakTimeout:
return nil, fmt.Errorf("wanted %v, only found %v txs "+ return nil, fmt.Errorf("wanted %v, found %v txs "+
"in mempool", n, len(mempool)) "in mempool", n, len(mempool))
case <-ticker.C: case <-ticker.C:
mempool, err = miner.GetRawMempool() mempool, err = miner.GetRawMempool()
@ -4224,11 +4259,24 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(net *lntest.NetworkHarness
// commitment transaction of a prior *revoked* state, so he'll soon // commitment transaction of a prior *revoked* state, so he'll soon
// feel the wrath of Alice's retribution. // feel the wrath of Alice's retribution.
force := true force := true
closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force) closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol,
chanPoint, force)
if err != nil { if err != nil {
t.Fatalf("unable to close channel: %v", err) t.Fatalf("unable to close channel: %v", err)
} }
// Query the mempool for the breaching closing transaction, this should
// be broadcast by Carol when she force closes the channel above.
txid, err := waitForTxInMempool(net.Miner.Node, 20*time.Second)
if err != nil {
t.Fatalf("unable to find Carol's force close tx in mempool: %v",
err)
}
if *txid != *closeTxId {
t.Fatalf("expected closeTx(%v) in mempool, instead found %v",
closeTxId, txid)
}
// Finally, generate a single block, wait for the final close status // Finally, generate a single block, wait for the final close status
// update, then ensure that the closing transaction was included in the // update, then ensure that the closing transaction was included in the
// block. // block.
@ -4545,17 +4593,22 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
// commitment transaction of a prior *revoked* state, so she'll soon // commitment transaction of a prior *revoked* state, so she'll soon
// feel the wrath of Dave's retribution. // feel the wrath of Dave's retribution.
force := true force := true
closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force) closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol,
chanPoint, force)
if err != nil { if err != nil {
t.Fatalf("unable to close channel: %v", err) t.Fatalf("unable to close channel: %v", err)
} }
// Query the mempool for Dave's justice transaction, this should be // Query the mempool for the breaching closing transaction, this should
// broadcast as Carol's contract breaching transaction gets confirmed // be broadcast by Carol when she force closes the channel above.
// above. txid, err := waitForTxInMempool(net.Miner.Node, 20*time.Second)
_, err = waitForTxInMempool(net.Miner.Node, 5*time.Second)
if err != nil { if err != nil {
t.Fatalf("unable to find Dave's justice tx in mempool: %v", err) t.Fatalf("unable to find Carol's force close tx in mempool: %v",
err)
}
if *txid != *closeTxId {
t.Fatalf("expected closeTx(%v) in mempool, instead found %v",
closeTxId, txid)
} }
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
@ -4577,14 +4630,101 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
if err != nil { if err != nil {
t.Fatalf("error while waiting for channel close: %v", err) t.Fatalf("error while waiting for channel close: %v", err)
} }
if *breachTXID != *closeTxId {
t.Fatalf("expected breach ID(%v) to be equal to close ID (%v)",
breachTXID, closeTxId)
}
assertTxInBlock(t, block, breachTXID) assertTxInBlock(t, block, breachTXID)
// Query the mempool for Dave's justice transaction, this should be // Query the mempool for Dave's justice transaction, this should be
// broadcast as Carol's contract breaching transaction gets confirmed // broadcast as Carol's contract breaching transaction gets confirmed
// above. // above. Since Carol might have had the time to take some of the HTLC
justiceTXID, err := waitForTxInMempool(net.Miner.Node, 5*time.Second) // outputs to the second level before Alice broadcasts her justice tx,
// we'll search through the mempool for a tx that matches the number of
// expected inputs in the justice tx.
// TODO(halseth): change to deterministic check if/when only acting on
// confirmed second level spends?
var predErr error
var justiceTxid *chainhash.Hash
err = lntest.WaitPredicate(func() bool {
mempool, err := net.Miner.Node.GetRawMempool()
if err != nil {
t.Fatalf("unable to get mempool from miner: %v", err)
}
for _, txid := range mempool {
// Check that the justice tx has the appropriate number
// of inputs.
tx, err := net.Miner.Node.GetRawTransaction(txid)
if err != nil {
predErr = fmt.Errorf("unable to query for "+
"txs: %v", err)
return false
}
exNumInputs := 2 + numInvoices
if len(tx.MsgTx().TxIn) == exNumInputs {
justiceTxid = txid
return true
}
}
predErr = fmt.Errorf("justice tx not found")
return false
}, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("unable to find Dave's justice tx in mempool: %v", err) t.Fatalf(predErr.Error())
}
justiceTx, err := net.Miner.Node.GetRawTransaction(justiceTxid)
if err != nil {
t.Fatalf("unable to query for justice tx: %v", err)
}
// isSecondLevelSpend checks that the passed secondLevelTxid is a
// potentitial second level spend spending from the commit tx.
isSecondLevelSpend := func(commitTxid, secondLevelTxid *chainhash.Hash) bool {
secondLevel, err := net.Miner.Node.GetRawTransaction(
secondLevelTxid)
if err != nil {
t.Fatalf("unable to query for tx: %v", err)
}
// A second level spend should have only one input, and one
// output.
if len(secondLevel.MsgTx().TxIn) != 1 {
return false
}
if len(secondLevel.MsgTx().TxOut) != 1 {
return false
}
// The sole input should be spending from the commit tx.
txIn := secondLevel.MsgTx().TxIn[0]
if !bytes.Equal(txIn.PreviousOutPoint.Hash[:], commitTxid[:]) {
return false
}
return true
}
// Check that all the inputs of this transaction are spending outputs
// generated by Carol's breach transaction above.
for _, txIn := range justiceTx.MsgTx().TxIn {
if bytes.Equal(txIn.PreviousOutPoint.Hash[:], breachTXID[:]) {
continue
}
// If the justice tx is spending from an output that was not on
// the breach tx, Carol might have had the time to take an
// output to the second level. In that case, check that the
// justice tx is spending this second level output.
if isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash) {
continue
}
t.Fatalf("justice tx not spending commitment utxo "+
"instead is: %v", txIn.PreviousOutPoint)
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@ -4597,36 +4737,12 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
t.Fatalf("unable to restart Dave's node: %v", err) t.Fatalf("unable to restart Dave's node: %v", err)
} }
// Query for the mempool transaction found above. Then assert that (1)
// the justice tx has the appropriate number of inputs, and (2) all the
// inputs of this transaction are spending outputs generated by Carol's
// breach transaction above, and also the HTLCs from Carol to Dave.
justiceTx, err := net.Miner.Node.GetRawTransaction(justiceTXID)
if err != nil {
t.Fatalf("unable to query for justice tx: %v", err)
}
exNumInputs := 2 + numInvoices
if len(justiceTx.MsgTx().TxIn) != exNumInputs {
t.Fatalf("justice tx should have exactly 2 commitment inputs"+
"and %v htlc inputs, expected %v in total, got %v",
numInvoices/2, exNumInputs,
len(justiceTx.MsgTx().TxIn))
}
// Now mine a block, this transaction should include Dave's justice // Now mine a block, this transaction should include Dave's justice
// transaction which was just accepted into the mempool. // transaction which was just accepted into the mempool.
block = mineBlocks(t, net, 1)[0] block = mineBlocks(t, net, 1)[0]
assertTxInBlock(t, block, justiceTxid)
// The block should have exactly *two* transactions, one of which is // Dave should have no open channels.
// the justice transaction.
if len(block.Transactions) != 2 {
t.Fatalf("transaction wasn't mined")
}
justiceSha := block.Transactions[1].TxHash()
if !bytes.Equal(justiceTx.Hash()[:], justiceSha[:]) {
t.Fatalf("justice tx wasn't mined")
}
assertNodeNumChannels(t, ctxb, dave, 0) assertNodeNumChannels(t, ctxb, dave, 0)
} }
@ -4649,7 +4765,13 @@ func assertNodeNumChannels(t *harnessTest, ctxb context.Context,
// Return true if the query returned the expected number of // Return true if the query returned the expected number of
// channels. // channels.
return len(chanInfo.Channels) == numChannels num := len(chanInfo.Channels)
if num != numChannels {
predErr = fmt.Errorf("expected %v channels, got %v",
numChannels, num)
return false
}
return true
} }
if err := lntest.WaitPredicate(pred, time.Second*15); err != nil { if err := lntest.WaitPredicate(pred, time.Second*15); err != nil {
@ -6020,6 +6142,9 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
}, },
) )
// Mine a block to confirm the closing transaction.
mineBlocks(t, net, 1)
// At this point, Bob should have cancelled backwards the dust HTLC // At this point, Bob should have cancelled backwards the dust HTLC
// that we sent earlier. This means Alice should now only have a single // that we sent earlier. This means Alice should now only have a single
// HTLC on her channel. // HTLC on her channel.
@ -6033,7 +6158,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
return true return true
}, time.Second*15) }, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("htlc mismatch: %v", err) t.Fatalf("htlc mismatch: %v", predErr)
} }
// TODO(roasbeef): need to fix utxn so it can accept incubation for // TODO(roasbeef): need to fix utxn so it can accept incubation for
@ -6050,7 +6175,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
// The second layer HTLC timeout transaction should now have been // The second layer HTLC timeout transaction should now have been
// broadcast on-chain. // broadcast on-chain.
_, err = waitForTxInMempool(net.Miner.Node, time.Second*10) secondLayerHash, err := waitForTxInMempool(net.Miner.Node, time.Second*10)
if err != nil { if err != nil {
t.Fatalf("unable to find bob's second layer transaction") t.Fatalf("unable to find bob's second layer transaction")
} }
@ -6077,13 +6202,12 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
} }
// Now we'll mine an additional block. // Now we'll mine an additional block.
if _, err := net.Miner.Node.Generate(1); err != nil { block := mineBlocks(t, net, 1)[0]
t.Fatalf("unable to generate blocks: %v", err)
}
// The block should have confirmed Bob's second layer sweeping // The block should have confirmed Bob's second layer sweeping
// transaction. Therefore, at this point, there should be no active // transaction. Therefore, at this point, there should be no active
// HTLC's on the commitment transaction from Alice -> Bob. // HTLC's on the commitment transaction from Alice -> Bob.
assertTxInBlock(t, block, secondLayerHash)
nodes = []*lntest.HarnessNode{net.Alice} nodes = []*lntest.HarnessNode{net.Alice}
err = lntest.WaitPredicate(func() bool { err = lntest.WaitPredicate(func() bool {
return assertNumActiveHtlcs(nodes, 0) return assertNumActiveHtlcs(nodes, 0)
@ -6216,58 +6340,73 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest)
// At this point, Carol should broadcast her active commitment // At this point, Carol should broadcast her active commitment
// transaction in order to go to the chain and sweep her HTLC. // transaction in order to go to the chain and sweep her HTLC.
// Additionally, Carol's should have broadcast her second layer sweep txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*20)
// transaction for the HTLC as well.
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("transactions not found in mempool: %v", err) t.Fatalf("expected transaction not found in mempool: %v", err)
} }
txidHash, err := getChanPointFundingTxid(bobChanPoint) txidHash, err := getChanPointFundingTxid(bobChanPoint)
if err != nil { if err != nil {
t.Fatalf("unable to get txid: %v", err) t.Fatalf("unable to get txid: %v", err)
} }
bobFundingTxid, err := chainhash.NewHash(txidHash) bobFundingTxid, err := chainhash.NewHash(txidHash)
if err != nil {
t.Fatalf("unable to create sha hash: %v", err)
}
carolFundingPoint := wire.OutPoint{ carolFundingPoint := wire.OutPoint{
Hash: *bobFundingTxid, Hash: *bobFundingTxid,
Index: bobChanPoint.OutputIndex, Index: bobChanPoint.OutputIndex,
} }
tx1, err := net.Miner.Node.GetRawTransaction(txids[0]) // The commitment transaction should be spending from the funding
// transaction.
commitHash := txids[0]
tx, err := net.Miner.Node.GetRawTransaction(commitHash)
if err != nil { if err != nil {
t.Fatalf("unable to get txn: %v", err) t.Fatalf("unable to get txn: %v", err)
} }
tx1Hash := tx1.MsgTx().TxHash() commitTx := tx.MsgTx()
tx2, err := net.Miner.Node.GetRawTransaction(txids[1])
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
tx2Hash := tx2.MsgTx().TxHash()
// Of the two transactions, one should be spending from the funding if commitTx.TxIn[0].PreviousOutPoint != carolFundingPoint {
// transaction, and the second transaction should then be spending from t.Fatalf("commit transaction not spending from expected "+
"outpoint: %v", spew.Sdump(commitTx))
}
// Confirm the commitment.
mineBlocks(t, net, 1)
// After the force close transaction is mined, Carol should broadcast
// her second level HTLC transaction. Bob will broadcast a sweep tx to
// sweep his output in the channel with Carol. When Bob notices Carol's
// second level transaction in the mempool, he will extract the
// preimage and settle the HTLC back off-chain.
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 2,
time.Second*15)
if err != nil {
t.Fatalf("transactions not found in mempool: %v", err)
}
// Carol's second level transaction should be spending from
// the commitment transaction. // the commitment transaction.
var commitHash *chainhash.Hash var secondLevelHash *chainhash.Hash
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint { for _, txid := range secondLevelHashes {
commitHash = &tx1Hash tx, err := net.Miner.Node.GetRawTransaction(txid)
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash { if err != nil {
t.Fatalf("second transaction not spending commit tx: %v", t.Fatalf("unable to get txn: %v", err)
spew.Sdump(tx2)) }
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
secondLevelHash = txid
} }
} }
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint { if secondLevelHash == nil {
commitHash = &tx2Hash t.Fatalf("Carol's second level tx not found")
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx1))
}
}
if commitHash == nil {
t.Fatalf("commit tx not found in mempool")
} }
// We'll now mine an additional block which should confirm both the // We'll now mine an additional block which should confirm both the
// second layer transaction as well as the commitment transaction // second layer transactions.
// itself.
if _, err := net.Miner.Node.Generate(1); err != nil { if _, err := net.Miner.Node.Generate(1); err != nil {
t.Fatalf("unable to generate block: %v", err) t.Fatalf("unable to generate block: %v", err)
} }
@ -6427,17 +6566,32 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// At this point, Bob should have a pending force close channel as he // At this point, Bob should have a pending force close channel as he
// just went to chain. // just went to chain.
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
pendingChanResp, err := net.Bob.PendingChannels(ctxb, pendingChansRequest) err = lntest.WaitPredicate(func() bool {
pendingChanResp, err := net.Bob.PendingChannels(ctxb,
pendingChansRequest)
if err != nil {
predErr = fmt.Errorf("unable to query for pending "+
"channels: %v", err)
return false
}
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
predErr = fmt.Errorf("bob should have pending for " +
"close chan but doesn't")
return false
}
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if forceCloseChan.LimboBalance == 0 {
predErr = fmt.Errorf("bob should have nonzero limbo "+
"balance instead has: %v",
forceCloseChan.LimboBalance)
return false
}
return true
}, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) t.Fatalf(predErr.Error())
}
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
t.Fatalf("bob should have pending for close chan but doesn't")
}
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if forceCloseChan.LimboBalance == 0 {
t.Fatalf("bob should have nonzero limbo balance instead "+
"has: %v", forceCloseChan.LimboBalance)
} }
// We'll now mine enough blocks for the HTLC to expire. After this, Bob // We'll now mine enough blocks for the HTLC to expire. After this, Bob
@ -6459,12 +6613,12 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
} }
if len(pendingChanResp.PendingForceClosingChannels) == 0 { if len(pendingChanResp.PendingForceClosingChannels) == 0 {
predErr = fmt.Errorf("bob should have pending for " + predErr = fmt.Errorf("bob should have pending force " +
"close chan but doesn't") "close chan but doesn't")
return false return false
} }
forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if len(forceCloseChan.PendingHtlcs) != 1 { if len(forceCloseChan.PendingHtlcs) != 1 {
predErr = fmt.Errorf("bob should have pending htlc " + predErr = fmt.Errorf("bob should have pending htlc " +
"but doesn't") "but doesn't")
@ -6523,7 +6677,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
return false return false
} }
forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
if len(forceCloseChan.PendingHtlcs) != 1 { if len(forceCloseChan.PendingHtlcs) != 1 {
predErr = fmt.Errorf("bob should have pending htlc " + predErr = fmt.Errorf("bob should have pending htlc " +
"but doesn't") "but doesn't")
@ -6559,7 +6713,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// At this point, Bob should no longer show any channels as pending // At this point, Bob should no longer show any channels as pending
// close. // close.
err = lntest.WaitPredicate(func() bool { err = lntest.WaitPredicate(func() bool {
pendingChanResp, err = net.Bob.PendingChannels( pendingChanResp, err := net.Bob.PendingChannels(
ctxb, pendingChansRequest, ctxb, pendingChansRequest,
) )
if err != nil { if err != nil {
@ -6642,7 +6796,7 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
return true return true
}, time.Second*15) }, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("htlc mismatch: %v", err) t.Fatalf("htlc mismatch: %v", predErr)
} }
// At this point, we'll now instruct Carol to force close the // At this point, we'll now instruct Carol to force close the
@ -6654,12 +6808,25 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// At this point, Bob should have a pending force close channel as // At this point, Bob should have a pending force close channel as
// Carol has gone directly to chain. // Carol has gone directly to chain.
pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChansRequest := &lnrpc.PendingChannelsRequest{}
pendingChanResp, err := net.Bob.PendingChannels(ctxb, pendingChansRequest) err = lntest.WaitPredicate(func() bool {
pendingChanResp, err := net.Bob.PendingChannels(
ctxb, pendingChansRequest,
)
if err != nil {
predErr = fmt.Errorf("unable to query for "+
"pending channels: %v", err)
return false
}
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
predErr = fmt.Errorf("bob should have pending " +
"force close channels but doesn't")
return false
}
return true
}, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("unable to query for pending channels: %v", err) t.Fatalf(predErr.Error())
}
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
t.Fatalf("bob should have pending for close chan but doesn't")
} }
// Next, we'll mine enough blocks for the HTLC to expire. At this // Next, we'll mine enough blocks for the HTLC to expire. At this
@ -6739,7 +6906,7 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
// commitment, he doesn't have to wait for any CSV delays. As a result, // commitment, he doesn't have to wait for any CSV delays. As a result,
// he should show no additional pending transactions. // he should show no additional pending transactions.
err = lntest.WaitPredicate(func() bool { err = lntest.WaitPredicate(func() bool {
pendingChanResp, err = net.Bob.PendingChannels( pendingChanResp, err := net.Bob.PendingChannels(
ctxb, pendingChansRequest, ctxb, pendingChansRequest,
) )
if err != nil { if err != nil {
@ -6835,9 +7002,8 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
t.Fatalf("unable to generate blocks") t.Fatalf("unable to generate blocks")
} }
// Carol's commitment transaction should now be in the mempool. She // Carol's commitment transaction should now be in the mempool.
// should also have broadcast her second level HTLC transaction. txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("transactions not found in mempool: %v", err) t.Fatalf("transactions not found in mempool: %v", err)
} }
@ -6854,50 +7020,52 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
Index: bobChanPoint.OutputIndex, Index: bobChanPoint.OutputIndex,
} }
// Of the two transactions, one should be spending from the funding // The tx should be spending from the funding transaction,
// transaction, and the second transaction should then be spending from commitHash := txids[0]
tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
t.Fatalf("commit transaction not spending fundingtx: %v",
spew.Sdump(tx1))
}
// Mine a block that should confirm the commit tx.
block := mineBlocks(t, net, 1)[0]
if len(block.Transactions) != 2 {
t.Fatalf("expected 2 transactions in block, got %v",
len(block.Transactions))
}
assertTxInBlock(t, block, commitHash)
// After the force close transacion is mined, Carol should broadcast
// her second level HTLC transacion. Bob will braodcast a sweep tx to
// sweep his output in the channel with Carol. When Bob notices Carol's
// second level transaction in the mempool, he will extract the
// preimage and broadcast a second level tx to claim the HTLC in his
// (already closed) channel with Alice.
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
time.Second*20)
if err != nil {
t.Fatalf("transactions not found in mempool: %v", err)
}
// Carol's second level transaction should be spending from
// the commitment transaction. // the commitment transaction.
var commitHash *chainhash.Hash var secondLevelHash *chainhash.Hash
tx1, err := net.Miner.Node.GetRawTransaction(txids[0]) for _, txid := range secondLevelHashes {
if err != nil { tx, err := net.Miner.Node.GetRawTransaction(txid)
t.Fatalf("unable to get txn: %v", err) if err != nil {
} t.Fatalf("unable to get txn: %v", err)
tx1Hash := tx1.MsgTx().TxHash() }
tx2, err := net.Miner.Node.GetRawTransaction(txids[1])
if err != nil { if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
t.Fatalf("unable to get txn: %v", err) secondLevelHash = txid
}
tx2Hash := tx2.MsgTx().TxHash()
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx1Hash
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx2))
} }
} }
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint { if secondLevelHash == nil {
commitHash = &tx2Hash t.Fatalf("Carol's second level tx not found")
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx1))
}
}
if commitHash == nil {
t.Fatalf("commit tx not found in mempool")
}
// We'll now mine a block which should confirm both the second layer
// transaction as well as the commitment transaction.
if _, err := net.Miner.Node.Generate(1); err != nil {
t.Fatalf("unable to generate block: %v", err)
}
// At this point, Bob should detect that Carol has revealed the
// preimage on-chain. As a result, he should now attempt to broadcast
// his second-layer claim transaction to claim the output.
_, err = waitForTxInMempool(net.Miner.Node, time.Second*10)
if err != nil {
t.Fatalf("unable to find bob's sweeping transaction")
} }
// At this point, Bob should have broadcast his second layer success // At this point, Bob should have broadcast his second layer success
@ -6929,9 +7097,11 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
"but doesn't") "but doesn't")
return false return false
} }
if forceCloseChan.PendingHtlcs[0].Stage != 1 { stage := forceCloseChan.PendingHtlcs[0].Stage
if stage != 1 {
predErr = fmt.Errorf("bob's htlc should have "+ predErr = fmt.Errorf("bob's htlc should have "+
"advanced to the first stage: %v", err) "advanced to the first stage but was "+
"stage: %v", stage)
return false return false
} }
} }
@ -6942,6 +7112,15 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr)
} }
// We'll now mine a block which should confirm the two second layer
// transactions and the commit sweep.
block = mineBlocks(t, net, 1)[0]
if len(block.Transactions) != 4 {
t.Fatalf("expected 4 transactions in block, got %v",
len(block.Transactions))
}
assertTxInBlock(t, block, secondLevelHash)
// If we then mine 4 additional blocks, Bob should pull the output // If we then mine 4 additional blocks, Bob should pull the output
// destined for him. // destined for him.
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
@ -6994,6 +7173,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
timeout := time.Duration(time.Second * 15) timeout := time.Duration(time.Second * 15)
ctxb := context.Background() ctxb := context.Background()
defaultCSV := uint32(4)
// First, we'll create a three hop network: Alice -> Bob -> Carol, with // First, we'll create a three hop network: Alice -> Bob -> Carol, with
// Carol refusing to actually settle or directly cancel any HTLC's // Carol refusing to actually settle or directly cancel any HTLC's
// self. // self.
@ -7052,9 +7233,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
t.Fatalf("unable to generate blocks") t.Fatalf("unable to generate blocks")
} }
// Carol's commitment transaction should now be in the mempool. She // Carol's commitment transaction should now be in the mempool.
// should also have broadcast her second level HTLC transaction. txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("transactions not found in mempool: %v", err) t.Fatalf("transactions not found in mempool: %v", err)
} }
@ -7071,48 +7251,70 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
Index: bobChanPoint.OutputIndex, Index: bobChanPoint.OutputIndex,
} }
// Of the two transactions, one should be spending from the funding // The transaction should be spending from the funding transaction
// transaction, and the second transaction should then be spending from commitHash := txids[0]
// the commitment transaction. tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
var commitHash *chainhash.Hash
tx1, err := net.Miner.Node.GetRawTransaction(txids[0])
if err != nil { if err != nil {
t.Fatalf("unable to get txn: %v", err) t.Fatalf("unable to get txn: %v", err)
} }
tx1Hash := tx1.MsgTx().TxHash() if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
tx2, err := net.Miner.Node.GetRawTransaction(txids[1]) t.Fatalf("commit transaction not spending fundingtx: %v",
if err != nil { spew.Sdump(tx1))
t.Fatalf("unable to get txn: %v", err)
}
tx2Hash := tx2.MsgTx().TxHash()
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx1Hash
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx2))
}
}
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
commitHash = &tx2Hash
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
t.Fatalf("second transaction not spending commit tx: %v",
spew.Sdump(tx1))
}
}
if commitHash == nil {
t.Fatalf("commit tx not found in mempool")
} }
// We'll now mine a block which should confirm both the second layer // Mine a block, which should contain the commitment.
// transaction as well as the commitment transaction. block := mineBlocks(t, net, 1)[0]
if _, err := net.Miner.Node.Generate(1); err != nil { if len(block.Transactions) != 2 {
t.Fatalf("expected 2 transactions in block, got %v",
len(block.Transactions))
}
assertTxInBlock(t, block, commitHash)
// After the force close transacion is mined, Carol should broadcast
// her second level HTLC transacion. Bob will braodcast a sweep tx to
// sweep his output in the channel with Carol. When Bob notices Carol's
// second level transaction in the mempool, he will extract the
// preimage and broadcast a second level tx to claim the HTLC in his
// (already closed) channel with Alice.
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
time.Second*20)
if err != nil {
t.Fatalf("transactions not found in mempool: %v", err)
}
// Carol's second level transaction should be spending from
// the commitment transaction.
var secondLevelHash *chainhash.Hash
for _, txid := range secondLevelHashes {
tx, err := net.Miner.Node.GetRawTransaction(txid)
if err != nil {
t.Fatalf("unable to get txn: %v", err)
}
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
secondLevelHash = txid
}
}
if secondLevelHash == nil {
t.Fatalf("Carol's second level tx not found")
}
// We'll now mine a block which should confirm the two second layer
// transactions and the commit sweep.
block = mineBlocks(t, net, 1)[0]
if len(block.Transactions) != 4 {
t.Fatalf("expected 4 transactions in block, got %v",
len(block.Transactions))
}
assertTxInBlock(t, block, secondLevelHash)
// If we then mine 4 additional blocks, Bob should pull the output
// destined for him.
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
t.Fatalf("unable to generate block: %v", err) t.Fatalf("unable to generate block: %v", err)
} }
// With the block mined above, Bob should detect that Carol is _, err = waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
// attempting to sweep the HTLC on-chain, and should obtain the
// preimage.
_, err = waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
if err != nil { if err != nil {
t.Fatalf("unable to find bob's sweeping transaction") t.Fatalf("unable to find bob's sweeping transaction")
} }

View File

@ -521,7 +521,9 @@ func (m *ChannelPoint) String() string { return proto.CompactTextStri
func (*ChannelPoint) ProtoMessage() {} func (*ChannelPoint) ProtoMessage() {}
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
type isChannelPoint_FundingTxid interface{ isChannelPoint_FundingTxid() } type isChannelPoint_FundingTxid interface {
isChannelPoint_FundingTxid()
}
type ChannelPoint_FundingTxidBytes struct { type ChannelPoint_FundingTxidBytes struct {
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"` FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
@ -1608,7 +1610,9 @@ func (m *CloseStatusUpdate) String() string { return proto.CompactTex
func (*CloseStatusUpdate) ProtoMessage() {} func (*CloseStatusUpdate) ProtoMessage() {}
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} }
type isCloseStatusUpdate_Update interface{ isCloseStatusUpdate_Update() } type isCloseStatusUpdate_Update interface {
isCloseStatusUpdate_Update()
}
type CloseStatusUpdate_ClosePending struct { type CloseStatusUpdate_ClosePending struct {
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"` ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
@ -1871,7 +1875,9 @@ func (m *OpenStatusUpdate) String() string { return proto.CompactText
func (*OpenStatusUpdate) ProtoMessage() {} func (*OpenStatusUpdate) ProtoMessage() {}
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} } func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
type isOpenStatusUpdate_Update interface{ isOpenStatusUpdate_Update() } type isOpenStatusUpdate_Update interface {
isOpenStatusUpdate_Update()
}
type OpenStatusUpdate_ChanPending struct { type OpenStatusUpdate_ChanPending struct {
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"` ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
@ -2090,6 +2096,8 @@ type PendingChannelsResponse struct {
PendingClosingChannels []*PendingChannelsResponse_ClosedChannel `protobuf:"bytes,3,rep,name=pending_closing_channels" json:"pending_closing_channels,omitempty"` PendingClosingChannels []*PendingChannelsResponse_ClosedChannel `protobuf:"bytes,3,rep,name=pending_closing_channels" json:"pending_closing_channels,omitempty"`
// / Channels pending force closing // / Channels pending force closing
PendingForceClosingChannels []*PendingChannelsResponse_ForceClosedChannel `protobuf:"bytes,4,rep,name=pending_force_closing_channels" json:"pending_force_closing_channels,omitempty"` PendingForceClosingChannels []*PendingChannelsResponse_ForceClosedChannel `protobuf:"bytes,4,rep,name=pending_force_closing_channels" json:"pending_force_closing_channels,omitempty"`
// / Channels waiting for closing tx to confirm
WaitingCloseChannels []*PendingChannelsResponse_WaitingCloseChannel `protobuf:"bytes,5,rep,name=waiting_close_channels" json:"waiting_close_channels,omitempty"`
} }
func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse{} } func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse{} }
@ -2125,6 +2133,13 @@ func (m *PendingChannelsResponse) GetPendingForceClosingChannels() []*PendingCha
return nil return nil
} }
func (m *PendingChannelsResponse) GetWaitingCloseChannels() []*PendingChannelsResponse_WaitingCloseChannel {
if m != nil {
return m.WaitingCloseChannels
}
return nil
}
type PendingChannelsResponse_PendingChannel struct { type PendingChannelsResponse_PendingChannel struct {
RemoteNodePub string `protobuf:"bytes,1,opt,name=remote_node_pub" json:"remote_node_pub,omitempty"` RemoteNodePub string `protobuf:"bytes,1,opt,name=remote_node_pub" json:"remote_node_pub,omitempty"`
ChannelPoint string `protobuf:"bytes,2,opt,name=channel_point" json:"channel_point,omitempty"` ChannelPoint string `protobuf:"bytes,2,opt,name=channel_point" json:"channel_point,omitempty"`
@ -2244,6 +2259,38 @@ func (m *PendingChannelsResponse_PendingOpenChannel) GetFeePerKw() int64 {
return 0 return 0
} }
type PendingChannelsResponse_WaitingCloseChannel struct {
// / The pending channel waiting for closing tx to confirm
Channel *PendingChannelsResponse_PendingChannel `protobuf:"bytes,1,opt,name=channel" json:"channel,omitempty"`
// / The balance in satoshis encumbered in this channel
LimboBalance int64 `protobuf:"varint,2,opt,name=limbo_balance" json:"limbo_balance,omitempty"`
}
func (m *PendingChannelsResponse_WaitingCloseChannel) Reset() {
*m = PendingChannelsResponse_WaitingCloseChannel{}
}
func (m *PendingChannelsResponse_WaitingCloseChannel) String() string {
return proto.CompactTextString(m)
}
func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {}
func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{47, 2}
}
func (m *PendingChannelsResponse_WaitingCloseChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
if m != nil {
return m.Channel
}
return nil
}
func (m *PendingChannelsResponse_WaitingCloseChannel) GetLimboBalance() int64 {
if m != nil {
return m.LimboBalance
}
return 0
}
type PendingChannelsResponse_ClosedChannel struct { type PendingChannelsResponse_ClosedChannel struct {
// / The pending channel to be closed // / The pending channel to be closed
Channel *PendingChannelsResponse_PendingChannel `protobuf:"bytes,1,opt,name=channel" json:"channel,omitempty"` Channel *PendingChannelsResponse_PendingChannel `protobuf:"bytes,1,opt,name=channel" json:"channel,omitempty"`
@ -2255,7 +2302,7 @@ func (m *PendingChannelsResponse_ClosedChannel) Reset() { *m = PendingCh
func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) } func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{47, 2} return fileDescriptor0, []int{47, 3}
} }
func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
@ -2299,7 +2346,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string {
} }
func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) { func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{47, 3} return fileDescriptor0, []int{47, 4}
} }
func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
@ -3916,7 +3963,9 @@ func (m *PolicyUpdateRequest) String() string { return proto.CompactT
func (*PolicyUpdateRequest) ProtoMessage() {} func (*PolicyUpdateRequest) ProtoMessage() {}
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} } func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
type isPolicyUpdateRequest_Scope interface{ isPolicyUpdateRequest_Scope() } type isPolicyUpdateRequest_Scope interface {
isPolicyUpdateRequest_Scope()
}
type PolicyUpdateRequest_Global struct { type PolicyUpdateRequest_Global struct {
Global bool `protobuf:"varint,1,opt,name=global,oneof"` Global bool `protobuf:"varint,1,opt,name=global,oneof"`
@ -4234,6 +4283,7 @@ func init() {
proto.RegisterType((*PendingChannelsResponse)(nil), "lnrpc.PendingChannelsResponse") proto.RegisterType((*PendingChannelsResponse)(nil), "lnrpc.PendingChannelsResponse")
proto.RegisterType((*PendingChannelsResponse_PendingChannel)(nil), "lnrpc.PendingChannelsResponse.PendingChannel") proto.RegisterType((*PendingChannelsResponse_PendingChannel)(nil), "lnrpc.PendingChannelsResponse.PendingChannel")
proto.RegisterType((*PendingChannelsResponse_PendingOpenChannel)(nil), "lnrpc.PendingChannelsResponse.PendingOpenChannel") proto.RegisterType((*PendingChannelsResponse_PendingOpenChannel)(nil), "lnrpc.PendingChannelsResponse.PendingOpenChannel")
proto.RegisterType((*PendingChannelsResponse_WaitingCloseChannel)(nil), "lnrpc.PendingChannelsResponse.WaitingCloseChannel")
proto.RegisterType((*PendingChannelsResponse_ClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ClosedChannel") proto.RegisterType((*PendingChannelsResponse_ClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ClosedChannel")
proto.RegisterType((*PendingChannelsResponse_ForceClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ForceClosedChannel") proto.RegisterType((*PendingChannelsResponse_ForceClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ForceClosedChannel")
proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest") proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest")
@ -6301,353 +6351,356 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 5566 bytes of a gzipped FileDescriptorProto // 5611 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0xcd, 0x93, 0x1c, 0xc9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0xcb, 0x73, 0x1c, 0xc9,
0x55, 0x57, 0xf5, 0xf4, 0x7c, 0xf4, 0xeb, 0x9e, 0x9e, 0x99, 0x9c, 0xd1, 0xa8, 0xd5, 0xd2, 0x6a, 0x71, 0x37, 0x7b, 0x30, 0x78, 0x4c, 0xce, 0x60, 0x00, 0x14, 0x40, 0x70, 0x38, 0xe4, 0x72, 0xb9,
0xb5, 0xe5, 0x8d, 0x95, 0x18, 0x16, 0x8d, 0x76, 0x6c, 0x2f, 0xeb, 0x15, 0xac, 0xd1, 0xf7, 0xac, 0xad, 0x0d, 0x91, 0x1f, 0xbe, 0x35, 0xc1, 0x85, 0xa4, 0xf5, 0x6a, 0x69, 0x4b, 0xe6, 0x1b, 0x2b,
0xad, 0x95, 0xc7, 0x35, 0x92, 0x17, 0xbc, 0x40, 0xbb, 0xa6, 0x3b, 0xa7, 0xa7, 0xac, 0xea, 0xaa, 0x71, 0x29, 0xa8, 0x41, 0x8a, 0xb6, 0x64, 0x7b, 0xd4, 0x98, 0x29, 0x0c, 0x7a, 0xd9, 0xd3, 0xdd,
0xda, 0xaa, 0xea, 0x19, 0xf5, 0x2e, 0x8a, 0xe0, 0x23, 0x82, 0x13, 0x0e, 0x0e, 0x70, 0x31, 0x04, 0xdb, 0xdd, 0x03, 0x70, 0x76, 0xcd, 0x08, 0xbf, 0xc2, 0x27, 0x2b, 0x7c, 0xb0, 0x2f, 0xb2, 0xc3,
0x41, 0x84, 0x7d, 0x81, 0x03, 0x47, 0x4e, 0xe6, 0x2f, 0x70, 0x04, 0xc1, 0x61, 0x4f, 0x0e, 0x6e, 0xe1, 0x08, 0xe9, 0x62, 0x1f, 0x7c, 0xf4, 0x49, 0xf6, 0x3f, 0xe0, 0x08, 0x87, 0x0f, 0x7b, 0x52,
0x7c, 0x1c, 0xc0, 0xc1, 0x85, 0x08, 0x2e, 0x1c, 0x08, 0xe2, 0xbd, 0xfc, 0xa8, 0xcc, 0xaa, 0x1a, 0xf8, 0xe6, 0xc7, 0xc1, 0x56, 0xf8, 0xe2, 0x08, 0x5f, 0x7c, 0x70, 0x38, 0x32, 0xeb, 0xd1, 0x55,
0x49, 0xb6, 0xc1, 0xb7, 0xce, 0x5f, 0xbe, 0x7a, 0xf9, 0xf5, 0xde, 0xcb, 0xf7, 0x5e, 0x66, 0x36, 0xdd, 0x0d, 0x92, 0x7a, 0xd8, 0xb7, 0xa9, 0x5f, 0x65, 0x67, 0xbd, 0x32, 0xb3, 0x32, 0xb3, 0xaa,
0xb4, 0xd2, 0x64, 0x78, 0x2d, 0x49, 0xe3, 0x3c, 0x66, 0xf3, 0x61, 0x94, 0x26, 0xc3, 0xfe, 0xc5, 0x06, 0x5a, 0x69, 0x32, 0xbc, 0x96, 0xa4, 0x71, 0x1e, 0xb3, 0xf9, 0x30, 0x4a, 0x93, 0x61, 0xff,
0x71, 0x1c, 0x8f, 0x43, 0xbe, 0xed, 0x27, 0xc1, 0xb6, 0x1f, 0x45, 0x71, 0xee, 0xe7, 0x41, 0x1c, 0xe2, 0x38, 0x8e, 0xc7, 0x21, 0xdf, 0xf6, 0x93, 0x60, 0xdb, 0x8f, 0xa2, 0x38, 0xf7, 0xf3, 0x20,
0x65, 0x82, 0xc8, 0xfd, 0x16, 0x74, 0xef, 0xf3, 0x68, 0x9f, 0xf3, 0x91, 0xc7, 0x3f, 0x9e, 0xf2, 0x8e, 0x32, 0x41, 0xe4, 0x7e, 0x1b, 0xba, 0xf7, 0x79, 0xb4, 0xcf, 0xf9, 0xc8, 0xe3, 0x1f, 0x4d,
0x2c, 0x67, 0xbf, 0x08, 0x6b, 0x3e, 0xff, 0x84, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39, 0x4a, 0x79, 0x96, 0xb3, 0xff, 0x0f, 0x6b, 0x3e, 0xff, 0x98, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39,
0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0x5e, 0x83, 0x4a, 0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0xde,
0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae, 0x80, 0x80, 0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae,
0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24, 0x47, 0x80, 0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24,
0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57, 0x5b, 0x47, 0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57,
0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f, 0xe8, 0x5b, 0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f,
0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0xcf, 0x1d, 0x58, 0x7b, 0x3f, 0x0a, 0xf2, 0x0f, 0xe8, 0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0x4f, 0x1c, 0x58, 0x7b, 0x3f, 0x0a, 0xf2,
0xfd, 0x30, 0xe4, 0xb9, 0x1a, 0xd3, 0x15, 0x58, 0x39, 0x21, 0x80, 0xc6, 0x74, 0x12, 0xa7, 0x23, 0x27, 0x7e, 0x18, 0xf2, 0x5c, 0x8d, 0xe9, 0x0a, 0xac, 0x9c, 0x10, 0x40, 0x63, 0x3a, 0x89, 0xd3,
0x39, 0xa2, 0xae, 0x80, 0xf7, 0x24, 0x7a, 0x6a, 0xcf, 0x1a, 0xa7, 0xf6, 0xac, 0x76, 0xba, 0xe6, 0x91, 0x1c, 0x51, 0x57, 0xc0, 0x7b, 0x12, 0x3d, 0xb5, 0x67, 0x8d, 0x53, 0x7b, 0x56, 0x3b, 0x5d,
0xea, 0xa7, 0xcb, 0xdd, 0x00, 0x66, 0x76, 0x4e, 0x4c, 0x87, 0xfb, 0x1e, 0xac, 0x3f, 0x8e, 0xc2, 0x73, 0xf5, 0xd3, 0xe5, 0x6e, 0x00, 0x33, 0x3b, 0x27, 0xa6, 0xc3, 0xfd, 0x12, 0xac, 0x3f, 0x8e,
0x78, 0xf8, 0xe4, 0xa7, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b, 0xd0, 0xc2, 0x78, 0xf8, 0xf4, 0x27, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b,
0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0xa7, 0x83, 0x23, 0x3f, 0xd0, 0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0x67, 0x83, 0x23,
0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34, 0xab, 0x3f, 0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34,
0x73, 0x9e, 0x2c, 0xb1, 0x37, 0x61, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2, 0x89, 0xab, 0x73, 0x9e, 0x2c, 0xb1, 0xb7, 0x60, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2,
0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, 0x26, 0x89, 0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89,
0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, 0x85, 0x26, 0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91,
0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, 0xaa, 0x85, 0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10,
0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x1b, 0xd0, 0x1d, 0xaa, 0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x67, 0xa1,
0xf1, 0x2c, 0x1f, 0xf8, 0xa3, 0x51, 0xca, 0xb3, 0x8c, 0x67, 0xbd, 0x25, 0x5a, 0xba, 0x12, 0xea, 0x3b, 0xe2, 0x59, 0x3e, 0xf0, 0x47, 0xa3, 0x94, 0x67, 0x19, 0xcf, 0x7a, 0x4b, 0xb4, 0x74, 0x25,
0xf6, 0x60, 0xf3, 0x3e, 0xcf, 0x8d, 0xd9, 0xc9, 0xe4, 0xb4, 0xbb, 0x0f, 0x80, 0x19, 0xf0, 0x1d, 0xd4, 0xed, 0xc1, 0xe6, 0x7d, 0x9e, 0x1b, 0xb3, 0x93, 0xc9, 0x69, 0x77, 0x1f, 0x00, 0x33, 0xe0,
0x9e, 0xfb, 0x41, 0x98, 0xb1, 0xb7, 0xa1, 0x93, 0x1b, 0xc4, 0x24, 0xaa, 0xed, 0x1d, 0x76, 0x8d, 0x3b, 0x3c, 0xf7, 0x83, 0x30, 0x63, 0xef, 0x40, 0x27, 0x37, 0x88, 0x49, 0x54, 0xdb, 0x3b, 0xec,
0x74, 0xec, 0x9a, 0xf1, 0x81, 0x67, 0xd1, 0xb9, 0xff, 0xed, 0x40, 0x7b, 0x9f, 0x47, 0x5a, 0xbb, 0x1a, 0xe9, 0xd8, 0x35, 0xe3, 0x03, 0xcf, 0xa2, 0x73, 0xff, 0xcb, 0x81, 0xf6, 0x3e, 0x8f, 0xb4,
0x18, 0x34, 0xb1, 0x27, 0x72, 0x25, 0xe9, 0x37, 0x7b, 0x15, 0xda, 0xd4, 0xbb, 0x2c, 0x4f, 0x83, 0x76, 0x31, 0x68, 0x62, 0x4f, 0xe4, 0x4a, 0xd2, 0x6f, 0xf6, 0x3a, 0xb4, 0xa9, 0x77, 0x59, 0x9e,
0x68, 0x4c, 0x4b, 0xd0, 0xf2, 0x00, 0xa1, 0x7d, 0x42, 0xd8, 0x2a, 0xcc, 0xf9, 0x93, 0x9c, 0x26, 0x06, 0xd1, 0x98, 0x96, 0xa0, 0xe5, 0x01, 0x42, 0xfb, 0x84, 0xb0, 0x55, 0x98, 0xf3, 0x27, 0x39,
0x7e, 0xce, 0xc3, 0x9f, 0xa8, 0x77, 0x89, 0x3f, 0x9b, 0xf0, 0x28, 0x2f, 0x26, 0xbb, 0xe3, 0xb5, 0x4d, 0xfc, 0x9c, 0x87, 0x3f, 0x51, 0xef, 0x12, 0x7f, 0x36, 0xe1, 0x51, 0x5e, 0x4c, 0x76, 0xc7,
0x25, 0xb6, 0x8b, 0xb3, 0x7d, 0x0d, 0xd6, 0x4d, 0x12, 0xc5, 0x7d, 0x9e, 0xb8, 0xaf, 0x19, 0x94, 0x6b, 0x4b, 0x6c, 0x17, 0x67, 0xfb, 0x1a, 0xac, 0x9b, 0x24, 0x8a, 0xfb, 0x3c, 0x71, 0x5f, 0x33,
0xb2, 0x91, 0x2b, 0xb0, 0xa2, 0xe8, 0x53, 0xd1, 0x59, 0x9a, 0xfe, 0x96, 0xd7, 0x95, 0xb0, 0x1a, 0x28, 0x65, 0x23, 0x57, 0x60, 0x45, 0xd1, 0xa7, 0xa2, 0xb3, 0x34, 0xfd, 0x2d, 0xaf, 0x2b, 0x61,
0xc2, 0x55, 0x58, 0x3d, 0x0c, 0x22, 0x3f, 0x1c, 0x0c, 0xc3, 0xfc, 0x78, 0x30, 0xe2, 0x61, 0xee, 0x35, 0x84, 0xab, 0xb0, 0x7a, 0x18, 0x44, 0x7e, 0x38, 0x18, 0x86, 0xf9, 0xf1, 0x60, 0xc4, 0xc3,
0xd3, 0x42, 0xcc, 0x7b, 0x5d, 0xc2, 0x6f, 0x87, 0xf9, 0xf1, 0x1d, 0x44, 0xdd, 0x3f, 0x75, 0xa0, 0xdc, 0xa7, 0x85, 0x98, 0xf7, 0xba, 0x84, 0xdf, 0x0e, 0xf3, 0xe3, 0x3b, 0x88, 0xba, 0x7f, 0xe4,
0x23, 0x06, 0x2f, 0x15, 0xff, 0x75, 0x58, 0x56, 0x6d, 0xf0, 0x34, 0x8d, 0x53, 0x29, 0x87, 0x36, 0x40, 0x47, 0x0c, 0x5e, 0x2a, 0xfe, 0x9b, 0xb0, 0xac, 0xda, 0xe0, 0x69, 0x1a, 0xa7, 0x52, 0x0e,
0xc8, 0xb6, 0x60, 0x55, 0x01, 0x49, 0xca, 0x83, 0x89, 0x3f, 0xe6, 0x52, 0xdb, 0x2b, 0x38, 0xdb, 0x6d, 0x90, 0x6d, 0xc1, 0xaa, 0x02, 0x92, 0x94, 0x07, 0x13, 0x7f, 0xcc, 0xa5, 0xb6, 0x57, 0x70,
0x29, 0x38, 0xa6, 0xf1, 0x34, 0x17, 0xaa, 0xd7, 0xde, 0xe9, 0xc8, 0x85, 0xf1, 0x10, 0xf3, 0x6c, 0xb6, 0x53, 0x70, 0x4c, 0xe3, 0x69, 0x2e, 0x54, 0xaf, 0xbd, 0xd3, 0x91, 0x0b, 0xe3, 0x21, 0xe6,
0x12, 0xf7, 0x7b, 0x0e, 0x74, 0x6e, 0x1f, 0xf9, 0x51, 0xc4, 0xc3, 0xbd, 0x38, 0x88, 0x72, 0x76, 0xd9, 0x24, 0xee, 0xf7, 0x1c, 0xe8, 0xdc, 0x3e, 0xf2, 0xa3, 0x88, 0x87, 0x7b, 0x71, 0x10, 0xe5,
0x1d, 0xd8, 0xe1, 0x34, 0x1a, 0x05, 0xd1, 0x78, 0x90, 0x3f, 0x0d, 0x46, 0x83, 0x83, 0x59, 0xce, 0xec, 0x3a, 0xb0, 0xc3, 0x69, 0x34, 0x0a, 0xa2, 0xf1, 0x20, 0x7f, 0x16, 0x8c, 0x06, 0x07, 0xb3,
0x33, 0xb1, 0x44, 0xbb, 0x67, 0xbc, 0x9a, 0x3a, 0xf6, 0x26, 0xac, 0x5a, 0x68, 0x96, 0xa7, 0x62, 0x9c, 0x67, 0x62, 0x89, 0x76, 0xcf, 0x78, 0x35, 0x75, 0xec, 0x2d, 0x58, 0xb5, 0xd0, 0x2c, 0x4f,
0xdd, 0x76, 0xcf, 0x78, 0x95, 0x1a, 0x14, 0xfc, 0x78, 0x9a, 0x27, 0xd3, 0x7c, 0x10, 0x44, 0x23, 0xc5, 0xba, 0xed, 0x9e, 0xf1, 0x2a, 0x35, 0x28, 0xf8, 0xf1, 0x34, 0x4f, 0xa6, 0xf9, 0x20, 0x88,
0xfe, 0x94, 0xfa, 0xb8, 0xec, 0x59, 0xd8, 0xad, 0x2e, 0x74, 0xcc, 0xef, 0xdc, 0xf7, 0x60, 0xf5, 0x46, 0xfc, 0x19, 0xf5, 0x71, 0xd9, 0xb3, 0xb0, 0x5b, 0x5d, 0xe8, 0x98, 0xdf, 0xb9, 0x5f, 0x82,
0x01, 0x6a, 0x44, 0x14, 0x44, 0xe3, 0x9b, 0x42, 0x6c, 0x51, 0x4d, 0x93, 0xe9, 0xc1, 0x13, 0x3e, 0xd5, 0x07, 0xa8, 0x11, 0x51, 0x10, 0x8d, 0x6f, 0x0a, 0xb1, 0x45, 0x35, 0x4d, 0xa6, 0x07, 0x4f,
0x93, 0xf3, 0x26, 0x4b, 0x28, 0x54, 0x47, 0x71, 0x96, 0x4b, 0xc9, 0xa1, 0xdf, 0xee, 0x3f, 0x3b, 0xf9, 0x4c, 0xce, 0x9b, 0x2c, 0xa1, 0x50, 0x1d, 0xc5, 0x59, 0x2e, 0x25, 0x87, 0x7e, 0xbb, 0xff,
0xb0, 0x82, 0x73, 0xff, 0x81, 0x1f, 0xcd, 0xd4, 0xca, 0x3d, 0x80, 0x0e, 0xb2, 0x7a, 0x14, 0xdf, 0xe4, 0xc0, 0x0a, 0xce, 0xfd, 0x07, 0x7e, 0x34, 0x53, 0x2b, 0xf7, 0x00, 0x3a, 0xc8, 0xea, 0x51,
0x14, 0xca, 0x2e, 0x84, 0xf8, 0xaa, 0x9c, 0xab, 0x12, 0xf5, 0x35, 0x93, 0x14, 0x8d, 0xf9, 0xcc, 0x7c, 0x53, 0x28, 0xbb, 0x10, 0xe2, 0xab, 0x72, 0xae, 0x4a, 0xd4, 0xd7, 0x4c, 0x52, 0x34, 0xe6,
0xb3, 0xbe, 0x46, 0xb1, 0xcd, 0xfd, 0x74, 0xcc, 0x73, 0x32, 0x03, 0xd2, 0x2c, 0x80, 0x80, 0x6e, 0x33, 0xcf, 0xfa, 0x1a, 0xc5, 0x36, 0xf7, 0xd3, 0x31, 0xcf, 0xc9, 0x0c, 0x48, 0xb3, 0x00, 0x02,
0xc7, 0xd1, 0x21, 0xbb, 0x0c, 0x9d, 0xcc, 0xcf, 0x07, 0x09, 0x4f, 0x69, 0xd6, 0x48, 0xf4, 0xe6, 0xba, 0x1d, 0x47, 0x87, 0xec, 0x32, 0x74, 0x32, 0x3f, 0x1f, 0x24, 0x3c, 0xa5, 0x59, 0x23, 0xd1,
0x3c, 0xc8, 0xfc, 0x7c, 0x8f, 0xa7, 0xb7, 0x66, 0x39, 0xef, 0x7f, 0x19, 0xd6, 0x2a, 0xad, 0xa0, 0x9b, 0xf3, 0x20, 0xf3, 0xf3, 0x3d, 0x9e, 0xde, 0x9a, 0xe5, 0xbc, 0xff, 0x65, 0x58, 0xab, 0xb4,
0xb4, 0x17, 0x43, 0xc4, 0x9f, 0x6c, 0x03, 0xe6, 0x8f, 0xfd, 0x70, 0xca, 0xa5, 0x75, 0x12, 0x85, 0x82, 0xd2, 0x5e, 0x0c, 0x11, 0x7f, 0xb2, 0x0d, 0x98, 0x3f, 0xf6, 0xc3, 0x29, 0x97, 0xd6, 0x49,
0x77, 0x1b, 0xef, 0x38, 0xee, 0x1b, 0xb0, 0x5a, 0x74, 0x5b, 0x0a, 0x19, 0x83, 0x26, 0xce, 0xa0, 0x14, 0xde, 0x6b, 0xbc, 0xeb, 0xb8, 0x9f, 0x85, 0xd5, 0xa2, 0xdb, 0x52, 0xc8, 0x18, 0x34, 0x71,
0x64, 0x40, 0xbf, 0xdd, 0xdf, 0x73, 0x04, 0xe1, 0xed, 0x38, 0xd0, 0x9a, 0x8e, 0x84, 0x68, 0x10, 0x06, 0x25, 0x03, 0xfa, 0xed, 0xfe, 0x96, 0x23, 0x08, 0x6f, 0xc7, 0x81, 0xd6, 0x74, 0x24, 0x44,
0x14, 0x21, 0xfe, 0x3e, 0xd5, 0x12, 0xfe, 0xec, 0x83, 0x75, 0xaf, 0xc0, 0x9a, 0xd1, 0x85, 0xe7, 0x83, 0xa0, 0x08, 0xf1, 0xf7, 0xa9, 0x96, 0xf0, 0xa7, 0x1f, 0xac, 0x7b, 0x05, 0xd6, 0x8c, 0x2e,
0x74, 0xf6, 0x3b, 0x0e, 0xac, 0x3d, 0xe4, 0x27, 0x72, 0xd5, 0x55, 0x6f, 0xdf, 0x81, 0x66, 0x3e, 0xbc, 0xa0, 0xb3, 0xdf, 0x71, 0x60, 0xed, 0x21, 0x3f, 0x91, 0xab, 0xae, 0x7a, 0xfb, 0x2e, 0x34,
0x4b, 0xc4, 0x56, 0xdc, 0xdd, 0x79, 0x5d, 0x2e, 0x5a, 0x85, 0xee, 0x9a, 0x2c, 0x3e, 0x9a, 0x25, 0xf3, 0x59, 0x22, 0xb6, 0xe2, 0xee, 0xce, 0x9b, 0x72, 0xd1, 0x2a, 0x74, 0xd7, 0x64, 0xf1, 0xd1,
0xdc, 0xa3, 0x2f, 0xdc, 0xf7, 0xa0, 0x6d, 0x80, 0xec, 0x1c, 0xac, 0x7f, 0xf8, 0xfe, 0xa3, 0x87, 0x2c, 0xe1, 0x1e, 0x7d, 0xe1, 0x7e, 0x09, 0xda, 0x06, 0xc8, 0xce, 0xc1, 0xfa, 0x93, 0xf7, 0x1f,
0x77, 0xf7, 0xf7, 0x07, 0x7b, 0x8f, 0x6f, 0x7d, 0xf5, 0xee, 0x6f, 0x0c, 0x76, 0x6f, 0xee, 0xef, 0x3d, 0xbc, 0xbb, 0xbf, 0x3f, 0xd8, 0x7b, 0x7c, 0xeb, 0xab, 0x77, 0x7f, 0x65, 0xb0, 0x7b, 0x73,
0xae, 0x9e, 0x61, 0x9b, 0xc0, 0x1e, 0xde, 0xdd, 0x7f, 0x74, 0xf7, 0x8e, 0x85, 0x3b, 0x6e, 0x1f, 0x7f, 0x77, 0xf5, 0x0c, 0xdb, 0x04, 0xf6, 0xf0, 0xee, 0xfe, 0xa3, 0xbb, 0x77, 0x2c, 0xdc, 0x71,
0x7a, 0x0f, 0xf9, 0xc9, 0x87, 0x41, 0x1e, 0xf1, 0x2c, 0xb3, 0x5b, 0x73, 0xaf, 0x01, 0x33, 0xbb, 0xfb, 0xd0, 0x7b, 0xc8, 0x4f, 0x9e, 0x04, 0x79, 0xc4, 0xb3, 0xcc, 0x6e, 0xcd, 0xbd, 0x06, 0xcc,
0x20, 0x47, 0xd5, 0x83, 0x45, 0x69, 0x6a, 0xd5, 0x4e, 0x23, 0x8b, 0xee, 0x1b, 0xc0, 0xf6, 0x83, 0xec, 0x82, 0x1c, 0x55, 0x0f, 0x16, 0xa5, 0xa9, 0x55, 0x3b, 0x8d, 0x2c, 0xba, 0x9f, 0x05, 0xb6,
0x71, 0xf4, 0x01, 0xcf, 0x32, 0x7f, 0xcc, 0xd5, 0xd8, 0x56, 0x61, 0x6e, 0x92, 0x8d, 0xa5, 0x51, 0x1f, 0x8c, 0xa3, 0x0f, 0x78, 0x96, 0xf9, 0x63, 0xae, 0xc6, 0xb6, 0x0a, 0x73, 0x93, 0x6c, 0x2c,
0xc4, 0x9f, 0xee, 0xe7, 0x61, 0xdd, 0xa2, 0x93, 0x8c, 0x2f, 0x42, 0x2b, 0x0b, 0xc6, 0x91, 0x9f, 0x8d, 0x22, 0xfe, 0x74, 0x3f, 0x07, 0xeb, 0x16, 0x9d, 0x64, 0x7c, 0x11, 0x5a, 0x59, 0x30, 0x8e,
0x4f, 0x53, 0x2e, 0x59, 0x17, 0x80, 0x7b, 0x0f, 0x36, 0xbe, 0xc1, 0xd3, 0xe0, 0x70, 0xf6, 0x22, 0xfc, 0x7c, 0x9a, 0x72, 0xc9, 0xba, 0x00, 0xdc, 0x7b, 0xb0, 0xf1, 0x0d, 0x9e, 0x06, 0x87, 0xb3,
0xf6, 0x36, 0x9f, 0x46, 0x99, 0xcf, 0x5d, 0x38, 0x5b, 0xe2, 0x23, 0x9b, 0x17, 0x82, 0x28, 0x97, 0x97, 0xb1, 0xb7, 0xf9, 0x34, 0xca, 0x7c, 0xee, 0xc2, 0xd9, 0x12, 0x1f, 0xd9, 0xbc, 0x10, 0x44,
0x6b, 0xc9, 0x13, 0x05, 0x43, 0x2d, 0x1b, 0xa6, 0x5a, 0xba, 0x8f, 0x81, 0xdd, 0x8e, 0xa3, 0x88, 0xb9, 0x5c, 0x4b, 0x9e, 0x28, 0x18, 0x6a, 0xd9, 0x30, 0xd5, 0xd2, 0x7d, 0x0c, 0xec, 0x76, 0x1c,
0x0f, 0xf3, 0x3d, 0xce, 0xd3, 0xc2, 0xbf, 0x2a, 0xa4, 0xae, 0xbd, 0x73, 0x4e, 0xae, 0x63, 0x59, 0x45, 0x7c, 0x98, 0xef, 0x71, 0x9e, 0x16, 0xfe, 0x55, 0x21, 0x75, 0xed, 0x9d, 0x73, 0x72, 0x1d,
0xd7, 0xa5, 0x38, 0x32, 0x68, 0x26, 0x3c, 0x9d, 0x10, 0xe3, 0x25, 0x8f, 0x7e, 0xbb, 0x67, 0x61, 0xcb, 0xba, 0x2e, 0xc5, 0x91, 0x41, 0x33, 0xe1, 0xe9, 0x84, 0x18, 0x2f, 0x79, 0xf4, 0xdb, 0x3d,
0xdd, 0x62, 0x2b, 0x77, 0xfb, 0xb7, 0xe0, 0xec, 0x9d, 0x20, 0x1b, 0x56, 0x1b, 0xec, 0xc1, 0x62, 0x0b, 0xeb, 0x16, 0x5b, 0xb9, 0xdb, 0xbf, 0x0d, 0x67, 0xef, 0x04, 0xd9, 0xb0, 0xda, 0x60, 0x0f,
0x32, 0x3d, 0x18, 0x14, 0x3a, 0xa5, 0x8a, 0xb8, 0x09, 0x96, 0x3f, 0x91, 0xcc, 0xfe, 0xd0, 0x81, 0x16, 0x93, 0xe9, 0xc1, 0xa0, 0xd0, 0x29, 0x55, 0xc4, 0x4d, 0xb0, 0xfc, 0x89, 0x64, 0xf6, 0x7b,
0xe6, 0xee, 0xa3, 0x07, 0xb7, 0x59, 0x1f, 0x96, 0x82, 0x68, 0x18, 0x4f, 0x70, 0xeb, 0x10, 0x83, 0x0e, 0x34, 0x77, 0x1f, 0x3d, 0xb8, 0xcd, 0xfa, 0xb0, 0x14, 0x44, 0xc3, 0x78, 0x82, 0x5b, 0x87,
0xd6, 0xe5, 0x53, 0x75, 0xe5, 0x22, 0xb4, 0x68, 0xc7, 0xc1, 0x7d, 0x5d, 0xba, 0x42, 0x05, 0x80, 0x18, 0xb4, 0x2e, 0x9f, 0xaa, 0x2b, 0x17, 0xa1, 0x45, 0x3b, 0x0e, 0xee, 0xeb, 0xd2, 0x15, 0x2a,
0x3e, 0x05, 0x7f, 0x9a, 0x04, 0x29, 0x39, 0x0d, 0xca, 0x15, 0x68, 0x92, 0x45, 0xac, 0x56, 0xb8, 0x00, 0xf4, 0x29, 0xf8, 0xb3, 0x24, 0x48, 0xc9, 0x69, 0x50, 0xae, 0x40, 0x93, 0x2c, 0x62, 0xb5,
0xff, 0xd3, 0x84, 0x45, 0x69, 0xab, 0xa9, 0xbd, 0x61, 0x1e, 0x1c, 0x73, 0xd9, 0x13, 0x59, 0xc2, 0xc2, 0xfd, 0xef, 0x26, 0x2c, 0x4a, 0x5b, 0x4d, 0xed, 0x0d, 0xf3, 0xe0, 0x98, 0xcb, 0x9e, 0xc8,
0x5d, 0x25, 0xe5, 0x93, 0x38, 0xe7, 0x03, 0x6b, 0x19, 0x6c, 0x10, 0xa9, 0x86, 0x82, 0xd1, 0x20, 0x12, 0xee, 0x2a, 0x29, 0x9f, 0xc4, 0x39, 0x1f, 0x58, 0xcb, 0x60, 0x83, 0x48, 0x35, 0x14, 0x8c,
0x41, 0xab, 0x4f, 0x3d, 0x6b, 0x79, 0x36, 0x88, 0x93, 0x85, 0xc0, 0x20, 0x18, 0x51, 0x9f, 0x9a, 0x06, 0x09, 0x5a, 0x7d, 0xea, 0x59, 0xcb, 0xb3, 0x41, 0x9c, 0x2c, 0x04, 0x06, 0xc1, 0x88, 0xfa,
0x9e, 0x2a, 0xe2, 0x4c, 0x0c, 0xfd, 0xc4, 0x1f, 0x06, 0xf9, 0x4c, 0x2a, 0xb7, 0x2e, 0x23, 0xef, 0xd4, 0xf4, 0x54, 0x11, 0x67, 0x62, 0xe8, 0x27, 0xfe, 0x30, 0xc8, 0x67, 0x52, 0xb9, 0x75, 0x19,
0x30, 0x1e, 0xfa, 0xe1, 0xe0, 0xc0, 0x0f, 0xfd, 0x68, 0xc8, 0xa5, 0xe3, 0x62, 0x83, 0xe8, 0x9b, 0x79, 0x87, 0xf1, 0xd0, 0x0f, 0x07, 0x07, 0x7e, 0xe8, 0x47, 0x43, 0x2e, 0x1d, 0x17, 0x1b, 0x44,
0xc8, 0x2e, 0x29, 0x32, 0xe1, 0xbf, 0x94, 0x50, 0xf4, 0x71, 0x86, 0xf1, 0x64, 0x12, 0xe4, 0xe8, 0xdf, 0x44, 0x76, 0x49, 0x91, 0x09, 0xff, 0xa5, 0x84, 0xa2, 0x8f, 0x33, 0x8c, 0x27, 0x93, 0x20,
0xd2, 0xf4, 0x96, 0x84, 0x21, 0x29, 0x10, 0x1a, 0x89, 0x28, 0x9d, 0x88, 0xd9, 0x6b, 0x89, 0xd6, 0x47, 0x97, 0xa6, 0xb7, 0x24, 0x0c, 0x49, 0x81, 0xd0, 0x48, 0x44, 0xe9, 0x44, 0xcc, 0x5e, 0x4b,
0x2c, 0x10, 0xb9, 0x1c, 0x72, 0x4e, 0x06, 0xe9, 0xc9, 0x49, 0x0f, 0x04, 0x97, 0x02, 0xc1, 0x75, 0xb4, 0x66, 0x81, 0xc8, 0xe5, 0x90, 0x73, 0x32, 0x48, 0x4f, 0x4f, 0x7a, 0x20, 0xb8, 0x14, 0x08,
0x98, 0x46, 0x19, 0xcf, 0xf3, 0x90, 0x8f, 0x74, 0x87, 0xda, 0x44, 0x56, 0xad, 0x60, 0xd7, 0x61, 0xae, 0xc3, 0x34, 0xca, 0x78, 0x9e, 0x87, 0x7c, 0xa4, 0x3b, 0xd4, 0x26, 0xb2, 0x6a, 0x05, 0xbb,
0x5d, 0x78, 0x59, 0x99, 0x9f, 0xc7, 0xd9, 0x51, 0x90, 0x0d, 0x32, 0x1e, 0xe5, 0xbd, 0x0e, 0xd1, 0x0e, 0xeb, 0xc2, 0xcb, 0xca, 0xfc, 0x3c, 0xce, 0x8e, 0x82, 0x6c, 0x90, 0xf1, 0x28, 0xef, 0x75,
0xd7, 0x55, 0xb1, 0x77, 0xe0, 0x5c, 0x09, 0x4e, 0xf9, 0x90, 0x07, 0xc7, 0x7c, 0xd4, 0x5b, 0xa6, 0x88, 0xbe, 0xae, 0x8a, 0xbd, 0x0b, 0xe7, 0x4a, 0x70, 0xca, 0x87, 0x3c, 0x38, 0xe6, 0xa3, 0xde,
0xaf, 0x4e, 0xab, 0x66, 0x97, 0xa1, 0x8d, 0xce, 0xe5, 0x34, 0x19, 0xf9, 0xb8, 0x0f, 0x77, 0x69, 0x32, 0x7d, 0x75, 0x5a, 0x35, 0xbb, 0x0c, 0x6d, 0x74, 0x2e, 0xa7, 0xc9, 0xc8, 0xc7, 0x7d, 0xb8,
0x1d, 0x4c, 0x88, 0xbd, 0x05, 0xcb, 0x09, 0x17, 0x9b, 0xe5, 0x51, 0x1e, 0x0e, 0xb3, 0xde, 0x0a, 0x4b, 0xeb, 0x60, 0x42, 0xec, 0x6d, 0x58, 0x4e, 0xb8, 0xd8, 0x2c, 0x8f, 0xf2, 0x70, 0x98, 0xf5,
0xed, 0x64, 0x6d, 0xa9, 0x4c, 0x28, 0xb9, 0x9e, 0x4d, 0x81, 0x42, 0x39, 0xcc, 0xc8, 0x5d, 0xf1, 0x56, 0x68, 0x27, 0x6b, 0x4b, 0x65, 0x42, 0xc9, 0xf5, 0x6c, 0x0a, 0x14, 0xca, 0x61, 0x46, 0xee,
0x67, 0xbd, 0x55, 0x12, 0xb7, 0x02, 0x20, 0x1d, 0x49, 0x83, 0x63, 0x3f, 0xe7, 0xbd, 0x35, 0x92, 0x8a, 0x3f, 0xeb, 0xad, 0x92, 0xb8, 0x15, 0x00, 0xe9, 0x48, 0x1a, 0x1c, 0xfb, 0x39, 0xef, 0xad,
0x2d, 0x55, 0x74, 0xff, 0xd2, 0x81, 0xf5, 0x07, 0x41, 0x96, 0x4b, 0x21, 0xd4, 0xe6, 0xf8, 0x55, 0x91, 0x6c, 0xa9, 0xa2, 0xfb, 0x67, 0x0e, 0xac, 0x3f, 0x08, 0xb2, 0x5c, 0x0a, 0xa1, 0x36, 0xc7,
0x68, 0x0b, 0xf1, 0x1b, 0xc4, 0x51, 0x38, 0x93, 0x12, 0x09, 0x02, 0xfa, 0x5a, 0x14, 0xce, 0xd8, 0xaf, 0x43, 0x5b, 0x88, 0xdf, 0x20, 0x8e, 0xc2, 0x99, 0x94, 0x48, 0x10, 0xd0, 0xd7, 0xa2, 0x70,
0xe7, 0x60, 0x39, 0x88, 0x4c, 0x12, 0xa1, 0xc3, 0x1d, 0x05, 0x12, 0xd1, 0xab, 0xd0, 0x4e, 0xa6, 0xc6, 0x3e, 0x03, 0xcb, 0x41, 0x64, 0x92, 0x08, 0x1d, 0xee, 0x28, 0x90, 0x88, 0x5e, 0x87, 0x76,
0x07, 0x61, 0x30, 0x14, 0x24, 0x73, 0x82, 0x8b, 0x80, 0x88, 0x00, 0x1d, 0x3d, 0xd1, 0x13, 0x41, 0x32, 0x3d, 0x08, 0x83, 0xa1, 0x20, 0x99, 0x13, 0x5c, 0x04, 0x44, 0x04, 0xe8, 0xe8, 0x89, 0x9e,
0xd1, 0x24, 0x8a, 0xb6, 0xc4, 0x90, 0xc4, 0xbd, 0x05, 0x1b, 0x76, 0x07, 0xa5, 0xb1, 0xda, 0x82, 0x08, 0x8a, 0x26, 0x51, 0xb4, 0x25, 0x86, 0x24, 0xee, 0x2d, 0xd8, 0xb0, 0x3b, 0x28, 0x8d, 0xd5,
0x25, 0x29, 0xdb, 0x59, 0xaf, 0x4d, 0xf3, 0xd3, 0x95, 0xf3, 0x23, 0x49, 0x3d, 0x5d, 0xef, 0xfe, 0x16, 0x2c, 0x49, 0xd9, 0xce, 0x7a, 0x6d, 0x9a, 0x9f, 0xae, 0x9c, 0x1f, 0x49, 0xea, 0xe9, 0x7a,
0xbb, 0x03, 0x4d, 0x34, 0x00, 0xa7, 0x1b, 0x0b, 0xd3, 0xa6, 0xcf, 0x59, 0x36, 0x9d, 0xfc, 0x7e, 0xf7, 0xdf, 0x1c, 0x68, 0xa2, 0x01, 0x38, 0xdd, 0x58, 0x98, 0x36, 0x7d, 0xce, 0xb2, 0xe9, 0xe4,
0xf4, 0x8a, 0x84, 0x48, 0x08, 0xb5, 0x31, 0x90, 0xa2, 0x3e, 0xe5, 0xc3, 0x63, 0xd2, 0x1d, 0x5d, 0xf7, 0xa3, 0x57, 0x24, 0x44, 0x42, 0xa8, 0x8d, 0x81, 0x14, 0xf5, 0x29, 0x1f, 0x1e, 0x93, 0xee,
0x8f, 0x08, 0x6a, 0x16, 0x6e, 0x9d, 0xf4, 0xb5, 0x50, 0x1c, 0x5d, 0x56, 0x75, 0xf4, 0xe5, 0x62, 0xe8, 0x7a, 0x44, 0x50, 0xb3, 0x70, 0xeb, 0xa4, 0xaf, 0x85, 0xe2, 0xe8, 0xb2, 0xaa, 0xa3, 0x2f,
0x51, 0x47, 0xdf, 0xf5, 0x60, 0x31, 0x88, 0x0e, 0xe2, 0x69, 0x34, 0x22, 0x25, 0x59, 0xf2, 0x54, 0x17, 0x8b, 0x3a, 0xfa, 0xae, 0x07, 0x8b, 0x41, 0x74, 0x10, 0x4f, 0xa3, 0x11, 0x29, 0xc9, 0x92,
0x11, 0x17, 0x3b, 0x21, 0x4f, 0x2a, 0x98, 0x70, 0xa9, 0x1d, 0x05, 0xe0, 0x32, 0x74, 0xad, 0x32, 0xa7, 0x8a, 0xb8, 0xd8, 0x09, 0x79, 0x52, 0xc1, 0x84, 0x4b, 0xed, 0x28, 0x00, 0x97, 0xa1, 0x6b,
0x32, 0x78, 0x7a, 0x1f, 0x7b, 0x1b, 0xd6, 0x0c, 0x4c, 0xce, 0xe0, 0x6b, 0x30, 0x9f, 0x20, 0x20, 0x95, 0x91, 0xc1, 0xd3, 0xfb, 0xd8, 0x3b, 0xb0, 0x66, 0x60, 0x72, 0x06, 0xdf, 0x80, 0xf9, 0x04,
0x1d, 0x25, 0x25, 0x5e, 0x64, 0x29, 0x45, 0x8d, 0xbb, 0x8a, 0xf1, 0x73, 0xfe, 0x7e, 0x74, 0x18, 0x01, 0xe9, 0x28, 0x29, 0xf1, 0x22, 0x4b, 0x29, 0x6a, 0xdc, 0x55, 0x8c, 0x9f, 0xf3, 0xf7, 0xa3,
0x2b, 0x4e, 0x3f, 0x9a, 0xc3, 0x80, 0x57, 0x42, 0x92, 0xd1, 0x55, 0x58, 0x09, 0x46, 0x3c, 0xca, 0xc3, 0x58, 0x71, 0xfa, 0xe1, 0x1c, 0x06, 0xbc, 0x12, 0x92, 0x8c, 0xae, 0xc2, 0x4a, 0x30, 0xe2,
0x83, 0x7c, 0x36, 0xb0, 0x3c, 0xb8, 0x32, 0x8c, 0x3b, 0x8c, 0x1f, 0x06, 0x7e, 0x26, 0x6d, 0x98, 0x51, 0x1e, 0xe4, 0xb3, 0x81, 0xe5, 0xc1, 0x95, 0x61, 0xdc, 0x61, 0xfc, 0x30, 0xf0, 0x33, 0x69,
0x28, 0xb0, 0x1d, 0xd8, 0x40, 0xf1, 0x57, 0x12, 0xad, 0x97, 0x55, 0x38, 0x92, 0xb5, 0x75, 0xa8, 0xc3, 0x44, 0x81, 0xed, 0xc0, 0x06, 0x8a, 0xbf, 0x92, 0x68, 0xbd, 0xac, 0xc2, 0x91, 0xac, 0xad,
0xb1, 0x88, 0x4b, 0x09, 0xd4, 0x9f, 0x08, 0x4b, 0x5b, 0x57, 0x85, 0xb3, 0x26, 0x38, 0xe1, 0x90, 0x43, 0x8d, 0x45, 0x5c, 0x4a, 0xa0, 0xfe, 0x44, 0x58, 0xda, 0xba, 0x2a, 0x9c, 0x35, 0xc1, 0x09,
0xe7, 0x85, 0x8a, 0x68, 0xa0, 0x12, 0xbd, 0x2d, 0x08, 0x27, 0xb6, 0x1c, 0xbd, 0x19, 0x11, 0xe0, 0x87, 0x3c, 0x2f, 0x54, 0x44, 0x03, 0x95, 0xe8, 0x6d, 0x41, 0x38, 0xb1, 0xe5, 0xe8, 0xcd, 0x88,
0x52, 0x25, 0x02, 0xbc, 0x0a, 0x2b, 0xd9, 0x2c, 0x1a, 0xf2, 0xd1, 0x20, 0x8f, 0xb1, 0xdd, 0x20, 0x00, 0x97, 0x2a, 0x11, 0xe0, 0x55, 0x58, 0xc9, 0x66, 0xd1, 0x90, 0x8f, 0x06, 0x79, 0x8c, 0xed,
0xa2, 0xd5, 0x59, 0xf2, 0xca, 0x30, 0xc5, 0xaa, 0x3c, 0xcb, 0x23, 0x9e, 0x93, 0xe9, 0x5a, 0xf2, 0x06, 0x11, 0xad, 0xce, 0x92, 0x57, 0x86, 0x29, 0x56, 0xe5, 0x59, 0x1e, 0xf1, 0x9c, 0x4c, 0xd7,
0x54, 0x11, 0x77, 0x01, 0x22, 0x11, 0x42, 0xdd, 0xf2, 0x64, 0x09, 0xb7, 0xca, 0x69, 0x1a, 0x64, 0x92, 0xa7, 0x8a, 0xb8, 0x0b, 0x10, 0x89, 0x10, 0xea, 0x96, 0x27, 0x4b, 0xb8, 0x55, 0x4e, 0xd3,
0xbd, 0x0e, 0xa1, 0xf4, 0x9b, 0x7d, 0x01, 0xce, 0x1e, 0x60, 0x64, 0x75, 0xc4, 0xfd, 0x11, 0x4f, 0x20, 0xeb, 0x75, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xf6, 0x00, 0x23, 0xab, 0x23, 0xee, 0x8f,
0x69, 0xf5, 0x45, 0x60, 0x29, 0x2c, 0x50, 0x7d, 0x25, 0xb6, 0x7d, 0xcc, 0xd3, 0x2c, 0x88, 0x23, 0x78, 0x4a, 0xab, 0x2f, 0x02, 0x4b, 0x61, 0x81, 0xea, 0x2b, 0xb1, 0xed, 0x63, 0x9e, 0x66, 0x41,
0xb2, 0x3d, 0x2d, 0x4f, 0x15, 0xdd, 0x4f, 0x68, 0x47, 0xd7, 0x21, 0xef, 0x63, 0x32, 0x47, 0xec, 0x1c, 0x91, 0xed, 0x69, 0x79, 0xaa, 0xe8, 0x7e, 0x4c, 0x3b, 0xba, 0x0e, 0x79, 0x1f, 0x93, 0x39,
0x02, 0xb4, 0xc4, 0x18, 0xb3, 0x23, 0x5f, 0x3a, 0x19, 0x4b, 0x04, 0xec, 0x1f, 0xf9, 0xa8, 0xc0, 0x62, 0x17, 0xa0, 0x25, 0xc6, 0x98, 0x1d, 0xf9, 0xd2, 0xc9, 0x58, 0x22, 0x60, 0xff, 0xc8, 0x47,
0xd6, 0xb4, 0x35, 0xc8, 0x73, 0x6c, 0x13, 0xb6, 0x2b, 0x66, 0xed, 0x75, 0xe8, 0xaa, 0x60, 0x3a, 0x05, 0xb6, 0xa6, 0xad, 0x41, 0x9e, 0x63, 0x9b, 0xb0, 0x5d, 0x31, 0x6b, 0x6f, 0x42, 0x57, 0x05,
0x1b, 0x84, 0xfc, 0x30, 0x57, 0x01, 0x42, 0x34, 0x9d, 0x60, 0x73, 0xd9, 0x03, 0x7e, 0x98, 0xbb, 0xd3, 0xd9, 0x20, 0xe4, 0x87, 0xb9, 0x0a, 0x10, 0xa2, 0xe9, 0x04, 0x9b, 0xcb, 0x1e, 0xf0, 0xc3,
0x0f, 0x61, 0x4d, 0xea, 0xed, 0xd7, 0x12, 0xae, 0x9a, 0xfe, 0x52, 0x79, 0x53, 0x13, 0x5e, 0xc5, 0xdc, 0x7d, 0x08, 0x6b, 0x52, 0x6f, 0xbf, 0x96, 0x70, 0xd5, 0xf4, 0x17, 0xcb, 0x9b, 0x9a, 0xf0,
0xba, 0xad, 0xe8, 0x14, 0xe5, 0x94, 0x76, 0x3a, 0xd7, 0x03, 0x26, 0xab, 0x6f, 0x87, 0x71, 0xc6, 0x2a, 0xd6, 0x6d, 0x45, 0xa7, 0x28, 0xa7, 0xb4, 0xd3, 0xb9, 0x1e, 0x30, 0x59, 0x7d, 0x3b, 0x8c,
0x25, 0x43, 0x17, 0x3a, 0xc3, 0x30, 0xce, 0x54, 0x18, 0x22, 0x87, 0x63, 0x61, 0x38, 0x3f, 0xd9, 0x33, 0x2e, 0x19, 0xba, 0xd0, 0x19, 0x86, 0x71, 0xa6, 0xc2, 0x10, 0x39, 0x1c, 0x0b, 0xc3, 0xf9,
0x74, 0x38, 0x44, 0x4b, 0x20, 0x6c, 0x9a, 0x2a, 0xba, 0x7f, 0xe5, 0xc0, 0x3a, 0x71, 0x53, 0x16, 0xc9, 0xa6, 0xc3, 0x21, 0x5a, 0x02, 0x61, 0xd3, 0x54, 0xd1, 0xfd, 0x73, 0x07, 0xd6, 0x89, 0x9b,
0x46, 0xfb, 0xae, 0x2f, 0xdf, 0xcd, 0xce, 0xd0, 0x0c, 0xcd, 0x36, 0x60, 0xfe, 0x30, 0x4e, 0x87, 0xb2, 0x30, 0xda, 0x77, 0x7d, 0xf5, 0x6e, 0x76, 0x86, 0x66, 0x68, 0xb6, 0x01, 0xf3, 0x87, 0x71,
0x5c, 0xb6, 0x24, 0x0a, 0x3f, 0xb9, 0x37, 0xde, 0xac, 0x78, 0xe3, 0x3f, 0x72, 0x60, 0x8d, 0xba, 0x3a, 0xe4, 0xb2, 0x25, 0x51, 0xf8, 0xf1, 0xbd, 0xf1, 0x66, 0xc5, 0x1b, 0xff, 0xa1, 0x03, 0x6b,
0xba, 0x9f, 0xfb, 0xf9, 0x34, 0x93, 0xc3, 0xff, 0x15, 0x58, 0xc6, 0xa1, 0x72, 0xa5, 0x4e, 0xb2, 0xd4, 0xd5, 0xfd, 0xdc, 0xcf, 0xa7, 0x99, 0x1c, 0xfe, 0x2f, 0xc0, 0x32, 0x0e, 0x95, 0x2b, 0x75,
0xa3, 0x1b, 0x5a, 0xf3, 0x09, 0x15, 0xc4, 0xbb, 0x67, 0x3c, 0x9b, 0x98, 0x7d, 0x19, 0x3a, 0x66, 0x92, 0x1d, 0xdd, 0xd0, 0x9a, 0x4f, 0xa8, 0x20, 0xde, 0x3d, 0xe3, 0xd9, 0xc4, 0xec, 0xcb, 0xd0,
0x46, 0x84, 0xfa, 0xdc, 0xde, 0x39, 0xaf, 0x46, 0x59, 0x91, 0x9c, 0xdd, 0x33, 0x9e, 0xf5, 0x01, 0x31, 0x33, 0x22, 0xd4, 0xe7, 0xf6, 0xce, 0x79, 0x35, 0xca, 0x8a, 0xe4, 0xec, 0x9e, 0xf1, 0xac,
0xbb, 0x01, 0x40, 0xee, 0x06, 0xb1, 0x95, 0xa1, 0xec, 0x79, 0x7b, 0x92, 0x8c, 0xc5, 0xda, 0x3d, 0x0f, 0xd8, 0x0d, 0x00, 0x72, 0x37, 0x88, 0xad, 0x0c, 0x65, 0xcf, 0xdb, 0x93, 0x64, 0x2c, 0xd6,
0xe3, 0x19, 0xe4, 0xb7, 0x96, 0x60, 0x41, 0xec, 0x8f, 0xee, 0x7d, 0x58, 0xb6, 0x7a, 0x6a, 0x45, 0xee, 0x19, 0xcf, 0x20, 0xbf, 0xb5, 0x04, 0x0b, 0x62, 0x7f, 0x74, 0xef, 0xc3, 0xb2, 0xd5, 0x53,
0x19, 0x1d, 0x11, 0x65, 0x54, 0x82, 0xd2, 0x46, 0x35, 0x28, 0x75, 0xff, 0xb5, 0x01, 0x0c, 0xa5, 0x2b, 0xca, 0xe8, 0x88, 0x28, 0xa3, 0x12, 0x94, 0x36, 0xaa, 0x41, 0xa9, 0xfb, 0x2f, 0x0d, 0x60,
0xad, 0xb4, 0x9c, 0xb8, 0x41, 0xc7, 0x23, 0xcb, 0xdd, 0xea, 0x78, 0x26, 0xc4, 0xae, 0x01, 0x33, 0x28, 0x6d, 0xa5, 0xe5, 0xc4, 0x0d, 0x3a, 0x1e, 0x59, 0xee, 0x56, 0xc7, 0x33, 0x21, 0x76, 0x0d,
0x8a, 0x2a, 0xf7, 0x20, 0xf6, 0x8d, 0x9a, 0x1a, 0x34, 0x70, 0xc2, 0x57, 0x52, 0x31, 0xb0, 0x74, 0x98, 0x51, 0x54, 0xb9, 0x07, 0xb1, 0x6f, 0xd4, 0xd4, 0xa0, 0x81, 0x13, 0xbe, 0x92, 0x8a, 0x81,
0x2c, 0xc5, 0xba, 0xd5, 0xd6, 0xe1, 0xd6, 0x90, 0x4c, 0xb3, 0x23, 0x74, 0x20, 0x94, 0x43, 0xa6, 0xa5, 0x63, 0x29, 0xd6, 0xad, 0xb6, 0x0e, 0xb7, 0x86, 0x64, 0x9a, 0x1d, 0xa1, 0x03, 0xa1, 0x1c,
0xca, 0x65, 0x01, 0x59, 0x78, 0xa1, 0x80, 0x2c, 0x96, 0x05, 0xc4, 0x74, 0x09, 0x96, 0x2c, 0x97, 0x32, 0x55, 0x2e, 0x0b, 0xc8, 0xc2, 0x4b, 0x05, 0x64, 0xb1, 0x2c, 0x20, 0xa6, 0x4b, 0xb0, 0x64,
0x00, 0xfd, 0xaf, 0x49, 0x10, 0x91, 0x5f, 0x31, 0x98, 0x60, 0xeb, 0xd2, 0xff, 0xb2, 0x40, 0xb6, 0xb9, 0x04, 0xe8, 0x7f, 0x4d, 0x82, 0x88, 0xfc, 0x8a, 0xc1, 0x04, 0x5b, 0x97, 0xfe, 0x97, 0x05,
0x05, 0xab, 0xd2, 0xaf, 0x2b, 0xfc, 0x0e, 0xa0, 0x39, 0xae, 0xe0, 0xee, 0x67, 0x0e, 0xac, 0xe2, 0xb2, 0x2d, 0x58, 0x95, 0x7e, 0x5d, 0xe1, 0x77, 0x00, 0xcd, 0x71, 0x05, 0x77, 0x3f, 0x75, 0x60,
0x3c, 0x5b, 0xb2, 0xf8, 0x2e, 0x90, 0x2a, 0xbc, 0xa4, 0x28, 0x5a, 0xb4, 0x3f, 0xbb, 0x24, 0xbe, 0x15, 0xe7, 0xd9, 0x92, 0xc5, 0xf7, 0x80, 0x54, 0xe1, 0x15, 0x45, 0xd1, 0xa2, 0xfd, 0xe9, 0x25,
0x03, 0x2d, 0x62, 0x18, 0x27, 0x3c, 0x92, 0x82, 0xd8, 0xb3, 0x05, 0xb1, 0xb0, 0x42, 0xbb, 0x67, 0xf1, 0x5d, 0x68, 0x11, 0xc3, 0x38, 0xe1, 0x91, 0x14, 0xc4, 0x9e, 0x2d, 0x88, 0x85, 0x15, 0xda,
0xbc, 0x82, 0xd8, 0x10, 0xc3, 0x7f, 0x70, 0xa0, 0x2d, 0xbb, 0xf9, 0x53, 0xc7, 0x12, 0x7d, 0x58, 0x3d, 0xe3, 0x15, 0xc4, 0x86, 0x18, 0xfe, 0xbd, 0x03, 0x6d, 0xd9, 0xcd, 0x9f, 0x38, 0x96, 0xe8,
0x42, 0x89, 0x34, 0x1c, 0x76, 0x5d, 0xc6, 0xdd, 0x64, 0x82, 0x01, 0x1b, 0x6e, 0x9f, 0x56, 0x1c, 0xc3, 0x12, 0x4a, 0xa4, 0xe1, 0xb0, 0xeb, 0x32, 0xee, 0x26, 0x13, 0x0c, 0xd8, 0x70, 0xfb, 0xb4,
0x51, 0x86, 0x71, 0x2f, 0x24, 0x83, 0x9b, 0x0d, 0xf2, 0x20, 0x1c, 0xa8, 0x5a, 0x99, 0x80, 0xac, 0xe2, 0x88, 0x32, 0x8c, 0x7b, 0x21, 0x19, 0xdc, 0x6c, 0x90, 0x07, 0xe1, 0x40, 0xd5, 0xca, 0x04,
0xab, 0x42, 0xbb, 0x93, 0xe5, 0xfe, 0x98, 0xcb, 0x6d, 0x4e, 0x14, 0x30, 0x60, 0x92, 0x03, 0x2a, 0x64, 0x5d, 0x15, 0xda, 0x9d, 0x2c, 0xf7, 0xc7, 0x5c, 0x6e, 0x73, 0xa2, 0x80, 0x01, 0x93, 0x1c,
0xb9, 0x83, 0xee, 0x0f, 0x01, 0xce, 0x55, 0xaa, 0x74, 0xba, 0x5b, 0x3a, 0xc8, 0x61, 0x30, 0x39, 0x50, 0xc9, 0x1d, 0x74, 0xff, 0xa6, 0x03, 0xe7, 0x2a, 0x55, 0x3a, 0xdd, 0x2d, 0x1d, 0xe4, 0x30,
0x88, 0xb5, 0xaf, 0xed, 0x98, 0xbe, 0xb3, 0x55, 0xc5, 0xc6, 0x70, 0x56, 0xed, 0xe7, 0x38, 0xa7, 0x98, 0x1c, 0xc4, 0xda, 0xd7, 0x76, 0x4c, 0xdf, 0xd9, 0xaa, 0x62, 0x63, 0x38, 0xab, 0xf6, 0x73,
0xc5, 0xee, 0xdd, 0x20, 0x47, 0xe4, 0x2d, 0x5b, 0x06, 0xca, 0x0d, 0x2a, 0xdc, 0xd4, 0xdc, 0x7a, 0x9c, 0xd3, 0x62, 0xf7, 0x6e, 0x90, 0x23, 0xf2, 0xb6, 0x2d, 0x03, 0xe5, 0x06, 0x15, 0x6e, 0x6a,
0x7e, 0xec, 0x08, 0x7a, 0xda, 0x71, 0x90, 0x26, 0xde, 0x70, 0x2e, 0xb0, 0xad, 0x37, 0x5f, 0xd0, 0x6e, 0x3d, 0x3f, 0x76, 0x04, 0x3d, 0xed, 0x38, 0x48, 0x13, 0x6f, 0x38, 0x17, 0xd8, 0xd6, 0x5b,
0x16, 0xd9, 0xa3, 0x91, 0x6a, 0xe6, 0x54, 0x6e, 0x6c, 0x06, 0x97, 0x54, 0x1d, 0xd9, 0xf0, 0x6a, 0x2f, 0x69, 0x8b, 0xec, 0xd1, 0x48, 0x35, 0x73, 0x2a, 0x37, 0x36, 0x83, 0x4b, 0xaa, 0x8e, 0x6c,
0x7b, 0xcd, 0x97, 0x1a, 0xdb, 0x3d, 0xfc, 0xd8, 0x6e, 0xf4, 0x05, 0x8c, 0xfb, 0x3f, 0x74, 0xa0, 0x78, 0xb5, 0xbd, 0xe6, 0x2b, 0x8d, 0xed, 0x1e, 0x7e, 0x6c, 0x37, 0xfa, 0x12, 0xc6, 0xec, 0x43,
0x6b, 0xb3, 0x43, 0xd1, 0x91, 0x4a, 0xa8, 0x8c, 0x91, 0x72, 0xc8, 0x4a, 0x70, 0x35, 0x6c, 0x6c, 0xd8, 0x3c, 0xf1, 0x83, 0x5c, 0x75, 0xcb, 0x70, 0x86, 0xe6, 0xa9, 0xc9, 0x9d, 0x97, 0x34, 0xf9,
0xd4, 0x85, 0x8d, 0x66, 0x70, 0x38, 0xf7, 0xa2, 0xe0, 0xb0, 0xf9, 0x72, 0xc1, 0xe1, 0x7c, 0x5d, 0x44, 0x7c, 0x6c, 0x6d, 0x6c, 0xa7, 0x70, 0xec, 0xff, 0xad, 0x03, 0x5d, 0x9b, 0x0f, 0x8a, 0xa9,
0x70, 0xd8, 0xff, 0x2f, 0x07, 0x58, 0x75, 0x7d, 0xd9, 0x7d, 0x11, 0xb7, 0x46, 0x3c, 0x94, 0x76, 0x54, 0x78, 0x65, 0xf8, 0x94, 0xf3, 0x57, 0x82, 0xab, 0x21, 0x6a, 0xa3, 0x2e, 0x44, 0x35, 0x03,
0xe2, 0x97, 0x5e, 0x4e, 0x46, 0xd4, 0x1c, 0xaa, 0xaf, 0x51, 0x58, 0x4d, 0x43, 0x60, 0xba, 0x2d, 0xd1, 0xb9, 0x97, 0x05, 0xa2, 0xcd, 0x57, 0x0b, 0x44, 0xe7, 0xeb, 0x02, 0xd1, 0xfe, 0x7f, 0x3a,
0xcb, 0x5e, 0x5d, 0x55, 0x29, 0x5c, 0x6d, 0xbe, 0x38, 0x5c, 0x9d, 0x7f, 0x71, 0xb8, 0xba, 0x50, 0xc0, 0xaa, 0xb2, 0xc4, 0xee, 0x8b, 0x18, 0x39, 0xe2, 0xa1, 0xb4, 0x49, 0x3f, 0xf7, 0x6a, 0xf2,
0x0e, 0x57, 0xfb, 0xbf, 0x03, 0xcb, 0xd6, 0xaa, 0xff, 0xdf, 0x8d, 0xb8, 0xec, 0xf2, 0x88, 0x05, 0xa8, 0xe6, 0x4e, 0x7d, 0x8d, 0x8a, 0x61, 0x1a, 0x1d, 0xd3, 0x45, 0x5a, 0xf6, 0xea, 0xaa, 0x4a,
0xb6, 0xb0, 0xfe, 0x8f, 0x1b, 0xc0, 0xaa, 0x92, 0xf7, 0x73, 0xed, 0x03, 0xc9, 0x91, 0x65, 0x40, 0xa1, 0x71, 0xf3, 0xe5, 0xa1, 0xf1, 0xfc, 0xcb, 0x43, 0xe3, 0x85, 0x72, 0x68, 0xdc, 0xff, 0x5d,
0xe6, 0xa4, 0x1c, 0x59, 0xa6, 0xe3, 0xff, 0xd3, 0x28, 0xbe, 0x09, 0x6b, 0x29, 0x1f, 0xc6, 0xc7, 0x07, 0xd6, 0x6b, 0x16, 0xfd, 0x67, 0x37, 0x70, 0x5c, 0x26, 0xcb, 0x16, 0x34, 0xe4, 0x32, 0x99,
0x74, 0x08, 0x67, 0xa7, 0x3a, 0xaa, 0x15, 0xe8, 0xf4, 0xd9, 0x41, 0xfa, 0x92, 0x75, 0x66, 0x62, 0x60, 0xff, 0x37, 0x60, 0xd9, 0x12, 0xf4, 0x9f, 0x5d, 0xfb, 0x65, 0x2f, 0x4f, 0xc8, 0x99, 0x85,
0xec, 0x0c, 0xa5, 0x58, 0xdd, 0xdd, 0x84, 0x0d, 0x71, 0x94, 0x75, 0x4b, 0xb0, 0x52, 0x46, 0xf6, 0xf5, 0x7f, 0xd4, 0x00, 0x56, 0x55, 0xb6, 0xff, 0xd3, 0x3e, 0x54, 0xe7, 0x69, 0xae, 0x66, 0x9e,
0x2f, 0x1c, 0x38, 0x5b, 0xaa, 0x28, 0x0e, 0x16, 0x84, 0x1d, 0xb5, 0x8d, 0xab, 0x0d, 0x62, 0xff, 0xfe, 0x57, 0xf7, 0x81, 0xb7, 0x60, 0x2d, 0xe5, 0xc3, 0xf8, 0x98, 0xce, 0x1d, 0xed, 0xec, 0x4e,
0xa5, 0x00, 0x1b, 0xfd, 0x17, 0xfb, 0x4d, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac, 0xb5, 0x02, 0xfd, 0x5c, 0x3b, 0x2f, 0xb1, 0x64, 0x1d, 0x13, 0x19, 0x9b, 0x61, 0x29, 0x3d, 0xe1,
0xd7, 0x55, 0xb9, 0xe7, 0xe0, 0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x64, 0x6e, 0xc2, 0x86, 0x38, 0xbd, 0xbb, 0x25, 0x58, 0xa9, 0x7d, 0xe5, 0x4f, 0x1d, 0x38, 0x5b, 0xaa,
0x4a, 0xed, 0x2e, 0xab, 0x22, 0xba, 0x44, 0x96, 0xcd, 0xb6, 0xfb, 0x5b, 0x5b, 0xe7, 0xfe, 0x36, 0x28, 0xce, 0x52, 0xc4, 0xd6, 0x61, 0xef, 0x27, 0x36, 0x88, 0xfd, 0x97, 0x7a, 0x64, 0xf4, 0x5f,
0xb0, 0xaf, 0x4f, 0x79, 0x3a, 0xa3, 0x63, 0x0f, 0x9d, 0xaa, 0x38, 0x57, 0x8e, 0xe9, 0x17, 0x92, 0x48, 0x5b, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac, 0xd7, 0x55, 0xb9, 0xe7, 0xe0,
0xe9, 0xc1, 0x57, 0xf9, 0x4c, 0x9d, 0x2b, 0x35, 0x8a, 0x73, 0xa5, 0x57, 0x00, 0x30, 0x14, 0xa1, 0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x24, 0x87, 0xed, 0x2e, 0xab, 0x22,
0x73, 0x12, 0x75, 0xd2, 0x87, 0x31, 0xa0, 0x60, 0xe8, 0xde, 0x80, 0x75, 0x8b, 0xbf, 0x9e, 0xfd, 0x7a, 0x81, 0xd6, 0x36, 0x65, 0xf7, 0xb7, 0xb6, 0xce, 0xfd, 0x75, 0x60, 0x5f, 0x9f, 0xf2, 0x74,
0x05, 0xf9, 0x85, 0x08, 0x94, 0xed, 0xd3, 0x17, 0x59, 0xe7, 0xfe, 0x87, 0x03, 0x73, 0xbb, 0x71, 0x46, 0x27, 0x3d, 0x3a, 0x3b, 0x73, 0xae, 0x9c, 0xc6, 0x58, 0x48, 0xa6, 0x07, 0x5f, 0xe5, 0x33,
0x62, 0xa6, 0xd8, 0x1c, 0x3b, 0xc5, 0x26, 0x6d, 0xed, 0x40, 0x9b, 0xd2, 0x86, 0xb4, 0x14, 0x26, 0x75, 0x94, 0xd6, 0x28, 0x8e, 0xd2, 0x5e, 0x03, 0xc0, 0xe8, 0x8b, 0x8e, 0x86, 0xd4, 0xe1, 0x26,
0xc8, 0xb6, 0xa0, 0xeb, 0x4f, 0x72, 0x0c, 0x15, 0x0f, 0xe3, 0xf4, 0xc4, 0x4f, 0x47, 0x62, 0x49, 0x86, 0xbd, 0x82, 0xa1, 0x7b, 0x03, 0xd6, 0x2d, 0xfe, 0x7a, 0xf6, 0x17, 0xe4, 0x17, 0x22, 0x37,
0x6e, 0x35, 0x7a, 0x8e, 0x57, 0xaa, 0x61, 0x1b, 0x30, 0xa7, 0x8d, 0x12, 0x11, 0x60, 0x11, 0x9d, 0x60, 0x1f, 0x38, 0xc9, 0x3a, 0xf7, 0xdf, 0x1d, 0x98, 0xdb, 0x8d, 0x13, 0x33, 0xab, 0xe8, 0xd8,
0x0d, 0xca, 0x34, 0xce, 0x64, 0x94, 0x2b, 0x4b, 0xb8, 0xe2, 0xf6, 0xf7, 0xc2, 0xbd, 0x13, 0x12, 0x59, 0x45, 0x69, 0xf2, 0x07, 0xda, 0xa2, 0x4b, 0x4b, 0x60, 0x81, 0x6c, 0x0b, 0xba, 0xfe, 0x24,
0x5e, 0x57, 0x85, 0x76, 0x1f, 0x6d, 0x14, 0x91, 0xc9, 0xf4, 0x84, 0x2a, 0xbb, 0xff, 0xe6, 0xc0, 0xc7, 0xe8, 0xf8, 0x30, 0x4e, 0x4f, 0xfc, 0x74, 0x24, 0x96, 0xe4, 0x56, 0xa3, 0xe7, 0x78, 0xa5,
0x3c, 0xcd, 0x00, 0xea, 0xa4, 0x10, 0x44, 0x3a, 0xc8, 0xa4, 0xb4, 0xa8, 0x23, 0x74, 0xb2, 0x04, 0x1a, 0xb6, 0x01, 0x73, 0xda, 0x36, 0x12, 0x01, 0x16, 0xd1, 0xbf, 0xa2, 0xe4, 0xea, 0x4c, 0x06,
0x33, 0xd7, 0x3a, 0xde, 0x6c, 0xe8, 0x6e, 0x9b, 0x47, 0x9c, 0x97, 0xa1, 0x25, 0x4a, 0xfa, 0x4c, 0xf6, 0xb2, 0x84, 0x2b, 0x6e, 0x7f, 0x2f, 0x3c, 0x5a, 0x21, 0xe1, 0x75, 0x55, 0xb8, 0xfd, 0xa0,
0x90, 0x48, 0x0a, 0x90, 0x5d, 0x82, 0xe6, 0x51, 0x9c, 0xa8, 0x1d, 0x15, 0x54, 0x56, 0x2c, 0x4e, 0xa9, 0x24, 0x32, 0x99, 0x91, 0x51, 0x65, 0xf7, 0x5f, 0x1d, 0x98, 0xa7, 0x19, 0x40, 0x9d, 0x14,
0x3c, 0xc2, 0x8b, 0xfe, 0x20, 0x3f, 0xd1, 0x79, 0x61, 0x93, 0xcb, 0x30, 0xee, 0x4a, 0x9a, 0xad, 0x82, 0x48, 0x67, 0xb7, 0x94, 0x09, 0x76, 0x84, 0x4e, 0x96, 0x60, 0xe6, 0x5a, 0x27, 0xba, 0x0d,
0x39, 0x19, 0x25, 0xd4, 0xdd, 0x82, 0x95, 0x87, 0xf1, 0x88, 0x1b, 0x79, 0x90, 0x53, 0xa5, 0xce, 0xdd, 0x6d, 0xf3, 0x54, 0xf7, 0x32, 0xb4, 0x44, 0x49, 0x1f, 0x83, 0x12, 0x49, 0x01, 0xb2, 0x4b,
0xfd, 0x5d, 0x07, 0x96, 0x14, 0x31, 0xbb, 0x0a, 0x4d, 0xdc, 0x6a, 0x4b, 0xce, 0xad, 0xce, 0x86, 0xd0, 0x3c, 0x8a, 0x13, 0xe5, 0x44, 0x80, 0x4a, 0x04, 0xc6, 0x89, 0x47, 0x78, 0xd1, 0x1f, 0xe4,
0x23, 0x9d, 0x47, 0x14, 0x68, 0x22, 0x29, 0x4a, 0x2e, 0x5c, 0x21, 0x15, 0x23, 0x17, 0x4e, 0x86, 0x27, 0x3a, 0x2f, 0xb6, 0x86, 0x32, 0x8c, 0x9b, 0xa3, 0x66, 0x6b, 0x4e, 0x46, 0x09, 0x75, 0xb7,
0xee, 0x6e, 0x69, 0x33, 0x2e, 0xa1, 0xee, 0x5f, 0x3b, 0xb0, 0x6c, 0xb5, 0x81, 0x21, 0x4d, 0xe8, 0x60, 0xe5, 0x61, 0x3c, 0xe2, 0x46, 0xea, 0xe7, 0x54, 0xa9, 0x73, 0x7f, 0xd3, 0x81, 0x25, 0x45,
0x67, 0xb9, 0xcc, 0x30, 0xca, 0xe5, 0x31, 0x21, 0x33, 0x33, 0xd6, 0xb0, 0x33, 0x63, 0x3a, 0x67, 0xcc, 0xae, 0x42, 0x13, 0x77, 0xfc, 0x92, 0x3f, 0xaf, 0x0f, 0x00, 0x90, 0xce, 0x23, 0x0a, 0x34,
0x33, 0x67, 0xe6, 0x6c, 0xae, 0x43, 0xab, 0x38, 0x84, 0x6e, 0x5a, 0xa6, 0x0f, 0x5b, 0x54, 0x79, 0x91, 0x94, 0x18, 0x28, 0xbc, 0x3f, 0x95, 0x16, 0x28, 0x9c, 0x1b, 0xdd, 0xdd, 0x92, 0x4f, 0x50,
0xfe, 0x82, 0x08, 0xf9, 0x0c, 0xe3, 0x30, 0x4e, 0xe5, 0x19, 0xad, 0x28, 0xb8, 0x37, 0xa0, 0x6d, 0x42, 0xdd, 0xbf, 0x70, 0x60, 0xd9, 0x6a, 0x03, 0xa3, 0xb8, 0xd0, 0xcf, 0x72, 0x99, 0x54, 0x95,
0xd0, 0x63, 0x37, 0x22, 0x9e, 0x9f, 0xc4, 0xe9, 0x13, 0x95, 0xa0, 0x93, 0x45, 0x7d, 0x9c, 0xd5, 0xcb, 0x63, 0x42, 0x66, 0x32, 0xb0, 0x61, 0x27, 0x03, 0x75, 0x9a, 0x6a, 0xce, 0x4c, 0x53, 0x5d,
0x28, 0x8e, 0xb3, 0xdc, 0xbf, 0x71, 0x60, 0x19, 0x65, 0x30, 0x88, 0xc6, 0x7b, 0x71, 0x18, 0x0c, 0x87, 0x56, 0x71, 0xee, 0xde, 0xb4, 0x4c, 0x1f, 0xb6, 0xa8, 0x8e, 0x36, 0x0a, 0x22, 0xe4, 0x33,
0x67, 0xb4, 0xf6, 0x4a, 0xdc, 0xe4, 0xe1, 0xad, 0x92, 0x45, 0x1b, 0x46, 0xd9, 0x56, 0x11, 0x8d, 0x8c, 0xc3, 0x38, 0x95, 0xc7, 0xd2, 0xa2, 0xe0, 0xde, 0x80, 0xb6, 0x41, 0x8f, 0xdd, 0x88, 0x78,
0x54, 0x44, 0x5d, 0x46, 0x4d, 0x45, 0x39, 0x3f, 0xf0, 0x33, 0x29, 0xfc, 0x72, 0x2f, 0xb2, 0x40, 0x7e, 0x12, 0xa7, 0x4f, 0x55, 0x4e, 0x52, 0x16, 0xf5, 0x09, 0x5e, 0xa3, 0x38, 0xc1, 0x73, 0xff,
0xd4, 0x27, 0x04, 0x52, 0x3f, 0xe7, 0x83, 0x49, 0x10, 0x86, 0x81, 0xa0, 0x15, 0x2e, 0x42, 0x5d, 0xd2, 0x81, 0x65, 0x94, 0xc1, 0x20, 0x1a, 0xef, 0xc5, 0x61, 0x30, 0x9c, 0xd1, 0xda, 0x2b, 0x71,
0x95, 0xfb, 0x83, 0x06, 0xb4, 0xa5, 0xa5, 0xbc, 0x3b, 0x1a, 0x8b, 0x54, 0xb8, 0x74, 0xb4, 0xb4, 0x93, 0xe7, 0xd5, 0x4a, 0x16, 0x6d, 0x18, 0x65, 0x5b, 0x05, 0x71, 0x52, 0x11, 0x75, 0x19, 0x35,
0xb9, 0x30, 0x10, 0x55, 0x6f, 0xb9, 0x66, 0x06, 0x52, 0x5e, 0xd6, 0xb9, 0xea, 0xb2, 0x5e, 0x84, 0x15, 0xe5, 0xfc, 0xc0, 0xcf, 0xa4, 0xf0, 0xcb, 0xbd, 0xc8, 0x02, 0x51, 0x9f, 0x10, 0x48, 0xfd,
0x16, 0x8a, 0xd7, 0x5b, 0xe4, 0x03, 0x8a, 0x3b, 0x0b, 0x05, 0xa0, 0x6a, 0x77, 0xa8, 0x76, 0xbe, 0x9c, 0x0f, 0x26, 0x41, 0x18, 0x06, 0x82, 0x56, 0x78, 0x2a, 0x75, 0x55, 0xee, 0x0f, 0x1a, 0xd0,
0xa8, 0x25, 0xc0, 0xf2, 0xfa, 0x16, 0x4a, 0x5e, 0xdf, 0x3b, 0xd0, 0x91, 0x6c, 0x68, 0xde, 0xc9, 0x96, 0x96, 0xf2, 0xee, 0x68, 0x2c, 0xb2, 0xff, 0xd2, 0xdf, 0xd3, 0xe6, 0xc2, 0x40, 0x54, 0xbd,
0x3a, 0x14, 0x02, 0x6e, 0xad, 0x89, 0x67, 0x51, 0xaa, 0x2f, 0x77, 0xd4, 0x97, 0x4b, 0x2f, 0xfa, 0xe5, 0x21, 0x1a, 0x48, 0x79, 0x59, 0xe7, 0xaa, 0xcb, 0x7a, 0x11, 0x5a, 0x28, 0x5e, 0x6f, 0x93,
0x52, 0x51, 0xd2, 0xc9, 0x90, 0x98, 0x9b, 0xfb, 0xa9, 0x9f, 0x1c, 0xa9, 0xdd, 0x67, 0xa4, 0x8f, 0x2b, 0x2a, 0xae, 0x69, 0x14, 0x80, 0xaa, 0xdd, 0xa1, 0xda, 0xf9, 0xa2, 0x96, 0x00, 0xcb, 0xf9,
0xbb, 0x09, 0x66, 0x5b, 0x30, 0x8f, 0x9f, 0x29, 0x6b, 0x5d, 0xaf, 0x74, 0x82, 0x84, 0x5d, 0x85, 0x5c, 0x28, 0x39, 0x9f, 0xef, 0x42, 0x47, 0xb2, 0xa1, 0x79, 0x27, 0xeb, 0x50, 0x08, 0xb8, 0xb5,
0x79, 0x3e, 0x1a, 0x73, 0x15, 0x79, 0x30, 0x3b, 0x06, 0xc4, 0x35, 0xf2, 0x04, 0x01, 0x9a, 0x00, 0x26, 0x9e, 0x45, 0xa9, 0xbe, 0xdc, 0x51, 0x5f, 0x2e, 0xbd, 0xec, 0x4b, 0x45, 0x49, 0x87, 0x61,
0x44, 0x4b, 0x26, 0xc0, 0xb6, 0xf4, 0x0b, 0x58, 0x7c, 0x7f, 0xe4, 0x6e, 0x00, 0x7b, 0x28, 0xa4, 0x62, 0x6e, 0xee, 0xa7, 0x7e, 0x72, 0xa4, 0x76, 0x9f, 0x91, 0x3e, 0xe1, 0x27, 0x98, 0x6d, 0xc1,
0xd6, 0xcc, 0x9c, 0xfe, 0xc1, 0x1c, 0xb4, 0x0d, 0x18, 0xb5, 0x79, 0x8c, 0x1d, 0x1e, 0x8c, 0x02, 0x3c, 0x7e, 0xa6, 0xac, 0x75, 0xbd, 0xd2, 0x09, 0x12, 0x76, 0x15, 0xe6, 0xf9, 0x68, 0xcc, 0x55,
0x7f, 0xc2, 0x73, 0x9e, 0x4a, 0x49, 0x2d, 0xa1, 0x48, 0xe7, 0x1f, 0x8f, 0x07, 0xf1, 0x34, 0x1f, 0xb0, 0xc5, 0xec, 0xb0, 0x17, 0xd7, 0xc8, 0x13, 0x04, 0x68, 0x02, 0x10, 0x2d, 0x99, 0x00, 0xdb,
0x8c, 0xf8, 0x38, 0xe5, 0x62, 0x8f, 0xc4, 0xcd, 0xc0, 0x42, 0x91, 0x6e, 0xe2, 0x3f, 0x35, 0xe9, 0xd2, 0x2f, 0x60, 0xf1, 0xfd, 0x91, 0xbb, 0x01, 0xec, 0xa1, 0x90, 0x5a, 0x33, 0x59, 0xfc, 0x3b,
0x84, 0x3c, 0x94, 0x50, 0x95, 0x07, 0x15, 0x73, 0xd4, 0x2c, 0xf2, 0xa0, 0x62, 0x46, 0xca, 0x76, 0x73, 0xd0, 0x36, 0x60, 0xd4, 0xe6, 0x31, 0x76, 0x78, 0x30, 0x0a, 0xfc, 0x09, 0xcf, 0x79, 0x2a,
0x68, 0xbe, 0xc6, 0x0e, 0xbd, 0x0d, 0x9b, 0xc2, 0xe2, 0x48, 0xdd, 0x1c, 0x94, 0xc4, 0xe4, 0x94, 0x25, 0xb5, 0x84, 0x22, 0x9d, 0x7f, 0x3c, 0x1e, 0xc4, 0xd3, 0x7c, 0x30, 0xe2, 0xe3, 0x94, 0x8b,
0x5a, 0xb6, 0x05, 0xab, 0xd8, 0x67, 0x25, 0xe0, 0x59, 0xf0, 0x89, 0xc8, 0x4c, 0x38, 0x5e, 0x05, 0x3d, 0x12, 0x37, 0x03, 0x0b, 0x45, 0xba, 0x89, 0xff, 0xcc, 0xa4, 0x13, 0xf2, 0x50, 0x42, 0x55,
0x47, 0x5a, 0x54, 0x47, 0x8b, 0x56, 0x9c, 0x15, 0x55, 0x70, 0xa2, 0xf5, 0x9f, 0xda, 0xb4, 0x2d, 0xea, 0x57, 0xcc, 0x51, 0xb3, 0x48, 0xfd, 0x8a, 0x19, 0x29, 0xdb, 0xa1, 0xf9, 0x1a, 0x3b, 0xf4,
0x49, 0x5b, 0xc2, 0xdd, 0x65, 0x68, 0xef, 0xe7, 0x71, 0xa2, 0x16, 0xa5, 0x0b, 0x1d, 0x51, 0x94, 0x0e, 0x6c, 0x0a, 0x8b, 0x23, 0x75, 0x73, 0x50, 0x12, 0x93, 0x53, 0x6a, 0xd9, 0x16, 0xac, 0x62,
0x27, 0x83, 0x17, 0xe0, 0x3c, 0x49, 0xd1, 0xa3, 0x38, 0x89, 0xc3, 0x78, 0x3c, 0xdb, 0x9f, 0x1e, 0x9f, 0x95, 0x80, 0x67, 0xc1, 0xc7, 0x22, 0x19, 0xe3, 0x78, 0x15, 0x1c, 0x69, 0x51, 0x1d, 0x2d,
0x64, 0xc3, 0x34, 0x48, 0x30, 0x22, 0x70, 0xff, 0xde, 0x81, 0x75, 0xab, 0x56, 0xa6, 0x32, 0xbe, 0x5a, 0x71, 0x3c, 0x56, 0xc1, 0x89, 0xd6, 0x7f, 0x66, 0xd3, 0xb6, 0x24, 0x6d, 0x09, 0x77, 0x97,
0x20, 0x44, 0x5a, 0x1f, 0xe9, 0x08, 0xc1, 0x5b, 0x33, 0xcc, 0xa1, 0x20, 0x14, 0x49, 0xa4, 0xc7, 0xa1, 0xbd, 0x9f, 0xc7, 0x89, 0x5a, 0x94, 0x2e, 0x74, 0x44, 0x51, 0x1e, 0x86, 0x5e, 0x80, 0xf3,
0xf2, 0x94, 0xe7, 0x26, 0xac, 0xa8, 0x9e, 0xa9, 0x0f, 0x85, 0x14, 0xf6, 0xaa, 0x52, 0x28, 0xbf, 0x24, 0x45, 0x8f, 0xe2, 0x24, 0x0e, 0xe3, 0xf1, 0x6c, 0x7f, 0x7a, 0x90, 0x0d, 0xd3, 0x20, 0xc1,
0xef, 0xca, 0x0f, 0x14, 0x8b, 0x5f, 0x15, 0x7e, 0x35, 0x1f, 0xd1, 0x18, 0x55, 0x4c, 0xdb, 0x57, 0xc0, 0xc4, 0xfd, 0x3b, 0x07, 0xd6, 0xad, 0x5a, 0x99, 0xbd, 0xf9, 0xbc, 0x10, 0x69, 0x7d, 0x8a,
0xdf, 0x9b, 0xce, 0xbc, 0xea, 0xc1, 0x50, 0x83, 0x99, 0xfb, 0x47, 0x0e, 0x40, 0xd1, 0x3b, 0x14, 0x25, 0x04, 0x6f, 0xcd, 0x30, 0x87, 0x82, 0x50, 0xe4, 0xcd, 0x1e, 0xcb, 0x83, 0xad, 0x9b, 0xb0,
0x8c, 0xc2, 0xa4, 0x8b, 0xcb, 0x6a, 0x86, 0xf9, 0x7e, 0x0d, 0x3a, 0x3a, 0x9b, 0x5f, 0xec, 0x12, 0xa2, 0x7a, 0xa6, 0x3e, 0x14, 0x52, 0xd8, 0xab, 0x4a, 0xa1, 0xfc, 0xbe, 0x2b, 0x3f, 0x50, 0x2c,
0x6d, 0x85, 0xa1, 0xc3, 0x75, 0x05, 0x56, 0xc6, 0x61, 0x7c, 0x40, 0x5b, 0x2c, 0x1d, 0x35, 0x67, 0x7e, 0x51, 0xf8, 0xd5, 0x7c, 0x44, 0x63, 0x54, 0x61, 0x7c, 0x5f, 0x7d, 0x6f, 0x3a, 0xf3, 0xaa,
0xf2, 0x7c, 0xb4, 0x2b, 0xe0, 0x7b, 0x12, 0x2d, 0xb6, 0x94, 0xa6, 0xb1, 0xa5, 0xb8, 0xdf, 0x69, 0x07, 0x43, 0x0d, 0x66, 0xee, 0xef, 0x3b, 0x00, 0x45, 0xef, 0x50, 0x30, 0x0a, 0x93, 0x2e, 0xee,
0xe8, 0x1c, 0x70, 0x31, 0xe6, 0x53, 0xb5, 0x8c, 0xed, 0x54, 0x8c, 0xe3, 0x29, 0x29, 0x57, 0xca, 0xe7, 0x19, 0xe6, 0xfb, 0x0d, 0xe8, 0xe8, 0x03, 0x8c, 0x62, 0x97, 0x68, 0x2b, 0x0c, 0x1d, 0xae,
0xde, 0xec, 0xbd, 0x30, 0x90, 0xbd, 0x01, 0xdd, 0x54, 0x58, 0x1f, 0x65, 0x9a, 0x9a, 0xcf, 0x31, 0x2b, 0xb0, 0x32, 0x0e, 0xe3, 0x03, 0xda, 0x62, 0xe9, 0x74, 0x3d, 0x93, 0x47, 0xc2, 0x5d, 0x01,
0x4d, 0xcb, 0xa9, 0xb5, 0xef, 0xfc, 0x02, 0xac, 0xfa, 0xa3, 0x63, 0x9e, 0xe6, 0x01, 0x45, 0x34, 0xdf, 0x93, 0x68, 0xb1, 0xa5, 0x34, 0x8d, 0x2d, 0xc5, 0xfd, 0x4e, 0x43, 0xa7, 0xbd, 0x8b, 0x31,
0xb4, 0xe9, 0x0b, 0x83, 0xba, 0x62, 0xe0, 0xb4, 0x17, 0x5f, 0x81, 0x15, 0x79, 0x26, 0xad, 0x29, 0x9f, 0xaa, 0x65, 0x6c, 0xa7, 0x62, 0x1c, 0x4f, 0xc9, 0x32, 0x53, 0xc2, 0x6a, 0xef, 0xa5, 0xf1,
0xe5, 0x4d, 0xa4, 0x02, 0x46, 0x42, 0xf7, 0xfb, 0x2a, 0xdd, 0x6c, 0xaf, 0xe1, 0xe9, 0x33, 0x62, 0xf4, 0x0d, 0xe8, 0xa6, 0xc2, 0xfa, 0x28, 0xd3, 0xd4, 0x7c, 0x81, 0x69, 0x5a, 0x4e, 0xad, 0x7d,
0x8e, 0xae, 0x51, 0x1a, 0xdd, 0xe7, 0x64, 0xea, 0x77, 0xa4, 0xc2, 0x26, 0x99, 0x84, 0x17, 0xa0, 0xe7, 0xff, 0xc1, 0xaa, 0x3f, 0x3a, 0xe6, 0x69, 0x1e, 0x50, 0x44, 0x43, 0x9b, 0xbe, 0x30, 0xa8,
0x4c, 0xd5, 0xdb, 0x53, 0xda, 0x7c, 0x99, 0x29, 0x75, 0x3f, 0x73, 0x60, 0x71, 0x37, 0x4e, 0x76, 0x2b, 0x06, 0x4e, 0x7b, 0xf1, 0x15, 0x58, 0x91, 0xc7, 0xf0, 0x9a, 0x52, 0x5e, 0xbe, 0x2a, 0x60,
0xe5, 0xf1, 0x32, 0x29, 0x82, 0xbe, 0xf1, 0xa1, 0x8a, 0xa6, 0x57, 0xdc, 0xa8, 0x78, 0xc5, 0xd5, 0x24, 0x74, 0xbf, 0xaf, 0x32, 0xec, 0xf6, 0x1a, 0x9e, 0x3e, 0x23, 0xe6, 0xe8, 0x1a, 0xa5, 0xd1,
0xbd, 0x76, 0xb9, 0xbc, 0xd7, 0xfe, 0x1a, 0x5c, 0xa0, 0x68, 0x39, 0x8d, 0x93, 0x38, 0x45, 0x65, 0x7d, 0x46, 0x66, 0xbb, 0x47, 0x2a, 0x6c, 0x92, 0xe7, 0x0e, 0x02, 0x94, 0xa7, 0x13, 0xf6, 0x94,
0xf4, 0x43, 0xb1, 0xb1, 0xc6, 0x51, 0x7e, 0xa4, 0xcc, 0xd8, 0xf3, 0x48, 0x28, 0x3a, 0x0a, 0xf3, 0x36, 0x5f, 0x65, 0x4a, 0xdd, 0x4f, 0x1d, 0x58, 0xdc, 0x8d, 0x93, 0x5d, 0x79, 0xa2, 0x4e, 0x8a,
0xe3, 0x81, 0x70, 0x86, 0xa5, 0x6f, 0x20, 0xac, 0x5b, 0xb5, 0xc2, 0xfd, 0x12, 0xb4, 0xc8, 0xb9, 0xa0, 0x2f, 0xb9, 0xa8, 0xa2, 0xe9, 0x15, 0x37, 0x2a, 0x5e, 0x71, 0x75, 0xaf, 0x5d, 0x2e, 0xef,
0xa5, 0x61, 0xbd, 0x09, 0xad, 0xa3, 0x38, 0x19, 0x1c, 0x05, 0x51, 0xae, 0x94, 0xbb, 0x5b, 0x78, 0xb5, 0xbf, 0x04, 0x17, 0x28, 0x68, 0x4f, 0xe3, 0x24, 0x4e, 0x51, 0x19, 0xfd, 0x50, 0x6c, 0xac,
0x9d, 0xbb, 0x34, 0x21, 0x9a, 0xc0, 0xfd, 0xf1, 0x1c, 0x2c, 0xbe, 0x1f, 0x1d, 0xc7, 0xc1, 0x90, 0x71, 0x94, 0x1f, 0x29, 0x33, 0xf6, 0x22, 0x12, 0x8a, 0x8e, 0xc2, 0xfc, 0x78, 0x20, 0x9c, 0x61,
0x32, 0xd3, 0x13, 0x3e, 0x89, 0xd5, 0xfd, 0x17, 0xfc, 0x8d, 0x53, 0x41, 0x67, 0xc1, 0x49, 0x2e, 0xe9, 0x1b, 0x08, 0xeb, 0x56, 0xad, 0x70, 0xbf, 0x08, 0x2d, 0x72, 0x6e, 0x69, 0x58, 0x6f, 0x41,
0x53, 0xcb, 0xaa, 0x88, 0xdb, 0x7d, 0x5a, 0xdc, 0x09, 0x13, 0xaa, 0x63, 0x20, 0xe8, 0xd8, 0xa7, 0xeb, 0x28, 0x4e, 0x06, 0x47, 0x41, 0x94, 0x2b, 0xe5, 0xee, 0x16, 0x5e, 0xe7, 0x2e, 0x4d, 0x88,
0xe6, 0x85, 0x38, 0x59, 0x2a, 0x2e, 0x10, 0xcd, 0x1b, 0x17, 0x88, 0xe8, 0x1c, 0x43, 0x1c, 0x73, 0x26, 0x70, 0x7f, 0x34, 0x07, 0x8b, 0xef, 0x47, 0xc7, 0x71, 0x30, 0xa4, 0x64, 0xfc, 0x84, 0x4f,
0x93, 0x7c, 0x2d, 0x79, 0xaa, 0x48, 0x81, 0x48, 0xca, 0x45, 0x96, 0x83, 0x1c, 0x87, 0x45, 0x19, 0x62, 0x75, 0xe5, 0x07, 0x7f, 0xe3, 0x54, 0xd0, 0xf1, 0x77, 0x92, 0xcb, 0x6c, 0xba, 0x2a, 0xe2,
0x88, 0x98, 0x20, 0x3a, 0x17, 0xe2, 0x03, 0x41, 0x23, 0x8c, 0xaf, 0x09, 0xa1, 0xb3, 0x55, 0xbe, 0x76, 0x9f, 0x16, 0xd7, 0xe0, 0x84, 0xea, 0x18, 0x08, 0x3a, 0xf6, 0xa9, 0x79, 0x07, 0x50, 0x96,
0x53, 0xd7, 0x12, 0x32, 0x5f, 0x82, 0xd1, 0x42, 0x8f, 0xb8, 0x36, 0xa4, 0x62, 0x0c, 0x20, 0xee, 0x8a, 0x3b, 0x53, 0xf3, 0xc6, 0x9d, 0x29, 0x3a, 0xba, 0x11, 0x27, 0xfb, 0x24, 0x5f, 0x4b, 0x9e,
0xbc, 0x95, 0x71, 0x23, 0x7c, 0x11, 0xc7, 0xf5, 0x2a, 0x7c, 0x41, 0x41, 0xf1, 0xc3, 0xf0, 0xc0, 0x2a, 0x52, 0x20, 0x92, 0x72, 0x91, 0x6c, 0x21, 0xc7, 0x61, 0x51, 0x06, 0x22, 0x26, 0x88, 0xce,
0x1f, 0x3e, 0xa1, 0x9b, 0x8e, 0x74, 0x3a, 0xdf, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x9d, 0x85, 0xf8, 0x40, 0xd0, 0x08, 0xe3, 0x6b, 0x42, 0xe8, 0x6c, 0x95, 0xaf, 0x11, 0xb6, 0x84, 0xcc,
0x84, 0x35, 0x3d, 0x13, 0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5, 0x97, 0x60, 0xb4, 0xd0, 0x23, 0xae, 0x0d, 0xa9, 0x18, 0x03, 0x88, 0x6b, 0x7e, 0x65, 0xdc, 0x08,
0x8c, 0xe9, 0x68, 0x45, 0x4d, 0x22, 0x33, 0x5b, 0xbe, 0x62, 0x1f, 0xa0, 0x7f, 0x03, 0xd8, 0xcd, 0x5f, 0xc4, 0x0d, 0x05, 0x15, 0xbe, 0xa0, 0xa0, 0xf8, 0x61, 0x78, 0xe0, 0x0f, 0x9f, 0xd2, 0xe5,
0xd1, 0x48, 0xae, 0xb7, 0x0e, 0x19, 0x8b, 0x95, 0x72, 0xac, 0x95, 0xaa, 0x99, 0xb1, 0x46, 0xed, 0x4e, 0xba, 0x90, 0xd0, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x1d, 0xfe, 0x35, 0x3d, 0x13,
0x8c, 0xb9, 0x77, 0xa1, 0xbd, 0x67, 0x5c, 0x77, 0x24, 0xd1, 0x50, 0x17, 0x1d, 0xa5, 0x38, 0x19, 0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5, 0x8c, 0xe9, 0x68, 0x45,
0x88, 0xd1, 0x60, 0xc3, 0x6c, 0xd0, 0xfd, 0x65, 0x60, 0x0f, 0x82, 0x2c, 0xd7, 0xfd, 0x13, 0xcb, 0x4d, 0x22, 0xf3, 0x80, 0x60, 0xc5, 0xbe, 0x33, 0xf0, 0x0d, 0x60, 0x37, 0x47, 0x23, 0xb9, 0xde,
0xf1, 0x1a, 0x74, 0x74, 0x80, 0x5d, 0x1c, 0xef, 0xb7, 0x25, 0x46, 0xc7, 0xee, 0x37, 0xc5, 0xbd, 0x3a, 0x64, 0x2c, 0x56, 0xca, 0xb1, 0x56, 0xaa, 0x66, 0xc6, 0x1a, 0xb5, 0x33, 0xe6, 0xde, 0x85,
0x80, 0xf2, 0xc0, 0xb6, 0x60, 0x29, 0x10, 0x50, 0x59, 0x13, 0x14, 0xa5, 0xae, 0x47, 0x7f, 0x4d, 0xf6, 0x9e, 0x71, 0xc3, 0x93, 0x44, 0x43, 0xdd, 0xed, 0x94, 0xe2, 0x64, 0x20, 0x46, 0x83, 0x0d,
0x82, 0xd6, 0x2e, 0xfa, 0x03, 0x07, 0x16, 0xe5, 0xd0, 0xd0, 0xdb, 0xb0, 0x2e, 0x7a, 0x8a, 0x81, 0xb3, 0x41, 0xf7, 0xe7, 0x81, 0x3d, 0x08, 0xb2, 0x5c, 0xf7, 0x4f, 0x2c, 0xc7, 0x1b, 0xd0, 0xd1,
0x59, 0x58, 0xfd, 0xf5, 0xb8, 0xaa, 0x0c, 0xcf, 0xd5, 0xc9, 0x30, 0x83, 0x66, 0xe2, 0xe7, 0x47, 0x01, 0x76, 0x71, 0xa3, 0xa1, 0x2d, 0x31, 0xba, 0x69, 0x70, 0x53, 0x5c, 0x85, 0x28, 0x0f, 0x6c,
0x14, 0xa0, 0xb4, 0x3c, 0xfa, 0xcd, 0x56, 0x45, 0xd0, 0x2c, 0x74, 0x85, 0x02, 0xe6, 0xba, 0x1b, 0x0b, 0x96, 0x02, 0x01, 0x95, 0x35, 0x41, 0x51, 0xea, 0x7a, 0xf4, 0xd7, 0x24, 0x68, 0xed, 0xa2,
0x99, 0xc2, 0x24, 0x57, 0x70, 0x1c, 0x14, 0x9d, 0xa4, 0x0b, 0x5c, 0x27, 0xc8, 0xe5, 0x2d, 0x85, 0x3f, 0x70, 0x60, 0x51, 0x0e, 0x0d, 0xbd, 0x0d, 0xeb, 0x6e, 0xab, 0x18, 0x98, 0x85, 0xd5, 0xdf,
0x02, 0x2e, 0xe6, 0x4b, 0xb2, 0x28, 0xcf, 0x97, 0x24, 0xf5, 0x74, 0xbd, 0xdb, 0x87, 0xde, 0x1d, 0x08, 0xac, 0xca, 0xf0, 0x5c, 0x9d, 0x0c, 0x33, 0x68, 0x26, 0x7e, 0x7e, 0x44, 0x01, 0x4a, 0xcb,
0x1e, 0xf2, 0x9c, 0xdf, 0x0c, 0xc3, 0x32, 0xff, 0x0b, 0x70, 0xbe, 0xa6, 0x4e, 0x3a, 0x2d, 0xf7, 0xa3, 0xdf, 0x6c, 0x55, 0x04, 0xcd, 0x42, 0x57, 0x28, 0x60, 0xae, 0xbb, 0x84, 0x2a, 0x4c, 0x72,
0x60, 0xed, 0x0e, 0x3f, 0x98, 0x8e, 0x1f, 0xf0, 0xe3, 0xe2, 0x14, 0x8b, 0x41, 0x33, 0x3b, 0x8a, 0x05, 0xc7, 0x41, 0xd1, 0xe5, 0x01, 0x81, 0xeb, 0x33, 0x01, 0x79, 0x31, 0xa3, 0x80, 0x8b, 0xf9,
0x4f, 0xe4, 0xda, 0xd2, 0x6f, 0xf6, 0x0a, 0x40, 0x88, 0x34, 0x83, 0x2c, 0xe1, 0x43, 0x75, 0x31, 0x92, 0x2c, 0xca, 0xf3, 0x25, 0x49, 0x3d, 0x5d, 0xef, 0xf6, 0xa1, 0x77, 0x87, 0x87, 0x3c, 0xe7,
0x8c, 0x90, 0xfd, 0x84, 0x0f, 0xdd, 0xb7, 0x81, 0x99, 0x7c, 0xe4, 0x10, 0xd0, 0x0e, 0x4c, 0x0f, 0x37, 0xc3, 0xb0, 0xcc, 0xff, 0x02, 0x9c, 0xaf, 0xa9, 0x93, 0x4e, 0xcb, 0x3d, 0x58, 0xbb, 0xc3,
0x06, 0xd9, 0x2c, 0xcb, 0xf9, 0x44, 0xdd, 0x78, 0x33, 0x21, 0xf7, 0x0a, 0x74, 0xf6, 0xfc, 0x99, 0x0f, 0xa6, 0xe3, 0x07, 0xfc, 0xb8, 0x38, 0xb8, 0x63, 0xd0, 0xcc, 0x8e, 0xe2, 0x13, 0xb9, 0xb6,
0xc7, 0x3f, 0x96, 0x77, 0x6d, 0x31, 0x36, 0xf6, 0x67, 0x28, 0xca, 0x3a, 0x36, 0xa6, 0x6a, 0xf7, 0xf4, 0x9b, 0xbd, 0x06, 0x10, 0x22, 0xcd, 0x20, 0x4b, 0xf8, 0x50, 0xdd, 0x85, 0x23, 0x64, 0x3f,
0x3f, 0x1b, 0xb0, 0x20, 0x28, 0x91, 0xeb, 0x88, 0x67, 0x79, 0x10, 0x89, 0x13, 0x1c, 0xc9, 0xd5, 0xe1, 0x43, 0xf7, 0x1d, 0x60, 0x26, 0x1f, 0x39, 0x04, 0xb4, 0x03, 0xd3, 0x83, 0x41, 0x36, 0xcb,
0x80, 0x2a, 0xb2, 0xd1, 0xa8, 0x91, 0x0d, 0xe9, 0xad, 0xaa, 0x4b, 0x36, 0x52, 0x08, 0x2c, 0x0c, 0x72, 0x3e, 0x51, 0x97, 0xfc, 0x4c, 0xc8, 0xbd, 0x02, 0x9d, 0x3d, 0x7f, 0xe6, 0xf1, 0x8f, 0xe4,
0xdd, 0x9a, 0xe2, 0x64, 0x5c, 0x04, 0x67, 0x05, 0x50, 0x4a, 0x96, 0x14, 0xd6, 0x46, 0xf4, 0x4f, 0xf5, 0x62, 0x8c, 0x8d, 0xfd, 0x19, 0x8a, 0xb2, 0x8e, 0x8d, 0xa9, 0xda, 0xfd, 0x8f, 0x06, 0x2c,
0x09, 0xad, 0x14, 0x07, 0x13, 0xaa, 0xb5, 0x69, 0x8b, 0x42, 0x6a, 0x2a, 0x36, 0xad, 0x62, 0xbb, 0x08, 0x4a, 0xe4, 0x3a, 0xe2, 0x59, 0x1e, 0x44, 0xe2, 0xd0, 0x4a, 0x72, 0x35, 0xa0, 0x8a, 0x6c,
0x96, 0x5e, 0xc2, 0x76, 0x09, 0x17, 0xf6, 0x79, 0xb6, 0x0b, 0x5e, 0xc2, 0x76, 0xb9, 0x0c, 0x56, 0x34, 0x6a, 0x64, 0x43, 0x7a, 0xab, 0xea, 0x5e, 0x91, 0x14, 0x02, 0x0b, 0x43, 0xb7, 0xa6, 0xb8,
0xef, 0x71, 0xee, 0x71, 0xdc, 0x15, 0x95, 0x38, 0x7d, 0xd7, 0x81, 0x55, 0xb9, 0xa1, 0xeb, 0x3a, 0x0c, 0x20, 0x82, 0xb3, 0x02, 0x28, 0x25, 0x4b, 0x0a, 0x6b, 0x23, 0xfa, 0xa7, 0x84, 0x56, 0x8a,
0xf6, 0x9a, 0xb5, 0xfb, 0x3b, 0x75, 0x07, 0x01, 0xaf, 0xc3, 0x32, 0xed, 0xc9, 0x3a, 0x2b, 0x24, 0x83, 0x09, 0xd5, 0xda, 0xb4, 0x45, 0x21, 0x35, 0x15, 0x9b, 0x56, 0xb1, 0x5d, 0x4b, 0xaf, 0x60,
0x53, 0x58, 0x16, 0x88, 0xe3, 0x50, 0xa9, 0xed, 0x49, 0x10, 0xca, 0x45, 0x31, 0x21, 0x95, 0x58, 0xbb, 0x84, 0x0b, 0xfb, 0x22, 0xdb, 0x05, 0xaf, 0x60, 0xbb, 0x5c, 0x06, 0xab, 0xf7, 0x38, 0xf7,
0xc2, 0xf8, 0x98, 0x96, 0xc4, 0xf1, 0x74, 0xd9, 0xfd, 0x3b, 0x07, 0xd6, 0x8c, 0x0e, 0x4b, 0x29, 0x38, 0xee, 0x8a, 0x4a, 0x9c, 0xbe, 0xeb, 0xc0, 0xaa, 0xdc, 0xd0, 0x75, 0x1d, 0x7b, 0xc3, 0xda,
0xbc, 0x01, 0xea, 0xe4, 0x5c, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b, 0xfd, 0x9d, 0xba, 0xf3, 0x88, 0x37, 0x61, 0x99, 0xf6, 0x64, 0x9d, 0x15, 0x92, 0x29, 0x2c, 0x0b,
0x98, 0x16, 0xd3, 0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2, 0xc4, 0x71, 0xa8, 0x0c, 0xfb, 0x24, 0x08, 0xe5, 0xa2, 0x98, 0x90, 0x4a, 0x2c, 0x61, 0x7c, 0x4c,
0xf9, 0x13, 0x4d, 0x32, 0x47, 0x24, 0x16, 0x46, 0x07, 0xa3, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0x17, 0x4b, 0xe2, 0x78, 0xba, 0xec, 0xfe, 0xb5, 0x03, 0x6b, 0x46, 0x87, 0xa5, 0x14, 0xde, 0x00, 0x75,
0xc8, 0x06, 0xdd, 0x7f, 0x74, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0xf7, 0x14, 0x17, 0x84, 0x59, 0x40, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b, 0x98, 0x16, 0xd3,
0x17, 0x2c, 0x34, 0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x8b, 0x2f, 0xe9, 0xc8, 0xea, 0x03, 0xf1, 0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2, 0xf9, 0x53, 0x4d,
0x53, 0xd6, 0x62, 0xae, 0x6e, 0x2d, 0x9e, 0x33, 0xd3, 0x75, 0xc9, 0x92, 0xf9, 0xda, 0x64, 0xc9, 0x32, 0x47, 0x24, 0x16, 0x46, 0x67, 0xc1, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0xfd, 0xc9, 0x06, 0xdd,
0xad, 0x45, 0x98, 0xcf, 0x86, 0x71, 0xc2, 0xdd, 0x4d, 0xd8, 0xb0, 0x07, 0x27, 0x4d, 0xd0, 0xf7, 0x7f, 0x70, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0x57, 0x33, 0x17, 0x84, 0x17, 0x2c, 0x34,
0x1c, 0xe8, 0xdd, 0x13, 0xa9, 0xc3, 0x20, 0x1a, 0xef, 0x06, 0x59, 0x1e, 0xa7, 0xfa, 0x62, 0xf6, 0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x0b, 0xaf, 0xe8, 0xc8, 0xea, 0x3b, 0x00, 0xa7, 0xac, 0xc5,
0x25, 0x80, 0x2c, 0xf7, 0xd3, 0x5c, 0x5c, 0x58, 0x92, 0x69, 0x8e, 0x02, 0xc1, 0x3e, 0xf2, 0x68, 0x5c, 0xdd, 0x5a, 0xbc, 0x60, 0xa6, 0xeb, 0x92, 0x25, 0xf3, 0xb5, 0xc9, 0x92, 0x5b, 0x8b, 0x30,
0x24, 0x6a, 0xc5, 0xda, 0xe8, 0x32, 0x2e, 0x0c, 0x1d, 0xd6, 0x0f, 0xe2, 0xc3, 0xc3, 0x8c, 0x6b, 0x9f, 0x0d, 0xe3, 0x84, 0xbb, 0x9b, 0xb0, 0x61, 0x0f, 0x4e, 0x9a, 0xa0, 0xef, 0x39, 0xd0, 0xbb,
0xb7, 0xd5, 0xc4, 0x30, 0xf2, 0x45, 0x8d, 0xc7, 0x58, 0x8f, 0x1f, 0x93, 0xa9, 0x15, 0xfe, 0x60, 0x27, 0x52, 0x87, 0x41, 0x34, 0xde, 0x0d, 0xb2, 0x3c, 0x4e, 0xf5, 0x5d, 0xf4, 0x4b, 0x00, 0x59,
0x09, 0x75, 0xff, 0xd6, 0x81, 0x95, 0xa2, 0x93, 0x77, 0x11, 0xb4, 0xad, 0x83, 0xe8, 0x9a, 0x61, 0xee, 0xa7, 0xb9, 0xb8, 0xa3, 0x25, 0xd3, 0x1c, 0x05, 0x82, 0x7d, 0xe4, 0xd1, 0x48, 0xd4, 0x8a,
0x1d, 0x54, 0x02, 0x26, 0x18, 0x0d, 0x82, 0x48, 0xf6, 0xcd, 0x40, 0x48, 0x63, 0x65, 0x29, 0x9e, 0xb5, 0xd1, 0x65, 0x5c, 0x18, 0xba, 0x9f, 0x30, 0x88, 0x0f, 0x0f, 0x33, 0xae, 0xdd, 0x56, 0x13,
0xaa, 0xcb, 0x61, 0x26, 0x24, 0x4e, 0x7e, 0x73, 0xfc, 0x5a, 0xdc, 0x0c, 0x93, 0x25, 0xba, 0x6f, 0xc3, 0xc8, 0x17, 0x35, 0x1e, 0x63, 0x3d, 0x7e, 0x4c, 0xa6, 0x56, 0xf8, 0x83, 0x25, 0xd4, 0xfd,
0x36, 0xc9, 0xe9, 0xab, 0x05, 0xe1, 0x10, 0xcb, 0xa2, 0xda, 0x9f, 0x16, 0x09, 0xc5, 0x9f, 0xee, 0x2b, 0x07, 0x56, 0x8a, 0x4e, 0xde, 0x45, 0xd0, 0xb6, 0x0e, 0xa2, 0x6b, 0x86, 0x75, 0x50, 0x09,
0x1f, 0x3b, 0x70, 0xbe, 0x66, 0x72, 0xa5, 0x66, 0xdc, 0x81, 0xb5, 0x43, 0x5d, 0xa9, 0x26, 0x40, 0x98, 0x60, 0x34, 0x08, 0x22, 0xd9, 0x37, 0x03, 0x21, 0x8d, 0x95, 0xa5, 0x78, 0xaa, 0xee, 0xc3,
0xa8, 0xc7, 0xa6, 0x94, 0xa2, 0xd2, 0xa0, 0xbd, 0xea, 0x07, 0xe8, 0x1e, 0x53, 0xde, 0x48, 0x4c, 0x99, 0x90, 0x38, 0xec, 0xce, 0xf1, 0x6b, 0x71, 0x19, 0x4e, 0x96, 0xe8, 0x8a, 0xdd, 0x24, 0xa7,
0xa9, 0x75, 0x69, 0xa2, 0x5a, 0xb1, 0xf3, 0xfd, 0x06, 0x74, 0xc5, 0x51, 0x85, 0x78, 0x9a, 0xc3, 0xaf, 0x16, 0x84, 0x43, 0x2c, 0x8b, 0x6a, 0x7f, 0x5a, 0x24, 0x14, 0x7f, 0xba, 0x7f, 0xe0, 0xc0,
0x53, 0xf6, 0x01, 0x2c, 0xca, 0x87, 0x50, 0xec, 0xac, 0x6c, 0xd6, 0x7e, 0x7a, 0xd5, 0xdf, 0x2c, 0xf9, 0x9a, 0xc9, 0x95, 0x9a, 0x71, 0x07, 0xd6, 0x0e, 0x75, 0xa5, 0x9a, 0x00, 0xa1, 0x1e, 0x9b,
0xc3, 0x52, 0x76, 0xd6, 0x7f, 0xff, 0xb3, 0x7f, 0xf9, 0x93, 0xc6, 0x32, 0x6b, 0x6f, 0x1f, 0xbf, 0x52, 0x8a, 0x4a, 0x83, 0xf6, 0xaa, 0x1f, 0xa0, 0x7b, 0x4c, 0x79, 0x23, 0x31, 0xa5, 0xd6, 0x3d,
0xb5, 0x3d, 0xe6, 0x51, 0x86, 0x3c, 0x7e, 0x13, 0xa0, 0x78, 0x4b, 0xc4, 0x7a, 0xda, 0xc9, 0x28, 0x91, 0x6a, 0xc5, 0xce, 0xf7, 0x1b, 0xd0, 0x15, 0x47, 0x15, 0xe2, 0x35, 0x12, 0x4f, 0xd9, 0x07,
0xbd, 0x7d, 0xea, 0x9f, 0xaf, 0xa9, 0x91, 0x7c, 0xcf, 0x13, 0xdf, 0x75, 0xb7, 0x8b, 0x7c, 0x83, 0xb0, 0x28, 0xdf, 0x7e, 0xb1, 0xb3, 0xb2, 0x59, 0xfb, 0xb5, 0x59, 0x7f, 0xb3, 0x0c, 0x4b, 0xd9,
0x28, 0xc8, 0xc5, 0xc3, 0xa2, 0x77, 0x9d, 0x2d, 0x36, 0x82, 0x8e, 0xf9, 0xa6, 0x88, 0xa9, 0x90, 0x59, 0xff, 0xed, 0x4f, 0xff, 0xf9, 0x0f, 0x1b, 0xcb, 0xac, 0xbd, 0x7d, 0xfc, 0xf6, 0xf6, 0x98,
0xb9, 0xe6, 0xa1, 0x52, 0xff, 0x42, 0x6d, 0x9d, 0xca, 0x17, 0x50, 0x1b, 0x67, 0xdd, 0x55, 0x6c, 0x47, 0x19, 0xf2, 0xf8, 0x55, 0x80, 0xe2, 0xf9, 0x14, 0xeb, 0x69, 0x27, 0xa3, 0xf4, 0xdc, 0xab,
0x63, 0x4a, 0x14, 0xba, 0x95, 0x9d, 0x7f, 0xba, 0x00, 0x2d, 0x9d, 0x76, 0x62, 0xdf, 0x86, 0x65, 0x7f, 0xbe, 0xa6, 0x46, 0xf2, 0x3d, 0x4f, 0x7c, 0xd7, 0xdd, 0x2e, 0xf2, 0x0d, 0xa2, 0x20, 0x17,
0xeb, 0x74, 0x87, 0x29, 0xc6, 0x75, 0x87, 0x41, 0xfd, 0x8b, 0xf5, 0x95, 0xb2, 0xd9, 0x4b, 0xd4, 0x6f, 0xa9, 0xde, 0x73, 0xb6, 0xd8, 0x08, 0x3a, 0xe6, 0x33, 0x2a, 0xa6, 0x42, 0xe6, 0x9a, 0xb7,
0x6c, 0x8f, 0x6d, 0x62, 0xb3, 0xf2, 0x78, 0x64, 0x9b, 0xce, 0xb4, 0xc4, 0xfd, 0xb2, 0x27, 0xd0, 0x59, 0xfd, 0x0b, 0xb5, 0x75, 0x2a, 0x5f, 0x40, 0x6d, 0x9c, 0x75, 0x57, 0xb1, 0x8d, 0x29, 0x51,
0xb5, 0x4f, 0x64, 0xd8, 0x45, 0xdb, 0xa0, 0x94, 0x5a, 0x7b, 0xe5, 0x94, 0x5a, 0xd9, 0xdc, 0x45, 0xe8, 0x56, 0x76, 0xfe, 0xf1, 0x02, 0xb4, 0x74, 0xda, 0x89, 0x7d, 0x08, 0xcb, 0xd6, 0xe9, 0x0e,
0x6a, 0x6e, 0x93, 0x6d, 0x98, 0xcd, 0xe9, 0x74, 0x10, 0xa7, 0x1b, 0x81, 0xe6, 0x63, 0x23, 0xf6, 0x53, 0x8c, 0xeb, 0x0e, 0x83, 0xfa, 0x17, 0xeb, 0x2b, 0x65, 0xb3, 0x97, 0xa8, 0xd9, 0x1e, 0xdb,
0x8a, 0x5e, 0xea, 0xba, 0x47, 0x48, 0x7a, 0xd1, 0xaa, 0x2f, 0x91, 0xdc, 0x1e, 0x35, 0xc5, 0x18, 0xc4, 0x66, 0xe5, 0xf1, 0xc8, 0x36, 0x9d, 0x69, 0x89, 0x2b, 0x75, 0x4f, 0xa1, 0x6b, 0x9f, 0xc8,
0x4d, 0xa8, 0xf9, 0xd6, 0x88, 0x7d, 0x04, 0x2d, 0xfd, 0xc0, 0x80, 0x9d, 0x33, 0x5e, 0x75, 0x98, 0xb0, 0x8b, 0xb6, 0x41, 0x29, 0xb5, 0xf6, 0xda, 0x29, 0xb5, 0xb2, 0xb9, 0x8b, 0xd4, 0xdc, 0x26,
0xaf, 0x1e, 0xfa, 0xbd, 0x6a, 0x45, 0xdd, 0x52, 0x99, 0x9c, 0x51, 0x20, 0x1e, 0xc0, 0x59, 0xe9, 0xdb, 0x30, 0x9b, 0xd3, 0xe9, 0x20, 0x4e, 0x97, 0x20, 0xcd, 0xf7, 0x55, 0xec, 0x35, 0xbd, 0xd4,
0xa4, 0x1e, 0xf0, 0x9f, 0x64, 0x24, 0x35, 0x4f, 0xa4, 0xae, 0x3b, 0xec, 0x06, 0x2c, 0xa9, 0x77, 0x75, 0xef, 0xae, 0xf4, 0xa2, 0x55, 0x1f, 0x5f, 0xb9, 0x3d, 0x6a, 0x8a, 0x31, 0x9a, 0x50, 0xf3,
0x1b, 0x6c, 0xb3, 0xfe, 0xfd, 0x49, 0xff, 0x5c, 0x05, 0x97, 0xfa, 0x7c, 0x13, 0xa0, 0x78, 0x73, 0x79, 0x15, 0xfb, 0x16, 0xb4, 0xf4, 0x9b, 0x0a, 0x76, 0xce, 0x78, 0xc8, 0x62, 0x3e, 0xf4, 0xe8,
0xa0, 0x25, 0xbf, 0xf2, 0x12, 0x42, 0x4f, 0x62, 0xcd, 0x03, 0x85, 0x31, 0xbd, 0xb0, 0xb0, 0x9f, 0xf7, 0xaa, 0x15, 0x75, 0x4b, 0x65, 0x72, 0x46, 0x81, 0x78, 0x00, 0x67, 0xa5, 0x93, 0x7a, 0xc0,
0x34, 0xb0, 0x57, 0x0b, 0xfa, 0xda, 0xc7, 0x0e, 0xcf, 0x61, 0xe8, 0x6e, 0xd2, 0xdc, 0xad, 0x32, 0x7f, 0x9c, 0x91, 0xd4, 0xbc, 0x0a, 0xbb, 0xee, 0xb0, 0x1b, 0xb0, 0xa4, 0x9e, 0xaa, 0xb0, 0xcd,
0x52, 0xa5, 0x88, 0x9f, 0xa8, 0xbb, 0xb1, 0x77, 0xa0, 0x6d, 0xbc, 0x63, 0x60, 0x8a, 0x43, 0xf5, 0xfa, 0x27, 0x37, 0xfd, 0x73, 0x15, 0x5c, 0xea, 0xf3, 0x4d, 0x80, 0xe2, 0x99, 0x85, 0x96, 0xfc,
0x0d, 0x44, 0xbf, 0x5f, 0x57, 0x25, 0xbb, 0xfb, 0x15, 0x58, 0xb6, 0x1e, 0x24, 0x68, 0xcd, 0xa8, 0xca, 0xe3, 0x0f, 0x3d, 0x89, 0x35, 0x6f, 0x32, 0xc6, 0xf4, 0xa8, 0xc4, 0x7e, 0xc5, 0xc1, 0x5e,
0x7b, 0xee, 0xa0, 0x35, 0xa3, 0xfe, 0x0d, 0xc3, 0x37, 0xa1, 0x6d, 0x3c, 0x1f, 0x60, 0xc6, 0x9d, 0x2f, 0xe8, 0x6b, 0xdf, 0x77, 0xbc, 0x80, 0xa1, 0xbb, 0x49, 0x73, 0xb7, 0xca, 0x48, 0x95, 0x22,
0xa0, 0xd2, 0xc3, 0x01, 0xdd, 0xa3, 0xba, 0xd7, 0x06, 0x1b, 0x34, 0xde, 0xae, 0xdb, 0xc2, 0xf1, 0x7e, 0xa2, 0xae, 0x03, 0xdf, 0x81, 0xb6, 0xf1, 0x74, 0x83, 0x29, 0x0e, 0xd5, 0x67, 0x1f, 0xfd,
0xd2, 0x05, 0x51, 0x14, 0x92, 0x6f, 0x43, 0xd7, 0x7e, 0x50, 0xa0, 0xb5, 0xaa, 0xf6, 0x69, 0x82, 0x7e, 0x5d, 0x95, 0xec, 0xee, 0x57, 0x60, 0xd9, 0x7a, 0x83, 0xa1, 0x35, 0xa3, 0xee, 0x85, 0x87,
0xd6, 0xaa, 0x53, 0x5e, 0x21, 0x48, 0x81, 0xdc, 0x5a, 0xd7, 0x8d, 0x6c, 0x7f, 0x2a, 0x0f, 0x5d, 0xd6, 0x8c, 0xfa, 0x67, 0x1b, 0xdf, 0x84, 0xb6, 0xf1, 0x62, 0x82, 0x19, 0xd7, 0xa0, 0x4a, 0x6f,
0x9e, 0xb1, 0xaf, 0xa3, 0xe9, 0x90, 0x37, 0x76, 0x59, 0xf1, 0x8c, 0xc2, 0xbe, 0xd7, 0xab, 0xa5, 0x25, 0x74, 0x8f, 0xea, 0x1e, 0x58, 0x6c, 0xd0, 0x78, 0xbb, 0x6e, 0x0b, 0xc7, 0x4b, 0x77, 0x62,
0xbd, 0x72, 0xb9, 0xd7, 0x5d, 0x23, 0xe6, 0x6d, 0x56, 0x8c, 0x40, 0x58, 0x68, 0xba, 0xb9, 0x6b, 0x51, 0x48, 0x3e, 0x84, 0xae, 0xfd, 0x86, 0x42, 0x6b, 0x55, 0xed, 0x6b, 0x0c, 0xad, 0x55, 0xa7,
0x58, 0x68, 0xf3, 0x72, 0xaf, 0x61, 0xa1, 0xad, 0x0b, 0xbe, 0x65, 0x0b, 0x9d, 0x07, 0xc8, 0x23, 0x3c, 0xbc, 0x90, 0x02, 0xb9, 0xb5, 0xae, 0x1b, 0xd9, 0xfe, 0x44, 0x1e, 0xba, 0x3c, 0x67, 0x5f,
0x82, 0x95, 0xd2, 0x3d, 0x00, 0xad, 0x2c, 0xf5, 0xb7, 0x88, 0xfa, 0x97, 0x9e, 0x7f, 0x7d, 0xc0, 0x47, 0xd3, 0x21, 0x2f, 0x29, 0xb3, 0xe2, 0xe5, 0x88, 0x7d, 0x95, 0x59, 0x4b, 0x7b, 0xe5, 0x3e,
0x36, 0x33, 0xca, 0xbc, 0x6c, 0xab, 0x4b, 0x5f, 0xbf, 0x05, 0x1d, 0xf3, 0x22, 0xb8, 0xb6, 0xd9, 0xb3, 0xbb, 0x46, 0xcc, 0xdb, 0xac, 0x18, 0x81, 0xb0, 0xd0, 0x74, 0x59, 0xd9, 0xb0, 0xd0, 0xe6,
0x35, 0xd7, 0xd7, 0xb5, 0xcd, 0xae, 0xbb, 0x39, 0xae, 0x16, 0x97, 0x75, 0xcc, 0x66, 0xd8, 0x37, 0x7d, 0x66, 0xc3, 0x42, 0x5b, 0x77, 0x9a, 0xcb, 0x16, 0x3a, 0x0f, 0x90, 0x47, 0x04, 0x2b, 0xa5,
0x61, 0xc5, 0xb8, 0x71, 0xb2, 0x3f, 0x8b, 0x86, 0x5a, 0x78, 0xaa, 0x77, 0x04, 0xfb, 0x75, 0xfe, 0x7b, 0x00, 0x5a, 0x59, 0xea, 0x2f, 0x4e, 0xf5, 0x2f, 0xbd, 0xf8, 0xfa, 0x80, 0x6d, 0x66, 0x94,
0x99, 0x7b, 0x8e, 0x18, 0xaf, 0xb9, 0x16, 0x63, 0x14, 0x9c, 0xdb, 0xd0, 0x36, 0x6f, 0xb3, 0x3c, 0x79, 0xd9, 0x56, 0xf7, 0xdc, 0x7e, 0x0d, 0x3a, 0xe6, 0xdd, 0x77, 0x6d, 0xb3, 0x6b, 0x6e, 0xec,
0x87, 0xef, 0x39, 0xa3, 0xca, 0xbc, 0x2e, 0x77, 0xdd, 0x61, 0x7f, 0xe6, 0x40, 0xc7, 0xbc, 0x7d, 0x6b, 0x9b, 0x5d, 0x77, 0x59, 0x5e, 0x2d, 0x2e, 0xeb, 0x98, 0xcd, 0xb0, 0x6f, 0xc2, 0x8a, 0x71,
0xca, 0xac, 0x3c, 0x6f, 0x89, 0x4f, 0xcf, 0xac, 0x33, 0x19, 0xb9, 0x1e, 0x75, 0xf2, 0xc1, 0xd6, 0xf1, 0x65, 0x7f, 0x16, 0x0d, 0xb5, 0xf0, 0x54, 0xaf, 0x45, 0xf6, 0xeb, 0xfc, 0x33, 0xf7, 0x1c,
0x57, 0xac, 0x49, 0xfe, 0xd4, 0xf2, 0xf3, 0xaf, 0x95, 0xdf, 0xf8, 0x3d, 0x2b, 0x13, 0x98, 0xf7, 0x31, 0x5e, 0x73, 0x2d, 0xc6, 0x28, 0x38, 0xb7, 0xa1, 0x6d, 0x5e, 0xaa, 0x79, 0x01, 0xdf, 0x73,
0x28, 0x9f, 0x5d, 0x77, 0xd8, 0xbb, 0xe2, 0x1d, 0xa8, 0x8a, 0xeb, 0x99, 0x61, 0xdc, 0xca, 0x53, 0x46, 0x95, 0x79, 0x43, 0xf0, 0xba, 0xc3, 0xfe, 0xd8, 0x81, 0x8e, 0x75, 0x45, 0xc5, 0xca, 0xf3,
0x66, 0x3e, 0x99, 0xbc, 0xea, 0x5c, 0x77, 0xd8, 0xb7, 0xc4, 0x53, 0x3e, 0xf9, 0x2d, 0xcd, 0xfc, 0x96, 0xf8, 0xf4, 0xcc, 0x3a, 0x93, 0x91, 0xeb, 0x51, 0x27, 0x1f, 0x6c, 0x7d, 0xc5, 0x9a, 0xe4,
0xcb, 0x7e, 0xef, 0xbe, 0x4e, 0xa3, 0xb9, 0xe4, 0x9e, 0xb7, 0x46, 0x53, 0xb6, 0xee, 0x7b, 0x00, 0x4f, 0x2c, 0x3f, 0xff, 0x5a, 0xf9, 0x59, 0xe3, 0xf3, 0x32, 0x81, 0x79, 0x75, 0xf4, 0xf9, 0x75,
0x45, 0x92, 0x86, 0x95, 0x32, 0x16, 0xda, 0xee, 0x55, 0xf3, 0x38, 0xf6, 0x8a, 0xaa, 0xc4, 0x06, 0x87, 0xbd, 0x27, 0x9e, 0xbe, 0xaa, 0xb8, 0x9e, 0x19, 0xc6, 0xad, 0x3c, 0x65, 0xe6, 0x2b, 0xd1,
0x72, 0xfc, 0x48, 0x08, 0xa3, 0xa4, 0xcf, 0xf4, 0x92, 0x56, 0x93, 0x2d, 0xfd, 0x7e, 0x5d, 0x55, 0xab, 0xce, 0x75, 0x87, 0x7d, 0x5b, 0xbc, 0x5e, 0x94, 0xdf, 0xd2, 0xcc, 0xbf, 0xea, 0xf7, 0xee,
0x9d, 0x28, 0x2a, 0xfe, 0xec, 0x31, 0x2c, 0x3f, 0x88, 0xe3, 0x27, 0xd3, 0x44, 0xa7, 0x11, 0xed, 0x9b, 0x34, 0x9a, 0x4b, 0xee, 0x79, 0x6b, 0x34, 0x65, 0xeb, 0xbe, 0x07, 0x50, 0x24, 0x69, 0x58,
0x9c, 0xc1, 0xae, 0x9f, 0x1d, 0xf5, 0x4b, 0xa3, 0x70, 0x2f, 0x13, 0xab, 0x3e, 0xeb, 0x19, 0xac, 0x29, 0x63, 0xa1, 0xed, 0x5e, 0x35, 0x8f, 0x63, 0xaf, 0xa8, 0x4a, 0x6c, 0x20, 0xc7, 0x6f, 0x09,
0xb6, 0x3f, 0x2d, 0x52, 0x44, 0xcf, 0x98, 0x0f, 0x6b, 0x7a, 0x8f, 0xd3, 0x1d, 0xef, 0xdb, 0x6c, 0x61, 0x94, 0xf4, 0x99, 0x5e, 0xd2, 0x6a, 0xb2, 0xa5, 0xdf, 0xaf, 0xab, 0xaa, 0x13, 0x45, 0xc5,
0xcc, 0x4c, 0x4d, 0xa5, 0x09, 0xcb, 0xeb, 0x50, 0xbd, 0xdd, 0xce, 0x14, 0xcf, 0xeb, 0x0e, 0xdb, 0x9f, 0x3d, 0x86, 0xe5, 0x07, 0x71, 0xfc, 0x74, 0x9a, 0xe8, 0x34, 0xa2, 0x9d, 0x33, 0xd8, 0xf5,
0x83, 0xce, 0x1d, 0x3e, 0x8c, 0x47, 0x5c, 0x46, 0xf9, 0xeb, 0x45, 0xc7, 0x75, 0x7a, 0xa0, 0xbf, 0xb3, 0xa3, 0x7e, 0x69, 0x14, 0xee, 0x65, 0x62, 0xd5, 0x67, 0x3d, 0x83, 0xd5, 0xf6, 0x27, 0x45,
0x6c, 0x81, 0xb6, 0xd6, 0x27, 0xfe, 0x2c, 0xe5, 0x1f, 0x6f, 0x7f, 0x2a, 0xf3, 0x07, 0xcf, 0x94, 0x8a, 0xe8, 0x39, 0xf3, 0x61, 0x4d, 0xef, 0x71, 0xba, 0xe3, 0x7d, 0x9b, 0x8d, 0x99, 0xa9, 0xa9,
0xd6, 0xab, 0x9c, 0x87, 0xa5, 0xf5, 0xa5, 0x24, 0x89, 0xa5, 0xf5, 0x95, 0x24, 0x89, 0x35, 0xd5, 0x34, 0x61, 0x79, 0x1d, 0xaa, 0xb7, 0xdb, 0x99, 0xe2, 0x79, 0xdd, 0x61, 0x7b, 0xd0, 0xb9, 0xc3,
0x2a, 0xe7, 0xc2, 0x42, 0x58, 0xab, 0xe4, 0x55, 0xf4, 0x4e, 0x79, 0x5a, 0x36, 0xa6, 0x7f, 0xf9, 0x87, 0xf1, 0x88, 0xcb, 0x28, 0x7f, 0xbd, 0xe8, 0xb8, 0x4e, 0x0f, 0xf4, 0x97, 0x2d, 0xd0, 0xd6,
0x74, 0x02, 0xbb, 0xb5, 0x2d, 0xbb, 0xb5, 0x7d, 0x58, 0xbe, 0xc3, 0xc5, 0x64, 0x89, 0xb3, 0xca, 0xfa, 0xc4, 0x9f, 0xa5, 0xfc, 0xa3, 0xed, 0x4f, 0x64, 0xfe, 0xe0, 0xb9, 0xd2, 0x7a, 0x95, 0xf3,
0xbe, 0x6d, 0x46, 0xcc, 0x73, 0xcd, 0xb2, 0x89, 0xa1, 0x3a, 0xdb, 0xac, 0xd3, 0x41, 0x21, 0xfb, 0xb0, 0xb4, 0xbe, 0x94, 0x24, 0xb1, 0xb4, 0xbe, 0x92, 0x24, 0xb1, 0xa6, 0x5a, 0xe5, 0x5c, 0x58,
0x08, 0xda, 0xf7, 0x79, 0xae, 0x0e, 0x27, 0xb5, 0xbf, 0x51, 0x3a, 0xad, 0xec, 0xd7, 0x9c, 0x6d, 0x08, 0x6b, 0x95, 0xbc, 0x8a, 0xde, 0x29, 0x4f, 0xcb, 0xc6, 0xf4, 0x2f, 0x9f, 0x4e, 0x60, 0xb7,
0xda, 0x32, 0x43, 0xdc, 0xb6, 0xf9, 0x68, 0xcc, 0x85, 0xb2, 0x0f, 0x82, 0xd1, 0x33, 0xf6, 0xeb, 0xb6, 0x65, 0xb7, 0xb6, 0x0f, 0xcb, 0x77, 0xb8, 0x98, 0x2c, 0x71, 0x56, 0xd9, 0xb7, 0xcd, 0x88,
0xc4, 0x5c, 0xdf, 0x67, 0xd8, 0x34, 0xce, 0xb4, 0x4c, 0xe6, 0x2b, 0x25, 0xbc, 0x8e, 0x73, 0x14, 0x79, 0xae, 0x59, 0x36, 0x31, 0x54, 0x67, 0x9b, 0x75, 0x3a, 0x28, 0x64, 0xdf, 0x82, 0xf6, 0x7d,
0x8f, 0xb8, 0xb1, 0xc1, 0x45, 0xd0, 0x36, 0x2e, 0xdb, 0x68, 0x05, 0xaa, 0x5e, 0xf0, 0xd1, 0x0a, 0x9e, 0xab, 0xc3, 0x49, 0xed, 0x6f, 0x94, 0x4e, 0x2b, 0xfb, 0x35, 0x67, 0x9b, 0xb6, 0xcc, 0x10,
0x54, 0x73, 0x37, 0xc7, 0xbd, 0x4a, 0xed, 0xb8, 0xec, 0x72, 0xd1, 0x8e, 0xb8, 0x8f, 0x53, 0xb4, 0xb7, 0x6d, 0x3e, 0x1a, 0x73, 0xa1, 0xec, 0x83, 0x60, 0xf4, 0x9c, 0xfd, 0x32, 0x31, 0xd7, 0xf7,
0xb4, 0xfd, 0xa9, 0x3f, 0xc9, 0x9f, 0xb1, 0x0f, 0xe9, 0x29, 0x8b, 0x79, 0x00, 0x5b, 0xf8, 0x3b, 0x19, 0x36, 0x8d, 0x33, 0x2d, 0x93, 0xf9, 0x4a, 0x09, 0xaf, 0xe3, 0x1c, 0xc5, 0x23, 0x6e, 0x6c,
0xe5, 0xb3, 0x5a, 0x3d, 0x59, 0x46, 0x95, 0xed, 0x03, 0x89, 0xa6, 0x68, 0x1f, 0xfc, 0x22, 0xc0, 0x70, 0x11, 0xb4, 0x8d, 0xcb, 0x36, 0x5a, 0x81, 0xaa, 0x17, 0x7c, 0xb4, 0x02, 0xd5, 0xdc, 0xcd,
0x7e, 0x1e, 0x27, 0x77, 0x7c, 0x3e, 0x89, 0xa3, 0xc2, 0x72, 0x15, 0x87, 0x8c, 0x85, 0xe5, 0x32, 0x71, 0xaf, 0x52, 0x3b, 0x2e, 0xbb, 0x5c, 0xb4, 0x23, 0xee, 0xe3, 0x14, 0x2d, 0x6d, 0x7f, 0xe2,
0x4e, 0x1a, 0xd9, 0x87, 0x86, 0xc7, 0x69, 0x9d, 0x5f, 0x2b, 0xe1, 0x3a, 0xf5, 0x1c, 0x52, 0x4f, 0x4f, 0xf2, 0xe7, 0xec, 0x09, 0xbd, 0xde, 0x31, 0x0f, 0x60, 0x0b, 0x7f, 0xa7, 0x7c, 0x56, 0xab,
0x48, 0xcd, 0x59, 0xe4, 0x75, 0x07, 0xfd, 0xc7, 0x22, 0x8b, 0xa7, 0xfd, 0xc7, 0x4a, 0x82, 0x50, 0x27, 0xcb, 0xa8, 0xb2, 0x7d, 0x20, 0xd1, 0x14, 0xed, 0x83, 0x5f, 0x00, 0xd8, 0xcf, 0xe3, 0xe4,
0x9b, 0xbd, 0x9a, 0x94, 0xdf, 0x1e, 0xb4, 0x8a, 0xb4, 0x90, 0xda, 0x92, 0xca, 0x49, 0x24, 0xbd, 0x8e, 0xcf, 0x27, 0x71, 0x54, 0x58, 0xae, 0xe2, 0x90, 0xb1, 0xb0, 0x5c, 0xc6, 0x49, 0x23, 0x7b,
0xc7, 0x54, 0x92, 0x35, 0xee, 0x2a, 0x4d, 0x15, 0xb0, 0x25, 0x9c, 0x2a, 0xca, 0xc0, 0x04, 0xb0, 0x62, 0x78, 0x9c, 0xd6, 0xf9, 0xb5, 0x12, 0xae, 0x53, 0xcf, 0x21, 0xf5, 0x84, 0xd4, 0x9c, 0x45,
0x2e, 0x3a, 0xa8, 0x37, 0x4c, 0x3a, 0x36, 0x53, 0x23, 0xa9, 0x49, 0x98, 0x68, 0x6d, 0xae, 0xcd, 0x5e, 0x77, 0xd0, 0x7f, 0x2c, 0xb2, 0x78, 0xda, 0x7f, 0xac, 0x24, 0x08, 0xb5, 0xd9, 0xab, 0x49,
0x37, 0x58, 0xb1, 0x1d, 0x4a, 0xab, 0x38, 0xb2, 0x43, 0xd3, 0x3c, 0x81, 0xb5, 0x4a, 0xb0, 0xac, 0xf9, 0xed, 0x41, 0xab, 0x48, 0x0b, 0xa9, 0x2d, 0xa9, 0x9c, 0x44, 0xd2, 0x7b, 0x4c, 0x25, 0x59,
0x55, 0xfa, 0xb4, 0x1c, 0x85, 0x56, 0xe9, 0x53, 0xe3, 0x6c, 0xf7, 0x2c, 0x35, 0xb9, 0xe2, 0x02, 0xe3, 0xae, 0xd2, 0x54, 0x01, 0x5b, 0xc2, 0xa9, 0xa2, 0x0c, 0x4c, 0x00, 0xeb, 0xa2, 0x83, 0x7a,
0x36, 0x99, 0x9d, 0x04, 0xf9, 0xf0, 0xe8, 0x5d, 0x67, 0xeb, 0x60, 0x81, 0xfe, 0x69, 0xe4, 0xf3, 0xc3, 0xa4, 0x63, 0x33, 0x35, 0x92, 0x9a, 0x84, 0x89, 0xd6, 0xe6, 0xda, 0x7c, 0x83, 0x15, 0xdb,
0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xc7, 0xfe, 0x7f, 0x9b, 0x44, 0x00, 0x00, 0xa1, 0xb4, 0x8a, 0x23, 0x3b, 0x34, 0xcd, 0x13, 0x58, 0xab, 0x04, 0xcb, 0x5a, 0xa5, 0x4f, 0xcb,
0x51, 0x68, 0x95, 0x3e, 0x35, 0xce, 0x76, 0xcf, 0x52, 0x93, 0x2b, 0x2e, 0x60, 0x93, 0xd9, 0x49,
0x90, 0x0f, 0x8f, 0xde, 0x73, 0xb6, 0x0e, 0x16, 0xe8, 0xcf, 0x55, 0x3e, 0xf7, 0x3f, 0x01, 0x00,
0x00, 0xff, 0xff, 0x8f, 0xec, 0x5b, 0xef, 0x8e, 0x45, 0x00, 0x00,
} }

View File

@ -1063,6 +1063,14 @@ message PendingChannelsResponse {
int64 fee_per_kw = 6 [ json_name = "fee_per_kw" ]; int64 fee_per_kw = 6 [ json_name = "fee_per_kw" ];
} }
message WaitingCloseChannel {
/// The pending channel waiting for closing tx to confirm
PendingChannel channel = 1;
/// The balance in satoshis encumbered in this channel
int64 limbo_balance = 2 [ json_name = "limbo_balance" ];
}
message ClosedChannel { message ClosedChannel {
/// The pending channel to be closed /// The pending channel to be closed
PendingChannel channel = 1; PendingChannel channel = 1;
@ -1108,6 +1116,9 @@ message PendingChannelsResponse {
/// Channels pending force closing /// Channels pending force closing
repeated ForceClosedChannel pending_force_closing_channels = 4 [ json_name = "pending_force_closing_channels" ]; repeated ForceClosedChannel pending_force_closing_channels = 4 [ json_name = "pending_force_closing_channels" ];
/// Channels waiting for closing tx to confirm
repeated WaitingCloseChannel waiting_close_channels = 5 [ json_name = "waiting_close_channels" ];
} }
message WalletBalanceRequest { message WalletBalanceRequest {

View File

@ -893,6 +893,20 @@
} }
} }
}, },
"PendingChannelsResponseWaitingCloseChannel": {
"type": "object",
"properties": {
"channel": {
"$ref": "#/definitions/PendingChannelsResponsePendingChannel",
"title": "/ The pending channel waiting for closing tx to confirm"
},
"limbo_balance": {
"type": "string",
"format": "int64",
"title": "/ The balance in satoshis encumbered in this channel"
}
}
},
"lnrpcAddInvoiceResponse": { "lnrpcAddInvoiceResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2035,6 +2049,13 @@
"$ref": "#/definitions/PendingChannelsResponseForceClosedChannel" "$ref": "#/definitions/PendingChannelsResponseForceClosedChannel"
}, },
"title": "/ Channels pending force closing" "title": "/ Channels pending force closing"
},
"waiting_close_channels": {
"type": "array",
"items": {
"$ref": "#/definitions/PendingChannelsResponseWaitingCloseChannel"
},
"title": "/ Channels waiting for closing tx to confirm"
} }
} }
}, },

View File

@ -770,6 +770,7 @@ func (n *NetworkHarness) CloseChannel(ctx context.Context,
return func() bool { return func() bool {
channel, err := filterChannel(node, chanPoint) channel, err := filterChannel(node, chanPoint)
if err != nil { if err != nil {
return false
} }
return channel.Active return channel.Active

View File

@ -4650,7 +4650,7 @@ type CommitOutputResolution struct {
// had any outgoing HTLC's within the commitment transaction, then an // had any outgoing HTLC's within the commitment transaction, then an
// OutgoingHtlcResolution for each output will included. // OutgoingHtlcResolution for each output will included.
type UnilateralCloseSummary struct { type UnilateralCloseSummary struct {
// SpendDetail is a struct that describes how and when the commitment // SpendDetail is a struct that describes how and when the funding
// output was spent. // output was spent.
*chainntnfs.SpendDetail *chainntnfs.SpendDetail
@ -4754,7 +4754,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer,
RemotePub: chanState.IdentityPub, RemotePub: chanState.IdentityPub,
Capacity: chanState.Capacity, Capacity: chanState.Capacity,
SettledBalance: localBalance, SettledBalance: localBalance,
CloseType: channeldb.ForceClose, CloseType: channeldb.RemoteForceClose,
IsPending: true, IsPending: true,
} }

View File

@ -329,9 +329,9 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
// Skip adding any permanently irreconcilable channels to the // Skip adding any permanently irreconcilable channels to the
// htlcswitch. // htlcswitch.
if dbChan.IsBorked { if dbChan.ChanStatus != channeldb.Default {
peerLog.Warnf("ChannelPoint(%v) is borked, won't "+ peerLog.Warnf("ChannelPoint(%v) has status %v, won't "+
"start.", chanPoint) "start.", chanPoint, dbChan.ChanStatus)
lnChan.Stop() lnChan.Stop()
continue continue
} }

View File

@ -1343,18 +1343,24 @@ func (r *rpcServer) WalletBalance(ctx context.Context,
func (r *rpcServer) ChannelBalance(ctx context.Context, func (r *rpcServer) ChannelBalance(ctx context.Context,
in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) { in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) {
channels, err := r.server.chanDB.FetchAllChannels() openChannels, err := r.server.chanDB.FetchAllOpenChannels()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var pendingOpenBalance, balance btcutil.Amount var balance btcutil.Amount
for _, channel := range channels { for _, channel := range openChannels {
if channel.IsPending { balance += channel.LocalCommitment.LocalBalance.ToSatoshis()
pendingOpenBalance += channel.LocalCommitment.LocalBalance.ToSatoshis() }
} else {
balance += channel.LocalCommitment.LocalBalance.ToSatoshis() pendingChannels, err := r.server.chanDB.FetchPendingChannels()
} if err != nil {
return nil, err
}
var pendingOpenBalance btcutil.Amount
for _, channel := range pendingChannels {
pendingOpenBalance += channel.LocalCommitment.LocalBalance.ToSatoshis()
} }
return &lnrpc.ChannelBalanceResponse{ return &lnrpc.ChannelBalanceResponse{
@ -1457,7 +1463,8 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
// If the channel was force closed, then we'll need to query // If the channel was force closed, then we'll need to query
// the utxoNursery for additional information. // the utxoNursery for additional information.
case channeldb.ForceClose: // TODO(halseth): distinguish remote and local case?
case channeldb.LocalForceClose, channeldb.RemoteForceClose:
forceClose := &lnrpc.PendingChannelsResponse_ForceClosedChannel{ forceClose := &lnrpc.PendingChannelsResponse_ForceClosedChannel{
Channel: channel, Channel: channel,
ClosingTxid: closeTXID, ClosingTxid: closeTXID,
@ -1522,6 +1529,39 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
} }
} }
// We'll also fetch all channels that are open, but have had their
// commitment broadcasted, meaning they are waiting for the closing
// transaction to confirm.
waitingCloseChans, err := r.server.chanDB.FetchWaitingCloseChannels()
if err != nil {
rpcsLog.Errorf("unable to fetch channels waiting close: %v",
err)
return nil, err
}
for _, waitingClose := range waitingCloseChans {
pub := waitingClose.IdentityPub.SerializeCompressed()
chanPoint := waitingClose.FundingOutpoint
channel := &lnrpc.PendingChannelsResponse_PendingChannel{
RemoteNodePub: hex.EncodeToString(pub),
ChannelPoint: chanPoint.String(),
Capacity: int64(waitingClose.Capacity),
LocalBalance: int64(waitingClose.LocalCommitment.LocalBalance.ToSatoshis()),
}
// A close tx has been broadcasted, all our balance will be in
// limbo until it confirms.
resp.WaitingCloseChannels = append(
resp.WaitingCloseChannels,
&lnrpc.PendingChannelsResponse_WaitingCloseChannel{
Channel: channel,
LimboBalance: channel.LocalBalance,
},
)
resp.TotalLimboBalance += channel.LocalBalance
}
return resp, nil return resp, nil
} }
@ -1544,7 +1584,7 @@ func (r *rpcServer) ListChannels(ctx context.Context,
graph := r.server.chanDB.ChannelGraph() graph := r.server.chanDB.ChannelGraph()
dbChannels, err := r.server.chanDB.FetchAllChannels() dbChannels, err := r.server.chanDB.FetchAllOpenChannels()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1553,10 +1593,6 @@ func (r *rpcServer) ListChannels(ctx context.Context,
len(dbChannels)) len(dbChannels))
for _, dbChannel := range dbChannels { for _, dbChannel := range dbChannels {
if dbChannel.IsPending {
continue
}
nodePub := dbChannel.IdentityPub nodePub := dbChannel.IdentityPub
nodeID := hex.EncodeToString(nodePub.SerializeCompressed()) nodeID := hex.EncodeToString(nodePub.SerializeCompressed())
chanPoint := dbChannel.FundingOutpoint chanPoint := dbChannel.FundingOutpoint