diff --git a/breacharbiter.go b/breacharbiter.go index 7663a9bd..33e1e3e7 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -696,6 +696,22 @@ func (b *breachArbiter) breachObserver( brarLog.Debugf("Breach observer for ChannelPoint(%v) started ", 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 { // A read from this channel indicates that the contract has been // 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. case <-chainEvents.CooperativeClosure: - // 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) + gracefullyExit() // The channel has been closed by a normal means: force closing with // the latest commitment transaction. - case <-chainEvents.UnilateralClosure: - // 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) + case <-chainEvents.LocalUnilateralClosure: + gracefullyExit() + case <-chainEvents.RemoteUnilateralClosure: + gracefullyExit() // A read from this channel indicates that a channel breach has been // detected! So we notify the main coordination goroutine with the diff --git a/breacharbiter_test.go b/breacharbiter_test.go index 5ac4efa6..a4e7ba0f 100644 --- a/breacharbiter_test.go +++ b/breacharbiter_test.go @@ -951,11 +951,12 @@ func TestBreachHandoffSuccess(t *testing.T) { // Instantiate a breach arbiter to handle the breach of alice's channel. alicePoint := alice.ChannelPoint() spendEvents := contractcourt.ChainEventSubscription{ - UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), - CooperativeClosure: make(chan struct{}, 1), - ContractBreach: make(chan *lnwallet.BreachRetribution, 1), - ProcessACK: make(chan error, 1), - ChanPoint: *alicePoint, + RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), + LocalUnilateralClosure: make(chan *contractcourt.LocalUnilateralCloseInfo, 1), + CooperativeClosure: make(chan struct{}, 1), + ContractBreach: make(chan *lnwallet.BreachRetribution, 1), + ProcessACK: make(chan error, 1), + ChanPoint: *alicePoint, Cancel: func() { }, } @@ -1039,11 +1040,12 @@ func TestBreachHandoffFail(t *testing.T) { // Instantiate a breach arbiter to handle the breach of alice's channel. alicePoint := alice.ChannelPoint() spendEvents := contractcourt.ChainEventSubscription{ - UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), - CooperativeClosure: make(chan struct{}, 1), - ContractBreach: make(chan *lnwallet.BreachRetribution, 1), - ProcessACK: make(chan error, 1), - ChanPoint: *alicePoint, + RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), + LocalUnilateralClosure: make(chan *contractcourt.LocalUnilateralCloseInfo, 1), + CooperativeClosure: make(chan struct{}, 1), + ContractBreach: make(chan *lnwallet.BreachRetribution, 1), + ProcessACK: make(chan error, 1), + ChanPoint: *alicePoint, Cancel: func() { }, } diff --git a/channeldb/channel.go b/channeldb/channel.go index a2435c24..b15943a2 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -285,6 +285,38 @@ type ChannelCommitment struct { // * 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 // with a remote node. An open channel supports several options for on-disk // serialization depending on the exact context. Full (upon channel creation) @@ -322,10 +354,9 @@ type OpenChannel struct { // negotiate fees, or close the channel. IsInitiator bool - // IsBorked 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. - IsBorked bool + // ChanStatus is the current status of this channel. If it is not in + // the state Default, it should not be used for forwarding payments. + ChanStatus ChannelStatus // FundingBroadcastHeight is the height in which the funding // transaction was broadcast. This value can be used by higher level @@ -571,6 +602,20 @@ func (c *OpenChannel) MarkBorked() error { c.Lock() 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 { chanBucket, err := updateChanBucket(tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash) @@ -583,14 +628,15 @@ func (c *OpenChannel) MarkBorked() error { return err } - channel.IsBorked = true + channel.ChanStatus = status return putOpenChannel(chanBucket, channel) }); err != nil { return err } - c.IsBorked = true + // Update the in-memory representation to keep it in sync with the DB. + c.ChanStatus = status 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 -// was closed. Three closure types are currently possible: cooperative, force, -// and breach. +// was closed. Three closure types are currently possible: none, cooperative, +// local force close, remote force close, and (remote) breach. type ClosureType uint8 const ( @@ -1487,21 +1533,25 @@ const ( // cooperatively. This means that both channel peers were online and // signed a new transaction paying out the settled balance of the // 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. - ForceClose + LocalForceClose ClosureType = 1 - // BreachClose indicates that one peer attempted to broadcast a prior - // _revoked_ channel state. - BreachClose + // RemoteForceClose indicates that the remote peer has unilaterally + // broadcast their current commitment state on-chain. + 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 // 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 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 @@ -1549,8 +1599,9 @@ type ChannelCloseSummary struct { // outstanding outgoing HTLC's at the time of channel closure. TimeLockedBalance btcutil.Amount - // CloseType details exactly _how_ the channel was closed. Three - // closure types are possible: cooperative, force, and breach. + // CloseType details exactly _how_ the channel was closed. Five closure + // types are possible: cooperative, local force, remote force, breach + // and funding canceled. CloseType ClosureType // 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, channel.ChanType, channel.ChainHash, channel.FundingOutpoint, channel.ShortChanID, channel.IsPending, channel.IsInitiator, - channel.IsBorked, channel.FundingBroadcastHeight, + channel.ChanStatus, channel.FundingBroadcastHeight, channel.NumConfsRequired, channel.ChannelFlags, channel.IdentityPub, channel.Capacity, channel.TotalMSatSent, channel.TotalMSatReceived, @@ -1912,7 +1963,7 @@ func fetchChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error { if err := readElements(r, &channel.ChanType, &channel.ChainHash, &channel.FundingOutpoint, &channel.ShortChanID, &channel.IsPending, &channel.IsInitiator, - &channel.IsBorked, &channel.FundingBroadcastHeight, + &channel.ChanStatus, &channel.FundingBroadcastHeight, &channel.NumConfsRequired, &channel.ChannelFlags, &channel.IdentityPub, &channel.Capacity, &channel.TotalMSatSent, &channel.TotalMSatReceived, diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 66e9e597..cb734b8d 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -606,7 +606,7 @@ func TestChannelStateTransition(t *testing.T) { SettledBalance: btcutil.Amount(500), TimeLockedBalance: btcutil.Amount(10000), IsPending: false, - CloseType: ForceClose, + CloseType: RemoteForceClose, } if err := updatedChannel[0].CloseChannel(closeSummary); err != nil { t.Fatalf("unable to delete updated channel: %v", err) @@ -770,7 +770,7 @@ func TestFetchClosedChannels(t *testing.T) { Capacity: state.Capacity, SettledBalance: state.LocalCommitment.LocalBalance.ToSatoshis(), TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000, - CloseType: ForceClose, + CloseType: RemoteForceClose, IsPending: true, } if err := state.CloseChannel(summary); err != nil { diff --git a/channeldb/codec.go b/channeldb/codec.go index 14665e0c..086c2533 100644 --- a/channeldb/codec.go +++ b/channeldb/codec.go @@ -148,10 +148,16 @@ func writeElement(w io.Writer, element interface{}) error { return err } + case ChannelStatus: + if err := binary.Write(w, byteOrder, e); err != nil { + return err + } + case ClosureType: if err := binary.Write(w, byteOrder, e); err != nil { return err } + case lnwire.FundingFlag: if err := binary.Write(w, byteOrder, e); err != nil { return err @@ -321,10 +327,16 @@ func readElement(r io.Reader, element interface{}) error { *e = msg + case *ChannelStatus: + if err := binary.Read(r, byteOrder, e); err != nil { + return err + } + case *ClosureType: if err := binary.Read(r, byteOrder, e); err != nil { return err } + case *lnwire.FundingFlag: if err := binary.Read(r, byteOrder, e); err != nil { return err diff --git a/channeldb/db.go b/channeldb/db.go index fd198720..8292febb 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -312,23 +312,60 @@ func (d *DB) fetchNodeChannels(chainBucket *bolt.Bucket) ([]*OpenChannel, error) } // 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) { - 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 // generating and broadcasting funding transactions, but whose funding // transactions have yet to be confirmed on the blockchain. 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 -// database. The pendingOnly parameter determines whether only pending channels -// will be returned. If no active channels exist within the network, then -// ErrNoActiveChannels is returned. -func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) { +// database. The pending parameter determines whether only pending channels +// will be returned, or only open channels will be returned. The waitingClose +// parameter determines wheter only channels waiting for a closing transaction +// 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 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, "+ "node_key=%x: %v", chainHash[:], k, err) } - // TODO(roasbeef): simplify - if pendingOnly { - for _, channel := range nodeChans { - if channel.IsPending { - channels = append(channels, channel) - } + for _, channel := range nodeChans { + if channel.IsPending != pending { + continue } - } 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 }) }) }) + if err != nil { + return nil, err + } - return channels, err + return channels, nil } // FetchClosedChannels attempts to fetch all closed channels from the database. diff --git a/contractcourt/briefcase.go b/contractcourt/briefcase.go index e9c8acc3..d0b8db97 100644 --- a/contractcourt/briefcase.go +++ b/contractcourt/briefcase.go @@ -112,13 +112,19 @@ const ( // so yet. 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 - // already been "closed". At this point, we can now examine our active - // contracts, in order to create the proper resolver for each one. + // already been "closed", meaning the commitment is confirmed on chain. + // At this point, we can now examine our active contracts, in order to + // create the proper resolver for each one. StateContractClosed ArbitratorState = 2 // 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. StateWaitingFullResolution ArbitratorState = 3 @@ -142,6 +148,9 @@ func (a ArbitratorState) String() string { case StateBroadcastCommit: return "StateBroadcastCommit" + case StateCommitmentBroadcasted: + return "StateCommitmentBroadcasted" + case StateContractClosed: return "StateContractClosed" diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index 82ab1add..95bc071c 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -231,14 +231,9 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel, return chanMachine.ForceClose() }, - CloseChannel: func(summary *channeldb.ChannelCloseSummary) error { - log.Tracef("ChannelArbitrator(%v): closing "+ - "channel", chanPoint) - - return channel.CloseChannel(summary) - }, - ChainArbitratorConfig: c.cfg, - ChainEvents: chanEvents, + MarkCommitmentBroadcasted: channel.MarkCommitmentBroadcasted, + ChainArbitratorConfig: c.cfg, + ChainEvents: chanEvents, } // The final component needed is an arbitrator log that the arbitrator diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index b3dfd929..ffd632b6 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -16,6 +16,13 @@ import ( "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 // for any on-chain events related to a channel. There are three types of // 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 wire.OutPoint - // UnilateralClosure is a channel that will be sent upon in the event that - // the remote party broadcasts their latest version of the commitment - // transaction. - UnilateralClosure chan *lnwallet.UnilateralCloseSummary + // RemoteUnilateralClosure is a channel that will be sent upon in the + // event that the remote party's commitment transaction is confirmed. + RemoteUnilateralClosure chan *lnwallet.UnilateralCloseSummary - // CooperativeClosure is a signal that will be sent upon once a cooperative - // channel closure has been detected. + // LocalUnilateralClosure is a channel that will be sent upon in the + // 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 CooperativeClosure chan struct{} @@ -180,7 +190,7 @@ func (c *chainWatcher) Start() error { } spendNtfn, err := c.notifier.RegisterSpendNtfn( - fundingOut, heightHint, true, + fundingOut, heightHint, false, ) if err != nil { return err @@ -226,10 +236,11 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs clientID, c.chanState.FundingOutpoint) sub := &ChainEventSubscription{ - ChanPoint: c.chanState.FundingOutpoint, - UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), - CooperativeClosure: make(chan struct{}, 1), - ContractBreach: make(chan *lnwallet.BreachRetribution, 1), + ChanPoint: c.chanState.FundingOutpoint, + RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), + LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1), + CooperativeClosure: make(chan struct{}, 1), + ContractBreach: make(chan *lnwallet.BreachRetribution, 1), Cancel: func() { c.Lock() delete(c.clientSubscriptions, clientID) @@ -240,6 +251,30 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs if syncDispatch { 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() @@ -307,6 +342,13 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) { &commitmentHash, ) 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 } @@ -349,7 +391,7 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) { // has a fail crash _after_ accepting the new state, // but _before_ sending their signature to us. case broadcastStateNum >= remoteStateNum: - if err := c.dispatchRemoteClose( + if err := c.dispatchRemoteForceClose( commitSpend, *remoteCommit, ); err != nil { 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 // 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 // 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 { 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? // * no harm in repeated ntfns: at least once semantics select { - case sub.UnilateralClosure <- uniClose: + case sub.RemoteUnilateralClosure <- uniClose: case <-c.quit: c.Unlock() return fmt.Errorf("exiting") diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 5cc9d9fe..cd6fc2a9 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -10,9 +10,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/wire" - "github.com/roasbeef/btcutil" ) const ( @@ -91,11 +89,9 @@ type ChannelArbitratorConfig struct { // eventually resolve all outputs on chain. ForceCloseChan func() (*lnwallet.LocalForceCloseSummary, error) - // CloseChannel is a function closure that marks a channel under watch - // as "closing". In this phase, we will no longer accept any updates to - // the channel as the commitment transaction has been broadcast, and - // possibly fully confirmed. - CloseChannel func(*channeldb.ChannelCloseSummary) error + // MarkCommitmentBroadcasted should mark the channel as the commitment + // being broadcast, and we are waiting for the commitment to confirm. + MarkCommitmentBroadcasted func() error // MarkChannelResolved is a function closure that serves to mark a // 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, c.state) - bestHash, bestHeight, err := c.cfg.ChainIO.GetBestBlock() + _, bestHeight, err := c.cfg.ChainIO.GetBestBlock() if err != nil { return err } @@ -248,7 +244,7 @@ func (c *ChannelArbitrator) Start() error { // on-chain state, and our set of active contracts. startingState := c.state nextState, _, err := c.advanceState( - uint32(bestHeight), bestHash, chainTrigger, nil, + uint32(bestHeight), chainTrigger, nil, ) if err != nil { return err @@ -280,7 +276,7 @@ func (c *ChannelArbitrator) Start() error { // TODO(roasbeef): cancel if breached c.wg.Add(1) - go c.channelAttendant(bestHeight, bestHash) + go c.channelAttendant(bestHeight) return nil } @@ -320,14 +316,18 @@ const ( // being attached. 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 - // 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. 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 @@ -337,12 +337,15 @@ func (t transitionTrigger) String() string { case chainTrigger: return "chainTrigger" - case remotePeerTrigger: - return "remotePeerTrigger" + case remoteCloseTrigger: + return "remoteCloseTrigger" case userTrigger: return "userTrigger" + case localCloseTrigger: + return "localCloseTrigger" + default: return "unknown trigger" } @@ -352,7 +355,7 @@ func (t transitionTrigger) String() string { // the appropriate state transition if necessary. The next state we transition // to is returned, Additionally, if the next transition results in a commitment // 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) { 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 // to see if while we were down, conditions have changed. case StateDefault: - log.Debugf("ChannelArbitrator(%v): new block (height=%v, "+ - "hash=%v) examining active HTLC's", - c.cfg.ChanPoint, bestHeight, bestHash) + log.Debugf("ChannelArbitrator(%v): new block (height=%v) "+ + "examining active HTLC's", c.cfg.ChanPoint, + triggerHeight) // 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 // claims on behalf of the channel contract that we're // arbitrating for. - chainActions := c.checkChainActions(uint32(bestHeight), - trigger) + chainActions := c.checkChainActions(triggerHeight, trigger) // 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 @@ -409,10 +411,16 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has case userTrigger: nextState = StateBroadcastCommit - // Otherwise, if this state advance was triggered by the remote - // peer, then we'll jump straight to the state where the - // contract has already been closed. - case remotePeerTrigger: + // Otherwise, if this state advance was triggered by a + // commitment being confirmed on chain, then we'll jump + // straight to the state where the contract has already been + // closed. + case localCloseTrigger: + log.Errorf("ChannelArbitrator(%v): unexpected local "+ + "commitment confirmed while in StateDefault", + c.cfg.ChanPoint) + fallthrough + case remoteCloseTrigger: 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 - // out 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. - 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 - } + if err := c.cfg.MarkCommitmentBroadcasted(); err != nil { + log.Errorf("ChannelArbitrator(%v): unable to "+ + "mark commitment broadcasted: %v", + c.cfg.ChanPoint, err) } - contractRes := ContractResolutions{ - CommitHash: closeTx.TxHash(), - CommitResolution: closeSummary.CommitResolution, - HtlcResolutions: *closeSummary.HtlcResolutions, + // We go to the StateCommitmentBroadcasted state, where we'll + // be waiting for the commitment to be confirmed. + nextState = StateCommitmentBroadcasted + + // 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 // outside sub-systems, so we'll process the prior set of on-chain // contract actions and launch a set of resolvers. @@ -539,12 +525,32 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has 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 // actions, wen create the structures we need to resolve all // outstanding contracts. htlcResolvers, pktsToSend, err := c.prepContractResolutions( - chainActions, contractResolutions, uint32(bestHeight), - trigger, + chainActions, contractResolutions, triggerHeight, ) if err != nil { log.Errorf("ChannelArbitrator(%v): unable to "+ @@ -601,7 +607,7 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has nextState = StateFullyResolved 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() } @@ -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 // param is a callback that allows the caller to execute an arbitrary action // after each state transition. -func (c *ChannelArbitrator) advanceState(currentHeight uint32, - bestHash *chainhash.Hash, trigger transitionTrigger, - stateCallback func(ArbitratorState) error) (ArbitratorState, *wire.MsgTx, error) { +func (c *ChannelArbitrator) advanceState(triggerHeight uint32, + trigger transitionTrigger, stateCallback func(ArbitratorState) error) ( + ArbitratorState, *wire.MsgTx, error) { var ( priorState ArbitratorState 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 // transition to is that same state that we started at. for { 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( - currentHeight, bestHash, trigger, + triggerHeight, trigger, ) if err != nil { log.Errorf("unable to advance state: %v", err) @@ -926,7 +932,6 @@ func (c *ChannelArbitrator) checkChainActions(height uint32, // are properly resolved. func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap, contractResolutions *ContractResolutions, height uint32, - trigger transitionTrigger, ) ([]ContractResolver, []ResolutionMsg, error) { // 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. // // NOTE: This MUST be run as a goroutine. -func (c *ChannelArbitrator) channelAttendant(bestHeight int32, - bestHash *chainhash.Hash) { +func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { // TODO(roasbeef): tell top chain arb we're done defer c.wg.Done() @@ -1295,7 +1299,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32, return } bestHeight = blockEpoch.Height - bestHash = blockEpoch.Hash // If we're not in the default state, then we can // 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 // advance our state forward. nextState, _, err := c.advanceState( - uint32(bestHeight), bestHash, chainTrigger, nil, + uint32(bestHeight), chainTrigger, nil, ) if err != nil { 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 - // needed. + // We've cooperatively closed the channel, so we're no longer + // needed. case <-c.cfg.ChainEvents.CooperativeClosure: log.Infof("ChannelArbitrator(%v) closing due to co-op "+ "closure", c.cfg.ChanPoint) 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. // We'll examine our state to determine if we need to act at // all. - case uniClosure := <-c.cfg.ChainEvents.UnilateralClosure: - if c.state != StateDefault { - continue - } + case uniClosure := <-c.cfg.ChainEvents.RemoteUnilateralClosure: log.Infof("ChannelArbitrator(%v): remote party has "+ "closed channel out on-chain", c.cfg.ChanPoint) @@ -1378,17 +1428,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32, CommitResolution: uniClosure.CommitResolution, 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 // cooperative closures? @@ -1399,19 +1438,21 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32, // present on their commitment. c.activeHTLCs = newHtlcSet(uniClosure.RemoteCommit.Htlcs) - // When processing a remote party initiated event, - // we'll skip the BroadcastCommit state, and transition - // directly to the ContractClosed state. As a result, - // we'll now manually log out set of resolutions. + // 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 { - err := c.log.LogContractResolutions( - contractRes, - ) - if err != nil { - return fmt.Errorf("unable to write "+ - "resolutions: %v", err) - } + if nextState != StateContractClosed { + return nil + } + + err := c.log.LogContractResolutions( + contractRes, + ) + if err != nil { + return fmt.Errorf("unable to write "+ + "resolutions: %v", err) } return nil @@ -1420,8 +1461,8 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32, // We'll now advance our state machine until it reaches // a terminal state. _, _, err := c.advanceState( - uint32(bestHeight), bestHash, - remotePeerTrigger, stateCb, + uint32(uniClosure.SpendingHeight), + remoteCloseTrigger, stateCb, ) if err != nil { log.Errorf("unable to advance state: %v", err) @@ -1466,7 +1507,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32, } nextState, closeTx, err := c.advanceState( - uint32(bestHeight), bestHash, userTrigger, nil, + uint32(bestHeight), userTrigger, nil, ) if err != nil { log.Errorf("unable to advance state: %v", err) @@ -1490,6 +1531,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32, log.Infof("ChannelArbitrator(%v): all "+ "contracts resolved, exiting", c.cfg.ChanPoint) + return } 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) -} diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index 236a3336..76579643 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -1 +1,341 @@ 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) +} diff --git a/htlcswitch/circuit_map.go b/htlcswitch/circuit_map.go index e5076544..844a9316 100644 --- a/htlcswitch/circuit_map.go +++ b/htlcswitch/circuit_map.go @@ -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 // interfering with normal operation. func (cm *circuitMap) trimAllOpenCircuits() error { - activeChannels, err := cm.cfg.DB.FetchAllChannels() + activeChannels, err := cm.cfg.DB.FetchAllOpenChannels() if err != nil { return err } for _, activeChannel := range activeChannels { - if activeChannel.IsPending { - continue - } - chanID := activeChannel.ShortChanID start := activeChannel.LocalCommitment.LocalHtlcIndex if err := cm.TrimOpenCircuits(chanID, start); err != nil { diff --git a/htlcswitch/link.go b/htlcswitch/link.go index c18d3332..01b5f2d5 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -832,7 +832,7 @@ out: // the contract as fully settled. Afterwards we can exit. // // 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", l.channel.ChannelPoint()) diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 11b4efed..cf1750c8 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -1513,16 +1513,12 @@ func (s *Switch) Start() error { // forwarding packages and reforwards any Settle or Fail HTLCs found. This is // used to resurrect the switch's mailboxes after a restart. func (s *Switch) reforwardResponses() error { - activeChannels, err := s.cfg.DB.FetchAllChannels() + activeChannels, err := s.cfg.DB.FetchAllOpenChannels() if err != nil { return err } for _, activeChannel := range activeChannels { - if activeChannel.IsPending { - continue - } - shortChanID := activeChannel.ShortChanID fwdPkgs, err := s.loadChannelFwdPkgs(shortChanID) if err != nil { diff --git a/lnd_test.go b/lnd_test.go index 8ca60dd4..af09964b 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -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 @@ -1109,8 +1109,17 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) { // Disconnect Alice-peer from Bob-peer without getting error // about existing channels. - if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil { - t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v", err) + var predErr error + 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. @@ -1353,6 +1362,27 @@ func findForceClosedChannel(t *harnessTest, 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, forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, 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 // belonging to a force closed channel, testing for the expected stage number, // 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 - // PendingChannels RPC under the force close section. + // PendingChannels RPC under the waiting close section. pendingChansRequest := &lnrpc.PendingChannelsRequest{} pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest) if err != nil { 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 // locate the pending channel information in the rpc responses. @@ -1597,21 +1639,12 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { 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, - // and the pending channels response should not indicate that any funds - // have been recovered. - if forceClose.LimboBalance == 0 { + // Immediately after force closing, all of the funds should be in limbo. + if waitingClose.LimboBalance == 0 { 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 // 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 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) if err != nil { t.Fatalf("unable to query for pending channels: %v", err) } 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 // height and number of blocks to confirm populated. @@ -3828,7 +3863,7 @@ func waitForNTxsInMempool(miner *rpcclient.Client, n int, for { select { 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)) case <-ticker.C: mempool, err = miner.GetRawMempool() @@ -4224,11 +4259,24 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(net *lntest.NetworkHarness // commitment transaction of a prior *revoked* state, so he'll soon // feel the wrath of Alice's retribution. force := true - closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force) + closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol, + chanPoint, force) if err != nil { 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 // update, then ensure that the closing transaction was included in the // block. @@ -4545,17 +4593,22 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness, // commitment transaction of a prior *revoked* state, so she'll soon // feel the wrath of Dave's retribution. force := true - closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force) + closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol, + chanPoint, force) if err != nil { t.Fatalf("unable to close channel: %v", err) } - // Query the mempool for Dave's justice transaction, this should be - // broadcast as Carol's contract breaching transaction gets confirmed - // above. - _, err = waitForTxInMempool(net.Miner.Node, 5*time.Second) + // 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 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) @@ -4577,14 +4630,101 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness, if err != nil { 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) // Query the mempool for Dave's justice transaction, this should be // broadcast as Carol's contract breaching transaction gets confirmed - // above. - justiceTXID, err := waitForTxInMempool(net.Miner.Node, 5*time.Second) + // above. Since Carol might have had the time to take some of the HTLC + // 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 { - 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) @@ -4597,36 +4737,12 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness, 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 // transaction which was just accepted into the mempool. block = mineBlocks(t, net, 1)[0] + assertTxInBlock(t, block, justiceTxid) - // The block should have exactly *two* transactions, one of which is - // 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") - } - + // Dave should have no open channels. 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 // 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 { @@ -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 // that we sent earlier. This means Alice should now only have a single // HTLC on her channel. @@ -6033,7 +6158,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { return true }, time.Second*15) 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 @@ -6050,7 +6175,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // The second layer HTLC timeout transaction should now have been // broadcast on-chain. - _, err = waitForTxInMempool(net.Miner.Node, time.Second*10) + secondLayerHash, err := waitForTxInMempool(net.Miner.Node, time.Second*10) if err != nil { 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. - if _, err := net.Miner.Node.Generate(1); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } + block := mineBlocks(t, net, 1)[0] // The block should have confirmed Bob's second layer sweeping // transaction. Therefore, at this point, there should be no active // HTLC's on the commitment transaction from Alice -> Bob. + assertTxInBlock(t, block, secondLayerHash) nodes = []*lntest.HarnessNode{net.Alice} err = lntest.WaitPredicate(func() bool { return assertNumActiveHtlcs(nodes, 0) @@ -6216,58 +6340,73 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) // At this point, Carol should broadcast her active commitment // transaction in order to go to the chain and sweep her HTLC. - // Additionally, Carol's should have broadcast her second layer sweep - // transaction for the HTLC as well. - txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15) + txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*20) 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) if err != nil { t.Fatalf("unable to get txid: %v", err) } + bobFundingTxid, err := chainhash.NewHash(txidHash) + if err != nil { + t.Fatalf("unable to create sha hash: %v", err) + } + carolFundingPoint := wire.OutPoint{ Hash: *bobFundingTxid, 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 { t.Fatalf("unable to get txn: %v", err) } - tx1Hash := tx1.MsgTx().TxHash() - tx2, err := net.Miner.Node.GetRawTransaction(txids[1]) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - tx2Hash := tx2.MsgTx().TxHash() + commitTx := tx.MsgTx() - // Of the two transactions, one should be spending from the funding - // transaction, and the second transaction should then be spending from + if commitTx.TxIn[0].PreviousOutPoint != carolFundingPoint { + 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. - var commitHash *chainhash.Hash - 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)) + 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 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") + if secondLevelHash == nil { + t.Fatalf("Carol's second level tx not found") } // We'll now mine an additional block which should confirm both the - // second layer transaction as well as the commitment transaction - // itself. + // second layer transactions. if _, err := net.Miner.Node.Generate(1); err != nil { 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 // just went to chain. 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 { - t.Fatalf("unable to query for pending channels: %v", err) - } - 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) + t.Fatalf(predErr.Error()) } // 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 { - predErr = fmt.Errorf("bob should have pending for " + + predErr = fmt.Errorf("bob should have pending force " + "close chan but doesn't") return false } - forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] if len(forceCloseChan.PendingHtlcs) != 1 { predErr = fmt.Errorf("bob should have pending htlc " + "but doesn't") @@ -6523,7 +6677,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, return false } - forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] if len(forceCloseChan.PendingHtlcs) != 1 { predErr = fmt.Errorf("bob should have pending htlc " + "but doesn't") @@ -6559,7 +6713,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // At this point, Bob should no longer show any channels as pending // close. err = lntest.WaitPredicate(func() bool { - pendingChanResp, err = net.Bob.PendingChannels( + pendingChanResp, err := net.Bob.PendingChannels( ctxb, pendingChansRequest, ) if err != nil { @@ -6642,7 +6796,7 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, return true }, time.Second*15) 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 @@ -6654,12 +6808,25 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // At this point, Bob should have a pending force close channel as // Carol has gone directly to chain. 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 { - t.Fatalf("unable to query for pending channels: %v", err) - } - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - t.Fatalf("bob should have pending for close chan but doesn't") + t.Fatalf(predErr.Error()) } // 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, // he should show no additional pending transactions. err = lntest.WaitPredicate(func() bool { - pendingChanResp, err = net.Bob.PendingChannels( + pendingChanResp, err := net.Bob.PendingChannels( ctxb, pendingChansRequest, ) if err != nil { @@ -6835,9 +7002,8 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) t.Fatalf("unable to generate blocks") } - // Carol's commitment transaction should now be in the mempool. She - // should also have broadcast her second level HTLC transaction. - txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15) + // Carol's commitment transaction should now be in the mempool. + txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15) if err != nil { t.Fatalf("transactions not found in mempool: %v", err) } @@ -6854,50 +7020,52 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) Index: bobChanPoint.OutputIndex, } - // Of the two transactions, one should be spending from the funding - // transaction, and the second transaction should then be spending from + // The tx should be spending from the funding transaction, + 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. - var commitHash *chainhash.Hash - tx1, err := net.Miner.Node.GetRawTransaction(txids[0]) - 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 { - 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)) + 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 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 - // 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") + if secondLevelHash == nil { + t.Fatalf("Carol's second level tx not found") } // 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") return false } - if forceCloseChan.PendingHtlcs[0].Stage != 1 { + stage := forceCloseChan.PendingHtlcs[0].Stage + if stage != 1 { 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 } } @@ -6942,6 +7112,15 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) 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 // destined for him. 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) ctxb := context.Background() + defaultCSV := uint32(4) + // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. @@ -7052,9 +7233,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest t.Fatalf("unable to generate blocks") } - // Carol's commitment transaction should now be in the mempool. She - // should also have broadcast her second level HTLC transaction. - txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15) + // Carol's commitment transaction should now be in the mempool. + txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15) if err != nil { t.Fatalf("transactions not found in mempool: %v", err) } @@ -7071,48 +7251,70 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest Index: bobChanPoint.OutputIndex, } - // Of the two transactions, one should be spending from the funding - // transaction, and the second transaction should then be spending from - // the commitment transaction. - var commitHash *chainhash.Hash - tx1, err := net.Miner.Node.GetRawTransaction(txids[0]) + // The transaction should be spending from the funding transaction + commitHash := txids[0] + tx1, err := net.Miner.Node.GetRawTransaction(commitHash) 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 { - 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") + if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint { + t.Fatalf("commit transaction not spending fundingtx: %v", + spew.Sdump(tx1)) } - // 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 { + // Mine a block, which should contain the commitment. + 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. + 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) } - // With the block mined above, Bob should detect that Carol is - // attempting to sweep the HTLC on-chain, and should obtain the - // preimage. - _, err = waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15) + _, err = waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15) if err != nil { t.Fatalf("unable to find bob's sweeping transaction") } diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index d78e9f28..50c2ebac 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -521,7 +521,9 @@ func (m *ChannelPoint) String() string { return proto.CompactTextStri func (*ChannelPoint) ProtoMessage() {} 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 { 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) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } -type isCloseStatusUpdate_Update interface{ isCloseStatusUpdate_Update() } +type isCloseStatusUpdate_Update interface { + isCloseStatusUpdate_Update() +} type CloseStatusUpdate_ClosePending struct { 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) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} } -type isOpenStatusUpdate_Update interface{ isOpenStatusUpdate_Update() } +type isOpenStatusUpdate_Update interface { + isOpenStatusUpdate_Update() +} type OpenStatusUpdate_ChanPending struct { 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"` // / Channels pending force closing 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{} } @@ -2125,6 +2133,13 @@ func (m *PendingChannelsResponse) GetPendingForceClosingChannels() []*PendingCha return nil } +func (m *PendingChannelsResponse) GetWaitingCloseChannels() []*PendingChannelsResponse_WaitingCloseChannel { + if m != nil { + return m.WaitingCloseChannels + } + return nil +} + type PendingChannelsResponse_PendingChannel struct { 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"` @@ -2244,6 +2259,38 @@ func (m *PendingChannelsResponse_PendingOpenChannel) GetFeePerKw() int64 { 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 { // / The pending channel to be closed 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 (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{47, 2} + return fileDescriptor0, []int{47, 3} } func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -2299,7 +2346,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string { } func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{47, 3} + return fileDescriptor0, []int{47, 4} } func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -3916,7 +3963,9 @@ func (m *PolicyUpdateRequest) String() string { return proto.CompactT func (*PolicyUpdateRequest) ProtoMessage() {} 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 { Global bool `protobuf:"varint,1,opt,name=global,oneof"` @@ -4234,6 +4283,7 @@ func init() { proto.RegisterType((*PendingChannelsResponse)(nil), "lnrpc.PendingChannelsResponse") proto.RegisterType((*PendingChannelsResponse_PendingChannel)(nil), "lnrpc.PendingChannelsResponse.PendingChannel") 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_ForceClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ForceClosedChannel") proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest") @@ -6301,353 +6351,356 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 5566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0xcd, 0x93, 0x1c, 0xc9, - 0x55, 0x57, 0xf5, 0xf4, 0x7c, 0xf4, 0xeb, 0x9e, 0x9e, 0x99, 0x9c, 0xd1, 0xa8, 0xd5, 0xd2, 0x6a, - 0xb5, 0xe5, 0x8d, 0x95, 0x18, 0x16, 0x8d, 0x76, 0x6c, 0x2f, 0xeb, 0x15, 0xac, 0xd1, 0xf7, 0xac, - 0xad, 0x95, 0xc7, 0x35, 0x92, 0x17, 0xbc, 0x40, 0xbb, 0xa6, 0x3b, 0xa7, 0xa7, 0xac, 0xea, 0xaa, - 0xda, 0xaa, 0xea, 0x19, 0xf5, 0x2e, 0x8a, 0xe0, 0x23, 0x82, 0x13, 0x0e, 0x0e, 0x70, 0x31, 0x04, - 0x41, 0x84, 0x7d, 0x81, 0x03, 0x47, 0x4e, 0xe6, 0x2f, 0x70, 0x04, 0xc1, 0x61, 0x4f, 0x0e, 0x6e, - 0x7c, 0x1c, 0xc0, 0xc1, 0x85, 0x08, 0x2e, 0x1c, 0x08, 0xe2, 0xbd, 0xfc, 0xa8, 0xcc, 0xaa, 0x1a, - 0x49, 0xb6, 0xc1, 0xb7, 0xce, 0x5f, 0xbe, 0x7a, 0xf9, 0xf5, 0xde, 0xcb, 0xf7, 0x5e, 0x66, 0x36, - 0xb4, 0xd2, 0x64, 0x78, 0x2d, 0x49, 0xe3, 0x3c, 0x66, 0xf3, 0x61, 0x94, 0x26, 0xc3, 0xfe, 0xc5, - 0x71, 0x1c, 0x8f, 0x43, 0xbe, 0xed, 0x27, 0xc1, 0xb6, 0x1f, 0x45, 0x71, 0xee, 0xe7, 0x41, 0x1c, - 0x65, 0x82, 0xc8, 0xfd, 0x16, 0x74, 0xef, 0xf3, 0x68, 0x9f, 0xf3, 0x91, 0xc7, 0x3f, 0x9e, 0xf2, - 0x2c, 0x67, 0xbf, 0x08, 0x6b, 0x3e, 0xff, 0x84, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39, 0x4a, - 0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0x5e, 0x83, - 0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae, 0x80, - 0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24, 0x47, - 0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57, 0x5b, - 0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f, 0xe8, - 0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0xcf, 0x1d, 0x58, 0x7b, 0x3f, 0x0a, 0xf2, 0x0f, - 0xfd, 0x30, 0xe4, 0xb9, 0x1a, 0xd3, 0x15, 0x58, 0x39, 0x21, 0x80, 0xc6, 0x74, 0x12, 0xa7, 0x23, - 0x39, 0xa2, 0xae, 0x80, 0xf7, 0x24, 0x7a, 0x6a, 0xcf, 0x1a, 0xa7, 0xf6, 0xac, 0x76, 0xba, 0xe6, - 0xea, 0xa7, 0xcb, 0xdd, 0x00, 0x66, 0x76, 0x4e, 0x4c, 0x87, 0xfb, 0x1e, 0xac, 0x3f, 0x8e, 0xc2, - 0x78, 0xf8, 0xe4, 0xa7, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b, 0xd0, - 0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0xa7, 0x83, 0x23, 0x3f, - 0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34, 0xab, - 0x73, 0x9e, 0x2c, 0xb1, 0x37, 0x61, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2, 0x89, - 0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, 0x26, - 0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, 0x85, - 0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, 0xaa, - 0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x1b, 0xd0, 0x1d, - 0xf1, 0x2c, 0x1f, 0xf8, 0xa3, 0x51, 0xca, 0xb3, 0x8c, 0x67, 0xbd, 0x25, 0x5a, 0xba, 0x12, 0xea, - 0xf6, 0x60, 0xf3, 0x3e, 0xcf, 0x8d, 0xd9, 0xc9, 0xe4, 0xb4, 0xbb, 0x0f, 0x80, 0x19, 0xf0, 0x1d, - 0x9e, 0xfb, 0x41, 0x98, 0xb1, 0xb7, 0xa1, 0x93, 0x1b, 0xc4, 0x24, 0xaa, 0xed, 0x1d, 0x76, 0x8d, - 0x74, 0xec, 0x9a, 0xf1, 0x81, 0x67, 0xd1, 0xb9, 0xff, 0xed, 0x40, 0x7b, 0x9f, 0x47, 0x5a, 0xbb, - 0x18, 0x34, 0xb1, 0x27, 0x72, 0x25, 0xe9, 0x37, 0x7b, 0x15, 0xda, 0xd4, 0xbb, 0x2c, 0x4f, 0x83, - 0x68, 0x4c, 0x4b, 0xd0, 0xf2, 0x00, 0xa1, 0x7d, 0x42, 0xd8, 0x2a, 0xcc, 0xf9, 0x93, 0x9c, 0x26, - 0x7e, 0xce, 0xc3, 0x9f, 0xa8, 0x77, 0x89, 0x3f, 0x9b, 0xf0, 0x28, 0x2f, 0x26, 0xbb, 0xe3, 0xb5, - 0x25, 0xb6, 0x8b, 0xb3, 0x7d, 0x0d, 0xd6, 0x4d, 0x12, 0xc5, 0x7d, 0x9e, 0xb8, 0xaf, 0x19, 0x94, - 0xb2, 0x91, 0x2b, 0xb0, 0xa2, 0xe8, 0x53, 0xd1, 0x59, 0x9a, 0xfe, 0x96, 0xd7, 0x95, 0xb0, 0x1a, - 0xc2, 0x55, 0x58, 0x3d, 0x0c, 0x22, 0x3f, 0x1c, 0x0c, 0xc3, 0xfc, 0x78, 0x30, 0xe2, 0x61, 0xee, - 0xd3, 0x42, 0xcc, 0x7b, 0x5d, 0xc2, 0x6f, 0x87, 0xf9, 0xf1, 0x1d, 0x44, 0xdd, 0x3f, 0x75, 0xa0, - 0x23, 0x06, 0x2f, 0x15, 0xff, 0x75, 0x58, 0x56, 0x6d, 0xf0, 0x34, 0x8d, 0x53, 0x29, 0x87, 0x36, - 0xc8, 0xb6, 0x60, 0x55, 0x01, 0x49, 0xca, 0x83, 0x89, 0x3f, 0xe6, 0x52, 0xdb, 0x2b, 0x38, 0xdb, - 0x29, 0x38, 0xa6, 0xf1, 0x34, 0x17, 0xaa, 0xd7, 0xde, 0xe9, 0xc8, 0x85, 0xf1, 0x10, 0xf3, 0x6c, - 0x12, 0xf7, 0x7b, 0x0e, 0x74, 0x6e, 0x1f, 0xf9, 0x51, 0xc4, 0xc3, 0xbd, 0x38, 0x88, 0x72, 0x76, - 0x1d, 0xd8, 0xe1, 0x34, 0x1a, 0x05, 0xd1, 0x78, 0x90, 0x3f, 0x0d, 0x46, 0x83, 0x83, 0x59, 0xce, - 0x33, 0xb1, 0x44, 0xbb, 0x67, 0xbc, 0x9a, 0x3a, 0xf6, 0x26, 0xac, 0x5a, 0x68, 0x96, 0xa7, 0x62, - 0xdd, 0x76, 0xcf, 0x78, 0x95, 0x1a, 0x14, 0xfc, 0x78, 0x9a, 0x27, 0xd3, 0x7c, 0x10, 0x44, 0x23, - 0xfe, 0x94, 0xfa, 0xb8, 0xec, 0x59, 0xd8, 0xad, 0x2e, 0x74, 0xcc, 0xef, 0xdc, 0xf7, 0x60, 0xf5, - 0x01, 0x6a, 0x44, 0x14, 0x44, 0xe3, 0x9b, 0x42, 0x6c, 0x51, 0x4d, 0x93, 0xe9, 0xc1, 0x13, 0x3e, - 0x93, 0xf3, 0x26, 0x4b, 0x28, 0x54, 0x47, 0x71, 0x96, 0x4b, 0xc9, 0xa1, 0xdf, 0xee, 0x3f, 0x3b, - 0xb0, 0x82, 0x73, 0xff, 0x81, 0x1f, 0xcd, 0xd4, 0xca, 0x3d, 0x80, 0x0e, 0xb2, 0x7a, 0x14, 0xdf, - 0x14, 0xca, 0x2e, 0x84, 0xf8, 0xaa, 0x9c, 0xab, 0x12, 0xf5, 0x35, 0x93, 0x14, 0x8d, 0xf9, 0xcc, - 0xb3, 0xbe, 0x46, 0xb1, 0xcd, 0xfd, 0x74, 0xcc, 0x73, 0x32, 0x03, 0xd2, 0x2c, 0x80, 0x80, 0x6e, - 0xc7, 0xd1, 0x21, 0xbb, 0x0c, 0x9d, 0xcc, 0xcf, 0x07, 0x09, 0x4f, 0x69, 0xd6, 0x48, 0xf4, 0xe6, - 0x3c, 0xc8, 0xfc, 0x7c, 0x8f, 0xa7, 0xb7, 0x66, 0x39, 0xef, 0x7f, 0x19, 0xd6, 0x2a, 0xad, 0xa0, - 0xb4, 0x17, 0x43, 0xc4, 0x9f, 0x6c, 0x03, 0xe6, 0x8f, 0xfd, 0x70, 0xca, 0xa5, 0x75, 0x12, 0x85, - 0x77, 0x1b, 0xef, 0x38, 0xee, 0x1b, 0xb0, 0x5a, 0x74, 0x5b, 0x0a, 0x19, 0x83, 0x26, 0xce, 0xa0, - 0x64, 0x40, 0xbf, 0xdd, 0xdf, 0x73, 0x04, 0xe1, 0xed, 0x38, 0xd0, 0x9a, 0x8e, 0x84, 0x68, 0x10, - 0x14, 0x21, 0xfe, 0x3e, 0xd5, 0x12, 0xfe, 0xec, 0x83, 0x75, 0xaf, 0xc0, 0x9a, 0xd1, 0x85, 0xe7, - 0x74, 0xf6, 0x3b, 0x0e, 0xac, 0x3d, 0xe4, 0x27, 0x72, 0xd5, 0x55, 0x6f, 0xdf, 0x81, 0x66, 0x3e, - 0x4b, 0xc4, 0x56, 0xdc, 0xdd, 0x79, 0x5d, 0x2e, 0x5a, 0x85, 0xee, 0x9a, 0x2c, 0x3e, 0x9a, 0x25, - 0xdc, 0xa3, 0x2f, 0xdc, 0xf7, 0xa0, 0x6d, 0x80, 0xec, 0x1c, 0xac, 0x7f, 0xf8, 0xfe, 0xa3, 0x87, - 0x77, 0xf7, 0xf7, 0x07, 0x7b, 0x8f, 0x6f, 0x7d, 0xf5, 0xee, 0x6f, 0x0c, 0x76, 0x6f, 0xee, 0xef, - 0xae, 0x9e, 0x61, 0x9b, 0xc0, 0x1e, 0xde, 0xdd, 0x7f, 0x74, 0xf7, 0x8e, 0x85, 0x3b, 0x6e, 0x1f, - 0x7a, 0x0f, 0xf9, 0xc9, 0x87, 0x41, 0x1e, 0xf1, 0x2c, 0xb3, 0x5b, 0x73, 0xaf, 0x01, 0x33, 0xbb, - 0x20, 0x47, 0xd5, 0x83, 0x45, 0x69, 0x6a, 0xd5, 0x4e, 0x23, 0x8b, 0xee, 0x1b, 0xc0, 0xf6, 0x83, - 0x71, 0xf4, 0x01, 0xcf, 0x32, 0x7f, 0xcc, 0xd5, 0xd8, 0x56, 0x61, 0x6e, 0x92, 0x8d, 0xa5, 0x51, - 0xc4, 0x9f, 0xee, 0xe7, 0x61, 0xdd, 0xa2, 0x93, 0x8c, 0x2f, 0x42, 0x2b, 0x0b, 0xc6, 0x91, 0x9f, - 0x4f, 0x53, 0x2e, 0x59, 0x17, 0x80, 0x7b, 0x0f, 0x36, 0xbe, 0xc1, 0xd3, 0xe0, 0x70, 0xf6, 0x22, - 0xf6, 0x36, 0x9f, 0x46, 0x99, 0xcf, 0x5d, 0x38, 0x5b, 0xe2, 0x23, 0x9b, 0x17, 0x82, 0x28, 0x97, - 0x6b, 0xc9, 0x13, 0x05, 0x43, 0x2d, 0x1b, 0xa6, 0x5a, 0xba, 0x8f, 0x81, 0xdd, 0x8e, 0xa3, 0x88, - 0x0f, 0xf3, 0x3d, 0xce, 0xd3, 0xc2, 0xbf, 0x2a, 0xa4, 0xae, 0xbd, 0x73, 0x4e, 0xae, 0x63, 0x59, - 0xd7, 0xa5, 0x38, 0x32, 0x68, 0x26, 0x3c, 0x9d, 0x10, 0xe3, 0x25, 0x8f, 0x7e, 0xbb, 0x67, 0x61, - 0xdd, 0x62, 0x2b, 0x77, 0xfb, 0xb7, 0xe0, 0xec, 0x9d, 0x20, 0x1b, 0x56, 0x1b, 0xec, 0xc1, 0x62, - 0x32, 0x3d, 0x18, 0x14, 0x3a, 0xa5, 0x8a, 0xb8, 0x09, 0x96, 0x3f, 0x91, 0xcc, 0xfe, 0xd0, 0x81, - 0xe6, 0xee, 0xa3, 0x07, 0xb7, 0x59, 0x1f, 0x96, 0x82, 0x68, 0x18, 0x4f, 0x70, 0xeb, 0x10, 0x83, - 0xd6, 0xe5, 0x53, 0x75, 0xe5, 0x22, 0xb4, 0x68, 0xc7, 0xc1, 0x7d, 0x5d, 0xba, 0x42, 0x05, 0x80, - 0x3e, 0x05, 0x7f, 0x9a, 0x04, 0x29, 0x39, 0x0d, 0xca, 0x15, 0x68, 0x92, 0x45, 0xac, 0x56, 0xb8, - 0xff, 0xd3, 0x84, 0x45, 0x69, 0xab, 0xa9, 0xbd, 0x61, 0x1e, 0x1c, 0x73, 0xd9, 0x13, 0x59, 0xc2, - 0x5d, 0x25, 0xe5, 0x93, 0x38, 0xe7, 0x03, 0x6b, 0x19, 0x6c, 0x10, 0xa9, 0x86, 0x82, 0xd1, 0x20, - 0x41, 0xab, 0x4f, 0x3d, 0x6b, 0x79, 0x36, 0x88, 0x93, 0x85, 0xc0, 0x20, 0x18, 0x51, 0x9f, 0x9a, - 0x9e, 0x2a, 0xe2, 0x4c, 0x0c, 0xfd, 0xc4, 0x1f, 0x06, 0xf9, 0x4c, 0x2a, 0xb7, 0x2e, 0x23, 0xef, - 0x30, 0x1e, 0xfa, 0xe1, 0xe0, 0xc0, 0x0f, 0xfd, 0x68, 0xc8, 0xa5, 0xe3, 0x62, 0x83, 0xe8, 0x9b, - 0xc8, 0x2e, 0x29, 0x32, 0xe1, 0xbf, 0x94, 0x50, 0xf4, 0x71, 0x86, 0xf1, 0x64, 0x12, 0xe4, 0xe8, - 0xd2, 0xf4, 0x96, 0x84, 0x21, 0x29, 0x10, 0x1a, 0x89, 0x28, 0x9d, 0x88, 0xd9, 0x6b, 0x89, 0xd6, - 0x2c, 0x10, 0xb9, 0x1c, 0x72, 0x4e, 0x06, 0xe9, 0xc9, 0x49, 0x0f, 0x04, 0x97, 0x02, 0xc1, 0x75, - 0x98, 0x46, 0x19, 0xcf, 0xf3, 0x90, 0x8f, 0x74, 0x87, 0xda, 0x44, 0x56, 0xad, 0x60, 0xd7, 0x61, - 0x5d, 0x78, 0x59, 0x99, 0x9f, 0xc7, 0xd9, 0x51, 0x90, 0x0d, 0x32, 0x1e, 0xe5, 0xbd, 0x0e, 0xd1, - 0xd7, 0x55, 0xb1, 0x77, 0xe0, 0x5c, 0x09, 0x4e, 0xf9, 0x90, 0x07, 0xc7, 0x7c, 0xd4, 0x5b, 0xa6, - 0xaf, 0x4e, 0xab, 0x66, 0x97, 0xa1, 0x8d, 0xce, 0xe5, 0x34, 0x19, 0xf9, 0xb8, 0x0f, 0x77, 0x69, - 0x1d, 0x4c, 0x88, 0xbd, 0x05, 0xcb, 0x09, 0x17, 0x9b, 0xe5, 0x51, 0x1e, 0x0e, 0xb3, 0xde, 0x0a, - 0xed, 0x64, 0x6d, 0xa9, 0x4c, 0x28, 0xb9, 0x9e, 0x4d, 0x81, 0x42, 0x39, 0xcc, 0xc8, 0x5d, 0xf1, - 0x67, 0xbd, 0x55, 0x12, 0xb7, 0x02, 0x20, 0x1d, 0x49, 0x83, 0x63, 0x3f, 0xe7, 0xbd, 0x35, 0x92, - 0x2d, 0x55, 0x74, 0xff, 0xd2, 0x81, 0xf5, 0x07, 0x41, 0x96, 0x4b, 0x21, 0xd4, 0xe6, 0xf8, 0x55, - 0x68, 0x0b, 0xf1, 0x1b, 0xc4, 0x51, 0x38, 0x93, 0x12, 0x09, 0x02, 0xfa, 0x5a, 0x14, 0xce, 0xd8, - 0xe7, 0x60, 0x39, 0x88, 0x4c, 0x12, 0xa1, 0xc3, 0x1d, 0x05, 0x12, 0xd1, 0xab, 0xd0, 0x4e, 0xa6, - 0x07, 0x61, 0x30, 0x14, 0x24, 0x73, 0x82, 0x8b, 0x80, 0x88, 0x00, 0x1d, 0x3d, 0xd1, 0x13, 0x41, - 0xd1, 0x24, 0x8a, 0xb6, 0xc4, 0x90, 0xc4, 0xbd, 0x05, 0x1b, 0x76, 0x07, 0xa5, 0xb1, 0xda, 0x82, - 0x25, 0x29, 0xdb, 0x59, 0xaf, 0x4d, 0xf3, 0xd3, 0x95, 0xf3, 0x23, 0x49, 0x3d, 0x5d, 0xef, 0xfe, - 0xbb, 0x03, 0x4d, 0x34, 0x00, 0xa7, 0x1b, 0x0b, 0xd3, 0xa6, 0xcf, 0x59, 0x36, 0x9d, 0xfc, 0x7e, - 0xf4, 0x8a, 0x84, 0x48, 0x08, 0xb5, 0x31, 0x90, 0xa2, 0x3e, 0xe5, 0xc3, 0x63, 0xd2, 0x1d, 0x5d, - 0x8f, 0x08, 0x6a, 0x16, 0x6e, 0x9d, 0xf4, 0xb5, 0x50, 0x1c, 0x5d, 0x56, 0x75, 0xf4, 0xe5, 0x62, - 0x51, 0x47, 0xdf, 0xf5, 0x60, 0x31, 0x88, 0x0e, 0xe2, 0x69, 0x34, 0x22, 0x25, 0x59, 0xf2, 0x54, - 0x11, 0x17, 0x3b, 0x21, 0x4f, 0x2a, 0x98, 0x70, 0xa9, 0x1d, 0x05, 0xe0, 0x32, 0x74, 0xad, 0x32, - 0x32, 0x78, 0x7a, 0x1f, 0x7b, 0x1b, 0xd6, 0x0c, 0x4c, 0xce, 0xe0, 0x6b, 0x30, 0x9f, 0x20, 0x20, - 0x1d, 0x25, 0x25, 0x5e, 0x64, 0x29, 0x45, 0x8d, 0xbb, 0x8a, 0xf1, 0x73, 0xfe, 0x7e, 0x74, 0x18, - 0x2b, 0x4e, 0x3f, 0x9a, 0xc3, 0x80, 0x57, 0x42, 0x92, 0xd1, 0x55, 0x58, 0x09, 0x46, 0x3c, 0xca, - 0x83, 0x7c, 0x36, 0xb0, 0x3c, 0xb8, 0x32, 0x8c, 0x3b, 0x8c, 0x1f, 0x06, 0x7e, 0x26, 0x6d, 0x98, - 0x28, 0xb0, 0x1d, 0xd8, 0x40, 0xf1, 0x57, 0x12, 0xad, 0x97, 0x55, 0x38, 0x92, 0xb5, 0x75, 0xa8, - 0xb1, 0x88, 0x4b, 0x09, 0xd4, 0x9f, 0x08, 0x4b, 0x5b, 0x57, 0x85, 0xb3, 0x26, 0x38, 0xe1, 0x90, - 0xe7, 0x85, 0x8a, 0x68, 0xa0, 0x12, 0xbd, 0x2d, 0x08, 0x27, 0xb6, 0x1c, 0xbd, 0x19, 0x11, 0xe0, - 0x52, 0x25, 0x02, 0xbc, 0x0a, 0x2b, 0xd9, 0x2c, 0x1a, 0xf2, 0xd1, 0x20, 0x8f, 0xb1, 0xdd, 0x20, - 0xa2, 0xd5, 0x59, 0xf2, 0xca, 0x30, 0xc5, 0xaa, 0x3c, 0xcb, 0x23, 0x9e, 0x93, 0xe9, 0x5a, 0xf2, - 0x54, 0x11, 0x77, 0x01, 0x22, 0x11, 0x42, 0xdd, 0xf2, 0x64, 0x09, 0xb7, 0xca, 0x69, 0x1a, 0x64, - 0xbd, 0x0e, 0xa1, 0xf4, 0x9b, 0x7d, 0x01, 0xce, 0x1e, 0x60, 0x64, 0x75, 0xc4, 0xfd, 0x11, 0x4f, - 0x69, 0xf5, 0x45, 0x60, 0x29, 0x2c, 0x50, 0x7d, 0x25, 0xb6, 0x7d, 0xcc, 0xd3, 0x2c, 0x88, 0x23, - 0xb2, 0x3d, 0x2d, 0x4f, 0x15, 0xdd, 0x4f, 0x68, 0x47, 0xd7, 0x21, 0xef, 0x63, 0x32, 0x47, 0xec, - 0x02, 0xb4, 0xc4, 0x18, 0xb3, 0x23, 0x5f, 0x3a, 0x19, 0x4b, 0x04, 0xec, 0x1f, 0xf9, 0xa8, 0xc0, - 0xd6, 0xb4, 0x35, 0xc8, 0x73, 0x6c, 0x13, 0xb6, 0x2b, 0x66, 0xed, 0x75, 0xe8, 0xaa, 0x60, 0x3a, - 0x1b, 0x84, 0xfc, 0x30, 0x57, 0x01, 0x42, 0x34, 0x9d, 0x60, 0x73, 0xd9, 0x03, 0x7e, 0x98, 0xbb, - 0x0f, 0x61, 0x4d, 0xea, 0xed, 0xd7, 0x12, 0xae, 0x9a, 0xfe, 0x52, 0x79, 0x53, 0x13, 0x5e, 0xc5, - 0xba, 0xad, 0xe8, 0x14, 0xe5, 0x94, 0x76, 0x3a, 0xd7, 0x03, 0x26, 0xab, 0x6f, 0x87, 0x71, 0xc6, - 0x25, 0x43, 0x17, 0x3a, 0xc3, 0x30, 0xce, 0x54, 0x18, 0x22, 0x87, 0x63, 0x61, 0x38, 0x3f, 0xd9, - 0x74, 0x38, 0x44, 0x4b, 0x20, 0x6c, 0x9a, 0x2a, 0xba, 0x7f, 0xe5, 0xc0, 0x3a, 0x71, 0x53, 0x16, - 0x46, 0xfb, 0xae, 0x2f, 0xdf, 0xcd, 0xce, 0xd0, 0x0c, 0xcd, 0x36, 0x60, 0xfe, 0x30, 0x4e, 0x87, - 0x5c, 0xb6, 0x24, 0x0a, 0x3f, 0xb9, 0x37, 0xde, 0xac, 0x78, 0xe3, 0x3f, 0x72, 0x60, 0x8d, 0xba, - 0xba, 0x9f, 0xfb, 0xf9, 0x34, 0x93, 0xc3, 0xff, 0x15, 0x58, 0xc6, 0xa1, 0x72, 0xa5, 0x4e, 0xb2, - 0xa3, 0x1b, 0x5a, 0xf3, 0x09, 0x15, 0xc4, 0xbb, 0x67, 0x3c, 0x9b, 0x98, 0x7d, 0x19, 0x3a, 0x66, - 0x46, 0x84, 0xfa, 0xdc, 0xde, 0x39, 0xaf, 0x46, 0x59, 0x91, 0x9c, 0xdd, 0x33, 0x9e, 0xf5, 0x01, - 0xbb, 0x01, 0x40, 0xee, 0x06, 0xb1, 0x95, 0xa1, 0xec, 0x79, 0x7b, 0x92, 0x8c, 0xc5, 0xda, 0x3d, - 0xe3, 0x19, 0xe4, 0xb7, 0x96, 0x60, 0x41, 0xec, 0x8f, 0xee, 0x7d, 0x58, 0xb6, 0x7a, 0x6a, 0x45, - 0x19, 0x1d, 0x11, 0x65, 0x54, 0x82, 0xd2, 0x46, 0x35, 0x28, 0x75, 0xff, 0xb5, 0x01, 0x0c, 0xa5, - 0xad, 0xb4, 0x9c, 0xb8, 0x41, 0xc7, 0x23, 0xcb, 0xdd, 0xea, 0x78, 0x26, 0xc4, 0xae, 0x01, 0x33, - 0x8a, 0x2a, 0xf7, 0x20, 0xf6, 0x8d, 0x9a, 0x1a, 0x34, 0x70, 0xc2, 0x57, 0x52, 0x31, 0xb0, 0x74, - 0x2c, 0xc5, 0xba, 0xd5, 0xd6, 0xe1, 0xd6, 0x90, 0x4c, 0xb3, 0x23, 0x74, 0x20, 0x94, 0x43, 0xa6, - 0xca, 0x65, 0x01, 0x59, 0x78, 0xa1, 0x80, 0x2c, 0x96, 0x05, 0xc4, 0x74, 0x09, 0x96, 0x2c, 0x97, - 0x00, 0xfd, 0xaf, 0x49, 0x10, 0x91, 0x5f, 0x31, 0x98, 0x60, 0xeb, 0xd2, 0xff, 0xb2, 0x40, 0xb6, - 0x05, 0xab, 0xd2, 0xaf, 0x2b, 0xfc, 0x0e, 0xa0, 0x39, 0xae, 0xe0, 0xee, 0x67, 0x0e, 0xac, 0xe2, - 0x3c, 0x5b, 0xb2, 0xf8, 0x2e, 0x90, 0x2a, 0xbc, 0xa4, 0x28, 0x5a, 0xb4, 0x3f, 0xbb, 0x24, 0xbe, - 0x03, 0x2d, 0x62, 0x18, 0x27, 0x3c, 0x92, 0x82, 0xd8, 0xb3, 0x05, 0xb1, 0xb0, 0x42, 0xbb, 0x67, - 0xbc, 0x82, 0xd8, 0x10, 0xc3, 0x7f, 0x70, 0xa0, 0x2d, 0xbb, 0xf9, 0x53, 0xc7, 0x12, 0x7d, 0x58, - 0x42, 0x89, 0x34, 0x1c, 0x76, 0x5d, 0xc6, 0xdd, 0x64, 0x82, 0x01, 0x1b, 0x6e, 0x9f, 0x56, 0x1c, - 0x51, 0x86, 0x71, 0x2f, 0x24, 0x83, 0x9b, 0x0d, 0xf2, 0x20, 0x1c, 0xa8, 0x5a, 0x99, 0x80, 0xac, - 0xab, 0x42, 0xbb, 0x93, 0xe5, 0xfe, 0x98, 0xcb, 0x6d, 0x4e, 0x14, 0x30, 0x60, 0x92, 0x03, 0x2a, - 0xb9, 0x83, 0xee, 0x0f, 0x01, 0xce, 0x55, 0xaa, 0x74, 0xba, 0x5b, 0x3a, 0xc8, 0x61, 0x30, 0x39, - 0x88, 0xb5, 0xaf, 0xed, 0x98, 0xbe, 0xb3, 0x55, 0xc5, 0xc6, 0x70, 0x56, 0xed, 0xe7, 0x38, 0xa7, - 0xc5, 0xee, 0xdd, 0x20, 0x47, 0xe4, 0x2d, 0x5b, 0x06, 0xca, 0x0d, 0x2a, 0xdc, 0xd4, 0xdc, 0x7a, - 0x7e, 0xec, 0x08, 0x7a, 0xda, 0x71, 0x90, 0x26, 0xde, 0x70, 0x2e, 0xb0, 0xad, 0x37, 0x5f, 0xd0, - 0x16, 0xd9, 0xa3, 0x91, 0x6a, 0xe6, 0x54, 0x6e, 0x6c, 0x06, 0x97, 0x54, 0x1d, 0xd9, 0xf0, 0x6a, - 0x7b, 0xcd, 0x97, 0x1a, 0xdb, 0x3d, 0xfc, 0xd8, 0x6e, 0xf4, 0x05, 0x8c, 0xfb, 0x3f, 0x74, 0xa0, - 0x6b, 0xb3, 0x43, 0xd1, 0x91, 0x4a, 0xa8, 0x8c, 0x91, 0x72, 0xc8, 0x4a, 0x70, 0x35, 0x6c, 0x6c, - 0xd4, 0x85, 0x8d, 0x66, 0x70, 0x38, 0xf7, 0xa2, 0xe0, 0xb0, 0xf9, 0x72, 0xc1, 0xe1, 0x7c, 0x5d, - 0x70, 0xd8, 0xff, 0x2f, 0x07, 0x58, 0x75, 0x7d, 0xd9, 0x7d, 0x11, 0xb7, 0x46, 0x3c, 0x94, 0x76, - 0xe2, 0x97, 0x5e, 0x4e, 0x46, 0xd4, 0x1c, 0xaa, 0xaf, 0x51, 0x58, 0x4d, 0x43, 0x60, 0xba, 0x2d, - 0xcb, 0x5e, 0x5d, 0x55, 0x29, 0x5c, 0x6d, 0xbe, 0x38, 0x5c, 0x9d, 0x7f, 0x71, 0xb8, 0xba, 0x50, - 0x0e, 0x57, 0xfb, 0xbf, 0x03, 0xcb, 0xd6, 0xaa, 0xff, 0xdf, 0x8d, 0xb8, 0xec, 0xf2, 0x88, 0x05, - 0xb6, 0xb0, 0xfe, 0x8f, 0x1b, 0xc0, 0xaa, 0x92, 0xf7, 0x73, 0xed, 0x03, 0xc9, 0x91, 0x65, 0x40, - 0xe6, 0xa4, 0x1c, 0x59, 0xa6, 0xe3, 0xff, 0xd3, 0x28, 0xbe, 0x09, 0x6b, 0x29, 0x1f, 0xc6, 0xc7, - 0x74, 0x08, 0x67, 0xa7, 0x3a, 0xaa, 0x15, 0xe8, 0xf4, 0xd9, 0x41, 0xfa, 0x92, 0x75, 0x66, 0x62, - 0xec, 0x0c, 0xa5, 0x58, 0xdd, 0xdd, 0x84, 0x0d, 0x71, 0x94, 0x75, 0x4b, 0xb0, 0x52, 0x46, 0xf6, - 0x2f, 0x1c, 0x38, 0x5b, 0xaa, 0x28, 0x0e, 0x16, 0x84, 0x1d, 0xb5, 0x8d, 0xab, 0x0d, 0x62, 0xff, - 0xa5, 0x00, 0x1b, 0xfd, 0x17, 0xfb, 0x4d, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac, - 0xd7, 0x55, 0xb9, 0xe7, 0xe0, 0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x64, - 0x4a, 0xed, 0x2e, 0xab, 0x22, 0xba, 0x44, 0x96, 0xcd, 0xb6, 0xfb, 0x5b, 0x5b, 0xe7, 0xfe, 0x36, - 0xb0, 0xaf, 0x4f, 0x79, 0x3a, 0xa3, 0x63, 0x0f, 0x9d, 0xaa, 0x38, 0x57, 0x8e, 0xe9, 0x17, 0x92, - 0xe9, 0xc1, 0x57, 0xf9, 0x4c, 0x9d, 0x2b, 0x35, 0x8a, 0x73, 0xa5, 0x57, 0x00, 0x30, 0x14, 0xa1, - 0x73, 0x12, 0x75, 0xd2, 0x87, 0x31, 0xa0, 0x60, 0xe8, 0xde, 0x80, 0x75, 0x8b, 0xbf, 0x9e, 0xfd, - 0x05, 0xf9, 0x85, 0x08, 0x94, 0xed, 0xd3, 0x17, 0x59, 0xe7, 0xfe, 0x87, 0x03, 0x73, 0xbb, 0x71, - 0x62, 0xa6, 0xd8, 0x1c, 0x3b, 0xc5, 0x26, 0x6d, 0xed, 0x40, 0x9b, 0xd2, 0x86, 0xb4, 0x14, 0x26, - 0xc8, 0xb6, 0xa0, 0xeb, 0x4f, 0x72, 0x0c, 0x15, 0x0f, 0xe3, 0xf4, 0xc4, 0x4f, 0x47, 0x62, 0x49, - 0x6e, 0x35, 0x7a, 0x8e, 0x57, 0xaa, 0x61, 0x1b, 0x30, 0xa7, 0x8d, 0x12, 0x11, 0x60, 0x11, 0x9d, - 0x0d, 0xca, 0x34, 0xce, 0x64, 0x94, 0x2b, 0x4b, 0xb8, 0xe2, 0xf6, 0xf7, 0xc2, 0xbd, 0x13, 0x12, - 0x5e, 0x57, 0x85, 0x76, 0x1f, 0x6d, 0x14, 0x91, 0xc9, 0xf4, 0x84, 0x2a, 0xbb, 0xff, 0xe6, 0xc0, - 0x3c, 0xcd, 0x00, 0xea, 0xa4, 0x10, 0x44, 0x3a, 0xc8, 0xa4, 0xb4, 0xa8, 0x23, 0x74, 0xb2, 0x04, - 0x33, 0xd7, 0x3a, 0xde, 0x6c, 0xe8, 0x6e, 0x9b, 0x47, 0x9c, 0x97, 0xa1, 0x25, 0x4a, 0xfa, 0x4c, - 0x90, 0x48, 0x0a, 0x90, 0x5d, 0x82, 0xe6, 0x51, 0x9c, 0xa8, 0x1d, 0x15, 0x54, 0x56, 0x2c, 0x4e, - 0x3c, 0xc2, 0x8b, 0xfe, 0x20, 0x3f, 0xd1, 0x79, 0x61, 0x93, 0xcb, 0x30, 0xee, 0x4a, 0x9a, 0xad, - 0x39, 0x19, 0x25, 0xd4, 0xdd, 0x82, 0x95, 0x87, 0xf1, 0x88, 0x1b, 0x79, 0x90, 0x53, 0xa5, 0xce, - 0xfd, 0x5d, 0x07, 0x96, 0x14, 0x31, 0xbb, 0x0a, 0x4d, 0xdc, 0x6a, 0x4b, 0xce, 0xad, 0xce, 0x86, - 0x23, 0x9d, 0x47, 0x14, 0x68, 0x22, 0x29, 0x4a, 0x2e, 0x5c, 0x21, 0x15, 0x23, 0x17, 0x4e, 0x86, - 0xee, 0x6e, 0x69, 0x33, 0x2e, 0xa1, 0xee, 0x5f, 0x3b, 0xb0, 0x6c, 0xb5, 0x81, 0x21, 0x4d, 0xe8, - 0x67, 0xb9, 0xcc, 0x30, 0xca, 0xe5, 0x31, 0x21, 0x33, 0x33, 0xd6, 0xb0, 0x33, 0x63, 0x3a, 0x67, - 0x33, 0x67, 0xe6, 0x6c, 0xae, 0x43, 0xab, 0x38, 0x84, 0x6e, 0x5a, 0xa6, 0x0f, 0x5b, 0x54, 0x79, - 0xfe, 0x82, 0x08, 0xf9, 0x0c, 0xe3, 0x30, 0x4e, 0xe5, 0x19, 0xad, 0x28, 0xb8, 0x37, 0xa0, 0x6d, - 0xd0, 0x63, 0x37, 0x22, 0x9e, 0x9f, 0xc4, 0xe9, 0x13, 0x95, 0xa0, 0x93, 0x45, 0x7d, 0x9c, 0xd5, - 0x28, 0x8e, 0xb3, 0xdc, 0xbf, 0x71, 0x60, 0x19, 0x65, 0x30, 0x88, 0xc6, 0x7b, 0x71, 0x18, 0x0c, - 0x67, 0xb4, 0xf6, 0x4a, 0xdc, 0xe4, 0xe1, 0xad, 0x92, 0x45, 0x1b, 0x46, 0xd9, 0x56, 0x11, 0x8d, - 0x54, 0x44, 0x5d, 0x46, 0x4d, 0x45, 0x39, 0x3f, 0xf0, 0x33, 0x29, 0xfc, 0x72, 0x2f, 0xb2, 0x40, - 0xd4, 0x27, 0x04, 0x52, 0x3f, 0xe7, 0x83, 0x49, 0x10, 0x86, 0x81, 0xa0, 0x15, 0x2e, 0x42, 0x5d, - 0x95, 0xfb, 0x83, 0x06, 0xb4, 0xa5, 0xa5, 0xbc, 0x3b, 0x1a, 0x8b, 0x54, 0xb8, 0x74, 0xb4, 0xb4, - 0xb9, 0x30, 0x10, 0x55, 0x6f, 0xb9, 0x66, 0x06, 0x52, 0x5e, 0xd6, 0xb9, 0xea, 0xb2, 0x5e, 0x84, - 0x16, 0x8a, 0xd7, 0x5b, 0xe4, 0x03, 0x8a, 0x3b, 0x0b, 0x05, 0xa0, 0x6a, 0x77, 0xa8, 0x76, 0xbe, - 0xa8, 0x25, 0xc0, 0xf2, 0xfa, 0x16, 0x4a, 0x5e, 0xdf, 0x3b, 0xd0, 0x91, 0x6c, 0x68, 0xde, 0xc9, - 0x3a, 0x14, 0x02, 0x6e, 0xad, 0x89, 0x67, 0x51, 0xaa, 0x2f, 0x77, 0xd4, 0x97, 0x4b, 0x2f, 0xfa, - 0x52, 0x51, 0xd2, 0xc9, 0x90, 0x98, 0x9b, 0xfb, 0xa9, 0x9f, 0x1c, 0xa9, 0xdd, 0x67, 0xa4, 0x8f, - 0xbb, 0x09, 0x66, 0x5b, 0x30, 0x8f, 0x9f, 0x29, 0x6b, 0x5d, 0xaf, 0x74, 0x82, 0x84, 0x5d, 0x85, - 0x79, 0x3e, 0x1a, 0x73, 0x15, 0x79, 0x30, 0x3b, 0x06, 0xc4, 0x35, 0xf2, 0x04, 0x01, 0x9a, 0x00, - 0x44, 0x4b, 0x26, 0xc0, 0xb6, 0xf4, 0x0b, 0x58, 0x7c, 0x7f, 0xe4, 0x6e, 0x00, 0x7b, 0x28, 0xa4, - 0xd6, 0xcc, 0x9c, 0xfe, 0xc1, 0x1c, 0xb4, 0x0d, 0x18, 0xb5, 0x79, 0x8c, 0x1d, 0x1e, 0x8c, 0x02, - 0x7f, 0xc2, 0x73, 0x9e, 0x4a, 0x49, 0x2d, 0xa1, 0x48, 0xe7, 0x1f, 0x8f, 0x07, 0xf1, 0x34, 0x1f, - 0x8c, 0xf8, 0x38, 0xe5, 0x62, 0x8f, 0xc4, 0xcd, 0xc0, 0x42, 0x91, 0x6e, 0xe2, 0x3f, 0x35, 0xe9, - 0x84, 0x3c, 0x94, 0x50, 0x95, 0x07, 0x15, 0x73, 0xd4, 0x2c, 0xf2, 0xa0, 0x62, 0x46, 0xca, 0x76, - 0x68, 0xbe, 0xc6, 0x0e, 0xbd, 0x0d, 0x9b, 0xc2, 0xe2, 0x48, 0xdd, 0x1c, 0x94, 0xc4, 0xe4, 0x94, - 0x5a, 0xb6, 0x05, 0xab, 0xd8, 0x67, 0x25, 0xe0, 0x59, 0xf0, 0x89, 0xc8, 0x4c, 0x38, 0x5e, 0x05, - 0x47, 0x5a, 0x54, 0x47, 0x8b, 0x56, 0x9c, 0x15, 0x55, 0x70, 0xa2, 0xf5, 0x9f, 0xda, 0xb4, 0x2d, - 0x49, 0x5b, 0xc2, 0xdd, 0x65, 0x68, 0xef, 0xe7, 0x71, 0xa2, 0x16, 0xa5, 0x0b, 0x1d, 0x51, 0x94, - 0x27, 0x83, 0x17, 0xe0, 0x3c, 0x49, 0xd1, 0xa3, 0x38, 0x89, 0xc3, 0x78, 0x3c, 0xdb, 0x9f, 0x1e, - 0x64, 0xc3, 0x34, 0x48, 0x30, 0x22, 0x70, 0xff, 0xde, 0x81, 0x75, 0xab, 0x56, 0xa6, 0x32, 0xbe, - 0x20, 0x44, 0x5a, 0x1f, 0xe9, 0x08, 0xc1, 0x5b, 0x33, 0xcc, 0xa1, 0x20, 0x14, 0x49, 0xa4, 0xc7, - 0xf2, 0x94, 0xe7, 0x26, 0xac, 0xa8, 0x9e, 0xa9, 0x0f, 0x85, 0x14, 0xf6, 0xaa, 0x52, 0x28, 0xbf, - 0xef, 0xca, 0x0f, 0x14, 0x8b, 0x5f, 0x15, 0x7e, 0x35, 0x1f, 0xd1, 0x18, 0x55, 0x4c, 0xdb, 0x57, - 0xdf, 0x9b, 0xce, 0xbc, 0xea, 0xc1, 0x50, 0x83, 0x99, 0xfb, 0x47, 0x0e, 0x40, 0xd1, 0x3b, 0x14, - 0x8c, 0xc2, 0xa4, 0x8b, 0xcb, 0x6a, 0x86, 0xf9, 0x7e, 0x0d, 0x3a, 0x3a, 0x9b, 0x5f, 0xec, 0x12, - 0x6d, 0x85, 0xa1, 0xc3, 0x75, 0x05, 0x56, 0xc6, 0x61, 0x7c, 0x40, 0x5b, 0x2c, 0x1d, 0x35, 0x67, - 0xf2, 0x7c, 0xb4, 0x2b, 0xe0, 0x7b, 0x12, 0x2d, 0xb6, 0x94, 0xa6, 0xb1, 0xa5, 0xb8, 0xdf, 0x69, - 0xe8, 0x1c, 0x70, 0x31, 0xe6, 0x53, 0xb5, 0x8c, 0xed, 0x54, 0x8c, 0xe3, 0x29, 0x29, 0x57, 0xca, - 0xde, 0xec, 0xbd, 0x30, 0x90, 0xbd, 0x01, 0xdd, 0x54, 0x58, 0x1f, 0x65, 0x9a, 0x9a, 0xcf, 0x31, - 0x4d, 0xcb, 0xa9, 0xb5, 0xef, 0xfc, 0x02, 0xac, 0xfa, 0xa3, 0x63, 0x9e, 0xe6, 0x01, 0x45, 0x34, - 0xb4, 0xe9, 0x0b, 0x83, 0xba, 0x62, 0xe0, 0xb4, 0x17, 0x5f, 0x81, 0x15, 0x79, 0x26, 0xad, 0x29, - 0xe5, 0x4d, 0xa4, 0x02, 0x46, 0x42, 0xf7, 0xfb, 0x2a, 0xdd, 0x6c, 0xaf, 0xe1, 0xe9, 0x33, 0x62, - 0x8e, 0xae, 0x51, 0x1a, 0xdd, 0xe7, 0x64, 0xea, 0x77, 0xa4, 0xc2, 0x26, 0x99, 0x84, 0x17, 0xa0, - 0x4c, 0xd5, 0xdb, 0x53, 0xda, 0x7c, 0x99, 0x29, 0x75, 0x3f, 0x73, 0x60, 0x71, 0x37, 0x4e, 0x76, - 0xe5, 0xf1, 0x32, 0x29, 0x82, 0xbe, 0xf1, 0xa1, 0x8a, 0xa6, 0x57, 0xdc, 0xa8, 0x78, 0xc5, 0xd5, - 0xbd, 0x76, 0xb9, 0xbc, 0xd7, 0xfe, 0x1a, 0x5c, 0xa0, 0x68, 0x39, 0x8d, 0x93, 0x38, 0x45, 0x65, - 0xf4, 0x43, 0xb1, 0xb1, 0xc6, 0x51, 0x7e, 0xa4, 0xcc, 0xd8, 0xf3, 0x48, 0x28, 0x3a, 0x0a, 0xf3, - 0xe3, 0x81, 0x70, 0x86, 0xa5, 0x6f, 0x20, 0xac, 0x5b, 0xb5, 0xc2, 0xfd, 0x12, 0xb4, 0xc8, 0xb9, - 0xa5, 0x61, 0xbd, 0x09, 0xad, 0xa3, 0x38, 0x19, 0x1c, 0x05, 0x51, 0xae, 0x94, 0xbb, 0x5b, 0x78, - 0x9d, 0xbb, 0x34, 0x21, 0x9a, 0xc0, 0xfd, 0xf1, 0x1c, 0x2c, 0xbe, 0x1f, 0x1d, 0xc7, 0xc1, 0x90, - 0x32, 0xd3, 0x13, 0x3e, 0x89, 0xd5, 0xfd, 0x17, 0xfc, 0x8d, 0x53, 0x41, 0x67, 0xc1, 0x49, 0x2e, - 0x53, 0xcb, 0xaa, 0x88, 0xdb, 0x7d, 0x5a, 0xdc, 0x09, 0x13, 0xaa, 0x63, 0x20, 0xe8, 0xd8, 0xa7, - 0xe6, 0x85, 0x38, 0x59, 0x2a, 0x2e, 0x10, 0xcd, 0x1b, 0x17, 0x88, 0xe8, 0x1c, 0x43, 0x1c, 0x73, - 0x93, 0x7c, 0x2d, 0x79, 0xaa, 0x48, 0x81, 0x48, 0xca, 0x45, 0x96, 0x83, 0x1c, 0x87, 0x45, 0x19, - 0x88, 0x98, 0x20, 0x3a, 0x17, 0xe2, 0x03, 0x41, 0x23, 0x8c, 0xaf, 0x09, 0xa1, 0xb3, 0x55, 0xbe, - 0x53, 0xd7, 0x12, 0x32, 0x5f, 0x82, 0xd1, 0x42, 0x8f, 0xb8, 0x36, 0xa4, 0x62, 0x0c, 0x20, 0xee, - 0xbc, 0x95, 0x71, 0x23, 0x7c, 0x11, 0xc7, 0xf5, 0x2a, 0x7c, 0x41, 0x41, 0xf1, 0xc3, 0xf0, 0xc0, - 0x1f, 0x3e, 0xa1, 0x9b, 0x8e, 0x74, 0x3a, 0xdf, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x9d, - 0x84, 0x35, 0x3d, 0x13, 0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5, - 0x8c, 0xe9, 0x68, 0x45, 0x4d, 0x22, 0x33, 0x5b, 0xbe, 0x62, 0x1f, 0xa0, 0x7f, 0x03, 0xd8, 0xcd, - 0xd1, 0x48, 0xae, 0xb7, 0x0e, 0x19, 0x8b, 0x95, 0x72, 0xac, 0x95, 0xaa, 0x99, 0xb1, 0x46, 0xed, - 0x8c, 0xb9, 0x77, 0xa1, 0xbd, 0x67, 0x5c, 0x77, 0x24, 0xd1, 0x50, 0x17, 0x1d, 0xa5, 0x38, 0x19, - 0x88, 0xd1, 0x60, 0xc3, 0x6c, 0xd0, 0xfd, 0x65, 0x60, 0x0f, 0x82, 0x2c, 0xd7, 0xfd, 0x13, 0xcb, - 0xf1, 0x1a, 0x74, 0x74, 0x80, 0x5d, 0x1c, 0xef, 0xb7, 0x25, 0x46, 0xc7, 0xee, 0x37, 0xc5, 0xbd, - 0x80, 0xf2, 0xc0, 0xb6, 0x60, 0x29, 0x10, 0x50, 0x59, 0x13, 0x14, 0xa5, 0xae, 0x47, 0x7f, 0x4d, - 0x82, 0xd6, 0x2e, 0xfa, 0x03, 0x07, 0x16, 0xe5, 0xd0, 0xd0, 0xdb, 0xb0, 0x2e, 0x7a, 0x8a, 0x81, - 0x59, 0x58, 0xfd, 0xf5, 0xb8, 0xaa, 0x0c, 0xcf, 0xd5, 0xc9, 0x30, 0x83, 0x66, 0xe2, 0xe7, 0x47, - 0x14, 0xa0, 0xb4, 0x3c, 0xfa, 0xcd, 0x56, 0x45, 0xd0, 0x2c, 0x74, 0x85, 0x02, 0xe6, 0xba, 0x1b, - 0x99, 0xc2, 0x24, 0x57, 0x70, 0x1c, 0x14, 0x9d, 0xa4, 0x0b, 0x5c, 0x27, 0xc8, 0xe5, 0x2d, 0x85, - 0x02, 0x2e, 0xe6, 0x4b, 0xb2, 0x28, 0xcf, 0x97, 0x24, 0xf5, 0x74, 0xbd, 0xdb, 0x87, 0xde, 0x1d, - 0x1e, 0xf2, 0x9c, 0xdf, 0x0c, 0xc3, 0x32, 0xff, 0x0b, 0x70, 0xbe, 0xa6, 0x4e, 0x3a, 0x2d, 0xf7, - 0x60, 0xed, 0x0e, 0x3f, 0x98, 0x8e, 0x1f, 0xf0, 0xe3, 0xe2, 0x14, 0x8b, 0x41, 0x33, 0x3b, 0x8a, - 0x4f, 0xe4, 0xda, 0xd2, 0x6f, 0xf6, 0x0a, 0x40, 0x88, 0x34, 0x83, 0x2c, 0xe1, 0x43, 0x75, 0x31, - 0x8c, 0x90, 0xfd, 0x84, 0x0f, 0xdd, 0xb7, 0x81, 0x99, 0x7c, 0xe4, 0x10, 0xd0, 0x0e, 0x4c, 0x0f, - 0x06, 0xd9, 0x2c, 0xcb, 0xf9, 0x44, 0xdd, 0x78, 0x33, 0x21, 0xf7, 0x0a, 0x74, 0xf6, 0xfc, 0x99, - 0xc7, 0x3f, 0x96, 0x77, 0x6d, 0x31, 0x36, 0xf6, 0x67, 0x28, 0xca, 0x3a, 0x36, 0xa6, 0x6a, 0xf7, - 0x3f, 0x1b, 0xb0, 0x20, 0x28, 0x91, 0xeb, 0x88, 0x67, 0x79, 0x10, 0x89, 0x13, 0x1c, 0xc9, 0xd5, - 0x80, 0x2a, 0xb2, 0xd1, 0xa8, 0x91, 0x0d, 0xe9, 0xad, 0xaa, 0x4b, 0x36, 0x52, 0x08, 0x2c, 0x0c, - 0xdd, 0x9a, 0xe2, 0x64, 0x5c, 0x04, 0x67, 0x05, 0x50, 0x4a, 0x96, 0x14, 0xd6, 0x46, 0xf4, 0x4f, - 0x09, 0xad, 0x14, 0x07, 0x13, 0xaa, 0xb5, 0x69, 0x8b, 0x42, 0x6a, 0x2a, 0x36, 0xad, 0x62, 0xbb, - 0x96, 0x5e, 0xc2, 0x76, 0x09, 0x17, 0xf6, 0x79, 0xb6, 0x0b, 0x5e, 0xc2, 0x76, 0xb9, 0x0c, 0x56, - 0xef, 0x71, 0xee, 0x71, 0xdc, 0x15, 0x95, 0x38, 0x7d, 0xd7, 0x81, 0x55, 0xb9, 0xa1, 0xeb, 0x3a, - 0xf6, 0x9a, 0xb5, 0xfb, 0x3b, 0x75, 0x07, 0x01, 0xaf, 0xc3, 0x32, 0xed, 0xc9, 0x3a, 0x2b, 0x24, - 0x53, 0x58, 0x16, 0x88, 0xe3, 0x50, 0xa9, 0xed, 0x49, 0x10, 0xca, 0x45, 0x31, 0x21, 0x95, 0x58, - 0xc2, 0xf8, 0x98, 0x96, 0xc4, 0xf1, 0x74, 0xd9, 0xfd, 0x3b, 0x07, 0xd6, 0x8c, 0x0e, 0x4b, 0x29, - 0xbc, 0x01, 0xea, 0xe4, 0x5c, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b, - 0x98, 0x16, 0xd3, 0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2, - 0xf9, 0x13, 0x4d, 0x32, 0x47, 0x24, 0x16, 0x46, 0x07, 0xa3, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0x17, - 0xc8, 0x06, 0xdd, 0x7f, 0x74, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0xf7, 0x14, 0x17, 0x84, - 0x17, 0x2c, 0x34, 0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x8b, 0x2f, 0xe9, 0xc8, 0xea, 0x03, 0xf1, - 0x53, 0xd6, 0x62, 0xae, 0x6e, 0x2d, 0x9e, 0x33, 0xd3, 0x75, 0xc9, 0x92, 0xf9, 0xda, 0x64, 0xc9, - 0xad, 0x45, 0x98, 0xcf, 0x86, 0x71, 0xc2, 0xdd, 0x4d, 0xd8, 0xb0, 0x07, 0x27, 0x4d, 0xd0, 0xf7, - 0x1c, 0xe8, 0xdd, 0x13, 0xa9, 0xc3, 0x20, 0x1a, 0xef, 0x06, 0x59, 0x1e, 0xa7, 0xfa, 0x62, 0xf6, - 0x25, 0x80, 0x2c, 0xf7, 0xd3, 0x5c, 0x5c, 0x58, 0x92, 0x69, 0x8e, 0x02, 0xc1, 0x3e, 0xf2, 0x68, - 0x24, 0x6a, 0xc5, 0xda, 0xe8, 0x32, 0x2e, 0x0c, 0x1d, 0xd6, 0x0f, 0xe2, 0xc3, 0xc3, 0x8c, 0x6b, - 0xb7, 0xd5, 0xc4, 0x30, 0xf2, 0x45, 0x8d, 0xc7, 0x58, 0x8f, 0x1f, 0x93, 0xa9, 0x15, 0xfe, 0x60, - 0x09, 0x75, 0xff, 0xd6, 0x81, 0x95, 0xa2, 0x93, 0x77, 0x11, 0xb4, 0xad, 0x83, 0xe8, 0x9a, 0x61, - 0x1d, 0x54, 0x02, 0x26, 0x18, 0x0d, 0x82, 0x48, 0xf6, 0xcd, 0x40, 0x48, 0x63, 0x65, 0x29, 0x9e, - 0xaa, 0xcb, 0x61, 0x26, 0x24, 0x4e, 0x7e, 0x73, 0xfc, 0x5a, 0xdc, 0x0c, 0x93, 0x25, 0xba, 0x6f, - 0x36, 0xc9, 0xe9, 0xab, 0x05, 0xe1, 0x10, 0xcb, 0xa2, 0xda, 0x9f, 0x16, 0x09, 0xc5, 0x9f, 0xee, - 0x1f, 0x3b, 0x70, 0xbe, 0x66, 0x72, 0xa5, 0x66, 0xdc, 0x81, 0xb5, 0x43, 0x5d, 0xa9, 0x26, 0x40, - 0xa8, 0xc7, 0xa6, 0x94, 0xa2, 0xd2, 0xa0, 0xbd, 0xea, 0x07, 0xe8, 0x1e, 0x53, 0xde, 0x48, 0x4c, - 0xa9, 0x75, 0x69, 0xa2, 0x5a, 0xb1, 0xf3, 0xfd, 0x06, 0x74, 0xc5, 0x51, 0x85, 0x78, 0x9a, 0xc3, - 0x53, 0xf6, 0x01, 0x2c, 0xca, 0x87, 0x50, 0xec, 0xac, 0x6c, 0xd6, 0x7e, 0x7a, 0xd5, 0xdf, 0x2c, - 0xc3, 0x52, 0x76, 0xd6, 0x7f, 0xff, 0xb3, 0x7f, 0xf9, 0x93, 0xc6, 0x32, 0x6b, 0x6f, 0x1f, 0xbf, - 0xb5, 0x3d, 0xe6, 0x51, 0x86, 0x3c, 0x7e, 0x13, 0xa0, 0x78, 0x4b, 0xc4, 0x7a, 0xda, 0xc9, 0x28, - 0xbd, 0x7d, 0xea, 0x9f, 0xaf, 0xa9, 0x91, 0x7c, 0xcf, 0x13, 0xdf, 0x75, 0xb7, 0x8b, 0x7c, 0x83, - 0x28, 0xc8, 0xc5, 0xc3, 0xa2, 0x77, 0x9d, 0x2d, 0x36, 0x82, 0x8e, 0xf9, 0xa6, 0x88, 0xa9, 0x90, - 0xb9, 0xe6, 0xa1, 0x52, 0xff, 0x42, 0x6d, 0x9d, 0xca, 0x17, 0x50, 0x1b, 0x67, 0xdd, 0x55, 0x6c, - 0x63, 0x4a, 0x14, 0xba, 0x95, 0x9d, 0x7f, 0xba, 0x00, 0x2d, 0x9d, 0x76, 0x62, 0xdf, 0x86, 0x65, - 0xeb, 0x74, 0x87, 0x29, 0xc6, 0x75, 0x87, 0x41, 0xfd, 0x8b, 0xf5, 0x95, 0xb2, 0xd9, 0x4b, 0xd4, - 0x6c, 0x8f, 0x6d, 0x62, 0xb3, 0xf2, 0x78, 0x64, 0x9b, 0xce, 0xb4, 0xc4, 0xfd, 0xb2, 0x27, 0xd0, - 0xb5, 0x4f, 0x64, 0xd8, 0x45, 0xdb, 0xa0, 0x94, 0x5a, 0x7b, 0xe5, 0x94, 0x5a, 0xd9, 0xdc, 0x45, - 0x6a, 0x6e, 0x93, 0x6d, 0x98, 0xcd, 0xe9, 0x74, 0x10, 0xa7, 0x1b, 0x81, 0xe6, 0x63, 0x23, 0xf6, - 0x8a, 0x5e, 0xea, 0xba, 0x47, 0x48, 0x7a, 0xd1, 0xaa, 0x2f, 0x91, 0xdc, 0x1e, 0x35, 0xc5, 0x18, - 0x4d, 0xa8, 0xf9, 0xd6, 0x88, 0x7d, 0x04, 0x2d, 0xfd, 0xc0, 0x80, 0x9d, 0x33, 0x5e, 0x75, 0x98, - 0xaf, 0x1e, 0xfa, 0xbd, 0x6a, 0x45, 0xdd, 0x52, 0x99, 0x9c, 0x51, 0x20, 0x1e, 0xc0, 0x59, 0xe9, - 0xa4, 0x1e, 0xf0, 0x9f, 0x64, 0x24, 0x35, 0x4f, 0xa4, 0xae, 0x3b, 0xec, 0x06, 0x2c, 0xa9, 0x77, - 0x1b, 0x6c, 0xb3, 0xfe, 0xfd, 0x49, 0xff, 0x5c, 0x05, 0x97, 0xfa, 0x7c, 0x13, 0xa0, 0x78, 0x73, - 0xa0, 0x25, 0xbf, 0xf2, 0x12, 0x42, 0x4f, 0x62, 0xcd, 0x03, 0x85, 0x31, 0xbd, 0xb0, 0xb0, 0x9f, - 0x34, 0xb0, 0x57, 0x0b, 0xfa, 0xda, 0xc7, 0x0e, 0xcf, 0x61, 0xe8, 0x6e, 0xd2, 0xdc, 0xad, 0x32, - 0x52, 0xa5, 0x88, 0x9f, 0xa8, 0xbb, 0xb1, 0x77, 0xa0, 0x6d, 0xbc, 0x63, 0x60, 0x8a, 0x43, 0xf5, - 0x0d, 0x44, 0xbf, 0x5f, 0x57, 0x25, 0xbb, 0xfb, 0x15, 0x58, 0xb6, 0x1e, 0x24, 0x68, 0xcd, 0xa8, - 0x7b, 0xee, 0xa0, 0x35, 0xa3, 0xfe, 0x0d, 0xc3, 0x37, 0xa1, 0x6d, 0x3c, 0x1f, 0x60, 0xc6, 0x9d, - 0xa0, 0xd2, 0xc3, 0x01, 0xdd, 0xa3, 0xba, 0xd7, 0x06, 0x1b, 0x34, 0xde, 0xae, 0xdb, 0xc2, 0xf1, - 0xd2, 0x05, 0x51, 0x14, 0x92, 0x6f, 0x43, 0xd7, 0x7e, 0x50, 0xa0, 0xb5, 0xaa, 0xf6, 0x69, 0x82, - 0xd6, 0xaa, 0x53, 0x5e, 0x21, 0x48, 0x81, 0xdc, 0x5a, 0xd7, 0x8d, 0x6c, 0x7f, 0x2a, 0x0f, 0x5d, - 0x9e, 0xb1, 0xaf, 0xa3, 0xe9, 0x90, 0x37, 0x76, 0x59, 0xf1, 0x8c, 0xc2, 0xbe, 0xd7, 0xab, 0xa5, - 0xbd, 0x72, 0xb9, 0xd7, 0x5d, 0x23, 0xe6, 0x6d, 0x56, 0x8c, 0x40, 0x58, 0x68, 0xba, 0xb9, 0x6b, - 0x58, 0x68, 0xf3, 0x72, 0xaf, 0x61, 0xa1, 0xad, 0x0b, 0xbe, 0x65, 0x0b, 0x9d, 0x07, 0xc8, 0x23, - 0x82, 0x95, 0xd2, 0x3d, 0x00, 0xad, 0x2c, 0xf5, 0xb7, 0x88, 0xfa, 0x97, 0x9e, 0x7f, 0x7d, 0xc0, - 0x36, 0x33, 0xca, 0xbc, 0x6c, 0xab, 0x4b, 0x5f, 0xbf, 0x05, 0x1d, 0xf3, 0x22, 0xb8, 0xb6, 0xd9, - 0x35, 0xd7, 0xd7, 0xb5, 0xcd, 0xae, 0xbb, 0x39, 0xae, 0x16, 0x97, 0x75, 0xcc, 0x66, 0xd8, 0x37, - 0x61, 0xc5, 0xb8, 0x71, 0xb2, 0x3f, 0x8b, 0x86, 0x5a, 0x78, 0xaa, 0x77, 0x04, 0xfb, 0x75, 0xfe, - 0x99, 0x7b, 0x8e, 0x18, 0xaf, 0xb9, 0x16, 0x63, 0x14, 0x9c, 0xdb, 0xd0, 0x36, 0x6f, 0xb3, 0x3c, - 0x87, 0xef, 0x39, 0xa3, 0xca, 0xbc, 0x2e, 0x77, 0xdd, 0x61, 0x7f, 0xe6, 0x40, 0xc7, 0xbc, 0x7d, - 0xca, 0xac, 0x3c, 0x6f, 0x89, 0x4f, 0xcf, 0xac, 0x33, 0x19, 0xb9, 0x1e, 0x75, 0xf2, 0xc1, 0xd6, - 0x57, 0xac, 0x49, 0xfe, 0xd4, 0xf2, 0xf3, 0xaf, 0x95, 0xdf, 0xf8, 0x3d, 0x2b, 0x13, 0x98, 0xf7, - 0x28, 0x9f, 0x5d, 0x77, 0xd8, 0xbb, 0xe2, 0x1d, 0xa8, 0x8a, 0xeb, 0x99, 0x61, 0xdc, 0xca, 0x53, - 0x66, 0x3e, 0x99, 0xbc, 0xea, 0x5c, 0x77, 0xd8, 0xb7, 0xc4, 0x53, 0x3e, 0xf9, 0x2d, 0xcd, 0xfc, - 0xcb, 0x7e, 0xef, 0xbe, 0x4e, 0xa3, 0xb9, 0xe4, 0x9e, 0xb7, 0x46, 0x53, 0xb6, 0xee, 0x7b, 0x00, - 0x45, 0x92, 0x86, 0x95, 0x32, 0x16, 0xda, 0xee, 0x55, 0xf3, 0x38, 0xf6, 0x8a, 0xaa, 0xc4, 0x06, - 0x72, 0xfc, 0x48, 0x08, 0xa3, 0xa4, 0xcf, 0xf4, 0x92, 0x56, 0x93, 0x2d, 0xfd, 0x7e, 0x5d, 0x55, - 0x9d, 0x28, 0x2a, 0xfe, 0xec, 0x31, 0x2c, 0x3f, 0x88, 0xe3, 0x27, 0xd3, 0x44, 0xa7, 0x11, 0xed, - 0x9c, 0xc1, 0xae, 0x9f, 0x1d, 0xf5, 0x4b, 0xa3, 0x70, 0x2f, 0x13, 0xab, 0x3e, 0xeb, 0x19, 0xac, - 0xb6, 0x3f, 0x2d, 0x52, 0x44, 0xcf, 0x98, 0x0f, 0x6b, 0x7a, 0x8f, 0xd3, 0x1d, 0xef, 0xdb, 0x6c, - 0xcc, 0x4c, 0x4d, 0xa5, 0x09, 0xcb, 0xeb, 0x50, 0xbd, 0xdd, 0xce, 0x14, 0xcf, 0xeb, 0x0e, 0xdb, - 0x83, 0xce, 0x1d, 0x3e, 0x8c, 0x47, 0x5c, 0x46, 0xf9, 0xeb, 0x45, 0xc7, 0x75, 0x7a, 0xa0, 0xbf, - 0x6c, 0x81, 0xb6, 0xd6, 0x27, 0xfe, 0x2c, 0xe5, 0x1f, 0x6f, 0x7f, 0x2a, 0xf3, 0x07, 0xcf, 0x94, - 0xd6, 0xab, 0x9c, 0x87, 0xa5, 0xf5, 0xa5, 0x24, 0x89, 0xa5, 0xf5, 0x95, 0x24, 0x89, 0x35, 0xd5, - 0x2a, 0xe7, 0xc2, 0x42, 0x58, 0xab, 0xe4, 0x55, 0xf4, 0x4e, 0x79, 0x5a, 0x36, 0xa6, 0x7f, 0xf9, - 0x74, 0x02, 0xbb, 0xb5, 0x2d, 0xbb, 0xb5, 0x7d, 0x58, 0xbe, 0xc3, 0xc5, 0x64, 0x89, 0xb3, 0xca, - 0xbe, 0x6d, 0x46, 0xcc, 0x73, 0xcd, 0xb2, 0x89, 0xa1, 0x3a, 0xdb, 0xac, 0xd3, 0x41, 0x21, 0xfb, - 0x08, 0xda, 0xf7, 0x79, 0xae, 0x0e, 0x27, 0xb5, 0xbf, 0x51, 0x3a, 0xad, 0xec, 0xd7, 0x9c, 0x6d, - 0xda, 0x32, 0x43, 0xdc, 0xb6, 0xf9, 0x68, 0xcc, 0x85, 0xb2, 0x0f, 0x82, 0xd1, 0x33, 0xf6, 0xeb, - 0xc4, 0x5c, 0xdf, 0x67, 0xd8, 0x34, 0xce, 0xb4, 0x4c, 0xe6, 0x2b, 0x25, 0xbc, 0x8e, 0x73, 0x14, - 0x8f, 0xb8, 0xb1, 0xc1, 0x45, 0xd0, 0x36, 0x2e, 0xdb, 0x68, 0x05, 0xaa, 0x5e, 0xf0, 0xd1, 0x0a, - 0x54, 0x73, 0x37, 0xc7, 0xbd, 0x4a, 0xed, 0xb8, 0xec, 0x72, 0xd1, 0x8e, 0xb8, 0x8f, 0x53, 0xb4, - 0xb4, 0xfd, 0xa9, 0x3f, 0xc9, 0x9f, 0xb1, 0x0f, 0xe9, 0x29, 0x8b, 0x79, 0x00, 0x5b, 0xf8, 0x3b, - 0xe5, 0xb3, 0x5a, 0x3d, 0x59, 0x46, 0x95, 0xed, 0x03, 0x89, 0xa6, 0x68, 0x1f, 0xfc, 0x22, 0xc0, - 0x7e, 0x1e, 0x27, 0x77, 0x7c, 0x3e, 0x89, 0xa3, 0xc2, 0x72, 0x15, 0x87, 0x8c, 0x85, 0xe5, 0x32, - 0x4e, 0x1a, 0xd9, 0x87, 0x86, 0xc7, 0x69, 0x9d, 0x5f, 0x2b, 0xe1, 0x3a, 0xf5, 0x1c, 0x52, 0x4f, - 0x48, 0xcd, 0x59, 0xe4, 0x75, 0x07, 0xfd, 0xc7, 0x22, 0x8b, 0xa7, 0xfd, 0xc7, 0x4a, 0x82, 0x50, - 0x9b, 0xbd, 0x9a, 0x94, 0xdf, 0x1e, 0xb4, 0x8a, 0xb4, 0x90, 0xda, 0x92, 0xca, 0x49, 0x24, 0xbd, - 0xc7, 0x54, 0x92, 0x35, 0xee, 0x2a, 0x4d, 0x15, 0xb0, 0x25, 0x9c, 0x2a, 0xca, 0xc0, 0x04, 0xb0, - 0x2e, 0x3a, 0xa8, 0x37, 0x4c, 0x3a, 0x36, 0x53, 0x23, 0xa9, 0x49, 0x98, 0x68, 0x6d, 0xae, 0xcd, - 0x37, 0x58, 0xb1, 0x1d, 0x4a, 0xab, 0x38, 0xb2, 0x43, 0xd3, 0x3c, 0x81, 0xb5, 0x4a, 0xb0, 0xac, - 0x55, 0xfa, 0xb4, 0x1c, 0x85, 0x56, 0xe9, 0x53, 0xe3, 0x6c, 0xf7, 0x2c, 0x35, 0xb9, 0xe2, 0x02, - 0x36, 0x99, 0x9d, 0x04, 0xf9, 0xf0, 0xe8, 0x5d, 0x67, 0xeb, 0x60, 0x81, 0xfe, 0x69, 0xe4, 0xf3, - 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xc7, 0xfe, 0x7f, 0x9b, 0x44, 0x00, 0x00, + // 5611 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0xcb, 0x73, 0x1c, 0xc9, + 0x71, 0x37, 0x7b, 0x30, 0x78, 0x4c, 0xce, 0x60, 0x00, 0x14, 0x40, 0x70, 0x38, 0xe4, 0x72, 0xb9, + 0xad, 0x0d, 0x91, 0x1f, 0xbe, 0x35, 0xc1, 0x85, 0xa4, 0xf5, 0x6a, 0x69, 0x4b, 0xe6, 0x1b, 0x2b, + 0x71, 0x29, 0xa8, 0x41, 0x8a, 0xb6, 0x64, 0x7b, 0xd4, 0x98, 0x29, 0x0c, 0x7a, 0xd9, 0xd3, 0xdd, + 0xdb, 0xdd, 0x03, 0x70, 0x76, 0xcd, 0x08, 0xbf, 0xc2, 0x27, 0x2b, 0x7c, 0xb0, 0x2f, 0xb2, 0xc3, + 0xe1, 0x08, 0xe9, 0x62, 0x1f, 0x7c, 0xf4, 0x49, 0xf6, 0x3f, 0xe0, 0x08, 0x87, 0x0f, 0x7b, 0x52, + 0xf8, 0xe6, 0xc7, 0xc1, 0x56, 0xf8, 0xe2, 0x08, 0x5f, 0x7c, 0x70, 0x38, 0x32, 0xeb, 0xd1, 0x55, + 0xdd, 0x0d, 0x92, 0x7a, 0xd8, 0xb7, 0xa9, 0x5f, 0x65, 0x67, 0xbd, 0x32, 0xb3, 0x32, 0xb3, 0xaa, + 0x06, 0x5a, 0x69, 0x32, 0xbc, 0x96, 0xa4, 0x71, 0x1e, 0xb3, 0xf9, 0x30, 0x4a, 0x93, 0x61, 0xff, + 0xe2, 0x38, 0x8e, 0xc7, 0x21, 0xdf, 0xf6, 0x93, 0x60, 0xdb, 0x8f, 0xa2, 0x38, 0xf7, 0xf3, 0x20, + 0x8e, 0x32, 0x41, 0xe4, 0x7e, 0x1b, 0xba, 0xf7, 0x79, 0xb4, 0xcf, 0xf9, 0xc8, 0xe3, 0x1f, 0x4d, + 0x79, 0x96, 0xb3, 0xff, 0x0f, 0x6b, 0x3e, 0xff, 0x98, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39, + 0x4a, 0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0xde, + 0x80, 0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae, + 0x80, 0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24, + 0x47, 0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57, + 0x5b, 0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f, + 0xe8, 0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0x4f, 0x1c, 0x58, 0x7b, 0x3f, 0x0a, 0xf2, + 0x27, 0x7e, 0x18, 0xf2, 0x5c, 0x8d, 0xe9, 0x0a, 0xac, 0x9c, 0x10, 0x40, 0x63, 0x3a, 0x89, 0xd3, + 0x91, 0x1c, 0x51, 0x57, 0xc0, 0x7b, 0x12, 0x3d, 0xb5, 0x67, 0x8d, 0x53, 0x7b, 0x56, 0x3b, 0x5d, + 0x73, 0xf5, 0xd3, 0xe5, 0x6e, 0x00, 0x33, 0x3b, 0x27, 0xa6, 0xc3, 0xfd, 0x12, 0xac, 0x3f, 0x8e, + 0xc2, 0x78, 0xf8, 0xf4, 0x27, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b, + 0xd0, 0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0x67, 0x83, 0x23, + 0x3f, 0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34, + 0xab, 0x73, 0x9e, 0x2c, 0xb1, 0xb7, 0x60, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2, + 0x89, 0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, + 0x26, 0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, + 0x85, 0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, + 0xaa, 0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x67, 0xa1, + 0x3b, 0xe2, 0x59, 0x3e, 0xf0, 0x47, 0xa3, 0x94, 0x67, 0x19, 0xcf, 0x7a, 0x4b, 0xb4, 0x74, 0x25, + 0xd4, 0xed, 0xc1, 0xe6, 0x7d, 0x9e, 0x1b, 0xb3, 0x93, 0xc9, 0x69, 0x77, 0x1f, 0x00, 0x33, 0xe0, + 0x3b, 0x3c, 0xf7, 0x83, 0x30, 0x63, 0xef, 0x40, 0x27, 0x37, 0x88, 0x49, 0x54, 0xdb, 0x3b, 0xec, + 0x1a, 0xe9, 0xd8, 0x35, 0xe3, 0x03, 0xcf, 0xa2, 0x73, 0xff, 0xcb, 0x81, 0xf6, 0x3e, 0x8f, 0xb4, + 0x76, 0x31, 0x68, 0x62, 0x4f, 0xe4, 0x4a, 0xd2, 0x6f, 0xf6, 0x3a, 0xb4, 0xa9, 0x77, 0x59, 0x9e, + 0x06, 0xd1, 0x98, 0x96, 0xa0, 0xe5, 0x01, 0x42, 0xfb, 0x84, 0xb0, 0x55, 0x98, 0xf3, 0x27, 0x39, + 0x4d, 0xfc, 0x9c, 0x87, 0x3f, 0x51, 0xef, 0x12, 0x7f, 0x36, 0xe1, 0x51, 0x5e, 0x4c, 0x76, 0xc7, + 0x6b, 0x4b, 0x6c, 0x17, 0x67, 0xfb, 0x1a, 0xac, 0x9b, 0x24, 0x8a, 0xfb, 0x3c, 0x71, 0x5f, 0x33, + 0x28, 0x65, 0x23, 0x57, 0x60, 0x45, 0xd1, 0xa7, 0xa2, 0xb3, 0x34, 0xfd, 0x2d, 0xaf, 0x2b, 0x61, + 0x35, 0x84, 0xab, 0xb0, 0x7a, 0x18, 0x44, 0x7e, 0x38, 0x18, 0x86, 0xf9, 0xf1, 0x60, 0xc4, 0xc3, + 0xdc, 0xa7, 0x85, 0x98, 0xf7, 0xba, 0x84, 0xdf, 0x0e, 0xf3, 0xe3, 0x3b, 0x88, 0xba, 0x7f, 0xe4, + 0x40, 0x47, 0x0c, 0x5e, 0x2a, 0xfe, 0x9b, 0xb0, 0xac, 0xda, 0xe0, 0x69, 0x1a, 0xa7, 0x52, 0x0e, + 0x6d, 0x90, 0x6d, 0xc1, 0xaa, 0x02, 0x92, 0x94, 0x07, 0x13, 0x7f, 0xcc, 0xa5, 0xb6, 0x57, 0x70, + 0xb6, 0x53, 0x70, 0x4c, 0xe3, 0x69, 0x2e, 0x54, 0xaf, 0xbd, 0xd3, 0x91, 0x0b, 0xe3, 0x21, 0xe6, + 0xd9, 0x24, 0xee, 0xf7, 0x1c, 0xe8, 0xdc, 0x3e, 0xf2, 0xa3, 0x88, 0x87, 0x7b, 0x71, 0x10, 0xe5, + 0xec, 0x3a, 0xb0, 0xc3, 0x69, 0x34, 0x0a, 0xa2, 0xf1, 0x20, 0x7f, 0x16, 0x8c, 0x06, 0x07, 0xb3, + 0x9c, 0x67, 0x62, 0x89, 0x76, 0xcf, 0x78, 0x35, 0x75, 0xec, 0x2d, 0x58, 0xb5, 0xd0, 0x2c, 0x4f, + 0xc5, 0xba, 0xed, 0x9e, 0xf1, 0x2a, 0x35, 0x28, 0xf8, 0xf1, 0x34, 0x4f, 0xa6, 0xf9, 0x20, 0x88, + 0x46, 0xfc, 0x19, 0xf5, 0x71, 0xd9, 0xb3, 0xb0, 0x5b, 0x5d, 0xe8, 0x98, 0xdf, 0xb9, 0x5f, 0x82, + 0xd5, 0x07, 0xa8, 0x11, 0x51, 0x10, 0x8d, 0x6f, 0x0a, 0xb1, 0x45, 0x35, 0x4d, 0xa6, 0x07, 0x4f, + 0xf9, 0x4c, 0xce, 0x9b, 0x2c, 0xa1, 0x50, 0x1d, 0xc5, 0x59, 0x2e, 0x25, 0x87, 0x7e, 0xbb, 0xff, + 0xe4, 0xc0, 0x0a, 0xce, 0xfd, 0x07, 0x7e, 0x34, 0x53, 0x2b, 0xf7, 0x00, 0x3a, 0xc8, 0xea, 0x51, + 0x7c, 0x53, 0x28, 0xbb, 0x10, 0xe2, 0xab, 0x72, 0xae, 0x4a, 0xd4, 0xd7, 0x4c, 0x52, 0x34, 0xe6, + 0x33, 0xcf, 0xfa, 0x1a, 0xc5, 0x36, 0xf7, 0xd3, 0x31, 0xcf, 0xc9, 0x0c, 0x48, 0xb3, 0x00, 0x02, + 0xba, 0x1d, 0x47, 0x87, 0xec, 0x32, 0x74, 0x32, 0x3f, 0x1f, 0x24, 0x3c, 0xa5, 0x59, 0x23, 0xd1, + 0x9b, 0xf3, 0x20, 0xf3, 0xf3, 0x3d, 0x9e, 0xde, 0x9a, 0xe5, 0xbc, 0xff, 0x65, 0x58, 0xab, 0xb4, + 0x82, 0xd2, 0x5e, 0x0c, 0x11, 0x7f, 0xb2, 0x0d, 0x98, 0x3f, 0xf6, 0xc3, 0x29, 0x97, 0xd6, 0x49, + 0x14, 0xde, 0x6b, 0xbc, 0xeb, 0xb8, 0x9f, 0x85, 0xd5, 0xa2, 0xdb, 0x52, 0xc8, 0x18, 0x34, 0x71, + 0x06, 0x25, 0x03, 0xfa, 0xed, 0xfe, 0x96, 0x23, 0x08, 0x6f, 0xc7, 0x81, 0xd6, 0x74, 0x24, 0x44, + 0x83, 0xa0, 0x08, 0xf1, 0xf7, 0xa9, 0x96, 0xf0, 0xa7, 0x1f, 0xac, 0x7b, 0x05, 0xd6, 0x8c, 0x2e, + 0xbc, 0xa0, 0xb3, 0xdf, 0x71, 0x60, 0xed, 0x21, 0x3f, 0x91, 0xab, 0xae, 0x7a, 0xfb, 0x2e, 0x34, + 0xf3, 0x59, 0x22, 0xb6, 0xe2, 0xee, 0xce, 0x9b, 0x72, 0xd1, 0x2a, 0x74, 0xd7, 0x64, 0xf1, 0xd1, + 0x2c, 0xe1, 0x1e, 0x7d, 0xe1, 0x7e, 0x09, 0xda, 0x06, 0xc8, 0xce, 0xc1, 0xfa, 0x93, 0xf7, 0x1f, + 0x3d, 0xbc, 0xbb, 0xbf, 0x3f, 0xd8, 0x7b, 0x7c, 0xeb, 0xab, 0x77, 0x7f, 0x65, 0xb0, 0x7b, 0x73, + 0x7f, 0x77, 0xf5, 0x0c, 0xdb, 0x04, 0xf6, 0xf0, 0xee, 0xfe, 0xa3, 0xbb, 0x77, 0x2c, 0xdc, 0x71, + 0xfb, 0xd0, 0x7b, 0xc8, 0x4f, 0x9e, 0x04, 0x79, 0xc4, 0xb3, 0xcc, 0x6e, 0xcd, 0xbd, 0x06, 0xcc, + 0xec, 0x82, 0x1c, 0x55, 0x0f, 0x16, 0xa5, 0xa9, 0x55, 0x3b, 0x8d, 0x2c, 0xba, 0x9f, 0x05, 0xb6, + 0x1f, 0x8c, 0xa3, 0x0f, 0x78, 0x96, 0xf9, 0x63, 0xae, 0xc6, 0xb6, 0x0a, 0x73, 0x93, 0x6c, 0x2c, + 0x8d, 0x22, 0xfe, 0x74, 0x3f, 0x07, 0xeb, 0x16, 0x9d, 0x64, 0x7c, 0x11, 0x5a, 0x59, 0x30, 0x8e, + 0xfc, 0x7c, 0x9a, 0x72, 0xc9, 0xba, 0x00, 0xdc, 0x7b, 0xb0, 0xf1, 0x0d, 0x9e, 0x06, 0x87, 0xb3, + 0x97, 0xb1, 0xb7, 0xf9, 0x34, 0xca, 0x7c, 0xee, 0xc2, 0xd9, 0x12, 0x1f, 0xd9, 0xbc, 0x10, 0x44, + 0xb9, 0x5c, 0x4b, 0x9e, 0x28, 0x18, 0x6a, 0xd9, 0x30, 0xd5, 0xd2, 0x7d, 0x0c, 0xec, 0x76, 0x1c, + 0x45, 0x7c, 0x98, 0xef, 0x71, 0x9e, 0x16, 0xfe, 0x55, 0x21, 0x75, 0xed, 0x9d, 0x73, 0x72, 0x1d, + 0xcb, 0xba, 0x2e, 0xc5, 0x91, 0x41, 0x33, 0xe1, 0xe9, 0x84, 0x18, 0x2f, 0x79, 0xf4, 0xdb, 0x3d, + 0x0b, 0xeb, 0x16, 0x5b, 0xb9, 0xdb, 0xbf, 0x0d, 0x67, 0xef, 0x04, 0xd9, 0xb0, 0xda, 0x60, 0x0f, + 0x16, 0x93, 0xe9, 0xc1, 0xa0, 0xd0, 0x29, 0x55, 0xc4, 0x4d, 0xb0, 0xfc, 0x89, 0x64, 0xf6, 0x7b, + 0x0e, 0x34, 0x77, 0x1f, 0x3d, 0xb8, 0xcd, 0xfa, 0xb0, 0x14, 0x44, 0xc3, 0x78, 0x82, 0x5b, 0x87, + 0x18, 0xb4, 0x2e, 0x9f, 0xaa, 0x2b, 0x17, 0xa1, 0x45, 0x3b, 0x0e, 0xee, 0xeb, 0xd2, 0x15, 0x2a, + 0x00, 0xf4, 0x29, 0xf8, 0xb3, 0x24, 0x48, 0xc9, 0x69, 0x50, 0xae, 0x40, 0x93, 0x2c, 0x62, 0xb5, + 0xc2, 0xfd, 0xef, 0x26, 0x2c, 0x4a, 0x5b, 0x4d, 0xed, 0x0d, 0xf3, 0xe0, 0x98, 0xcb, 0x9e, 0xc8, + 0x12, 0xee, 0x2a, 0x29, 0x9f, 0xc4, 0x39, 0x1f, 0x58, 0xcb, 0x60, 0x83, 0x48, 0x35, 0x14, 0x8c, + 0x06, 0x09, 0x5a, 0x7d, 0xea, 0x59, 0xcb, 0xb3, 0x41, 0x9c, 0x2c, 0x04, 0x06, 0xc1, 0x88, 0xfa, + 0xd4, 0xf4, 0x54, 0x11, 0x67, 0x62, 0xe8, 0x27, 0xfe, 0x30, 0xc8, 0x67, 0x52, 0xb9, 0x75, 0x19, + 0x79, 0x87, 0xf1, 0xd0, 0x0f, 0x07, 0x07, 0x7e, 0xe8, 0x47, 0x43, 0x2e, 0x1d, 0x17, 0x1b, 0x44, + 0xdf, 0x44, 0x76, 0x49, 0x91, 0x09, 0xff, 0xa5, 0x84, 0xa2, 0x8f, 0x33, 0x8c, 0x27, 0x93, 0x20, + 0x47, 0x97, 0xa6, 0xb7, 0x24, 0x0c, 0x49, 0x81, 0xd0, 0x48, 0x44, 0xe9, 0x44, 0xcc, 0x5e, 0x4b, + 0xb4, 0x66, 0x81, 0xc8, 0xe5, 0x90, 0x73, 0x32, 0x48, 0x4f, 0x4f, 0x7a, 0x20, 0xb8, 0x14, 0x08, + 0xae, 0xc3, 0x34, 0xca, 0x78, 0x9e, 0x87, 0x7c, 0xa4, 0x3b, 0xd4, 0x26, 0xb2, 0x6a, 0x05, 0xbb, + 0x0e, 0xeb, 0xc2, 0xcb, 0xca, 0xfc, 0x3c, 0xce, 0x8e, 0x82, 0x6c, 0x90, 0xf1, 0x28, 0xef, 0x75, + 0x88, 0xbe, 0xae, 0x8a, 0xbd, 0x0b, 0xe7, 0x4a, 0x70, 0xca, 0x87, 0x3c, 0x38, 0xe6, 0xa3, 0xde, + 0x32, 0x7d, 0x75, 0x5a, 0x35, 0xbb, 0x0c, 0x6d, 0x74, 0x2e, 0xa7, 0xc9, 0xc8, 0xc7, 0x7d, 0xb8, + 0x4b, 0xeb, 0x60, 0x42, 0xec, 0x6d, 0x58, 0x4e, 0xb8, 0xd8, 0x2c, 0x8f, 0xf2, 0x70, 0x98, 0xf5, + 0x56, 0x68, 0x27, 0x6b, 0x4b, 0x65, 0x42, 0xc9, 0xf5, 0x6c, 0x0a, 0x14, 0xca, 0x61, 0x46, 0xee, + 0x8a, 0x3f, 0xeb, 0xad, 0x92, 0xb8, 0x15, 0x00, 0xe9, 0x48, 0x1a, 0x1c, 0xfb, 0x39, 0xef, 0xad, + 0x91, 0x6c, 0xa9, 0xa2, 0xfb, 0x67, 0x0e, 0xac, 0x3f, 0x08, 0xb2, 0x5c, 0x0a, 0xa1, 0x36, 0xc7, + 0xaf, 0x43, 0x5b, 0x88, 0xdf, 0x20, 0x8e, 0xc2, 0x99, 0x94, 0x48, 0x10, 0xd0, 0xd7, 0xa2, 0x70, + 0xc6, 0x3e, 0x03, 0xcb, 0x41, 0x64, 0x92, 0x08, 0x1d, 0xee, 0x28, 0x90, 0x88, 0x5e, 0x87, 0x76, + 0x32, 0x3d, 0x08, 0x83, 0xa1, 0x20, 0x99, 0x13, 0x5c, 0x04, 0x44, 0x04, 0xe8, 0xe8, 0x89, 0x9e, + 0x08, 0x8a, 0x26, 0x51, 0xb4, 0x25, 0x86, 0x24, 0xee, 0x2d, 0xd8, 0xb0, 0x3b, 0x28, 0x8d, 0xd5, + 0x16, 0x2c, 0x49, 0xd9, 0xce, 0x7a, 0x6d, 0x9a, 0x9f, 0xae, 0x9c, 0x1f, 0x49, 0xea, 0xe9, 0x7a, + 0xf7, 0xdf, 0x1c, 0x68, 0xa2, 0x01, 0x38, 0xdd, 0x58, 0x98, 0x36, 0x7d, 0xce, 0xb2, 0xe9, 0xe4, + 0xf7, 0xa3, 0x57, 0x24, 0x44, 0x42, 0xa8, 0x8d, 0x81, 0x14, 0xf5, 0x29, 0x1f, 0x1e, 0x93, 0xee, + 0xe8, 0x7a, 0x44, 0x50, 0xb3, 0x70, 0xeb, 0xa4, 0xaf, 0x85, 0xe2, 0xe8, 0xb2, 0xaa, 0xa3, 0x2f, + 0x17, 0x8b, 0x3a, 0xfa, 0xae, 0x07, 0x8b, 0x41, 0x74, 0x10, 0x4f, 0xa3, 0x11, 0x29, 0xc9, 0x92, + 0xa7, 0x8a, 0xb8, 0xd8, 0x09, 0x79, 0x52, 0xc1, 0x84, 0x4b, 0xed, 0x28, 0x00, 0x97, 0xa1, 0x6b, + 0x95, 0x91, 0xc1, 0xd3, 0xfb, 0xd8, 0x3b, 0xb0, 0x66, 0x60, 0x72, 0x06, 0xdf, 0x80, 0xf9, 0x04, + 0x01, 0xe9, 0x28, 0x29, 0xf1, 0x22, 0x4b, 0x29, 0x6a, 0xdc, 0x55, 0x8c, 0x9f, 0xf3, 0xf7, 0xa3, + 0xc3, 0x58, 0x71, 0xfa, 0xe1, 0x1c, 0x06, 0xbc, 0x12, 0x92, 0x8c, 0xae, 0xc2, 0x4a, 0x30, 0xe2, + 0x51, 0x1e, 0xe4, 0xb3, 0x81, 0xe5, 0xc1, 0x95, 0x61, 0xdc, 0x61, 0xfc, 0x30, 0xf0, 0x33, 0x69, + 0xc3, 0x44, 0x81, 0xed, 0xc0, 0x06, 0x8a, 0xbf, 0x92, 0x68, 0xbd, 0xac, 0xc2, 0x91, 0xac, 0xad, + 0x43, 0x8d, 0x45, 0x5c, 0x4a, 0xa0, 0xfe, 0x44, 0x58, 0xda, 0xba, 0x2a, 0x9c, 0x35, 0xc1, 0x09, + 0x87, 0x3c, 0x2f, 0x54, 0x44, 0x03, 0x95, 0xe8, 0x6d, 0x41, 0x38, 0xb1, 0xe5, 0xe8, 0xcd, 0x88, + 0x00, 0x97, 0x2a, 0x11, 0xe0, 0x55, 0x58, 0xc9, 0x66, 0xd1, 0x90, 0x8f, 0x06, 0x79, 0x8c, 0xed, + 0x06, 0x11, 0xad, 0xce, 0x92, 0x57, 0x86, 0x29, 0x56, 0xe5, 0x59, 0x1e, 0xf1, 0x9c, 0x4c, 0xd7, + 0x92, 0xa7, 0x8a, 0xb8, 0x0b, 0x10, 0x89, 0x10, 0xea, 0x96, 0x27, 0x4b, 0xb8, 0x55, 0x4e, 0xd3, + 0x20, 0xeb, 0x75, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xf6, 0x00, 0x23, 0xab, 0x23, 0xee, 0x8f, + 0x78, 0x4a, 0xab, 0x2f, 0x02, 0x4b, 0x61, 0x81, 0xea, 0x2b, 0xb1, 0xed, 0x63, 0x9e, 0x66, 0x41, + 0x1c, 0x91, 0xed, 0x69, 0x79, 0xaa, 0xe8, 0x7e, 0x4c, 0x3b, 0xba, 0x0e, 0x79, 0x1f, 0x93, 0x39, + 0x62, 0x17, 0xa0, 0x25, 0xc6, 0x98, 0x1d, 0xf9, 0xd2, 0xc9, 0x58, 0x22, 0x60, 0xff, 0xc8, 0x47, + 0x05, 0xb6, 0xa6, 0xad, 0x41, 0x9e, 0x63, 0x9b, 0xb0, 0x5d, 0x31, 0x6b, 0x6f, 0x42, 0x57, 0x05, + 0xd3, 0xd9, 0x20, 0xe4, 0x87, 0xb9, 0x0a, 0x10, 0xa2, 0xe9, 0x04, 0x9b, 0xcb, 0x1e, 0xf0, 0xc3, + 0xdc, 0x7d, 0x08, 0x6b, 0x52, 0x6f, 0xbf, 0x96, 0x70, 0xd5, 0xf4, 0x17, 0xcb, 0x9b, 0x9a, 0xf0, + 0x2a, 0xd6, 0x6d, 0x45, 0xa7, 0x28, 0xa7, 0xb4, 0xd3, 0xb9, 0x1e, 0x30, 0x59, 0x7d, 0x3b, 0x8c, + 0x33, 0x2e, 0x19, 0xba, 0xd0, 0x19, 0x86, 0x71, 0xa6, 0xc2, 0x10, 0x39, 0x1c, 0x0b, 0xc3, 0xf9, + 0xc9, 0xa6, 0xc3, 0x21, 0x5a, 0x02, 0x61, 0xd3, 0x54, 0xd1, 0xfd, 0x73, 0x07, 0xd6, 0x89, 0x9b, + 0xb2, 0x30, 0xda, 0x77, 0x7d, 0xf5, 0x6e, 0x76, 0x86, 0x66, 0x68, 0xb6, 0x01, 0xf3, 0x87, 0x71, + 0x3a, 0xe4, 0xb2, 0x25, 0x51, 0xf8, 0xf1, 0xbd, 0xf1, 0x66, 0xc5, 0x1b, 0xff, 0xa1, 0x03, 0x6b, + 0xd4, 0xd5, 0xfd, 0xdc, 0xcf, 0xa7, 0x99, 0x1c, 0xfe, 0x2f, 0xc0, 0x32, 0x0e, 0x95, 0x2b, 0x75, + 0x92, 0x1d, 0xdd, 0xd0, 0x9a, 0x4f, 0xa8, 0x20, 0xde, 0x3d, 0xe3, 0xd9, 0xc4, 0xec, 0xcb, 0xd0, + 0x31, 0x33, 0x22, 0xd4, 0xe7, 0xf6, 0xce, 0x79, 0x35, 0xca, 0x8a, 0xe4, 0xec, 0x9e, 0xf1, 0xac, + 0x0f, 0xd8, 0x0d, 0x00, 0x72, 0x37, 0x88, 0xad, 0x0c, 0x65, 0xcf, 0xdb, 0x93, 0x64, 0x2c, 0xd6, + 0xee, 0x19, 0xcf, 0x20, 0xbf, 0xb5, 0x04, 0x0b, 0x62, 0x7f, 0x74, 0xef, 0xc3, 0xb2, 0xd5, 0x53, + 0x2b, 0xca, 0xe8, 0x88, 0x28, 0xa3, 0x12, 0x94, 0x36, 0xaa, 0x41, 0xa9, 0xfb, 0x2f, 0x0d, 0x60, + 0x28, 0x6d, 0xa5, 0xe5, 0xc4, 0x0d, 0x3a, 0x1e, 0x59, 0xee, 0x56, 0xc7, 0x33, 0x21, 0x76, 0x0d, + 0x98, 0x51, 0x54, 0xb9, 0x07, 0xb1, 0x6f, 0xd4, 0xd4, 0xa0, 0x81, 0x13, 0xbe, 0x92, 0x8a, 0x81, + 0xa5, 0x63, 0x29, 0xd6, 0xad, 0xb6, 0x0e, 0xb7, 0x86, 0x64, 0x9a, 0x1d, 0xa1, 0x03, 0xa1, 0x1c, + 0x32, 0x55, 0x2e, 0x0b, 0xc8, 0xc2, 0x4b, 0x05, 0x64, 0xb1, 0x2c, 0x20, 0xa6, 0x4b, 0xb0, 0x64, + 0xb9, 0x04, 0xe8, 0x7f, 0x4d, 0x82, 0x88, 0xfc, 0x8a, 0xc1, 0x04, 0x5b, 0x97, 0xfe, 0x97, 0x05, + 0xb2, 0x2d, 0x58, 0x95, 0x7e, 0x5d, 0xe1, 0x77, 0x00, 0xcd, 0x71, 0x05, 0x77, 0x3f, 0x75, 0x60, + 0x15, 0xe7, 0xd9, 0x92, 0xc5, 0xf7, 0x80, 0x54, 0xe1, 0x15, 0x45, 0xd1, 0xa2, 0xfd, 0xe9, 0x25, + 0xf1, 0x5d, 0x68, 0x11, 0xc3, 0x38, 0xe1, 0x91, 0x14, 0xc4, 0x9e, 0x2d, 0x88, 0x85, 0x15, 0xda, + 0x3d, 0xe3, 0x15, 0xc4, 0x86, 0x18, 0xfe, 0xbd, 0x03, 0x6d, 0xd9, 0xcd, 0x9f, 0x38, 0x96, 0xe8, + 0xc3, 0x12, 0x4a, 0xa4, 0xe1, 0xb0, 0xeb, 0x32, 0xee, 0x26, 0x13, 0x0c, 0xd8, 0x70, 0xfb, 0xb4, + 0xe2, 0x88, 0x32, 0x8c, 0x7b, 0x21, 0x19, 0xdc, 0x6c, 0x90, 0x07, 0xe1, 0x40, 0xd5, 0xca, 0x04, + 0x64, 0x5d, 0x15, 0xda, 0x9d, 0x2c, 0xf7, 0xc7, 0x5c, 0x6e, 0x73, 0xa2, 0x80, 0x01, 0x93, 0x1c, + 0x50, 0xc9, 0x1d, 0x74, 0xff, 0xa6, 0x03, 0xe7, 0x2a, 0x55, 0x3a, 0xdd, 0x2d, 0x1d, 0xe4, 0x30, + 0x98, 0x1c, 0xc4, 0xda, 0xd7, 0x76, 0x4c, 0xdf, 0xd9, 0xaa, 0x62, 0x63, 0x38, 0xab, 0xf6, 0x73, + 0x9c, 0xd3, 0x62, 0xf7, 0x6e, 0x90, 0x23, 0xf2, 0xb6, 0x2d, 0x03, 0xe5, 0x06, 0x15, 0x6e, 0x6a, + 0x6e, 0x3d, 0x3f, 0x76, 0x04, 0x3d, 0xed, 0x38, 0x48, 0x13, 0x6f, 0x38, 0x17, 0xd8, 0xd6, 0x5b, + 0x2f, 0x69, 0x8b, 0xec, 0xd1, 0x48, 0x35, 0x73, 0x2a, 0x37, 0x36, 0x83, 0x4b, 0xaa, 0x8e, 0x6c, + 0x78, 0xb5, 0xbd, 0xe6, 0x2b, 0x8d, 0xed, 0x1e, 0x7e, 0x6c, 0x37, 0xfa, 0x12, 0xc6, 0xec, 0x43, + 0xd8, 0x3c, 0xf1, 0x83, 0x5c, 0x75, 0xcb, 0x70, 0x86, 0xe6, 0xa9, 0xc9, 0x9d, 0x97, 0x34, 0xf9, + 0x44, 0x7c, 0x6c, 0x6d, 0x6c, 0xa7, 0x70, 0xec, 0xff, 0xad, 0x03, 0x5d, 0x9b, 0x0f, 0x8a, 0xa9, + 0x54, 0x78, 0x65, 0xf8, 0x94, 0xf3, 0x57, 0x82, 0xab, 0x21, 0x6a, 0xa3, 0x2e, 0x44, 0x35, 0x03, + 0xd1, 0xb9, 0x97, 0x05, 0xa2, 0xcd, 0x57, 0x0b, 0x44, 0xe7, 0xeb, 0x02, 0xd1, 0xfe, 0x7f, 0x3a, + 0xc0, 0xaa, 0xb2, 0xc4, 0xee, 0x8b, 0x18, 0x39, 0xe2, 0xa1, 0xb4, 0x49, 0x3f, 0xf7, 0x6a, 0xf2, + 0xa8, 0xe6, 0x4e, 0x7d, 0x8d, 0x8a, 0x61, 0x1a, 0x1d, 0xd3, 0x45, 0x5a, 0xf6, 0xea, 0xaa, 0x4a, + 0xa1, 0x71, 0xf3, 0xe5, 0xa1, 0xf1, 0xfc, 0xcb, 0x43, 0xe3, 0x85, 0x72, 0x68, 0xdc, 0xff, 0x5d, + 0x07, 0xd6, 0x6b, 0x16, 0xfd, 0x67, 0x37, 0x70, 0x5c, 0x26, 0xcb, 0x16, 0x34, 0xe4, 0x32, 0x99, + 0x60, 0xff, 0x37, 0x60, 0xd9, 0x12, 0xf4, 0x9f, 0x5d, 0xfb, 0x65, 0x2f, 0x4f, 0xc8, 0x99, 0x85, + 0xf5, 0x7f, 0xd4, 0x00, 0x56, 0x55, 0xb6, 0xff, 0xd3, 0x3e, 0x54, 0xe7, 0x69, 0xae, 0x66, 0x9e, + 0xfe, 0x57, 0xf7, 0x81, 0xb7, 0x60, 0x2d, 0xe5, 0xc3, 0xf8, 0x98, 0xce, 0x1d, 0xed, 0xec, 0x4e, + 0xb5, 0x02, 0xfd, 0x5c, 0x3b, 0x2f, 0xb1, 0x64, 0x1d, 0x13, 0x19, 0x9b, 0x61, 0x29, 0x3d, 0xe1, + 0x6e, 0xc2, 0x86, 0x38, 0xbd, 0xbb, 0x25, 0x58, 0xa9, 0x7d, 0xe5, 0x4f, 0x1d, 0x38, 0x5b, 0xaa, + 0x28, 0xce, 0x52, 0xc4, 0xd6, 0x61, 0xef, 0x27, 0x36, 0x88, 0xfd, 0x97, 0x7a, 0x64, 0xf4, 0x5f, + 0x48, 0x5b, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac, 0xd7, 0x55, 0xb9, 0xe7, 0xe0, + 0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x24, 0x87, 0xed, 0x2e, 0xab, 0x22, + 0x7a, 0x81, 0xd6, 0x36, 0x65, 0xf7, 0xb7, 0xb6, 0xce, 0xfd, 0x75, 0x60, 0x5f, 0x9f, 0xf2, 0x74, + 0x46, 0x27, 0x3d, 0x3a, 0x3b, 0x73, 0xae, 0x9c, 0xc6, 0x58, 0x48, 0xa6, 0x07, 0x5f, 0xe5, 0x33, + 0x75, 0x94, 0xd6, 0x28, 0x8e, 0xd2, 0x5e, 0x03, 0xc0, 0xe8, 0x8b, 0x8e, 0x86, 0xd4, 0xe1, 0x26, + 0x86, 0xbd, 0x82, 0xa1, 0x7b, 0x03, 0xd6, 0x2d, 0xfe, 0x7a, 0xf6, 0x17, 0xe4, 0x17, 0x22, 0x37, + 0x60, 0x1f, 0x38, 0xc9, 0x3a, 0xf7, 0xdf, 0x1d, 0x98, 0xdb, 0x8d, 0x13, 0x33, 0xab, 0xe8, 0xd8, + 0x59, 0x45, 0x69, 0xf2, 0x07, 0xda, 0xa2, 0x4b, 0x4b, 0x60, 0x81, 0x6c, 0x0b, 0xba, 0xfe, 0x24, + 0xc7, 0xe8, 0xf8, 0x30, 0x4e, 0x4f, 0xfc, 0x74, 0x24, 0x96, 0xe4, 0x56, 0xa3, 0xe7, 0x78, 0xa5, + 0x1a, 0xb6, 0x01, 0x73, 0xda, 0x36, 0x12, 0x01, 0x16, 0xd1, 0xbf, 0xa2, 0xe4, 0xea, 0x4c, 0x06, + 0xf6, 0xb2, 0x84, 0x2b, 0x6e, 0x7f, 0x2f, 0x3c, 0x5a, 0x21, 0xe1, 0x75, 0x55, 0xb8, 0xfd, 0xa0, + 0xa9, 0x24, 0x32, 0x99, 0x91, 0x51, 0x65, 0xf7, 0x5f, 0x1d, 0x98, 0xa7, 0x19, 0x40, 0x9d, 0x14, + 0x82, 0x48, 0x67, 0xb7, 0x94, 0x09, 0x76, 0x84, 0x4e, 0x96, 0x60, 0xe6, 0x5a, 0x27, 0xba, 0x0d, + 0xdd, 0x6d, 0xf3, 0x54, 0xf7, 0x32, 0xb4, 0x44, 0x49, 0x1f, 0x83, 0x12, 0x49, 0x01, 0xb2, 0x4b, + 0xd0, 0x3c, 0x8a, 0x13, 0xe5, 0x44, 0x80, 0x4a, 0x04, 0xc6, 0x89, 0x47, 0x78, 0xd1, 0x1f, 0xe4, + 0x27, 0x3a, 0x2f, 0xb6, 0x86, 0x32, 0x8c, 0x9b, 0xa3, 0x66, 0x6b, 0x4e, 0x46, 0x09, 0x75, 0xb7, + 0x60, 0xe5, 0x61, 0x3c, 0xe2, 0x46, 0xea, 0xe7, 0x54, 0xa9, 0x73, 0x7f, 0xd3, 0x81, 0x25, 0x45, + 0xcc, 0xae, 0x42, 0x13, 0x77, 0xfc, 0x92, 0x3f, 0xaf, 0x0f, 0x00, 0x90, 0xce, 0x23, 0x0a, 0x34, + 0x91, 0x94, 0x18, 0x28, 0xbc, 0x3f, 0x95, 0x16, 0x28, 0x9c, 0x1b, 0xdd, 0xdd, 0x92, 0x4f, 0x50, + 0x42, 0xdd, 0xbf, 0x70, 0x60, 0xd9, 0x6a, 0x03, 0xa3, 0xb8, 0xd0, 0xcf, 0x72, 0x99, 0x54, 0x95, + 0xcb, 0x63, 0x42, 0x66, 0x32, 0xb0, 0x61, 0x27, 0x03, 0x75, 0x9a, 0x6a, 0xce, 0x4c, 0x53, 0x5d, + 0x87, 0x56, 0x71, 0xee, 0xde, 0xb4, 0x4c, 0x1f, 0xb6, 0xa8, 0x8e, 0x36, 0x0a, 0x22, 0xe4, 0x33, + 0x8c, 0xc3, 0x38, 0x95, 0xc7, 0xd2, 0xa2, 0xe0, 0xde, 0x80, 0xb6, 0x41, 0x8f, 0xdd, 0x88, 0x78, + 0x7e, 0x12, 0xa7, 0x4f, 0x55, 0x4e, 0x52, 0x16, 0xf5, 0x09, 0x5e, 0xa3, 0x38, 0xc1, 0x73, 0xff, + 0xd2, 0x81, 0x65, 0x94, 0xc1, 0x20, 0x1a, 0xef, 0xc5, 0x61, 0x30, 0x9c, 0xd1, 0xda, 0x2b, 0x71, + 0x93, 0xe7, 0xd5, 0x4a, 0x16, 0x6d, 0x18, 0x65, 0x5b, 0x05, 0x71, 0x52, 0x11, 0x75, 0x19, 0x35, + 0x15, 0xe5, 0xfc, 0xc0, 0xcf, 0xa4, 0xf0, 0xcb, 0xbd, 0xc8, 0x02, 0x51, 0x9f, 0x10, 0x48, 0xfd, + 0x9c, 0x0f, 0x26, 0x41, 0x18, 0x06, 0x82, 0x56, 0x78, 0x2a, 0x75, 0x55, 0xee, 0x0f, 0x1a, 0xd0, + 0x96, 0x96, 0xf2, 0xee, 0x68, 0x2c, 0xb2, 0xff, 0xd2, 0xdf, 0xd3, 0xe6, 0xc2, 0x40, 0x54, 0xbd, + 0xe5, 0x21, 0x1a, 0x48, 0x79, 0x59, 0xe7, 0xaa, 0xcb, 0x7a, 0x11, 0x5a, 0x28, 0x5e, 0x6f, 0x93, + 0x2b, 0x2a, 0xae, 0x69, 0x14, 0x80, 0xaa, 0xdd, 0xa1, 0xda, 0xf9, 0xa2, 0x96, 0x00, 0xcb, 0xf9, + 0x5c, 0x28, 0x39, 0x9f, 0xef, 0x42, 0x47, 0xb2, 0xa1, 0x79, 0x27, 0xeb, 0x50, 0x08, 0xb8, 0xb5, + 0x26, 0x9e, 0x45, 0xa9, 0xbe, 0xdc, 0x51, 0x5f, 0x2e, 0xbd, 0xec, 0x4b, 0x45, 0x49, 0x87, 0x61, + 0x62, 0x6e, 0xee, 0xa7, 0x7e, 0x72, 0xa4, 0x76, 0x9f, 0x91, 0x3e, 0xe1, 0x27, 0x98, 0x6d, 0xc1, + 0x3c, 0x7e, 0xa6, 0xac, 0x75, 0xbd, 0xd2, 0x09, 0x12, 0x76, 0x15, 0xe6, 0xf9, 0x68, 0xcc, 0x55, + 0xb0, 0xc5, 0xec, 0xb0, 0x17, 0xd7, 0xc8, 0x13, 0x04, 0x68, 0x02, 0x10, 0x2d, 0x99, 0x00, 0xdb, + 0xd2, 0x2f, 0x60, 0xf1, 0xfd, 0x91, 0xbb, 0x01, 0xec, 0xa1, 0x90, 0x5a, 0x33, 0x59, 0xfc, 0x3b, + 0x73, 0xd0, 0x36, 0x60, 0xd4, 0xe6, 0x31, 0x76, 0x78, 0x30, 0x0a, 0xfc, 0x09, 0xcf, 0x79, 0x2a, + 0x25, 0xb5, 0x84, 0x22, 0x9d, 0x7f, 0x3c, 0x1e, 0xc4, 0xd3, 0x7c, 0x30, 0xe2, 0xe3, 0x94, 0x8b, + 0x3d, 0x12, 0x37, 0x03, 0x0b, 0x45, 0xba, 0x89, 0xff, 0xcc, 0xa4, 0x13, 0xf2, 0x50, 0x42, 0x55, + 0xea, 0x57, 0xcc, 0x51, 0xb3, 0x48, 0xfd, 0x8a, 0x19, 0x29, 0xdb, 0xa1, 0xf9, 0x1a, 0x3b, 0xf4, + 0x0e, 0x6c, 0x0a, 0x8b, 0x23, 0x75, 0x73, 0x50, 0x12, 0x93, 0x53, 0x6a, 0xd9, 0x16, 0xac, 0x62, + 0x9f, 0x95, 0x80, 0x67, 0xc1, 0xc7, 0x22, 0x19, 0xe3, 0x78, 0x15, 0x1c, 0x69, 0x51, 0x1d, 0x2d, + 0x5a, 0x71, 0x3c, 0x56, 0xc1, 0x89, 0xd6, 0x7f, 0x66, 0xd3, 0xb6, 0x24, 0x6d, 0x09, 0x77, 0x97, + 0xa1, 0xbd, 0x9f, 0xc7, 0x89, 0x5a, 0x94, 0x2e, 0x74, 0x44, 0x51, 0x1e, 0x86, 0x5e, 0x80, 0xf3, + 0x24, 0x45, 0x8f, 0xe2, 0x24, 0x0e, 0xe3, 0xf1, 0x6c, 0x7f, 0x7a, 0x90, 0x0d, 0xd3, 0x20, 0xc1, + 0xc0, 0xc4, 0xfd, 0x3b, 0x07, 0xd6, 0xad, 0x5a, 0x99, 0xbd, 0xf9, 0xbc, 0x10, 0x69, 0x7d, 0x8a, + 0x25, 0x04, 0x6f, 0xcd, 0x30, 0x87, 0x82, 0x50, 0xe4, 0xcd, 0x1e, 0xcb, 0x83, 0xad, 0x9b, 0xb0, + 0xa2, 0x7a, 0xa6, 0x3e, 0x14, 0x52, 0xd8, 0xab, 0x4a, 0xa1, 0xfc, 0xbe, 0x2b, 0x3f, 0x50, 0x2c, + 0x7e, 0x51, 0xf8, 0xd5, 0x7c, 0x44, 0x63, 0x54, 0x61, 0x7c, 0x5f, 0x7d, 0x6f, 0x3a, 0xf3, 0xaa, + 0x07, 0x43, 0x0d, 0x66, 0xee, 0xef, 0x3b, 0x00, 0x45, 0xef, 0x50, 0x30, 0x0a, 0x93, 0x2e, 0xee, + 0xe7, 0x19, 0xe6, 0xfb, 0x0d, 0xe8, 0xe8, 0x03, 0x8c, 0x62, 0x97, 0x68, 0x2b, 0x0c, 0x1d, 0xae, + 0x2b, 0xb0, 0x32, 0x0e, 0xe3, 0x03, 0xda, 0x62, 0xe9, 0x74, 0x3d, 0x93, 0x47, 0xc2, 0x5d, 0x01, + 0xdf, 0x93, 0x68, 0xb1, 0xa5, 0x34, 0x8d, 0x2d, 0xc5, 0xfd, 0x4e, 0x43, 0xa7, 0xbd, 0x8b, 0x31, + 0x9f, 0xaa, 0x65, 0x6c, 0xa7, 0x62, 0x1c, 0x4f, 0xc9, 0x32, 0x53, 0xc2, 0x6a, 0xef, 0xa5, 0xf1, + 0xf4, 0x0d, 0xe8, 0xa6, 0xc2, 0xfa, 0x28, 0xd3, 0xd4, 0x7c, 0x81, 0x69, 0x5a, 0x4e, 0xad, 0x7d, + 0xe7, 0xff, 0xc1, 0xaa, 0x3f, 0x3a, 0xe6, 0x69, 0x1e, 0x50, 0x44, 0x43, 0x9b, 0xbe, 0x30, 0xa8, + 0x2b, 0x06, 0x4e, 0x7b, 0xf1, 0x15, 0x58, 0x91, 0xc7, 0xf0, 0x9a, 0x52, 0x5e, 0xbe, 0x2a, 0x60, + 0x24, 0x74, 0xbf, 0xaf, 0x32, 0xec, 0xf6, 0x1a, 0x9e, 0x3e, 0x23, 0xe6, 0xe8, 0x1a, 0xa5, 0xd1, + 0x7d, 0x46, 0x66, 0xbb, 0x47, 0x2a, 0x6c, 0x92, 0xe7, 0x0e, 0x02, 0x94, 0xa7, 0x13, 0xf6, 0x94, + 0x36, 0x5f, 0x65, 0x4a, 0xdd, 0x4f, 0x1d, 0x58, 0xdc, 0x8d, 0x93, 0x5d, 0x79, 0xa2, 0x4e, 0x8a, + 0xa0, 0x2f, 0xb9, 0xa8, 0xa2, 0xe9, 0x15, 0x37, 0x2a, 0x5e, 0x71, 0x75, 0xaf, 0x5d, 0x2e, 0xef, + 0xb5, 0xbf, 0x04, 0x17, 0x28, 0x68, 0x4f, 0xe3, 0x24, 0x4e, 0x51, 0x19, 0xfd, 0x50, 0x6c, 0xac, + 0x71, 0x94, 0x1f, 0x29, 0x33, 0xf6, 0x22, 0x12, 0x8a, 0x8e, 0xc2, 0xfc, 0x78, 0x20, 0x9c, 0x61, + 0xe9, 0x1b, 0x08, 0xeb, 0x56, 0xad, 0x70, 0xbf, 0x08, 0x2d, 0x72, 0x6e, 0x69, 0x58, 0x6f, 0x41, + 0xeb, 0x28, 0x4e, 0x06, 0x47, 0x41, 0x94, 0x2b, 0xe5, 0xee, 0x16, 0x5e, 0xe7, 0x2e, 0x4d, 0x88, + 0x26, 0x70, 0x7f, 0x34, 0x07, 0x8b, 0xef, 0x47, 0xc7, 0x71, 0x30, 0xa4, 0x64, 0xfc, 0x84, 0x4f, + 0x62, 0x75, 0xe5, 0x07, 0x7f, 0xe3, 0x54, 0xd0, 0xf1, 0x77, 0x92, 0xcb, 0x6c, 0xba, 0x2a, 0xe2, + 0x76, 0x9f, 0x16, 0xd7, 0xe0, 0x84, 0xea, 0x18, 0x08, 0x3a, 0xf6, 0xa9, 0x79, 0x07, 0x50, 0x96, + 0x8a, 0x3b, 0x53, 0xf3, 0xc6, 0x9d, 0x29, 0x3a, 0xba, 0x11, 0x27, 0xfb, 0x24, 0x5f, 0x4b, 0x9e, + 0x2a, 0x52, 0x20, 0x92, 0x72, 0x91, 0x6c, 0x21, 0xc7, 0x61, 0x51, 0x06, 0x22, 0x26, 0x88, 0xce, + 0x85, 0xf8, 0x40, 0xd0, 0x08, 0xe3, 0x6b, 0x42, 0xe8, 0x6c, 0x95, 0xaf, 0x11, 0xb6, 0x84, 0xcc, + 0x97, 0x60, 0xb4, 0xd0, 0x23, 0xae, 0x0d, 0xa9, 0x18, 0x03, 0x88, 0x6b, 0x7e, 0x65, 0xdc, 0x08, + 0x5f, 0xc4, 0x0d, 0x05, 0x15, 0xbe, 0xa0, 0xa0, 0xf8, 0x61, 0x78, 0xe0, 0x0f, 0x9f, 0xd2, 0xe5, + 0x4e, 0xba, 0x90, 0xd0, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x1d, 0xfe, 0x35, 0x3d, 0x13, + 0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5, 0x8c, 0xe9, 0x68, 0x45, + 0x4d, 0x22, 0xf3, 0x80, 0x60, 0xc5, 0xbe, 0x33, 0xf0, 0x0d, 0x60, 0x37, 0x47, 0x23, 0xb9, 0xde, + 0x3a, 0x64, 0x2c, 0x56, 0xca, 0xb1, 0x56, 0xaa, 0x66, 0xc6, 0x1a, 0xb5, 0x33, 0xe6, 0xde, 0x85, + 0xf6, 0x9e, 0x71, 0xc3, 0x93, 0x44, 0x43, 0xdd, 0xed, 0x94, 0xe2, 0x64, 0x20, 0x46, 0x83, 0x0d, + 0xb3, 0x41, 0xf7, 0xe7, 0x81, 0x3d, 0x08, 0xb2, 0x5c, 0xf7, 0x4f, 0x2c, 0xc7, 0x1b, 0xd0, 0xd1, + 0x01, 0x76, 0x71, 0xa3, 0xa1, 0x2d, 0x31, 0xba, 0x69, 0x70, 0x53, 0x5c, 0x85, 0x28, 0x0f, 0x6c, + 0x0b, 0x96, 0x02, 0x01, 0x95, 0x35, 0x41, 0x51, 0xea, 0x7a, 0xf4, 0xd7, 0x24, 0x68, 0xed, 0xa2, + 0x3f, 0x70, 0x60, 0x51, 0x0e, 0x0d, 0xbd, 0x0d, 0xeb, 0x6e, 0xab, 0x18, 0x98, 0x85, 0xd5, 0xdf, + 0x08, 0xac, 0xca, 0xf0, 0x5c, 0x9d, 0x0c, 0x33, 0x68, 0x26, 0x7e, 0x7e, 0x44, 0x01, 0x4a, 0xcb, + 0xa3, 0xdf, 0x6c, 0x55, 0x04, 0xcd, 0x42, 0x57, 0x28, 0x60, 0xae, 0xbb, 0x84, 0x2a, 0x4c, 0x72, + 0x05, 0xc7, 0x41, 0xd1, 0xe5, 0x01, 0x81, 0xeb, 0x33, 0x01, 0x79, 0x31, 0xa3, 0x80, 0x8b, 0xf9, + 0x92, 0x2c, 0xca, 0xf3, 0x25, 0x49, 0x3d, 0x5d, 0xef, 0xf6, 0xa1, 0x77, 0x87, 0x87, 0x3c, 0xe7, + 0x37, 0xc3, 0xb0, 0xcc, 0xff, 0x02, 0x9c, 0xaf, 0xa9, 0x93, 0x4e, 0xcb, 0x3d, 0x58, 0xbb, 0xc3, + 0x0f, 0xa6, 0xe3, 0x07, 0xfc, 0xb8, 0x38, 0xb8, 0x63, 0xd0, 0xcc, 0x8e, 0xe2, 0x13, 0xb9, 0xb6, + 0xf4, 0x9b, 0xbd, 0x06, 0x10, 0x22, 0xcd, 0x20, 0x4b, 0xf8, 0x50, 0xdd, 0x85, 0x23, 0x64, 0x3f, + 0xe1, 0x43, 0xf7, 0x1d, 0x60, 0x26, 0x1f, 0x39, 0x04, 0xb4, 0x03, 0xd3, 0x83, 0x41, 0x36, 0xcb, + 0x72, 0x3e, 0x51, 0x97, 0xfc, 0x4c, 0xc8, 0xbd, 0x02, 0x9d, 0x3d, 0x7f, 0xe6, 0xf1, 0x8f, 0xe4, + 0xf5, 0x62, 0x8c, 0x8d, 0xfd, 0x19, 0x8a, 0xb2, 0x8e, 0x8d, 0xa9, 0xda, 0xfd, 0x8f, 0x06, 0x2c, + 0x08, 0x4a, 0xe4, 0x3a, 0xe2, 0x59, 0x1e, 0x44, 0xe2, 0xd0, 0x4a, 0x72, 0x35, 0xa0, 0x8a, 0x6c, + 0x34, 0x6a, 0x64, 0x43, 0x7a, 0xab, 0xea, 0x5e, 0x91, 0x14, 0x02, 0x0b, 0x43, 0xb7, 0xa6, 0xb8, + 0x0c, 0x20, 0x82, 0xb3, 0x02, 0x28, 0x25, 0x4b, 0x0a, 0x6b, 0x23, 0xfa, 0xa7, 0x84, 0x56, 0x8a, + 0x83, 0x09, 0xd5, 0xda, 0xb4, 0x45, 0x21, 0x35, 0x15, 0x9b, 0x56, 0xb1, 0x5d, 0x4b, 0xaf, 0x60, + 0xbb, 0x84, 0x0b, 0xfb, 0x22, 0xdb, 0x05, 0xaf, 0x60, 0xbb, 0x5c, 0x06, 0xab, 0xf7, 0x38, 0xf7, + 0x38, 0xee, 0x8a, 0x4a, 0x9c, 0xbe, 0xeb, 0xc0, 0xaa, 0xdc, 0xd0, 0x75, 0x1d, 0x7b, 0xc3, 0xda, + 0xfd, 0x9d, 0xba, 0xf3, 0x88, 0x37, 0x61, 0x99, 0xf6, 0x64, 0x9d, 0x15, 0x92, 0x29, 0x2c, 0x0b, + 0xc4, 0x71, 0xa8, 0x0c, 0xfb, 0x24, 0x08, 0xe5, 0xa2, 0x98, 0x90, 0x4a, 0x2c, 0x61, 0x7c, 0x4c, + 0x4b, 0xe2, 0x78, 0xba, 0xec, 0xfe, 0xb5, 0x03, 0x6b, 0x46, 0x87, 0xa5, 0x14, 0xde, 0x00, 0x75, + 0x59, 0x40, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b, 0x98, 0x16, 0xd3, + 0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2, 0xf9, 0x53, 0x4d, + 0x32, 0x47, 0x24, 0x16, 0x46, 0x67, 0xc1, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0xfd, 0xc9, 0x06, 0xdd, + 0x7f, 0x70, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0x57, 0x33, 0x17, 0x84, 0x17, 0x2c, 0x34, + 0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x0b, 0xaf, 0xe8, 0xc8, 0xea, 0x3b, 0x00, 0xa7, 0xac, 0xc5, + 0x5c, 0xdd, 0x5a, 0xbc, 0x60, 0xa6, 0xeb, 0x92, 0x25, 0xf3, 0xb5, 0xc9, 0x92, 0x5b, 0x8b, 0x30, + 0x9f, 0x0d, 0xe3, 0x84, 0xbb, 0x9b, 0xb0, 0x61, 0x0f, 0x4e, 0x9a, 0xa0, 0xef, 0x39, 0xd0, 0xbb, + 0x27, 0x52, 0x87, 0x41, 0x34, 0xde, 0x0d, 0xb2, 0x3c, 0x4e, 0xf5, 0x5d, 0xf4, 0x4b, 0x00, 0x59, + 0xee, 0xa7, 0xb9, 0xb8, 0xa3, 0x25, 0xd3, 0x1c, 0x05, 0x82, 0x7d, 0xe4, 0xd1, 0x48, 0xd4, 0x8a, + 0xb5, 0xd1, 0x65, 0x5c, 0x18, 0xba, 0x9f, 0x30, 0x88, 0x0f, 0x0f, 0x33, 0xae, 0xdd, 0x56, 0x13, + 0xc3, 0xc8, 0x17, 0x35, 0x1e, 0x63, 0x3d, 0x7e, 0x4c, 0xa6, 0x56, 0xf8, 0x83, 0x25, 0xd4, 0xfd, + 0x2b, 0x07, 0x56, 0x8a, 0x4e, 0xde, 0x45, 0xd0, 0xb6, 0x0e, 0xa2, 0x6b, 0x86, 0x75, 0x50, 0x09, + 0x98, 0x60, 0x34, 0x08, 0x22, 0xd9, 0x37, 0x03, 0x21, 0x8d, 0x95, 0xa5, 0x78, 0xaa, 0xee, 0xc3, + 0x99, 0x90, 0x38, 0xec, 0xce, 0xf1, 0x6b, 0x71, 0x19, 0x4e, 0x96, 0xe8, 0x8a, 0xdd, 0x24, 0xa7, + 0xaf, 0x16, 0x84, 0x43, 0x2c, 0x8b, 0x6a, 0x7f, 0x5a, 0x24, 0x14, 0x7f, 0xba, 0x7f, 0xe0, 0xc0, + 0xf9, 0x9a, 0xc9, 0x95, 0x9a, 0x71, 0x07, 0xd6, 0x0e, 0x75, 0xa5, 0x9a, 0x00, 0xa1, 0x1e, 0x9b, + 0x52, 0x8a, 0x4a, 0x83, 0xf6, 0xaa, 0x1f, 0xa0, 0x7b, 0x4c, 0x79, 0x23, 0x31, 0xa5, 0xd6, 0x3d, + 0x91, 0x6a, 0xc5, 0xce, 0xf7, 0x1b, 0xd0, 0x15, 0x47, 0x15, 0xe2, 0x35, 0x12, 0x4f, 0xd9, 0x07, + 0xb0, 0x28, 0xdf, 0x7e, 0xb1, 0xb3, 0xb2, 0x59, 0xfb, 0xb5, 0x59, 0x7f, 0xb3, 0x0c, 0x4b, 0xd9, + 0x59, 0xff, 0xed, 0x4f, 0xff, 0xf9, 0x0f, 0x1b, 0xcb, 0xac, 0xbd, 0x7d, 0xfc, 0xf6, 0xf6, 0x98, + 0x47, 0x19, 0xf2, 0xf8, 0x55, 0x80, 0xe2, 0xf9, 0x14, 0xeb, 0x69, 0x27, 0xa3, 0xf4, 0xdc, 0xab, + 0x7f, 0xbe, 0xa6, 0x46, 0xf2, 0x3d, 0x4f, 0x7c, 0xd7, 0xdd, 0x2e, 0xf2, 0x0d, 0xa2, 0x20, 0x17, + 0x6f, 0xa9, 0xde, 0x73, 0xb6, 0xd8, 0x08, 0x3a, 0xe6, 0x33, 0x2a, 0xa6, 0x42, 0xe6, 0x9a, 0xb7, + 0x59, 0xfd, 0x0b, 0xb5, 0x75, 0x2a, 0x5f, 0x40, 0x6d, 0x9c, 0x75, 0x57, 0xb1, 0x8d, 0x29, 0x51, + 0xe8, 0x56, 0x76, 0xfe, 0xf1, 0x02, 0xb4, 0x74, 0xda, 0x89, 0x7d, 0x08, 0xcb, 0xd6, 0xe9, 0x0e, + 0x53, 0x8c, 0xeb, 0x0e, 0x83, 0xfa, 0x17, 0xeb, 0x2b, 0x65, 0xb3, 0x97, 0xa8, 0xd9, 0x1e, 0xdb, + 0xc4, 0x66, 0xe5, 0xf1, 0xc8, 0x36, 0x9d, 0x69, 0x89, 0x2b, 0x75, 0x4f, 0xa1, 0x6b, 0x9f, 0xc8, + 0xb0, 0x8b, 0xb6, 0x41, 0x29, 0xb5, 0xf6, 0xda, 0x29, 0xb5, 0xb2, 0xb9, 0x8b, 0xd4, 0xdc, 0x26, + 0xdb, 0x30, 0x9b, 0xd3, 0xe9, 0x20, 0x4e, 0x97, 0x20, 0xcd, 0xf7, 0x55, 0xec, 0x35, 0xbd, 0xd4, + 0x75, 0xef, 0xae, 0xf4, 0xa2, 0x55, 0x1f, 0x5f, 0xb9, 0x3d, 0x6a, 0x8a, 0x31, 0x9a, 0x50, 0xf3, + 0x79, 0x15, 0xfb, 0x16, 0xb4, 0xf4, 0x9b, 0x0a, 0x76, 0xce, 0x78, 0xc8, 0x62, 0x3e, 0xf4, 0xe8, + 0xf7, 0xaa, 0x15, 0x75, 0x4b, 0x65, 0x72, 0x46, 0x81, 0x78, 0x00, 0x67, 0xa5, 0x93, 0x7a, 0xc0, + 0x7f, 0x9c, 0x91, 0xd4, 0xbc, 0x0a, 0xbb, 0xee, 0xb0, 0x1b, 0xb0, 0xa4, 0x9e, 0xaa, 0xb0, 0xcd, + 0xfa, 0x27, 0x37, 0xfd, 0x73, 0x15, 0x5c, 0xea, 0xf3, 0x4d, 0x80, 0xe2, 0x99, 0x85, 0x96, 0xfc, + 0xca, 0xe3, 0x0f, 0x3d, 0x89, 0x35, 0x6f, 0x32, 0xc6, 0xf4, 0xa8, 0xc4, 0x7e, 0xc5, 0xc1, 0x5e, + 0x2f, 0xe8, 0x6b, 0xdf, 0x77, 0xbc, 0x80, 0xa1, 0xbb, 0x49, 0x73, 0xb7, 0xca, 0x48, 0x95, 0x22, + 0x7e, 0xa2, 0xae, 0x03, 0xdf, 0x81, 0xb6, 0xf1, 0x74, 0x83, 0x29, 0x0e, 0xd5, 0x67, 0x1f, 0xfd, + 0x7e, 0x5d, 0x95, 0xec, 0xee, 0x57, 0x60, 0xd9, 0x7a, 0x83, 0xa1, 0x35, 0xa3, 0xee, 0x85, 0x87, + 0xd6, 0x8c, 0xfa, 0x67, 0x1b, 0xdf, 0x84, 0xb6, 0xf1, 0x62, 0x82, 0x19, 0xd7, 0xa0, 0x4a, 0x6f, + 0x25, 0x74, 0x8f, 0xea, 0x1e, 0x58, 0x6c, 0xd0, 0x78, 0xbb, 0x6e, 0x0b, 0xc7, 0x4b, 0x77, 0x62, + 0x51, 0x48, 0x3e, 0x84, 0xae, 0xfd, 0x86, 0x42, 0x6b, 0x55, 0xed, 0x6b, 0x0c, 0xad, 0x55, 0xa7, + 0x3c, 0xbc, 0x90, 0x02, 0xb9, 0xb5, 0xae, 0x1b, 0xd9, 0xfe, 0x44, 0x1e, 0xba, 0x3c, 0x67, 0x5f, + 0x47, 0xd3, 0x21, 0x2f, 0x29, 0xb3, 0xe2, 0xe5, 0x88, 0x7d, 0x95, 0x59, 0x4b, 0x7b, 0xe5, 0x3e, + 0xb3, 0xbb, 0x46, 0xcc, 0xdb, 0xac, 0x18, 0x81, 0xb0, 0xd0, 0x74, 0x59, 0xd9, 0xb0, 0xd0, 0xe6, + 0x7d, 0x66, 0xc3, 0x42, 0x5b, 0x77, 0x9a, 0xcb, 0x16, 0x3a, 0x0f, 0x90, 0x47, 0x04, 0x2b, 0xa5, + 0x7b, 0x00, 0x5a, 0x59, 0xea, 0x2f, 0x4e, 0xf5, 0x2f, 0xbd, 0xf8, 0xfa, 0x80, 0x6d, 0x66, 0x94, + 0x79, 0xd9, 0x56, 0xf7, 0xdc, 0x7e, 0x0d, 0x3a, 0xe6, 0xdd, 0x77, 0x6d, 0xb3, 0x6b, 0x6e, 0xec, + 0x6b, 0x9b, 0x5d, 0x77, 0x59, 0x5e, 0x2d, 0x2e, 0xeb, 0x98, 0xcd, 0xb0, 0x6f, 0xc2, 0x8a, 0x71, + 0xf1, 0x65, 0x7f, 0x16, 0x0d, 0xb5, 0xf0, 0x54, 0xaf, 0x45, 0xf6, 0xeb, 0xfc, 0x33, 0xf7, 0x1c, + 0x31, 0x5e, 0x73, 0x2d, 0xc6, 0x28, 0x38, 0xb7, 0xa1, 0x6d, 0x5e, 0xaa, 0x79, 0x01, 0xdf, 0x73, + 0x46, 0x95, 0x79, 0x43, 0xf0, 0xba, 0xc3, 0xfe, 0xd8, 0x81, 0x8e, 0x75, 0x45, 0xc5, 0xca, 0xf3, + 0x96, 0xf8, 0xf4, 0xcc, 0x3a, 0x93, 0x91, 0xeb, 0x51, 0x27, 0x1f, 0x6c, 0x7d, 0xc5, 0x9a, 0xe4, + 0x4f, 0x2c, 0x3f, 0xff, 0x5a, 0xf9, 0x59, 0xe3, 0xf3, 0x32, 0x81, 0x79, 0x75, 0xf4, 0xf9, 0x75, + 0x87, 0xbd, 0x27, 0x9e, 0xbe, 0xaa, 0xb8, 0x9e, 0x19, 0xc6, 0xad, 0x3c, 0x65, 0xe6, 0x2b, 0xd1, + 0xab, 0xce, 0x75, 0x87, 0x7d, 0x5b, 0xbc, 0x5e, 0x94, 0xdf, 0xd2, 0xcc, 0xbf, 0xea, 0xf7, 0xee, + 0x9b, 0x34, 0x9a, 0x4b, 0xee, 0x79, 0x6b, 0x34, 0x65, 0xeb, 0xbe, 0x07, 0x50, 0x24, 0x69, 0x58, + 0x29, 0x63, 0xa1, 0xed, 0x5e, 0x35, 0x8f, 0x63, 0xaf, 0xa8, 0x4a, 0x6c, 0x20, 0xc7, 0x6f, 0x09, + 0x61, 0x94, 0xf4, 0x99, 0x5e, 0xd2, 0x6a, 0xb2, 0xa5, 0xdf, 0xaf, 0xab, 0xaa, 0x13, 0x45, 0xc5, + 0x9f, 0x3d, 0x86, 0xe5, 0x07, 0x71, 0xfc, 0x74, 0x9a, 0xe8, 0x34, 0xa2, 0x9d, 0x33, 0xd8, 0xf5, + 0xb3, 0xa3, 0x7e, 0x69, 0x14, 0xee, 0x65, 0x62, 0xd5, 0x67, 0x3d, 0x83, 0xd5, 0xf6, 0x27, 0x45, + 0x8a, 0xe8, 0x39, 0xf3, 0x61, 0x4d, 0xef, 0x71, 0xba, 0xe3, 0x7d, 0x9b, 0x8d, 0x99, 0xa9, 0xa9, + 0x34, 0x61, 0x79, 0x1d, 0xaa, 0xb7, 0xdb, 0x99, 0xe2, 0x79, 0xdd, 0x61, 0x7b, 0xd0, 0xb9, 0xc3, + 0x87, 0xf1, 0x88, 0xcb, 0x28, 0x7f, 0xbd, 0xe8, 0xb8, 0x4e, 0x0f, 0xf4, 0x97, 0x2d, 0xd0, 0xd6, + 0xfa, 0xc4, 0x9f, 0xa5, 0xfc, 0xa3, 0xed, 0x4f, 0x64, 0xfe, 0xe0, 0xb9, 0xd2, 0x7a, 0x95, 0xf3, + 0xb0, 0xb4, 0xbe, 0x94, 0x24, 0xb1, 0xb4, 0xbe, 0x92, 0x24, 0xb1, 0xa6, 0x5a, 0xe5, 0x5c, 0x58, + 0x08, 0x6b, 0x95, 0xbc, 0x8a, 0xde, 0x29, 0x4f, 0xcb, 0xc6, 0xf4, 0x2f, 0x9f, 0x4e, 0x60, 0xb7, + 0xb6, 0x65, 0xb7, 0xb6, 0x0f, 0xcb, 0x77, 0xb8, 0x98, 0x2c, 0x71, 0x56, 0xd9, 0xb7, 0xcd, 0x88, + 0x79, 0xae, 0x59, 0x36, 0x31, 0x54, 0x67, 0x9b, 0x75, 0x3a, 0x28, 0x64, 0xdf, 0x82, 0xf6, 0x7d, + 0x9e, 0xab, 0xc3, 0x49, 0xed, 0x6f, 0x94, 0x4e, 0x2b, 0xfb, 0x35, 0x67, 0x9b, 0xb6, 0xcc, 0x10, + 0xb7, 0x6d, 0x3e, 0x1a, 0x73, 0xa1, 0xec, 0x83, 0x60, 0xf4, 0x9c, 0xfd, 0x32, 0x31, 0xd7, 0xf7, + 0x19, 0x36, 0x8d, 0x33, 0x2d, 0x93, 0xf9, 0x4a, 0x09, 0xaf, 0xe3, 0x1c, 0xc5, 0x23, 0x6e, 0x6c, + 0x70, 0x11, 0xb4, 0x8d, 0xcb, 0x36, 0x5a, 0x81, 0xaa, 0x17, 0x7c, 0xb4, 0x02, 0xd5, 0xdc, 0xcd, + 0x71, 0xaf, 0x52, 0x3b, 0x2e, 0xbb, 0x5c, 0xb4, 0x23, 0xee, 0xe3, 0x14, 0x2d, 0x6d, 0x7f, 0xe2, + 0x4f, 0xf2, 0xe7, 0xec, 0x09, 0xbd, 0xde, 0x31, 0x0f, 0x60, 0x0b, 0x7f, 0xa7, 0x7c, 0x56, 0xab, + 0x27, 0xcb, 0xa8, 0xb2, 0x7d, 0x20, 0xd1, 0x14, 0xed, 0x83, 0x5f, 0x00, 0xd8, 0xcf, 0xe3, 0xe4, + 0x8e, 0xcf, 0x27, 0x71, 0x54, 0x58, 0xae, 0xe2, 0x90, 0xb1, 0xb0, 0x5c, 0xc6, 0x49, 0x23, 0x7b, + 0x62, 0x78, 0x9c, 0xd6, 0xf9, 0xb5, 0x12, 0xae, 0x53, 0xcf, 0x21, 0xf5, 0x84, 0xd4, 0x9c, 0x45, + 0x5e, 0x77, 0xd0, 0x7f, 0x2c, 0xb2, 0x78, 0xda, 0x7f, 0xac, 0x24, 0x08, 0xb5, 0xd9, 0xab, 0x49, + 0xf9, 0xed, 0x41, 0xab, 0x48, 0x0b, 0xa9, 0x2d, 0xa9, 0x9c, 0x44, 0xd2, 0x7b, 0x4c, 0x25, 0x59, + 0xe3, 0xae, 0xd2, 0x54, 0x01, 0x5b, 0xc2, 0xa9, 0xa2, 0x0c, 0x4c, 0x00, 0xeb, 0xa2, 0x83, 0x7a, + 0xc3, 0xa4, 0x63, 0x33, 0x35, 0x92, 0x9a, 0x84, 0x89, 0xd6, 0xe6, 0xda, 0x7c, 0x83, 0x15, 0xdb, + 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, } diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index 8680b326..4f6760f1 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -1063,6 +1063,14 @@ message PendingChannelsResponse { 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 { /// The pending channel to be closed PendingChannel channel = 1; @@ -1108,6 +1116,9 @@ message PendingChannelsResponse { /// Channels pending force closing 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 { diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index 70bf8ab8..417a78ec 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -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": { "type": "object", "properties": { @@ -2035,6 +2049,13 @@ "$ref": "#/definitions/PendingChannelsResponseForceClosedChannel" }, "title": "/ Channels pending force closing" + }, + "waiting_close_channels": { + "type": "array", + "items": { + "$ref": "#/definitions/PendingChannelsResponseWaitingCloseChannel" + }, + "title": "/ Channels waiting for closing tx to confirm" } } }, diff --git a/lntest/harness.go b/lntest/harness.go index a582894d..e6e68580 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -770,6 +770,7 @@ func (n *NetworkHarness) CloseChannel(ctx context.Context, return func() bool { channel, err := filterChannel(node, chanPoint) if err != nil { + return false } return channel.Active diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 4042f40e..a17dd791 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -4650,7 +4650,7 @@ type CommitOutputResolution struct { // had any outgoing HTLC's within the commitment transaction, then an // OutgoingHtlcResolution for each output will included. 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. *chainntnfs.SpendDetail @@ -4754,7 +4754,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer, RemotePub: chanState.IdentityPub, Capacity: chanState.Capacity, SettledBalance: localBalance, - CloseType: channeldb.ForceClose, + CloseType: channeldb.RemoteForceClose, IsPending: true, } diff --git a/peer.go b/peer.go index dfa4e0f7..248e5830 100644 --- a/peer.go +++ b/peer.go @@ -329,9 +329,9 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error { // Skip adding any permanently irreconcilable channels to the // htlcswitch. - if dbChan.IsBorked { - peerLog.Warnf("ChannelPoint(%v) is borked, won't "+ - "start.", chanPoint) + if dbChan.ChanStatus != channeldb.Default { + peerLog.Warnf("ChannelPoint(%v) has status %v, won't "+ + "start.", chanPoint, dbChan.ChanStatus) lnChan.Stop() continue } diff --git a/rpcserver.go b/rpcserver.go index b70f960d..e6e694d0 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1343,18 +1343,24 @@ func (r *rpcServer) WalletBalance(ctx context.Context, func (r *rpcServer) ChannelBalance(ctx context.Context, in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) { - channels, err := r.server.chanDB.FetchAllChannels() + openChannels, err := r.server.chanDB.FetchAllOpenChannels() if err != nil { return nil, err } - var pendingOpenBalance, balance btcutil.Amount - for _, channel := range channels { - if channel.IsPending { - pendingOpenBalance += channel.LocalCommitment.LocalBalance.ToSatoshis() - } else { - balance += channel.LocalCommitment.LocalBalance.ToSatoshis() - } + var balance btcutil.Amount + for _, channel := range openChannels { + 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{ @@ -1457,7 +1463,8 @@ func (r *rpcServer) PendingChannels(ctx context.Context, // If the channel was force closed, then we'll need to query // the utxoNursery for additional information. - case channeldb.ForceClose: + // TODO(halseth): distinguish remote and local case? + case channeldb.LocalForceClose, channeldb.RemoteForceClose: forceClose := &lnrpc.PendingChannelsResponse_ForceClosedChannel{ Channel: channel, 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 } @@ -1544,7 +1584,7 @@ func (r *rpcServer) ListChannels(ctx context.Context, graph := r.server.chanDB.ChannelGraph() - dbChannels, err := r.server.chanDB.FetchAllChannels() + dbChannels, err := r.server.chanDB.FetchAllOpenChannels() if err != nil { return nil, err } @@ -1553,10 +1593,6 @@ func (r *rpcServer) ListChannels(ctx context.Context, len(dbChannels)) for _, dbChannel := range dbChannels { - if dbChannel.IsPending { - continue - } - nodePub := dbChannel.IdentityPub nodeID := hex.EncodeToString(nodePub.SerializeCompressed()) chanPoint := dbChannel.FundingOutpoint