mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-26 16:51:28 +02:00
Merge pull request #1017 from halseth/contract-court-without-on-chain
Contract court acting on confirmed chain events
This commit is contained in:
commit
86fd9e361e
@ -696,6 +696,22 @@ func (b *breachArbiter) breachObserver(
|
|||||||
brarLog.Debugf("Breach observer for ChannelPoint(%v) started ",
|
brarLog.Debugf("Breach observer for ChannelPoint(%v) started ",
|
||||||
chanPoint)
|
chanPoint)
|
||||||
|
|
||||||
|
gracefullyExit := func() {
|
||||||
|
// Launch a goroutine to cancel out this contract within the
|
||||||
|
// breachArbiter's main goroutine.
|
||||||
|
b.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer b.wg.Done()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case b.settledContracts <- chanPoint:
|
||||||
|
case <-b.quit:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
// A read from this channel indicates that the contract has been
|
// A read from this channel indicates that the contract has been
|
||||||
// settled cooperatively so we exit as our duties are no longer needed.
|
// settled cooperatively so we exit as our duties are no longer needed.
|
||||||
@ -704,36 +720,14 @@ func (b *breachArbiter) breachObserver(
|
|||||||
|
|
||||||
// The channel has been closed cooperatively, so we're done here.
|
// The channel has been closed cooperatively, so we're done here.
|
||||||
case <-chainEvents.CooperativeClosure:
|
case <-chainEvents.CooperativeClosure:
|
||||||
// Launch a goroutine to cancel out this contract within the
|
gracefullyExit()
|
||||||
// breachArbiter's main goroutine.
|
|
||||||
b.wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer b.wg.Done()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case b.settledContracts <- chanPoint:
|
|
||||||
case <-b.quit:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
|
|
||||||
|
|
||||||
// The channel has been closed by a normal means: force closing with
|
// The channel has been closed by a normal means: force closing with
|
||||||
// the latest commitment transaction.
|
// the latest commitment transaction.
|
||||||
case <-chainEvents.UnilateralClosure:
|
case <-chainEvents.LocalUnilateralClosure:
|
||||||
// Launch a goroutine to cancel out this contract within the
|
gracefullyExit()
|
||||||
// breachArbiter's main goroutine.
|
case <-chainEvents.RemoteUnilateralClosure:
|
||||||
b.wg.Add(1)
|
gracefullyExit()
|
||||||
go func() {
|
|
||||||
defer b.wg.Done()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case b.settledContracts <- chanPoint:
|
|
||||||
case <-b.quit:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
b.cfg.CloseLink(&chanPoint, htlcswitch.CloseBreach)
|
|
||||||
|
|
||||||
// A read from this channel indicates that a channel breach has been
|
// A read from this channel indicates that a channel breach has been
|
||||||
// detected! So we notify the main coordination goroutine with the
|
// detected! So we notify the main coordination goroutine with the
|
||||||
|
@ -951,11 +951,12 @@ func TestBreachHandoffSuccess(t *testing.T) {
|
|||||||
// Instantiate a breach arbiter to handle the breach of alice's channel.
|
// Instantiate a breach arbiter to handle the breach of alice's channel.
|
||||||
alicePoint := alice.ChannelPoint()
|
alicePoint := alice.ChannelPoint()
|
||||||
spendEvents := contractcourt.ChainEventSubscription{
|
spendEvents := contractcourt.ChainEventSubscription{
|
||||||
UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
||||||
CooperativeClosure: make(chan struct{}, 1),
|
LocalUnilateralClosure: make(chan *contractcourt.LocalUnilateralCloseInfo, 1),
|
||||||
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
CooperativeClosure: make(chan struct{}, 1),
|
||||||
ProcessACK: make(chan error, 1),
|
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
||||||
ChanPoint: *alicePoint,
|
ProcessACK: make(chan error, 1),
|
||||||
|
ChanPoint: *alicePoint,
|
||||||
Cancel: func() {
|
Cancel: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1039,11 +1040,12 @@ func TestBreachHandoffFail(t *testing.T) {
|
|||||||
// Instantiate a breach arbiter to handle the breach of alice's channel.
|
// Instantiate a breach arbiter to handle the breach of alice's channel.
|
||||||
alicePoint := alice.ChannelPoint()
|
alicePoint := alice.ChannelPoint()
|
||||||
spendEvents := contractcourt.ChainEventSubscription{
|
spendEvents := contractcourt.ChainEventSubscription{
|
||||||
UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
||||||
CooperativeClosure: make(chan struct{}, 1),
|
LocalUnilateralClosure: make(chan *contractcourt.LocalUnilateralCloseInfo, 1),
|
||||||
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
CooperativeClosure: make(chan struct{}, 1),
|
||||||
ProcessACK: make(chan error, 1),
|
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
||||||
ChanPoint: *alicePoint,
|
ProcessACK: make(chan error, 1),
|
||||||
|
ChanPoint: *alicePoint,
|
||||||
Cancel: func() {
|
Cancel: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -285,6 +285,38 @@ type ChannelCommitment struct {
|
|||||||
// * lets just walk through
|
// * lets just walk through
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelStatus is used to indicate whether an OpenChannel is in the default
|
||||||
|
// usable state, or a state where it shouldn't be used.
|
||||||
|
type ChannelStatus uint8
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Default is the normal state of an open channel.
|
||||||
|
Default ChannelStatus = 0
|
||||||
|
|
||||||
|
// Borked indicates that the channel has entered an irreconcilable
|
||||||
|
// state, triggered by a state desynchronization or channel breach.
|
||||||
|
// Channels in this state should never be added to the htlc switch.
|
||||||
|
Borked ChannelStatus = 1
|
||||||
|
|
||||||
|
// CommitmentBroadcasted indicates that a commitment for this channel
|
||||||
|
// has been broadcasted.
|
||||||
|
CommitmentBroadcasted ChannelStatus = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a human-readable representation of the ChannelStatus.
|
||||||
|
func (c ChannelStatus) String() string {
|
||||||
|
switch c {
|
||||||
|
case Default:
|
||||||
|
return "Default"
|
||||||
|
case Borked:
|
||||||
|
return "Borked"
|
||||||
|
case CommitmentBroadcasted:
|
||||||
|
return "CommitmentBroadcasted"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenChannel encapsulates the persistent and dynamic state of an open channel
|
// OpenChannel encapsulates the persistent and dynamic state of an open channel
|
||||||
// with a remote node. An open channel supports several options for on-disk
|
// with a remote node. An open channel supports several options for on-disk
|
||||||
// serialization depending on the exact context. Full (upon channel creation)
|
// serialization depending on the exact context. Full (upon channel creation)
|
||||||
@ -322,10 +354,9 @@ type OpenChannel struct {
|
|||||||
// negotiate fees, or close the channel.
|
// negotiate fees, or close the channel.
|
||||||
IsInitiator bool
|
IsInitiator bool
|
||||||
|
|
||||||
// IsBorked indicates that the channel has entered an irreconcilable
|
// ChanStatus is the current status of this channel. If it is not in
|
||||||
// state, triggered by a state desynchronization or channel breach.
|
// the state Default, it should not be used for forwarding payments.
|
||||||
// Channels in this state should never be added to the htlc switch.
|
ChanStatus ChannelStatus
|
||||||
IsBorked bool
|
|
||||||
|
|
||||||
// FundingBroadcastHeight is the height in which the funding
|
// FundingBroadcastHeight is the height in which the funding
|
||||||
// transaction was broadcast. This value can be used by higher level
|
// transaction was broadcast. This value can be used by higher level
|
||||||
@ -571,6 +602,20 @@ func (c *OpenChannel) MarkBorked() error {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
return c.putChanStatus(Borked)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkCommitmentBroadcasted marks the channel as a commitment transaction has
|
||||||
|
// been broadcast, either our own or the remote, and we should watch the chain
|
||||||
|
// for it to confirm before taking any further action.
|
||||||
|
func (c *OpenChannel) MarkCommitmentBroadcasted() error {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
return c.putChanStatus(CommitmentBroadcasted)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *OpenChannel) putChanStatus(status ChannelStatus) error {
|
||||||
if err := c.Db.Update(func(tx *bolt.Tx) error {
|
if err := c.Db.Update(func(tx *bolt.Tx) error {
|
||||||
chanBucket, err := updateChanBucket(tx, c.IdentityPub,
|
chanBucket, err := updateChanBucket(tx, c.IdentityPub,
|
||||||
&c.FundingOutpoint, c.ChainHash)
|
&c.FundingOutpoint, c.ChainHash)
|
||||||
@ -583,14 +628,15 @@ func (c *OpenChannel) MarkBorked() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.IsBorked = true
|
channel.ChanStatus = status
|
||||||
|
|
||||||
return putOpenChannel(chanBucket, channel)
|
return putOpenChannel(chanBucket, channel)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.IsBorked = true
|
// Update the in-memory representation to keep it in sync with the DB.
|
||||||
|
c.ChanStatus = status
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1478,8 +1524,8 @@ func (c *OpenChannel) FindPreviousState(updateNum uint64) (*ChannelCommitment, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ClosureType is an enum like structure that details exactly _how_ a channel
|
// ClosureType is an enum like structure that details exactly _how_ a channel
|
||||||
// was closed. Three closure types are currently possible: cooperative, force,
|
// was closed. Three closure types are currently possible: none, cooperative,
|
||||||
// and breach.
|
// local force close, remote force close, and (remote) breach.
|
||||||
type ClosureType uint8
|
type ClosureType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -1487,21 +1533,25 @@ const (
|
|||||||
// cooperatively. This means that both channel peers were online and
|
// cooperatively. This means that both channel peers were online and
|
||||||
// signed a new transaction paying out the settled balance of the
|
// signed a new transaction paying out the settled balance of the
|
||||||
// contract.
|
// contract.
|
||||||
CooperativeClose ClosureType = iota
|
CooperativeClose ClosureType = 0
|
||||||
|
|
||||||
// ForceClose indicates that one peer unilaterally broadcast their
|
// LocalForceClose indicates that we have unilaterally broadcast our
|
||||||
// current commitment state on-chain.
|
// current commitment state on-chain.
|
||||||
ForceClose
|
LocalForceClose ClosureType = 1
|
||||||
|
|
||||||
// BreachClose indicates that one peer attempted to broadcast a prior
|
// RemoteForceClose indicates that the remote peer has unilaterally
|
||||||
// _revoked_ channel state.
|
// broadcast their current commitment state on-chain.
|
||||||
BreachClose
|
RemoteForceClose ClosureType = 4
|
||||||
|
|
||||||
|
// BreachClose indicates that the remote peer attempted to broadcast a
|
||||||
|
// prior _revoked_ channel state.
|
||||||
|
BreachClose ClosureType = 2
|
||||||
|
|
||||||
// FundingCanceled indicates that the channel never was fully opened
|
// FundingCanceled indicates that the channel never was fully opened
|
||||||
// before it was marked as closed in the database. This can happen if
|
// before it was marked as closed in the database. This can happen if
|
||||||
// we or the remote fail at some point during the opening workflow, or
|
// we or the remote fail at some point during the opening workflow, or
|
||||||
// we timeout waiting for the funding transaction to be confirmed.
|
// we timeout waiting for the funding transaction to be confirmed.
|
||||||
FundingCanceled
|
FundingCanceled ClosureType = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChannelCloseSummary contains the final state of a channel at the point it
|
// ChannelCloseSummary contains the final state of a channel at the point it
|
||||||
@ -1549,8 +1599,9 @@ type ChannelCloseSummary struct {
|
|||||||
// outstanding outgoing HTLC's at the time of channel closure.
|
// outstanding outgoing HTLC's at the time of channel closure.
|
||||||
TimeLockedBalance btcutil.Amount
|
TimeLockedBalance btcutil.Amount
|
||||||
|
|
||||||
// CloseType details exactly _how_ the channel was closed. Three
|
// CloseType details exactly _how_ the channel was closed. Five closure
|
||||||
// closure types are possible: cooperative, force, and breach.
|
// types are possible: cooperative, local force, remote force, breach
|
||||||
|
// and funding canceled.
|
||||||
CloseType ClosureType
|
CloseType ClosureType
|
||||||
|
|
||||||
// IsPending indicates whether this channel is in the 'pending close'
|
// IsPending indicates whether this channel is in the 'pending close'
|
||||||
@ -1804,7 +1855,7 @@ func putChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error {
|
|||||||
if err := writeElements(&w,
|
if err := writeElements(&w,
|
||||||
channel.ChanType, channel.ChainHash, channel.FundingOutpoint,
|
channel.ChanType, channel.ChainHash, channel.FundingOutpoint,
|
||||||
channel.ShortChanID, channel.IsPending, channel.IsInitiator,
|
channel.ShortChanID, channel.IsPending, channel.IsInitiator,
|
||||||
channel.IsBorked, channel.FundingBroadcastHeight,
|
channel.ChanStatus, channel.FundingBroadcastHeight,
|
||||||
channel.NumConfsRequired, channel.ChannelFlags,
|
channel.NumConfsRequired, channel.ChannelFlags,
|
||||||
channel.IdentityPub, channel.Capacity, channel.TotalMSatSent,
|
channel.IdentityPub, channel.Capacity, channel.TotalMSatSent,
|
||||||
channel.TotalMSatReceived,
|
channel.TotalMSatReceived,
|
||||||
@ -1912,7 +1963,7 @@ func fetchChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error {
|
|||||||
if err := readElements(r,
|
if err := readElements(r,
|
||||||
&channel.ChanType, &channel.ChainHash, &channel.FundingOutpoint,
|
&channel.ChanType, &channel.ChainHash, &channel.FundingOutpoint,
|
||||||
&channel.ShortChanID, &channel.IsPending, &channel.IsInitiator,
|
&channel.ShortChanID, &channel.IsPending, &channel.IsInitiator,
|
||||||
&channel.IsBorked, &channel.FundingBroadcastHeight,
|
&channel.ChanStatus, &channel.FundingBroadcastHeight,
|
||||||
&channel.NumConfsRequired, &channel.ChannelFlags,
|
&channel.NumConfsRequired, &channel.ChannelFlags,
|
||||||
&channel.IdentityPub, &channel.Capacity, &channel.TotalMSatSent,
|
&channel.IdentityPub, &channel.Capacity, &channel.TotalMSatSent,
|
||||||
&channel.TotalMSatReceived,
|
&channel.TotalMSatReceived,
|
||||||
|
@ -606,7 +606,7 @@ func TestChannelStateTransition(t *testing.T) {
|
|||||||
SettledBalance: btcutil.Amount(500),
|
SettledBalance: btcutil.Amount(500),
|
||||||
TimeLockedBalance: btcutil.Amount(10000),
|
TimeLockedBalance: btcutil.Amount(10000),
|
||||||
IsPending: false,
|
IsPending: false,
|
||||||
CloseType: ForceClose,
|
CloseType: RemoteForceClose,
|
||||||
}
|
}
|
||||||
if err := updatedChannel[0].CloseChannel(closeSummary); err != nil {
|
if err := updatedChannel[0].CloseChannel(closeSummary); err != nil {
|
||||||
t.Fatalf("unable to delete updated channel: %v", err)
|
t.Fatalf("unable to delete updated channel: %v", err)
|
||||||
@ -770,7 +770,7 @@ func TestFetchClosedChannels(t *testing.T) {
|
|||||||
Capacity: state.Capacity,
|
Capacity: state.Capacity,
|
||||||
SettledBalance: state.LocalCommitment.LocalBalance.ToSatoshis(),
|
SettledBalance: state.LocalCommitment.LocalBalance.ToSatoshis(),
|
||||||
TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000,
|
TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000,
|
||||||
CloseType: ForceClose,
|
CloseType: RemoteForceClose,
|
||||||
IsPending: true,
|
IsPending: true,
|
||||||
}
|
}
|
||||||
if err := state.CloseChannel(summary); err != nil {
|
if err := state.CloseChannel(summary); err != nil {
|
||||||
|
@ -148,10 +148,16 @@ func writeElement(w io.Writer, element interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ChannelStatus:
|
||||||
|
if err := binary.Write(w, byteOrder, e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
case ClosureType:
|
case ClosureType:
|
||||||
if err := binary.Write(w, byteOrder, e); err != nil {
|
if err := binary.Write(w, byteOrder, e); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case lnwire.FundingFlag:
|
case lnwire.FundingFlag:
|
||||||
if err := binary.Write(w, byteOrder, e); err != nil {
|
if err := binary.Write(w, byteOrder, e); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -321,10 +327,16 @@ func readElement(r io.Reader, element interface{}) error {
|
|||||||
|
|
||||||
*e = msg
|
*e = msg
|
||||||
|
|
||||||
|
case *ChannelStatus:
|
||||||
|
if err := binary.Read(r, byteOrder, e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
case *ClosureType:
|
case *ClosureType:
|
||||||
if err := binary.Read(r, byteOrder, e); err != nil {
|
if err := binary.Read(r, byteOrder, e); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case *lnwire.FundingFlag:
|
case *lnwire.FundingFlag:
|
||||||
if err := binary.Read(r, byteOrder, e); err != nil {
|
if err := binary.Read(r, byteOrder, e); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -312,23 +312,60 @@ func (d *DB) fetchNodeChannels(chainBucket *bolt.Bucket) ([]*OpenChannel, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FetchAllChannels attempts to retrieve all open channels currently stored
|
// FetchAllChannels attempts to retrieve all open channels currently stored
|
||||||
// within the database.
|
// within the database, including pending open, fully open and channels waiting
|
||||||
|
// for a closing transaction to confirm.
|
||||||
func (d *DB) FetchAllChannels() ([]*OpenChannel, error) {
|
func (d *DB) FetchAllChannels() ([]*OpenChannel, error) {
|
||||||
return fetchChannels(d, false)
|
var channels []*OpenChannel
|
||||||
|
|
||||||
|
// TODO(halseth): fetch all in one db tx.
|
||||||
|
openChannels, err := d.FetchAllOpenChannels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
channels = append(channels, openChannels...)
|
||||||
|
|
||||||
|
pendingChannels, err := d.FetchPendingChannels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
channels = append(channels, pendingChannels...)
|
||||||
|
|
||||||
|
waitingClose, err := d.FetchWaitingCloseChannels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
channels = append(channels, waitingClose...)
|
||||||
|
|
||||||
|
return channels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchAllOpenChannels will return all channels that have the funding
|
||||||
|
// transaction confirmed, and is not waiting for a closing transaction to be
|
||||||
|
// confirmed.
|
||||||
|
func (d *DB) FetchAllOpenChannels() ([]*OpenChannel, error) {
|
||||||
|
return fetchChannels(d, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchPendingChannels will return channels that have completed the process of
|
// FetchPendingChannels will return channels that have completed the process of
|
||||||
// generating and broadcasting funding transactions, but whose funding
|
// generating and broadcasting funding transactions, but whose funding
|
||||||
// transactions have yet to be confirmed on the blockchain.
|
// transactions have yet to be confirmed on the blockchain.
|
||||||
func (d *DB) FetchPendingChannels() ([]*OpenChannel, error) {
|
func (d *DB) FetchPendingChannels() ([]*OpenChannel, error) {
|
||||||
return fetchChannels(d, true)
|
return fetchChannels(d, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchWaitingCloseChannels will return all channels that have been opened,
|
||||||
|
// but now is waiting for a closing transaction to be confirmed.
|
||||||
|
func (d *DB) FetchWaitingCloseChannels() ([]*OpenChannel, error) {
|
||||||
|
return fetchChannels(d, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchChannels attempts to retrieve channels currently stored in the
|
// fetchChannels attempts to retrieve channels currently stored in the
|
||||||
// database. The pendingOnly parameter determines whether only pending channels
|
// database. The pending parameter determines whether only pending channels
|
||||||
// will be returned. If no active channels exist within the network, then
|
// will be returned, or only open channels will be returned. The waitingClose
|
||||||
// ErrNoActiveChannels is returned.
|
// parameter determines wheter only channels waiting for a closing transaction
|
||||||
func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) {
|
// to be confirmed should be returned. If no active channels exist within the
|
||||||
|
// network, then ErrNoActiveChannels is returned.
|
||||||
|
func fetchChannels(d *DB, pending, waitingClose bool) ([]*OpenChannel, error) {
|
||||||
var channels []*OpenChannel
|
var channels []*OpenChannel
|
||||||
|
|
||||||
err := d.View(func(tx *bolt.Tx) error {
|
err := d.View(func(tx *bolt.Tx) error {
|
||||||
@ -377,23 +414,36 @@ func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) {
|
|||||||
"channel for chain_hash=%x, "+
|
"channel for chain_hash=%x, "+
|
||||||
"node_key=%x: %v", chainHash[:], k, err)
|
"node_key=%x: %v", chainHash[:], k, err)
|
||||||
}
|
}
|
||||||
// TODO(roasbeef): simplify
|
for _, channel := range nodeChans {
|
||||||
if pendingOnly {
|
if channel.IsPending != pending {
|
||||||
for _, channel := range nodeChans {
|
continue
|
||||||
if channel.IsPending {
|
|
||||||
channels = append(channels, channel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
channels = append(channels, nodeChans...)
|
// If the channel is in any other state
|
||||||
|
// than Default, then it means it is
|
||||||
|
// waiting to be closed.
|
||||||
|
channelWaitingClose :=
|
||||||
|
channel.ChanStatus != Default
|
||||||
|
|
||||||
|
// Only include it if we requested
|
||||||
|
// channels with the same waitingClose
|
||||||
|
// status.
|
||||||
|
if channelWaitingClose != waitingClose {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
channels = append(channels, channel)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return channels, err
|
return channels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchClosedChannels attempts to fetch all closed channels from the database.
|
// FetchClosedChannels attempts to fetch all closed channels from the database.
|
||||||
|
@ -112,13 +112,19 @@ const (
|
|||||||
// so yet.
|
// so yet.
|
||||||
StateBroadcastCommit ArbitratorState = 1
|
StateBroadcastCommit ArbitratorState = 1
|
||||||
|
|
||||||
|
// StateCommitmentBroadcasted is a state that indicates that the
|
||||||
|
// attendant has broadcasted the commitment transaction, and is now
|
||||||
|
// waiting for it to confirm.
|
||||||
|
StateCommitmentBroadcasted ArbitratorState = 6
|
||||||
|
|
||||||
// StateContractClosed is a state that indicates the contract has
|
// StateContractClosed is a state that indicates the contract has
|
||||||
// already been "closed". At this point, we can now examine our active
|
// already been "closed", meaning the commitment is confirmed on chain.
|
||||||
// contracts, in order to create the proper resolver for each one.
|
// At this point, we can now examine our active contracts, in order to
|
||||||
|
// create the proper resolver for each one.
|
||||||
StateContractClosed ArbitratorState = 2
|
StateContractClosed ArbitratorState = 2
|
||||||
|
|
||||||
// StateWaitingFullResolution is a state that indicates that the
|
// StateWaitingFullResolution is a state that indicates that the
|
||||||
// commitment transaction has been broadcast, and the attendant is now
|
// commitment transaction has been confirmed, and the attendant is now
|
||||||
// waiting for all unresolved contracts to be fully resolved.
|
// waiting for all unresolved contracts to be fully resolved.
|
||||||
StateWaitingFullResolution ArbitratorState = 3
|
StateWaitingFullResolution ArbitratorState = 3
|
||||||
|
|
||||||
@ -142,6 +148,9 @@ func (a ArbitratorState) String() string {
|
|||||||
case StateBroadcastCommit:
|
case StateBroadcastCommit:
|
||||||
return "StateBroadcastCommit"
|
return "StateBroadcastCommit"
|
||||||
|
|
||||||
|
case StateCommitmentBroadcasted:
|
||||||
|
return "StateCommitmentBroadcasted"
|
||||||
|
|
||||||
case StateContractClosed:
|
case StateContractClosed:
|
||||||
return "StateContractClosed"
|
return "StateContractClosed"
|
||||||
|
|
||||||
|
@ -231,14 +231,9 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel,
|
|||||||
|
|
||||||
return chanMachine.ForceClose()
|
return chanMachine.ForceClose()
|
||||||
},
|
},
|
||||||
CloseChannel: func(summary *channeldb.ChannelCloseSummary) error {
|
MarkCommitmentBroadcasted: channel.MarkCommitmentBroadcasted,
|
||||||
log.Tracef("ChannelArbitrator(%v): closing "+
|
ChainArbitratorConfig: c.cfg,
|
||||||
"channel", chanPoint)
|
ChainEvents: chanEvents,
|
||||||
|
|
||||||
return channel.CloseChannel(summary)
|
|
||||||
},
|
|
||||||
ChainArbitratorConfig: c.cfg,
|
|
||||||
ChainEvents: chanEvents,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The final component needed is an arbitrator log that the arbitrator
|
// The final component needed is an arbitrator log that the arbitrator
|
||||||
|
@ -16,6 +16,13 @@ import (
|
|||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LocalUnilateralCloseInfo encapsulates all the informnation we need to act
|
||||||
|
// on a local force close that gets confirmed.
|
||||||
|
type LocalUnilateralCloseInfo struct {
|
||||||
|
*chainntnfs.SpendDetail
|
||||||
|
*lnwallet.LocalForceCloseSummary
|
||||||
|
}
|
||||||
|
|
||||||
// ChainEventSubscription is a struct that houses a subscription to be notified
|
// ChainEventSubscription is a struct that houses a subscription to be notified
|
||||||
// for any on-chain events related to a channel. There are three types of
|
// for any on-chain events related to a channel. There are three types of
|
||||||
// possible on-chain events: a cooperative channel closure, a unilateral
|
// possible on-chain events: a cooperative channel closure, a unilateral
|
||||||
@ -25,13 +32,16 @@ type ChainEventSubscription struct {
|
|||||||
// ChanPoint is that channel that chain events will be dispatched for.
|
// ChanPoint is that channel that chain events will be dispatched for.
|
||||||
ChanPoint wire.OutPoint
|
ChanPoint wire.OutPoint
|
||||||
|
|
||||||
// UnilateralClosure is a channel that will be sent upon in the event that
|
// RemoteUnilateralClosure is a channel that will be sent upon in the
|
||||||
// the remote party broadcasts their latest version of the commitment
|
// event that the remote party's commitment transaction is confirmed.
|
||||||
// transaction.
|
RemoteUnilateralClosure chan *lnwallet.UnilateralCloseSummary
|
||||||
UnilateralClosure chan *lnwallet.UnilateralCloseSummary
|
|
||||||
|
|
||||||
// CooperativeClosure is a signal that will be sent upon once a cooperative
|
// LocalUnilateralClosure is a channel that will be sent upon in the
|
||||||
// channel closure has been detected.
|
// event that our commitment transaction is confirmed.
|
||||||
|
LocalUnilateralClosure chan *LocalUnilateralCloseInfo
|
||||||
|
|
||||||
|
// CooperativeClosure is a signal that will be sent upon once a
|
||||||
|
// cooperative channel closure has been detected confirmed.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): or something else
|
// TODO(roasbeef): or something else
|
||||||
CooperativeClosure chan struct{}
|
CooperativeClosure chan struct{}
|
||||||
@ -180,7 +190,7 @@ func (c *chainWatcher) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
spendNtfn, err := c.notifier.RegisterSpendNtfn(
|
spendNtfn, err := c.notifier.RegisterSpendNtfn(
|
||||||
fundingOut, heightHint, true,
|
fundingOut, heightHint, false,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -226,10 +236,11 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs
|
|||||||
clientID, c.chanState.FundingOutpoint)
|
clientID, c.chanState.FundingOutpoint)
|
||||||
|
|
||||||
sub := &ChainEventSubscription{
|
sub := &ChainEventSubscription{
|
||||||
ChanPoint: c.chanState.FundingOutpoint,
|
ChanPoint: c.chanState.FundingOutpoint,
|
||||||
UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
||||||
CooperativeClosure: make(chan struct{}, 1),
|
LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1),
|
||||||
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
CooperativeClosure: make(chan struct{}, 1),
|
||||||
|
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
||||||
Cancel: func() {
|
Cancel: func() {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
delete(c.clientSubscriptions, clientID)
|
delete(c.clientSubscriptions, clientID)
|
||||||
@ -240,6 +251,30 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs
|
|||||||
|
|
||||||
if syncDispatch {
|
if syncDispatch {
|
||||||
sub.ProcessACK = make(chan error, 1)
|
sub.ProcessACK = make(chan error, 1)
|
||||||
|
|
||||||
|
// If this client is syncDispatch, we cannot safely delete it
|
||||||
|
// from our list of clients. This is because of a potential
|
||||||
|
// race at shutdown, where the client shuts down and calls
|
||||||
|
// Cancel(). In this case we must make sure the ChainWatcher
|
||||||
|
// doesn't think it has successfully handed off a contract
|
||||||
|
// breach to the client. We start a goroutine that will send an
|
||||||
|
// error on the ProcessACK channel until the ChainWatcher is
|
||||||
|
// shutdown.
|
||||||
|
sub.Cancel = func() {
|
||||||
|
c.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer c.wg.Done()
|
||||||
|
|
||||||
|
err := fmt.Errorf("cancelled")
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case sub.ProcessACK <- err:
|
||||||
|
case <-c.quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Lock()
|
c.Lock()
|
||||||
@ -307,6 +342,13 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
|
|||||||
&commitmentHash,
|
&commitmentHash,
|
||||||
)
|
)
|
||||||
if isOurCommitment {
|
if isOurCommitment {
|
||||||
|
if err := c.dispatchLocalForceClose(
|
||||||
|
commitSpend, *localCommit,
|
||||||
|
); err != nil {
|
||||||
|
log.Errorf("unable to handle local"+
|
||||||
|
"close for chan_point=%v: %v",
|
||||||
|
c.chanState.FundingOutpoint, err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,7 +391,7 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
|
|||||||
// has a fail crash _after_ accepting the new state,
|
// has a fail crash _after_ accepting the new state,
|
||||||
// but _before_ sending their signature to us.
|
// but _before_ sending their signature to us.
|
||||||
case broadcastStateNum >= remoteStateNum:
|
case broadcastStateNum >= remoteStateNum:
|
||||||
if err := c.dispatchRemoteClose(
|
if err := c.dispatchRemoteForceClose(
|
||||||
commitSpend, *remoteCommit,
|
commitSpend, *remoteCommit,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Errorf("unable to handle remote "+
|
log.Errorf("unable to handle remote "+
|
||||||
@ -500,12 +542,77 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatchRemoteClose processes a detected unilateral channel closure by the
|
// dispatchLocalForceClose processes a unilateral close by us being confirmed.
|
||||||
|
func (c *chainWatcher) dispatchLocalForceClose(
|
||||||
|
commitSpend *chainntnfs.SpendDetail,
|
||||||
|
localCommit channeldb.ChannelCommitment) error {
|
||||||
|
|
||||||
|
log.Infof("Local unilateral close of ChannelPoint(%v) "+
|
||||||
|
"detected", c.chanState.FundingOutpoint)
|
||||||
|
|
||||||
|
forceClose, err := lnwallet.NewLocalForceCloseSummary(
|
||||||
|
c.chanState, c.signer, c.pCache, commitSpend.SpendingTx,
|
||||||
|
localCommit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// As we've detected that the channel has been closed, immediately
|
||||||
|
// delete the state from disk, creating a close summary for future
|
||||||
|
// usage by related sub-systems.
|
||||||
|
chanSnapshot := forceClose.ChanSnapshot
|
||||||
|
closeSummary := &channeldb.ChannelCloseSummary{
|
||||||
|
ChanPoint: chanSnapshot.ChannelPoint,
|
||||||
|
ChainHash: chanSnapshot.ChainHash,
|
||||||
|
ClosingTXID: forceClose.CloseTx.TxHash(),
|
||||||
|
RemotePub: &chanSnapshot.RemoteIdentity,
|
||||||
|
Capacity: chanSnapshot.Capacity,
|
||||||
|
CloseType: channeldb.LocalForceClose,
|
||||||
|
IsPending: true,
|
||||||
|
ShortChanID: c.chanState.ShortChanID,
|
||||||
|
CloseHeight: uint32(commitSpend.SpendingHeight),
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our commitment output isn't dust or we have active HTLC's on the
|
||||||
|
// commitment transaction, then we'll populate the balances on the
|
||||||
|
// close channel summary.
|
||||||
|
if forceClose.CommitResolution != nil {
|
||||||
|
closeSummary.SettledBalance = chanSnapshot.LocalBalance.ToSatoshis()
|
||||||
|
closeSummary.TimeLockedBalance = chanSnapshot.LocalBalance.ToSatoshis()
|
||||||
|
}
|
||||||
|
for _, htlc := range forceClose.HtlcResolutions.OutgoingHTLCs {
|
||||||
|
htlcValue := btcutil.Amount(htlc.SweepSignDesc.Output.Value)
|
||||||
|
closeSummary.TimeLockedBalance += htlcValue
|
||||||
|
}
|
||||||
|
err = c.chanState.CloseChannel(closeSummary)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to delete channel state: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the event processed, we'll now notify all subscribers of the
|
||||||
|
// event.
|
||||||
|
closeInfo := &LocalUnilateralCloseInfo{commitSpend, forceClose}
|
||||||
|
c.Lock()
|
||||||
|
for _, sub := range c.clientSubscriptions {
|
||||||
|
select {
|
||||||
|
case sub.LocalUnilateralClosure <- closeInfo:
|
||||||
|
case <-c.quit:
|
||||||
|
c.Unlock()
|
||||||
|
return fmt.Errorf("exiting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatchRemoteForceClose processes a detected unilateral channel closure by the
|
||||||
// remote party. This function will prepare a UnilateralCloseSummary which will
|
// remote party. This function will prepare a UnilateralCloseSummary which will
|
||||||
// then be sent to any subscribers allowing them to resolve all our funds in
|
// then be sent to any subscribers allowing them to resolve all our funds in
|
||||||
// the channel on chain. Once this close summary is prepared, all registered
|
// the channel on chain. Once this close summary is prepared, all registered
|
||||||
// subscribers will receive a notification of this event.
|
// subscribers will receive a notification of this event.
|
||||||
func (c *chainWatcher) dispatchRemoteClose(commitSpend *chainntnfs.SpendDetail,
|
func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDetail,
|
||||||
remoteCommit channeldb.ChannelCommitment) error {
|
remoteCommit channeldb.ChannelCommitment) error {
|
||||||
|
|
||||||
log.Infof("Unilateral close of ChannelPoint(%v) "+
|
log.Infof("Unilateral close of ChannelPoint(%v) "+
|
||||||
@ -538,7 +645,7 @@ func (c *chainWatcher) dispatchRemoteClose(commitSpend *chainntnfs.SpendDetail,
|
|||||||
// * get ACK from the consumer of the ntfn before writing to disk?
|
// * get ACK from the consumer of the ntfn before writing to disk?
|
||||||
// * no harm in repeated ntfns: at least once semantics
|
// * no harm in repeated ntfns: at least once semantics
|
||||||
select {
|
select {
|
||||||
case sub.UnilateralClosure <- uniClose:
|
case sub.RemoteUnilateralClosure <- uniClose:
|
||||||
case <-c.quit:
|
case <-c.quit:
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
return fmt.Errorf("exiting")
|
return fmt.Errorf("exiting")
|
||||||
|
@ -10,9 +10,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
"github.com/roasbeef/btcutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -91,11 +89,9 @@ type ChannelArbitratorConfig struct {
|
|||||||
// eventually resolve all outputs on chain.
|
// eventually resolve all outputs on chain.
|
||||||
ForceCloseChan func() (*lnwallet.LocalForceCloseSummary, error)
|
ForceCloseChan func() (*lnwallet.LocalForceCloseSummary, error)
|
||||||
|
|
||||||
// CloseChannel is a function closure that marks a channel under watch
|
// MarkCommitmentBroadcasted should mark the channel as the commitment
|
||||||
// as "closing". In this phase, we will no longer accept any updates to
|
// being broadcast, and we are waiting for the commitment to confirm.
|
||||||
// the channel as the commitment transaction has been broadcast, and
|
MarkCommitmentBroadcasted func() error
|
||||||
// possibly fully confirmed.
|
|
||||||
CloseChannel func(*channeldb.ChannelCloseSummary) error
|
|
||||||
|
|
||||||
// MarkChannelResolved is a function closure that serves to mark a
|
// MarkChannelResolved is a function closure that serves to mark a
|
||||||
// channel as "fully resolved". A channel itself can be considered
|
// channel as "fully resolved". A channel itself can be considered
|
||||||
@ -239,7 +235,7 @@ func (c *ChannelArbitrator) Start() error {
|
|||||||
log.Infof("ChannelArbitrator(%v): starting state=%v", c.cfg.ChanPoint,
|
log.Infof("ChannelArbitrator(%v): starting state=%v", c.cfg.ChanPoint,
|
||||||
c.state)
|
c.state)
|
||||||
|
|
||||||
bestHash, bestHeight, err := c.cfg.ChainIO.GetBestBlock()
|
_, bestHeight, err := c.cfg.ChainIO.GetBestBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -248,7 +244,7 @@ func (c *ChannelArbitrator) Start() error {
|
|||||||
// on-chain state, and our set of active contracts.
|
// on-chain state, and our set of active contracts.
|
||||||
startingState := c.state
|
startingState := c.state
|
||||||
nextState, _, err := c.advanceState(
|
nextState, _, err := c.advanceState(
|
||||||
uint32(bestHeight), bestHash, chainTrigger, nil,
|
uint32(bestHeight), chainTrigger, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -280,7 +276,7 @@ func (c *ChannelArbitrator) Start() error {
|
|||||||
// TODO(roasbeef): cancel if breached
|
// TODO(roasbeef): cancel if breached
|
||||||
|
|
||||||
c.wg.Add(1)
|
c.wg.Add(1)
|
||||||
go c.channelAttendant(bestHeight, bestHash)
|
go c.channelAttendant(bestHeight)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,14 +316,18 @@ const (
|
|||||||
// being attached.
|
// being attached.
|
||||||
chainTrigger transitionTrigger = iota
|
chainTrigger transitionTrigger = iota
|
||||||
|
|
||||||
// remotePeerTrigger is a transition trigger driven by actions of the
|
|
||||||
// remote peer.
|
|
||||||
remotePeerTrigger
|
|
||||||
|
|
||||||
// userTrigger is a transition trigger driven by user action. Examples
|
// userTrigger is a transition trigger driven by user action. Examples
|
||||||
// of such a trigger include a user requesting a forgive closure of the
|
// of such a trigger include a user requesting a force closure of the
|
||||||
// channel.
|
// channel.
|
||||||
userTrigger
|
userTrigger
|
||||||
|
|
||||||
|
// remoteCloseTrigger is a transition trigger driven by the remote
|
||||||
|
// peer's commitment being confirmed.
|
||||||
|
remoteCloseTrigger
|
||||||
|
|
||||||
|
// localCloseTrigger is a transition trigger driven by our commitment
|
||||||
|
// being confirmed.
|
||||||
|
localCloseTrigger
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns a human readable string describing the passed
|
// String returns a human readable string describing the passed
|
||||||
@ -337,12 +337,15 @@ func (t transitionTrigger) String() string {
|
|||||||
case chainTrigger:
|
case chainTrigger:
|
||||||
return "chainTrigger"
|
return "chainTrigger"
|
||||||
|
|
||||||
case remotePeerTrigger:
|
case remoteCloseTrigger:
|
||||||
return "remotePeerTrigger"
|
return "remoteCloseTrigger"
|
||||||
|
|
||||||
case userTrigger:
|
case userTrigger:
|
||||||
return "userTrigger"
|
return "userTrigger"
|
||||||
|
|
||||||
|
case localCloseTrigger:
|
||||||
|
return "localCloseTrigger"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "unknown trigger"
|
return "unknown trigger"
|
||||||
}
|
}
|
||||||
@ -352,7 +355,7 @@ func (t transitionTrigger) String() string {
|
|||||||
// the appropriate state transition if necessary. The next state we transition
|
// the appropriate state transition if necessary. The next state we transition
|
||||||
// to is returned, Additionally, if the next transition results in a commitment
|
// to is returned, Additionally, if the next transition results in a commitment
|
||||||
// broadcast, the commitment transaction itself is returned.
|
// broadcast, the commitment transaction itself is returned.
|
||||||
func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Hash,
|
func (c *ChannelArbitrator) stateStep(triggerHeight uint32,
|
||||||
trigger transitionTrigger) (ArbitratorState, *wire.MsgTx, error) {
|
trigger transitionTrigger) (ArbitratorState, *wire.MsgTx, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -364,16 +367,15 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
|
|||||||
// If we're in the default state, then we'll check our set of actions
|
// If we're in the default state, then we'll check our set of actions
|
||||||
// to see if while we were down, conditions have changed.
|
// to see if while we were down, conditions have changed.
|
||||||
case StateDefault:
|
case StateDefault:
|
||||||
log.Debugf("ChannelArbitrator(%v): new block (height=%v, "+
|
log.Debugf("ChannelArbitrator(%v): new block (height=%v) "+
|
||||||
"hash=%v) examining active HTLC's",
|
"examining active HTLC's", c.cfg.ChanPoint,
|
||||||
c.cfg.ChanPoint, bestHeight, bestHash)
|
triggerHeight)
|
||||||
|
|
||||||
// As a new block has been connected to the end of the main
|
// As a new block has been connected to the end of the main
|
||||||
// chain, we'll check to see if we need to make any on-chain
|
// chain, we'll check to see if we need to make any on-chain
|
||||||
// claims on behalf of the channel contract that we're
|
// claims on behalf of the channel contract that we're
|
||||||
// arbitrating for.
|
// arbitrating for.
|
||||||
chainActions := c.checkChainActions(uint32(bestHeight),
|
chainActions := c.checkChainActions(triggerHeight, trigger)
|
||||||
trigger)
|
|
||||||
|
|
||||||
// If there are no actions to be made, then we'll remain in the
|
// If there are no actions to be made, then we'll remain in the
|
||||||
// default state. If this isn't a self initiated event (we're
|
// default state. If this isn't a self initiated event (we're
|
||||||
@ -409,10 +411,16 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
|
|||||||
case userTrigger:
|
case userTrigger:
|
||||||
nextState = StateBroadcastCommit
|
nextState = StateBroadcastCommit
|
||||||
|
|
||||||
// Otherwise, if this state advance was triggered by the remote
|
// Otherwise, if this state advance was triggered by a
|
||||||
// peer, then we'll jump straight to the state where the
|
// commitment being confirmed on chain, then we'll jump
|
||||||
// contract has already been closed.
|
// straight to the state where the contract has already been
|
||||||
case remotePeerTrigger:
|
// closed.
|
||||||
|
case localCloseTrigger:
|
||||||
|
log.Errorf("ChannelArbitrator(%v): unexpected local "+
|
||||||
|
"commitment confirmed while in StateDefault",
|
||||||
|
c.cfg.ChanPoint)
|
||||||
|
fallthrough
|
||||||
|
case remoteCloseTrigger:
|
||||||
nextState = StateContractClosed
|
nextState = StateContractClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,60 +464,38 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// As we've have broadcast the commitment transaction, we send
|
if err := c.cfg.MarkCommitmentBroadcasted(); err != nil {
|
||||||
// out commitment output for incubation, but only if it wasn't
|
log.Errorf("ChannelArbitrator(%v): unable to "+
|
||||||
// trimmed. We'll need to wait for a CSV timeout before we can
|
"mark commitment broadcasted: %v",
|
||||||
// reclaim the funds.
|
c.cfg.ChanPoint, err)
|
||||||
if closeSummary.CommitResolution != nil {
|
|
||||||
log.Infof("ChannelArbitrator(%v): sending commit "+
|
|
||||||
"output for incubation", c.cfg.ChanPoint)
|
|
||||||
|
|
||||||
err = c.cfg.IncubateOutputs(
|
|
||||||
c.cfg.ChanPoint, closeSummary.CommitResolution,
|
|
||||||
nil, nil,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
// TODO(roasbeef): check for AlreadyExists errors
|
|
||||||
log.Errorf("unable to incubate commitment "+
|
|
||||||
"output: %v", err)
|
|
||||||
return StateError, closeTx, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contractRes := ContractResolutions{
|
// We go to the StateCommitmentBroadcasted state, where we'll
|
||||||
CommitHash: closeTx.TxHash(),
|
// be waiting for the commitment to be confirmed.
|
||||||
CommitResolution: closeSummary.CommitResolution,
|
nextState = StateCommitmentBroadcasted
|
||||||
HtlcResolutions: *closeSummary.HtlcResolutions,
|
|
||||||
|
// In this state we have broadcasted our own commitment, and will need
|
||||||
|
// to wait for a commitment (not necessarily the one we broadcasted!)
|
||||||
|
// to be confirmed.
|
||||||
|
case StateCommitmentBroadcasted:
|
||||||
|
switch trigger {
|
||||||
|
// We are waiting for a commitment to be confirmed, so any
|
||||||
|
// other trigger will be ignored.
|
||||||
|
case chainTrigger, userTrigger:
|
||||||
|
log.Infof("ChannelArbitrator(%v): noop state %v",
|
||||||
|
c.cfg.ChanPoint, trigger)
|
||||||
|
nextState = StateCommitmentBroadcasted
|
||||||
|
|
||||||
|
// If this state advance was triggered by any of the
|
||||||
|
// commitments being confirmed, then we'll jump to the state
|
||||||
|
// where the contract has been closed.
|
||||||
|
case localCloseTrigger, remoteCloseTrigger:
|
||||||
|
log.Infof("ChannelArbitrator(%v): state %v, "+
|
||||||
|
" going to StateContractClosed",
|
||||||
|
c.cfg.ChanPoint, trigger)
|
||||||
|
nextState = StateContractClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that the transaction has been broadcast, we can mark
|
|
||||||
// that it has been closed to outside sub-systems.
|
|
||||||
err = c.markContractClosed(
|
|
||||||
closeTx, closeSummary.ChanSnapshot, &contractRes,
|
|
||||||
bestHeight,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to close contract: %v", err)
|
|
||||||
return StateError, closeTx, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// With the channel force closed, we'll now log our
|
|
||||||
// resolutions, then advance our state forward.
|
|
||||||
log.Infof("ChannelArbitrator(%v): logging contract "+
|
|
||||||
"resolutions: commit=%v, num_htlcs=%v",
|
|
||||||
c.cfg.ChanPoint,
|
|
||||||
closeSummary.CommitResolution != nil,
|
|
||||||
len(closeSummary.HtlcResolutions.IncomingHTLCs)+
|
|
||||||
len(closeSummary.HtlcResolutions.OutgoingHTLCs))
|
|
||||||
|
|
||||||
err = c.log.LogContractResolutions(&contractRes)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to write resolutions: %v", err)
|
|
||||||
return StateError, closeTx, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nextState = StateContractClosed
|
|
||||||
|
|
||||||
// If we're in this state, then the contract has been fully closed to
|
// If we're in this state, then the contract has been fully closed to
|
||||||
// outside sub-systems, so we'll process the prior set of on-chain
|
// outside sub-systems, so we'll process the prior set of on-chain
|
||||||
// contract actions and launch a set of resolvers.
|
// contract actions and launch a set of resolvers.
|
||||||
@ -539,12 +525,32 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we've have broadcast the commitment transaction, we send
|
||||||
|
// our commitment output for incubation, but only if it wasn't
|
||||||
|
// trimmed. We'll need to wait for a CSV timeout before we can
|
||||||
|
// reclaim the funds.
|
||||||
|
commitRes := contractResolutions.CommitResolution
|
||||||
|
if commitRes != nil && commitRes.MaturityDelay > 0 {
|
||||||
|
log.Infof("ChannelArbitrator(%v): sending commit "+
|
||||||
|
"output for incubation", c.cfg.ChanPoint)
|
||||||
|
|
||||||
|
err = c.cfg.IncubateOutputs(
|
||||||
|
c.cfg.ChanPoint, commitRes,
|
||||||
|
nil, nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
// TODO(roasbeef): check for AlreadyExists errors
|
||||||
|
log.Errorf("unable to incubate commitment "+
|
||||||
|
"output: %v", err)
|
||||||
|
return StateError, closeTx, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we know we'll need to act, we'll process the htlc
|
// Now that we know we'll need to act, we'll process the htlc
|
||||||
// actions, wen create the structures we need to resolve all
|
// actions, wen create the structures we need to resolve all
|
||||||
// outstanding contracts.
|
// outstanding contracts.
|
||||||
htlcResolvers, pktsToSend, err := c.prepContractResolutions(
|
htlcResolvers, pktsToSend, err := c.prepContractResolutions(
|
||||||
chainActions, contractResolutions, uint32(bestHeight),
|
chainActions, contractResolutions, triggerHeight,
|
||||||
trigger,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("ChannelArbitrator(%v): unable to "+
|
log.Errorf("ChannelArbitrator(%v): unable to "+
|
||||||
@ -601,7 +607,7 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
|
|||||||
nextState = StateFullyResolved
|
nextState = StateFullyResolved
|
||||||
|
|
||||||
log.Infof("ChannelPoint(%v) has been fully resolved "+
|
log.Infof("ChannelPoint(%v) has been fully resolved "+
|
||||||
"on-chain at height=%v", c.cfg.ChanPoint, bestHeight)
|
"on-chain at height=%v", c.cfg.ChanPoint, triggerHeight)
|
||||||
return nextState, closeTx, c.cfg.MarkChannelResolved()
|
return nextState, closeTx, c.cfg.MarkChannelResolved()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,25 +628,25 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has
|
|||||||
// redundant transition, meaning that the state transition is a noop. The final
|
// redundant transition, meaning that the state transition is a noop. The final
|
||||||
// param is a callback that allows the caller to execute an arbitrary action
|
// param is a callback that allows the caller to execute an arbitrary action
|
||||||
// after each state transition.
|
// after each state transition.
|
||||||
func (c *ChannelArbitrator) advanceState(currentHeight uint32,
|
func (c *ChannelArbitrator) advanceState(triggerHeight uint32,
|
||||||
bestHash *chainhash.Hash, trigger transitionTrigger,
|
trigger transitionTrigger, stateCallback func(ArbitratorState) error) (
|
||||||
stateCallback func(ArbitratorState) error) (ArbitratorState, *wire.MsgTx, error) {
|
ArbitratorState, *wire.MsgTx, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
priorState ArbitratorState
|
priorState ArbitratorState
|
||||||
forceCloseTx *wire.MsgTx
|
forceCloseTx *wire.MsgTx
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Tracef("ChannelArbitrator(%v): attempting state step with "+
|
|
||||||
"trigger=%v", c.cfg.ChanPoint, trigger)
|
|
||||||
|
|
||||||
// We'll continue to advance our state forward until the state we
|
// We'll continue to advance our state forward until the state we
|
||||||
// transition to is that same state that we started at.
|
// transition to is that same state that we started at.
|
||||||
for {
|
for {
|
||||||
priorState = c.state
|
priorState = c.state
|
||||||
|
log.Tracef("ChannelArbitrator(%v): attempting state step with "+
|
||||||
|
"trigger=%v from state=%v", c.cfg.ChanPoint, trigger,
|
||||||
|
priorState)
|
||||||
|
|
||||||
nextState, closeTx, err := c.stateStep(
|
nextState, closeTx, err := c.stateStep(
|
||||||
currentHeight, bestHash, trigger,
|
triggerHeight, trigger,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to advance state: %v", err)
|
log.Errorf("unable to advance state: %v", err)
|
||||||
@ -926,7 +932,6 @@ func (c *ChannelArbitrator) checkChainActions(height uint32,
|
|||||||
// are properly resolved.
|
// are properly resolved.
|
||||||
func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap,
|
func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap,
|
||||||
contractResolutions *ContractResolutions, height uint32,
|
contractResolutions *ContractResolutions, height uint32,
|
||||||
trigger transitionTrigger,
|
|
||||||
) ([]ContractResolver, []ResolutionMsg, error) {
|
) ([]ContractResolver, []ResolutionMsg, error) {
|
||||||
|
|
||||||
// There may be a class of HTLC's which we can fail back immediately,
|
// There may be a class of HTLC's which we can fail back immediately,
|
||||||
@ -1278,8 +1283,7 @@ func (c *ChannelArbitrator) UpdateContractSignals(newSignals *ContractSignals) {
|
|||||||
// Nursery for incubation, and ultimate sweeping.
|
// Nursery for incubation, and ultimate sweeping.
|
||||||
//
|
//
|
||||||
// NOTE: This MUST be run as a goroutine.
|
// NOTE: This MUST be run as a goroutine.
|
||||||
func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
|
||||||
bestHash *chainhash.Hash) {
|
|
||||||
|
|
||||||
// TODO(roasbeef): tell top chain arb we're done
|
// TODO(roasbeef): tell top chain arb we're done
|
||||||
defer c.wg.Done()
|
defer c.wg.Done()
|
||||||
@ -1295,7 +1299,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
bestHeight = blockEpoch.Height
|
bestHeight = blockEpoch.Height
|
||||||
bestHash = blockEpoch.Hash
|
|
||||||
|
|
||||||
// If we're not in the default state, then we can
|
// If we're not in the default state, then we can
|
||||||
// ignore this signal as we're waiting for contract
|
// ignore this signal as we're waiting for contract
|
||||||
@ -1307,7 +1310,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
// Now that a new block has arrived, we'll attempt to
|
// Now that a new block has arrived, we'll attempt to
|
||||||
// advance our state forward.
|
// advance our state forward.
|
||||||
nextState, _, err := c.advanceState(
|
nextState, _, err := c.advanceState(
|
||||||
uint32(bestHeight), bestHash, chainTrigger, nil,
|
uint32(bestHeight), chainTrigger, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to advance state: %v", err)
|
log.Errorf("unable to advance state: %v", err)
|
||||||
@ -1352,20 +1355,67 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
// We've cooperatively closed the channel, so we're no longer
|
// We've cooperatively closed the channel, so we're no longer
|
||||||
// needed.
|
// needed.
|
||||||
case <-c.cfg.ChainEvents.CooperativeClosure:
|
case <-c.cfg.ChainEvents.CooperativeClosure:
|
||||||
log.Infof("ChannelArbitrator(%v) closing due to co-op "+
|
log.Infof("ChannelArbitrator(%v) closing due to co-op "+
|
||||||
"closure", c.cfg.ChanPoint)
|
"closure", c.cfg.ChanPoint)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// We have broadcasted our commitment, and it is now confirmed
|
||||||
|
// on-chain.
|
||||||
|
case closeInfo := <-c.cfg.ChainEvents.LocalUnilateralClosure:
|
||||||
|
log.Infof("ChannelArbitrator(%v): local on-chain "+
|
||||||
|
"channel close", c.cfg.ChanPoint)
|
||||||
|
|
||||||
|
if c.state != StateCommitmentBroadcasted {
|
||||||
|
log.Errorf("ChannelArbitrator(%v): unexpected "+
|
||||||
|
"local on-chain channel close",
|
||||||
|
c.cfg.ChanPoint)
|
||||||
|
}
|
||||||
|
closeTx := closeInfo.CloseTx
|
||||||
|
|
||||||
|
contractRes := &ContractResolutions{
|
||||||
|
CommitHash: closeTx.TxHash(),
|
||||||
|
CommitResolution: closeInfo.CommitResolution,
|
||||||
|
HtlcResolutions: *closeInfo.HtlcResolutions,
|
||||||
|
}
|
||||||
|
|
||||||
|
// When processing a unilateral close event, we'll
|
||||||
|
// transition directly to the ContractClosed state.
|
||||||
|
// When the state machine reaches that state, we'll log
|
||||||
|
// out the set of resolutions.
|
||||||
|
stateCb := func(nextState ArbitratorState) error {
|
||||||
|
if nextState != StateContractClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.log.LogContractResolutions(
|
||||||
|
contractRes,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to "+
|
||||||
|
"write resolutions: %v",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll now advance our state machine until it reaches
|
||||||
|
// a terminal state.
|
||||||
|
_, _, err := c.advanceState(
|
||||||
|
uint32(closeInfo.SpendingHeight),
|
||||||
|
localCloseTrigger, stateCb,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to advance state: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// The remote party has broadcast the commitment on-chain.
|
// The remote party has broadcast the commitment on-chain.
|
||||||
// We'll examine our state to determine if we need to act at
|
// We'll examine our state to determine if we need to act at
|
||||||
// all.
|
// all.
|
||||||
case uniClosure := <-c.cfg.ChainEvents.UnilateralClosure:
|
case uniClosure := <-c.cfg.ChainEvents.RemoteUnilateralClosure:
|
||||||
if c.state != StateDefault {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("ChannelArbitrator(%v): remote party has "+
|
log.Infof("ChannelArbitrator(%v): remote party has "+
|
||||||
"closed channel out on-chain", c.cfg.ChanPoint)
|
"closed channel out on-chain", c.cfg.ChanPoint)
|
||||||
@ -1378,17 +1428,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
CommitResolution: uniClosure.CommitResolution,
|
CommitResolution: uniClosure.CommitResolution,
|
||||||
HtlcResolutions: *uniClosure.HtlcResolutions,
|
HtlcResolutions: *uniClosure.HtlcResolutions,
|
||||||
}
|
}
|
||||||
if contractRes.IsEmpty() {
|
|
||||||
log.Infof("ChannelArbitrator(%v): contract "+
|
|
||||||
"resolutions empty, exiting", c.cfg.ChanPoint)
|
|
||||||
|
|
||||||
err := c.cfg.MarkChannelResolved()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to resolve "+
|
|
||||||
"contract: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(roasbeef): modify signal to also detect
|
// TODO(roasbeef): modify signal to also detect
|
||||||
// cooperative closures?
|
// cooperative closures?
|
||||||
@ -1399,19 +1438,21 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
// present on their commitment.
|
// present on their commitment.
|
||||||
c.activeHTLCs = newHtlcSet(uniClosure.RemoteCommit.Htlcs)
|
c.activeHTLCs = newHtlcSet(uniClosure.RemoteCommit.Htlcs)
|
||||||
|
|
||||||
// When processing a remote party initiated event,
|
// When processing a unilateral close event, we'll
|
||||||
// we'll skip the BroadcastCommit state, and transition
|
// transition directly to the ContractClosed state.
|
||||||
// directly to the ContractClosed state. As a result,
|
// When the state machine reaches that state, we'll log
|
||||||
// we'll now manually log out set of resolutions.
|
// out the set of resolutions.
|
||||||
stateCb := func(nextState ArbitratorState) error {
|
stateCb := func(nextState ArbitratorState) error {
|
||||||
if nextState == StateContractClosed {
|
if nextState != StateContractClosed {
|
||||||
err := c.log.LogContractResolutions(
|
return nil
|
||||||
contractRes,
|
}
|
||||||
)
|
|
||||||
if err != nil {
|
err := c.log.LogContractResolutions(
|
||||||
return fmt.Errorf("unable to write "+
|
contractRes,
|
||||||
"resolutions: %v", err)
|
)
|
||||||
}
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to write "+
|
||||||
|
"resolutions: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1420,8 +1461,8 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
// We'll now advance our state machine until it reaches
|
// We'll now advance our state machine until it reaches
|
||||||
// a terminal state.
|
// a terminal state.
|
||||||
_, _, err := c.advanceState(
|
_, _, err := c.advanceState(
|
||||||
uint32(bestHeight), bestHash,
|
uint32(uniClosure.SpendingHeight),
|
||||||
remotePeerTrigger, stateCb,
|
remoteCloseTrigger, stateCb,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to advance state: %v", err)
|
log.Errorf("unable to advance state: %v", err)
|
||||||
@ -1466,7 +1507,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextState, closeTx, err := c.advanceState(
|
nextState, closeTx, err := c.advanceState(
|
||||||
uint32(bestHeight), bestHash, userTrigger, nil,
|
uint32(bestHeight), userTrigger, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to advance state: %v", err)
|
log.Errorf("unable to advance state: %v", err)
|
||||||
@ -1490,6 +1531,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
log.Infof("ChannelArbitrator(%v): all "+
|
log.Infof("ChannelArbitrator(%v): all "+
|
||||||
"contracts resolved, exiting",
|
"contracts resolved, exiting",
|
||||||
c.cfg.ChanPoint)
|
c.cfg.ChanPoint)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-c.quit:
|
case <-c.quit:
|
||||||
@ -1497,39 +1539,3 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// markContractClosed marks a contract as "pending closed". After this state,
|
|
||||||
// upon restart, we'll no longer watch for updates to the set of contracts as
|
|
||||||
// the channel cannot be updated any longer.
|
|
||||||
func (c *ChannelArbitrator) markContractClosed(closeTx *wire.MsgTx,
|
|
||||||
chanSnapshot channeldb.ChannelSnapshot,
|
|
||||||
contractResolution *ContractResolutions,
|
|
||||||
closeHeight uint32) error {
|
|
||||||
|
|
||||||
// TODO(roasbeef): also need height info?
|
|
||||||
closeInfo := &channeldb.ChannelCloseSummary{
|
|
||||||
ChanPoint: chanSnapshot.ChannelPoint,
|
|
||||||
ChainHash: chanSnapshot.ChainHash,
|
|
||||||
ClosingTXID: closeTx.TxHash(),
|
|
||||||
RemotePub: &chanSnapshot.RemoteIdentity,
|
|
||||||
Capacity: chanSnapshot.Capacity,
|
|
||||||
CloseType: channeldb.ForceClose,
|
|
||||||
IsPending: true,
|
|
||||||
ShortChanID: c.cfg.ShortChanID,
|
|
||||||
CloseHeight: closeHeight,
|
|
||||||
}
|
|
||||||
|
|
||||||
// If our commitment output isn't dust or we have active HTLC's on the
|
|
||||||
// commitment transaction, then we'll populate the balances on the
|
|
||||||
// close channel summary.
|
|
||||||
if contractResolution.CommitResolution != nil {
|
|
||||||
closeInfo.SettledBalance = chanSnapshot.LocalBalance.ToSatoshis()
|
|
||||||
closeInfo.TimeLockedBalance = chanSnapshot.LocalBalance.ToSatoshis()
|
|
||||||
}
|
|
||||||
for _, htlc := range contractResolution.HtlcResolutions.OutgoingHTLCs {
|
|
||||||
htlcValue := btcutil.Amount(htlc.SweepSignDesc.Output.Value)
|
|
||||||
closeInfo.TimeLockedBalance += htlcValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.cfg.CloseChannel(closeInfo)
|
|
||||||
}
|
|
||||||
|
@ -1 +1,341 @@
|
|||||||
package contractcourt
|
package contractcourt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockChainIO struct{}
|
||||||
|
|
||||||
|
func (*mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) {
|
||||||
|
return nil, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*mockChainIO) GetUtxo(op *wire.OutPoint,
|
||||||
|
heightHint uint32) (*wire.TxOut, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestChannelArbitrator() (*ChannelArbitrator, chan struct{}, func(), error) {
|
||||||
|
blockEpoch := &chainntnfs.BlockEpochEvent{
|
||||||
|
Cancel: func() {},
|
||||||
|
}
|
||||||
|
|
||||||
|
chanPoint := wire.OutPoint{}
|
||||||
|
shortChanID := lnwire.ShortChannelID{}
|
||||||
|
chanEvents := &ChainEventSubscription{
|
||||||
|
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1),
|
||||||
|
LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1),
|
||||||
|
CooperativeClosure: make(chan struct{}, 1),
|
||||||
|
ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
chainIO := &mockChainIO{}
|
||||||
|
chainArbCfg := ChainArbitratorConfig{
|
||||||
|
ChainIO: chainIO,
|
||||||
|
PublishTx: func(*wire.MsgTx) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll use the resolvedChan to synchronize on call to
|
||||||
|
// MarkChannelResolved.
|
||||||
|
resolvedChan := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
// Next we'll create the matching configuration struct that contains
|
||||||
|
// all interfaces and methods the arbitrator needs to do its job.
|
||||||
|
arbCfg := ChannelArbitratorConfig{
|
||||||
|
ChanPoint: chanPoint,
|
||||||
|
ShortChanID: shortChanID,
|
||||||
|
BlockEpochs: blockEpoch,
|
||||||
|
MarkChannelResolved: func() error {
|
||||||
|
resolvedChan <- struct{}{}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
ForceCloseChan: func() (*lnwallet.LocalForceCloseSummary, error) {
|
||||||
|
summary := &lnwallet.LocalForceCloseSummary{
|
||||||
|
CloseTx: &wire.MsgTx{},
|
||||||
|
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
||||||
|
}
|
||||||
|
return summary, nil
|
||||||
|
},
|
||||||
|
MarkCommitmentBroadcasted: func() error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
ChainArbitratorConfig: chainArbCfg,
|
||||||
|
ChainEvents: chanEvents,
|
||||||
|
}
|
||||||
|
testLog, cleanUp, err := newTestBoltArbLog(
|
||||||
|
testChainHash, testChanPoint1,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("unable to create test log: %v",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewChannelArbitrator(arbCfg, nil, testLog),
|
||||||
|
resolvedChan, cleanUp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertState checks that the ChannelArbitrator is in the state we expect it
|
||||||
|
// to be.
|
||||||
|
func assertState(t *testing.T, c *ChannelArbitrator, expected ArbitratorState) {
|
||||||
|
if c.state != expected {
|
||||||
|
t.Fatalf("expected state %v, was %v", expected, c.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannelArbitratorCooperativeClose tests that the ChannelArbitertor
|
||||||
|
// correctly does nothing in case a cooperative close is confirmed.
|
||||||
|
func TestChannelArbitratorCooperativeClose(t *testing.T) {
|
||||||
|
chanArb, _, cleanUp, err := createTestChannelArbitrator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
if err := chanArb.Start(); err != nil {
|
||||||
|
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer chanArb.Stop()
|
||||||
|
|
||||||
|
// It should start out in the default state.
|
||||||
|
assertState(t, chanArb, StateDefault)
|
||||||
|
|
||||||
|
// Cooperative close should do nothing.
|
||||||
|
// TODO: this will change?
|
||||||
|
chanArb.cfg.ChainEvents.CooperativeClosure <- struct{}{}
|
||||||
|
assertState(t, chanArb, StateDefault)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannelArbitratorRemoteForceClose checks that the ChannelArbitrotor goes
|
||||||
|
// through the expected states if a remote force close is observed in the
|
||||||
|
// chain.
|
||||||
|
func TestChannelArbitratorRemoteForceClose(t *testing.T) {
|
||||||
|
chanArb, resolved, cleanUp, err := createTestChannelArbitrator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
if err := chanArb.Start(); err != nil {
|
||||||
|
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer chanArb.Stop()
|
||||||
|
|
||||||
|
// It should start out in the default state.
|
||||||
|
assertState(t, chanArb, StateDefault)
|
||||||
|
|
||||||
|
// Send a remote force close event.
|
||||||
|
commitSpend := &chainntnfs.SpendDetail{
|
||||||
|
SpenderTxHash: &chainhash.Hash{},
|
||||||
|
}
|
||||||
|
|
||||||
|
uniClose := &lnwallet.UnilateralCloseSummary{
|
||||||
|
SpendDetail: commitSpend,
|
||||||
|
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
||||||
|
}
|
||||||
|
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose
|
||||||
|
|
||||||
|
// It should mark the channel as resolved.
|
||||||
|
select {
|
||||||
|
case <-resolved:
|
||||||
|
// Expected.
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatalf("contract was not resolved")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: intermediate states.
|
||||||
|
// We expect the ChannelArbitrator to end up in the the resolved state.
|
||||||
|
assertState(t, chanArb, StateFullyResolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannelArbitratorLocalForceClose tests that the ChannelArbitrator goes
|
||||||
|
// through the expected states in case we request it to force close the channel,
|
||||||
|
// and the local force close event is observed in chain.
|
||||||
|
func TestChannelArbitratorLocalForceClose(t *testing.T) {
|
||||||
|
chanArb, resolved, cleanUp, err := createTestChannelArbitrator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
if err := chanArb.Start(); err != nil {
|
||||||
|
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer chanArb.Stop()
|
||||||
|
|
||||||
|
// It should start out in the default state.
|
||||||
|
assertState(t, chanArb, StateDefault)
|
||||||
|
|
||||||
|
// We create a channel we can use to pause the ChannelArbitrator at the
|
||||||
|
// point where it broadcasts the close tx, and check its state.
|
||||||
|
stateChan := make(chan ArbitratorState)
|
||||||
|
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
|
||||||
|
// When the force close tx is being broadcasted, check that the
|
||||||
|
// state is correct at that point.
|
||||||
|
select {
|
||||||
|
case stateChan <- chanArb.state:
|
||||||
|
case <-chanArb.quit:
|
||||||
|
return fmt.Errorf("exiting")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
respChan := make(chan *wire.MsgTx, 1)
|
||||||
|
|
||||||
|
// With the channel found, and the request crafted, we'll send over a
|
||||||
|
// force close request to the arbitrator that watches this channel.
|
||||||
|
chanArb.forceCloseReqs <- &forceCloseReq{
|
||||||
|
errResp: errChan,
|
||||||
|
closeTx: respChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
// When it is broadcasting the force close, its state should be
|
||||||
|
// StateBroadcastCommit.
|
||||||
|
select {
|
||||||
|
case state := <-stateChan:
|
||||||
|
if state != StateBroadcastCommit {
|
||||||
|
t.Fatalf("state during PublishTx was %v", state)
|
||||||
|
}
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
t.Fatalf("did not get state update")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-respChan:
|
||||||
|
case err := <-errChan:
|
||||||
|
t.Fatalf("error force closing channel: %v", err)
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
t.Fatalf("did not receive reponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
// After broadcasting the close tx, it should be in state
|
||||||
|
// StateCommitmentBroadcasted.
|
||||||
|
assertState(t, chanArb, StateCommitmentBroadcasted)
|
||||||
|
|
||||||
|
// Now notify about the local force close getting confirmed.
|
||||||
|
chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{
|
||||||
|
&chainntnfs.SpendDetail{},
|
||||||
|
&lnwallet.LocalForceCloseSummary{
|
||||||
|
CloseTx: &wire.MsgTx{},
|
||||||
|
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// It should mark the channel as resolved.
|
||||||
|
select {
|
||||||
|
case <-resolved:
|
||||||
|
// Expected.
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatalf("contract was not resolved")
|
||||||
|
}
|
||||||
|
|
||||||
|
// And end up in the StateFullyResolved state.
|
||||||
|
// TODO: intermediate states as well.
|
||||||
|
assertState(t, chanArb, StateFullyResolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannelArbitratorLocalForceCloseRemoteConfiremd tests that the
|
||||||
|
// ChannelArbitrator behaves as expected in the case where we request a local
|
||||||
|
// force close, but a remote commitment ends up being confirmed in chain.
|
||||||
|
func TestChannelArbitratorLocalForceCloseRemoteConfirmed(t *testing.T) {
|
||||||
|
chanArb, resolved, cleanUp, err := createTestChannelArbitrator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
if err := chanArb.Start(); err != nil {
|
||||||
|
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
defer chanArb.Stop()
|
||||||
|
|
||||||
|
// It should start out in the default state.
|
||||||
|
assertState(t, chanArb, StateDefault)
|
||||||
|
|
||||||
|
// Create a channel we can use to assert the state when it publishes
|
||||||
|
// the close tx.
|
||||||
|
stateChan := make(chan ArbitratorState)
|
||||||
|
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
|
||||||
|
// When the force close tx is being broadcasted, check that the
|
||||||
|
// state is correct at that point.
|
||||||
|
select {
|
||||||
|
case stateChan <- chanArb.state:
|
||||||
|
case <-chanArb.quit:
|
||||||
|
return fmt.Errorf("exiting")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
respChan := make(chan *wire.MsgTx, 1)
|
||||||
|
|
||||||
|
// With the channel found, and the request crafted, we'll send over a
|
||||||
|
// force close request to the arbitrator that watches this channel.
|
||||||
|
chanArb.forceCloseReqs <- &forceCloseReq{
|
||||||
|
errResp: errChan,
|
||||||
|
closeTx: respChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect it to be in state StateBroadcastCommit when publishing
|
||||||
|
// the force close.
|
||||||
|
select {
|
||||||
|
case state := <-stateChan:
|
||||||
|
if state != StateBroadcastCommit {
|
||||||
|
t.Fatalf("state during PublishTx was %v", state)
|
||||||
|
}
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
t.Fatalf("no state update received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a response to the force close.
|
||||||
|
select {
|
||||||
|
case <-respChan:
|
||||||
|
case err := <-errChan:
|
||||||
|
t.Fatalf("error force closing channel: %v", err)
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
t.Fatalf("no response received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The state should be StateCommitmentBroadcasted.
|
||||||
|
assertState(t, chanArb, StateCommitmentBroadcasted)
|
||||||
|
|
||||||
|
// Now notify about the _REMOTE_ commitment getting confirmed.
|
||||||
|
commitSpend := &chainntnfs.SpendDetail{
|
||||||
|
SpenderTxHash: &chainhash.Hash{},
|
||||||
|
}
|
||||||
|
uniClose := &lnwallet.UnilateralCloseSummary{
|
||||||
|
SpendDetail: commitSpend,
|
||||||
|
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
||||||
|
}
|
||||||
|
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose
|
||||||
|
|
||||||
|
// It should resolve.
|
||||||
|
select {
|
||||||
|
case <-resolved:
|
||||||
|
// Expected.
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
t.Fatalf("contract was not resolved")
|
||||||
|
}
|
||||||
|
|
||||||
|
// And we expect it to end up in StateFullyResolved.
|
||||||
|
// TODO: intermediate states as well.
|
||||||
|
assertState(t, chanArb, StateFullyResolved)
|
||||||
|
}
|
||||||
|
@ -350,16 +350,12 @@ func (cm *circuitMap) decodeCircuit(v []byte) (*PaymentCircuit, error) {
|
|||||||
// channels. Therefore, it must be called before any links are created to avoid
|
// channels. Therefore, it must be called before any links are created to avoid
|
||||||
// interfering with normal operation.
|
// interfering with normal operation.
|
||||||
func (cm *circuitMap) trimAllOpenCircuits() error {
|
func (cm *circuitMap) trimAllOpenCircuits() error {
|
||||||
activeChannels, err := cm.cfg.DB.FetchAllChannels()
|
activeChannels, err := cm.cfg.DB.FetchAllOpenChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, activeChannel := range activeChannels {
|
for _, activeChannel := range activeChannels {
|
||||||
if activeChannel.IsPending {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
chanID := activeChannel.ShortChanID
|
chanID := activeChannel.ShortChanID
|
||||||
start := activeChannel.LocalCommitment.LocalHtlcIndex
|
start := activeChannel.LocalCommitment.LocalHtlcIndex
|
||||||
if err := cm.TrimOpenCircuits(chanID, start); err != nil {
|
if err := cm.TrimOpenCircuits(chanID, start); err != nil {
|
||||||
|
@ -832,7 +832,7 @@ out:
|
|||||||
// the contract as fully settled. Afterwards we can exit.
|
// the contract as fully settled. Afterwards we can exit.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): add force closure? also breach?
|
// TODO(roasbeef): add force closure? also breach?
|
||||||
case <-l.cfg.ChainEvents.UnilateralClosure:
|
case <-l.cfg.ChainEvents.RemoteUnilateralClosure:
|
||||||
log.Warnf("Remote peer has closed ChannelPoint(%v) on-chain",
|
log.Warnf("Remote peer has closed ChannelPoint(%v) on-chain",
|
||||||
l.channel.ChannelPoint())
|
l.channel.ChannelPoint())
|
||||||
|
|
||||||
|
@ -1513,16 +1513,12 @@ func (s *Switch) Start() error {
|
|||||||
// forwarding packages and reforwards any Settle or Fail HTLCs found. This is
|
// forwarding packages and reforwards any Settle or Fail HTLCs found. This is
|
||||||
// used to resurrect the switch's mailboxes after a restart.
|
// used to resurrect the switch's mailboxes after a restart.
|
||||||
func (s *Switch) reforwardResponses() error {
|
func (s *Switch) reforwardResponses() error {
|
||||||
activeChannels, err := s.cfg.DB.FetchAllChannels()
|
activeChannels, err := s.cfg.DB.FetchAllOpenChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, activeChannel := range activeChannels {
|
for _, activeChannel := range activeChannels {
|
||||||
if activeChannel.IsPending {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
shortChanID := activeChannel.ShortChanID
|
shortChanID := activeChannel.ShortChanID
|
||||||
fwdPkgs, err := s.loadChannelFwdPkgs(shortChanID)
|
fwdPkgs, err := s.loadChannelFwdPkgs(shortChanID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
592
lnd_test.go
592
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
|
// mineBlocks mine 'num' of blocks and check that blocks are present in
|
||||||
@ -1109,8 +1109,17 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
|
|
||||||
// Disconnect Alice-peer from Bob-peer without getting error
|
// Disconnect Alice-peer from Bob-peer without getting error
|
||||||
// about existing channels.
|
// about existing channels.
|
||||||
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil {
|
var predErr error
|
||||||
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v", err)
|
err = lntest.WaitPredicate(func() bool {
|
||||||
|
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil {
|
||||||
|
predErr = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, time.Second*15)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v",
|
||||||
|
predErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check zero peer connections.
|
// Check zero peer connections.
|
||||||
@ -1353,6 +1362,27 @@ func findForceClosedChannel(t *harnessTest,
|
|||||||
return forceClose
|
return forceClose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findWaitingCloseChannel searches a pending channel response for a particular
|
||||||
|
// channel, returning the waiting close channel upon success.
|
||||||
|
func findWaitingCloseChannel(t *harnessTest,
|
||||||
|
pendingChanResp *lnrpc.PendingChannelsResponse,
|
||||||
|
op *wire.OutPoint) *lnrpc.PendingChannelsResponse_WaitingCloseChannel {
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
var waitingClose *lnrpc.PendingChannelsResponse_WaitingCloseChannel
|
||||||
|
for _, waitingClose = range pendingChanResp.WaitingCloseChannels {
|
||||||
|
if waitingClose.Channel.ChannelPoint == op.String() {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("channel not marked as waiting close")
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitingClose
|
||||||
|
}
|
||||||
|
|
||||||
func assertCommitmentMaturity(t *harnessTest,
|
func assertCommitmentMaturity(t *harnessTest,
|
||||||
forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel,
|
forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel,
|
||||||
maturityHeight uint32, blocksTilMaturity int32) {
|
maturityHeight uint32, blocksTilMaturity int32) {
|
||||||
@ -1394,6 +1424,18 @@ func assertNumForceClosedChannels(t *harnessTest,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assertNumWaitingCloseChannels checks that a pending channel response has the
|
||||||
|
// expected number of channels waiting for closing tx to confirm.
|
||||||
|
func assertNumWaitingCloseChannels(t *harnessTest,
|
||||||
|
pendingChanResp *lnrpc.PendingChannelsResponse, expectedNumChans int) {
|
||||||
|
|
||||||
|
if len(pendingChanResp.WaitingCloseChannels) != expectedNumChans {
|
||||||
|
t.Fatalf("expected to find %d channels waiting closure, got %d",
|
||||||
|
expectedNumChans,
|
||||||
|
len(pendingChanResp.WaitingCloseChannels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// assertPendingHtlcStageAndMaturity uniformly tests all pending htlc's
|
// assertPendingHtlcStageAndMaturity uniformly tests all pending htlc's
|
||||||
// belonging to a force closed channel, testing for the expected stage number,
|
// belonging to a force closed channel, testing for the expected stage number,
|
||||||
// blocks till maturity, and the maturity height.
|
// blocks till maturity, and the maturity height.
|
||||||
@ -1574,13 +1616,13 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now that the channel has been force closed, it should show up in the
|
// Now that the channel has been force closed, it should show up in the
|
||||||
// PendingChannels RPC under the force close section.
|
// PendingChannels RPC under the waiting close section.
|
||||||
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
||||||
pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest)
|
pendingChanResp, err := net.Alice.PendingChannels(ctxb, pendingChansRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to query for pending channels: %v", err)
|
t.Fatalf("unable to query for pending channels: %v", err)
|
||||||
}
|
}
|
||||||
assertNumForceClosedChannels(t, pendingChanResp, 1)
|
assertNumWaitingCloseChannels(t, pendingChanResp, 1)
|
||||||
|
|
||||||
// Compute the outpoint of the channel, which we will use repeatedly to
|
// Compute the outpoint of the channel, which we will use repeatedly to
|
||||||
// locate the pending channel information in the rpc responses.
|
// locate the pending channel information in the rpc responses.
|
||||||
@ -1597,21 +1639,12 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
Index: chanPoint.OutputIndex,
|
Index: chanPoint.OutputIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
forceClose := findForceClosedChannel(t, pendingChanResp, &op)
|
waitingClose := findWaitingCloseChannel(t, pendingChanResp, &op)
|
||||||
|
|
||||||
// Immediately after force closing, all of the funds should be in limbo,
|
// Immediately after force closing, all of the funds should be in limbo.
|
||||||
// and the pending channels response should not indicate that any funds
|
if waitingClose.LimboBalance == 0 {
|
||||||
// have been recovered.
|
|
||||||
if forceClose.LimboBalance == 0 {
|
|
||||||
t.Fatalf("all funds should still be in limbo")
|
t.Fatalf("all funds should still be in limbo")
|
||||||
}
|
}
|
||||||
if forceClose.RecoveredBalance != 0 {
|
|
||||||
t.Fatalf("no funds should yet be shown as recovered")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The commitment transaction has not been confirmed, so we expect to
|
|
||||||
// see a maturity height and blocks til maturity of 0.
|
|
||||||
assertCommitmentMaturity(t, forceClose, 0, 0)
|
|
||||||
|
|
||||||
// The several restarts in this test are intended to ensure that when a
|
// The several restarts in this test are intended to ensure that when a
|
||||||
// channel is force-closed, the UTXO nursery has persisted the state of
|
// channel is force-closed, the UTXO nursery has persisted the state of
|
||||||
@ -1639,13 +1672,15 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
duration := time.Millisecond * 300
|
duration := time.Millisecond * 300
|
||||||
time.Sleep(duration)
|
time.Sleep(duration)
|
||||||
|
|
||||||
|
// Now that the commitment has been confirmed, the channel should be
|
||||||
|
// marked as force closed.
|
||||||
pendingChanResp, err = net.Alice.PendingChannels(ctxb, pendingChansRequest)
|
pendingChanResp, err = net.Alice.PendingChannels(ctxb, pendingChansRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to query for pending channels: %v", err)
|
t.Fatalf("unable to query for pending channels: %v", err)
|
||||||
}
|
}
|
||||||
assertNumForceClosedChannels(t, pendingChanResp, 1)
|
assertNumForceClosedChannels(t, pendingChanResp, 1)
|
||||||
|
|
||||||
forceClose = findForceClosedChannel(t, pendingChanResp, &op)
|
forceClose := findForceClosedChannel(t, pendingChanResp, &op)
|
||||||
|
|
||||||
// Now that the channel has been force closed, it should now have the
|
// Now that the channel has been force closed, it should now have the
|
||||||
// height and number of blocks to confirm populated.
|
// height and number of blocks to confirm populated.
|
||||||
@ -3828,7 +3863,7 @@ func waitForNTxsInMempool(miner *rpcclient.Client, n int,
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-breakTimeout:
|
case <-breakTimeout:
|
||||||
return nil, fmt.Errorf("wanted %v, only found %v txs "+
|
return nil, fmt.Errorf("wanted %v, found %v txs "+
|
||||||
"in mempool", n, len(mempool))
|
"in mempool", n, len(mempool))
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
mempool, err = miner.GetRawMempool()
|
mempool, err = miner.GetRawMempool()
|
||||||
@ -4224,11 +4259,24 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(net *lntest.NetworkHarness
|
|||||||
// commitment transaction of a prior *revoked* state, so he'll soon
|
// commitment transaction of a prior *revoked* state, so he'll soon
|
||||||
// feel the wrath of Alice's retribution.
|
// feel the wrath of Alice's retribution.
|
||||||
force := true
|
force := true
|
||||||
closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force)
|
closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol,
|
||||||
|
chanPoint, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to close channel: %v", err)
|
t.Fatalf("unable to close channel: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query the mempool for the breaching closing transaction, this should
|
||||||
|
// be broadcast by Carol when she force closes the channel above.
|
||||||
|
txid, err := waitForTxInMempool(net.Miner.Node, 20*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find Carol's force close tx in mempool: %v",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
if *txid != *closeTxId {
|
||||||
|
t.Fatalf("expected closeTx(%v) in mempool, instead found %v",
|
||||||
|
closeTxId, txid)
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, generate a single block, wait for the final close status
|
// Finally, generate a single block, wait for the final close status
|
||||||
// update, then ensure that the closing transaction was included in the
|
// update, then ensure that the closing transaction was included in the
|
||||||
// block.
|
// block.
|
||||||
@ -4545,17 +4593,22 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
|||||||
// commitment transaction of a prior *revoked* state, so she'll soon
|
// commitment transaction of a prior *revoked* state, so she'll soon
|
||||||
// feel the wrath of Dave's retribution.
|
// feel the wrath of Dave's retribution.
|
||||||
force := true
|
force := true
|
||||||
closeUpdates, _, err := net.CloseChannel(ctxb, carol, chanPoint, force)
|
closeUpdates, closeTxId, err := net.CloseChannel(ctxb, carol,
|
||||||
|
chanPoint, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to close channel: %v", err)
|
t.Fatalf("unable to close channel: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the mempool for Dave's justice transaction, this should be
|
// Query the mempool for the breaching closing transaction, this should
|
||||||
// broadcast as Carol's contract breaching transaction gets confirmed
|
// be broadcast by Carol when she force closes the channel above.
|
||||||
// above.
|
txid, err := waitForTxInMempool(net.Miner.Node, 20*time.Second)
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, 5*time.Second)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find Dave's justice tx in mempool: %v", err)
|
t.Fatalf("unable to find Carol's force close tx in mempool: %v",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
if *txid != *closeTxId {
|
||||||
|
t.Fatalf("expected closeTx(%v) in mempool, instead found %v",
|
||||||
|
closeTxId, txid)
|
||||||
}
|
}
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
@ -4577,14 +4630,101 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error while waiting for channel close: %v", err)
|
t.Fatalf("error while waiting for channel close: %v", err)
|
||||||
}
|
}
|
||||||
|
if *breachTXID != *closeTxId {
|
||||||
|
t.Fatalf("expected breach ID(%v) to be equal to close ID (%v)",
|
||||||
|
breachTXID, closeTxId)
|
||||||
|
}
|
||||||
assertTxInBlock(t, block, breachTXID)
|
assertTxInBlock(t, block, breachTXID)
|
||||||
|
|
||||||
// Query the mempool for Dave's justice transaction, this should be
|
// Query the mempool for Dave's justice transaction, this should be
|
||||||
// broadcast as Carol's contract breaching transaction gets confirmed
|
// broadcast as Carol's contract breaching transaction gets confirmed
|
||||||
// above.
|
// above. Since Carol might have had the time to take some of the HTLC
|
||||||
justiceTXID, err := waitForTxInMempool(net.Miner.Node, 5*time.Second)
|
// outputs to the second level before Alice broadcasts her justice tx,
|
||||||
|
// we'll search through the mempool for a tx that matches the number of
|
||||||
|
// expected inputs in the justice tx.
|
||||||
|
// TODO(halseth): change to deterministic check if/when only acting on
|
||||||
|
// confirmed second level spends?
|
||||||
|
var predErr error
|
||||||
|
var justiceTxid *chainhash.Hash
|
||||||
|
err = lntest.WaitPredicate(func() bool {
|
||||||
|
mempool, err := net.Miner.Node.GetRawMempool()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get mempool from miner: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, txid := range mempool {
|
||||||
|
// Check that the justice tx has the appropriate number
|
||||||
|
// of inputs.
|
||||||
|
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for "+
|
||||||
|
"txs: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
exNumInputs := 2 + numInvoices
|
||||||
|
if len(tx.MsgTx().TxIn) == exNumInputs {
|
||||||
|
justiceTxid = txid
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
predErr = fmt.Errorf("justice tx not found")
|
||||||
|
return false
|
||||||
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find Dave's justice tx in mempool: %v", err)
|
t.Fatalf(predErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
justiceTx, err := net.Miner.Node.GetRawTransaction(justiceTxid)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to query for justice tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSecondLevelSpend checks that the passed secondLevelTxid is a
|
||||||
|
// potentitial second level spend spending from the commit tx.
|
||||||
|
isSecondLevelSpend := func(commitTxid, secondLevelTxid *chainhash.Hash) bool {
|
||||||
|
secondLevel, err := net.Miner.Node.GetRawTransaction(
|
||||||
|
secondLevelTxid)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to query for tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A second level spend should have only one input, and one
|
||||||
|
// output.
|
||||||
|
if len(secondLevel.MsgTx().TxIn) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(secondLevel.MsgTx().TxOut) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sole input should be spending from the commit tx.
|
||||||
|
txIn := secondLevel.MsgTx().TxIn[0]
|
||||||
|
if !bytes.Equal(txIn.PreviousOutPoint.Hash[:], commitTxid[:]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all the inputs of this transaction are spending outputs
|
||||||
|
// generated by Carol's breach transaction above.
|
||||||
|
for _, txIn := range justiceTx.MsgTx().TxIn {
|
||||||
|
if bytes.Equal(txIn.PreviousOutPoint.Hash[:], breachTXID[:]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the justice tx is spending from an output that was not on
|
||||||
|
// the breach tx, Carol might have had the time to take an
|
||||||
|
// output to the second level. In that case, check that the
|
||||||
|
// justice tx is spending this second level output.
|
||||||
|
if isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Fatalf("justice tx not spending commitment utxo "+
|
||||||
|
"instead is: %v", txIn.PreviousOutPoint)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
@ -4597,36 +4737,12 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
|||||||
t.Fatalf("unable to restart Dave's node: %v", err)
|
t.Fatalf("unable to restart Dave's node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query for the mempool transaction found above. Then assert that (1)
|
|
||||||
// the justice tx has the appropriate number of inputs, and (2) all the
|
|
||||||
// inputs of this transaction are spending outputs generated by Carol's
|
|
||||||
// breach transaction above, and also the HTLCs from Carol to Dave.
|
|
||||||
justiceTx, err := net.Miner.Node.GetRawTransaction(justiceTXID)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to query for justice tx: %v", err)
|
|
||||||
}
|
|
||||||
exNumInputs := 2 + numInvoices
|
|
||||||
if len(justiceTx.MsgTx().TxIn) != exNumInputs {
|
|
||||||
t.Fatalf("justice tx should have exactly 2 commitment inputs"+
|
|
||||||
"and %v htlc inputs, expected %v in total, got %v",
|
|
||||||
numInvoices/2, exNumInputs,
|
|
||||||
len(justiceTx.MsgTx().TxIn))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now mine a block, this transaction should include Dave's justice
|
// Now mine a block, this transaction should include Dave's justice
|
||||||
// transaction which was just accepted into the mempool.
|
// transaction which was just accepted into the mempool.
|
||||||
block = mineBlocks(t, net, 1)[0]
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
assertTxInBlock(t, block, justiceTxid)
|
||||||
|
|
||||||
// The block should have exactly *two* transactions, one of which is
|
// Dave should have no open channels.
|
||||||
// the justice transaction.
|
|
||||||
if len(block.Transactions) != 2 {
|
|
||||||
t.Fatalf("transaction wasn't mined")
|
|
||||||
}
|
|
||||||
justiceSha := block.Transactions[1].TxHash()
|
|
||||||
if !bytes.Equal(justiceTx.Hash()[:], justiceSha[:]) {
|
|
||||||
t.Fatalf("justice tx wasn't mined")
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNodeNumChannels(t, ctxb, dave, 0)
|
assertNodeNumChannels(t, ctxb, dave, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4649,7 +4765,13 @@ func assertNodeNumChannels(t *harnessTest, ctxb context.Context,
|
|||||||
|
|
||||||
// Return true if the query returned the expected number of
|
// Return true if the query returned the expected number of
|
||||||
// channels.
|
// channels.
|
||||||
return len(chanInfo.Channels) == numChannels
|
num := len(chanInfo.Channels)
|
||||||
|
if num != numChannels {
|
||||||
|
predErr = fmt.Errorf("expected %v channels, got %v",
|
||||||
|
numChannels, num)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := lntest.WaitPredicate(pred, time.Second*15); err != nil {
|
if err := lntest.WaitPredicate(pred, time.Second*15); err != nil {
|
||||||
@ -6020,6 +6142,9 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Mine a block to confirm the closing transaction.
|
||||||
|
mineBlocks(t, net, 1)
|
||||||
|
|
||||||
// At this point, Bob should have cancelled backwards the dust HTLC
|
// At this point, Bob should have cancelled backwards the dust HTLC
|
||||||
// that we sent earlier. This means Alice should now only have a single
|
// that we sent earlier. This means Alice should now only have a single
|
||||||
// HTLC on her channel.
|
// HTLC on her channel.
|
||||||
@ -6033,7 +6158,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
return true
|
return true
|
||||||
}, time.Second*15)
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("htlc mismatch: %v", err)
|
t.Fatalf("htlc mismatch: %v", predErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): need to fix utxn so it can accept incubation for
|
// TODO(roasbeef): need to fix utxn so it can accept incubation for
|
||||||
@ -6050,7 +6175,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
|
|
||||||
// The second layer HTLC timeout transaction should now have been
|
// The second layer HTLC timeout transaction should now have been
|
||||||
// broadcast on-chain.
|
// broadcast on-chain.
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, time.Second*10)
|
secondLayerHash, err := waitForTxInMempool(net.Miner.Node, time.Second*10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find bob's second layer transaction")
|
t.Fatalf("unable to find bob's second layer transaction")
|
||||||
}
|
}
|
||||||
@ -6077,13 +6202,12 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now we'll mine an additional block.
|
// Now we'll mine an additional block.
|
||||||
if _, err := net.Miner.Node.Generate(1); err != nil {
|
block := mineBlocks(t, net, 1)[0]
|
||||||
t.Fatalf("unable to generate blocks: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The block should have confirmed Bob's second layer sweeping
|
// The block should have confirmed Bob's second layer sweeping
|
||||||
// transaction. Therefore, at this point, there should be no active
|
// transaction. Therefore, at this point, there should be no active
|
||||||
// HTLC's on the commitment transaction from Alice -> Bob.
|
// HTLC's on the commitment transaction from Alice -> Bob.
|
||||||
|
assertTxInBlock(t, block, secondLayerHash)
|
||||||
nodes = []*lntest.HarnessNode{net.Alice}
|
nodes = []*lntest.HarnessNode{net.Alice}
|
||||||
err = lntest.WaitPredicate(func() bool {
|
err = lntest.WaitPredicate(func() bool {
|
||||||
return assertNumActiveHtlcs(nodes, 0)
|
return assertNumActiveHtlcs(nodes, 0)
|
||||||
@ -6216,58 +6340,73 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
|
|
||||||
// At this point, Carol should broadcast her active commitment
|
// At this point, Carol should broadcast her active commitment
|
||||||
// transaction in order to go to the chain and sweep her HTLC.
|
// transaction in order to go to the chain and sweep her HTLC.
|
||||||
// Additionally, Carol's should have broadcast her second layer sweep
|
txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*20)
|
||||||
// transaction for the HTLC as well.
|
|
||||||
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("expected transaction not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
txidHash, err := getChanPointFundingTxid(bobChanPoint)
|
txidHash, err := getChanPointFundingTxid(bobChanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get txid: %v", err)
|
t.Fatalf("unable to get txid: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bobFundingTxid, err := chainhash.NewHash(txidHash)
|
bobFundingTxid, err := chainhash.NewHash(txidHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create sha hash: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
carolFundingPoint := wire.OutPoint{
|
carolFundingPoint := wire.OutPoint{
|
||||||
Hash: *bobFundingTxid,
|
Hash: *bobFundingTxid,
|
||||||
Index: bobChanPoint.OutputIndex,
|
Index: bobChanPoint.OutputIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
tx1, err := net.Miner.Node.GetRawTransaction(txids[0])
|
// The commitment transaction should be spending from the funding
|
||||||
|
// transaction.
|
||||||
|
commitHash := txids[0]
|
||||||
|
tx, err := net.Miner.Node.GetRawTransaction(commitHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
}
|
}
|
||||||
tx1Hash := tx1.MsgTx().TxHash()
|
commitTx := tx.MsgTx()
|
||||||
tx2, err := net.Miner.Node.GetRawTransaction(txids[1])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
|
||||||
}
|
|
||||||
tx2Hash := tx2.MsgTx().TxHash()
|
|
||||||
|
|
||||||
// Of the two transactions, one should be spending from the funding
|
if commitTx.TxIn[0].PreviousOutPoint != carolFundingPoint {
|
||||||
// transaction, and the second transaction should then be spending from
|
t.Fatalf("commit transaction not spending from expected "+
|
||||||
|
"outpoint: %v", spew.Sdump(commitTx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm the commitment.
|
||||||
|
mineBlocks(t, net, 1)
|
||||||
|
|
||||||
|
// After the force close transaction is mined, Carol should broadcast
|
||||||
|
// her second level HTLC transaction. Bob will broadcast a sweep tx to
|
||||||
|
// sweep his output in the channel with Carol. When Bob notices Carol's
|
||||||
|
// second level transaction in the mempool, he will extract the
|
||||||
|
// preimage and settle the HTLC back off-chain.
|
||||||
|
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 2,
|
||||||
|
time.Second*15)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carol's second level transaction should be spending from
|
||||||
// the commitment transaction.
|
// the commitment transaction.
|
||||||
var commitHash *chainhash.Hash
|
var secondLevelHash *chainhash.Hash
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
|
for _, txid := range secondLevelHashes {
|
||||||
commitHash = &tx1Hash
|
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
||||||
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
if err != nil {
|
||||||
t.Fatalf("second transaction not spending commit tx: %v",
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
spew.Sdump(tx2))
|
}
|
||||||
|
|
||||||
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
|
||||||
|
secondLevelHash = txid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
|
if secondLevelHash == nil {
|
||||||
commitHash = &tx2Hash
|
t.Fatalf("Carol's second level tx not found")
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
|
||||||
t.Fatalf("second transaction not spending commit tx: %v",
|
|
||||||
spew.Sdump(tx1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if commitHash == nil {
|
|
||||||
t.Fatalf("commit tx not found in mempool")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll now mine an additional block which should confirm both the
|
// We'll now mine an additional block which should confirm both the
|
||||||
// second layer transaction as well as the commitment transaction
|
// second layer transactions.
|
||||||
// itself.
|
|
||||||
if _, err := net.Miner.Node.Generate(1); err != nil {
|
if _, err := net.Miner.Node.Generate(1); err != nil {
|
||||||
t.Fatalf("unable to generate block: %v", err)
|
t.Fatalf("unable to generate block: %v", err)
|
||||||
}
|
}
|
||||||
@ -6427,17 +6566,32 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
// At this point, Bob should have a pending force close channel as he
|
// At this point, Bob should have a pending force close channel as he
|
||||||
// just went to chain.
|
// just went to chain.
|
||||||
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
||||||
pendingChanResp, err := net.Bob.PendingChannels(ctxb, pendingChansRequest)
|
err = lntest.WaitPredicate(func() bool {
|
||||||
|
pendingChanResp, err := net.Bob.PendingChannels(ctxb,
|
||||||
|
pendingChansRequest)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for pending "+
|
||||||
|
"channels: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
|
||||||
|
predErr = fmt.Errorf("bob should have pending for " +
|
||||||
|
"close chan but doesn't")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
|
||||||
|
if forceCloseChan.LimboBalance == 0 {
|
||||||
|
predErr = fmt.Errorf("bob should have nonzero limbo "+
|
||||||
|
"balance instead has: %v",
|
||||||
|
forceCloseChan.LimboBalance)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to query for pending channels: %v", err)
|
t.Fatalf(predErr.Error())
|
||||||
}
|
|
||||||
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
|
|
||||||
t.Fatalf("bob should have pending for close chan but doesn't")
|
|
||||||
}
|
|
||||||
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
|
|
||||||
if forceCloseChan.LimboBalance == 0 {
|
|
||||||
t.Fatalf("bob should have nonzero limbo balance instead "+
|
|
||||||
"has: %v", forceCloseChan.LimboBalance)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll now mine enough blocks for the HTLC to expire. After this, Bob
|
// We'll now mine enough blocks for the HTLC to expire. After this, Bob
|
||||||
@ -6459,12 +6613,12 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
|
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
|
||||||
predErr = fmt.Errorf("bob should have pending for " +
|
predErr = fmt.Errorf("bob should have pending force " +
|
||||||
"close chan but doesn't")
|
"close chan but doesn't")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forceCloseChan = pendingChanResp.PendingForceClosingChannels[0]
|
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
|
||||||
if len(forceCloseChan.PendingHtlcs) != 1 {
|
if len(forceCloseChan.PendingHtlcs) != 1 {
|
||||||
predErr = fmt.Errorf("bob should have pending htlc " +
|
predErr = fmt.Errorf("bob should have pending htlc " +
|
||||||
"but doesn't")
|
"but doesn't")
|
||||||
@ -6523,7 +6677,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forceCloseChan = pendingChanResp.PendingForceClosingChannels[0]
|
forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
|
||||||
if len(forceCloseChan.PendingHtlcs) != 1 {
|
if len(forceCloseChan.PendingHtlcs) != 1 {
|
||||||
predErr = fmt.Errorf("bob should have pending htlc " +
|
predErr = fmt.Errorf("bob should have pending htlc " +
|
||||||
"but doesn't")
|
"but doesn't")
|
||||||
@ -6559,7 +6713,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
// At this point, Bob should no longer show any channels as pending
|
// At this point, Bob should no longer show any channels as pending
|
||||||
// close.
|
// close.
|
||||||
err = lntest.WaitPredicate(func() bool {
|
err = lntest.WaitPredicate(func() bool {
|
||||||
pendingChanResp, err = net.Bob.PendingChannels(
|
pendingChanResp, err := net.Bob.PendingChannels(
|
||||||
ctxb, pendingChansRequest,
|
ctxb, pendingChansRequest,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -6642,7 +6796,7 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
return true
|
return true
|
||||||
}, time.Second*15)
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("htlc mismatch: %v", err)
|
t.Fatalf("htlc mismatch: %v", predErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we'll now instruct Carol to force close the
|
// At this point, we'll now instruct Carol to force close the
|
||||||
@ -6654,12 +6808,25 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
// At this point, Bob should have a pending force close channel as
|
// At this point, Bob should have a pending force close channel as
|
||||||
// Carol has gone directly to chain.
|
// Carol has gone directly to chain.
|
||||||
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
pendingChansRequest := &lnrpc.PendingChannelsRequest{}
|
||||||
pendingChanResp, err := net.Bob.PendingChannels(ctxb, pendingChansRequest)
|
err = lntest.WaitPredicate(func() bool {
|
||||||
|
pendingChanResp, err := net.Bob.PendingChannels(
|
||||||
|
ctxb, pendingChansRequest,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
predErr = fmt.Errorf("unable to query for "+
|
||||||
|
"pending channels: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
|
||||||
|
predErr = fmt.Errorf("bob should have pending " +
|
||||||
|
"force close channels but doesn't")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}, time.Second*15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to query for pending channels: %v", err)
|
t.Fatalf(predErr.Error())
|
||||||
}
|
|
||||||
if len(pendingChanResp.PendingForceClosingChannels) == 0 {
|
|
||||||
t.Fatalf("bob should have pending for close chan but doesn't")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, we'll mine enough blocks for the HTLC to expire. At this
|
// Next, we'll mine enough blocks for the HTLC to expire. At this
|
||||||
@ -6739,7 +6906,7 @@ func testMultHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
// commitment, he doesn't have to wait for any CSV delays. As a result,
|
// commitment, he doesn't have to wait for any CSV delays. As a result,
|
||||||
// he should show no additional pending transactions.
|
// he should show no additional pending transactions.
|
||||||
err = lntest.WaitPredicate(func() bool {
|
err = lntest.WaitPredicate(func() bool {
|
||||||
pendingChanResp, err = net.Bob.PendingChannels(
|
pendingChanResp, err := net.Bob.PendingChannels(
|
||||||
ctxb, pendingChansRequest,
|
ctxb, pendingChansRequest,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -6835,9 +7002,8 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
t.Fatalf("unable to generate blocks")
|
t.Fatalf("unable to generate blocks")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's commitment transaction should now be in the mempool. She
|
// Carol's commitment transaction should now be in the mempool.
|
||||||
// should also have broadcast her second level HTLC transaction.
|
txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
|
||||||
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
@ -6854,50 +7020,52 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
Index: bobChanPoint.OutputIndex,
|
Index: bobChanPoint.OutputIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Of the two transactions, one should be spending from the funding
|
// The tx should be spending from the funding transaction,
|
||||||
// transaction, and the second transaction should then be spending from
|
commitHash := txids[0]
|
||||||
|
tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
|
}
|
||||||
|
if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
|
||||||
|
t.Fatalf("commit transaction not spending fundingtx: %v",
|
||||||
|
spew.Sdump(tx1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine a block that should confirm the commit tx.
|
||||||
|
block := mineBlocks(t, net, 1)[0]
|
||||||
|
if len(block.Transactions) != 2 {
|
||||||
|
t.Fatalf("expected 2 transactions in block, got %v",
|
||||||
|
len(block.Transactions))
|
||||||
|
}
|
||||||
|
assertTxInBlock(t, block, commitHash)
|
||||||
|
|
||||||
|
// After the force close transacion is mined, Carol should broadcast
|
||||||
|
// her second level HTLC transacion. Bob will braodcast a sweep tx to
|
||||||
|
// sweep his output in the channel with Carol. When Bob notices Carol's
|
||||||
|
// second level transaction in the mempool, he will extract the
|
||||||
|
// preimage and broadcast a second level tx to claim the HTLC in his
|
||||||
|
// (already closed) channel with Alice.
|
||||||
|
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
|
||||||
|
time.Second*20)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carol's second level transaction should be spending from
|
||||||
// the commitment transaction.
|
// the commitment transaction.
|
||||||
var commitHash *chainhash.Hash
|
var secondLevelHash *chainhash.Hash
|
||||||
tx1, err := net.Miner.Node.GetRawTransaction(txids[0])
|
for _, txid := range secondLevelHashes {
|
||||||
if err != nil {
|
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
if err != nil {
|
||||||
}
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
tx1Hash := tx1.MsgTx().TxHash()
|
}
|
||||||
tx2, err := net.Miner.Node.GetRawTransaction(txids[1])
|
|
||||||
if err != nil {
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
secondLevelHash = txid
|
||||||
}
|
|
||||||
tx2Hash := tx2.MsgTx().TxHash()
|
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
|
|
||||||
commitHash = &tx1Hash
|
|
||||||
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
|
||||||
t.Fatalf("second transaction not spending commit tx: %v",
|
|
||||||
spew.Sdump(tx2))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
|
if secondLevelHash == nil {
|
||||||
commitHash = &tx2Hash
|
t.Fatalf("Carol's second level tx not found")
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
|
||||||
t.Fatalf("second transaction not spending commit tx: %v",
|
|
||||||
spew.Sdump(tx1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if commitHash == nil {
|
|
||||||
t.Fatalf("commit tx not found in mempool")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll now mine a block which should confirm both the second layer
|
|
||||||
// transaction as well as the commitment transaction.
|
|
||||||
if _, err := net.Miner.Node.Generate(1); err != nil {
|
|
||||||
t.Fatalf("unable to generate block: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, Bob should detect that Carol has revealed the
|
|
||||||
// preimage on-chain. As a result, he should now attempt to broadcast
|
|
||||||
// his second-layer claim transaction to claim the output.
|
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, time.Second*10)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to find bob's sweeping transaction")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, Bob should have broadcast his second layer success
|
// At this point, Bob should have broadcast his second layer success
|
||||||
@ -6929,9 +7097,11 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
"but doesn't")
|
"but doesn't")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if forceCloseChan.PendingHtlcs[0].Stage != 1 {
|
stage := forceCloseChan.PendingHtlcs[0].Stage
|
||||||
|
if stage != 1 {
|
||||||
predErr = fmt.Errorf("bob's htlc should have "+
|
predErr = fmt.Errorf("bob's htlc should have "+
|
||||||
"advanced to the first stage: %v", err)
|
"advanced to the first stage but was "+
|
||||||
|
"stage: %v", stage)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6942,6 +7112,15 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr)
|
t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll now mine a block which should confirm the two second layer
|
||||||
|
// transactions and the commit sweep.
|
||||||
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
if len(block.Transactions) != 4 {
|
||||||
|
t.Fatalf("expected 4 transactions in block, got %v",
|
||||||
|
len(block.Transactions))
|
||||||
|
}
|
||||||
|
assertTxInBlock(t, block, secondLevelHash)
|
||||||
|
|
||||||
// If we then mine 4 additional blocks, Bob should pull the output
|
// If we then mine 4 additional blocks, Bob should pull the output
|
||||||
// destined for him.
|
// destined for him.
|
||||||
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
|
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
|
||||||
@ -6994,6 +7173,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
timeout := time.Duration(time.Second * 15)
|
timeout := time.Duration(time.Second * 15)
|
||||||
ctxb := context.Background()
|
ctxb := context.Background()
|
||||||
|
|
||||||
|
defaultCSV := uint32(4)
|
||||||
|
|
||||||
// First, we'll create a three hop network: Alice -> Bob -> Carol, with
|
// First, we'll create a three hop network: Alice -> Bob -> Carol, with
|
||||||
// Carol refusing to actually settle or directly cancel any HTLC's
|
// Carol refusing to actually settle or directly cancel any HTLC's
|
||||||
// self.
|
// self.
|
||||||
@ -7052,9 +7233,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
t.Fatalf("unable to generate blocks")
|
t.Fatalf("unable to generate blocks")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's commitment transaction should now be in the mempool. She
|
// Carol's commitment transaction should now be in the mempool.
|
||||||
// should also have broadcast her second level HTLC transaction.
|
txids, err := waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
|
||||||
txids, err := waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
@ -7071,48 +7251,70 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
Index: bobChanPoint.OutputIndex,
|
Index: bobChanPoint.OutputIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Of the two transactions, one should be spending from the funding
|
// The transaction should be spending from the funding transaction
|
||||||
// transaction, and the second transaction should then be spending from
|
commitHash := txids[0]
|
||||||
// the commitment transaction.
|
tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
|
||||||
var commitHash *chainhash.Hash
|
|
||||||
tx1, err := net.Miner.Node.GetRawTransaction(txids[0])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
}
|
}
|
||||||
tx1Hash := tx1.MsgTx().TxHash()
|
if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
|
||||||
tx2, err := net.Miner.Node.GetRawTransaction(txids[1])
|
t.Fatalf("commit transaction not spending fundingtx: %v",
|
||||||
if err != nil {
|
spew.Sdump(tx1))
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
|
||||||
}
|
|
||||||
tx2Hash := tx2.MsgTx().TxHash()
|
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
|
|
||||||
commitHash = &tx1Hash
|
|
||||||
if tx2.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
|
||||||
t.Fatalf("second transaction not spending commit tx: %v",
|
|
||||||
spew.Sdump(tx2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tx2.MsgTx().TxIn[0].PreviousOutPoint == carolFundingPoint {
|
|
||||||
commitHash = &tx2Hash
|
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash {
|
|
||||||
t.Fatalf("second transaction not spending commit tx: %v",
|
|
||||||
spew.Sdump(tx1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if commitHash == nil {
|
|
||||||
t.Fatalf("commit tx not found in mempool")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll now mine a block which should confirm both the second layer
|
// Mine a block, which should contain the commitment.
|
||||||
// transaction as well as the commitment transaction.
|
block := mineBlocks(t, net, 1)[0]
|
||||||
if _, err := net.Miner.Node.Generate(1); err != nil {
|
if len(block.Transactions) != 2 {
|
||||||
|
t.Fatalf("expected 2 transactions in block, got %v",
|
||||||
|
len(block.Transactions))
|
||||||
|
}
|
||||||
|
assertTxInBlock(t, block, commitHash)
|
||||||
|
|
||||||
|
// After the force close transacion is mined, Carol should broadcast
|
||||||
|
// her second level HTLC transacion. Bob will braodcast a sweep tx to
|
||||||
|
// sweep his output in the channel with Carol. When Bob notices Carol's
|
||||||
|
// second level transaction in the mempool, he will extract the
|
||||||
|
// preimage and broadcast a second level tx to claim the HTLC in his
|
||||||
|
// (already closed) channel with Alice.
|
||||||
|
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 3,
|
||||||
|
time.Second*20)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carol's second level transaction should be spending from
|
||||||
|
// the commitment transaction.
|
||||||
|
var secondLevelHash *chainhash.Hash
|
||||||
|
for _, txid := range secondLevelHashes {
|
||||||
|
tx, err := net.Miner.Node.GetRawTransaction(txid)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get txn: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash {
|
||||||
|
secondLevelHash = txid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if secondLevelHash == nil {
|
||||||
|
t.Fatalf("Carol's second level tx not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll now mine a block which should confirm the two second layer
|
||||||
|
// transactions and the commit sweep.
|
||||||
|
block = mineBlocks(t, net, 1)[0]
|
||||||
|
if len(block.Transactions) != 4 {
|
||||||
|
t.Fatalf("expected 4 transactions in block, got %v",
|
||||||
|
len(block.Transactions))
|
||||||
|
}
|
||||||
|
assertTxInBlock(t, block, secondLevelHash)
|
||||||
|
|
||||||
|
// If we then mine 4 additional blocks, Bob should pull the output
|
||||||
|
// destined for him.
|
||||||
|
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
|
||||||
t.Fatalf("unable to generate block: %v", err)
|
t.Fatalf("unable to generate block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// With the block mined above, Bob should detect that Carol is
|
_, err = waitForNTxsInMempool(net.Miner.Node, 1, time.Second*15)
|
||||||
// attempting to sweep the HTLC on-chain, and should obtain the
|
|
||||||
// preimage.
|
|
||||||
_, err = waitForNTxsInMempool(net.Miner.Node, 2, time.Second*15)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find bob's sweeping transaction")
|
t.Fatalf("unable to find bob's sweeping transaction")
|
||||||
}
|
}
|
||||||
|
763
lnrpc/rpc.pb.go
763
lnrpc/rpc.pb.go
@ -521,7 +521,9 @@ func (m *ChannelPoint) String() string { return proto.CompactTextStri
|
|||||||
func (*ChannelPoint) ProtoMessage() {}
|
func (*ChannelPoint) ProtoMessage() {}
|
||||||
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
|
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
|
||||||
|
|
||||||
type isChannelPoint_FundingTxid interface{ isChannelPoint_FundingTxid() }
|
type isChannelPoint_FundingTxid interface {
|
||||||
|
isChannelPoint_FundingTxid()
|
||||||
|
}
|
||||||
|
|
||||||
type ChannelPoint_FundingTxidBytes struct {
|
type ChannelPoint_FundingTxidBytes struct {
|
||||||
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
|
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
|
||||||
@ -1608,7 +1610,9 @@ func (m *CloseStatusUpdate) String() string { return proto.CompactTex
|
|||||||
func (*CloseStatusUpdate) ProtoMessage() {}
|
func (*CloseStatusUpdate) ProtoMessage() {}
|
||||||
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} }
|
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} }
|
||||||
|
|
||||||
type isCloseStatusUpdate_Update interface{ isCloseStatusUpdate_Update() }
|
type isCloseStatusUpdate_Update interface {
|
||||||
|
isCloseStatusUpdate_Update()
|
||||||
|
}
|
||||||
|
|
||||||
type CloseStatusUpdate_ClosePending struct {
|
type CloseStatusUpdate_ClosePending struct {
|
||||||
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
|
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
|
||||||
@ -1871,7 +1875,9 @@ func (m *OpenStatusUpdate) String() string { return proto.CompactText
|
|||||||
func (*OpenStatusUpdate) ProtoMessage() {}
|
func (*OpenStatusUpdate) ProtoMessage() {}
|
||||||
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
|
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
|
||||||
|
|
||||||
type isOpenStatusUpdate_Update interface{ isOpenStatusUpdate_Update() }
|
type isOpenStatusUpdate_Update interface {
|
||||||
|
isOpenStatusUpdate_Update()
|
||||||
|
}
|
||||||
|
|
||||||
type OpenStatusUpdate_ChanPending struct {
|
type OpenStatusUpdate_ChanPending struct {
|
||||||
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
|
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
|
||||||
@ -2090,6 +2096,8 @@ type PendingChannelsResponse struct {
|
|||||||
PendingClosingChannels []*PendingChannelsResponse_ClosedChannel `protobuf:"bytes,3,rep,name=pending_closing_channels" json:"pending_closing_channels,omitempty"`
|
PendingClosingChannels []*PendingChannelsResponse_ClosedChannel `protobuf:"bytes,3,rep,name=pending_closing_channels" json:"pending_closing_channels,omitempty"`
|
||||||
// / Channels pending force closing
|
// / Channels pending force closing
|
||||||
PendingForceClosingChannels []*PendingChannelsResponse_ForceClosedChannel `protobuf:"bytes,4,rep,name=pending_force_closing_channels" json:"pending_force_closing_channels,omitempty"`
|
PendingForceClosingChannels []*PendingChannelsResponse_ForceClosedChannel `protobuf:"bytes,4,rep,name=pending_force_closing_channels" json:"pending_force_closing_channels,omitempty"`
|
||||||
|
// / Channels waiting for closing tx to confirm
|
||||||
|
WaitingCloseChannels []*PendingChannelsResponse_WaitingCloseChannel `protobuf:"bytes,5,rep,name=waiting_close_channels" json:"waiting_close_channels,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse{} }
|
func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse{} }
|
||||||
@ -2125,6 +2133,13 @@ func (m *PendingChannelsResponse) GetPendingForceClosingChannels() []*PendingCha
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *PendingChannelsResponse) GetWaitingCloseChannels() []*PendingChannelsResponse_WaitingCloseChannel {
|
||||||
|
if m != nil {
|
||||||
|
return m.WaitingCloseChannels
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type PendingChannelsResponse_PendingChannel struct {
|
type PendingChannelsResponse_PendingChannel struct {
|
||||||
RemoteNodePub string `protobuf:"bytes,1,opt,name=remote_node_pub" json:"remote_node_pub,omitempty"`
|
RemoteNodePub string `protobuf:"bytes,1,opt,name=remote_node_pub" json:"remote_node_pub,omitempty"`
|
||||||
ChannelPoint string `protobuf:"bytes,2,opt,name=channel_point" json:"channel_point,omitempty"`
|
ChannelPoint string `protobuf:"bytes,2,opt,name=channel_point" json:"channel_point,omitempty"`
|
||||||
@ -2244,6 +2259,38 @@ func (m *PendingChannelsResponse_PendingOpenChannel) GetFeePerKw() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PendingChannelsResponse_WaitingCloseChannel struct {
|
||||||
|
// / The pending channel waiting for closing tx to confirm
|
||||||
|
Channel *PendingChannelsResponse_PendingChannel `protobuf:"bytes,1,opt,name=channel" json:"channel,omitempty"`
|
||||||
|
// / The balance in satoshis encumbered in this channel
|
||||||
|
LimboBalance int64 `protobuf:"varint,2,opt,name=limbo_balance" json:"limbo_balance,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PendingChannelsResponse_WaitingCloseChannel) Reset() {
|
||||||
|
*m = PendingChannelsResponse_WaitingCloseChannel{}
|
||||||
|
}
|
||||||
|
func (m *PendingChannelsResponse_WaitingCloseChannel) String() string {
|
||||||
|
return proto.CompactTextString(m)
|
||||||
|
}
|
||||||
|
func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {}
|
||||||
|
func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor0, []int{47, 2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PendingChannelsResponse_WaitingCloseChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
|
||||||
|
if m != nil {
|
||||||
|
return m.Channel
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PendingChannelsResponse_WaitingCloseChannel) GetLimboBalance() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.LimboBalance
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type PendingChannelsResponse_ClosedChannel struct {
|
type PendingChannelsResponse_ClosedChannel struct {
|
||||||
// / The pending channel to be closed
|
// / The pending channel to be closed
|
||||||
Channel *PendingChannelsResponse_PendingChannel `protobuf:"bytes,1,opt,name=channel" json:"channel,omitempty"`
|
Channel *PendingChannelsResponse_PendingChannel `protobuf:"bytes,1,opt,name=channel" json:"channel,omitempty"`
|
||||||
@ -2255,7 +2302,7 @@ func (m *PendingChannelsResponse_ClosedChannel) Reset() { *m = PendingCh
|
|||||||
func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) }
|
func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) }
|
||||||
func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {}
|
func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {}
|
||||||
func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) {
|
func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor0, []int{47, 2}
|
return fileDescriptor0, []int{47, 3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
|
func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
|
||||||
@ -2299,7 +2346,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string {
|
|||||||
}
|
}
|
||||||
func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {}
|
func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {}
|
||||||
func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) {
|
func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor0, []int{47, 3}
|
return fileDescriptor0, []int{47, 4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
|
func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel {
|
||||||
@ -3916,7 +3963,9 @@ func (m *PolicyUpdateRequest) String() string { return proto.CompactT
|
|||||||
func (*PolicyUpdateRequest) ProtoMessage() {}
|
func (*PolicyUpdateRequest) ProtoMessage() {}
|
||||||
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
|
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
|
||||||
|
|
||||||
type isPolicyUpdateRequest_Scope interface{ isPolicyUpdateRequest_Scope() }
|
type isPolicyUpdateRequest_Scope interface {
|
||||||
|
isPolicyUpdateRequest_Scope()
|
||||||
|
}
|
||||||
|
|
||||||
type PolicyUpdateRequest_Global struct {
|
type PolicyUpdateRequest_Global struct {
|
||||||
Global bool `protobuf:"varint,1,opt,name=global,oneof"`
|
Global bool `protobuf:"varint,1,opt,name=global,oneof"`
|
||||||
@ -4234,6 +4283,7 @@ func init() {
|
|||||||
proto.RegisterType((*PendingChannelsResponse)(nil), "lnrpc.PendingChannelsResponse")
|
proto.RegisterType((*PendingChannelsResponse)(nil), "lnrpc.PendingChannelsResponse")
|
||||||
proto.RegisterType((*PendingChannelsResponse_PendingChannel)(nil), "lnrpc.PendingChannelsResponse.PendingChannel")
|
proto.RegisterType((*PendingChannelsResponse_PendingChannel)(nil), "lnrpc.PendingChannelsResponse.PendingChannel")
|
||||||
proto.RegisterType((*PendingChannelsResponse_PendingOpenChannel)(nil), "lnrpc.PendingChannelsResponse.PendingOpenChannel")
|
proto.RegisterType((*PendingChannelsResponse_PendingOpenChannel)(nil), "lnrpc.PendingChannelsResponse.PendingOpenChannel")
|
||||||
|
proto.RegisterType((*PendingChannelsResponse_WaitingCloseChannel)(nil), "lnrpc.PendingChannelsResponse.WaitingCloseChannel")
|
||||||
proto.RegisterType((*PendingChannelsResponse_ClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ClosedChannel")
|
proto.RegisterType((*PendingChannelsResponse_ClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ClosedChannel")
|
||||||
proto.RegisterType((*PendingChannelsResponse_ForceClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ForceClosedChannel")
|
proto.RegisterType((*PendingChannelsResponse_ForceClosedChannel)(nil), "lnrpc.PendingChannelsResponse.ForceClosedChannel")
|
||||||
proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest")
|
proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest")
|
||||||
@ -6301,353 +6351,356 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
|
|||||||
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 5566 bytes of a gzipped FileDescriptorProto
|
// 5611 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0xcd, 0x93, 0x1c, 0xc9,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0xcb, 0x73, 0x1c, 0xc9,
|
||||||
0x55, 0x57, 0xf5, 0xf4, 0x7c, 0xf4, 0xeb, 0x9e, 0x9e, 0x99, 0x9c, 0xd1, 0xa8, 0xd5, 0xd2, 0x6a,
|
0x71, 0x37, 0x7b, 0x30, 0x78, 0x4c, 0xce, 0x60, 0x00, 0x14, 0x40, 0x70, 0x38, 0xe4, 0x72, 0xb9,
|
||||||
0xb5, 0xe5, 0x8d, 0x95, 0x18, 0x16, 0x8d, 0x76, 0x6c, 0x2f, 0xeb, 0x15, 0xac, 0xd1, 0xf7, 0xac,
|
0xad, 0x0d, 0x91, 0x1f, 0xbe, 0x35, 0xc1, 0x85, 0xa4, 0xf5, 0x6a, 0x69, 0x4b, 0xe6, 0x1b, 0x2b,
|
||||||
0xad, 0x95, 0xc7, 0x35, 0x92, 0x17, 0xbc, 0x40, 0xbb, 0xa6, 0x3b, 0xa7, 0xa7, 0xac, 0xea, 0xaa,
|
0x71, 0x29, 0xa8, 0x41, 0x8a, 0xb6, 0x64, 0x7b, 0xd4, 0x98, 0x29, 0x0c, 0x7a, 0xd9, 0xd3, 0xdd,
|
||||||
0xda, 0xaa, 0xea, 0x19, 0xf5, 0x2e, 0x8a, 0xe0, 0x23, 0x82, 0x13, 0x0e, 0x0e, 0x70, 0x31, 0x04,
|
0xdb, 0xdd, 0x03, 0x70, 0x76, 0xcd, 0x08, 0xbf, 0xc2, 0x27, 0x2b, 0x7c, 0xb0, 0x2f, 0xb2, 0xc3,
|
||||||
0x41, 0x84, 0x7d, 0x81, 0x03, 0x47, 0x4e, 0xe6, 0x2f, 0x70, 0x04, 0xc1, 0x61, 0x4f, 0x0e, 0x6e,
|
0xe1, 0x08, 0xe9, 0x62, 0x1f, 0x7c, 0xf4, 0x49, 0xf6, 0x3f, 0xe0, 0x08, 0x87, 0x0f, 0x7b, 0x52,
|
||||||
0x7c, 0x1c, 0xc0, 0xc1, 0x85, 0x08, 0x2e, 0x1c, 0x08, 0xe2, 0xbd, 0xfc, 0xa8, 0xcc, 0xaa, 0x1a,
|
0xf8, 0xe6, 0xc7, 0xc1, 0x56, 0xf8, 0xe2, 0x08, 0x5f, 0x7c, 0x70, 0x38, 0x32, 0xeb, 0xd1, 0x55,
|
||||||
0x49, 0xb6, 0xc1, 0xb7, 0xce, 0x5f, 0xbe, 0x7a, 0xf9, 0xf5, 0xde, 0xcb, 0xf7, 0x5e, 0x66, 0x36,
|
0xdd, 0x0d, 0x92, 0x7a, 0xd8, 0xb7, 0xa9, 0x5f, 0x65, 0x67, 0xbd, 0x32, 0xb3, 0x32, 0xb3, 0xaa,
|
||||||
0xb4, 0xd2, 0x64, 0x78, 0x2d, 0x49, 0xe3, 0x3c, 0x66, 0xf3, 0x61, 0x94, 0x26, 0xc3, 0xfe, 0xc5,
|
0x06, 0x5a, 0x69, 0x32, 0xbc, 0x96, 0xa4, 0x71, 0x1e, 0xb3, 0xf9, 0x30, 0x4a, 0x93, 0x61, 0xff,
|
||||||
0x71, 0x1c, 0x8f, 0x43, 0xbe, 0xed, 0x27, 0xc1, 0xb6, 0x1f, 0x45, 0x71, 0xee, 0xe7, 0x41, 0x1c,
|
0xe2, 0x38, 0x8e, 0xc7, 0x21, 0xdf, 0xf6, 0x93, 0x60, 0xdb, 0x8f, 0xa2, 0x38, 0xf7, 0xf3, 0x20,
|
||||||
0x65, 0x82, 0xc8, 0xfd, 0x16, 0x74, 0xef, 0xf3, 0x68, 0x9f, 0xf3, 0x91, 0xc7, 0x3f, 0x9e, 0xf2,
|
0x8e, 0x32, 0x41, 0xe4, 0x7e, 0x1b, 0xba, 0xf7, 0x79, 0xb4, 0xcf, 0xf9, 0xc8, 0xe3, 0x1f, 0x4d,
|
||||||
0x2c, 0x67, 0xbf, 0x08, 0x6b, 0x3e, 0xff, 0x84, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39, 0x4a,
|
0x79, 0x96, 0xb3, 0xff, 0x0f, 0x6b, 0x3e, 0xff, 0x98, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39,
|
||||||
0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0x5e, 0x83,
|
0x4a, 0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0xde,
|
||||||
0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae, 0x80,
|
0x80, 0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae,
|
||||||
0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24, 0x47,
|
0x80, 0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24,
|
||||||
0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57, 0x5b,
|
0x47, 0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57,
|
||||||
0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f, 0xe8,
|
0x5b, 0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f,
|
||||||
0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0xcf, 0x1d, 0x58, 0x7b, 0x3f, 0x0a, 0xf2, 0x0f,
|
0xe8, 0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0x4f, 0x1c, 0x58, 0x7b, 0x3f, 0x0a, 0xf2,
|
||||||
0xfd, 0x30, 0xe4, 0xb9, 0x1a, 0xd3, 0x15, 0x58, 0x39, 0x21, 0x80, 0xc6, 0x74, 0x12, 0xa7, 0x23,
|
0x27, 0x7e, 0x18, 0xf2, 0x5c, 0x8d, 0xe9, 0x0a, 0xac, 0x9c, 0x10, 0x40, 0x63, 0x3a, 0x89, 0xd3,
|
||||||
0x39, 0xa2, 0xae, 0x80, 0xf7, 0x24, 0x7a, 0x6a, 0xcf, 0x1a, 0xa7, 0xf6, 0xac, 0x76, 0xba, 0xe6,
|
0x91, 0x1c, 0x51, 0x57, 0xc0, 0x7b, 0x12, 0x3d, 0xb5, 0x67, 0x8d, 0x53, 0x7b, 0x56, 0x3b, 0x5d,
|
||||||
0xea, 0xa7, 0xcb, 0xdd, 0x00, 0x66, 0x76, 0x4e, 0x4c, 0x87, 0xfb, 0x1e, 0xac, 0x3f, 0x8e, 0xc2,
|
0x73, 0xf5, 0xd3, 0xe5, 0x6e, 0x00, 0x33, 0x3b, 0x27, 0xa6, 0xc3, 0xfd, 0x12, 0xac, 0x3f, 0x8e,
|
||||||
0x78, 0xf8, 0xe4, 0xa7, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b, 0xd0,
|
0xc2, 0x78, 0xf8, 0xf4, 0x27, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b,
|
||||||
0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0xa7, 0x83, 0x23, 0x3f,
|
0xd0, 0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0x67, 0x83, 0x23,
|
||||||
0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34, 0xab,
|
0x3f, 0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34,
|
||||||
0x73, 0x9e, 0x2c, 0xb1, 0x37, 0x61, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2, 0x89,
|
0xab, 0x73, 0x9e, 0x2c, 0xb1, 0xb7, 0x60, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2,
|
||||||
0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, 0x26,
|
0x89, 0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89,
|
||||||
0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, 0x85,
|
0x26, 0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91,
|
||||||
0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, 0xaa,
|
0x85, 0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10,
|
||||||
0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x1b, 0xd0, 0x1d,
|
0xaa, 0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x67, 0xa1,
|
||||||
0xf1, 0x2c, 0x1f, 0xf8, 0xa3, 0x51, 0xca, 0xb3, 0x8c, 0x67, 0xbd, 0x25, 0x5a, 0xba, 0x12, 0xea,
|
0x3b, 0xe2, 0x59, 0x3e, 0xf0, 0x47, 0xa3, 0x94, 0x67, 0x19, 0xcf, 0x7a, 0x4b, 0xb4, 0x74, 0x25,
|
||||||
0xf6, 0x60, 0xf3, 0x3e, 0xcf, 0x8d, 0xd9, 0xc9, 0xe4, 0xb4, 0xbb, 0x0f, 0x80, 0x19, 0xf0, 0x1d,
|
0xd4, 0xed, 0xc1, 0xe6, 0x7d, 0x9e, 0x1b, 0xb3, 0x93, 0xc9, 0x69, 0x77, 0x1f, 0x00, 0x33, 0xe0,
|
||||||
0x9e, 0xfb, 0x41, 0x98, 0xb1, 0xb7, 0xa1, 0x93, 0x1b, 0xc4, 0x24, 0xaa, 0xed, 0x1d, 0x76, 0x8d,
|
0x3b, 0x3c, 0xf7, 0x83, 0x30, 0x63, 0xef, 0x40, 0x27, 0x37, 0x88, 0x49, 0x54, 0xdb, 0x3b, 0xec,
|
||||||
0x74, 0xec, 0x9a, 0xf1, 0x81, 0x67, 0xd1, 0xb9, 0xff, 0xed, 0x40, 0x7b, 0x9f, 0x47, 0x5a, 0xbb,
|
0x1a, 0xe9, 0xd8, 0x35, 0xe3, 0x03, 0xcf, 0xa2, 0x73, 0xff, 0xcb, 0x81, 0xf6, 0x3e, 0x8f, 0xb4,
|
||||||
0x18, 0x34, 0xb1, 0x27, 0x72, 0x25, 0xe9, 0x37, 0x7b, 0x15, 0xda, 0xd4, 0xbb, 0x2c, 0x4f, 0x83,
|
0x76, 0x31, 0x68, 0x62, 0x4f, 0xe4, 0x4a, 0xd2, 0x6f, 0xf6, 0x3a, 0xb4, 0xa9, 0x77, 0x59, 0x9e,
|
||||||
0x68, 0x4c, 0x4b, 0xd0, 0xf2, 0x00, 0xa1, 0x7d, 0x42, 0xd8, 0x2a, 0xcc, 0xf9, 0x93, 0x9c, 0x26,
|
0x06, 0xd1, 0x98, 0x96, 0xa0, 0xe5, 0x01, 0x42, 0xfb, 0x84, 0xb0, 0x55, 0x98, 0xf3, 0x27, 0x39,
|
||||||
0x7e, 0xce, 0xc3, 0x9f, 0xa8, 0x77, 0x89, 0x3f, 0x9b, 0xf0, 0x28, 0x2f, 0x26, 0xbb, 0xe3, 0xb5,
|
0x4d, 0xfc, 0x9c, 0x87, 0x3f, 0x51, 0xef, 0x12, 0x7f, 0x36, 0xe1, 0x51, 0x5e, 0x4c, 0x76, 0xc7,
|
||||||
0x25, 0xb6, 0x8b, 0xb3, 0x7d, 0x0d, 0xd6, 0x4d, 0x12, 0xc5, 0x7d, 0x9e, 0xb8, 0xaf, 0x19, 0x94,
|
0x6b, 0x4b, 0x6c, 0x17, 0x67, 0xfb, 0x1a, 0xac, 0x9b, 0x24, 0x8a, 0xfb, 0x3c, 0x71, 0x5f, 0x33,
|
||||||
0xb2, 0x91, 0x2b, 0xb0, 0xa2, 0xe8, 0x53, 0xd1, 0x59, 0x9a, 0xfe, 0x96, 0xd7, 0x95, 0xb0, 0x1a,
|
0x28, 0x65, 0x23, 0x57, 0x60, 0x45, 0xd1, 0xa7, 0xa2, 0xb3, 0x34, 0xfd, 0x2d, 0xaf, 0x2b, 0x61,
|
||||||
0xc2, 0x55, 0x58, 0x3d, 0x0c, 0x22, 0x3f, 0x1c, 0x0c, 0xc3, 0xfc, 0x78, 0x30, 0xe2, 0x61, 0xee,
|
0x35, 0x84, 0xab, 0xb0, 0x7a, 0x18, 0x44, 0x7e, 0x38, 0x18, 0x86, 0xf9, 0xf1, 0x60, 0xc4, 0xc3,
|
||||||
0xd3, 0x42, 0xcc, 0x7b, 0x5d, 0xc2, 0x6f, 0x87, 0xf9, 0xf1, 0x1d, 0x44, 0xdd, 0x3f, 0x75, 0xa0,
|
0xdc, 0xa7, 0x85, 0x98, 0xf7, 0xba, 0x84, 0xdf, 0x0e, 0xf3, 0xe3, 0x3b, 0x88, 0xba, 0x7f, 0xe4,
|
||||||
0x23, 0x06, 0x2f, 0x15, 0xff, 0x75, 0x58, 0x56, 0x6d, 0xf0, 0x34, 0x8d, 0x53, 0x29, 0x87, 0x36,
|
0x40, 0x47, 0x0c, 0x5e, 0x2a, 0xfe, 0x9b, 0xb0, 0xac, 0xda, 0xe0, 0x69, 0x1a, 0xa7, 0x52, 0x0e,
|
||||||
0xc8, 0xb6, 0x60, 0x55, 0x01, 0x49, 0xca, 0x83, 0x89, 0x3f, 0xe6, 0x52, 0xdb, 0x2b, 0x38, 0xdb,
|
0x6d, 0x90, 0x6d, 0xc1, 0xaa, 0x02, 0x92, 0x94, 0x07, 0x13, 0x7f, 0xcc, 0xa5, 0xb6, 0x57, 0x70,
|
||||||
0x29, 0x38, 0xa6, 0xf1, 0x34, 0x17, 0xaa, 0xd7, 0xde, 0xe9, 0xc8, 0x85, 0xf1, 0x10, 0xf3, 0x6c,
|
0xb6, 0x53, 0x70, 0x4c, 0xe3, 0x69, 0x2e, 0x54, 0xaf, 0xbd, 0xd3, 0x91, 0x0b, 0xe3, 0x21, 0xe6,
|
||||||
0x12, 0xf7, 0x7b, 0x0e, 0x74, 0x6e, 0x1f, 0xf9, 0x51, 0xc4, 0xc3, 0xbd, 0x38, 0x88, 0x72, 0x76,
|
0xd9, 0x24, 0xee, 0xf7, 0x1c, 0xe8, 0xdc, 0x3e, 0xf2, 0xa3, 0x88, 0x87, 0x7b, 0x71, 0x10, 0xe5,
|
||||||
0x1d, 0xd8, 0xe1, 0x34, 0x1a, 0x05, 0xd1, 0x78, 0x90, 0x3f, 0x0d, 0x46, 0x83, 0x83, 0x59, 0xce,
|
0xec, 0x3a, 0xb0, 0xc3, 0x69, 0x34, 0x0a, 0xa2, 0xf1, 0x20, 0x7f, 0x16, 0x8c, 0x06, 0x07, 0xb3,
|
||||||
0x33, 0xb1, 0x44, 0xbb, 0x67, 0xbc, 0x9a, 0x3a, 0xf6, 0x26, 0xac, 0x5a, 0x68, 0x96, 0xa7, 0x62,
|
0x9c, 0x67, 0x62, 0x89, 0x76, 0xcf, 0x78, 0x35, 0x75, 0xec, 0x2d, 0x58, 0xb5, 0xd0, 0x2c, 0x4f,
|
||||||
0xdd, 0x76, 0xcf, 0x78, 0x95, 0x1a, 0x14, 0xfc, 0x78, 0x9a, 0x27, 0xd3, 0x7c, 0x10, 0x44, 0x23,
|
0xc5, 0xba, 0xed, 0x9e, 0xf1, 0x2a, 0x35, 0x28, 0xf8, 0xf1, 0x34, 0x4f, 0xa6, 0xf9, 0x20, 0x88,
|
||||||
0xfe, 0x94, 0xfa, 0xb8, 0xec, 0x59, 0xd8, 0xad, 0x2e, 0x74, 0xcc, 0xef, 0xdc, 0xf7, 0x60, 0xf5,
|
0x46, 0xfc, 0x19, 0xf5, 0x71, 0xd9, 0xb3, 0xb0, 0x5b, 0x5d, 0xe8, 0x98, 0xdf, 0xb9, 0x5f, 0x82,
|
||||||
0x01, 0x6a, 0x44, 0x14, 0x44, 0xe3, 0x9b, 0x42, 0x6c, 0x51, 0x4d, 0x93, 0xe9, 0xc1, 0x13, 0x3e,
|
0xd5, 0x07, 0xa8, 0x11, 0x51, 0x10, 0x8d, 0x6f, 0x0a, 0xb1, 0x45, 0x35, 0x4d, 0xa6, 0x07, 0x4f,
|
||||||
0x93, 0xf3, 0x26, 0x4b, 0x28, 0x54, 0x47, 0x71, 0x96, 0x4b, 0xc9, 0xa1, 0xdf, 0xee, 0x3f, 0x3b,
|
0xf9, 0x4c, 0xce, 0x9b, 0x2c, 0xa1, 0x50, 0x1d, 0xc5, 0x59, 0x2e, 0x25, 0x87, 0x7e, 0xbb, 0xff,
|
||||||
0xb0, 0x82, 0x73, 0xff, 0x81, 0x1f, 0xcd, 0xd4, 0xca, 0x3d, 0x80, 0x0e, 0xb2, 0x7a, 0x14, 0xdf,
|
0xe4, 0xc0, 0x0a, 0xce, 0xfd, 0x07, 0x7e, 0x34, 0x53, 0x2b, 0xf7, 0x00, 0x3a, 0xc8, 0xea, 0x51,
|
||||||
0x14, 0xca, 0x2e, 0x84, 0xf8, 0xaa, 0x9c, 0xab, 0x12, 0xf5, 0x35, 0x93, 0x14, 0x8d, 0xf9, 0xcc,
|
0x7c, 0x53, 0x28, 0xbb, 0x10, 0xe2, 0xab, 0x72, 0xae, 0x4a, 0xd4, 0xd7, 0x4c, 0x52, 0x34, 0xe6,
|
||||||
0xb3, 0xbe, 0x46, 0xb1, 0xcd, 0xfd, 0x74, 0xcc, 0x73, 0x32, 0x03, 0xd2, 0x2c, 0x80, 0x80, 0x6e,
|
0x33, 0xcf, 0xfa, 0x1a, 0xc5, 0x36, 0xf7, 0xd3, 0x31, 0xcf, 0xc9, 0x0c, 0x48, 0xb3, 0x00, 0x02,
|
||||||
0xc7, 0xd1, 0x21, 0xbb, 0x0c, 0x9d, 0xcc, 0xcf, 0x07, 0x09, 0x4f, 0x69, 0xd6, 0x48, 0xf4, 0xe6,
|
0xba, 0x1d, 0x47, 0x87, 0xec, 0x32, 0x74, 0x32, 0x3f, 0x1f, 0x24, 0x3c, 0xa5, 0x59, 0x23, 0xd1,
|
||||||
0x3c, 0xc8, 0xfc, 0x7c, 0x8f, 0xa7, 0xb7, 0x66, 0x39, 0xef, 0x7f, 0x19, 0xd6, 0x2a, 0xad, 0xa0,
|
0x9b, 0xf3, 0x20, 0xf3, 0xf3, 0x3d, 0x9e, 0xde, 0x9a, 0xe5, 0xbc, 0xff, 0x65, 0x58, 0xab, 0xb4,
|
||||||
0xb4, 0x17, 0x43, 0xc4, 0x9f, 0x6c, 0x03, 0xe6, 0x8f, 0xfd, 0x70, 0xca, 0xa5, 0x75, 0x12, 0x85,
|
0x82, 0xd2, 0x5e, 0x0c, 0x11, 0x7f, 0xb2, 0x0d, 0x98, 0x3f, 0xf6, 0xc3, 0x29, 0x97, 0xd6, 0x49,
|
||||||
0x77, 0x1b, 0xef, 0x38, 0xee, 0x1b, 0xb0, 0x5a, 0x74, 0x5b, 0x0a, 0x19, 0x83, 0x26, 0xce, 0xa0,
|
0x14, 0xde, 0x6b, 0xbc, 0xeb, 0xb8, 0x9f, 0x85, 0xd5, 0xa2, 0xdb, 0x52, 0xc8, 0x18, 0x34, 0x71,
|
||||||
0x64, 0x40, 0xbf, 0xdd, 0xdf, 0x73, 0x04, 0xe1, 0xed, 0x38, 0xd0, 0x9a, 0x8e, 0x84, 0x68, 0x10,
|
0x06, 0x25, 0x03, 0xfa, 0xed, 0xfe, 0x96, 0x23, 0x08, 0x6f, 0xc7, 0x81, 0xd6, 0x74, 0x24, 0x44,
|
||||||
0x14, 0x21, 0xfe, 0x3e, 0xd5, 0x12, 0xfe, 0xec, 0x83, 0x75, 0xaf, 0xc0, 0x9a, 0xd1, 0x85, 0xe7,
|
0x83, 0xa0, 0x08, 0xf1, 0xf7, 0xa9, 0x96, 0xf0, 0xa7, 0x1f, 0xac, 0x7b, 0x05, 0xd6, 0x8c, 0x2e,
|
||||||
0x74, 0xf6, 0x3b, 0x0e, 0xac, 0x3d, 0xe4, 0x27, 0x72, 0xd5, 0x55, 0x6f, 0xdf, 0x81, 0x66, 0x3e,
|
0xbc, 0xa0, 0xb3, 0xdf, 0x71, 0x60, 0xed, 0x21, 0x3f, 0x91, 0xab, 0xae, 0x7a, 0xfb, 0x2e, 0x34,
|
||||||
0x4b, 0xc4, 0x56, 0xdc, 0xdd, 0x79, 0x5d, 0x2e, 0x5a, 0x85, 0xee, 0x9a, 0x2c, 0x3e, 0x9a, 0x25,
|
0xf3, 0x59, 0x22, 0xb6, 0xe2, 0xee, 0xce, 0x9b, 0x72, 0xd1, 0x2a, 0x74, 0xd7, 0x64, 0xf1, 0xd1,
|
||||||
0xdc, 0xa3, 0x2f, 0xdc, 0xf7, 0xa0, 0x6d, 0x80, 0xec, 0x1c, 0xac, 0x7f, 0xf8, 0xfe, 0xa3, 0x87,
|
0x2c, 0xe1, 0x1e, 0x7d, 0xe1, 0x7e, 0x09, 0xda, 0x06, 0xc8, 0xce, 0xc1, 0xfa, 0x93, 0xf7, 0x1f,
|
||||||
0x77, 0xf7, 0xf7, 0x07, 0x7b, 0x8f, 0x6f, 0x7d, 0xf5, 0xee, 0x6f, 0x0c, 0x76, 0x6f, 0xee, 0xef,
|
0x3d, 0xbc, 0xbb, 0xbf, 0x3f, 0xd8, 0x7b, 0x7c, 0xeb, 0xab, 0x77, 0x7f, 0x65, 0xb0, 0x7b, 0x73,
|
||||||
0xae, 0x9e, 0x61, 0x9b, 0xc0, 0x1e, 0xde, 0xdd, 0x7f, 0x74, 0xf7, 0x8e, 0x85, 0x3b, 0x6e, 0x1f,
|
0x7f, 0x77, 0xf5, 0x0c, 0xdb, 0x04, 0xf6, 0xf0, 0xee, 0xfe, 0xa3, 0xbb, 0x77, 0x2c, 0xdc, 0x71,
|
||||||
0x7a, 0x0f, 0xf9, 0xc9, 0x87, 0x41, 0x1e, 0xf1, 0x2c, 0xb3, 0x5b, 0x73, 0xaf, 0x01, 0x33, 0xbb,
|
0xfb, 0xd0, 0x7b, 0xc8, 0x4f, 0x9e, 0x04, 0x79, 0xc4, 0xb3, 0xcc, 0x6e, 0xcd, 0xbd, 0x06, 0xcc,
|
||||||
0x20, 0x47, 0xd5, 0x83, 0x45, 0x69, 0x6a, 0xd5, 0x4e, 0x23, 0x8b, 0xee, 0x1b, 0xc0, 0xf6, 0x83,
|
0xec, 0x82, 0x1c, 0x55, 0x0f, 0x16, 0xa5, 0xa9, 0x55, 0x3b, 0x8d, 0x2c, 0xba, 0x9f, 0x05, 0xb6,
|
||||||
0x71, 0xf4, 0x01, 0xcf, 0x32, 0x7f, 0xcc, 0xd5, 0xd8, 0x56, 0x61, 0x6e, 0x92, 0x8d, 0xa5, 0x51,
|
0x1f, 0x8c, 0xa3, 0x0f, 0x78, 0x96, 0xf9, 0x63, 0xae, 0xc6, 0xb6, 0x0a, 0x73, 0x93, 0x6c, 0x2c,
|
||||||
0xc4, 0x9f, 0xee, 0xe7, 0x61, 0xdd, 0xa2, 0x93, 0x8c, 0x2f, 0x42, 0x2b, 0x0b, 0xc6, 0x91, 0x9f,
|
0x8d, 0x22, 0xfe, 0x74, 0x3f, 0x07, 0xeb, 0x16, 0x9d, 0x64, 0x7c, 0x11, 0x5a, 0x59, 0x30, 0x8e,
|
||||||
0x4f, 0x53, 0x2e, 0x59, 0x17, 0x80, 0x7b, 0x0f, 0x36, 0xbe, 0xc1, 0xd3, 0xe0, 0x70, 0xf6, 0x22,
|
0xfc, 0x7c, 0x9a, 0x72, 0xc9, 0xba, 0x00, 0xdc, 0x7b, 0xb0, 0xf1, 0x0d, 0x9e, 0x06, 0x87, 0xb3,
|
||||||
0xf6, 0x36, 0x9f, 0x46, 0x99, 0xcf, 0x5d, 0x38, 0x5b, 0xe2, 0x23, 0x9b, 0x17, 0x82, 0x28, 0x97,
|
0x97, 0xb1, 0xb7, 0xf9, 0x34, 0xca, 0x7c, 0xee, 0xc2, 0xd9, 0x12, 0x1f, 0xd9, 0xbc, 0x10, 0x44,
|
||||||
0x6b, 0xc9, 0x13, 0x05, 0x43, 0x2d, 0x1b, 0xa6, 0x5a, 0xba, 0x8f, 0x81, 0xdd, 0x8e, 0xa3, 0x88,
|
0xb9, 0x5c, 0x4b, 0x9e, 0x28, 0x18, 0x6a, 0xd9, 0x30, 0xd5, 0xd2, 0x7d, 0x0c, 0xec, 0x76, 0x1c,
|
||||||
0x0f, 0xf3, 0x3d, 0xce, 0xd3, 0xc2, 0xbf, 0x2a, 0xa4, 0xae, 0xbd, 0x73, 0x4e, 0xae, 0x63, 0x59,
|
0x45, 0x7c, 0x98, 0xef, 0x71, 0x9e, 0x16, 0xfe, 0x55, 0x21, 0x75, 0xed, 0x9d, 0x73, 0x72, 0x1d,
|
||||||
0xd7, 0xa5, 0x38, 0x32, 0x68, 0x26, 0x3c, 0x9d, 0x10, 0xe3, 0x25, 0x8f, 0x7e, 0xbb, 0x67, 0x61,
|
0xcb, 0xba, 0x2e, 0xc5, 0x91, 0x41, 0x33, 0xe1, 0xe9, 0x84, 0x18, 0x2f, 0x79, 0xf4, 0xdb, 0x3d,
|
||||||
0xdd, 0x62, 0x2b, 0x77, 0xfb, 0xb7, 0xe0, 0xec, 0x9d, 0x20, 0x1b, 0x56, 0x1b, 0xec, 0xc1, 0x62,
|
0x0b, 0xeb, 0x16, 0x5b, 0xb9, 0xdb, 0xbf, 0x0d, 0x67, 0xef, 0x04, 0xd9, 0xb0, 0xda, 0x60, 0x0f,
|
||||||
0x32, 0x3d, 0x18, 0x14, 0x3a, 0xa5, 0x8a, 0xb8, 0x09, 0x96, 0x3f, 0x91, 0xcc, 0xfe, 0xd0, 0x81,
|
0x16, 0x93, 0xe9, 0xc1, 0xa0, 0xd0, 0x29, 0x55, 0xc4, 0x4d, 0xb0, 0xfc, 0x89, 0x64, 0xf6, 0x7b,
|
||||||
0xe6, 0xee, 0xa3, 0x07, 0xb7, 0x59, 0x1f, 0x96, 0x82, 0x68, 0x18, 0x4f, 0x70, 0xeb, 0x10, 0x83,
|
0x0e, 0x34, 0x77, 0x1f, 0x3d, 0xb8, 0xcd, 0xfa, 0xb0, 0x14, 0x44, 0xc3, 0x78, 0x82, 0x5b, 0x87,
|
||||||
0xd6, 0xe5, 0x53, 0x75, 0xe5, 0x22, 0xb4, 0x68, 0xc7, 0xc1, 0x7d, 0x5d, 0xba, 0x42, 0x05, 0x80,
|
0x18, 0xb4, 0x2e, 0x9f, 0xaa, 0x2b, 0x17, 0xa1, 0x45, 0x3b, 0x0e, 0xee, 0xeb, 0xd2, 0x15, 0x2a,
|
||||||
0x3e, 0x05, 0x7f, 0x9a, 0x04, 0x29, 0x39, 0x0d, 0xca, 0x15, 0x68, 0x92, 0x45, 0xac, 0x56, 0xb8,
|
0x00, 0xf4, 0x29, 0xf8, 0xb3, 0x24, 0x48, 0xc9, 0x69, 0x50, 0xae, 0x40, 0x93, 0x2c, 0x62, 0xb5,
|
||||||
0xff, 0xd3, 0x84, 0x45, 0x69, 0xab, 0xa9, 0xbd, 0x61, 0x1e, 0x1c, 0x73, 0xd9, 0x13, 0x59, 0xc2,
|
0xc2, 0xfd, 0xef, 0x26, 0x2c, 0x4a, 0x5b, 0x4d, 0xed, 0x0d, 0xf3, 0xe0, 0x98, 0xcb, 0x9e, 0xc8,
|
||||||
0x5d, 0x25, 0xe5, 0x93, 0x38, 0xe7, 0x03, 0x6b, 0x19, 0x6c, 0x10, 0xa9, 0x86, 0x82, 0xd1, 0x20,
|
0x12, 0xee, 0x2a, 0x29, 0x9f, 0xc4, 0x39, 0x1f, 0x58, 0xcb, 0x60, 0x83, 0x48, 0x35, 0x14, 0x8c,
|
||||||
0x41, 0xab, 0x4f, 0x3d, 0x6b, 0x79, 0x36, 0x88, 0x93, 0x85, 0xc0, 0x20, 0x18, 0x51, 0x9f, 0x9a,
|
0x06, 0x09, 0x5a, 0x7d, 0xea, 0x59, 0xcb, 0xb3, 0x41, 0x9c, 0x2c, 0x04, 0x06, 0xc1, 0x88, 0xfa,
|
||||||
0x9e, 0x2a, 0xe2, 0x4c, 0x0c, 0xfd, 0xc4, 0x1f, 0x06, 0xf9, 0x4c, 0x2a, 0xb7, 0x2e, 0x23, 0xef,
|
0xd4, 0xf4, 0x54, 0x11, 0x67, 0x62, 0xe8, 0x27, 0xfe, 0x30, 0xc8, 0x67, 0x52, 0xb9, 0x75, 0x19,
|
||||||
0x30, 0x1e, 0xfa, 0xe1, 0xe0, 0xc0, 0x0f, 0xfd, 0x68, 0xc8, 0xa5, 0xe3, 0x62, 0x83, 0xe8, 0x9b,
|
0x79, 0x87, 0xf1, 0xd0, 0x0f, 0x07, 0x07, 0x7e, 0xe8, 0x47, 0x43, 0x2e, 0x1d, 0x17, 0x1b, 0x44,
|
||||||
0xc8, 0x2e, 0x29, 0x32, 0xe1, 0xbf, 0x94, 0x50, 0xf4, 0x71, 0x86, 0xf1, 0x64, 0x12, 0xe4, 0xe8,
|
0xdf, 0x44, 0x76, 0x49, 0x91, 0x09, 0xff, 0xa5, 0x84, 0xa2, 0x8f, 0x33, 0x8c, 0x27, 0x93, 0x20,
|
||||||
0xd2, 0xf4, 0x96, 0x84, 0x21, 0x29, 0x10, 0x1a, 0x89, 0x28, 0x9d, 0x88, 0xd9, 0x6b, 0x89, 0xd6,
|
0x47, 0x97, 0xa6, 0xb7, 0x24, 0x0c, 0x49, 0x81, 0xd0, 0x48, 0x44, 0xe9, 0x44, 0xcc, 0x5e, 0x4b,
|
||||||
0x2c, 0x10, 0xb9, 0x1c, 0x72, 0x4e, 0x06, 0xe9, 0xc9, 0x49, 0x0f, 0x04, 0x97, 0x02, 0xc1, 0x75,
|
0xb4, 0x66, 0x81, 0xc8, 0xe5, 0x90, 0x73, 0x32, 0x48, 0x4f, 0x4f, 0x7a, 0x20, 0xb8, 0x14, 0x08,
|
||||||
0x98, 0x46, 0x19, 0xcf, 0xf3, 0x90, 0x8f, 0x74, 0x87, 0xda, 0x44, 0x56, 0xad, 0x60, 0xd7, 0x61,
|
0xae, 0xc3, 0x34, 0xca, 0x78, 0x9e, 0x87, 0x7c, 0xa4, 0x3b, 0xd4, 0x26, 0xb2, 0x6a, 0x05, 0xbb,
|
||||||
0x5d, 0x78, 0x59, 0x99, 0x9f, 0xc7, 0xd9, 0x51, 0x90, 0x0d, 0x32, 0x1e, 0xe5, 0xbd, 0x0e, 0xd1,
|
0x0e, 0xeb, 0xc2, 0xcb, 0xca, 0xfc, 0x3c, 0xce, 0x8e, 0x82, 0x6c, 0x90, 0xf1, 0x28, 0xef, 0x75,
|
||||||
0xd7, 0x55, 0xb1, 0x77, 0xe0, 0x5c, 0x09, 0x4e, 0xf9, 0x90, 0x07, 0xc7, 0x7c, 0xd4, 0x5b, 0xa6,
|
0x88, 0xbe, 0xae, 0x8a, 0xbd, 0x0b, 0xe7, 0x4a, 0x70, 0xca, 0x87, 0x3c, 0x38, 0xe6, 0xa3, 0xde,
|
||||||
0xaf, 0x4e, 0xab, 0x66, 0x97, 0xa1, 0x8d, 0xce, 0xe5, 0x34, 0x19, 0xf9, 0xb8, 0x0f, 0x77, 0x69,
|
0x32, 0x7d, 0x75, 0x5a, 0x35, 0xbb, 0x0c, 0x6d, 0x74, 0x2e, 0xa7, 0xc9, 0xc8, 0xc7, 0x7d, 0xb8,
|
||||||
0x1d, 0x4c, 0x88, 0xbd, 0x05, 0xcb, 0x09, 0x17, 0x9b, 0xe5, 0x51, 0x1e, 0x0e, 0xb3, 0xde, 0x0a,
|
0x4b, 0xeb, 0x60, 0x42, 0xec, 0x6d, 0x58, 0x4e, 0xb8, 0xd8, 0x2c, 0x8f, 0xf2, 0x70, 0x98, 0xf5,
|
||||||
0xed, 0x64, 0x6d, 0xa9, 0x4c, 0x28, 0xb9, 0x9e, 0x4d, 0x81, 0x42, 0x39, 0xcc, 0xc8, 0x5d, 0xf1,
|
0x56, 0x68, 0x27, 0x6b, 0x4b, 0x65, 0x42, 0xc9, 0xf5, 0x6c, 0x0a, 0x14, 0xca, 0x61, 0x46, 0xee,
|
||||||
0x67, 0xbd, 0x55, 0x12, 0xb7, 0x02, 0x20, 0x1d, 0x49, 0x83, 0x63, 0x3f, 0xe7, 0xbd, 0x35, 0x92,
|
0x8a, 0x3f, 0xeb, 0xad, 0x92, 0xb8, 0x15, 0x00, 0xe9, 0x48, 0x1a, 0x1c, 0xfb, 0x39, 0xef, 0xad,
|
||||||
0x2d, 0x55, 0x74, 0xff, 0xd2, 0x81, 0xf5, 0x07, 0x41, 0x96, 0x4b, 0x21, 0xd4, 0xe6, 0xf8, 0x55,
|
0x91, 0x6c, 0xa9, 0xa2, 0xfb, 0x67, 0x0e, 0xac, 0x3f, 0x08, 0xb2, 0x5c, 0x0a, 0xa1, 0x36, 0xc7,
|
||||||
0x68, 0x0b, 0xf1, 0x1b, 0xc4, 0x51, 0x38, 0x93, 0x12, 0x09, 0x02, 0xfa, 0x5a, 0x14, 0xce, 0xd8,
|
0xaf, 0x43, 0x5b, 0x88, 0xdf, 0x20, 0x8e, 0xc2, 0x99, 0x94, 0x48, 0x10, 0xd0, 0xd7, 0xa2, 0x70,
|
||||||
0xe7, 0x60, 0x39, 0x88, 0x4c, 0x12, 0xa1, 0xc3, 0x1d, 0x05, 0x12, 0xd1, 0xab, 0xd0, 0x4e, 0xa6,
|
0xc6, 0x3e, 0x03, 0xcb, 0x41, 0x64, 0x92, 0x08, 0x1d, 0xee, 0x28, 0x90, 0x88, 0x5e, 0x87, 0x76,
|
||||||
0x07, 0x61, 0x30, 0x14, 0x24, 0x73, 0x82, 0x8b, 0x80, 0x88, 0x00, 0x1d, 0x3d, 0xd1, 0x13, 0x41,
|
0x32, 0x3d, 0x08, 0x83, 0xa1, 0x20, 0x99, 0x13, 0x5c, 0x04, 0x44, 0x04, 0xe8, 0xe8, 0x89, 0x9e,
|
||||||
0xd1, 0x24, 0x8a, 0xb6, 0xc4, 0x90, 0xc4, 0xbd, 0x05, 0x1b, 0x76, 0x07, 0xa5, 0xb1, 0xda, 0x82,
|
0x08, 0x8a, 0x26, 0x51, 0xb4, 0x25, 0x86, 0x24, 0xee, 0x2d, 0xd8, 0xb0, 0x3b, 0x28, 0x8d, 0xd5,
|
||||||
0x25, 0x29, 0xdb, 0x59, 0xaf, 0x4d, 0xf3, 0xd3, 0x95, 0xf3, 0x23, 0x49, 0x3d, 0x5d, 0xef, 0xfe,
|
0x16, 0x2c, 0x49, 0xd9, 0xce, 0x7a, 0x6d, 0x9a, 0x9f, 0xae, 0x9c, 0x1f, 0x49, 0xea, 0xe9, 0x7a,
|
||||||
0xbb, 0x03, 0x4d, 0x34, 0x00, 0xa7, 0x1b, 0x0b, 0xd3, 0xa6, 0xcf, 0x59, 0x36, 0x9d, 0xfc, 0x7e,
|
0xf7, 0xdf, 0x1c, 0x68, 0xa2, 0x01, 0x38, 0xdd, 0x58, 0x98, 0x36, 0x7d, 0xce, 0xb2, 0xe9, 0xe4,
|
||||||
0xf4, 0x8a, 0x84, 0x48, 0x08, 0xb5, 0x31, 0x90, 0xa2, 0x3e, 0xe5, 0xc3, 0x63, 0xd2, 0x1d, 0x5d,
|
0xf7, 0xa3, 0x57, 0x24, 0x44, 0x42, 0xa8, 0x8d, 0x81, 0x14, 0xf5, 0x29, 0x1f, 0x1e, 0x93, 0xee,
|
||||||
0x8f, 0x08, 0x6a, 0x16, 0x6e, 0x9d, 0xf4, 0xb5, 0x50, 0x1c, 0x5d, 0x56, 0x75, 0xf4, 0xe5, 0x62,
|
0xe8, 0x7a, 0x44, 0x50, 0xb3, 0x70, 0xeb, 0xa4, 0xaf, 0x85, 0xe2, 0xe8, 0xb2, 0xaa, 0xa3, 0x2f,
|
||||||
0x51, 0x47, 0xdf, 0xf5, 0x60, 0x31, 0x88, 0x0e, 0xe2, 0x69, 0x34, 0x22, 0x25, 0x59, 0xf2, 0x54,
|
0x17, 0x8b, 0x3a, 0xfa, 0xae, 0x07, 0x8b, 0x41, 0x74, 0x10, 0x4f, 0xa3, 0x11, 0x29, 0xc9, 0x92,
|
||||||
0x11, 0x17, 0x3b, 0x21, 0x4f, 0x2a, 0x98, 0x70, 0xa9, 0x1d, 0x05, 0xe0, 0x32, 0x74, 0xad, 0x32,
|
0xa7, 0x8a, 0xb8, 0xd8, 0x09, 0x79, 0x52, 0xc1, 0x84, 0x4b, 0xed, 0x28, 0x00, 0x97, 0xa1, 0x6b,
|
||||||
0x32, 0x78, 0x7a, 0x1f, 0x7b, 0x1b, 0xd6, 0x0c, 0x4c, 0xce, 0xe0, 0x6b, 0x30, 0x9f, 0x20, 0x20,
|
0x95, 0x91, 0xc1, 0xd3, 0xfb, 0xd8, 0x3b, 0xb0, 0x66, 0x60, 0x72, 0x06, 0xdf, 0x80, 0xf9, 0x04,
|
||||||
0x1d, 0x25, 0x25, 0x5e, 0x64, 0x29, 0x45, 0x8d, 0xbb, 0x8a, 0xf1, 0x73, 0xfe, 0x7e, 0x74, 0x18,
|
0x01, 0xe9, 0x28, 0x29, 0xf1, 0x22, 0x4b, 0x29, 0x6a, 0xdc, 0x55, 0x8c, 0x9f, 0xf3, 0xf7, 0xa3,
|
||||||
0x2b, 0x4e, 0x3f, 0x9a, 0xc3, 0x80, 0x57, 0x42, 0x92, 0xd1, 0x55, 0x58, 0x09, 0x46, 0x3c, 0xca,
|
0xc3, 0x58, 0x71, 0xfa, 0xe1, 0x1c, 0x06, 0xbc, 0x12, 0x92, 0x8c, 0xae, 0xc2, 0x4a, 0x30, 0xe2,
|
||||||
0x83, 0x7c, 0x36, 0xb0, 0x3c, 0xb8, 0x32, 0x8c, 0x3b, 0x8c, 0x1f, 0x06, 0x7e, 0x26, 0x6d, 0x98,
|
0x51, 0x1e, 0xe4, 0xb3, 0x81, 0xe5, 0xc1, 0x95, 0x61, 0xdc, 0x61, 0xfc, 0x30, 0xf0, 0x33, 0x69,
|
||||||
0x28, 0xb0, 0x1d, 0xd8, 0x40, 0xf1, 0x57, 0x12, 0xad, 0x97, 0x55, 0x38, 0x92, 0xb5, 0x75, 0xa8,
|
0xc3, 0x44, 0x81, 0xed, 0xc0, 0x06, 0x8a, 0xbf, 0x92, 0x68, 0xbd, 0xac, 0xc2, 0x91, 0xac, 0xad,
|
||||||
0xb1, 0x88, 0x4b, 0x09, 0xd4, 0x9f, 0x08, 0x4b, 0x5b, 0x57, 0x85, 0xb3, 0x26, 0x38, 0xe1, 0x90,
|
0x43, 0x8d, 0x45, 0x5c, 0x4a, 0xa0, 0xfe, 0x44, 0x58, 0xda, 0xba, 0x2a, 0x9c, 0x35, 0xc1, 0x09,
|
||||||
0xe7, 0x85, 0x8a, 0x68, 0xa0, 0x12, 0xbd, 0x2d, 0x08, 0x27, 0xb6, 0x1c, 0xbd, 0x19, 0x11, 0xe0,
|
0x87, 0x3c, 0x2f, 0x54, 0x44, 0x03, 0x95, 0xe8, 0x6d, 0x41, 0x38, 0xb1, 0xe5, 0xe8, 0xcd, 0x88,
|
||||||
0x52, 0x25, 0x02, 0xbc, 0x0a, 0x2b, 0xd9, 0x2c, 0x1a, 0xf2, 0xd1, 0x20, 0x8f, 0xb1, 0xdd, 0x20,
|
0x00, 0x97, 0x2a, 0x11, 0xe0, 0x55, 0x58, 0xc9, 0x66, 0xd1, 0x90, 0x8f, 0x06, 0x79, 0x8c, 0xed,
|
||||||
0xa2, 0xd5, 0x59, 0xf2, 0xca, 0x30, 0xc5, 0xaa, 0x3c, 0xcb, 0x23, 0x9e, 0x93, 0xe9, 0x5a, 0xf2,
|
0x06, 0x11, 0xad, 0xce, 0x92, 0x57, 0x86, 0x29, 0x56, 0xe5, 0x59, 0x1e, 0xf1, 0x9c, 0x4c, 0xd7,
|
||||||
0x54, 0x11, 0x77, 0x01, 0x22, 0x11, 0x42, 0xdd, 0xf2, 0x64, 0x09, 0xb7, 0xca, 0x69, 0x1a, 0x64,
|
0x92, 0xa7, 0x8a, 0xb8, 0x0b, 0x10, 0x89, 0x10, 0xea, 0x96, 0x27, 0x4b, 0xb8, 0x55, 0x4e, 0xd3,
|
||||||
0xbd, 0x0e, 0xa1, 0xf4, 0x9b, 0x7d, 0x01, 0xce, 0x1e, 0x60, 0x64, 0x75, 0xc4, 0xfd, 0x11, 0x4f,
|
0x20, 0xeb, 0x75, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xf6, 0x00, 0x23, 0xab, 0x23, 0xee, 0x8f,
|
||||||
0x69, 0xf5, 0x45, 0x60, 0x29, 0x2c, 0x50, 0x7d, 0x25, 0xb6, 0x7d, 0xcc, 0xd3, 0x2c, 0x88, 0x23,
|
0x78, 0x4a, 0xab, 0x2f, 0x02, 0x4b, 0x61, 0x81, 0xea, 0x2b, 0xb1, 0xed, 0x63, 0x9e, 0x66, 0x41,
|
||||||
0xb2, 0x3d, 0x2d, 0x4f, 0x15, 0xdd, 0x4f, 0x68, 0x47, 0xd7, 0x21, 0xef, 0x63, 0x32, 0x47, 0xec,
|
0x1c, 0x91, 0xed, 0x69, 0x79, 0xaa, 0xe8, 0x7e, 0x4c, 0x3b, 0xba, 0x0e, 0x79, 0x1f, 0x93, 0x39,
|
||||||
0x02, 0xb4, 0xc4, 0x18, 0xb3, 0x23, 0x5f, 0x3a, 0x19, 0x4b, 0x04, 0xec, 0x1f, 0xf9, 0xa8, 0xc0,
|
0x62, 0x17, 0xa0, 0x25, 0xc6, 0x98, 0x1d, 0xf9, 0xd2, 0xc9, 0x58, 0x22, 0x60, 0xff, 0xc8, 0x47,
|
||||||
0xd6, 0xb4, 0x35, 0xc8, 0x73, 0x6c, 0x13, 0xb6, 0x2b, 0x66, 0xed, 0x75, 0xe8, 0xaa, 0x60, 0x3a,
|
0x05, 0xb6, 0xa6, 0xad, 0x41, 0x9e, 0x63, 0x9b, 0xb0, 0x5d, 0x31, 0x6b, 0x6f, 0x42, 0x57, 0x05,
|
||||||
0x1b, 0x84, 0xfc, 0x30, 0x57, 0x01, 0x42, 0x34, 0x9d, 0x60, 0x73, 0xd9, 0x03, 0x7e, 0x98, 0xbb,
|
0xd3, 0xd9, 0x20, 0xe4, 0x87, 0xb9, 0x0a, 0x10, 0xa2, 0xe9, 0x04, 0x9b, 0xcb, 0x1e, 0xf0, 0xc3,
|
||||||
0x0f, 0x61, 0x4d, 0xea, 0xed, 0xd7, 0x12, 0xae, 0x9a, 0xfe, 0x52, 0x79, 0x53, 0x13, 0x5e, 0xc5,
|
0xdc, 0x7d, 0x08, 0x6b, 0x52, 0x6f, 0xbf, 0x96, 0x70, 0xd5, 0xf4, 0x17, 0xcb, 0x9b, 0x9a, 0xf0,
|
||||||
0xba, 0xad, 0xe8, 0x14, 0xe5, 0x94, 0x76, 0x3a, 0xd7, 0x03, 0x26, 0xab, 0x6f, 0x87, 0x71, 0xc6,
|
0x2a, 0xd6, 0x6d, 0x45, 0xa7, 0x28, 0xa7, 0xb4, 0xd3, 0xb9, 0x1e, 0x30, 0x59, 0x7d, 0x3b, 0x8c,
|
||||||
0x25, 0x43, 0x17, 0x3a, 0xc3, 0x30, 0xce, 0x54, 0x18, 0x22, 0x87, 0x63, 0x61, 0x38, 0x3f, 0xd9,
|
0x33, 0x2e, 0x19, 0xba, 0xd0, 0x19, 0x86, 0x71, 0xa6, 0xc2, 0x10, 0x39, 0x1c, 0x0b, 0xc3, 0xf9,
|
||||||
0x74, 0x38, 0x44, 0x4b, 0x20, 0x6c, 0x9a, 0x2a, 0xba, 0x7f, 0xe5, 0xc0, 0x3a, 0x71, 0x53, 0x16,
|
0xc9, 0xa6, 0xc3, 0x21, 0x5a, 0x02, 0x61, 0xd3, 0x54, 0xd1, 0xfd, 0x73, 0x07, 0xd6, 0x89, 0x9b,
|
||||||
0x46, 0xfb, 0xae, 0x2f, 0xdf, 0xcd, 0xce, 0xd0, 0x0c, 0xcd, 0x36, 0x60, 0xfe, 0x30, 0x4e, 0x87,
|
0xb2, 0x30, 0xda, 0x77, 0x7d, 0xf5, 0x6e, 0x76, 0x86, 0x66, 0x68, 0xb6, 0x01, 0xf3, 0x87, 0x71,
|
||||||
0x5c, 0xb6, 0x24, 0x0a, 0x3f, 0xb9, 0x37, 0xde, 0xac, 0x78, 0xe3, 0x3f, 0x72, 0x60, 0x8d, 0xba,
|
0x3a, 0xe4, 0xb2, 0x25, 0x51, 0xf8, 0xf1, 0xbd, 0xf1, 0x66, 0xc5, 0x1b, 0xff, 0xa1, 0x03, 0x6b,
|
||||||
0xba, 0x9f, 0xfb, 0xf9, 0x34, 0x93, 0xc3, 0xff, 0x15, 0x58, 0xc6, 0xa1, 0x72, 0xa5, 0x4e, 0xb2,
|
0xd4, 0xd5, 0xfd, 0xdc, 0xcf, 0xa7, 0x99, 0x1c, 0xfe, 0x2f, 0xc0, 0x32, 0x0e, 0x95, 0x2b, 0x75,
|
||||||
0xa3, 0x1b, 0x5a, 0xf3, 0x09, 0x15, 0xc4, 0xbb, 0x67, 0x3c, 0x9b, 0x98, 0x7d, 0x19, 0x3a, 0x66,
|
0x92, 0x1d, 0xdd, 0xd0, 0x9a, 0x4f, 0xa8, 0x20, 0xde, 0x3d, 0xe3, 0xd9, 0xc4, 0xec, 0xcb, 0xd0,
|
||||||
0x46, 0x84, 0xfa, 0xdc, 0xde, 0x39, 0xaf, 0x46, 0x59, 0x91, 0x9c, 0xdd, 0x33, 0x9e, 0xf5, 0x01,
|
0x31, 0x33, 0x22, 0xd4, 0xe7, 0xf6, 0xce, 0x79, 0x35, 0xca, 0x8a, 0xe4, 0xec, 0x9e, 0xf1, 0xac,
|
||||||
0xbb, 0x01, 0x40, 0xee, 0x06, 0xb1, 0x95, 0xa1, 0xec, 0x79, 0x7b, 0x92, 0x8c, 0xc5, 0xda, 0x3d,
|
0x0f, 0xd8, 0x0d, 0x00, 0x72, 0x37, 0x88, 0xad, 0x0c, 0x65, 0xcf, 0xdb, 0x93, 0x64, 0x2c, 0xd6,
|
||||||
0xe3, 0x19, 0xe4, 0xb7, 0x96, 0x60, 0x41, 0xec, 0x8f, 0xee, 0x7d, 0x58, 0xb6, 0x7a, 0x6a, 0x45,
|
0xee, 0x19, 0xcf, 0x20, 0xbf, 0xb5, 0x04, 0x0b, 0x62, 0x7f, 0x74, 0xef, 0xc3, 0xb2, 0xd5, 0x53,
|
||||||
0x19, 0x1d, 0x11, 0x65, 0x54, 0x82, 0xd2, 0x46, 0x35, 0x28, 0x75, 0xff, 0xb5, 0x01, 0x0c, 0xa5,
|
0x2b, 0xca, 0xe8, 0x88, 0x28, 0xa3, 0x12, 0x94, 0x36, 0xaa, 0x41, 0xa9, 0xfb, 0x2f, 0x0d, 0x60,
|
||||||
0xad, 0xb4, 0x9c, 0xb8, 0x41, 0xc7, 0x23, 0xcb, 0xdd, 0xea, 0x78, 0x26, 0xc4, 0xae, 0x01, 0x33,
|
0x28, 0x6d, 0xa5, 0xe5, 0xc4, 0x0d, 0x3a, 0x1e, 0x59, 0xee, 0x56, 0xc7, 0x33, 0x21, 0x76, 0x0d,
|
||||||
0x8a, 0x2a, 0xf7, 0x20, 0xf6, 0x8d, 0x9a, 0x1a, 0x34, 0x70, 0xc2, 0x57, 0x52, 0x31, 0xb0, 0x74,
|
0x98, 0x51, 0x54, 0xb9, 0x07, 0xb1, 0x6f, 0xd4, 0xd4, 0xa0, 0x81, 0x13, 0xbe, 0x92, 0x8a, 0x81,
|
||||||
0x2c, 0xc5, 0xba, 0xd5, 0xd6, 0xe1, 0xd6, 0x90, 0x4c, 0xb3, 0x23, 0x74, 0x20, 0x94, 0x43, 0xa6,
|
0xa5, 0x63, 0x29, 0xd6, 0xad, 0xb6, 0x0e, 0xb7, 0x86, 0x64, 0x9a, 0x1d, 0xa1, 0x03, 0xa1, 0x1c,
|
||||||
0xca, 0x65, 0x01, 0x59, 0x78, 0xa1, 0x80, 0x2c, 0x96, 0x05, 0xc4, 0x74, 0x09, 0x96, 0x2c, 0x97,
|
0x32, 0x55, 0x2e, 0x0b, 0xc8, 0xc2, 0x4b, 0x05, 0x64, 0xb1, 0x2c, 0x20, 0xa6, 0x4b, 0xb0, 0x64,
|
||||||
0x00, 0xfd, 0xaf, 0x49, 0x10, 0x91, 0x5f, 0x31, 0x98, 0x60, 0xeb, 0xd2, 0xff, 0xb2, 0x40, 0xb6,
|
0xb9, 0x04, 0xe8, 0x7f, 0x4d, 0x82, 0x88, 0xfc, 0x8a, 0xc1, 0x04, 0x5b, 0x97, 0xfe, 0x97, 0x05,
|
||||||
0x05, 0xab, 0xd2, 0xaf, 0x2b, 0xfc, 0x0e, 0xa0, 0x39, 0xae, 0xe0, 0xee, 0x67, 0x0e, 0xac, 0xe2,
|
0xb2, 0x2d, 0x58, 0x95, 0x7e, 0x5d, 0xe1, 0x77, 0x00, 0xcd, 0x71, 0x05, 0x77, 0x3f, 0x75, 0x60,
|
||||||
0x3c, 0x5b, 0xb2, 0xf8, 0x2e, 0x90, 0x2a, 0xbc, 0xa4, 0x28, 0x5a, 0xb4, 0x3f, 0xbb, 0x24, 0xbe,
|
0x15, 0xe7, 0xd9, 0x92, 0xc5, 0xf7, 0x80, 0x54, 0xe1, 0x15, 0x45, 0xd1, 0xa2, 0xfd, 0xe9, 0x25,
|
||||||
0x03, 0x2d, 0x62, 0x18, 0x27, 0x3c, 0x92, 0x82, 0xd8, 0xb3, 0x05, 0xb1, 0xb0, 0x42, 0xbb, 0x67,
|
0xf1, 0x5d, 0x68, 0x11, 0xc3, 0x38, 0xe1, 0x91, 0x14, 0xc4, 0x9e, 0x2d, 0x88, 0x85, 0x15, 0xda,
|
||||||
0xbc, 0x82, 0xd8, 0x10, 0xc3, 0x7f, 0x70, 0xa0, 0x2d, 0xbb, 0xf9, 0x53, 0xc7, 0x12, 0x7d, 0x58,
|
0x3d, 0xe3, 0x15, 0xc4, 0x86, 0x18, 0xfe, 0xbd, 0x03, 0x6d, 0xd9, 0xcd, 0x9f, 0x38, 0x96, 0xe8,
|
||||||
0x42, 0x89, 0x34, 0x1c, 0x76, 0x5d, 0xc6, 0xdd, 0x64, 0x82, 0x01, 0x1b, 0x6e, 0x9f, 0x56, 0x1c,
|
0xc3, 0x12, 0x4a, 0xa4, 0xe1, 0xb0, 0xeb, 0x32, 0xee, 0x26, 0x13, 0x0c, 0xd8, 0x70, 0xfb, 0xb4,
|
||||||
0x51, 0x86, 0x71, 0x2f, 0x24, 0x83, 0x9b, 0x0d, 0xf2, 0x20, 0x1c, 0xa8, 0x5a, 0x99, 0x80, 0xac,
|
0xe2, 0x88, 0x32, 0x8c, 0x7b, 0x21, 0x19, 0xdc, 0x6c, 0x90, 0x07, 0xe1, 0x40, 0xd5, 0xca, 0x04,
|
||||||
0xab, 0x42, 0xbb, 0x93, 0xe5, 0xfe, 0x98, 0xcb, 0x6d, 0x4e, 0x14, 0x30, 0x60, 0x92, 0x03, 0x2a,
|
0x64, 0x5d, 0x15, 0xda, 0x9d, 0x2c, 0xf7, 0xc7, 0x5c, 0x6e, 0x73, 0xa2, 0x80, 0x01, 0x93, 0x1c,
|
||||||
0xb9, 0x83, 0xee, 0x0f, 0x01, 0xce, 0x55, 0xaa, 0x74, 0xba, 0x5b, 0x3a, 0xc8, 0x61, 0x30, 0x39,
|
0x50, 0xc9, 0x1d, 0x74, 0xff, 0xa6, 0x03, 0xe7, 0x2a, 0x55, 0x3a, 0xdd, 0x2d, 0x1d, 0xe4, 0x30,
|
||||||
0x88, 0xb5, 0xaf, 0xed, 0x98, 0xbe, 0xb3, 0x55, 0xc5, 0xc6, 0x70, 0x56, 0xed, 0xe7, 0x38, 0xa7,
|
0x98, 0x1c, 0xc4, 0xda, 0xd7, 0x76, 0x4c, 0xdf, 0xd9, 0xaa, 0x62, 0x63, 0x38, 0xab, 0xf6, 0x73,
|
||||||
0xc5, 0xee, 0xdd, 0x20, 0x47, 0xe4, 0x2d, 0x5b, 0x06, 0xca, 0x0d, 0x2a, 0xdc, 0xd4, 0xdc, 0x7a,
|
0x9c, 0xd3, 0x62, 0xf7, 0x6e, 0x90, 0x23, 0xf2, 0xb6, 0x2d, 0x03, 0xe5, 0x06, 0x15, 0x6e, 0x6a,
|
||||||
0x7e, 0xec, 0x08, 0x7a, 0xda, 0x71, 0x90, 0x26, 0xde, 0x70, 0x2e, 0xb0, 0xad, 0x37, 0x5f, 0xd0,
|
0x6e, 0x3d, 0x3f, 0x76, 0x04, 0x3d, 0xed, 0x38, 0x48, 0x13, 0x6f, 0x38, 0x17, 0xd8, 0xd6, 0x5b,
|
||||||
0x16, 0xd9, 0xa3, 0x91, 0x6a, 0xe6, 0x54, 0x6e, 0x6c, 0x06, 0x97, 0x54, 0x1d, 0xd9, 0xf0, 0x6a,
|
0x2f, 0x69, 0x8b, 0xec, 0xd1, 0x48, 0x35, 0x73, 0x2a, 0x37, 0x36, 0x83, 0x4b, 0xaa, 0x8e, 0x6c,
|
||||||
0x7b, 0xcd, 0x97, 0x1a, 0xdb, 0x3d, 0xfc, 0xd8, 0x6e, 0xf4, 0x05, 0x8c, 0xfb, 0x3f, 0x74, 0xa0,
|
0x78, 0xb5, 0xbd, 0xe6, 0x2b, 0x8d, 0xed, 0x1e, 0x7e, 0x6c, 0x37, 0xfa, 0x12, 0xc6, 0xec, 0x43,
|
||||||
0x6b, 0xb3, 0x43, 0xd1, 0x91, 0x4a, 0xa8, 0x8c, 0x91, 0x72, 0xc8, 0x4a, 0x70, 0x35, 0x6c, 0x6c,
|
0xd8, 0x3c, 0xf1, 0x83, 0x5c, 0x75, 0xcb, 0x70, 0x86, 0xe6, 0xa9, 0xc9, 0x9d, 0x97, 0x34, 0xf9,
|
||||||
0xd4, 0x85, 0x8d, 0x66, 0x70, 0x38, 0xf7, 0xa2, 0xe0, 0xb0, 0xf9, 0x72, 0xc1, 0xe1, 0x7c, 0x5d,
|
0x44, 0x7c, 0x6c, 0x6d, 0x6c, 0xa7, 0x70, 0xec, 0xff, 0xad, 0x03, 0x5d, 0x9b, 0x0f, 0x8a, 0xa9,
|
||||||
0x70, 0xd8, 0xff, 0x2f, 0x07, 0x58, 0x75, 0x7d, 0xd9, 0x7d, 0x11, 0xb7, 0x46, 0x3c, 0x94, 0x76,
|
0x54, 0x78, 0x65, 0xf8, 0x94, 0xf3, 0x57, 0x82, 0xab, 0x21, 0x6a, 0xa3, 0x2e, 0x44, 0x35, 0x03,
|
||||||
0xe2, 0x97, 0x5e, 0x4e, 0x46, 0xd4, 0x1c, 0xaa, 0xaf, 0x51, 0x58, 0x4d, 0x43, 0x60, 0xba, 0x2d,
|
0xd1, 0xb9, 0x97, 0x05, 0xa2, 0xcd, 0x57, 0x0b, 0x44, 0xe7, 0xeb, 0x02, 0xd1, 0xfe, 0x7f, 0x3a,
|
||||||
0xcb, 0x5e, 0x5d, 0x55, 0x29, 0x5c, 0x6d, 0xbe, 0x38, 0x5c, 0x9d, 0x7f, 0x71, 0xb8, 0xba, 0x50,
|
0xc0, 0xaa, 0xb2, 0xc4, 0xee, 0x8b, 0x18, 0x39, 0xe2, 0xa1, 0xb4, 0x49, 0x3f, 0xf7, 0x6a, 0xf2,
|
||||||
0x0e, 0x57, 0xfb, 0xbf, 0x03, 0xcb, 0xd6, 0xaa, 0xff, 0xdf, 0x8d, 0xb8, 0xec, 0xf2, 0x88, 0x05,
|
0xa8, 0xe6, 0x4e, 0x7d, 0x8d, 0x8a, 0x61, 0x1a, 0x1d, 0xd3, 0x45, 0x5a, 0xf6, 0xea, 0xaa, 0x4a,
|
||||||
0xb6, 0xb0, 0xfe, 0x8f, 0x1b, 0xc0, 0xaa, 0x92, 0xf7, 0x73, 0xed, 0x03, 0xc9, 0x91, 0x65, 0x40,
|
0xa1, 0x71, 0xf3, 0xe5, 0xa1, 0xf1, 0xfc, 0xcb, 0x43, 0xe3, 0x85, 0x72, 0x68, 0xdc, 0xff, 0x5d,
|
||||||
0xe6, 0xa4, 0x1c, 0x59, 0xa6, 0xe3, 0xff, 0xd3, 0x28, 0xbe, 0x09, 0x6b, 0x29, 0x1f, 0xc6, 0xc7,
|
0x07, 0xd6, 0x6b, 0x16, 0xfd, 0x67, 0x37, 0x70, 0x5c, 0x26, 0xcb, 0x16, 0x34, 0xe4, 0x32, 0x99,
|
||||||
0x74, 0x08, 0x67, 0xa7, 0x3a, 0xaa, 0x15, 0xe8, 0xf4, 0xd9, 0x41, 0xfa, 0x92, 0x75, 0x66, 0x62,
|
0x60, 0xff, 0x37, 0x60, 0xd9, 0x12, 0xf4, 0x9f, 0x5d, 0xfb, 0x65, 0x2f, 0x4f, 0xc8, 0x99, 0x85,
|
||||||
0xec, 0x0c, 0xa5, 0x58, 0xdd, 0xdd, 0x84, 0x0d, 0x71, 0x94, 0x75, 0x4b, 0xb0, 0x52, 0x46, 0xf6,
|
0xf5, 0x7f, 0xd4, 0x00, 0x56, 0x55, 0xb6, 0xff, 0xd3, 0x3e, 0x54, 0xe7, 0x69, 0xae, 0x66, 0x9e,
|
||||||
0x2f, 0x1c, 0x38, 0x5b, 0xaa, 0x28, 0x0e, 0x16, 0x84, 0x1d, 0xb5, 0x8d, 0xab, 0x0d, 0x62, 0xff,
|
0xfe, 0x57, 0xf7, 0x81, 0xb7, 0x60, 0x2d, 0xe5, 0xc3, 0xf8, 0x98, 0xce, 0x1d, 0xed, 0xec, 0x4e,
|
||||||
0xa5, 0x00, 0x1b, 0xfd, 0x17, 0xfb, 0x4d, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac,
|
0xb5, 0x02, 0xfd, 0x5c, 0x3b, 0x2f, 0xb1, 0x64, 0x1d, 0x13, 0x19, 0x9b, 0x61, 0x29, 0x3d, 0xe1,
|
||||||
0xd7, 0x55, 0xb9, 0xe7, 0xe0, 0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x64,
|
0x6e, 0xc2, 0x86, 0x38, 0xbd, 0xbb, 0x25, 0x58, 0xa9, 0x7d, 0xe5, 0x4f, 0x1d, 0x38, 0x5b, 0xaa,
|
||||||
0x4a, 0xed, 0x2e, 0xab, 0x22, 0xba, 0x44, 0x96, 0xcd, 0xb6, 0xfb, 0x5b, 0x5b, 0xe7, 0xfe, 0x36,
|
0x28, 0xce, 0x52, 0xc4, 0xd6, 0x61, 0xef, 0x27, 0x36, 0x88, 0xfd, 0x97, 0x7a, 0x64, 0xf4, 0x5f,
|
||||||
0xb0, 0xaf, 0x4f, 0x79, 0x3a, 0xa3, 0x63, 0x0f, 0x9d, 0xaa, 0x38, 0x57, 0x8e, 0xe9, 0x17, 0x92,
|
0x48, 0x5b, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac, 0xd7, 0x55, 0xb9, 0xe7, 0xe0,
|
||||||
0xe9, 0xc1, 0x57, 0xf9, 0x4c, 0x9d, 0x2b, 0x35, 0x8a, 0x73, 0xa5, 0x57, 0x00, 0x30, 0x14, 0xa1,
|
0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x24, 0x87, 0xed, 0x2e, 0xab, 0x22,
|
||||||
0x73, 0x12, 0x75, 0xd2, 0x87, 0x31, 0xa0, 0x60, 0xe8, 0xde, 0x80, 0x75, 0x8b, 0xbf, 0x9e, 0xfd,
|
0x7a, 0x81, 0xd6, 0x36, 0x65, 0xf7, 0xb7, 0xb6, 0xce, 0xfd, 0x75, 0x60, 0x5f, 0x9f, 0xf2, 0x74,
|
||||||
0x05, 0xf9, 0x85, 0x08, 0x94, 0xed, 0xd3, 0x17, 0x59, 0xe7, 0xfe, 0x87, 0x03, 0x73, 0xbb, 0x71,
|
0x46, 0x27, 0x3d, 0x3a, 0x3b, 0x73, 0xae, 0x9c, 0xc6, 0x58, 0x48, 0xa6, 0x07, 0x5f, 0xe5, 0x33,
|
||||||
0x62, 0xa6, 0xd8, 0x1c, 0x3b, 0xc5, 0x26, 0x6d, 0xed, 0x40, 0x9b, 0xd2, 0x86, 0xb4, 0x14, 0x26,
|
0x75, 0x94, 0xd6, 0x28, 0x8e, 0xd2, 0x5e, 0x03, 0xc0, 0xe8, 0x8b, 0x8e, 0x86, 0xd4, 0xe1, 0x26,
|
||||||
0xc8, 0xb6, 0xa0, 0xeb, 0x4f, 0x72, 0x0c, 0x15, 0x0f, 0xe3, 0xf4, 0xc4, 0x4f, 0x47, 0x62, 0x49,
|
0x86, 0xbd, 0x82, 0xa1, 0x7b, 0x03, 0xd6, 0x2d, 0xfe, 0x7a, 0xf6, 0x17, 0xe4, 0x17, 0x22, 0x37,
|
||||||
0x6e, 0x35, 0x7a, 0x8e, 0x57, 0xaa, 0x61, 0x1b, 0x30, 0xa7, 0x8d, 0x12, 0x11, 0x60, 0x11, 0x9d,
|
0x60, 0x1f, 0x38, 0xc9, 0x3a, 0xf7, 0xdf, 0x1d, 0x98, 0xdb, 0x8d, 0x13, 0x33, 0xab, 0xe8, 0xd8,
|
||||||
0x0d, 0xca, 0x34, 0xce, 0x64, 0x94, 0x2b, 0x4b, 0xb8, 0xe2, 0xf6, 0xf7, 0xc2, 0xbd, 0x13, 0x12,
|
0x59, 0x45, 0x69, 0xf2, 0x07, 0xda, 0xa2, 0x4b, 0x4b, 0x60, 0x81, 0x6c, 0x0b, 0xba, 0xfe, 0x24,
|
||||||
0x5e, 0x57, 0x85, 0x76, 0x1f, 0x6d, 0x14, 0x91, 0xc9, 0xf4, 0x84, 0x2a, 0xbb, 0xff, 0xe6, 0xc0,
|
0xc7, 0xe8, 0xf8, 0x30, 0x4e, 0x4f, 0xfc, 0x74, 0x24, 0x96, 0xe4, 0x56, 0xa3, 0xe7, 0x78, 0xa5,
|
||||||
0x3c, 0xcd, 0x00, 0xea, 0xa4, 0x10, 0x44, 0x3a, 0xc8, 0xa4, 0xb4, 0xa8, 0x23, 0x74, 0xb2, 0x04,
|
0x1a, 0xb6, 0x01, 0x73, 0xda, 0x36, 0x12, 0x01, 0x16, 0xd1, 0xbf, 0xa2, 0xe4, 0xea, 0x4c, 0x06,
|
||||||
0x33, 0xd7, 0x3a, 0xde, 0x6c, 0xe8, 0x6e, 0x9b, 0x47, 0x9c, 0x97, 0xa1, 0x25, 0x4a, 0xfa, 0x4c,
|
0xf6, 0xb2, 0x84, 0x2b, 0x6e, 0x7f, 0x2f, 0x3c, 0x5a, 0x21, 0xe1, 0x75, 0x55, 0xb8, 0xfd, 0xa0,
|
||||||
0x90, 0x48, 0x0a, 0x90, 0x5d, 0x82, 0xe6, 0x51, 0x9c, 0xa8, 0x1d, 0x15, 0x54, 0x56, 0x2c, 0x4e,
|
0xa9, 0x24, 0x32, 0x99, 0x91, 0x51, 0x65, 0xf7, 0x5f, 0x1d, 0x98, 0xa7, 0x19, 0x40, 0x9d, 0x14,
|
||||||
0x3c, 0xc2, 0x8b, 0xfe, 0x20, 0x3f, 0xd1, 0x79, 0x61, 0x93, 0xcb, 0x30, 0xee, 0x4a, 0x9a, 0xad,
|
0x82, 0x48, 0x67, 0xb7, 0x94, 0x09, 0x76, 0x84, 0x4e, 0x96, 0x60, 0xe6, 0x5a, 0x27, 0xba, 0x0d,
|
||||||
0x39, 0x19, 0x25, 0xd4, 0xdd, 0x82, 0x95, 0x87, 0xf1, 0x88, 0x1b, 0x79, 0x90, 0x53, 0xa5, 0xce,
|
0xdd, 0x6d, 0xf3, 0x54, 0xf7, 0x32, 0xb4, 0x44, 0x49, 0x1f, 0x83, 0x12, 0x49, 0x01, 0xb2, 0x4b,
|
||||||
0xfd, 0x5d, 0x07, 0x96, 0x14, 0x31, 0xbb, 0x0a, 0x4d, 0xdc, 0x6a, 0x4b, 0xce, 0xad, 0xce, 0x86,
|
0xd0, 0x3c, 0x8a, 0x13, 0xe5, 0x44, 0x80, 0x4a, 0x04, 0xc6, 0x89, 0x47, 0x78, 0xd1, 0x1f, 0xe4,
|
||||||
0x23, 0x9d, 0x47, 0x14, 0x68, 0x22, 0x29, 0x4a, 0x2e, 0x5c, 0x21, 0x15, 0x23, 0x17, 0x4e, 0x86,
|
0x27, 0x3a, 0x2f, 0xb6, 0x86, 0x32, 0x8c, 0x9b, 0xa3, 0x66, 0x6b, 0x4e, 0x46, 0x09, 0x75, 0xb7,
|
||||||
0xee, 0x6e, 0x69, 0x33, 0x2e, 0xa1, 0xee, 0x5f, 0x3b, 0xb0, 0x6c, 0xb5, 0x81, 0x21, 0x4d, 0xe8,
|
0x60, 0xe5, 0x61, 0x3c, 0xe2, 0x46, 0xea, 0xe7, 0x54, 0xa9, 0x73, 0x7f, 0xd3, 0x81, 0x25, 0x45,
|
||||||
0x67, 0xb9, 0xcc, 0x30, 0xca, 0xe5, 0x31, 0x21, 0x33, 0x33, 0xd6, 0xb0, 0x33, 0x63, 0x3a, 0x67,
|
0xcc, 0xae, 0x42, 0x13, 0x77, 0xfc, 0x92, 0x3f, 0xaf, 0x0f, 0x00, 0x90, 0xce, 0x23, 0x0a, 0x34,
|
||||||
0x33, 0x67, 0xe6, 0x6c, 0xae, 0x43, 0xab, 0x38, 0x84, 0x6e, 0x5a, 0xa6, 0x0f, 0x5b, 0x54, 0x79,
|
0x91, 0x94, 0x18, 0x28, 0xbc, 0x3f, 0x95, 0x16, 0x28, 0x9c, 0x1b, 0xdd, 0xdd, 0x92, 0x4f, 0x50,
|
||||||
0xfe, 0x82, 0x08, 0xf9, 0x0c, 0xe3, 0x30, 0x4e, 0xe5, 0x19, 0xad, 0x28, 0xb8, 0x37, 0xa0, 0x6d,
|
0x42, 0xdd, 0xbf, 0x70, 0x60, 0xd9, 0x6a, 0x03, 0xa3, 0xb8, 0xd0, 0xcf, 0x72, 0x99, 0x54, 0x95,
|
||||||
0xd0, 0x63, 0x37, 0x22, 0x9e, 0x9f, 0xc4, 0xe9, 0x13, 0x95, 0xa0, 0x93, 0x45, 0x7d, 0x9c, 0xd5,
|
0xcb, 0x63, 0x42, 0x66, 0x32, 0xb0, 0x61, 0x27, 0x03, 0x75, 0x9a, 0x6a, 0xce, 0x4c, 0x53, 0x5d,
|
||||||
0x28, 0x8e, 0xb3, 0xdc, 0xbf, 0x71, 0x60, 0x19, 0x65, 0x30, 0x88, 0xc6, 0x7b, 0x71, 0x18, 0x0c,
|
0x87, 0x56, 0x71, 0xee, 0xde, 0xb4, 0x4c, 0x1f, 0xb6, 0xa8, 0x8e, 0x36, 0x0a, 0x22, 0xe4, 0x33,
|
||||||
0x67, 0xb4, 0xf6, 0x4a, 0xdc, 0xe4, 0xe1, 0xad, 0x92, 0x45, 0x1b, 0x46, 0xd9, 0x56, 0x11, 0x8d,
|
0x8c, 0xc3, 0x38, 0x95, 0xc7, 0xd2, 0xa2, 0xe0, 0xde, 0x80, 0xb6, 0x41, 0x8f, 0xdd, 0x88, 0x78,
|
||||||
0x54, 0x44, 0x5d, 0x46, 0x4d, 0x45, 0x39, 0x3f, 0xf0, 0x33, 0x29, 0xfc, 0x72, 0x2f, 0xb2, 0x40,
|
0x7e, 0x12, 0xa7, 0x4f, 0x55, 0x4e, 0x52, 0x16, 0xf5, 0x09, 0x5e, 0xa3, 0x38, 0xc1, 0x73, 0xff,
|
||||||
0xd4, 0x27, 0x04, 0x52, 0x3f, 0xe7, 0x83, 0x49, 0x10, 0x86, 0x81, 0xa0, 0x15, 0x2e, 0x42, 0x5d,
|
0xd2, 0x81, 0x65, 0x94, 0xc1, 0x20, 0x1a, 0xef, 0xc5, 0x61, 0x30, 0x9c, 0xd1, 0xda, 0x2b, 0x71,
|
||||||
0x95, 0xfb, 0x83, 0x06, 0xb4, 0xa5, 0xa5, 0xbc, 0x3b, 0x1a, 0x8b, 0x54, 0xb8, 0x74, 0xb4, 0xb4,
|
0x93, 0xe7, 0xd5, 0x4a, 0x16, 0x6d, 0x18, 0x65, 0x5b, 0x05, 0x71, 0x52, 0x11, 0x75, 0x19, 0x35,
|
||||||
0xb9, 0x30, 0x10, 0x55, 0x6f, 0xb9, 0x66, 0x06, 0x52, 0x5e, 0xd6, 0xb9, 0xea, 0xb2, 0x5e, 0x84,
|
0x15, 0xe5, 0xfc, 0xc0, 0xcf, 0xa4, 0xf0, 0xcb, 0xbd, 0xc8, 0x02, 0x51, 0x9f, 0x10, 0x48, 0xfd,
|
||||||
0x16, 0x8a, 0xd7, 0x5b, 0xe4, 0x03, 0x8a, 0x3b, 0x0b, 0x05, 0xa0, 0x6a, 0x77, 0xa8, 0x76, 0xbe,
|
0x9c, 0x0f, 0x26, 0x41, 0x18, 0x06, 0x82, 0x56, 0x78, 0x2a, 0x75, 0x55, 0xee, 0x0f, 0x1a, 0xd0,
|
||||||
0xa8, 0x25, 0xc0, 0xf2, 0xfa, 0x16, 0x4a, 0x5e, 0xdf, 0x3b, 0xd0, 0x91, 0x6c, 0x68, 0xde, 0xc9,
|
0x96, 0x96, 0xf2, 0xee, 0x68, 0x2c, 0xb2, 0xff, 0xd2, 0xdf, 0xd3, 0xe6, 0xc2, 0x40, 0x54, 0xbd,
|
||||||
0x3a, 0x14, 0x02, 0x6e, 0xad, 0x89, 0x67, 0x51, 0xaa, 0x2f, 0x77, 0xd4, 0x97, 0x4b, 0x2f, 0xfa,
|
0xe5, 0x21, 0x1a, 0x48, 0x79, 0x59, 0xe7, 0xaa, 0xcb, 0x7a, 0x11, 0x5a, 0x28, 0x5e, 0x6f, 0x93,
|
||||||
0x52, 0x51, 0xd2, 0xc9, 0x90, 0x98, 0x9b, 0xfb, 0xa9, 0x9f, 0x1c, 0xa9, 0xdd, 0x67, 0xa4, 0x8f,
|
0x2b, 0x2a, 0xae, 0x69, 0x14, 0x80, 0xaa, 0xdd, 0xa1, 0xda, 0xf9, 0xa2, 0x96, 0x00, 0xcb, 0xf9,
|
||||||
0xbb, 0x09, 0x66, 0x5b, 0x30, 0x8f, 0x9f, 0x29, 0x6b, 0x5d, 0xaf, 0x74, 0x82, 0x84, 0x5d, 0x85,
|
0x5c, 0x28, 0x39, 0x9f, 0xef, 0x42, 0x47, 0xb2, 0xa1, 0x79, 0x27, 0xeb, 0x50, 0x08, 0xb8, 0xb5,
|
||||||
0x79, 0x3e, 0x1a, 0x73, 0x15, 0x79, 0x30, 0x3b, 0x06, 0xc4, 0x35, 0xf2, 0x04, 0x01, 0x9a, 0x00,
|
0x26, 0x9e, 0x45, 0xa9, 0xbe, 0xdc, 0x51, 0x5f, 0x2e, 0xbd, 0xec, 0x4b, 0x45, 0x49, 0x87, 0x61,
|
||||||
0x44, 0x4b, 0x26, 0xc0, 0xb6, 0xf4, 0x0b, 0x58, 0x7c, 0x7f, 0xe4, 0x6e, 0x00, 0x7b, 0x28, 0xa4,
|
0x62, 0x6e, 0xee, 0xa7, 0x7e, 0x72, 0xa4, 0x76, 0x9f, 0x91, 0x3e, 0xe1, 0x27, 0x98, 0x6d, 0xc1,
|
||||||
0xd6, 0xcc, 0x9c, 0xfe, 0xc1, 0x1c, 0xb4, 0x0d, 0x18, 0xb5, 0x79, 0x8c, 0x1d, 0x1e, 0x8c, 0x02,
|
0x3c, 0x7e, 0xa6, 0xac, 0x75, 0xbd, 0xd2, 0x09, 0x12, 0x76, 0x15, 0xe6, 0xf9, 0x68, 0xcc, 0x55,
|
||||||
0x7f, 0xc2, 0x73, 0x9e, 0x4a, 0x49, 0x2d, 0xa1, 0x48, 0xe7, 0x1f, 0x8f, 0x07, 0xf1, 0x34, 0x1f,
|
0xb0, 0xc5, 0xec, 0xb0, 0x17, 0xd7, 0xc8, 0x13, 0x04, 0x68, 0x02, 0x10, 0x2d, 0x99, 0x00, 0xdb,
|
||||||
0x8c, 0xf8, 0x38, 0xe5, 0x62, 0x8f, 0xc4, 0xcd, 0xc0, 0x42, 0x91, 0x6e, 0xe2, 0x3f, 0x35, 0xe9,
|
0xd2, 0x2f, 0x60, 0xf1, 0xfd, 0x91, 0xbb, 0x01, 0xec, 0xa1, 0x90, 0x5a, 0x33, 0x59, 0xfc, 0x3b,
|
||||||
0x84, 0x3c, 0x94, 0x50, 0x95, 0x07, 0x15, 0x73, 0xd4, 0x2c, 0xf2, 0xa0, 0x62, 0x46, 0xca, 0x76,
|
0x73, 0xd0, 0x36, 0x60, 0xd4, 0xe6, 0x31, 0x76, 0x78, 0x30, 0x0a, 0xfc, 0x09, 0xcf, 0x79, 0x2a,
|
||||||
0x68, 0xbe, 0xc6, 0x0e, 0xbd, 0x0d, 0x9b, 0xc2, 0xe2, 0x48, 0xdd, 0x1c, 0x94, 0xc4, 0xe4, 0x94,
|
0x25, 0xb5, 0x84, 0x22, 0x9d, 0x7f, 0x3c, 0x1e, 0xc4, 0xd3, 0x7c, 0x30, 0xe2, 0xe3, 0x94, 0x8b,
|
||||||
0x5a, 0xb6, 0x05, 0xab, 0xd8, 0x67, 0x25, 0xe0, 0x59, 0xf0, 0x89, 0xc8, 0x4c, 0x38, 0x5e, 0x05,
|
0x3d, 0x12, 0x37, 0x03, 0x0b, 0x45, 0xba, 0x89, 0xff, 0xcc, 0xa4, 0x13, 0xf2, 0x50, 0x42, 0x55,
|
||||||
0x47, 0x5a, 0x54, 0x47, 0x8b, 0x56, 0x9c, 0x15, 0x55, 0x70, 0xa2, 0xf5, 0x9f, 0xda, 0xb4, 0x2d,
|
0xea, 0x57, 0xcc, 0x51, 0xb3, 0x48, 0xfd, 0x8a, 0x19, 0x29, 0xdb, 0xa1, 0xf9, 0x1a, 0x3b, 0xf4,
|
||||||
0x49, 0x5b, 0xc2, 0xdd, 0x65, 0x68, 0xef, 0xe7, 0x71, 0xa2, 0x16, 0xa5, 0x0b, 0x1d, 0x51, 0x94,
|
0x0e, 0x6c, 0x0a, 0x8b, 0x23, 0x75, 0x73, 0x50, 0x12, 0x93, 0x53, 0x6a, 0xd9, 0x16, 0xac, 0x62,
|
||||||
0x27, 0x83, 0x17, 0xe0, 0x3c, 0x49, 0xd1, 0xa3, 0x38, 0x89, 0xc3, 0x78, 0x3c, 0xdb, 0x9f, 0x1e,
|
0x9f, 0x95, 0x80, 0x67, 0xc1, 0xc7, 0x22, 0x19, 0xe3, 0x78, 0x15, 0x1c, 0x69, 0x51, 0x1d, 0x2d,
|
||||||
0x64, 0xc3, 0x34, 0x48, 0x30, 0x22, 0x70, 0xff, 0xde, 0x81, 0x75, 0xab, 0x56, 0xa6, 0x32, 0xbe,
|
0x5a, 0x71, 0x3c, 0x56, 0xc1, 0x89, 0xd6, 0x7f, 0x66, 0xd3, 0xb6, 0x24, 0x6d, 0x09, 0x77, 0x97,
|
||||||
0x20, 0x44, 0x5a, 0x1f, 0xe9, 0x08, 0xc1, 0x5b, 0x33, 0xcc, 0xa1, 0x20, 0x14, 0x49, 0xa4, 0xc7,
|
0xa1, 0xbd, 0x9f, 0xc7, 0x89, 0x5a, 0x94, 0x2e, 0x74, 0x44, 0x51, 0x1e, 0x86, 0x5e, 0x80, 0xf3,
|
||||||
0xf2, 0x94, 0xe7, 0x26, 0xac, 0xa8, 0x9e, 0xa9, 0x0f, 0x85, 0x14, 0xf6, 0xaa, 0x52, 0x28, 0xbf,
|
0x24, 0x45, 0x8f, 0xe2, 0x24, 0x0e, 0xe3, 0xf1, 0x6c, 0x7f, 0x7a, 0x90, 0x0d, 0xd3, 0x20, 0xc1,
|
||||||
0xef, 0xca, 0x0f, 0x14, 0x8b, 0x5f, 0x15, 0x7e, 0x35, 0x1f, 0xd1, 0x18, 0x55, 0x4c, 0xdb, 0x57,
|
0xc0, 0xc4, 0xfd, 0x3b, 0x07, 0xd6, 0xad, 0x5a, 0x99, 0xbd, 0xf9, 0xbc, 0x10, 0x69, 0x7d, 0x8a,
|
||||||
0xdf, 0x9b, 0xce, 0xbc, 0xea, 0xc1, 0x50, 0x83, 0x99, 0xfb, 0x47, 0x0e, 0x40, 0xd1, 0x3b, 0x14,
|
0x25, 0x04, 0x6f, 0xcd, 0x30, 0x87, 0x82, 0x50, 0xe4, 0xcd, 0x1e, 0xcb, 0x83, 0xad, 0x9b, 0xb0,
|
||||||
0x8c, 0xc2, 0xa4, 0x8b, 0xcb, 0x6a, 0x86, 0xf9, 0x7e, 0x0d, 0x3a, 0x3a, 0x9b, 0x5f, 0xec, 0x12,
|
0xa2, 0x7a, 0xa6, 0x3e, 0x14, 0x52, 0xd8, 0xab, 0x4a, 0xa1, 0xfc, 0xbe, 0x2b, 0x3f, 0x50, 0x2c,
|
||||||
0x6d, 0x85, 0xa1, 0xc3, 0x75, 0x05, 0x56, 0xc6, 0x61, 0x7c, 0x40, 0x5b, 0x2c, 0x1d, 0x35, 0x67,
|
0x7e, 0x51, 0xf8, 0xd5, 0x7c, 0x44, 0x63, 0x54, 0x61, 0x7c, 0x5f, 0x7d, 0x6f, 0x3a, 0xf3, 0xaa,
|
||||||
0xf2, 0x7c, 0xb4, 0x2b, 0xe0, 0x7b, 0x12, 0x2d, 0xb6, 0x94, 0xa6, 0xb1, 0xa5, 0xb8, 0xdf, 0x69,
|
0x07, 0x43, 0x0d, 0x66, 0xee, 0xef, 0x3b, 0x00, 0x45, 0xef, 0x50, 0x30, 0x0a, 0x93, 0x2e, 0xee,
|
||||||
0xe8, 0x1c, 0x70, 0x31, 0xe6, 0x53, 0xb5, 0x8c, 0xed, 0x54, 0x8c, 0xe3, 0x29, 0x29, 0x57, 0xca,
|
0xe7, 0x19, 0xe6, 0xfb, 0x0d, 0xe8, 0xe8, 0x03, 0x8c, 0x62, 0x97, 0x68, 0x2b, 0x0c, 0x1d, 0xae,
|
||||||
0xde, 0xec, 0xbd, 0x30, 0x90, 0xbd, 0x01, 0xdd, 0x54, 0x58, 0x1f, 0x65, 0x9a, 0x9a, 0xcf, 0x31,
|
0x2b, 0xb0, 0x32, 0x0e, 0xe3, 0x03, 0xda, 0x62, 0xe9, 0x74, 0x3d, 0x93, 0x47, 0xc2, 0x5d, 0x01,
|
||||||
0x4d, 0xcb, 0xa9, 0xb5, 0xef, 0xfc, 0x02, 0xac, 0xfa, 0xa3, 0x63, 0x9e, 0xe6, 0x01, 0x45, 0x34,
|
0xdf, 0x93, 0x68, 0xb1, 0xa5, 0x34, 0x8d, 0x2d, 0xc5, 0xfd, 0x4e, 0x43, 0xa7, 0xbd, 0x8b, 0x31,
|
||||||
0xb4, 0xe9, 0x0b, 0x83, 0xba, 0x62, 0xe0, 0xb4, 0x17, 0x5f, 0x81, 0x15, 0x79, 0x26, 0xad, 0x29,
|
0x9f, 0xaa, 0x65, 0x6c, 0xa7, 0x62, 0x1c, 0x4f, 0xc9, 0x32, 0x53, 0xc2, 0x6a, 0xef, 0xa5, 0xf1,
|
||||||
0xe5, 0x4d, 0xa4, 0x02, 0x46, 0x42, 0xf7, 0xfb, 0x2a, 0xdd, 0x6c, 0xaf, 0xe1, 0xe9, 0x33, 0x62,
|
0xf4, 0x0d, 0xe8, 0xa6, 0xc2, 0xfa, 0x28, 0xd3, 0xd4, 0x7c, 0x81, 0x69, 0x5a, 0x4e, 0xad, 0x7d,
|
||||||
0x8e, 0xae, 0x51, 0x1a, 0xdd, 0xe7, 0x64, 0xea, 0x77, 0xa4, 0xc2, 0x26, 0x99, 0x84, 0x17, 0xa0,
|
0xe7, 0xff, 0xc1, 0xaa, 0x3f, 0x3a, 0xe6, 0x69, 0x1e, 0x50, 0x44, 0x43, 0x9b, 0xbe, 0x30, 0xa8,
|
||||||
0x4c, 0xd5, 0xdb, 0x53, 0xda, 0x7c, 0x99, 0x29, 0x75, 0x3f, 0x73, 0x60, 0x71, 0x37, 0x4e, 0x76,
|
0x2b, 0x06, 0x4e, 0x7b, 0xf1, 0x15, 0x58, 0x91, 0xc7, 0xf0, 0x9a, 0x52, 0x5e, 0xbe, 0x2a, 0x60,
|
||||||
0xe5, 0xf1, 0x32, 0x29, 0x82, 0xbe, 0xf1, 0xa1, 0x8a, 0xa6, 0x57, 0xdc, 0xa8, 0x78, 0xc5, 0xd5,
|
0x24, 0x74, 0xbf, 0xaf, 0x32, 0xec, 0xf6, 0x1a, 0x9e, 0x3e, 0x23, 0xe6, 0xe8, 0x1a, 0xa5, 0xd1,
|
||||||
0xbd, 0x76, 0xb9, 0xbc, 0xd7, 0xfe, 0x1a, 0x5c, 0xa0, 0x68, 0x39, 0x8d, 0x93, 0x38, 0x45, 0x65,
|
0x7d, 0x46, 0x66, 0xbb, 0x47, 0x2a, 0x6c, 0x92, 0xe7, 0x0e, 0x02, 0x94, 0xa7, 0x13, 0xf6, 0x94,
|
||||||
0xf4, 0x43, 0xb1, 0xb1, 0xc6, 0x51, 0x7e, 0xa4, 0xcc, 0xd8, 0xf3, 0x48, 0x28, 0x3a, 0x0a, 0xf3,
|
0x36, 0x5f, 0x65, 0x4a, 0xdd, 0x4f, 0x1d, 0x58, 0xdc, 0x8d, 0x93, 0x5d, 0x79, 0xa2, 0x4e, 0x8a,
|
||||||
0xe3, 0x81, 0x70, 0x86, 0xa5, 0x6f, 0x20, 0xac, 0x5b, 0xb5, 0xc2, 0xfd, 0x12, 0xb4, 0xc8, 0xb9,
|
0xa0, 0x2f, 0xb9, 0xa8, 0xa2, 0xe9, 0x15, 0x37, 0x2a, 0x5e, 0x71, 0x75, 0xaf, 0x5d, 0x2e, 0xef,
|
||||||
0xa5, 0x61, 0xbd, 0x09, 0xad, 0xa3, 0x38, 0x19, 0x1c, 0x05, 0x51, 0xae, 0x94, 0xbb, 0x5b, 0x78,
|
0xb5, 0xbf, 0x04, 0x17, 0x28, 0x68, 0x4f, 0xe3, 0x24, 0x4e, 0x51, 0x19, 0xfd, 0x50, 0x6c, 0xac,
|
||||||
0x9d, 0xbb, 0x34, 0x21, 0x9a, 0xc0, 0xfd, 0xf1, 0x1c, 0x2c, 0xbe, 0x1f, 0x1d, 0xc7, 0xc1, 0x90,
|
0x71, 0x94, 0x1f, 0x29, 0x33, 0xf6, 0x22, 0x12, 0x8a, 0x8e, 0xc2, 0xfc, 0x78, 0x20, 0x9c, 0x61,
|
||||||
0x32, 0xd3, 0x13, 0x3e, 0x89, 0xd5, 0xfd, 0x17, 0xfc, 0x8d, 0x53, 0x41, 0x67, 0xc1, 0x49, 0x2e,
|
0xe9, 0x1b, 0x08, 0xeb, 0x56, 0xad, 0x70, 0xbf, 0x08, 0x2d, 0x72, 0x6e, 0x69, 0x58, 0x6f, 0x41,
|
||||||
0x53, 0xcb, 0xaa, 0x88, 0xdb, 0x7d, 0x5a, 0xdc, 0x09, 0x13, 0xaa, 0x63, 0x20, 0xe8, 0xd8, 0xa7,
|
0xeb, 0x28, 0x4e, 0x06, 0x47, 0x41, 0x94, 0x2b, 0xe5, 0xee, 0x16, 0x5e, 0xe7, 0x2e, 0x4d, 0x88,
|
||||||
0xe6, 0x85, 0x38, 0x59, 0x2a, 0x2e, 0x10, 0xcd, 0x1b, 0x17, 0x88, 0xe8, 0x1c, 0x43, 0x1c, 0x73,
|
0x26, 0x70, 0x7f, 0x34, 0x07, 0x8b, 0xef, 0x47, 0xc7, 0x71, 0x30, 0xa4, 0x64, 0xfc, 0x84, 0x4f,
|
||||||
0x93, 0x7c, 0x2d, 0x79, 0xaa, 0x48, 0x81, 0x48, 0xca, 0x45, 0x96, 0x83, 0x1c, 0x87, 0x45, 0x19,
|
0x62, 0x75, 0xe5, 0x07, 0x7f, 0xe3, 0x54, 0xd0, 0xf1, 0x77, 0x92, 0xcb, 0x6c, 0xba, 0x2a, 0xe2,
|
||||||
0x88, 0x98, 0x20, 0x3a, 0x17, 0xe2, 0x03, 0x41, 0x23, 0x8c, 0xaf, 0x09, 0xa1, 0xb3, 0x55, 0xbe,
|
0x76, 0x9f, 0x16, 0xd7, 0xe0, 0x84, 0xea, 0x18, 0x08, 0x3a, 0xf6, 0xa9, 0x79, 0x07, 0x50, 0x96,
|
||||||
0x53, 0xd7, 0x12, 0x32, 0x5f, 0x82, 0xd1, 0x42, 0x8f, 0xb8, 0x36, 0xa4, 0x62, 0x0c, 0x20, 0xee,
|
0x8a, 0x3b, 0x53, 0xf3, 0xc6, 0x9d, 0x29, 0x3a, 0xba, 0x11, 0x27, 0xfb, 0x24, 0x5f, 0x4b, 0x9e,
|
||||||
0xbc, 0x95, 0x71, 0x23, 0x7c, 0x11, 0xc7, 0xf5, 0x2a, 0x7c, 0x41, 0x41, 0xf1, 0xc3, 0xf0, 0xc0,
|
0x2a, 0x52, 0x20, 0x92, 0x72, 0x91, 0x6c, 0x21, 0xc7, 0x61, 0x51, 0x06, 0x22, 0x26, 0x88, 0xce,
|
||||||
0x1f, 0x3e, 0xa1, 0x9b, 0x8e, 0x74, 0x3a, 0xdf, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x9d,
|
0x85, 0xf8, 0x40, 0xd0, 0x08, 0xe3, 0x6b, 0x42, 0xe8, 0x6c, 0x95, 0xaf, 0x11, 0xb6, 0x84, 0xcc,
|
||||||
0x84, 0x35, 0x3d, 0x13, 0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5,
|
0x97, 0x60, 0xb4, 0xd0, 0x23, 0xae, 0x0d, 0xa9, 0x18, 0x03, 0x88, 0x6b, 0x7e, 0x65, 0xdc, 0x08,
|
||||||
0x8c, 0xe9, 0x68, 0x45, 0x4d, 0x22, 0x33, 0x5b, 0xbe, 0x62, 0x1f, 0xa0, 0x7f, 0x03, 0xd8, 0xcd,
|
0x5f, 0xc4, 0x0d, 0x05, 0x15, 0xbe, 0xa0, 0xa0, 0xf8, 0x61, 0x78, 0xe0, 0x0f, 0x9f, 0xd2, 0xe5,
|
||||||
0xd1, 0x48, 0xae, 0xb7, 0x0e, 0x19, 0x8b, 0x95, 0x72, 0xac, 0x95, 0xaa, 0x99, 0xb1, 0x46, 0xed,
|
0x4e, 0xba, 0x90, 0xd0, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x1d, 0xfe, 0x35, 0x3d, 0x13,
|
||||||
0x8c, 0xb9, 0x77, 0xa1, 0xbd, 0x67, 0x5c, 0x77, 0x24, 0xd1, 0x50, 0x17, 0x1d, 0xa5, 0x38, 0x19,
|
0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5, 0x8c, 0xe9, 0x68, 0x45,
|
||||||
0x88, 0xd1, 0x60, 0xc3, 0x6c, 0xd0, 0xfd, 0x65, 0x60, 0x0f, 0x82, 0x2c, 0xd7, 0xfd, 0x13, 0xcb,
|
0x4d, 0x22, 0xf3, 0x80, 0x60, 0xc5, 0xbe, 0x33, 0xf0, 0x0d, 0x60, 0x37, 0x47, 0x23, 0xb9, 0xde,
|
||||||
0xf1, 0x1a, 0x74, 0x74, 0x80, 0x5d, 0x1c, 0xef, 0xb7, 0x25, 0x46, 0xc7, 0xee, 0x37, 0xc5, 0xbd,
|
0x3a, 0x64, 0x2c, 0x56, 0xca, 0xb1, 0x56, 0xaa, 0x66, 0xc6, 0x1a, 0xb5, 0x33, 0xe6, 0xde, 0x85,
|
||||||
0x80, 0xf2, 0xc0, 0xb6, 0x60, 0x29, 0x10, 0x50, 0x59, 0x13, 0x14, 0xa5, 0xae, 0x47, 0x7f, 0x4d,
|
0xf6, 0x9e, 0x71, 0xc3, 0x93, 0x44, 0x43, 0xdd, 0xed, 0x94, 0xe2, 0x64, 0x20, 0x46, 0x83, 0x0d,
|
||||||
0x82, 0xd6, 0x2e, 0xfa, 0x03, 0x07, 0x16, 0xe5, 0xd0, 0xd0, 0xdb, 0xb0, 0x2e, 0x7a, 0x8a, 0x81,
|
0xb3, 0x41, 0xf7, 0xe7, 0x81, 0x3d, 0x08, 0xb2, 0x5c, 0xf7, 0x4f, 0x2c, 0xc7, 0x1b, 0xd0, 0xd1,
|
||||||
0x59, 0x58, 0xfd, 0xf5, 0xb8, 0xaa, 0x0c, 0xcf, 0xd5, 0xc9, 0x30, 0x83, 0x66, 0xe2, 0xe7, 0x47,
|
0x01, 0x76, 0x71, 0xa3, 0xa1, 0x2d, 0x31, 0xba, 0x69, 0x70, 0x53, 0x5c, 0x85, 0x28, 0x0f, 0x6c,
|
||||||
0x14, 0xa0, 0xb4, 0x3c, 0xfa, 0xcd, 0x56, 0x45, 0xd0, 0x2c, 0x74, 0x85, 0x02, 0xe6, 0xba, 0x1b,
|
0x0b, 0x96, 0x02, 0x01, 0x95, 0x35, 0x41, 0x51, 0xea, 0x7a, 0xf4, 0xd7, 0x24, 0x68, 0xed, 0xa2,
|
||||||
0x99, 0xc2, 0x24, 0x57, 0x70, 0x1c, 0x14, 0x9d, 0xa4, 0x0b, 0x5c, 0x27, 0xc8, 0xe5, 0x2d, 0x85,
|
0x3f, 0x70, 0x60, 0x51, 0x0e, 0x0d, 0xbd, 0x0d, 0xeb, 0x6e, 0xab, 0x18, 0x98, 0x85, 0xd5, 0xdf,
|
||||||
0x02, 0x2e, 0xe6, 0x4b, 0xb2, 0x28, 0xcf, 0x97, 0x24, 0xf5, 0x74, 0xbd, 0xdb, 0x87, 0xde, 0x1d,
|
0x08, 0xac, 0xca, 0xf0, 0x5c, 0x9d, 0x0c, 0x33, 0x68, 0x26, 0x7e, 0x7e, 0x44, 0x01, 0x4a, 0xcb,
|
||||||
0x1e, 0xf2, 0x9c, 0xdf, 0x0c, 0xc3, 0x32, 0xff, 0x0b, 0x70, 0xbe, 0xa6, 0x4e, 0x3a, 0x2d, 0xf7,
|
0xa3, 0xdf, 0x6c, 0x55, 0x04, 0xcd, 0x42, 0x57, 0x28, 0x60, 0xae, 0xbb, 0x84, 0x2a, 0x4c, 0x72,
|
||||||
0x60, 0xed, 0x0e, 0x3f, 0x98, 0x8e, 0x1f, 0xf0, 0xe3, 0xe2, 0x14, 0x8b, 0x41, 0x33, 0x3b, 0x8a,
|
0x05, 0xc7, 0x41, 0xd1, 0xe5, 0x01, 0x81, 0xeb, 0x33, 0x01, 0x79, 0x31, 0xa3, 0x80, 0x8b, 0xf9,
|
||||||
0x4f, 0xe4, 0xda, 0xd2, 0x6f, 0xf6, 0x0a, 0x40, 0x88, 0x34, 0x83, 0x2c, 0xe1, 0x43, 0x75, 0x31,
|
0x92, 0x2c, 0xca, 0xf3, 0x25, 0x49, 0x3d, 0x5d, 0xef, 0xf6, 0xa1, 0x77, 0x87, 0x87, 0x3c, 0xe7,
|
||||||
0x8c, 0x90, 0xfd, 0x84, 0x0f, 0xdd, 0xb7, 0x81, 0x99, 0x7c, 0xe4, 0x10, 0xd0, 0x0e, 0x4c, 0x0f,
|
0x37, 0xc3, 0xb0, 0xcc, 0xff, 0x02, 0x9c, 0xaf, 0xa9, 0x93, 0x4e, 0xcb, 0x3d, 0x58, 0xbb, 0xc3,
|
||||||
0x06, 0xd9, 0x2c, 0xcb, 0xf9, 0x44, 0xdd, 0x78, 0x33, 0x21, 0xf7, 0x0a, 0x74, 0xf6, 0xfc, 0x99,
|
0x0f, 0xa6, 0xe3, 0x07, 0xfc, 0xb8, 0x38, 0xb8, 0x63, 0xd0, 0xcc, 0x8e, 0xe2, 0x13, 0xb9, 0xb6,
|
||||||
0xc7, 0x3f, 0x96, 0x77, 0x6d, 0x31, 0x36, 0xf6, 0x67, 0x28, 0xca, 0x3a, 0x36, 0xa6, 0x6a, 0xf7,
|
0xf4, 0x9b, 0xbd, 0x06, 0x10, 0x22, 0xcd, 0x20, 0x4b, 0xf8, 0x50, 0xdd, 0x85, 0x23, 0x64, 0x3f,
|
||||||
0x3f, 0x1b, 0xb0, 0x20, 0x28, 0x91, 0xeb, 0x88, 0x67, 0x79, 0x10, 0x89, 0x13, 0x1c, 0xc9, 0xd5,
|
0xe1, 0x43, 0xf7, 0x1d, 0x60, 0x26, 0x1f, 0x39, 0x04, 0xb4, 0x03, 0xd3, 0x83, 0x41, 0x36, 0xcb,
|
||||||
0x80, 0x2a, 0xb2, 0xd1, 0xa8, 0x91, 0x0d, 0xe9, 0xad, 0xaa, 0x4b, 0x36, 0x52, 0x08, 0x2c, 0x0c,
|
0x72, 0x3e, 0x51, 0x97, 0xfc, 0x4c, 0xc8, 0xbd, 0x02, 0x9d, 0x3d, 0x7f, 0xe6, 0xf1, 0x8f, 0xe4,
|
||||||
0xdd, 0x9a, 0xe2, 0x64, 0x5c, 0x04, 0x67, 0x05, 0x50, 0x4a, 0x96, 0x14, 0xd6, 0x46, 0xf4, 0x4f,
|
0xf5, 0x62, 0x8c, 0x8d, 0xfd, 0x19, 0x8a, 0xb2, 0x8e, 0x8d, 0xa9, 0xda, 0xfd, 0x8f, 0x06, 0x2c,
|
||||||
0x09, 0xad, 0x14, 0x07, 0x13, 0xaa, 0xb5, 0x69, 0x8b, 0x42, 0x6a, 0x2a, 0x36, 0xad, 0x62, 0xbb,
|
0x08, 0x4a, 0xe4, 0x3a, 0xe2, 0x59, 0x1e, 0x44, 0xe2, 0xd0, 0x4a, 0x72, 0x35, 0xa0, 0x8a, 0x6c,
|
||||||
0x96, 0x5e, 0xc2, 0x76, 0x09, 0x17, 0xf6, 0x79, 0xb6, 0x0b, 0x5e, 0xc2, 0x76, 0xb9, 0x0c, 0x56,
|
0x34, 0x6a, 0x64, 0x43, 0x7a, 0xab, 0xea, 0x5e, 0x91, 0x14, 0x02, 0x0b, 0x43, 0xb7, 0xa6, 0xb8,
|
||||||
0xef, 0x71, 0xee, 0x71, 0xdc, 0x15, 0x95, 0x38, 0x7d, 0xd7, 0x81, 0x55, 0xb9, 0xa1, 0xeb, 0x3a,
|
0x0c, 0x20, 0x82, 0xb3, 0x02, 0x28, 0x25, 0x4b, 0x0a, 0x6b, 0x23, 0xfa, 0xa7, 0x84, 0x56, 0x8a,
|
||||||
0xf6, 0x9a, 0xb5, 0xfb, 0x3b, 0x75, 0x07, 0x01, 0xaf, 0xc3, 0x32, 0xed, 0xc9, 0x3a, 0x2b, 0x24,
|
0x83, 0x09, 0xd5, 0xda, 0xb4, 0x45, 0x21, 0x35, 0x15, 0x9b, 0x56, 0xb1, 0x5d, 0x4b, 0xaf, 0x60,
|
||||||
0x53, 0x58, 0x16, 0x88, 0xe3, 0x50, 0xa9, 0xed, 0x49, 0x10, 0xca, 0x45, 0x31, 0x21, 0x95, 0x58,
|
0xbb, 0x84, 0x0b, 0xfb, 0x22, 0xdb, 0x05, 0xaf, 0x60, 0xbb, 0x5c, 0x06, 0xab, 0xf7, 0x38, 0xf7,
|
||||||
0xc2, 0xf8, 0x98, 0x96, 0xc4, 0xf1, 0x74, 0xd9, 0xfd, 0x3b, 0x07, 0xd6, 0x8c, 0x0e, 0x4b, 0x29,
|
0x38, 0xee, 0x8a, 0x4a, 0x9c, 0xbe, 0xeb, 0xc0, 0xaa, 0xdc, 0xd0, 0x75, 0x1d, 0x7b, 0xc3, 0xda,
|
||||||
0xbc, 0x01, 0xea, 0xe4, 0x5c, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b,
|
0xfd, 0x9d, 0xba, 0xf3, 0x88, 0x37, 0x61, 0x99, 0xf6, 0x64, 0x9d, 0x15, 0x92, 0x29, 0x2c, 0x0b,
|
||||||
0x98, 0x16, 0xd3, 0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2,
|
0xc4, 0x71, 0xa8, 0x0c, 0xfb, 0x24, 0x08, 0xe5, 0xa2, 0x98, 0x90, 0x4a, 0x2c, 0x61, 0x7c, 0x4c,
|
||||||
0xf9, 0x13, 0x4d, 0x32, 0x47, 0x24, 0x16, 0x46, 0x07, 0xa3, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0x17,
|
0x4b, 0xe2, 0x78, 0xba, 0xec, 0xfe, 0xb5, 0x03, 0x6b, 0x46, 0x87, 0xa5, 0x14, 0xde, 0x00, 0x75,
|
||||||
0xc8, 0x06, 0xdd, 0x7f, 0x74, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0xf7, 0x14, 0x17, 0x84,
|
0x59, 0x40, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b, 0x98, 0x16, 0xd3,
|
||||||
0x17, 0x2c, 0x34, 0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x8b, 0x2f, 0xe9, 0xc8, 0xea, 0x03, 0xf1,
|
0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2, 0xf9, 0x53, 0x4d,
|
||||||
0x53, 0xd6, 0x62, 0xae, 0x6e, 0x2d, 0x9e, 0x33, 0xd3, 0x75, 0xc9, 0x92, 0xf9, 0xda, 0x64, 0xc9,
|
0x32, 0x47, 0x24, 0x16, 0x46, 0x67, 0xc1, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0xfd, 0xc9, 0x06, 0xdd,
|
||||||
0xad, 0x45, 0x98, 0xcf, 0x86, 0x71, 0xc2, 0xdd, 0x4d, 0xd8, 0xb0, 0x07, 0x27, 0x4d, 0xd0, 0xf7,
|
0x7f, 0x70, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0x57, 0x33, 0x17, 0x84, 0x17, 0x2c, 0x34,
|
||||||
0x1c, 0xe8, 0xdd, 0x13, 0xa9, 0xc3, 0x20, 0x1a, 0xef, 0x06, 0x59, 0x1e, 0xa7, 0xfa, 0x62, 0xf6,
|
0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x0b, 0xaf, 0xe8, 0xc8, 0xea, 0x3b, 0x00, 0xa7, 0xac, 0xc5,
|
||||||
0x25, 0x80, 0x2c, 0xf7, 0xd3, 0x5c, 0x5c, 0x58, 0x92, 0x69, 0x8e, 0x02, 0xc1, 0x3e, 0xf2, 0x68,
|
0x5c, 0xdd, 0x5a, 0xbc, 0x60, 0xa6, 0xeb, 0x92, 0x25, 0xf3, 0xb5, 0xc9, 0x92, 0x5b, 0x8b, 0x30,
|
||||||
0x24, 0x6a, 0xc5, 0xda, 0xe8, 0x32, 0x2e, 0x0c, 0x1d, 0xd6, 0x0f, 0xe2, 0xc3, 0xc3, 0x8c, 0x6b,
|
0x9f, 0x0d, 0xe3, 0x84, 0xbb, 0x9b, 0xb0, 0x61, 0x0f, 0x4e, 0x9a, 0xa0, 0xef, 0x39, 0xd0, 0xbb,
|
||||||
0xb7, 0xd5, 0xc4, 0x30, 0xf2, 0x45, 0x8d, 0xc7, 0x58, 0x8f, 0x1f, 0x93, 0xa9, 0x15, 0xfe, 0x60,
|
0x27, 0x52, 0x87, 0x41, 0x34, 0xde, 0x0d, 0xb2, 0x3c, 0x4e, 0xf5, 0x5d, 0xf4, 0x4b, 0x00, 0x59,
|
||||||
0x09, 0x75, 0xff, 0xd6, 0x81, 0x95, 0xa2, 0x93, 0x77, 0x11, 0xb4, 0xad, 0x83, 0xe8, 0x9a, 0x61,
|
0xee, 0xa7, 0xb9, 0xb8, 0xa3, 0x25, 0xd3, 0x1c, 0x05, 0x82, 0x7d, 0xe4, 0xd1, 0x48, 0xd4, 0x8a,
|
||||||
0x1d, 0x54, 0x02, 0x26, 0x18, 0x0d, 0x82, 0x48, 0xf6, 0xcd, 0x40, 0x48, 0x63, 0x65, 0x29, 0x9e,
|
0xb5, 0xd1, 0x65, 0x5c, 0x18, 0xba, 0x9f, 0x30, 0x88, 0x0f, 0x0f, 0x33, 0xae, 0xdd, 0x56, 0x13,
|
||||||
0xaa, 0xcb, 0x61, 0x26, 0x24, 0x4e, 0x7e, 0x73, 0xfc, 0x5a, 0xdc, 0x0c, 0x93, 0x25, 0xba, 0x6f,
|
0xc3, 0xc8, 0x17, 0x35, 0x1e, 0x63, 0x3d, 0x7e, 0x4c, 0xa6, 0x56, 0xf8, 0x83, 0x25, 0xd4, 0xfd,
|
||||||
0x36, 0xc9, 0xe9, 0xab, 0x05, 0xe1, 0x10, 0xcb, 0xa2, 0xda, 0x9f, 0x16, 0x09, 0xc5, 0x9f, 0xee,
|
0x2b, 0x07, 0x56, 0x8a, 0x4e, 0xde, 0x45, 0xd0, 0xb6, 0x0e, 0xa2, 0x6b, 0x86, 0x75, 0x50, 0x09,
|
||||||
0x1f, 0x3b, 0x70, 0xbe, 0x66, 0x72, 0xa5, 0x66, 0xdc, 0x81, 0xb5, 0x43, 0x5d, 0xa9, 0x26, 0x40,
|
0x98, 0x60, 0x34, 0x08, 0x22, 0xd9, 0x37, 0x03, 0x21, 0x8d, 0x95, 0xa5, 0x78, 0xaa, 0xee, 0xc3,
|
||||||
0xa8, 0xc7, 0xa6, 0x94, 0xa2, 0xd2, 0xa0, 0xbd, 0xea, 0x07, 0xe8, 0x1e, 0x53, 0xde, 0x48, 0x4c,
|
0x99, 0x90, 0x38, 0xec, 0xce, 0xf1, 0x6b, 0x71, 0x19, 0x4e, 0x96, 0xe8, 0x8a, 0xdd, 0x24, 0xa7,
|
||||||
0xa9, 0x75, 0x69, 0xa2, 0x5a, 0xb1, 0xf3, 0xfd, 0x06, 0x74, 0xc5, 0x51, 0x85, 0x78, 0x9a, 0xc3,
|
0xaf, 0x16, 0x84, 0x43, 0x2c, 0x8b, 0x6a, 0x7f, 0x5a, 0x24, 0x14, 0x7f, 0xba, 0x7f, 0xe0, 0xc0,
|
||||||
0x53, 0xf6, 0x01, 0x2c, 0xca, 0x87, 0x50, 0xec, 0xac, 0x6c, 0xd6, 0x7e, 0x7a, 0xd5, 0xdf, 0x2c,
|
0xf9, 0x9a, 0xc9, 0x95, 0x9a, 0x71, 0x07, 0xd6, 0x0e, 0x75, 0xa5, 0x9a, 0x00, 0xa1, 0x1e, 0x9b,
|
||||||
0xc3, 0x52, 0x76, 0xd6, 0x7f, 0xff, 0xb3, 0x7f, 0xf9, 0x93, 0xc6, 0x32, 0x6b, 0x6f, 0x1f, 0xbf,
|
0x52, 0x8a, 0x4a, 0x83, 0xf6, 0xaa, 0x1f, 0xa0, 0x7b, 0x4c, 0x79, 0x23, 0x31, 0xa5, 0xd6, 0x3d,
|
||||||
0xb5, 0x3d, 0xe6, 0x51, 0x86, 0x3c, 0x7e, 0x13, 0xa0, 0x78, 0x4b, 0xc4, 0x7a, 0xda, 0xc9, 0x28,
|
0x91, 0x6a, 0xc5, 0xce, 0xf7, 0x1b, 0xd0, 0x15, 0x47, 0x15, 0xe2, 0x35, 0x12, 0x4f, 0xd9, 0x07,
|
||||||
0xbd, 0x7d, 0xea, 0x9f, 0xaf, 0xa9, 0x91, 0x7c, 0xcf, 0x13, 0xdf, 0x75, 0xb7, 0x8b, 0x7c, 0x83,
|
0xb0, 0x28, 0xdf, 0x7e, 0xb1, 0xb3, 0xb2, 0x59, 0xfb, 0xb5, 0x59, 0x7f, 0xb3, 0x0c, 0x4b, 0xd9,
|
||||||
0x28, 0xc8, 0xc5, 0xc3, 0xa2, 0x77, 0x9d, 0x2d, 0x36, 0x82, 0x8e, 0xf9, 0xa6, 0x88, 0xa9, 0x90,
|
0x59, 0xff, 0xed, 0x4f, 0xff, 0xf9, 0x0f, 0x1b, 0xcb, 0xac, 0xbd, 0x7d, 0xfc, 0xf6, 0xf6, 0x98,
|
||||||
0xb9, 0xe6, 0xa1, 0x52, 0xff, 0x42, 0x6d, 0x9d, 0xca, 0x17, 0x50, 0x1b, 0x67, 0xdd, 0x55, 0x6c,
|
0x47, 0x19, 0xf2, 0xf8, 0x55, 0x80, 0xe2, 0xf9, 0x14, 0xeb, 0x69, 0x27, 0xa3, 0xf4, 0xdc, 0xab,
|
||||||
0x63, 0x4a, 0x14, 0xba, 0x95, 0x9d, 0x7f, 0xba, 0x00, 0x2d, 0x9d, 0x76, 0x62, 0xdf, 0x86, 0x65,
|
0x7f, 0xbe, 0xa6, 0x46, 0xf2, 0x3d, 0x4f, 0x7c, 0xd7, 0xdd, 0x2e, 0xf2, 0x0d, 0xa2, 0x20, 0x17,
|
||||||
0xeb, 0x74, 0x87, 0x29, 0xc6, 0x75, 0x87, 0x41, 0xfd, 0x8b, 0xf5, 0x95, 0xb2, 0xd9, 0x4b, 0xd4,
|
0x6f, 0xa9, 0xde, 0x73, 0xb6, 0xd8, 0x08, 0x3a, 0xe6, 0x33, 0x2a, 0xa6, 0x42, 0xe6, 0x9a, 0xb7,
|
||||||
0x6c, 0x8f, 0x6d, 0x62, 0xb3, 0xf2, 0x78, 0x64, 0x9b, 0xce, 0xb4, 0xc4, 0xfd, 0xb2, 0x27, 0xd0,
|
0x59, 0xfd, 0x0b, 0xb5, 0x75, 0x2a, 0x5f, 0x40, 0x6d, 0x9c, 0x75, 0x57, 0xb1, 0x8d, 0x29, 0x51,
|
||||||
0xb5, 0x4f, 0x64, 0xd8, 0x45, 0xdb, 0xa0, 0x94, 0x5a, 0x7b, 0xe5, 0x94, 0x5a, 0xd9, 0xdc, 0x45,
|
0xe8, 0x56, 0x76, 0xfe, 0xf1, 0x02, 0xb4, 0x74, 0xda, 0x89, 0x7d, 0x08, 0xcb, 0xd6, 0xe9, 0x0e,
|
||||||
0x6a, 0x6e, 0x93, 0x6d, 0x98, 0xcd, 0xe9, 0x74, 0x10, 0xa7, 0x1b, 0x81, 0xe6, 0x63, 0x23, 0xf6,
|
0x53, 0x8c, 0xeb, 0x0e, 0x83, 0xfa, 0x17, 0xeb, 0x2b, 0x65, 0xb3, 0x97, 0xa8, 0xd9, 0x1e, 0xdb,
|
||||||
0x8a, 0x5e, 0xea, 0xba, 0x47, 0x48, 0x7a, 0xd1, 0xaa, 0x2f, 0x91, 0xdc, 0x1e, 0x35, 0xc5, 0x18,
|
0xc4, 0x66, 0xe5, 0xf1, 0xc8, 0x36, 0x9d, 0x69, 0x89, 0x2b, 0x75, 0x4f, 0xa1, 0x6b, 0x9f, 0xc8,
|
||||||
0x4d, 0xa8, 0xf9, 0xd6, 0x88, 0x7d, 0x04, 0x2d, 0xfd, 0xc0, 0x80, 0x9d, 0x33, 0x5e, 0x75, 0x98,
|
0xb0, 0x8b, 0xb6, 0x41, 0x29, 0xb5, 0xf6, 0xda, 0x29, 0xb5, 0xb2, 0xb9, 0x8b, 0xd4, 0xdc, 0x26,
|
||||||
0xaf, 0x1e, 0xfa, 0xbd, 0x6a, 0x45, 0xdd, 0x52, 0x99, 0x9c, 0x51, 0x20, 0x1e, 0xc0, 0x59, 0xe9,
|
0xdb, 0x30, 0x9b, 0xd3, 0xe9, 0x20, 0x4e, 0x97, 0x20, 0xcd, 0xf7, 0x55, 0xec, 0x35, 0xbd, 0xd4,
|
||||||
0xa4, 0x1e, 0xf0, 0x9f, 0x64, 0x24, 0x35, 0x4f, 0xa4, 0xae, 0x3b, 0xec, 0x06, 0x2c, 0xa9, 0x77,
|
0x75, 0xef, 0xae, 0xf4, 0xa2, 0x55, 0x1f, 0x5f, 0xb9, 0x3d, 0x6a, 0x8a, 0x31, 0x9a, 0x50, 0xf3,
|
||||||
0x1b, 0x6c, 0xb3, 0xfe, 0xfd, 0x49, 0xff, 0x5c, 0x05, 0x97, 0xfa, 0x7c, 0x13, 0xa0, 0x78, 0x73,
|
0x79, 0x15, 0xfb, 0x16, 0xb4, 0xf4, 0x9b, 0x0a, 0x76, 0xce, 0x78, 0xc8, 0x62, 0x3e, 0xf4, 0xe8,
|
||||||
0xa0, 0x25, 0xbf, 0xf2, 0x12, 0x42, 0x4f, 0x62, 0xcd, 0x03, 0x85, 0x31, 0xbd, 0xb0, 0xb0, 0x9f,
|
0xf7, 0xaa, 0x15, 0x75, 0x4b, 0x65, 0x72, 0x46, 0x81, 0x78, 0x00, 0x67, 0xa5, 0x93, 0x7a, 0xc0,
|
||||||
0x34, 0xb0, 0x57, 0x0b, 0xfa, 0xda, 0xc7, 0x0e, 0xcf, 0x61, 0xe8, 0x6e, 0xd2, 0xdc, 0xad, 0x32,
|
0x7f, 0x9c, 0x91, 0xd4, 0xbc, 0x0a, 0xbb, 0xee, 0xb0, 0x1b, 0xb0, 0xa4, 0x9e, 0xaa, 0xb0, 0xcd,
|
||||||
0x52, 0xa5, 0x88, 0x9f, 0xa8, 0xbb, 0xb1, 0x77, 0xa0, 0x6d, 0xbc, 0x63, 0x60, 0x8a, 0x43, 0xf5,
|
0xfa, 0x27, 0x37, 0xfd, 0x73, 0x15, 0x5c, 0xea, 0xf3, 0x4d, 0x80, 0xe2, 0x99, 0x85, 0x96, 0xfc,
|
||||||
0x0d, 0x44, 0xbf, 0x5f, 0x57, 0x25, 0xbb, 0xfb, 0x15, 0x58, 0xb6, 0x1e, 0x24, 0x68, 0xcd, 0xa8,
|
0xca, 0xe3, 0x0f, 0x3d, 0x89, 0x35, 0x6f, 0x32, 0xc6, 0xf4, 0xa8, 0xc4, 0x7e, 0xc5, 0xc1, 0x5e,
|
||||||
0x7b, 0xee, 0xa0, 0x35, 0xa3, 0xfe, 0x0d, 0xc3, 0x37, 0xa1, 0x6d, 0x3c, 0x1f, 0x60, 0xc6, 0x9d,
|
0x2f, 0xe8, 0x6b, 0xdf, 0x77, 0xbc, 0x80, 0xa1, 0xbb, 0x49, 0x73, 0xb7, 0xca, 0x48, 0x95, 0x22,
|
||||||
0xa0, 0xd2, 0xc3, 0x01, 0xdd, 0xa3, 0xba, 0xd7, 0x06, 0x1b, 0x34, 0xde, 0xae, 0xdb, 0xc2, 0xf1,
|
0x7e, 0xa2, 0xae, 0x03, 0xdf, 0x81, 0xb6, 0xf1, 0x74, 0x83, 0x29, 0x0e, 0xd5, 0x67, 0x1f, 0xfd,
|
||||||
0xd2, 0x05, 0x51, 0x14, 0x92, 0x6f, 0x43, 0xd7, 0x7e, 0x50, 0xa0, 0xb5, 0xaa, 0xf6, 0x69, 0x82,
|
0x7e, 0x5d, 0x95, 0xec, 0xee, 0x57, 0x60, 0xd9, 0x7a, 0x83, 0xa1, 0x35, 0xa3, 0xee, 0x85, 0x87,
|
||||||
0xd6, 0xaa, 0x53, 0x5e, 0x21, 0x48, 0x81, 0xdc, 0x5a, 0xd7, 0x8d, 0x6c, 0x7f, 0x2a, 0x0f, 0x5d,
|
0xd6, 0x8c, 0xfa, 0x67, 0x1b, 0xdf, 0x84, 0xb6, 0xf1, 0x62, 0x82, 0x19, 0xd7, 0xa0, 0x4a, 0x6f,
|
||||||
0x9e, 0xb1, 0xaf, 0xa3, 0xe9, 0x90, 0x37, 0x76, 0x59, 0xf1, 0x8c, 0xc2, 0xbe, 0xd7, 0xab, 0xa5,
|
0x25, 0x74, 0x8f, 0xea, 0x1e, 0x58, 0x6c, 0xd0, 0x78, 0xbb, 0x6e, 0x0b, 0xc7, 0x4b, 0x77, 0x62,
|
||||||
0xbd, 0x72, 0xb9, 0xd7, 0x5d, 0x23, 0xe6, 0x6d, 0x56, 0x8c, 0x40, 0x58, 0x68, 0xba, 0xb9, 0x6b,
|
0x51, 0x48, 0x3e, 0x84, 0xae, 0xfd, 0x86, 0x42, 0x6b, 0x55, 0xed, 0x6b, 0x0c, 0xad, 0x55, 0xa7,
|
||||||
0x58, 0x68, 0xf3, 0x72, 0xaf, 0x61, 0xa1, 0xad, 0x0b, 0xbe, 0x65, 0x0b, 0x9d, 0x07, 0xc8, 0x23,
|
0x3c, 0xbc, 0x90, 0x02, 0xb9, 0xb5, 0xae, 0x1b, 0xd9, 0xfe, 0x44, 0x1e, 0xba, 0x3c, 0x67, 0x5f,
|
||||||
0x82, 0x95, 0xd2, 0x3d, 0x00, 0xad, 0x2c, 0xf5, 0xb7, 0x88, 0xfa, 0x97, 0x9e, 0x7f, 0x7d, 0xc0,
|
0x47, 0xd3, 0x21, 0x2f, 0x29, 0xb3, 0xe2, 0xe5, 0x88, 0x7d, 0x95, 0x59, 0x4b, 0x7b, 0xe5, 0x3e,
|
||||||
0x36, 0x33, 0xca, 0xbc, 0x6c, 0xab, 0x4b, 0x5f, 0xbf, 0x05, 0x1d, 0xf3, 0x22, 0xb8, 0xb6, 0xd9,
|
0xb3, 0xbb, 0x46, 0xcc, 0xdb, 0xac, 0x18, 0x81, 0xb0, 0xd0, 0x74, 0x59, 0xd9, 0xb0, 0xd0, 0xe6,
|
||||||
0x35, 0xd7, 0xd7, 0xb5, 0xcd, 0xae, 0xbb, 0x39, 0xae, 0x16, 0x97, 0x75, 0xcc, 0x66, 0xd8, 0x37,
|
0x7d, 0x66, 0xc3, 0x42, 0x5b, 0x77, 0x9a, 0xcb, 0x16, 0x3a, 0x0f, 0x90, 0x47, 0x04, 0x2b, 0xa5,
|
||||||
0x61, 0xc5, 0xb8, 0x71, 0xb2, 0x3f, 0x8b, 0x86, 0x5a, 0x78, 0xaa, 0x77, 0x04, 0xfb, 0x75, 0xfe,
|
0x7b, 0x00, 0x5a, 0x59, 0xea, 0x2f, 0x4e, 0xf5, 0x2f, 0xbd, 0xf8, 0xfa, 0x80, 0x6d, 0x66, 0x94,
|
||||||
0x99, 0x7b, 0x8e, 0x18, 0xaf, 0xb9, 0x16, 0x63, 0x14, 0x9c, 0xdb, 0xd0, 0x36, 0x6f, 0xb3, 0x3c,
|
0x79, 0xd9, 0x56, 0xf7, 0xdc, 0x7e, 0x0d, 0x3a, 0xe6, 0xdd, 0x77, 0x6d, 0xb3, 0x6b, 0x6e, 0xec,
|
||||||
0x87, 0xef, 0x39, 0xa3, 0xca, 0xbc, 0x2e, 0x77, 0xdd, 0x61, 0x7f, 0xe6, 0x40, 0xc7, 0xbc, 0x7d,
|
0x6b, 0x9b, 0x5d, 0x77, 0x59, 0x5e, 0x2d, 0x2e, 0xeb, 0x98, 0xcd, 0xb0, 0x6f, 0xc2, 0x8a, 0x71,
|
||||||
0xca, 0xac, 0x3c, 0x6f, 0x89, 0x4f, 0xcf, 0xac, 0x33, 0x19, 0xb9, 0x1e, 0x75, 0xf2, 0xc1, 0xd6,
|
0xf1, 0x65, 0x7f, 0x16, 0x0d, 0xb5, 0xf0, 0x54, 0xaf, 0x45, 0xf6, 0xeb, 0xfc, 0x33, 0xf7, 0x1c,
|
||||||
0x57, 0xac, 0x49, 0xfe, 0xd4, 0xf2, 0xf3, 0xaf, 0x95, 0xdf, 0xf8, 0x3d, 0x2b, 0x13, 0x98, 0xf7,
|
0x31, 0x5e, 0x73, 0x2d, 0xc6, 0x28, 0x38, 0xb7, 0xa1, 0x6d, 0x5e, 0xaa, 0x79, 0x01, 0xdf, 0x73,
|
||||||
0x28, 0x9f, 0x5d, 0x77, 0xd8, 0xbb, 0xe2, 0x1d, 0xa8, 0x8a, 0xeb, 0x99, 0x61, 0xdc, 0xca, 0x53,
|
0x46, 0x95, 0x79, 0x43, 0xf0, 0xba, 0xc3, 0xfe, 0xd8, 0x81, 0x8e, 0x75, 0x45, 0xc5, 0xca, 0xf3,
|
||||||
0x66, 0x3e, 0x99, 0xbc, 0xea, 0x5c, 0x77, 0xd8, 0xb7, 0xc4, 0x53, 0x3e, 0xf9, 0x2d, 0xcd, 0xfc,
|
0x96, 0xf8, 0xf4, 0xcc, 0x3a, 0x93, 0x91, 0xeb, 0x51, 0x27, 0x1f, 0x6c, 0x7d, 0xc5, 0x9a, 0xe4,
|
||||||
0xcb, 0x7e, 0xef, 0xbe, 0x4e, 0xa3, 0xb9, 0xe4, 0x9e, 0xb7, 0x46, 0x53, 0xb6, 0xee, 0x7b, 0x00,
|
0x4f, 0x2c, 0x3f, 0xff, 0x5a, 0xf9, 0x59, 0xe3, 0xf3, 0x32, 0x81, 0x79, 0x75, 0xf4, 0xf9, 0x75,
|
||||||
0x45, 0x92, 0x86, 0x95, 0x32, 0x16, 0xda, 0xee, 0x55, 0xf3, 0x38, 0xf6, 0x8a, 0xaa, 0xc4, 0x06,
|
0x87, 0xbd, 0x27, 0x9e, 0xbe, 0xaa, 0xb8, 0x9e, 0x19, 0xc6, 0xad, 0x3c, 0x65, 0xe6, 0x2b, 0xd1,
|
||||||
0x72, 0xfc, 0x48, 0x08, 0xa3, 0xa4, 0xcf, 0xf4, 0x92, 0x56, 0x93, 0x2d, 0xfd, 0x7e, 0x5d, 0x55,
|
0xab, 0xce, 0x75, 0x87, 0x7d, 0x5b, 0xbc, 0x5e, 0x94, 0xdf, 0xd2, 0xcc, 0xbf, 0xea, 0xf7, 0xee,
|
||||||
0x9d, 0x28, 0x2a, 0xfe, 0xec, 0x31, 0x2c, 0x3f, 0x88, 0xe3, 0x27, 0xd3, 0x44, 0xa7, 0x11, 0xed,
|
0x9b, 0x34, 0x9a, 0x4b, 0xee, 0x79, 0x6b, 0x34, 0x65, 0xeb, 0xbe, 0x07, 0x50, 0x24, 0x69, 0x58,
|
||||||
0x9c, 0xc1, 0xae, 0x9f, 0x1d, 0xf5, 0x4b, 0xa3, 0x70, 0x2f, 0x13, 0xab, 0x3e, 0xeb, 0x19, 0xac,
|
0x29, 0x63, 0xa1, 0xed, 0x5e, 0x35, 0x8f, 0x63, 0xaf, 0xa8, 0x4a, 0x6c, 0x20, 0xc7, 0x6f, 0x09,
|
||||||
0xb6, 0x3f, 0x2d, 0x52, 0x44, 0xcf, 0x98, 0x0f, 0x6b, 0x7a, 0x8f, 0xd3, 0x1d, 0xef, 0xdb, 0x6c,
|
0x61, 0x94, 0xf4, 0x99, 0x5e, 0xd2, 0x6a, 0xb2, 0xa5, 0xdf, 0xaf, 0xab, 0xaa, 0x13, 0x45, 0xc5,
|
||||||
0xcc, 0x4c, 0x4d, 0xa5, 0x09, 0xcb, 0xeb, 0x50, 0xbd, 0xdd, 0xce, 0x14, 0xcf, 0xeb, 0x0e, 0xdb,
|
0x9f, 0x3d, 0x86, 0xe5, 0x07, 0x71, 0xfc, 0x74, 0x9a, 0xe8, 0x34, 0xa2, 0x9d, 0x33, 0xd8, 0xf5,
|
||||||
0x83, 0xce, 0x1d, 0x3e, 0x8c, 0x47, 0x5c, 0x46, 0xf9, 0xeb, 0x45, 0xc7, 0x75, 0x7a, 0xa0, 0xbf,
|
0xb3, 0xa3, 0x7e, 0x69, 0x14, 0xee, 0x65, 0x62, 0xd5, 0x67, 0x3d, 0x83, 0xd5, 0xf6, 0x27, 0x45,
|
||||||
0x6c, 0x81, 0xb6, 0xd6, 0x27, 0xfe, 0x2c, 0xe5, 0x1f, 0x6f, 0x7f, 0x2a, 0xf3, 0x07, 0xcf, 0x94,
|
0x8a, 0xe8, 0x39, 0xf3, 0x61, 0x4d, 0xef, 0x71, 0xba, 0xe3, 0x7d, 0x9b, 0x8d, 0x99, 0xa9, 0xa9,
|
||||||
0xd6, 0xab, 0x9c, 0x87, 0xa5, 0xf5, 0xa5, 0x24, 0x89, 0xa5, 0xf5, 0x95, 0x24, 0x89, 0x35, 0xd5,
|
0x34, 0x61, 0x79, 0x1d, 0xaa, 0xb7, 0xdb, 0x99, 0xe2, 0x79, 0xdd, 0x61, 0x7b, 0xd0, 0xb9, 0xc3,
|
||||||
0x2a, 0xe7, 0xc2, 0x42, 0x58, 0xab, 0xe4, 0x55, 0xf4, 0x4e, 0x79, 0x5a, 0x36, 0xa6, 0x7f, 0xf9,
|
0x87, 0xf1, 0x88, 0xcb, 0x28, 0x7f, 0xbd, 0xe8, 0xb8, 0x4e, 0x0f, 0xf4, 0x97, 0x2d, 0xd0, 0xd6,
|
||||||
0x74, 0x02, 0xbb, 0xb5, 0x2d, 0xbb, 0xb5, 0x7d, 0x58, 0xbe, 0xc3, 0xc5, 0x64, 0x89, 0xb3, 0xca,
|
0xfa, 0xc4, 0x9f, 0xa5, 0xfc, 0xa3, 0xed, 0x4f, 0x64, 0xfe, 0xe0, 0xb9, 0xd2, 0x7a, 0x95, 0xf3,
|
||||||
0xbe, 0x6d, 0x46, 0xcc, 0x73, 0xcd, 0xb2, 0x89, 0xa1, 0x3a, 0xdb, 0xac, 0xd3, 0x41, 0x21, 0xfb,
|
0xb0, 0xb4, 0xbe, 0x94, 0x24, 0xb1, 0xb4, 0xbe, 0x92, 0x24, 0xb1, 0xa6, 0x5a, 0xe5, 0x5c, 0x58,
|
||||||
0x08, 0xda, 0xf7, 0x79, 0xae, 0x0e, 0x27, 0xb5, 0xbf, 0x51, 0x3a, 0xad, 0xec, 0xd7, 0x9c, 0x6d,
|
0x08, 0x6b, 0x95, 0xbc, 0x8a, 0xde, 0x29, 0x4f, 0xcb, 0xc6, 0xf4, 0x2f, 0x9f, 0x4e, 0x60, 0xb7,
|
||||||
0xda, 0x32, 0x43, 0xdc, 0xb6, 0xf9, 0x68, 0xcc, 0x85, 0xb2, 0x0f, 0x82, 0xd1, 0x33, 0xf6, 0xeb,
|
0xb6, 0x65, 0xb7, 0xb6, 0x0f, 0xcb, 0x77, 0xb8, 0x98, 0x2c, 0x71, 0x56, 0xd9, 0xb7, 0xcd, 0x88,
|
||||||
0xc4, 0x5c, 0xdf, 0x67, 0xd8, 0x34, 0xce, 0xb4, 0x4c, 0xe6, 0x2b, 0x25, 0xbc, 0x8e, 0x73, 0x14,
|
0x79, 0xae, 0x59, 0x36, 0x31, 0x54, 0x67, 0x9b, 0x75, 0x3a, 0x28, 0x64, 0xdf, 0x82, 0xf6, 0x7d,
|
||||||
0x8f, 0xb8, 0xb1, 0xc1, 0x45, 0xd0, 0x36, 0x2e, 0xdb, 0x68, 0x05, 0xaa, 0x5e, 0xf0, 0xd1, 0x0a,
|
0x9e, 0xab, 0xc3, 0x49, 0xed, 0x6f, 0x94, 0x4e, 0x2b, 0xfb, 0x35, 0x67, 0x9b, 0xb6, 0xcc, 0x10,
|
||||||
0x54, 0x73, 0x37, 0xc7, 0xbd, 0x4a, 0xed, 0xb8, 0xec, 0x72, 0xd1, 0x8e, 0xb8, 0x8f, 0x53, 0xb4,
|
0xb7, 0x6d, 0x3e, 0x1a, 0x73, 0xa1, 0xec, 0x83, 0x60, 0xf4, 0x9c, 0xfd, 0x32, 0x31, 0xd7, 0xf7,
|
||||||
0xb4, 0xfd, 0xa9, 0x3f, 0xc9, 0x9f, 0xb1, 0x0f, 0xe9, 0x29, 0x8b, 0x79, 0x00, 0x5b, 0xf8, 0x3b,
|
0x19, 0x36, 0x8d, 0x33, 0x2d, 0x93, 0xf9, 0x4a, 0x09, 0xaf, 0xe3, 0x1c, 0xc5, 0x23, 0x6e, 0x6c,
|
||||||
0xe5, 0xb3, 0x5a, 0x3d, 0x59, 0x46, 0x95, 0xed, 0x03, 0x89, 0xa6, 0x68, 0x1f, 0xfc, 0x22, 0xc0,
|
0x70, 0x11, 0xb4, 0x8d, 0xcb, 0x36, 0x5a, 0x81, 0xaa, 0x17, 0x7c, 0xb4, 0x02, 0xd5, 0xdc, 0xcd,
|
||||||
0x7e, 0x1e, 0x27, 0x77, 0x7c, 0x3e, 0x89, 0xa3, 0xc2, 0x72, 0x15, 0x87, 0x8c, 0x85, 0xe5, 0x32,
|
0x71, 0xaf, 0x52, 0x3b, 0x2e, 0xbb, 0x5c, 0xb4, 0x23, 0xee, 0xe3, 0x14, 0x2d, 0x6d, 0x7f, 0xe2,
|
||||||
0x4e, 0x1a, 0xd9, 0x87, 0x86, 0xc7, 0x69, 0x9d, 0x5f, 0x2b, 0xe1, 0x3a, 0xf5, 0x1c, 0x52, 0x4f,
|
0x4f, 0xf2, 0xe7, 0xec, 0x09, 0xbd, 0xde, 0x31, 0x0f, 0x60, 0x0b, 0x7f, 0xa7, 0x7c, 0x56, 0xab,
|
||||||
0x48, 0xcd, 0x59, 0xe4, 0x75, 0x07, 0xfd, 0xc7, 0x22, 0x8b, 0xa7, 0xfd, 0xc7, 0x4a, 0x82, 0x50,
|
0x27, 0xcb, 0xa8, 0xb2, 0x7d, 0x20, 0xd1, 0x14, 0xed, 0x83, 0x5f, 0x00, 0xd8, 0xcf, 0xe3, 0xe4,
|
||||||
0x9b, 0xbd, 0x9a, 0x94, 0xdf, 0x1e, 0xb4, 0x8a, 0xb4, 0x90, 0xda, 0x92, 0xca, 0x49, 0x24, 0xbd,
|
0x8e, 0xcf, 0x27, 0x71, 0x54, 0x58, 0xae, 0xe2, 0x90, 0xb1, 0xb0, 0x5c, 0xc6, 0x49, 0x23, 0x7b,
|
||||||
0xc7, 0x54, 0x92, 0x35, 0xee, 0x2a, 0x4d, 0x15, 0xb0, 0x25, 0x9c, 0x2a, 0xca, 0xc0, 0x04, 0xb0,
|
0x62, 0x78, 0x9c, 0xd6, 0xf9, 0xb5, 0x12, 0xae, 0x53, 0xcf, 0x21, 0xf5, 0x84, 0xd4, 0x9c, 0x45,
|
||||||
0x2e, 0x3a, 0xa8, 0x37, 0x4c, 0x3a, 0x36, 0x53, 0x23, 0xa9, 0x49, 0x98, 0x68, 0x6d, 0xae, 0xcd,
|
0x5e, 0x77, 0xd0, 0x7f, 0x2c, 0xb2, 0x78, 0xda, 0x7f, 0xac, 0x24, 0x08, 0xb5, 0xd9, 0xab, 0x49,
|
||||||
0x37, 0x58, 0xb1, 0x1d, 0x4a, 0xab, 0x38, 0xb2, 0x43, 0xd3, 0x3c, 0x81, 0xb5, 0x4a, 0xb0, 0xac,
|
0xf9, 0xed, 0x41, 0xab, 0x48, 0x0b, 0xa9, 0x2d, 0xa9, 0x9c, 0x44, 0xd2, 0x7b, 0x4c, 0x25, 0x59,
|
||||||
0x55, 0xfa, 0xb4, 0x1c, 0x85, 0x56, 0xe9, 0x53, 0xe3, 0x6c, 0xf7, 0x2c, 0x35, 0xb9, 0xe2, 0x02,
|
0xe3, 0xae, 0xd2, 0x54, 0x01, 0x5b, 0xc2, 0xa9, 0xa2, 0x0c, 0x4c, 0x00, 0xeb, 0xa2, 0x83, 0x7a,
|
||||||
0x36, 0x99, 0x9d, 0x04, 0xf9, 0xf0, 0xe8, 0x5d, 0x67, 0xeb, 0x60, 0x81, 0xfe, 0x69, 0xe4, 0xf3,
|
0xc3, 0xa4, 0x63, 0x33, 0x35, 0x92, 0x9a, 0x84, 0x89, 0xd6, 0xe6, 0xda, 0x7c, 0x83, 0x15, 0xdb,
|
||||||
0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xc7, 0xfe, 0x7f, 0x9b, 0x44, 0x00, 0x00,
|
0xa1, 0xb4, 0x8a, 0x23, 0x3b, 0x34, 0xcd, 0x13, 0x58, 0xab, 0x04, 0xcb, 0x5a, 0xa5, 0x4f, 0xcb,
|
||||||
|
0x51, 0x68, 0x95, 0x3e, 0x35, 0xce, 0x76, 0xcf, 0x52, 0x93, 0x2b, 0x2e, 0x60, 0x93, 0xd9, 0x49,
|
||||||
|
0x90, 0x0f, 0x8f, 0xde, 0x73, 0xb6, 0x0e, 0x16, 0xe8, 0xcf, 0x55, 0x3e, 0xf7, 0x3f, 0x01, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0x8f, 0xec, 0x5b, 0xef, 0x8e, 0x45, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
@ -1063,6 +1063,14 @@ message PendingChannelsResponse {
|
|||||||
int64 fee_per_kw = 6 [ json_name = "fee_per_kw" ];
|
int64 fee_per_kw = 6 [ json_name = "fee_per_kw" ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WaitingCloseChannel {
|
||||||
|
/// The pending channel waiting for closing tx to confirm
|
||||||
|
PendingChannel channel = 1;
|
||||||
|
|
||||||
|
/// The balance in satoshis encumbered in this channel
|
||||||
|
int64 limbo_balance = 2 [ json_name = "limbo_balance" ];
|
||||||
|
}
|
||||||
|
|
||||||
message ClosedChannel {
|
message ClosedChannel {
|
||||||
/// The pending channel to be closed
|
/// The pending channel to be closed
|
||||||
PendingChannel channel = 1;
|
PendingChannel channel = 1;
|
||||||
@ -1108,6 +1116,9 @@ message PendingChannelsResponse {
|
|||||||
|
|
||||||
/// Channels pending force closing
|
/// Channels pending force closing
|
||||||
repeated ForceClosedChannel pending_force_closing_channels = 4 [ json_name = "pending_force_closing_channels" ];
|
repeated ForceClosedChannel pending_force_closing_channels = 4 [ json_name = "pending_force_closing_channels" ];
|
||||||
|
|
||||||
|
/// Channels waiting for closing tx to confirm
|
||||||
|
repeated WaitingCloseChannel waiting_close_channels = 5 [ json_name = "waiting_close_channels" ];
|
||||||
}
|
}
|
||||||
|
|
||||||
message WalletBalanceRequest {
|
message WalletBalanceRequest {
|
||||||
|
@ -893,6 +893,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"PendingChannelsResponseWaitingCloseChannel": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"channel": {
|
||||||
|
"$ref": "#/definitions/PendingChannelsResponsePendingChannel",
|
||||||
|
"title": "/ The pending channel waiting for closing tx to confirm"
|
||||||
|
},
|
||||||
|
"limbo_balance": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "int64",
|
||||||
|
"title": "/ The balance in satoshis encumbered in this channel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"lnrpcAddInvoiceResponse": {
|
"lnrpcAddInvoiceResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -2035,6 +2049,13 @@
|
|||||||
"$ref": "#/definitions/PendingChannelsResponseForceClosedChannel"
|
"$ref": "#/definitions/PendingChannelsResponseForceClosedChannel"
|
||||||
},
|
},
|
||||||
"title": "/ Channels pending force closing"
|
"title": "/ Channels pending force closing"
|
||||||
|
},
|
||||||
|
"waiting_close_channels": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/PendingChannelsResponseWaitingCloseChannel"
|
||||||
|
},
|
||||||
|
"title": "/ Channels waiting for closing tx to confirm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -770,6 +770,7 @@ func (n *NetworkHarness) CloseChannel(ctx context.Context,
|
|||||||
return func() bool {
|
return func() bool {
|
||||||
channel, err := filterChannel(node, chanPoint)
|
channel, err := filterChannel(node, chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return channel.Active
|
return channel.Active
|
||||||
|
@ -4650,7 +4650,7 @@ type CommitOutputResolution struct {
|
|||||||
// had any outgoing HTLC's within the commitment transaction, then an
|
// had any outgoing HTLC's within the commitment transaction, then an
|
||||||
// OutgoingHtlcResolution for each output will included.
|
// OutgoingHtlcResolution for each output will included.
|
||||||
type UnilateralCloseSummary struct {
|
type UnilateralCloseSummary struct {
|
||||||
// SpendDetail is a struct that describes how and when the commitment
|
// SpendDetail is a struct that describes how and when the funding
|
||||||
// output was spent.
|
// output was spent.
|
||||||
*chainntnfs.SpendDetail
|
*chainntnfs.SpendDetail
|
||||||
|
|
||||||
@ -4754,7 +4754,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer,
|
|||||||
RemotePub: chanState.IdentityPub,
|
RemotePub: chanState.IdentityPub,
|
||||||
Capacity: chanState.Capacity,
|
Capacity: chanState.Capacity,
|
||||||
SettledBalance: localBalance,
|
SettledBalance: localBalance,
|
||||||
CloseType: channeldb.ForceClose,
|
CloseType: channeldb.RemoteForceClose,
|
||||||
IsPending: true,
|
IsPending: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
peer.go
6
peer.go
@ -329,9 +329,9 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
|
|||||||
|
|
||||||
// Skip adding any permanently irreconcilable channels to the
|
// Skip adding any permanently irreconcilable channels to the
|
||||||
// htlcswitch.
|
// htlcswitch.
|
||||||
if dbChan.IsBorked {
|
if dbChan.ChanStatus != channeldb.Default {
|
||||||
peerLog.Warnf("ChannelPoint(%v) is borked, won't "+
|
peerLog.Warnf("ChannelPoint(%v) has status %v, won't "+
|
||||||
"start.", chanPoint)
|
"start.", chanPoint, dbChan.ChanStatus)
|
||||||
lnChan.Stop()
|
lnChan.Stop()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
64
rpcserver.go
64
rpcserver.go
@ -1343,18 +1343,24 @@ func (r *rpcServer) WalletBalance(ctx context.Context,
|
|||||||
func (r *rpcServer) ChannelBalance(ctx context.Context,
|
func (r *rpcServer) ChannelBalance(ctx context.Context,
|
||||||
in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) {
|
in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) {
|
||||||
|
|
||||||
channels, err := r.server.chanDB.FetchAllChannels()
|
openChannels, err := r.server.chanDB.FetchAllOpenChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pendingOpenBalance, balance btcutil.Amount
|
var balance btcutil.Amount
|
||||||
for _, channel := range channels {
|
for _, channel := range openChannels {
|
||||||
if channel.IsPending {
|
balance += channel.LocalCommitment.LocalBalance.ToSatoshis()
|
||||||
pendingOpenBalance += channel.LocalCommitment.LocalBalance.ToSatoshis()
|
}
|
||||||
} else {
|
|
||||||
balance += channel.LocalCommitment.LocalBalance.ToSatoshis()
|
pendingChannels, err := r.server.chanDB.FetchPendingChannels()
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pendingOpenBalance btcutil.Amount
|
||||||
|
for _, channel := range pendingChannels {
|
||||||
|
pendingOpenBalance += channel.LocalCommitment.LocalBalance.ToSatoshis()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lnrpc.ChannelBalanceResponse{
|
return &lnrpc.ChannelBalanceResponse{
|
||||||
@ -1457,7 +1463,8 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
|
|||||||
|
|
||||||
// If the channel was force closed, then we'll need to query
|
// If the channel was force closed, then we'll need to query
|
||||||
// the utxoNursery for additional information.
|
// the utxoNursery for additional information.
|
||||||
case channeldb.ForceClose:
|
// TODO(halseth): distinguish remote and local case?
|
||||||
|
case channeldb.LocalForceClose, channeldb.RemoteForceClose:
|
||||||
forceClose := &lnrpc.PendingChannelsResponse_ForceClosedChannel{
|
forceClose := &lnrpc.PendingChannelsResponse_ForceClosedChannel{
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
ClosingTxid: closeTXID,
|
ClosingTxid: closeTXID,
|
||||||
@ -1522,6 +1529,39 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll also fetch all channels that are open, but have had their
|
||||||
|
// commitment broadcasted, meaning they are waiting for the closing
|
||||||
|
// transaction to confirm.
|
||||||
|
waitingCloseChans, err := r.server.chanDB.FetchWaitingCloseChannels()
|
||||||
|
if err != nil {
|
||||||
|
rpcsLog.Errorf("unable to fetch channels waiting close: %v",
|
||||||
|
err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, waitingClose := range waitingCloseChans {
|
||||||
|
pub := waitingClose.IdentityPub.SerializeCompressed()
|
||||||
|
chanPoint := waitingClose.FundingOutpoint
|
||||||
|
channel := &lnrpc.PendingChannelsResponse_PendingChannel{
|
||||||
|
RemoteNodePub: hex.EncodeToString(pub),
|
||||||
|
ChannelPoint: chanPoint.String(),
|
||||||
|
Capacity: int64(waitingClose.Capacity),
|
||||||
|
LocalBalance: int64(waitingClose.LocalCommitment.LocalBalance.ToSatoshis()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// A close tx has been broadcasted, all our balance will be in
|
||||||
|
// limbo until it confirms.
|
||||||
|
resp.WaitingCloseChannels = append(
|
||||||
|
resp.WaitingCloseChannels,
|
||||||
|
&lnrpc.PendingChannelsResponse_WaitingCloseChannel{
|
||||||
|
Channel: channel,
|
||||||
|
LimboBalance: channel.LocalBalance,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
resp.TotalLimboBalance += channel.LocalBalance
|
||||||
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1544,7 +1584,7 @@ func (r *rpcServer) ListChannels(ctx context.Context,
|
|||||||
|
|
||||||
graph := r.server.chanDB.ChannelGraph()
|
graph := r.server.chanDB.ChannelGraph()
|
||||||
|
|
||||||
dbChannels, err := r.server.chanDB.FetchAllChannels()
|
dbChannels, err := r.server.chanDB.FetchAllOpenChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1553,10 +1593,6 @@ func (r *rpcServer) ListChannels(ctx context.Context,
|
|||||||
len(dbChannels))
|
len(dbChannels))
|
||||||
|
|
||||||
for _, dbChannel := range dbChannels {
|
for _, dbChannel := range dbChannels {
|
||||||
if dbChannel.IsPending {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
nodePub := dbChannel.IdentityPub
|
nodePub := dbChannel.IdentityPub
|
||||||
nodeID := hex.EncodeToString(nodePub.SerializeCompressed())
|
nodeID := hex.EncodeToString(nodePub.SerializeCompressed())
|
||||||
chanPoint := dbChannel.FundingOutpoint
|
chanPoint := dbChannel.FundingOutpoint
|
||||||
|
Loading…
x
Reference in New Issue
Block a user