From 51fc193ddc6fddfd1ee48409ef31038689edfd56 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:15:18 -0700 Subject: [PATCH 01/16] lnwallet/wallet: export InitFundingReserveMsg and required fields --- lnwallet/wallet.go | 68 ++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index b86b200a..d31b2bb1 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -44,7 +44,7 @@ func (e *ErrInsufficientFunds) Error() string { e.amountSelected) } -// initFundingReserveReq is the first message sent to initiate the workflow +// InitFundingReserveMsg is the first message sent to initiate the workflow // required to open a payment channel with a remote peer. The initial required // parameters are configurable across channels. These parameters are to be // chosen depending on the fee climate within the network, and time value of @@ -54,44 +54,44 @@ func (e *ErrInsufficientFunds) Error() string { // pending reservations. Therefore, all channels in reservation limbo will be // periodically timed out after an idle period in order to avoid "exhaustion" // attacks. -type initFundingReserveMsg struct { - // chainHash denotes that chain to be used to ultimately open the +type InitFundingReserveMsg struct { + // ChainHash denotes that chain to be used to ultimately open the // target channel. - chainHash *chainhash.Hash + ChainHash *chainhash.Hash - // nodeId is the ID of the remote node we would like to open a channel + // NodeID is the ID of the remote node we would like to open a channel // with. - nodeID *btcec.PublicKey + NodeID *btcec.PublicKey - // nodeAddr is the address port that we used to either establish or + // NodeAddr is the address port that we used to either establish or // accept the connection which led to the negotiation of this funding // workflow. - nodeAddr net.Addr + NodeAddr net.Addr - // fundingAmount is the amount of funds requested for this channel. - fundingAmount btcutil.Amount + // FundingAmount is the amount of funds requested for this channel. + FundingAmount btcutil.Amount - // capacity is the total capacity of the channel which includes the + // Capacity is the total capacity of the channel which includes the // amount of funds the remote party contributes (if any). - capacity btcutil.Amount + Capacity btcutil.Amount - // commitFeePerKw is the starting accepted satoshis/Kw fee for the set + // CommitFeePerKw is the starting accepted satoshis/Kw fee for the set // of initial commitment transactions. In order to ensure timely // confirmation, it is recommended that this fee should be generous, // paying some multiple of the accepted base fee rate of the network. - commitFeePerKw SatPerKWeight + CommitFeePerKw SatPerKWeight - // fundingFeePerKw is the fee rate in sat/kw to use for the initial + // FundingFeePerKw is the fee rate in sat/kw to use for the initial // funding transaction. - fundingFeePerKw SatPerKWeight + FundingFeePerKw SatPerKWeight - // pushMSat is the number of milli-satoshis that should be pushed over + // PushMSat is the number of milli-satoshis that should be pushed over // the responder as part of the initial channel creation. - pushMSat lnwire.MilliSatoshi + PushMSat lnwire.MilliSatoshi - // flags are the channel flags specified by the initiator in the + // Flags are the channel flags specified by the initiator in the // open_channel message. - flags lnwire.FundingFlag + Flags lnwire.FundingFlag // err is a channel in which all errors will be sent across. Will be // nil if this initial set is successful. @@ -372,7 +372,7 @@ out: select { case m := <-l.msgChan: switch msg := m.(type) { - case *initFundingReserveMsg: + case *InitFundingReserveMsg: l.handleFundingReserveRequest(msg) case *fundingReserveCancelMsg: l.handleFundingCancelRequest(msg) @@ -439,9 +439,9 @@ func (l *LightningWallet) InitChannelReservation( // handleFundingReserveRequest processes a message intending to create, and // validate a funding reservation request. -func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg) { +func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg) { // It isn't possible to create a channel with zero funds committed. - if req.fundingAmount+req.capacity == 0 { + if req.FundingAmount+req.Capacity == 0 { err := ErrZeroCapacity() req.err <- err req.resp <- nil @@ -450,18 +450,20 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // If the funding request is for a different chain than the one the // wallet is aware of, then we'll reject the request. - if !bytes.Equal(l.Cfg.NetParams.GenesisHash[:], req.chainHash[:]) { - err := ErrChainMismatch(l.Cfg.NetParams.GenesisHash, - req.chainHash) + if !bytes.Equal(l.Cfg.NetParams.GenesisHash[:], req.ChainHash[:]) { + err := ErrChainMismatch( + l.Cfg.NetParams.GenesisHash, req.ChainHash, + ) req.err <- err req.resp <- nil return } id := atomic.AddUint64(&l.nextFundingID, 1) - reservation, err := NewChannelReservation(req.capacity, req.fundingAmount, - req.commitFeePerKw, l, id, req.pushMSat, - l.Cfg.NetParams.GenesisHash, req.flags) + reservation, err := NewChannelReservation( + req.Capacity, req.FundingAmount, req.CommitFeePerKw, l, id, + req.PushMSat, l.Cfg.NetParams.GenesisHash, req.Flags, + ) if err != nil { req.err <- err req.resp <- nil @@ -472,17 +474,17 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg reservation.Lock() defer reservation.Unlock() - reservation.nodeAddr = req.nodeAddr - reservation.partialState.IdentityPub = req.nodeID + reservation.nodeAddr = req.NodeAddr + reservation.partialState.IdentityPub = req.NodeID // If we're on the receiving end of a single funder channel then we // don't need to perform any coin selection. Otherwise, attempt to // obtain enough coins to meet the required funding amount. - if req.fundingAmount != 0 { + if req.FundingAmount != 0 { // Coin selection is done on the basis of sat/kw, so we'll use // the fee rate passed in to perform coin selection. err := l.selectCoinsAndChange( - req.fundingFeePerKw, req.fundingAmount, + req.FundingFeePerKw, req.FundingAmount, reservation.ourContribution, ) if err != nil { From d76f97d497ec4ab2160776c3c768162afb529d6e Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:16:14 -0700 Subject: [PATCH 02/16] fundingmanager+lnwallet: make InitChannelReservation take in req rather than many args --- fundingmanager.go | 37 +++++++--- lnwallet/interface_test.go | 139 ++++++++++++++++++++++++++----------- lnwallet/wallet.go | 39 ++++------- 3 files changed, 138 insertions(+), 77 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index 4a4e14f2..c31080ae 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1042,12 +1042,19 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { // responding side of a single funder workflow, we don't commit any // funds to the channel ourselves. chainHash := chainhash.Hash(msg.ChainHash) - reservation, err := f.cfg.Wallet.InitChannelReservation( - amt, 0, msg.PushAmount, - lnwallet.SatPerKWeight(msg.FeePerKiloWeight), 0, - fmsg.peer.IdentityKey(), fmsg.peer.Address(), &chainHash, - msg.ChannelFlags, - ) + req := &lnwallet.InitFundingReserveMsg{ + ChainHash: &chainHash, + NodeID: fmsg.peer.IdentityKey(), + NodeAddr: fmsg.peer.Address(), + FundingAmount: 0, + Capacity: amt, + CommitFeePerKw: lnwallet.SatPerKWeight(msg.FeePerKiloWeight), + FundingFeePerKw: 0, + PushMSat: msg.PushAmount, + Flags: msg.ChannelFlags, + } + + reservation, err := f.cfg.Wallet.InitChannelReservation(req) if err != nil { fndgLog.Errorf("Unable to initialize reservation: %v", err) f.failFundingFlow(fmsg.peer, msg.PendingChannelID, err) @@ -2598,11 +2605,19 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { // Initialize a funding reservation with the local wallet. If the // wallet doesn't have enough funds to commit to this channel, then the // request will fail, and be aborted. - reservation, err := f.cfg.Wallet.InitChannelReservation( - capacity, localAmt, msg.pushAmt, commitFeePerKw, - msg.fundingFeePerKw, peerKey, msg.peer.Address(), - &msg.chainHash, channelFlags, - ) + req := &lnwallet.InitFundingReserveMsg{ + ChainHash: &msg.chainHash, + NodeID: peerKey, + NodeAddr: msg.peer.Address(), + FundingAmount: localAmt, + Capacity: capacity, + CommitFeePerKw: commitFeePerKw, + FundingFeePerKw: msg.fundingFeePerKw, + PushMSat: msg.pushAmt, + Flags: channelFlags, + } + + reservation, err := f.cfg.Wallet.InitChannelReservation(req) if err != nil { msg.err <- err return diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index d19b59ee..d2ded0f3 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -294,10 +294,18 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to query fee estimator: %v", err) } - aliceChanReservation, err := alice.InitChannelReservation( - fundingAmount*2, fundingAmount, 0, feePerKw, feePerKw, bobPub, - bobAddr, chainHash, lnwire.FFAnnounceChannel, - ) + aliceReq := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: bobPub, + NodeAddr: bobAddr, + FundingAmount: fundingAmount, + Capacity: fundingAmount * 2, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: 0, + Flags: lnwire.FFAnnounceChannel, + } + aliceChanReservation, err := alice.InitChannelReservation(aliceReq) if err != nil { t.Fatalf("unable to initialize funding reservation: %v", err) } @@ -325,10 +333,18 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, // Bob does the same, generating his own contribution. He then also // receives' Alice's contribution, and consumes that so we can continue // the funding process. - bobChanReservation, err := bob.InitChannelReservation( - fundingAmount*2, fundingAmount, 0, feePerKw, feePerKw, alicePub, - aliceAddr, chainHash, lnwire.FFAnnounceChannel, - ) + bobReq := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: alicePub, + NodeAddr: aliceAddr, + FundingAmount: fundingAmount, + Capacity: fundingAmount * 2, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: 0, + Flags: lnwire.FFAnnounceChannel, + } + bobChanReservation, err := bob.InitChannelReservation(bobReq) if err != nil { t.Fatalf("bob unable to init channel reservation: %v", err) } @@ -481,11 +497,18 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to query fee estimator: %v", err) } - _, err = alice.InitChannelReservation( - fundingAmount, fundingAmount, 0, feePerKw, feePerKw, bobPub, - bobAddr, chainHash, lnwire.FFAnnounceChannel, - ) - if err != nil { + req := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: bobPub, + NodeAddr: bobAddr, + FundingAmount: fundingAmount, + Capacity: fundingAmount, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: 0, + Flags: lnwire.FFAnnounceChannel, + } + if _, err := alice.InitChannelReservation(req); err != nil { t.Fatalf("unable to initialize funding reservation 1: %v", err) } @@ -496,10 +519,18 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to create amt: %v", err) } - failedReservation, err := alice.InitChannelReservation( - amt, amt, 0, feePerKw, feePerKw, bobPub, bobAddr, chainHash, - lnwire.FFAnnounceChannel, - ) + failedReq := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: bobPub, + NodeAddr: bobAddr, + FundingAmount: amt, + Capacity: amt, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: 0, + Flags: lnwire.FFAnnounceChannel, + } + failedReservation, err := alice.InitChannelReservation(failedReq) if err == nil { t.Fatalf("not error returned, should fail on coin selection") } @@ -524,19 +555,24 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to create amt: %v", err) } - chanReservation, err := alice.InitChannelReservation( - fundingAmount, fundingAmount, 0, feePerKw, feePerKw, bobPub, - bobAddr, chainHash, lnwire.FFAnnounceChannel, - ) + req := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: bobPub, + NodeAddr: bobAddr, + FundingAmount: fundingAmount, + Capacity: fundingAmount, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: 0, + Flags: lnwire.FFAnnounceChannel, + } + chanReservation, err := alice.InitChannelReservation(req) if err != nil { t.Fatalf("unable to initialize funding reservation: %v", err) } // Attempt to create another channel with 44 BTC, this should fail. - _, err = alice.InitChannelReservation( - fundingAmount, fundingAmount, 0, feePerKw, feePerKw, bobPub, - bobAddr, chainHash, lnwire.FFAnnounceChannel, - ) + _, err = alice.InitChannelReservation(req) if _, ok := err.(*lnwallet.ErrInsufficientFunds); !ok { t.Fatalf("coin selection succeeded should have insufficient funds: %v", err) @@ -565,10 +601,7 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness, // attempting coin selection. // Request to fund a new channel should now succeed. - _, err = alice.InitChannelReservation(fundingAmount, fundingAmount, - 0, feePerKw, feePerKw, bobPub, bobAddr, chainHash, - lnwire.FFAnnounceChannel) - if err != nil { + if _, err := alice.InitChannelReservation(req); err != nil { t.Fatalf("unable to initialize funding reservation: %v", err) } } @@ -612,10 +645,18 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness, feePerKw := lnwallet.SatPerKWeight( numBTC * numBTC * btcutil.SatoshiPerBitcoin, ) - _, err = alice.InitChannelReservation( - fundingAmount, fundingAmount, 0, feePerKw, feePerKw, bobPub, - bobAddr, chainHash, lnwire.FFAnnounceChannel, - ) + req := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: bobPub, + NodeAddr: bobAddr, + FundingAmount: fundingAmount, + Capacity: fundingAmount, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: 0, + Flags: lnwire.FFAnnounceChannel, + } + _, err = alice.InitChannelReservation(req) switch { case err == nil: t.Fatalf("initialization should have failed due to " + @@ -685,10 +726,18 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to query fee estimator: %v", err) } - aliceChanReservation, err := alice.InitChannelReservation( - fundingAmt, fundingAmt, pushAmt, feePerKw, feePerKw, bobPub, - bobAddr, chainHash, lnwire.FFAnnounceChannel, - ) + aliceReq := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: bobPub, + NodeAddr: bobAddr, + FundingAmount: fundingAmt, + Capacity: fundingAmt, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: pushAmt, + Flags: lnwire.FFAnnounceChannel, + } + aliceChanReservation, err := alice.InitChannelReservation(aliceReq) if err != nil { t.Fatalf("unable to init channel reservation: %v", err) } @@ -716,10 +765,18 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, // Next, Bob receives the initial request, generates a corresponding // reservation initiation, then consume Alice's contribution. - bobChanReservation, err := bob.InitChannelReservation( - fundingAmt, 0, pushAmt, feePerKw, feePerKw, alicePub, aliceAddr, - chainHash, lnwire.FFAnnounceChannel, - ) + bobReq := &lnwallet.InitFundingReserveMsg{ + ChainHash: chainHash, + NodeID: alicePub, + NodeAddr: aliceAddr, + FundingAmount: 0, + Capacity: fundingAmt, + CommitFeePerKw: feePerKw, + FundingFeePerKw: feePerKw, + PushMSat: pushAmt, + Flags: lnwire.FFAnnounceChannel, + } + bobChanReservation, err := bob.InitChannelReservation(bobReq) if err != nil { t.Fatalf("unable to create bob reservation: %v", err) } diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index d31b2bb1..cb03a16d 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -3,24 +3,24 @@ package lnwallet import ( "bytes" "crypto/sha256" + "errors" "fmt" "net" "sync" "sync/atomic" "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcutil/txsort" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/txsort" "github.com/lightningnetwork/lnd/shachain" ) @@ -412,29 +412,18 @@ out: // transaction, and that the signature we record for our version of the // commitment transaction is valid. func (l *LightningWallet) InitChannelReservation( - capacity, ourFundAmt btcutil.Amount, pushMSat lnwire.MilliSatoshi, - commitFeePerKw SatPerKWeight, fundingFeePerKw SatPerKWeight, - theirID *btcec.PublicKey, theirAddr net.Addr, - chainHash *chainhash.Hash, flags lnwire.FundingFlag) (*ChannelReservation, error) { + req *InitFundingReserveMsg) (*ChannelReservation, error) { - errChan := make(chan error, 1) - respChan := make(chan *ChannelReservation, 1) + req.resp = make(chan *ChannelReservation, 1) + req.err = make(chan error, 1) - l.msgChan <- &initFundingReserveMsg{ - chainHash: chainHash, - nodeID: theirID, - nodeAddr: theirAddr, - fundingAmount: ourFundAmt, - capacity: capacity, - commitFeePerKw: commitFeePerKw, - fundingFeePerKw: fundingFeePerKw, - pushMSat: pushMSat, - flags: flags, - err: errChan, - resp: respChan, + select { + case l.msgChan <- req: + case <-l.quit: + return nil, errors.New("wallet shutting down") } - return <-respChan, <-errChan + return <-req.resp, <-req.err } // handleFundingReserveRequest processes a message intending to create, and From 5c414bb1d0bdd00da454a157fe97429a03088beb Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:17:16 -0700 Subject: [PATCH 03/16] multi: make OpenChannel take in openChanReq --- pilot.go | 16 +++++++++-- rpcserver.go | 33 +++++++++++++++------- server.go | 78 ++++++++++++++++------------------------------------ 3 files changed, 59 insertions(+), 68 deletions(-) diff --git a/pilot.go b/pilot.go index 86093a6f..86c8ef5a 100644 --- a/pilot.go +++ b/pilot.go @@ -37,10 +37,20 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey, // TODO(halseth): make configurable? minHtlc := lnwire.NewMSatFromSatoshis(1) - updateStream, errChan := c.server.OpenChannel( - target, amt, 0, minHtlc, feePerKw, c.private, 0, - ) + // Construct the open channel request and send it to the server to begin + // the funding workflow. + req := &openChanReq{ + targetPubkey: target, + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: amt, + pushAmt: 0, + minHtlc: minHtlc, + fundingFeePerKw: feePerKw, + private: c.private, + remoteCsvDelay: 0, + } + updateStream, errChan := c.server.OpenChannel(req) select { case err := <-errChan: // If we were not able to actually open a channel to the peer diff --git a/rpcserver.go b/rpcserver.go index 87d78bb3..6f4e8e6c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -813,11 +813,18 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, // Instruct the server to trigger the necessary events to attempt to // open a new channel. A stream is returned in place, this stream will // be used to consume updates of the state of the pending channel. - updateChan, errChan := r.server.OpenChannel( - nodePubKey, localFundingAmt, - lnwire.NewMSatFromSatoshis(remoteInitialBalance), - minHtlc, feeRate, in.Private, remoteCsvDelay, - ) + req := &openChanReq{ + targetPubkey: nodePubKey, + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: localFundingAmt, + pushAmt: lnwire.NewMSatFromSatoshis(remoteInitialBalance), + minHtlc: minHtlc, + fundingFeePerKw: feeRate, + private: in.Private, + remoteCsvDelay: remoteCsvDelay, + } + + updateChan, errChan := r.server.OpenChannel(req) var outpoint wire.OutPoint out: @@ -941,12 +948,18 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, rpcsLog.Tracef("[openchannel] target sat/kw for funding tx: %v", int64(feeRate)) - updateChan, errChan := r.server.OpenChannel( - nodepubKey, localFundingAmt, - lnwire.NewMSatFromSatoshis(remoteInitialBalance), - minHtlc, feeRate, in.Private, remoteCsvDelay, - ) + req := &openChanReq{ + targetPubkey: nodepubKey, + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: localFundingAmt, + pushAmt: lnwire.NewMSatFromSatoshis(remoteInitialBalance), + minHtlc: minHtlc, + fundingFeePerKw: feeRate, + private: in.Private, + remoteCsvDelay: remoteCsvDelay, + } + updateChan, errChan := r.server.OpenChannel(req) select { // If an error occurs them immediately return the error to the client. case err := <-errChan: diff --git a/server.go b/server.go index bf770092..80443f41 100644 --- a/server.go +++ b/server.go @@ -2735,77 +2735,45 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error { // peer identified by nodeKey with the passed channel funding parameters. // // NOTE: This function is safe for concurrent access. -func (s *server) OpenChannel(nodeKey *btcec.PublicKey, - localAmt btcutil.Amount, pushAmt, minHtlc lnwire.MilliSatoshi, - fundingFeePerKw lnwallet.SatPerKWeight, private bool, - remoteCsvDelay uint16) (chan *lnrpc.OpenStatusUpdate, chan error) { +func (s *server) OpenChannel( + req *openChanReq) (chan *lnrpc.OpenStatusUpdate, chan error) { - // The updateChan will have a buffer of 2, since we expect a - // ChanPending + a ChanOpen update, and we want to make sure the - // funding process is not blocked if the caller is not reading the - // updates. - updateChan := make(chan *lnrpc.OpenStatusUpdate, 2) - errChan := make(chan error, 1) - - var ( - targetPeer *peer - pubKeyBytes []byte - err error - ) - - // If the user is targeting the peer by public key, then we'll need to - // convert that into a string for our map. Otherwise, we expect them to - // target by peer ID instead. - if nodeKey != nil { - pubKeyBytes = nodeKey.SerializeCompressed() - } + // The updateChan will have a buffer of 2, since we expect a ChanPending + // + a ChanOpen update, and we want to make sure the funding process is + // not blocked if the caller is not reading the updates. + req.updates = make(chan *lnrpc.OpenStatusUpdate, 2) + req.err = make(chan error, 1) // First attempt to locate the target peer to open a channel with, if // we're unable to locate the peer then this request will fail. + pubKeyBytes := req.targetPubkey.SerializeCompressed() s.mu.RLock() - if peer, ok := s.peersByPub[string(pubKeyBytes)]; ok { - targetPeer = peer + peer, ok := s.peersByPub[string(pubKeyBytes)] + if !ok { + req.err <- fmt.Errorf("peer %x is not online", pubKeyBytes) + return req.updates, req.err } s.mu.RUnlock() - if targetPeer == nil { - errChan <- fmt.Errorf("peer is not connected NodeKey(%x)", pubKeyBytes) - return updateChan, errChan - } - // If the fee rate wasn't specified, then we'll use a default // confirmation target. - if fundingFeePerKw == 0 { + if req.fundingFeePerKw == 0 { estimator := s.cc.feeEstimator - fundingFeePerKw, err = estimator.EstimateFeePerKW(6) + feeRate, err := estimator.EstimateFeePerKW(6) if err != nil { - errChan <- err - return updateChan, errChan + req.err <- err + return req.updates, req.err } + req.fundingFeePerKw = feeRate } - // Spawn a goroutine to send the funding workflow request to the - // funding manager. This allows the server to continue handling queries - // instead of blocking on this request which is exported as a - // synchronous request to the outside world. - req := &openChanReq{ - targetPubkey: nodeKey, - chainHash: *activeNetParams.GenesisHash, - localFundingAmt: localAmt, - fundingFeePerKw: fundingFeePerKw, - pushAmt: pushAmt, - private: private, - minHtlc: minHtlc, - remoteCsvDelay: remoteCsvDelay, - updates: updateChan, - err: errChan, - } + // Spawn a goroutine to send the funding workflow request to the funding + // manager. This allows the server to continue handling queries instead + // of blocking on this request which is exported as a synchronous + // request to the outside world. + go s.fundingMgr.initFundingWorkflow(peer, req) - // TODO(roasbeef): pass in chan that's closed if/when funding succeeds - // so can track as persistent peer? - go s.fundingMgr.initFundingWorkflow(targetPeer, req) - - return updateChan, errChan + return req.updates, req.err } // Peers returns a slice of all active peers. From 2908f672c34b6c9589b1c0a857d66b68b9596839 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:26:01 -0700 Subject: [PATCH 04/16] server: add minConfs field to openChanReq --- server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server.go b/server.go index 80443f41..5b1e704b 100644 --- a/server.go +++ b/server.go @@ -2597,6 +2597,10 @@ type openChanReq struct { remoteCsvDelay uint16 + // minConfs indicates the minimum number of confirmations that each + // output selected to fund the channel should satisfy. + minConfs int32 + // TODO(roasbeef): add ability to specify channel constraints as well updates chan *lnrpc.OpenStatusUpdate From 85729208fbdc96c9e08985fb4c3cb83c578f5396 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:37:32 -0700 Subject: [PATCH 05/16] lnrpc: add MinConfs field to OpenChannelRequest proto --- lnrpc/rpc.pb.go | 791 +++++++++++++++++++++-------------------- lnrpc/rpc.proto | 6 +- lnrpc/rpc.swagger.json | 5 + 3 files changed, 409 insertions(+), 393 deletions(-) diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index 58df3965..bf94f027 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -2214,6 +2214,8 @@ type OpenChannelRequest struct { MinHtlcMsat int64 `protobuf:"varint,9,opt,name=min_htlc_msat" json:"min_htlc_msat,omitempty"` // / The delay we require on the remote's commitment transaction. If this is not set, it will be scaled automatically with the channel size. RemoteCsvDelay uint32 `protobuf:"varint,10,opt,name=remote_csv_delay" json:"remote_csv_delay,omitempty"` + // / The minimum number of confirmations each one of your outputs used for the funding transaction must satisfy. + MinConfs int32 `protobuf:"varint,11,opt,name=min_confs" json:"min_confs,omitempty"` } func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} } @@ -2284,6 +2286,13 @@ func (m *OpenChannelRequest) GetRemoteCsvDelay() uint32 { return 0 } +func (m *OpenChannelRequest) GetMinConfs() int32 { + if m != nil { + return m.MinConfs + } + return 0 +} + type OpenStatusUpdate struct { // Types that are valid to be assigned to Update: // *OpenStatusUpdate_ChanPending @@ -7093,395 +7102,395 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 6227 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0xcd, 0x6f, 0x1c, 0xc9, - 0x75, 0x57, 0x0f, 0x87, 0x1f, 0xf3, 0x66, 0x48, 0x0e, 0x8b, 0x14, 0x35, 0x9a, 0xdd, 0xd5, 0x6a, - 0xdb, 0xc2, 0x4a, 0x66, 0x36, 0x92, 0x96, 0xb6, 0x37, 0xeb, 0xdd, 0xc4, 0x8e, 0x44, 0x52, 0xa2, - 0x6c, 0xae, 0x44, 0x37, 0x25, 0x2b, 0xb1, 0x13, 0x8c, 0x9b, 0x33, 0xc5, 0x61, 0x5b, 0x33, 0xdd, - 0xed, 0xee, 0x1e, 0x52, 0xe3, 0x8d, 0x00, 0xe7, 0x03, 0x39, 0xc5, 0x08, 0x82, 0xe4, 0xe2, 0x00, - 0x41, 0x00, 0x07, 0x08, 0x9c, 0x3f, 0x20, 0xb9, 0x38, 0x01, 0x72, 0xc8, 0xc5, 0x01, 0x82, 0x1c, - 0x7c, 0x32, 0x72, 0x4c, 0x72, 0x48, 0x82, 0x5c, 0x02, 0xe4, 0x1a, 0x04, 0xef, 0xd5, 0x47, 0x57, - 0x75, 0x37, 0x45, 0xf9, 0x23, 0xb9, 0x4d, 0xfd, 0xea, 0x75, 0x7d, 0xbe, 0xaf, 0x7a, 0xf5, 0x6a, - 0xa0, 0x91, 0xc4, 0xfd, 0x9b, 0x71, 0x12, 0x65, 0x11, 0x9b, 0x1d, 0x85, 0x49, 0xdc, 0xef, 0xbe, - 0x3e, 0x8c, 0xa2, 0xe1, 0x88, 0xdf, 0xf2, 0xe3, 0xe0, 0x96, 0x1f, 0x86, 0x51, 0xe6, 0x67, 0x41, - 0x14, 0xa6, 0x82, 0xc8, 0xfd, 0x1a, 0x2c, 0xdd, 0xe7, 0xe1, 0x01, 0xe7, 0x03, 0x8f, 0x7f, 0x63, - 0xc2, 0xd3, 0x8c, 0xfd, 0x1c, 0xac, 0xf8, 0xfc, 0x9b, 0x9c, 0x0f, 0x7a, 0xb1, 0x9f, 0xa6, 0xf1, - 0x71, 0xe2, 0xa7, 0xbc, 0xe3, 0x5c, 0x75, 0x6e, 0xb4, 0xbc, 0xb6, 0xa8, 0xd8, 0xd7, 0x38, 0x7b, - 0x0b, 0x5a, 0x29, 0x92, 0xf2, 0x30, 0x4b, 0xa2, 0x78, 0xda, 0xa9, 0x11, 0x5d, 0x13, 0xb1, 0x1d, - 0x01, 0xb9, 0x23, 0x58, 0xd6, 0x3d, 0xa4, 0x71, 0x14, 0xa6, 0x9c, 0xdd, 0x86, 0xb5, 0x7e, 0x10, - 0x1f, 0xf3, 0xa4, 0x47, 0x1f, 0x8f, 0x43, 0x3e, 0x8e, 0xc2, 0xa0, 0xdf, 0x71, 0xae, 0xce, 0xdc, - 0x68, 0x78, 0x4c, 0xd4, 0xe1, 0x17, 0x1f, 0xc9, 0x1a, 0x76, 0x1d, 0x96, 0x79, 0x28, 0x70, 0x3e, - 0xa0, 0xaf, 0x64, 0x57, 0x4b, 0x39, 0x8c, 0x1f, 0xb8, 0x7f, 0xe7, 0xc0, 0xca, 0x83, 0x30, 0xc8, - 0x9e, 0xfa, 0xa3, 0x11, 0xcf, 0xd4, 0x9c, 0xae, 0xc3, 0xf2, 0x29, 0x01, 0x34, 0xa7, 0xd3, 0x28, - 0x19, 0xc8, 0x19, 0x2d, 0x09, 0x78, 0x5f, 0xa2, 0x67, 0x8e, 0xac, 0x76, 0xe6, 0xc8, 0x2a, 0x97, - 0x6b, 0xe6, 0x8c, 0xe5, 0xba, 0x0e, 0xcb, 0x09, 0xef, 0x47, 0x27, 0x3c, 0x99, 0xf6, 0x4e, 0x83, - 0x70, 0x10, 0x9d, 0x76, 0xea, 0x57, 0x9d, 0x1b, 0xb3, 0xde, 0x92, 0x82, 0x9f, 0x12, 0xea, 0xae, - 0x01, 0x33, 0x67, 0x21, 0xd6, 0xcd, 0x1d, 0xc2, 0xea, 0x93, 0x70, 0x14, 0xf5, 0x9f, 0xfd, 0x84, - 0xb3, 0xab, 0xe8, 0xbe, 0x56, 0xd9, 0xfd, 0x3a, 0xac, 0xd9, 0x1d, 0xc9, 0x01, 0x70, 0xb8, 0xb8, - 0x75, 0xec, 0x87, 0x43, 0xae, 0x9a, 0x54, 0x43, 0xf8, 0x24, 0xb4, 0xfb, 0x93, 0x24, 0xe1, 0x61, - 0x69, 0x0c, 0xcb, 0x12, 0xd7, 0x83, 0x78, 0x0b, 0x5a, 0x21, 0x3f, 0xcd, 0xc9, 0x24, 0xcb, 0x84, - 0xfc, 0x54, 0x91, 0xb8, 0x1d, 0x58, 0x2f, 0x76, 0x23, 0x07, 0xf0, 0x9d, 0x1a, 0x34, 0x1f, 0x27, - 0x7e, 0x98, 0xfa, 0x7d, 0xe4, 0x62, 0xd6, 0x81, 0xf9, 0xec, 0x79, 0xef, 0xd8, 0x4f, 0x8f, 0xa9, - 0xbb, 0x86, 0xa7, 0x8a, 0x6c, 0x1d, 0xe6, 0xfc, 0x71, 0x34, 0x09, 0x33, 0xea, 0x60, 0xc6, 0x93, - 0x25, 0xf6, 0x0e, 0xac, 0x84, 0x93, 0x71, 0xaf, 0x1f, 0x85, 0x47, 0x41, 0x32, 0x16, 0xb2, 0x40, - 0xfb, 0x35, 0xeb, 0x95, 0x2b, 0xd8, 0x15, 0x80, 0x43, 0x5c, 0x07, 0xd1, 0x45, 0x9d, 0xba, 0x30, - 0x10, 0xe6, 0x42, 0x4b, 0x96, 0x78, 0x30, 0x3c, 0xce, 0x3a, 0xb3, 0xd4, 0x90, 0x85, 0x61, 0x1b, - 0x59, 0x30, 0xe6, 0xbd, 0x34, 0xf3, 0xc7, 0x71, 0x67, 0x8e, 0x46, 0x63, 0x20, 0x54, 0x1f, 0x65, - 0xfe, 0xa8, 0x77, 0xc4, 0x79, 0xda, 0x99, 0x97, 0xf5, 0x1a, 0x61, 0x6f, 0xc3, 0xd2, 0x80, 0xa7, - 0x59, 0xcf, 0x1f, 0x0c, 0x12, 0x9e, 0xa6, 0x3c, 0xed, 0x2c, 0x10, 0x37, 0x16, 0x50, 0x5c, 0xb5, - 0xfb, 0x3c, 0x33, 0x56, 0x27, 0x95, 0xbb, 0xe3, 0xee, 0x01, 0x33, 0xe0, 0x6d, 0x9e, 0xf9, 0xc1, - 0x28, 0x65, 0xef, 0x41, 0x2b, 0x33, 0x88, 0x49, 0xfa, 0x9a, 0x9b, 0xec, 0x26, 0xa9, 0x8d, 0x9b, - 0xc6, 0x07, 0x9e, 0x45, 0xe7, 0xde, 0x87, 0x85, 0x7b, 0x9c, 0xef, 0x05, 0xe3, 0x20, 0x63, 0xeb, - 0x30, 0x7b, 0x14, 0x3c, 0xe7, 0x62, 0xb3, 0x67, 0x76, 0x2f, 0x78, 0xa2, 0xc8, 0xba, 0x30, 0x1f, - 0xf3, 0xa4, 0xcf, 0xd5, 0xf2, 0xef, 0x5e, 0xf0, 0x14, 0x70, 0x77, 0x1e, 0x66, 0x47, 0xf8, 0xb1, - 0xfb, 0xbd, 0x1a, 0x34, 0x0f, 0x78, 0xa8, 0x99, 0x88, 0x41, 0x1d, 0xa7, 0x24, 0x19, 0x87, 0x7e, - 0xb3, 0x37, 0xa1, 0x49, 0xd3, 0x4c, 0xb3, 0x24, 0x08, 0x87, 0xd4, 0x58, 0xc3, 0x03, 0x84, 0x0e, - 0x08, 0x61, 0x6d, 0x98, 0xf1, 0xc7, 0x19, 0xed, 0xe0, 0x8c, 0x87, 0x3f, 0x91, 0xc1, 0x62, 0x7f, - 0x3a, 0x46, 0x5e, 0xd4, 0xbb, 0xd6, 0xf2, 0x9a, 0x12, 0xdb, 0xc5, 0x6d, 0xbb, 0x09, 0xab, 0x26, - 0x89, 0x6a, 0x7d, 0x96, 0x5a, 0x5f, 0x31, 0x28, 0x65, 0x27, 0xd7, 0x61, 0x59, 0xd1, 0x27, 0x62, - 0xb0, 0xb4, 0x8f, 0x0d, 0x6f, 0x49, 0xc2, 0x6a, 0x0a, 0x37, 0xa0, 0x7d, 0x14, 0x84, 0xfe, 0xa8, - 0xd7, 0x1f, 0x65, 0x27, 0xbd, 0x01, 0x1f, 0x65, 0x3e, 0xed, 0xe8, 0xac, 0xb7, 0x44, 0xf8, 0xd6, - 0x28, 0x3b, 0xd9, 0x46, 0x94, 0xbd, 0x03, 0x8d, 0x23, 0xce, 0x7b, 0xb4, 0x12, 0x9d, 0x85, 0xab, - 0xce, 0x8d, 0xe6, 0xe6, 0xb2, 0x5c, 0x7a, 0xb5, 0xba, 0xde, 0xc2, 0x91, 0xfc, 0xe5, 0xfe, 0x91, - 0x03, 0x2d, 0xb1, 0x54, 0x52, 0x85, 0x5e, 0x83, 0x45, 0x35, 0x22, 0x9e, 0x24, 0x51, 0x22, 0xd9, - 0xdf, 0x06, 0xd9, 0x06, 0xb4, 0x15, 0x10, 0x27, 0x3c, 0x18, 0xfb, 0x43, 0x2e, 0xe5, 0xad, 0x84, - 0xb3, 0xcd, 0xbc, 0xc5, 0x24, 0x9a, 0x64, 0x42, 0x89, 0x35, 0x37, 0x5b, 0x72, 0x50, 0x1e, 0x62, - 0x9e, 0x4d, 0xe2, 0x7e, 0xdb, 0x01, 0x86, 0xc3, 0x7a, 0x1c, 0x89, 0x6a, 0xb9, 0x0a, 0xc5, 0x1d, - 0x70, 0x5e, 0x79, 0x07, 0x6a, 0x67, 0xed, 0xc0, 0x35, 0x98, 0xa3, 0x2e, 0x51, 0x56, 0x67, 0x4a, - 0xc3, 0x92, 0x75, 0xee, 0x77, 0x1d, 0x68, 0xa1, 0xe6, 0x08, 0xf9, 0x68, 0x3f, 0x0a, 0xc2, 0x8c, - 0xdd, 0x06, 0x76, 0x34, 0x09, 0x07, 0x41, 0x38, 0xec, 0x65, 0xcf, 0x83, 0x41, 0xef, 0x70, 0x8a, - 0x4d, 0xd0, 0x78, 0x76, 0x2f, 0x78, 0x15, 0x75, 0xec, 0x1d, 0x68, 0x5b, 0x68, 0x9a, 0x25, 0x62, - 0x54, 0xbb, 0x17, 0xbc, 0x52, 0x0d, 0xca, 0x7f, 0x34, 0xc9, 0xe2, 0x49, 0xd6, 0x0b, 0xc2, 0x01, - 0x7f, 0x4e, 0x6b, 0xb6, 0xe8, 0x59, 0xd8, 0xdd, 0x25, 0x68, 0x99, 0xdf, 0xb9, 0x9f, 0x83, 0xf6, - 0x1e, 0x2a, 0x86, 0x30, 0x08, 0x87, 0x77, 0x84, 0xf4, 0xa2, 0xb6, 0x8a, 0x27, 0x87, 0xcf, 0xf8, - 0x54, 0xee, 0xa3, 0x2c, 0xa1, 0x48, 0x1c, 0x47, 0x69, 0x26, 0xd7, 0x85, 0x7e, 0xbb, 0xff, 0xec, - 0xc0, 0x32, 0x2e, 0xfa, 0x47, 0x7e, 0x38, 0x55, 0x2b, 0xbe, 0x07, 0x2d, 0x6c, 0xea, 0x71, 0x74, - 0x47, 0xe8, 0x3c, 0x21, 0xcb, 0x37, 0xe4, 0x22, 0x15, 0xa8, 0x6f, 0x9a, 0xa4, 0x68, 0xa6, 0xa7, - 0x9e, 0xf5, 0x35, 0x0a, 0x5d, 0xe6, 0x27, 0x43, 0x9e, 0x91, 0x36, 0x94, 0xda, 0x11, 0x04, 0xb4, - 0x15, 0x85, 0x47, 0xec, 0x2a, 0xb4, 0x52, 0x3f, 0xeb, 0xc5, 0x3c, 0xa1, 0x55, 0x23, 0xc1, 0x99, - 0xf1, 0x20, 0xf5, 0xb3, 0x7d, 0x9e, 0xdc, 0x9d, 0x66, 0xbc, 0xfb, 0x79, 0x58, 0x29, 0xf5, 0x82, - 0xb2, 0x9a, 0x4f, 0x11, 0x7f, 0xb2, 0x35, 0x98, 0x3d, 0xf1, 0x47, 0x13, 0x2e, 0x95, 0xb4, 0x28, - 0x7c, 0x50, 0x7b, 0xdf, 0x71, 0xdf, 0x86, 0x76, 0x3e, 0x6c, 0xc9, 0xf4, 0x0c, 0xea, 0xb8, 0x82, - 0xb2, 0x01, 0xfa, 0xed, 0xfe, 0xa6, 0x23, 0x08, 0xb7, 0xa2, 0x40, 0x2b, 0x3c, 0x24, 0x44, 0xbd, - 0xa8, 0x08, 0xf1, 0xf7, 0x99, 0x06, 0xe1, 0xa7, 0x9f, 0xac, 0x7b, 0x1d, 0x56, 0x8c, 0x21, 0xbc, - 0x64, 0xb0, 0xdf, 0x76, 0x60, 0xe5, 0x21, 0x3f, 0x95, 0xbb, 0xae, 0x46, 0xfb, 0x3e, 0xd4, 0xb3, - 0x69, 0x2c, 0x9c, 0xac, 0xa5, 0xcd, 0x6b, 0x72, 0xd3, 0x4a, 0x74, 0x37, 0x65, 0xf1, 0xf1, 0x34, - 0xe6, 0x1e, 0x7d, 0xe1, 0x7e, 0x0e, 0x9a, 0x06, 0xc8, 0x2e, 0xc1, 0xea, 0xd3, 0x07, 0x8f, 0x1f, - 0xee, 0x1c, 0x1c, 0xf4, 0xf6, 0x9f, 0xdc, 0xfd, 0xe2, 0xce, 0xaf, 0xf6, 0x76, 0xef, 0x1c, 0xec, - 0xb6, 0x2f, 0xb0, 0x75, 0x60, 0x0f, 0x77, 0x0e, 0x1e, 0xef, 0x6c, 0x5b, 0xb8, 0xe3, 0x76, 0xa1, - 0xf3, 0x90, 0x9f, 0x3e, 0x0d, 0xb2, 0x90, 0xa7, 0xa9, 0xdd, 0x9b, 0x7b, 0x13, 0x98, 0x39, 0x04, - 0x39, 0xab, 0x0e, 0xcc, 0x4b, 0x8b, 0xa3, 0x0c, 0xae, 0x2c, 0xba, 0x6f, 0x03, 0x3b, 0x08, 0x86, - 0xe1, 0x47, 0x3c, 0x4d, 0xfd, 0xa1, 0x56, 0x05, 0x6d, 0x98, 0x19, 0xa7, 0x43, 0xa9, 0x01, 0xf0, - 0xa7, 0xfb, 0x29, 0x58, 0xb5, 0xe8, 0x64, 0xc3, 0xaf, 0x43, 0x23, 0x0d, 0x86, 0xa1, 0x9f, 0x4d, - 0x12, 0x2e, 0x9b, 0xce, 0x01, 0xf7, 0x1e, 0xac, 0x7d, 0x99, 0x27, 0xc1, 0xd1, 0xf4, 0xbc, 0xe6, - 0xed, 0x76, 0x6a, 0xc5, 0x76, 0x76, 0xe0, 0x62, 0xa1, 0x1d, 0xd9, 0xbd, 0x60, 0x44, 0xb9, 0x5d, - 0x0b, 0x9e, 0x28, 0x18, 0x62, 0x59, 0x33, 0xc5, 0xd2, 0x7d, 0x02, 0x6c, 0x2b, 0x0a, 0x43, 0xde, - 0xcf, 0xf6, 0x39, 0x4f, 0x72, 0xcf, 0x39, 0xe7, 0xba, 0xe6, 0xe6, 0x25, 0xb9, 0x8f, 0x45, 0x59, - 0x97, 0xec, 0xc8, 0xa0, 0x1e, 0xf3, 0x64, 0x4c, 0x0d, 0x2f, 0x78, 0xf4, 0xdb, 0xbd, 0x08, 0xab, - 0x56, 0xb3, 0xd2, 0xe9, 0x79, 0x17, 0x2e, 0x6e, 0x07, 0x69, 0xbf, 0xdc, 0x61, 0x07, 0xe6, 0xe3, - 0xc9, 0x61, 0x2f, 0x97, 0x29, 0x55, 0x44, 0x5f, 0xa0, 0xf8, 0x89, 0x6c, 0xec, 0x77, 0x1d, 0xa8, - 0xef, 0x3e, 0xde, 0xdb, 0x62, 0x5d, 0x58, 0x08, 0xc2, 0x7e, 0x34, 0x46, 0xb5, 0x2b, 0x26, 0xad, - 0xcb, 0x67, 0xca, 0xca, 0xeb, 0xd0, 0x20, 0x6d, 0x8d, 0xee, 0x8d, 0x74, 0x72, 0x73, 0x00, 0x5d, - 0x2b, 0xfe, 0x3c, 0x0e, 0x12, 0xf2, 0x9d, 0x94, 0x47, 0x54, 0x27, 0x8d, 0x58, 0xae, 0x70, 0xff, - 0xa7, 0x0e, 0xf3, 0x52, 0x57, 0x53, 0x7f, 0xfd, 0x2c, 0x38, 0xe1, 0x72, 0x24, 0xb2, 0x84, 0x56, - 0x2e, 0xe1, 0xe3, 0x28, 0xe3, 0x3d, 0x6b, 0x1b, 0x6c, 0x10, 0xa9, 0xfa, 0xa2, 0xa1, 0x5e, 0x8c, - 0x5a, 0x9f, 0x46, 0xd6, 0xf0, 0x6c, 0x10, 0x17, 0x0b, 0x81, 0x5e, 0x30, 0xa0, 0x31, 0xd5, 0x3d, - 0x55, 0xc4, 0x95, 0xe8, 0xfb, 0xb1, 0xdf, 0x0f, 0xb2, 0xa9, 0x14, 0x6e, 0x5d, 0xc6, 0xb6, 0x47, - 0x51, 0xdf, 0x1f, 0xf5, 0x0e, 0xfd, 0x91, 0x1f, 0xf6, 0xb9, 0xf4, 0xdf, 0x6c, 0x10, 0x5d, 0x34, - 0x39, 0x24, 0x45, 0x26, 0xdc, 0xb8, 0x02, 0x8a, 0xae, 0x5e, 0x3f, 0x1a, 0x8f, 0x83, 0x0c, 0x3d, - 0x3b, 0xb2, 0xfa, 0x33, 0x9e, 0x81, 0xd0, 0x4c, 0x44, 0xe9, 0x54, 0xac, 0x5e, 0x43, 0xf4, 0x66, - 0x81, 0xd8, 0x0a, 0xba, 0x0e, 0xa8, 0x90, 0x9e, 0x9d, 0x76, 0x40, 0xb4, 0x92, 0x23, 0xb8, 0x0f, - 0x93, 0x30, 0xe5, 0x59, 0x36, 0xe2, 0x03, 0x3d, 0xa0, 0x26, 0x91, 0x95, 0x2b, 0xd8, 0x6d, 0x58, - 0x15, 0xce, 0x66, 0xea, 0x67, 0x51, 0x7a, 0x1c, 0xa4, 0xbd, 0x14, 0xdd, 0xb6, 0x16, 0xd1, 0x57, - 0x55, 0xb1, 0xf7, 0xe1, 0x52, 0x01, 0x4e, 0x78, 0x9f, 0x07, 0x27, 0x7c, 0xd0, 0x59, 0xa4, 0xaf, - 0xce, 0xaa, 0x66, 0x57, 0xa1, 0x89, 0x3e, 0xf6, 0x24, 0x1e, 0xf8, 0x68, 0x87, 0x97, 0x68, 0x1f, - 0x4c, 0x88, 0xbd, 0x0b, 0x8b, 0x31, 0x17, 0xc6, 0xf2, 0x38, 0x1b, 0xf5, 0xd3, 0xce, 0x32, 0x59, - 0xb2, 0xa6, 0x14, 0x26, 0xe4, 0x5c, 0xcf, 0xa6, 0x40, 0xa6, 0xec, 0xa7, 0xe4, 0x6c, 0xf9, 0xd3, - 0x4e, 0x9b, 0xd8, 0x2d, 0x07, 0x48, 0x46, 0x92, 0xe0, 0xc4, 0xcf, 0x78, 0x67, 0x85, 0x78, 0x4b, - 0x15, 0xdd, 0x3f, 0x75, 0x60, 0x75, 0x2f, 0x48, 0x33, 0xc9, 0x84, 0x5a, 0x1d, 0xbf, 0x09, 0x4d, - 0xc1, 0x7e, 0xbd, 0x28, 0x1c, 0x4d, 0x25, 0x47, 0x82, 0x80, 0x1e, 0x85, 0xa3, 0x29, 0xfb, 0x04, - 0x2c, 0x06, 0xa1, 0x49, 0x22, 0x64, 0xb8, 0xa5, 0x40, 0x22, 0x7a, 0x13, 0x9a, 0xf1, 0xe4, 0x70, - 0x14, 0xf4, 0x05, 0xc9, 0x8c, 0x68, 0x45, 0x40, 0x44, 0x80, 0x4e, 0x92, 0x18, 0x89, 0xa0, 0xa8, - 0x13, 0x45, 0x53, 0x62, 0x48, 0xe2, 0xde, 0x85, 0x35, 0x7b, 0x80, 0x52, 0x59, 0x6d, 0xc0, 0x82, - 0xe4, 0xed, 0xb4, 0xd3, 0xa4, 0xf5, 0x59, 0x92, 0xeb, 0x23, 0x49, 0x3d, 0x5d, 0xef, 0xfe, 0x79, - 0x1d, 0x56, 0x25, 0xba, 0x35, 0x8a, 0x52, 0x7e, 0x30, 0x19, 0x8f, 0xfd, 0xa4, 0x42, 0x68, 0x9c, - 0x73, 0x84, 0xa6, 0x66, 0x0b, 0x0d, 0xb2, 0xf2, 0xb1, 0x1f, 0x84, 0xc2, 0xc3, 0x13, 0x12, 0x67, - 0x20, 0xec, 0x06, 0x2c, 0xf7, 0x47, 0x51, 0x2a, 0xbc, 0x1e, 0xf3, 0xf8, 0x54, 0x84, 0xcb, 0x42, - 0x3e, 0x5b, 0x25, 0xe4, 0xa6, 0x90, 0xce, 0x15, 0x84, 0xd4, 0x85, 0x16, 0x36, 0xca, 0x95, 0xce, - 0x99, 0x17, 0x5e, 0x98, 0x89, 0xe1, 0x78, 0x8a, 0x22, 0x21, 0xe4, 0x6f, 0xb9, 0x4a, 0x20, 0xf0, - 0x74, 0x86, 0x3a, 0xcd, 0xa0, 0x6e, 0x48, 0x81, 0x28, 0x57, 0xb1, 0x7b, 0x00, 0xa2, 0x2f, 0x32, - 0xe3, 0x40, 0x66, 0xfc, 0x6d, 0x7b, 0x47, 0xcc, 0xb5, 0xbf, 0x89, 0x85, 0x49, 0xc2, 0xc9, 0x90, - 0x1b, 0x5f, 0xba, 0x1f, 0x43, 0xd3, 0xa8, 0x62, 0x17, 0x61, 0x65, 0xeb, 0xd1, 0xa3, 0xfd, 0x1d, - 0xef, 0xce, 0xe3, 0x07, 0x5f, 0xde, 0xe9, 0x6d, 0xed, 0x3d, 0x3a, 0xd8, 0x69, 0x5f, 0x40, 0x78, - 0xef, 0xd1, 0xd6, 0x9d, 0xbd, 0xde, 0xbd, 0x47, 0xde, 0x96, 0x82, 0x1d, 0xb4, 0xf1, 0xde, 0xce, - 0x47, 0x8f, 0x1e, 0xef, 0x58, 0x78, 0x8d, 0xb5, 0xa1, 0x75, 0xd7, 0xdb, 0xb9, 0xb3, 0xb5, 0x2b, - 0x91, 0x19, 0xb6, 0x06, 0xed, 0x7b, 0x4f, 0x1e, 0x6e, 0x3f, 0x78, 0x78, 0xbf, 0xb7, 0x75, 0xe7, - 0xe1, 0xd6, 0xce, 0xde, 0xce, 0x76, 0xbb, 0xee, 0xfe, 0xad, 0x03, 0x17, 0x69, 0x94, 0x83, 0xa2, - 0x40, 0x5c, 0x85, 0x66, 0x3f, 0x8a, 0x62, 0x8e, 0xfa, 0x5b, 0xab, 0x68, 0x13, 0x42, 0x66, 0x17, - 0x0a, 0xf1, 0x28, 0x4a, 0xfa, 0x5c, 0xca, 0x03, 0x10, 0x74, 0x0f, 0x11, 0x64, 0x76, 0xb9, 0x9d, - 0x82, 0x42, 0x88, 0x43, 0x53, 0x60, 0x82, 0x64, 0x1d, 0xe6, 0x0e, 0x13, 0xee, 0xf7, 0x8f, 0xa5, - 0x24, 0xc8, 0x12, 0xfb, 0x64, 0xee, 0x90, 0xf7, 0x71, 0xb5, 0x47, 0x7c, 0x40, 0x1c, 0xb2, 0xe0, - 0x2d, 0x4b, 0x7c, 0x4b, 0xc2, 0xee, 0x3e, 0xac, 0x17, 0x67, 0x20, 0x25, 0xe6, 0x3d, 0x43, 0x62, - 0x84, 0x6f, 0xdc, 0x3d, 0x7b, 0x7f, 0x0c, 0xe9, 0xf9, 0x77, 0x07, 0xea, 0x68, 0x3e, 0xcf, 0x36, - 0xb5, 0xa6, 0x47, 0x34, 0x63, 0x79, 0x44, 0x14, 0x3c, 0xc0, 0x33, 0x85, 0x50, 0xa8, 0xc2, 0xe8, - 0x18, 0x48, 0x5e, 0x9f, 0xf0, 0xfe, 0x09, 0xcd, 0x49, 0xd7, 0x23, 0x82, 0x2c, 0x8f, 0x8e, 0x27, - 0x7d, 0x2d, 0x59, 0x5e, 0x95, 0x55, 0x1d, 0x7d, 0x39, 0x9f, 0xd7, 0xd1, 0x77, 0x1d, 0x98, 0x0f, - 0xc2, 0xc3, 0x68, 0x12, 0x0e, 0x88, 0xc5, 0x17, 0x3c, 0x55, 0x44, 0x55, 0x19, 0x93, 0xe8, 0x05, - 0x63, 0xc5, 0xd0, 0x39, 0xe0, 0x32, 0x3c, 0x98, 0xa4, 0xe4, 0x2e, 0x68, 0x2f, 0xf0, 0x3d, 0x58, - 0x31, 0x30, 0xb9, 0x9a, 0x6f, 0xc1, 0x6c, 0x8c, 0x80, 0x5c, 0x4a, 0xa5, 0x9c, 0xc9, 0xcf, 0x10, - 0x35, 0x6e, 0x1b, 0x96, 0xee, 0xf3, 0xec, 0x41, 0x78, 0x14, 0xa9, 0x96, 0x7e, 0x34, 0x03, 0xcb, - 0x1a, 0x92, 0x0d, 0xdd, 0x80, 0xe5, 0x60, 0xc0, 0xc3, 0x2c, 0xc8, 0xa6, 0x3d, 0xeb, 0xfc, 0x53, - 0x84, 0xd1, 0x3f, 0xf3, 0x47, 0x81, 0x9f, 0x4a, 0x0f, 0x40, 0x14, 0xd8, 0x26, 0xac, 0xa1, 0xf1, - 0x50, 0xf6, 0x40, 0x6f, 0xb1, 0x38, 0x86, 0x55, 0xd6, 0xa1, 0x78, 0x23, 0x2e, 0xf5, 0xb7, 0xfe, - 0x44, 0xf8, 0x29, 0x55, 0x55, 0xb8, 0x6a, 0xa2, 0x25, 0x9c, 0xf2, 0xac, 0x30, 0x30, 0x1a, 0x28, - 0x85, 0x80, 0xe6, 0x84, 0xf2, 0x29, 0x86, 0x80, 0x8c, 0x30, 0xd2, 0x42, 0x29, 0x8c, 0x84, 0xca, - 0x69, 0x1a, 0xf6, 0xf9, 0xa0, 0x97, 0x45, 0x3d, 0x52, 0xa2, 0xb4, 0x3b, 0x0b, 0x5e, 0x11, 0xa6, - 0x80, 0x17, 0x4f, 0xb3, 0x90, 0x67, 0xa4, 0x67, 0x16, 0x3c, 0x55, 0x44, 0xf9, 0x21, 0x12, 0x61, - 0x12, 0x1a, 0x9e, 0x2c, 0xa1, 0xa3, 0x39, 0x49, 0x82, 0xb4, 0xd3, 0x22, 0x94, 0x7e, 0xb3, 0x4f, - 0xc3, 0xc5, 0x43, 0x9e, 0x66, 0xbd, 0x63, 0xee, 0x0f, 0x78, 0x42, 0xbb, 0x2f, 0xa2, 0x53, 0xc2, - 0x7e, 0x57, 0x57, 0x62, 0xdf, 0x27, 0x3c, 0x49, 0x83, 0x28, 0x24, 0xcb, 0xdd, 0xf0, 0x54, 0xd1, - 0xfd, 0x26, 0xf9, 0xc3, 0x3a, 0x6e, 0xf6, 0x84, 0x8c, 0x39, 0x7b, 0x0d, 0x1a, 0x62, 0x8e, 0xe9, - 0xb1, 0x2f, 0x5d, 0xf4, 0x05, 0x02, 0x0e, 0x8e, 0x7d, 0xd4, 0x08, 0xd6, 0xb2, 0x89, 0x40, 0x64, - 0x93, 0xb0, 0x5d, 0xb1, 0x6a, 0xd7, 0x60, 0x49, 0x45, 0xe4, 0xd2, 0xde, 0x88, 0x1f, 0x65, 0xea, - 0x78, 0x1d, 0x4e, 0xc6, 0xd8, 0x5d, 0xba, 0xc7, 0x8f, 0x32, 0xf7, 0x21, 0xac, 0x48, 0x19, 0x7e, - 0x14, 0x73, 0xd5, 0xf5, 0x67, 0xab, 0xac, 0x5b, 0x73, 0x73, 0xd5, 0x16, 0x7a, 0x8a, 0x11, 0x14, - 0x4c, 0x9e, 0xeb, 0x01, 0x33, 0x75, 0x82, 0x6c, 0x50, 0x9a, 0x18, 0x75, 0x88, 0x97, 0xd3, 0xb1, - 0x30, 0x5c, 0x9f, 0x74, 0xd2, 0xef, 0xa3, 0x26, 0x10, 0x1a, 0x50, 0x15, 0xdd, 0xef, 0x39, 0xb0, - 0x4a, 0xad, 0x29, 0xfb, 0xac, 0x4f, 0x7e, 0xaf, 0x3e, 0xcc, 0x56, 0xdf, 0x0c, 0x6c, 0xac, 0xc1, - 0xac, 0xa9, 0x6b, 0x45, 0xe1, 0xc7, 0x3f, 0xcb, 0xd6, 0x4b, 0x67, 0xd9, 0x1f, 0x39, 0xb0, 0x22, - 0x94, 0x61, 0xe6, 0x67, 0x93, 0x54, 0x4e, 0xff, 0x17, 0x61, 0x51, 0xd8, 0x29, 0x29, 0x4e, 0x72, - 0xa0, 0x6b, 0x5a, 0xf2, 0x09, 0x15, 0xc4, 0xbb, 0x17, 0x3c, 0x9b, 0x98, 0x7d, 0x1e, 0x5a, 0x66, - 0x58, 0x95, 0xc6, 0xdc, 0xdc, 0xbc, 0xac, 0x66, 0x59, 0xe2, 0x9c, 0xdd, 0x0b, 0x9e, 0xf5, 0x01, - 0xfb, 0x90, 0x9c, 0x8d, 0xb0, 0x47, 0xcd, 0xca, 0xc0, 0xd4, 0xe5, 0x0a, 0x05, 0xae, 0x3f, 0x37, - 0xc8, 0xef, 0x2e, 0xc0, 0x9c, 0xf0, 0x2e, 0xdd, 0xfb, 0xb0, 0x68, 0x8d, 0xd4, 0x3a, 0xa3, 0xb7, - 0xc4, 0x19, 0xbd, 0x14, 0xd2, 0xa9, 0x95, 0x43, 0x3a, 0xee, 0xbf, 0xd6, 0x80, 0x21, 0xb7, 0x15, - 0xb6, 0x13, 0xdd, 0xdb, 0x68, 0x60, 0x1d, 0x56, 0x5a, 0x9e, 0x09, 0xb1, 0x9b, 0xc0, 0x8c, 0xa2, - 0x8a, 0x7a, 0x09, 0xbb, 0x51, 0x51, 0x83, 0x0a, 0x4e, 0x1a, 0x56, 0x69, 0x02, 0xe5, 0xb1, 0x4c, - 0xec, 0x5b, 0x65, 0x1d, 0x9a, 0x86, 0x78, 0x92, 0x1e, 0xa3, 0xfb, 0xad, 0x8e, 0x33, 0xaa, 0x5c, - 0x64, 0x90, 0xb9, 0x73, 0x19, 0x64, 0xbe, 0xc8, 0x20, 0xa6, 0x43, 0xbd, 0x60, 0x39, 0xd4, 0xe8, - 0xc8, 0x8d, 0xd1, 0xfd, 0xcb, 0x46, 0xfd, 0xde, 0x18, 0x7b, 0x97, 0xa7, 0x17, 0x0b, 0x64, 0x1b, - 0xd0, 0x96, 0xae, 0x40, 0xee, 0xb5, 0x03, 0xad, 0x71, 0x09, 0x77, 0x7f, 0xe8, 0x40, 0x1b, 0xd7, - 0xd9, 0xe2, 0xc5, 0x0f, 0x80, 0x44, 0xe1, 0x15, 0x59, 0xd1, 0xa2, 0xfd, 0xe9, 0x39, 0xf1, 0x7d, - 0x68, 0x50, 0x83, 0x51, 0xcc, 0x43, 0xc9, 0x88, 0x1d, 0x9b, 0x11, 0x73, 0x2d, 0xb4, 0x7b, 0xc1, - 0xcb, 0x89, 0x0d, 0x36, 0xfc, 0x47, 0x07, 0x9a, 0x72, 0x98, 0x3f, 0xf1, 0x49, 0xbc, 0x0b, 0x0b, - 0xc8, 0x91, 0xc6, 0x71, 0x57, 0x97, 0xd1, 0x9a, 0x8c, 0xfd, 0x6c, 0x92, 0xa0, 0xf9, 0xb4, 0x4e, - 0xe1, 0x45, 0x18, 0x6d, 0x21, 0x29, 0xdc, 0xb4, 0x97, 0x05, 0xa3, 0x9e, 0xaa, 0x95, 0xb7, 0x18, - 0x55, 0x55, 0xa8, 0x77, 0xd2, 0xcc, 0x1f, 0x72, 0x69, 0xe6, 0x44, 0xc1, 0xed, 0xc0, 0xba, 0x9c, - 0x50, 0xc1, 0x77, 0x74, 0xff, 0xa6, 0x05, 0x97, 0x4a, 0x55, 0xfa, 0x1a, 0x50, 0x1e, 0x2f, 0x47, - 0xc1, 0xf8, 0x30, 0xd2, 0x8e, 0xb6, 0x63, 0x9e, 0x3c, 0xad, 0x2a, 0x36, 0x84, 0x8b, 0xca, 0x9e, - 0xe3, 0x9a, 0xe6, 0xd6, 0xbb, 0x46, 0x8e, 0xc8, 0xbb, 0x36, 0x0f, 0x14, 0x3b, 0x54, 0xb8, 0x29, - 0xb9, 0xd5, 0xed, 0xb1, 0x63, 0xe8, 0x68, 0xc7, 0x41, 0xaa, 0x78, 0xc3, 0xb9, 0xc0, 0xbe, 0xde, - 0x39, 0xa7, 0x2f, 0xcb, 0x11, 0xf5, 0xce, 0x6c, 0x8d, 0x4d, 0xe1, 0x8a, 0xaa, 0x23, 0x1d, 0x5e, - 0xee, 0xaf, 0xfe, 0x4a, 0x73, 0x23, 0x27, 0xda, 0xee, 0xf4, 0x9c, 0x86, 0xd9, 0xd7, 0x61, 0xfd, - 0xd4, 0x0f, 0x32, 0x35, 0x2c, 0xc3, 0x19, 0x9a, 0xa5, 0x2e, 0x37, 0xcf, 0xe9, 0xf2, 0xa9, 0xf8, - 0xd8, 0x32, 0x6c, 0x67, 0xb4, 0xd8, 0xfd, 0x7b, 0x07, 0x96, 0xec, 0x76, 0x90, 0x4d, 0xa5, 0xc0, - 0x2b, 0xc5, 0xa7, 0x9c, 0xbf, 0x02, 0x5c, 0x3e, 0xab, 0xd6, 0xaa, 0xce, 0xaa, 0xe6, 0x09, 0x71, - 0xe6, 0xbc, 0x30, 0x4e, 0xfd, 0xd5, 0xc2, 0x38, 0xb3, 0x55, 0x61, 0x9c, 0xee, 0x7f, 0x3b, 0xc0, - 0xca, 0xbc, 0xc4, 0xee, 0x8b, 0xc3, 0x72, 0xc8, 0x47, 0x52, 0x27, 0xfd, 0xfc, 0xab, 0xf1, 0xa3, - 0x5a, 0x3b, 0xf5, 0x35, 0x0a, 0x86, 0xa9, 0x74, 0x4c, 0x17, 0x69, 0xd1, 0xab, 0xaa, 0x2a, 0x04, - 0x96, 0xea, 0xe7, 0x07, 0x96, 0x66, 0xcf, 0x0f, 0x2c, 0xcd, 0x15, 0x03, 0x4b, 0xdd, 0xdf, 0x71, - 0x60, 0xb5, 0x62, 0xd3, 0x7f, 0x76, 0x13, 0xc7, 0x6d, 0xb2, 0x74, 0x41, 0x4d, 0x6e, 0x93, 0x09, - 0x76, 0x7f, 0x03, 0x16, 0x2d, 0x46, 0xff, 0xd9, 0xf5, 0x5f, 0xf4, 0xf2, 0x04, 0x9f, 0x59, 0x58, - 0xf7, 0x3f, 0x6a, 0xc0, 0xca, 0xc2, 0xf6, 0xff, 0x3a, 0x86, 0xf2, 0x3a, 0xcd, 0x54, 0xac, 0xd3, - 0xff, 0xa9, 0x1d, 0x78, 0x07, 0x56, 0x64, 0xce, 0x80, 0x11, 0x22, 0x11, 0x1c, 0x53, 0xae, 0x40, - 0x3f, 0xd7, 0x8e, 0xea, 0x2d, 0x58, 0x77, 0xcd, 0x86, 0x31, 0x2c, 0x04, 0xf7, 0xdc, 0x75, 0x58, - 0x13, 0x39, 0x08, 0x77, 0x45, 0x53, 0xca, 0xae, 0xfc, 0x89, 0x03, 0x17, 0x0b, 0x15, 0xf9, 0xcd, - 0xa8, 0x30, 0x1d, 0xb6, 0x3d, 0xb1, 0x41, 0x1c, 0xbf, 0x94, 0x23, 0x63, 0xfc, 0x82, 0xdb, 0xca, - 0x15, 0xb8, 0x3e, 0x93, 0xb0, 0x4c, 0x2f, 0x56, 0xbd, 0xaa, 0xca, 0xbd, 0x24, 0x32, 0x25, 0x42, - 0x3e, 0x2a, 0x0c, 0xfc, 0x48, 0xe4, 0x36, 0x98, 0x15, 0xf9, 0xd5, 0x8a, 0x3d, 0x64, 0x55, 0x44, - 0x2f, 0xd0, 0x32, 0x53, 0xf6, 0x78, 0x2b, 0xeb, 0xdc, 0xbf, 0x72, 0x80, 0x7d, 0x69, 0xc2, 0x93, - 0x29, 0xdd, 0x90, 0xea, 0x58, 0xce, 0xa5, 0x62, 0x1c, 0x63, 0x2e, 0x9e, 0x1c, 0x7e, 0x91, 0x4f, - 0xd5, 0x3d, 0x7a, 0x2d, 0xbf, 0x47, 0x7f, 0x03, 0x00, 0x8f, 0x5f, 0xfa, 0xda, 0x15, 0x79, 0x01, - 0xcf, 0xbd, 0xa2, 0xc1, 0xca, 0xab, 0xee, 0xfa, 0xf9, 0x57, 0xdd, 0xb3, 0xe7, 0x5d, 0x75, 0x7f, - 0x08, 0xab, 0xd6, 0xb8, 0xf5, 0xb6, 0xaa, 0x0b, 0x60, 0xe7, 0x25, 0x17, 0xc0, 0xff, 0xe9, 0xc0, - 0xcc, 0x6e, 0x14, 0x9b, 0x71, 0x4b, 0xc7, 0x8e, 0x5b, 0x4a, 0x5b, 0xd2, 0xd3, 0xa6, 0x42, 0xaa, - 0x18, 0x0b, 0x64, 0x1b, 0xb0, 0xe4, 0x8f, 0x33, 0x3c, 0x76, 0x1f, 0x45, 0xc9, 0xa9, 0x9f, 0x0c, - 0xc4, 0x5e, 0xdf, 0xad, 0x75, 0x1c, 0xaf, 0x50, 0xc3, 0xd6, 0x60, 0x46, 0x2b, 0x5d, 0x22, 0xc0, - 0x22, 0x3a, 0x6e, 0x74, 0xe7, 0x31, 0x95, 0x11, 0x03, 0x59, 0x42, 0x56, 0xb2, 0xbf, 0x17, 0xae, - 0xb2, 0x10, 0x9d, 0xaa, 0x2a, 0xb4, 0x6b, 0xb8, 0x7c, 0x44, 0x26, 0x43, 0x3d, 0xaa, 0xec, 0xfe, - 0x9b, 0x03, 0xb3, 0xb4, 0x02, 0x28, 0xec, 0x82, 0xc3, 0x75, 0x80, 0x92, 0x66, 0xbe, 0xe8, 0x15, - 0x61, 0xe6, 0x5a, 0xf9, 0x26, 0x35, 0x3d, 0x6c, 0x33, 0xe7, 0xe4, 0x2a, 0x34, 0x44, 0x49, 0xe7, - 0x56, 0x10, 0x49, 0x0e, 0xb2, 0x2b, 0x50, 0x3f, 0x8e, 0x62, 0xe5, 0x9d, 0x80, 0x8a, 0xcf, 0x47, - 0xb1, 0x47, 0x78, 0x3e, 0x1e, 0x6c, 0x4f, 0x0c, 0x5e, 0xd8, 0x9c, 0x22, 0x8c, 0x56, 0x57, 0x37, - 0x6b, 0x2e, 0x46, 0x01, 0x75, 0x37, 0x60, 0xf9, 0x61, 0x34, 0xe0, 0x46, 0x4c, 0xe9, 0x4c, 0x6e, - 0x76, 0xbf, 0xe5, 0xc0, 0x82, 0x22, 0x66, 0x37, 0xa0, 0x8e, 0xae, 0x44, 0xe1, 0xa0, 0xa0, 0xef, - 0xe5, 0x90, 0xce, 0x23, 0x0a, 0xd4, 0xbd, 0x14, 0x71, 0xc8, 0xdd, 0x4a, 0x15, 0x6f, 0xc8, 0xbd, - 0x26, 0x3d, 0xdc, 0x82, 0xb3, 0x51, 0x40, 0xdd, 0xbf, 0x70, 0x60, 0xd1, 0xea, 0x03, 0x8f, 0x87, - 0x23, 0x3f, 0xcd, 0xe4, 0x5d, 0x87, 0xdc, 0x1e, 0x13, 0x32, 0xa3, 0x8c, 0x35, 0x3b, 0xca, 0xa8, - 0xe3, 0x5f, 0x33, 0x66, 0xfc, 0xeb, 0x36, 0x34, 0xf2, 0xac, 0xa0, 0xba, 0xa5, 0x53, 0xb1, 0x47, - 0x75, 0xe3, 0x98, 0x13, 0x61, 0x3b, 0xfd, 0x68, 0x14, 0x25, 0x32, 0xc8, 0x2e, 0x0a, 0xee, 0x87, - 0xd0, 0x34, 0xe8, 0x71, 0x18, 0x21, 0xcf, 0x4e, 0xa3, 0xe4, 0x99, 0x0a, 0x76, 0xca, 0xa2, 0xbe, - 0x58, 0xaf, 0xe5, 0x17, 0xeb, 0xee, 0x0f, 0x1c, 0x58, 0x44, 0x1e, 0x0c, 0xc2, 0xe1, 0x7e, 0x34, - 0x0a, 0xfa, 0x53, 0xda, 0x7b, 0xc5, 0x6e, 0x52, 0x33, 0x28, 0x5e, 0xb4, 0x61, 0xe4, 0x6d, 0x75, - 0x3a, 0x94, 0x82, 0xa8, 0xcb, 0x28, 0xa9, 0xc8, 0xe7, 0x87, 0x7e, 0x2a, 0x99, 0x5f, 0x1a, 0x39, - 0x0b, 0x44, 0x79, 0x42, 0x20, 0xf1, 0x33, 0xde, 0x1b, 0x07, 0xa3, 0x51, 0x20, 0x68, 0x85, 0x0b, - 0x54, 0x55, 0x85, 0x7d, 0x0e, 0x82, 0xd4, 0x3f, 0xcc, 0x03, 0xc9, 0xba, 0xec, 0x7e, 0xbf, 0x06, - 0x4d, 0xa9, 0x9e, 0x77, 0x06, 0x43, 0x2e, 0x6f, 0x39, 0xc8, 0xc9, 0xd4, 0xaa, 0xc4, 0x40, 0x54, - 0xbd, 0xe5, 0x96, 0x1a, 0x48, 0x71, 0xcb, 0x67, 0xca, 0x5b, 0xfe, 0x3a, 0x34, 0x90, 0xf5, 0xde, - 0x25, 0xff, 0x57, 0xdc, 0x90, 0xe4, 0x80, 0xaa, 0xdd, 0xa4, 0xda, 0xd9, 0xbc, 0x96, 0x80, 0x97, - 0xde, 0x89, 0xbc, 0x0f, 0x2d, 0xd9, 0x0c, 0xed, 0x09, 0x69, 0x8e, 0x9c, 0xf9, 0xad, 0xfd, 0xf2, - 0x2c, 0x4a, 0xf5, 0xe5, 0xa6, 0xfa, 0x72, 0xe1, 0xbc, 0x2f, 0x15, 0x25, 0xdd, 0x5f, 0x8b, 0xb5, - 0xb9, 0x9f, 0xf8, 0xf1, 0xb1, 0x32, 0x79, 0x03, 0x9d, 0x94, 0x43, 0x30, 0xdb, 0x80, 0x59, 0xfc, - 0x4c, 0x69, 0xf2, 0x6a, 0x81, 0x14, 0x24, 0xec, 0x06, 0xcc, 0xf2, 0xc1, 0x90, 0xab, 0x13, 0x1e, - 0xb3, 0xcf, 0xda, 0xb8, 0x47, 0x9e, 0x20, 0x40, 0xf5, 0x80, 0x68, 0x41, 0x3d, 0xd8, 0x56, 0x60, - 0x0e, 0x8b, 0x0f, 0x06, 0xee, 0x1a, 0xb0, 0x87, 0x82, 0xa3, 0xcd, 0x08, 0xf5, 0x6f, 0xcf, 0x40, - 0xd3, 0x80, 0x51, 0xd2, 0x87, 0x38, 0xe0, 0xde, 0x20, 0xf0, 0xc7, 0x3c, 0xe3, 0x89, 0xe4, 0xe2, - 0x02, 0x8a, 0x74, 0xfe, 0xc9, 0xb0, 0x17, 0x4d, 0xb2, 0xde, 0x80, 0x0f, 0x13, 0x2e, 0x0c, 0x33, - 0x1a, 0x0a, 0x0b, 0x45, 0xba, 0xb1, 0xff, 0xdc, 0xa4, 0x13, 0xfc, 0x50, 0x40, 0x55, 0xbc, 0x59, - 0xac, 0x51, 0x3d, 0x8f, 0x37, 0x8b, 0x15, 0x29, 0xea, 0xa8, 0xd9, 0x0a, 0x1d, 0xf5, 0x1e, 0xac, - 0x0b, 0x6d, 0x24, 0xe5, 0xb6, 0x57, 0x60, 0x93, 0x33, 0x6a, 0xd9, 0x06, 0xb4, 0x71, 0xcc, 0x8a, - 0xc1, 0xd3, 0xe0, 0x9b, 0x22, 0x02, 0xe4, 0x78, 0x25, 0x1c, 0x69, 0x51, 0x54, 0x2d, 0x5a, 0x71, - 0xa3, 0x56, 0xc2, 0x89, 0xd6, 0x7f, 0x6e, 0xd3, 0x36, 0x24, 0x6d, 0x01, 0x77, 0x17, 0xa1, 0x79, - 0x90, 0x45, 0xb1, 0xda, 0x94, 0x25, 0x68, 0x89, 0xa2, 0xcc, 0x5f, 0x78, 0x0d, 0x2e, 0x13, 0x17, - 0x3d, 0x8e, 0xe2, 0x68, 0x14, 0x0d, 0xa7, 0x07, 0x93, 0xc3, 0xb4, 0x9f, 0x04, 0x31, 0x9e, 0x86, - 0xdc, 0x7f, 0x70, 0x60, 0xd5, 0xaa, 0x95, 0x21, 0xa3, 0x4f, 0x0b, 0x96, 0xd6, 0x17, 0xcf, 0x82, - 0xf1, 0x56, 0x0c, 0x55, 0x29, 0x08, 0x45, 0xb0, 0xee, 0x89, 0xbc, 0x8b, 0xbe, 0x03, 0xcb, 0x6a, - 0x64, 0xea, 0x43, 0xc1, 0x85, 0x9d, 0x32, 0x17, 0xca, 0xef, 0x97, 0xe4, 0x07, 0xaa, 0x89, 0x5f, - 0x92, 0x37, 0x93, 0x03, 0x9a, 0xa3, 0x8a, 0x1d, 0xe8, 0xbb, 0x27, 0xf3, 0x04, 0xa1, 0x46, 0xd0, - 0xd7, 0x60, 0xea, 0xfe, 0x9e, 0x03, 0x90, 0x8f, 0x0e, 0x19, 0x23, 0x57, 0xf7, 0x22, 0x59, 0xda, - 0x50, 0xed, 0x6f, 0x41, 0x4b, 0xdf, 0x9a, 0xe4, 0x16, 0xa4, 0xa9, 0x30, 0x74, 0xf2, 0xae, 0xc3, - 0xf2, 0x70, 0x14, 0x1d, 0x92, 0xf9, 0xa5, 0x84, 0x98, 0x54, 0x66, 0x71, 0x2c, 0x09, 0xf8, 0x9e, - 0x44, 0x73, 0x73, 0x53, 0x37, 0xcc, 0x8d, 0xfb, 0xed, 0x9a, 0x8e, 0xb5, 0xe7, 0x73, 0x3e, 0x53, - 0xca, 0xd8, 0x66, 0x49, 0x39, 0x9e, 0x11, 0xda, 0xa6, 0x28, 0xd9, 0xfe, 0xb9, 0x87, 0xf8, 0x0f, - 0x61, 0x29, 0x11, 0xda, 0x47, 0xa9, 0xa6, 0xfa, 0x4b, 0x54, 0xd3, 0x62, 0x62, 0xd9, 0xa4, 0x4f, - 0x42, 0xdb, 0x1f, 0x9c, 0xf0, 0x24, 0x0b, 0xe8, 0x18, 0x45, 0x0e, 0x81, 0x50, 0xa8, 0xcb, 0x06, - 0x4e, 0x76, 0xfa, 0x3a, 0x2c, 0xcb, 0xcc, 0x19, 0x4d, 0x29, 0xb3, 0x3d, 0x73, 0x18, 0x09, 0xdd, - 0x3f, 0x53, 0x61, 0x7d, 0x7b, 0x0f, 0xcf, 0x5e, 0x11, 0x73, 0x76, 0xb5, 0xc2, 0xec, 0x3e, 0x21, - 0x43, 0xec, 0x03, 0x75, 0x56, 0x9b, 0x31, 0x6e, 0xb1, 0x07, 0xf2, 0x4a, 0xc4, 0x5e, 0xd2, 0xfa, - 0xab, 0x2c, 0xa9, 0xfb, 0x43, 0x07, 0xe6, 0x77, 0xa3, 0x78, 0x57, 0xde, 0xe7, 0x93, 0x20, 0xe8, - 0xbc, 0x34, 0x55, 0x7c, 0xc9, 0x4d, 0x7f, 0xa5, 0x1d, 0x5e, 0x2c, 0xda, 0xe1, 0x5f, 0x86, 0xd7, - 0x28, 0x52, 0x90, 0x44, 0x71, 0x94, 0xa0, 0x30, 0xfa, 0x23, 0x61, 0x74, 0xa3, 0x30, 0x3b, 0x56, - 0x6a, 0xec, 0x65, 0x24, 0x74, 0x24, 0xc3, 0xa3, 0x84, 0x70, 0x94, 0xa5, 0xdf, 0x20, 0xb4, 0x5b, - 0xb9, 0xc2, 0xfd, 0x2c, 0x34, 0xc8, 0xf1, 0xa5, 0x69, 0xbd, 0x03, 0x8d, 0xe3, 0x28, 0xee, 0x1d, - 0x07, 0x61, 0xa6, 0x84, 0x7b, 0x29, 0xf7, 0x48, 0x77, 0x69, 0x41, 0x34, 0x81, 0xfb, 0x83, 0x3a, - 0xcc, 0x3f, 0x08, 0x4f, 0xa2, 0xa0, 0x4f, 0x37, 0x00, 0x63, 0x3e, 0x8e, 0x54, 0x96, 0x1e, 0xfe, - 0xc6, 0xa5, 0xa0, 0x8c, 0x95, 0x38, 0x93, 0x21, 0x7c, 0x55, 0x44, 0x73, 0x9f, 0xe4, 0x99, 0xb4, - 0x42, 0x74, 0x0c, 0x04, 0x9d, 0xfe, 0xc4, 0x4c, 0x3a, 0x96, 0xa5, 0x3c, 0xcd, 0x71, 0xd6, 0x48, - 0x73, 0xa4, 0xfb, 0x22, 0x91, 0x7b, 0x40, 0xfc, 0xb5, 0xe0, 0xa9, 0x22, 0x1d, 0x52, 0x12, 0x2e, - 0x22, 0x3c, 0xe4, 0x38, 0xcc, 0xcb, 0x43, 0x8a, 0x09, 0xa2, 0x73, 0x21, 0x3e, 0x10, 0x34, 0x42, - 0xf9, 0x9a, 0x10, 0x3a, 0x62, 0xc5, 0xbc, 0xe5, 0x86, 0xe0, 0xf9, 0x02, 0x8c, 0x1a, 0x7a, 0xc0, - 0xb5, 0x22, 0x15, 0x73, 0x00, 0x91, 0x29, 0x5c, 0xc4, 0x8d, 0xa3, 0x8d, 0x48, 0x2a, 0x52, 0x47, - 0x1b, 0x64, 0x14, 0x7f, 0x34, 0x3a, 0xf4, 0xfb, 0xcf, 0x28, 0x2d, 0x9d, 0x72, 0x88, 0x1a, 0x9e, - 0x0d, 0x52, 0x36, 0x41, 0xbe, 0x9b, 0x74, 0xe3, 0x58, 0xf7, 0x4c, 0x88, 0x6d, 0x42, 0x93, 0x8e, - 0x73, 0x72, 0x3f, 0x97, 0x68, 0x3f, 0xdb, 0xe6, 0x79, 0x8f, 0x76, 0xd4, 0x24, 0x32, 0x6f, 0x25, - 0x96, 0xed, 0x5b, 0x09, 0xa1, 0x34, 0xe5, 0x65, 0x4e, 0x9b, 0x7a, 0xcb, 0x01, 0xb4, 0xa6, 0x72, - 0xc1, 0x04, 0xc1, 0x0a, 0x11, 0x58, 0x18, 0x4a, 0x2d, 0x1e, 0x42, 0x62, 0x3f, 0x18, 0x74, 0x98, - 0x90, 0x5a, 0x55, 0x76, 0x33, 0x60, 0x77, 0x06, 0x03, 0xc9, 0x4d, 0xfa, 0xb0, 0x9a, 0xf3, 0x81, - 0x63, 0xf1, 0x41, 0xc5, 0x7e, 0xd4, 0xaa, 0xf7, 0xe3, 0xa5, 0xa3, 0x76, 0x77, 0xa0, 0xb9, 0x6f, - 0x24, 0x53, 0x13, 0x5b, 0xaa, 0x34, 0x6a, 0xc9, 0xca, 0x06, 0x62, 0x0c, 0xa7, 0x66, 0x0e, 0xc7, - 0xfd, 0x05, 0x60, 0x7b, 0x41, 0x9a, 0xe9, 0xd1, 0xe7, 0xd9, 0xdb, 0x2a, 0xa2, 0x90, 0x27, 0x40, - 0x35, 0x25, 0x46, 0x89, 0x49, 0x77, 0x44, 0xe6, 0x54, 0x71, 0xda, 0x1b, 0xb0, 0x10, 0x08, 0xa8, - 0x28, 0x85, 0x8a, 0x52, 0xd7, 0xbb, 0x4f, 0x61, 0x55, 0x82, 0xa6, 0x05, 0xb7, 0xe7, 0xed, 0x9c, - 0xb7, 0x5b, 0xb5, 0xf2, 0x6e, 0xb9, 0xdf, 0x77, 0x60, 0x5e, 0x2e, 0x0e, 0xd2, 0x97, 0x12, 0xd1, - 0x1b, 0x9e, 0x85, 0x55, 0xa7, 0x20, 0x97, 0x25, 0x70, 0xa6, 0x4a, 0x02, 0x19, 0xd4, 0x63, 0x3f, - 0x3b, 0xa6, 0xa3, 0x57, 0xc3, 0xa3, 0xdf, 0xac, 0x2d, 0xc2, 0x01, 0x42, 0xd2, 0x29, 0x14, 0x50, - 0x95, 0x85, 0x2f, 0x0c, 0x4a, 0x09, 0x47, 0x17, 0x9a, 0xf2, 0x2d, 0x04, 0xae, 0xaf, 0x51, 0x64, - 0x26, 0x58, 0x0e, 0xe7, 0x2b, 0x2e, 0x9b, 0x28, 0xae, 0xb8, 0x24, 0xf5, 0x74, 0xbd, 0xdb, 0x85, - 0xce, 0x36, 0x1f, 0xf1, 0x8c, 0xdf, 0x19, 0x8d, 0x8a, 0xed, 0xbf, 0x06, 0x97, 0x2b, 0xea, 0xa4, - 0xcb, 0x75, 0x0f, 0x56, 0xb6, 0xf9, 0xe1, 0x64, 0xb8, 0xc7, 0x4f, 0xf2, 0xbb, 0x4e, 0x06, 0xf5, - 0xf4, 0x38, 0x3a, 0x95, 0xdc, 0x41, 0xbf, 0xd9, 0x1b, 0x00, 0x23, 0xa4, 0xe9, 0xa5, 0x31, 0xef, - 0xab, 0xe4, 0x5b, 0x42, 0x0e, 0x62, 0xde, 0x77, 0xdf, 0x03, 0x66, 0xb6, 0x23, 0xa7, 0x80, 0x5a, - 0x6c, 0x72, 0xd8, 0x4b, 0xa7, 0x69, 0xc6, 0xc7, 0x2a, 0xab, 0xd8, 0x84, 0xdc, 0xeb, 0xd0, 0xda, - 0xf7, 0xa7, 0x1e, 0xff, 0x86, 0x7c, 0x0b, 0x80, 0xa7, 0x7e, 0x7f, 0x8a, 0xa2, 0xa2, 0x4f, 0xfd, - 0x54, 0xed, 0xfe, 0x57, 0x0d, 0xe6, 0x04, 0x25, 0xb6, 0x3a, 0xe0, 0x69, 0x16, 0x84, 0xe2, 0x9e, - 0x4f, 0xb6, 0x6a, 0x40, 0x25, 0xde, 0xa8, 0x55, 0xf0, 0x86, 0xf4, 0xb5, 0x55, 0x22, 0xa3, 0x64, - 0x02, 0x0b, 0x43, 0x8e, 0xcd, 0xf3, 0x27, 0xc4, 0xb1, 0x33, 0x07, 0x0a, 0x61, 0xa0, 0x5c, 0x57, - 0x8a, 0xf1, 0x29, 0xb6, 0x97, 0xec, 0x60, 0x42, 0x95, 0x1a, 0x79, 0x5e, 0x70, 0x4d, 0x49, 0x23, - 0x97, 0x34, 0xef, 0xc2, 0x2b, 0x68, 0x5e, 0xe1, 0x80, 0xbf, 0x4c, 0xf3, 0xc2, 0x2b, 0x68, 0x5e, - 0x97, 0x41, 0xfb, 0x1e, 0xe7, 0x1e, 0x47, 0x9b, 0xae, 0xd8, 0xe9, 0x3b, 0x0e, 0xb4, 0xa5, 0x3b, - 0xa2, 0xeb, 0xd8, 0x5b, 0x96, 0xef, 0x52, 0x99, 0x6e, 0x78, 0x0d, 0x16, 0xc9, 0xa3, 0xd0, 0xf1, - 0x2e, 0x19, 0x9c, 0xb3, 0x40, 0x9c, 0x87, 0xba, 0x94, 0x18, 0x07, 0x23, 0xb9, 0x29, 0x26, 0xa4, - 0x42, 0x66, 0x78, 0xf2, 0xa7, 0x2d, 0x71, 0x3c, 0x5d, 0x76, 0xff, 0xda, 0x81, 0x15, 0x63, 0xc0, - 0x92, 0x0b, 0x3f, 0x04, 0x95, 0x5f, 0x21, 0xc2, 0x62, 0x42, 0x98, 0x2e, 0xd9, 0xae, 0x55, 0xfe, - 0x99, 0x45, 0x4c, 0x9b, 0xe9, 0x4f, 0x69, 0x80, 0xe9, 0x64, 0x2c, 0xb5, 0x92, 0x09, 0x21, 0x23, - 0x9d, 0x72, 0xfe, 0x4c, 0x93, 0xcc, 0x08, 0xc5, 0x65, 0x62, 0x74, 0x7d, 0x8e, 0x9e, 0x90, 0x26, - 0x12, 0x19, 0x63, 0x36, 0xe8, 0xfe, 0x93, 0x03, 0xab, 0xc2, 0xa5, 0x95, 0x07, 0x06, 0x9d, 0x0b, - 0x3e, 0x27, 0x7c, 0x78, 0x21, 0x91, 0xbb, 0x17, 0x3c, 0x59, 0x66, 0x9f, 0x79, 0x45, 0x37, 0x5c, - 0xa7, 0x4d, 0x9c, 0xb1, 0x17, 0x33, 0x55, 0x7b, 0xf1, 0x92, 0x95, 0xae, 0x0a, 0x03, 0xcd, 0x56, - 0x86, 0x81, 0xee, 0xce, 0xc3, 0x6c, 0xda, 0x8f, 0x62, 0xee, 0xae, 0xc3, 0x9a, 0x3d, 0x39, 0xa9, - 0x82, 0xbe, 0xeb, 0x40, 0xe7, 0x9e, 0x08, 0x8a, 0x06, 0xe1, 0x70, 0x37, 0x48, 0xb3, 0x28, 0xd1, - 0x8f, 0x5f, 0xae, 0x00, 0xa4, 0x99, 0x9f, 0x64, 0x22, 0xad, 0x4d, 0x06, 0x69, 0x72, 0x04, 0xc7, - 0xc8, 0xc3, 0x81, 0xa8, 0x15, 0x7b, 0xa3, 0xcb, 0xb8, 0x31, 0x64, 0x36, 0x7a, 0xd1, 0xd1, 0x51, - 0xca, 0xb5, 0xd3, 0x6d, 0x62, 0x78, 0x6e, 0x47, 0x89, 0xc7, 0x93, 0x2a, 0x3f, 0x21, 0x55, 0x2b, - 0xbc, 0xd9, 0x02, 0xea, 0xfe, 0xa5, 0x03, 0xcb, 0xf9, 0x20, 0x77, 0x10, 0xb4, 0xb5, 0x83, 0xb4, - 0x67, 0xb9, 0x76, 0x50, 0xe1, 0xa3, 0x00, 0x0d, 0x9c, 0x1c, 0x9b, 0x81, 0x90, 0xc4, 0xca, 0x52, - 0x34, 0x51, 0x29, 0x84, 0x26, 0x24, 0xf2, 0x03, 0x32, 0xfc, 0x5a, 0xe4, 0x0f, 0xca, 0x12, 0x65, - 0x25, 0x8e, 0x33, 0xfa, 0x6a, 0x4e, 0xb8, 0xf3, 0xb2, 0xa8, 0xec, 0xd3, 0x3c, 0xa1, 0xf8, 0xd3, - 0xfd, 0x7d, 0x07, 0x2e, 0x57, 0x2c, 0xae, 0x94, 0x8c, 0x6d, 0x58, 0x39, 0xd2, 0x95, 0x6a, 0x01, - 0x84, 0x78, 0xac, 0xab, 0x28, 0xbe, 0x3d, 0x69, 0xaf, 0xfc, 0x01, 0x3a, 0xf7, 0x14, 0xf5, 0x12, - 0x4b, 0x6a, 0xa5, 0xd6, 0x94, 0x2b, 0x36, 0xff, 0x60, 0x06, 0x96, 0xc4, 0xed, 0x8e, 0x78, 0x86, - 0xca, 0x13, 0xf6, 0x11, 0xcc, 0xcb, 0x67, 0xc4, 0xec, 0xa2, 0xec, 0xd6, 0x7e, 0xb8, 0xdc, 0x5d, - 0x2f, 0xc2, 0x92, 0x77, 0x56, 0x7f, 0xeb, 0x87, 0xff, 0xf2, 0x87, 0xb5, 0x45, 0xd6, 0xbc, 0x75, - 0xf2, 0xee, 0xad, 0x21, 0x0f, 0x53, 0x6c, 0xe3, 0xd7, 0x00, 0xf2, 0x07, 0xb6, 0xac, 0xa3, 0xdd, - 0x94, 0xc2, 0xcb, 0xe1, 0xee, 0xe5, 0x8a, 0x1a, 0xd9, 0xee, 0x65, 0x6a, 0x77, 0xd5, 0x5d, 0xc2, - 0x76, 0x83, 0x30, 0xc8, 0xc4, 0x6b, 0xdb, 0x0f, 0x9c, 0x0d, 0x36, 0x80, 0x96, 0xf9, 0x7e, 0x96, - 0xa9, 0x03, 0x7f, 0xc5, 0xeb, 0xdd, 0xee, 0x6b, 0x95, 0x75, 0x2a, 0xda, 0x41, 0x7d, 0x5c, 0x74, - 0xdb, 0xd8, 0xc7, 0x84, 0x28, 0xf2, 0x5e, 0x46, 0xb0, 0x64, 0x3f, 0x93, 0x65, 0xaf, 0x1b, 0x62, - 0x5d, 0x7a, 0xa4, 0xdb, 0x7d, 0xe3, 0x8c, 0x5a, 0xd9, 0xd7, 0x1b, 0xd4, 0xd7, 0x25, 0x97, 0x61, - 0x5f, 0x7d, 0xa2, 0x51, 0x8f, 0x74, 0x3f, 0x70, 0x36, 0x36, 0xbf, 0x75, 0x05, 0x1a, 0x3a, 0x44, - 0xc7, 0xbe, 0x0e, 0x8b, 0xd6, 0xf5, 0x1b, 0x53, 0xd3, 0xa8, 0xba, 0xad, 0xeb, 0xbe, 0x5e, 0x5d, - 0x29, 0x3b, 0xbe, 0x42, 0x1d, 0x77, 0xd8, 0x3a, 0x76, 0x2c, 0xef, 0xaf, 0x6e, 0xd1, 0xa5, 0xa3, - 0xc8, 0x79, 0x7c, 0x26, 0xe6, 0x99, 0x5f, 0x99, 0x59, 0xf3, 0x2c, 0x5d, 0xb1, 0x59, 0xf3, 0x2c, - 0xdf, 0xb3, 0xb9, 0xaf, 0x53, 0x77, 0xeb, 0x6c, 0xcd, 0xec, 0x4e, 0x87, 0xce, 0x38, 0x65, 0xa9, - 0x9a, 0xaf, 0x68, 0xd9, 0x1b, 0x9a, 0xb1, 0xaa, 0x5e, 0xd7, 0x6a, 0x16, 0x29, 0x3f, 0xb1, 0x75, - 0x3b, 0xd4, 0x15, 0x63, 0xb4, 0x7d, 0xe6, 0x23, 0x5a, 0xf6, 0x55, 0x68, 0xe8, 0x27, 0x63, 0xec, - 0x92, 0xf1, 0x4e, 0xcf, 0x7c, 0xc7, 0xd6, 0xed, 0x94, 0x2b, 0xaa, 0x18, 0xc3, 0x6c, 0x19, 0x19, - 0x63, 0x0f, 0x2e, 0x4a, 0xa7, 0xfa, 0x90, 0xff, 0x38, 0x33, 0xa9, 0x78, 0xfb, 0x7b, 0xdb, 0x61, - 0x1f, 0xc2, 0x82, 0x7a, 0x89, 0xc7, 0xd6, 0xab, 0x5f, 0x14, 0x76, 0x2f, 0x95, 0x70, 0xa9, 0x3d, - 0xee, 0x00, 0xe4, 0xaf, 0xc8, 0xb4, 0x9c, 0x95, 0xde, 0xb6, 0xe9, 0x45, 0xac, 0x78, 0x72, 0x36, - 0xa4, 0x37, 0x73, 0xf6, 0x23, 0x35, 0xf6, 0x66, 0x4e, 0x5f, 0xf9, 0x7c, 0xed, 0x25, 0x0d, 0xba, - 0xeb, 0xb4, 0x76, 0x6d, 0x46, 0x82, 0x1b, 0xf2, 0x53, 0x95, 0xaf, 0xbd, 0x0d, 0x4d, 0xe3, 0x65, - 0x1a, 0x53, 0x2d, 0x94, 0x5f, 0xb5, 0x75, 0xbb, 0x55, 0x55, 0x72, 0xb8, 0x5f, 0x80, 0x45, 0xeb, - 0x89, 0x99, 0x96, 0x8c, 0xaa, 0x07, 0x6c, 0x5a, 0x32, 0xaa, 0x5f, 0xa5, 0x7d, 0x05, 0x9a, 0xc6, - 0x83, 0x30, 0x66, 0xe4, 0xa9, 0x15, 0x9e, 0x82, 0xe9, 0x11, 0x55, 0xbd, 0x1f, 0x5b, 0xa3, 0xf9, - 0x2e, 0xb9, 0x0d, 0x9c, 0x2f, 0x25, 0x2d, 0x23, 0x93, 0x7c, 0x1d, 0x96, 0xec, 0x27, 0x62, 0x5a, - 0xaa, 0x2a, 0x1f, 0x9b, 0x69, 0xa9, 0x3a, 0xe3, 0x5d, 0x99, 0x64, 0xc8, 0x8d, 0x55, 0xdd, 0xc9, - 0xad, 0x8f, 0xe5, 0xe5, 0xd5, 0x0b, 0xf6, 0x25, 0x54, 0x1d, 0x32, 0x8b, 0x9c, 0xe5, 0x0f, 0xe3, - 0xec, 0x5c, 0x73, 0xcd, 0xed, 0xa5, 0x84, 0x73, 0x77, 0x85, 0x1a, 0x6f, 0xb2, 0x7c, 0x06, 0xc2, - 0x1e, 0x50, 0x36, 0xb9, 0x61, 0x0f, 0xcc, 0x84, 0x73, 0xc3, 0x1e, 0x58, 0x49, 0xe7, 0x45, 0x7b, - 0x90, 0x05, 0xd8, 0x46, 0x08, 0xcb, 0x85, 0x44, 0x0d, 0x2d, 0x2c, 0xd5, 0x99, 0x6d, 0xdd, 0x2b, - 0x2f, 0xcf, 0xef, 0xb0, 0xd5, 0x8c, 0x52, 0x2f, 0xb7, 0x54, 0x22, 0xe2, 0xaf, 0x43, 0xcb, 0x7c, - 0xda, 0xa3, 0x2d, 0x44, 0xc5, 0x83, 0x24, 0x6d, 0x21, 0xaa, 0xde, 0x02, 0xa9, 0xcd, 0x65, 0x2d, - 0xb3, 0x1b, 0xdc, 0x5c, 0xfb, 0x25, 0x44, 0xae, 0x32, 0xab, 0x9e, 0x78, 0xe4, 0x2a, 0xb3, 0xf2, - 0xf9, 0x84, 0xda, 0x5c, 0xb6, 0x6a, 0xcd, 0x45, 0x44, 0x26, 0xd9, 0x57, 0x60, 0xd9, 0xc8, 0x82, - 0x3a, 0x98, 0x86, 0x7d, 0xcd, 0xa8, 0xe5, 0x1c, 0xd9, 0x6e, 0x95, 0xe7, 0xe9, 0x5e, 0xa2, 0xf6, - 0x57, 0x5c, 0x6b, 0x12, 0xc8, 0xa4, 0x5b, 0xd0, 0x34, 0x33, 0xac, 0x5e, 0xd2, 0xee, 0x25, 0xa3, - 0xca, 0x4c, 0x17, 0xbd, 0xed, 0xb0, 0x3f, 0x76, 0xa0, 0x65, 0xe5, 0x2b, 0x59, 0xf1, 0xf7, 0x42, - 0x3b, 0x1d, 0xb3, 0xce, 0x6c, 0xc8, 0xf5, 0x68, 0x90, 0x7b, 0x1b, 0x5f, 0xb0, 0x16, 0xe1, 0x63, - 0xeb, 0x04, 0x73, 0xb3, 0xf8, 0x42, 0xfc, 0x45, 0x91, 0xc0, 0xcc, 0x23, 0x7e, 0x71, 0xdb, 0x61, - 0x1f, 0x88, 0xff, 0x40, 0x50, 0x11, 0x0b, 0x66, 0x28, 0xd2, 0xe2, 0x92, 0x99, 0x7f, 0x00, 0x70, - 0xc3, 0xb9, 0xed, 0xb0, 0xaf, 0x89, 0x87, 0xe0, 0xf2, 0x5b, 0x5a, 0xf9, 0x57, 0xfd, 0xde, 0xbd, - 0x46, 0xb3, 0xb9, 0xe2, 0x5e, 0xb6, 0x66, 0x53, 0xb4, 0x24, 0x77, 0xc4, 0xe8, 0xe4, 0xfb, 0xfe, - 0x5c, 0x25, 0x96, 0xde, 0xfc, 0x9f, 0x3d, 0xc8, 0xb1, 0x18, 0xa4, 0x24, 0xb7, 0xd8, 0xe3, 0x15, - 0x9b, 0x71, 0x37, 0x68, 0xac, 0xd7, 0xdc, 0x37, 0xcf, 0x1c, 0xeb, 0x2d, 0x3a, 0x91, 0xe2, 0x88, - 0xf7, 0x01, 0xf2, 0x80, 0x1c, 0x2b, 0xc4, 0x9f, 0xb4, 0x55, 0x28, 0xc7, 0xec, 0x6c, 0x1e, 0x54, - 0x61, 0x2a, 0x6c, 0xf1, 0xab, 0x42, 0x54, 0x25, 0x7d, 0xaa, 0x47, 0x5f, 0x0e, 0x9d, 0x75, 0xbb, - 0x55, 0x55, 0x55, 0x82, 0xaa, 0xda, 0x67, 0x4f, 0x60, 0x71, 0x2f, 0x8a, 0x9e, 0x4d, 0x62, 0x1d, - 0x90, 0xb6, 0xe3, 0x37, 0xbb, 0x7e, 0x7a, 0xdc, 0x2d, 0xcc, 0xc2, 0xbd, 0x4a, 0x4d, 0x75, 0x59, - 0xc7, 0x68, 0xea, 0xd6, 0xc7, 0x79, 0xc0, 0xef, 0x05, 0xf3, 0x61, 0x45, 0x7b, 0x00, 0x7a, 0xe0, - 0x5d, 0xbb, 0x19, 0x33, 0xee, 0x56, 0xea, 0xc2, 0xf2, 0xc9, 0xd4, 0x68, 0x6f, 0xa5, 0xaa, 0xcd, - 0xdb, 0x0e, 0xdb, 0x87, 0xd6, 0x36, 0xef, 0x47, 0x03, 0x2e, 0x23, 0x2e, 0xab, 0xf9, 0xc0, 0x75, - 0xa8, 0xa6, 0xbb, 0x68, 0x81, 0xb6, 0x4e, 0x8c, 0xfd, 0x69, 0xc2, 0xbf, 0x71, 0xeb, 0x63, 0x19, - 0xcb, 0x79, 0xa1, 0x74, 0xa2, 0x8a, 0x3f, 0x59, 0x3a, 0xb1, 0x10, 0xb0, 0xb2, 0x74, 0x62, 0x29, - 0x60, 0x65, 0x2d, 0xb5, 0x8a, 0x7f, 0xb1, 0x11, 0xac, 0x94, 0x62, 0x5c, 0xda, 0x8f, 0x38, 0x2b, - 0x32, 0xd6, 0xbd, 0x7a, 0x36, 0x81, 0xdd, 0xdb, 0x86, 0xdd, 0xdb, 0x01, 0x2c, 0x6e, 0x73, 0xb1, - 0x58, 0xe2, 0xd6, 0xbb, 0xf0, 0xe0, 0xcc, 0xbc, 0x21, 0x2f, 0x2a, 0x45, 0xaa, 0xb3, 0x8d, 0x1e, - 0x5d, 0x39, 0xb3, 0xaf, 0x42, 0xf3, 0x3e, 0xcf, 0xd4, 0x35, 0xb7, 0xf6, 0xc6, 0x0a, 0xf7, 0xde, - 0xdd, 0x8a, 0x5b, 0x72, 0x9b, 0x67, 0xa8, 0xb5, 0x5b, 0x7c, 0x30, 0xe4, 0x42, 0x3d, 0xf5, 0x82, - 0xc1, 0x0b, 0xf6, 0x2b, 0xd4, 0xb8, 0xce, 0x9a, 0x59, 0x37, 0x6e, 0x47, 0xcd, 0xc6, 0x97, 0x0b, - 0x78, 0x55, 0xcb, 0x61, 0x34, 0xe0, 0x86, 0xf9, 0x0f, 0xa1, 0x69, 0xa4, 0x74, 0x69, 0x01, 0x2a, - 0xa7, 0xa7, 0x69, 0x01, 0xaa, 0xc8, 0x00, 0x73, 0x6f, 0x50, 0x3f, 0x2e, 0xbb, 0x9a, 0xf7, 0x23, - 0xb2, 0xbe, 0xf2, 0x9e, 0x6e, 0x7d, 0xec, 0x8f, 0xb3, 0x17, 0xec, 0x29, 0x3d, 0x3e, 0x33, 0xaf, - 0xf2, 0x73, 0x6f, 0xb0, 0x78, 0xeb, 0xaf, 0x17, 0xcb, 0xa8, 0xb2, 0x3d, 0x44, 0xd1, 0x15, 0x79, - 0x09, 0x9f, 0x01, 0x38, 0xc8, 0xa2, 0x78, 0xdb, 0xe7, 0xe3, 0x28, 0xcc, 0x75, 0x6d, 0x7e, 0x5d, - 0x9d, 0xeb, 0x2f, 0xe3, 0xce, 0x9a, 0x3d, 0x35, 0xfc, 0x71, 0x2b, 0x13, 0x42, 0x31, 0xd7, 0x99, - 0x37, 0xda, 0x7a, 0x41, 0x2a, 0x6e, 0xb5, 0x6f, 0x3b, 0xe8, 0x5d, 0xe7, 0x11, 0x55, 0xed, 0x5d, - 0x97, 0x82, 0xb5, 0x5a, 0xed, 0x55, 0x84, 0x5f, 0xf7, 0xa1, 0x91, 0x87, 0xe8, 0x2e, 0xe5, 0x69, - 0x79, 0x56, 0x40, 0x4f, 0x5b, 0xc5, 0x52, 0xe0, 0xcc, 0x6d, 0xd3, 0x52, 0x01, 0x5b, 0xc0, 0xa5, - 0xa2, 0x68, 0x58, 0x00, 0xab, 0x62, 0x80, 0xda, 0xc4, 0xd3, 0x05, 0xac, 0x9a, 0x49, 0x45, 0xf0, - 0x4a, 0x4b, 0x73, 0x65, 0xec, 0xc7, 0x3a, 0x67, 0x23, 0xb7, 0x8a, 0xcb, 0x5f, 0x54, 0xcd, 0x63, - 0x58, 0x29, 0x05, 0x2e, 0xb4, 0x48, 0x9f, 0x15, 0x2f, 0xd2, 0x22, 0x7d, 0x66, 0xcc, 0xc3, 0xbd, - 0x48, 0x5d, 0x2e, 0xbb, 0x80, 0x5d, 0xa6, 0xa7, 0x41, 0xd6, 0x3f, 0xfe, 0xc0, 0xd9, 0x38, 0x9c, - 0xa3, 0xff, 0x4c, 0xfb, 0xd4, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x2e, 0xa2, 0xa7, 0xf7, 0x65, - 0x4d, 0x00, 0x00, + // 6239 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0xdd, 0x8f, 0x1c, 0xd9, + 0x55, 0x77, 0x75, 0xf7, 0x7c, 0xf4, 0xe9, 0x9e, 0x99, 0x9e, 0x3b, 0xe3, 0x71, 0xbb, 0x77, 0xd7, + 0xeb, 0xad, 0x58, 0x6b, 0x67, 0x58, 0x6c, 0xef, 0x24, 0x59, 0x36, 0xbb, 0x90, 0x60, 0xcf, 0x8c, + 0x3d, 0x4e, 0x66, 0xed, 0x49, 0x8d, 0x1d, 0x43, 0x02, 0xea, 0xd4, 0x74, 0xdf, 0xe9, 0xa9, 0xb8, + 0xbb, 0xaa, 0x53, 0x55, 0x3d, 0xe3, 0xce, 0x62, 0x29, 0x10, 0xc4, 0x13, 0x11, 0x42, 0xf0, 0x12, + 0x24, 0x84, 0x14, 0x24, 0x14, 0xfe, 0x00, 0x78, 0x09, 0x48, 0x3c, 0xf0, 0x12, 0x24, 0xc4, 0x43, + 0x9e, 0x22, 0x1e, 0xe1, 0x05, 0x10, 0x2f, 0x48, 0xbc, 0x22, 0x74, 0xce, 0xfd, 0xa8, 0x7b, 0xab, + 0xaa, 0x3d, 0xce, 0x07, 0xbc, 0xf5, 0xfd, 0xdd, 0x53, 0xf7, 0xf3, 0x7c, 0xdd, 0x73, 0xcf, 0x6d, + 0xa8, 0xc7, 0xe3, 0xde, 0xcd, 0x71, 0x1c, 0xa5, 0x11, 0x9b, 0x1b, 0x86, 0xf1, 0xb8, 0xd7, 0x79, + 0x7d, 0x10, 0x45, 0x83, 0x21, 0xbf, 0xe5, 0x8f, 0x83, 0x5b, 0x7e, 0x18, 0x46, 0xa9, 0x9f, 0x06, + 0x51, 0x98, 0x08, 0x22, 0xf7, 0x6b, 0xb0, 0x7c, 0x9f, 0x87, 0x87, 0x9c, 0xf7, 0x3d, 0xfe, 0x8d, + 0x09, 0x4f, 0x52, 0xf6, 0x0b, 0xb0, 0xea, 0xf3, 0x6f, 0x72, 0xde, 0xef, 0x8e, 0xfd, 0x24, 0x19, + 0x9f, 0xc4, 0x7e, 0xc2, 0xdb, 0xce, 0x55, 0xe7, 0x46, 0xd3, 0x6b, 0x89, 0x8a, 0x03, 0x8d, 0xb3, + 0xb7, 0xa0, 0x99, 0x20, 0x29, 0x0f, 0xd3, 0x38, 0x1a, 0x4f, 0xdb, 0x15, 0xa2, 0x6b, 0x20, 0xb6, + 0x2b, 0x20, 0x77, 0x08, 0x2b, 0xba, 0x87, 0x64, 0x1c, 0x85, 0x09, 0x67, 0xb7, 0x61, 0xbd, 0x17, + 0x8c, 0x4f, 0x78, 0xdc, 0xa5, 0x8f, 0x47, 0x21, 0x1f, 0x45, 0x61, 0xd0, 0x6b, 0x3b, 0x57, 0xab, + 0x37, 0xea, 0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x47, 0xb2, 0x86, 0x5d, 0x87, 0x15, 0x1e, 0x0a, 0x9c, + 0xf7, 0xe9, 0x2b, 0xd9, 0xd5, 0x72, 0x06, 0xe3, 0x07, 0xee, 0xdf, 0x3b, 0xb0, 0xfa, 0x20, 0x0c, + 0xd2, 0xa7, 0xfe, 0x70, 0xc8, 0x53, 0x35, 0xa7, 0xeb, 0xb0, 0x72, 0x46, 0x00, 0xcd, 0xe9, 0x2c, + 0x8a, 0xfb, 0x72, 0x46, 0xcb, 0x02, 0x3e, 0x90, 0xe8, 0xcc, 0x91, 0x55, 0x66, 0x8e, 0xac, 0x74, + 0xb9, 0xaa, 0x33, 0x96, 0xeb, 0x3a, 0xac, 0xc4, 0xbc, 0x17, 0x9d, 0xf2, 0x78, 0xda, 0x3d, 0x0b, + 0xc2, 0x7e, 0x74, 0xd6, 0xae, 0x5d, 0x75, 0x6e, 0xcc, 0x79, 0xcb, 0x0a, 0x7e, 0x4a, 0xa8, 0xbb, + 0x0e, 0xcc, 0x9c, 0x85, 0x58, 0x37, 0x77, 0x00, 0x6b, 0x4f, 0xc2, 0x61, 0xd4, 0x7b, 0xf6, 0x53, + 0xce, 0xae, 0xa4, 0xfb, 0x4a, 0x69, 0xf7, 0x1b, 0xb0, 0x6e, 0x77, 0x24, 0x07, 0xc0, 0xe1, 0xe2, + 0xf6, 0x89, 0x1f, 0x0e, 0xb8, 0x6a, 0x52, 0x0d, 0xe1, 0x93, 0xd0, 0xea, 0x4d, 0xe2, 0x98, 0x87, + 0x85, 0x31, 0xac, 0x48, 0x5c, 0x0f, 0xe2, 0x2d, 0x68, 0x86, 0xfc, 0x2c, 0x23, 0x93, 0x2c, 0x13, + 0xf2, 0x33, 0x45, 0xe2, 0xb6, 0x61, 0x23, 0xdf, 0x8d, 0x1c, 0xc0, 0x77, 0x2b, 0xd0, 0x78, 0x1c, + 0xfb, 0x61, 0xe2, 0xf7, 0x90, 0x8b, 0x59, 0x1b, 0x16, 0xd2, 0xe7, 0xdd, 0x13, 0x3f, 0x39, 0xa1, + 0xee, 0xea, 0x9e, 0x2a, 0xb2, 0x0d, 0x98, 0xf7, 0x47, 0xd1, 0x24, 0x4c, 0xa9, 0x83, 0xaa, 0x27, + 0x4b, 0xec, 0x1d, 0x58, 0x0d, 0x27, 0xa3, 0x6e, 0x2f, 0x0a, 0x8f, 0x83, 0x78, 0x24, 0x64, 0x81, + 0xf6, 0x6b, 0xce, 0x2b, 0x56, 0xb0, 0x2b, 0x00, 0x47, 0xb8, 0x0e, 0xa2, 0x8b, 0x1a, 0x75, 0x61, + 0x20, 0xcc, 0x85, 0xa6, 0x2c, 0xf1, 0x60, 0x70, 0x92, 0xb6, 0xe7, 0xa8, 0x21, 0x0b, 0xc3, 0x36, + 0xd2, 0x60, 0xc4, 0xbb, 0x49, 0xea, 0x8f, 0xc6, 0xed, 0x79, 0x1a, 0x8d, 0x81, 0x50, 0x7d, 0x94, + 0xfa, 0xc3, 0xee, 0x31, 0xe7, 0x49, 0x7b, 0x41, 0xd6, 0x6b, 0x84, 0xbd, 0x0d, 0xcb, 0x7d, 0x9e, + 0xa4, 0x5d, 0xbf, 0xdf, 0x8f, 0x79, 0x92, 0xf0, 0xa4, 0xbd, 0x48, 0xdc, 0x98, 0x43, 0x71, 0xd5, + 0xee, 0xf3, 0xd4, 0x58, 0x9d, 0x44, 0xee, 0x8e, 0xbb, 0x0f, 0xcc, 0x80, 0x77, 0x78, 0xea, 0x07, + 0xc3, 0x84, 0xbd, 0x07, 0xcd, 0xd4, 0x20, 0x26, 0xe9, 0x6b, 0x6c, 0xb1, 0x9b, 0xa4, 0x36, 0x6e, + 0x1a, 0x1f, 0x78, 0x16, 0x9d, 0x7b, 0x1f, 0x16, 0xef, 0x71, 0xbe, 0x1f, 0x8c, 0x82, 0x94, 0x6d, + 0xc0, 0xdc, 0x71, 0xf0, 0x9c, 0x8b, 0xcd, 0xae, 0xee, 0x5d, 0xf0, 0x44, 0x91, 0x75, 0x60, 0x61, + 0xcc, 0xe3, 0x1e, 0x57, 0xcb, 0xbf, 0x77, 0xc1, 0x53, 0xc0, 0xdd, 0x05, 0x98, 0x1b, 0xe2, 0xc7, + 0xee, 0xf7, 0x2b, 0xd0, 0x38, 0xe4, 0xa1, 0x66, 0x22, 0x06, 0x35, 0x9c, 0x92, 0x64, 0x1c, 0xfa, + 0xcd, 0xde, 0x84, 0x06, 0x4d, 0x33, 0x49, 0xe3, 0x20, 0x1c, 0x50, 0x63, 0x75, 0x0f, 0x10, 0x3a, + 0x24, 0x84, 0xb5, 0xa0, 0xea, 0x8f, 0x52, 0xda, 0xc1, 0xaa, 0x87, 0x3f, 0x91, 0xc1, 0xc6, 0xfe, + 0x74, 0x84, 0xbc, 0xa8, 0x77, 0xad, 0xe9, 0x35, 0x24, 0xb6, 0x87, 0xdb, 0x76, 0x13, 0xd6, 0x4c, + 0x12, 0xd5, 0xfa, 0x1c, 0xb5, 0xbe, 0x6a, 0x50, 0xca, 0x4e, 0xae, 0xc3, 0x8a, 0xa2, 0x8f, 0xc5, + 0x60, 0x69, 0x1f, 0xeb, 0xde, 0xb2, 0x84, 0xd5, 0x14, 0x6e, 0x40, 0xeb, 0x38, 0x08, 0xfd, 0x61, + 0xb7, 0x37, 0x4c, 0x4f, 0xbb, 0x7d, 0x3e, 0x4c, 0x7d, 0xda, 0xd1, 0x39, 0x6f, 0x99, 0xf0, 0xed, + 0x61, 0x7a, 0xba, 0x83, 0x28, 0x7b, 0x07, 0xea, 0xc7, 0x9c, 0x77, 0x69, 0x25, 0xda, 0x8b, 0x57, + 0x9d, 0x1b, 0x8d, 0xad, 0x15, 0xb9, 0xf4, 0x6a, 0x75, 0xbd, 0xc5, 0x63, 0xf9, 0xcb, 0xfd, 0x63, + 0x07, 0x9a, 0x62, 0xa9, 0xa4, 0x0a, 0xbd, 0x06, 0x4b, 0x6a, 0x44, 0x3c, 0x8e, 0xa3, 0x58, 0xb2, + 0xbf, 0x0d, 0xb2, 0x4d, 0x68, 0x29, 0x60, 0x1c, 0xf3, 0x60, 0xe4, 0x0f, 0xb8, 0x94, 0xb7, 0x02, + 0xce, 0xb6, 0xb2, 0x16, 0xe3, 0x68, 0x92, 0x0a, 0x25, 0xd6, 0xd8, 0x6a, 0xca, 0x41, 0x79, 0x88, + 0x79, 0x36, 0x89, 0xfb, 0x1d, 0x07, 0x18, 0x0e, 0xeb, 0x71, 0x24, 0xaa, 0xe5, 0x2a, 0xe4, 0x77, + 0xc0, 0x79, 0xe5, 0x1d, 0xa8, 0xcc, 0xda, 0x81, 0x6b, 0x30, 0x4f, 0x5d, 0xa2, 0xac, 0x56, 0x0b, + 0xc3, 0x92, 0x75, 0xee, 0xf7, 0x1c, 0x68, 0xa2, 0xe6, 0x08, 0xf9, 0xf0, 0x20, 0x0a, 0xc2, 0x94, + 0xdd, 0x06, 0x76, 0x3c, 0x09, 0xfb, 0x41, 0x38, 0xe8, 0xa6, 0xcf, 0x83, 0x7e, 0xf7, 0x68, 0x8a, + 0x4d, 0xd0, 0x78, 0xf6, 0x2e, 0x78, 0x25, 0x75, 0xec, 0x1d, 0x68, 0x59, 0x68, 0x92, 0xc6, 0x62, + 0x54, 0x7b, 0x17, 0xbc, 0x42, 0x0d, 0xca, 0x7f, 0x34, 0x49, 0xc7, 0x93, 0xb4, 0x1b, 0x84, 0x7d, + 0xfe, 0x9c, 0xd6, 0x6c, 0xc9, 0xb3, 0xb0, 0xbb, 0xcb, 0xd0, 0x34, 0xbf, 0x73, 0x3f, 0x07, 0xad, + 0x7d, 0x54, 0x0c, 0x61, 0x10, 0x0e, 0xee, 0x08, 0xe9, 0x45, 0x6d, 0x35, 0x9e, 0x1c, 0x3d, 0xe3, + 0x53, 0xb9, 0x8f, 0xb2, 0x84, 0x22, 0x71, 0x12, 0x25, 0xa9, 0x5c, 0x17, 0xfa, 0xed, 0xfe, 0x8b, + 0x03, 0x2b, 0xb8, 0xe8, 0x1f, 0xf9, 0xe1, 0x54, 0xad, 0xf8, 0x3e, 0x34, 0xb1, 0xa9, 0xc7, 0xd1, + 0x1d, 0xa1, 0xf3, 0x84, 0x2c, 0xdf, 0x90, 0x8b, 0x94, 0xa3, 0xbe, 0x69, 0x92, 0xa2, 0x99, 0x9e, + 0x7a, 0xd6, 0xd7, 0x28, 0x74, 0xa9, 0x1f, 0x0f, 0x78, 0x4a, 0xda, 0x50, 0x6a, 0x47, 0x10, 0xd0, + 0x76, 0x14, 0x1e, 0xb3, 0xab, 0xd0, 0x4c, 0xfc, 0xb4, 0x3b, 0xe6, 0x31, 0xad, 0x1a, 0x09, 0x4e, + 0xd5, 0x83, 0xc4, 0x4f, 0x0f, 0x78, 0x7c, 0x77, 0x9a, 0xf2, 0xce, 0xe7, 0x61, 0xb5, 0xd0, 0x0b, + 0xca, 0x6a, 0x36, 0x45, 0xfc, 0xc9, 0xd6, 0x61, 0xee, 0xd4, 0x1f, 0x4e, 0xb8, 0x54, 0xd2, 0xa2, + 0xf0, 0x41, 0xe5, 0x7d, 0xc7, 0x7d, 0x1b, 0x5a, 0xd9, 0xb0, 0x25, 0xd3, 0x33, 0xa8, 0xe1, 0x0a, + 0xca, 0x06, 0xe8, 0xb7, 0xfb, 0xdb, 0x8e, 0x20, 0xdc, 0x8e, 0x02, 0xad, 0xf0, 0x90, 0x10, 0xf5, + 0xa2, 0x22, 0xc4, 0xdf, 0x33, 0x0d, 0xc2, 0xcf, 0x3e, 0x59, 0xf7, 0x3a, 0xac, 0x1a, 0x43, 0x78, + 0xc9, 0x60, 0xbf, 0xe3, 0xc0, 0xea, 0x43, 0x7e, 0x26, 0x77, 0x5d, 0x8d, 0xf6, 0x7d, 0xa8, 0xa5, + 0xd3, 0xb1, 0x70, 0xb2, 0x96, 0xb7, 0xae, 0xc9, 0x4d, 0x2b, 0xd0, 0xdd, 0x94, 0xc5, 0xc7, 0xd3, + 0x31, 0xf7, 0xe8, 0x0b, 0xf7, 0x73, 0xd0, 0x30, 0x40, 0x76, 0x09, 0xd6, 0x9e, 0x3e, 0x78, 0xfc, + 0x70, 0xf7, 0xf0, 0xb0, 0x7b, 0xf0, 0xe4, 0xee, 0x17, 0x77, 0x7f, 0xbd, 0xbb, 0x77, 0xe7, 0x70, + 0xaf, 0x75, 0x81, 0x6d, 0x00, 0x7b, 0xb8, 0x7b, 0xf8, 0x78, 0x77, 0xc7, 0xc2, 0x1d, 0xb7, 0x03, + 0xed, 0x87, 0xfc, 0xec, 0x69, 0x90, 0x86, 0x3c, 0x49, 0xec, 0xde, 0xdc, 0x9b, 0xc0, 0xcc, 0x21, + 0xc8, 0x59, 0xb5, 0x61, 0x41, 0x5a, 0x1c, 0x65, 0x70, 0x65, 0xd1, 0x7d, 0x1b, 0xd8, 0x61, 0x30, + 0x08, 0x3f, 0xe2, 0x49, 0xe2, 0x0f, 0xb4, 0x2a, 0x68, 0x41, 0x75, 0x94, 0x0c, 0xa4, 0x06, 0xc0, + 0x9f, 0xee, 0xa7, 0x60, 0xcd, 0xa2, 0x93, 0x0d, 0xbf, 0x0e, 0xf5, 0x24, 0x18, 0x84, 0x7e, 0x3a, + 0x89, 0xb9, 0x6c, 0x3a, 0x03, 0xdc, 0x7b, 0xb0, 0xfe, 0x65, 0x1e, 0x07, 0xc7, 0xd3, 0xf3, 0x9a, + 0xb7, 0xdb, 0xa9, 0xe4, 0xdb, 0xd9, 0x85, 0x8b, 0xb9, 0x76, 0x64, 0xf7, 0x82, 0x11, 0xe5, 0x76, + 0x2d, 0x7a, 0xa2, 0x60, 0x88, 0x65, 0xc5, 0x14, 0x4b, 0xf7, 0x09, 0xb0, 0xed, 0x28, 0x0c, 0x79, + 0x2f, 0x3d, 0xe0, 0x3c, 0xce, 0x3c, 0xe7, 0x8c, 0xeb, 0x1a, 0x5b, 0x97, 0xe4, 0x3e, 0xe6, 0x65, + 0x5d, 0xb2, 0x23, 0x83, 0xda, 0x98, 0xc7, 0x23, 0x6a, 0x78, 0xd1, 0xa3, 0xdf, 0xee, 0x45, 0x58, + 0xb3, 0x9a, 0x95, 0x4e, 0xcf, 0xbb, 0x70, 0x71, 0x27, 0x48, 0x7a, 0xc5, 0x0e, 0xdb, 0xb0, 0x30, + 0x9e, 0x1c, 0x75, 0x33, 0x99, 0x52, 0x45, 0xf4, 0x05, 0xf2, 0x9f, 0xc8, 0xc6, 0x7e, 0xcf, 0x81, + 0xda, 0xde, 0xe3, 0xfd, 0x6d, 0xd6, 0x81, 0xc5, 0x20, 0xec, 0x45, 0x23, 0x54, 0xbb, 0x62, 0xd2, + 0xba, 0x3c, 0x53, 0x56, 0x5e, 0x87, 0x3a, 0x69, 0x6b, 0x74, 0x6f, 0xa4, 0x93, 0x9b, 0x01, 0xe8, + 0x5a, 0xf1, 0xe7, 0xe3, 0x20, 0x26, 0xdf, 0x49, 0x79, 0x44, 0x35, 0xd2, 0x88, 0xc5, 0x0a, 0xf7, + 0x7f, 0x6a, 0xb0, 0x20, 0x75, 0x35, 0xf5, 0xd7, 0x4b, 0x83, 0x53, 0x2e, 0x47, 0x22, 0x4b, 0x68, + 0xe5, 0x62, 0x3e, 0x8a, 0x52, 0xde, 0xb5, 0xb6, 0xc1, 0x06, 0x91, 0xaa, 0x27, 0x1a, 0xea, 0x8e, + 0x51, 0xeb, 0xd3, 0xc8, 0xea, 0x9e, 0x0d, 0xe2, 0x62, 0x21, 0xd0, 0x0d, 0xfa, 0x34, 0xa6, 0x9a, + 0xa7, 0x8a, 0xb8, 0x12, 0x3d, 0x7f, 0xec, 0xf7, 0x82, 0x74, 0x2a, 0x85, 0x5b, 0x97, 0xb1, 0xed, + 0x61, 0xd4, 0xf3, 0x87, 0xdd, 0x23, 0x7f, 0xe8, 0x87, 0x3d, 0x2e, 0xfd, 0x37, 0x1b, 0x44, 0x17, + 0x4d, 0x0e, 0x49, 0x91, 0x09, 0x37, 0x2e, 0x87, 0xa2, 0xab, 0xd7, 0x8b, 0x46, 0xa3, 0x20, 0x45, + 0xcf, 0x8e, 0xac, 0x7e, 0xd5, 0x33, 0x10, 0x9a, 0x89, 0x28, 0x9d, 0x89, 0xd5, 0xab, 0x8b, 0xde, + 0x2c, 0x10, 0x5b, 0x41, 0xd7, 0x01, 0x15, 0xd2, 0xb3, 0xb3, 0x36, 0x88, 0x56, 0x32, 0x04, 0xf7, + 0x61, 0x12, 0x26, 0x3c, 0x4d, 0x87, 0xbc, 0xaf, 0x07, 0xd4, 0x20, 0xb2, 0x62, 0x05, 0xbb, 0x0d, + 0x6b, 0xc2, 0xd9, 0x4c, 0xfc, 0x34, 0x4a, 0x4e, 0x82, 0xa4, 0x9b, 0xa0, 0xdb, 0xd6, 0x24, 0xfa, + 0xb2, 0x2a, 0xf6, 0x3e, 0x5c, 0xca, 0xc1, 0x31, 0xef, 0xf1, 0xe0, 0x94, 0xf7, 0xdb, 0x4b, 0xf4, + 0xd5, 0xac, 0x6a, 0x76, 0x15, 0x1a, 0xe8, 0x63, 0x4f, 0xc6, 0x7d, 0x1f, 0xed, 0xf0, 0x32, 0xed, + 0x83, 0x09, 0xb1, 0x77, 0x61, 0x69, 0xcc, 0x85, 0xb1, 0x3c, 0x49, 0x87, 0xbd, 0xa4, 0xbd, 0x42, + 0x96, 0xac, 0x21, 0x85, 0x09, 0x39, 0xd7, 0xb3, 0x29, 0x90, 0x29, 0x7b, 0x09, 0x39, 0x5b, 0xfe, + 0xb4, 0xdd, 0x22, 0x76, 0xcb, 0x00, 0x92, 0x91, 0x38, 0x38, 0xf5, 0x53, 0xde, 0x5e, 0x25, 0xde, + 0x52, 0x45, 0xf7, 0xcf, 0x1c, 0x58, 0xdb, 0x0f, 0x92, 0x54, 0x32, 0xa1, 0x56, 0xc7, 0x6f, 0x42, + 0x43, 0xb0, 0x5f, 0x37, 0x0a, 0x87, 0x53, 0xc9, 0x91, 0x20, 0xa0, 0x47, 0xe1, 0x70, 0xca, 0x3e, + 0x01, 0x4b, 0x41, 0x68, 0x92, 0x08, 0x19, 0x6e, 0x2a, 0x90, 0x88, 0xde, 0x84, 0xc6, 0x78, 0x72, + 0x34, 0x0c, 0x7a, 0x82, 0xa4, 0x2a, 0x5a, 0x11, 0x10, 0x11, 0xa0, 0x93, 0x24, 0x46, 0x22, 0x28, + 0x6a, 0x44, 0xd1, 0x90, 0x18, 0x92, 0xb8, 0x77, 0x61, 0xdd, 0x1e, 0xa0, 0x54, 0x56, 0x9b, 0xb0, + 0x28, 0x79, 0x3b, 0x69, 0x37, 0x68, 0x7d, 0x96, 0xe5, 0xfa, 0x48, 0x52, 0x4f, 0xd7, 0xbb, 0x7f, + 0x51, 0x83, 0x35, 0x89, 0x6e, 0x0f, 0xa3, 0x84, 0x1f, 0x4e, 0x46, 0x23, 0x3f, 0x2e, 0x11, 0x1a, + 0xe7, 0x1c, 0xa1, 0xa9, 0xd8, 0x42, 0x83, 0xac, 0x7c, 0xe2, 0x07, 0xa1, 0xf0, 0xf0, 0x84, 0xc4, + 0x19, 0x08, 0xbb, 0x01, 0x2b, 0xbd, 0x61, 0x94, 0x08, 0xaf, 0xc7, 0x3c, 0x3e, 0xe5, 0xe1, 0xa2, + 0x90, 0xcf, 0x95, 0x09, 0xb9, 0x29, 0xa4, 0xf3, 0x39, 0x21, 0x75, 0xa1, 0x89, 0x8d, 0x72, 0xa5, + 0x73, 0x16, 0x84, 0x17, 0x66, 0x62, 0x38, 0x9e, 0xbc, 0x48, 0x08, 0xf9, 0x5b, 0x29, 0x13, 0x08, + 0x3c, 0x9d, 0xa1, 0x4e, 0x33, 0xa8, 0xeb, 0x52, 0x20, 0x8a, 0x55, 0xec, 0x1e, 0x80, 0xe8, 0x8b, + 0xcc, 0x38, 0x90, 0x19, 0x7f, 0xdb, 0xde, 0x11, 0x73, 0xed, 0x6f, 0x62, 0x61, 0x12, 0x73, 0x32, + 0xe4, 0xc6, 0x97, 0xee, 0xc7, 0xd0, 0x30, 0xaa, 0xd8, 0x45, 0x58, 0xdd, 0x7e, 0xf4, 0xe8, 0x60, + 0xd7, 0xbb, 0xf3, 0xf8, 0xc1, 0x97, 0x77, 0xbb, 0xdb, 0xfb, 0x8f, 0x0e, 0x77, 0x5b, 0x17, 0x10, + 0xde, 0x7f, 0xb4, 0x7d, 0x67, 0xbf, 0x7b, 0xef, 0x91, 0xb7, 0xad, 0x60, 0x07, 0x6d, 0xbc, 0xb7, + 0xfb, 0xd1, 0xa3, 0xc7, 0xbb, 0x16, 0x5e, 0x61, 0x2d, 0x68, 0xde, 0xf5, 0x76, 0xef, 0x6c, 0xef, + 0x49, 0xa4, 0xca, 0xd6, 0xa1, 0x75, 0xef, 0xc9, 0xc3, 0x9d, 0x07, 0x0f, 0xef, 0x77, 0xb7, 0xef, + 0x3c, 0xdc, 0xde, 0xdd, 0xdf, 0xdd, 0x69, 0xd5, 0xdc, 0xbf, 0x73, 0xe0, 0x22, 0x8d, 0xb2, 0x9f, + 0x17, 0x88, 0xab, 0xd0, 0xe8, 0x45, 0xd1, 0x98, 0xa3, 0xfe, 0xd6, 0x2a, 0xda, 0x84, 0x90, 0xd9, + 0x85, 0x42, 0x3c, 0x8e, 0xe2, 0x1e, 0x97, 0xf2, 0x00, 0x04, 0xdd, 0x43, 0x04, 0x99, 0x5d, 0x6e, + 0xa7, 0xa0, 0x10, 0xe2, 0xd0, 0x10, 0x98, 0x20, 0xd9, 0x80, 0xf9, 0xa3, 0x98, 0xfb, 0xbd, 0x13, + 0x29, 0x09, 0xb2, 0xc4, 0x3e, 0x99, 0x39, 0xe4, 0x3d, 0x5c, 0xed, 0x21, 0xef, 0x13, 0x87, 0x2c, + 0x7a, 0x2b, 0x12, 0xdf, 0x96, 0xb0, 0x7b, 0x00, 0x1b, 0xf9, 0x19, 0x48, 0x89, 0x79, 0xcf, 0x90, + 0x18, 0xe1, 0x1b, 0x77, 0x66, 0xef, 0x8f, 0x21, 0x3d, 0xff, 0xee, 0x40, 0x0d, 0xcd, 0xe7, 0x6c, + 0x53, 0x6b, 0x7a, 0x44, 0x55, 0xcb, 0x23, 0xa2, 0xe0, 0x01, 0x9e, 0x29, 0x84, 0x42, 0x15, 0x46, + 0xc7, 0x40, 0xb2, 0xfa, 0x98, 0xf7, 0x4e, 0x69, 0x4e, 0xba, 0x1e, 0x11, 0x64, 0x79, 0x74, 0x3c, + 0xe9, 0x6b, 0xc9, 0xf2, 0xaa, 0xac, 0xea, 0xe8, 0xcb, 0x85, 0xac, 0x8e, 0xbe, 0x6b, 0xc3, 0x42, + 0x10, 0x1e, 0x45, 0x93, 0xb0, 0x4f, 0x2c, 0xbe, 0xe8, 0xa9, 0x22, 0xaa, 0xca, 0x31, 0x89, 0x5e, + 0x30, 0x52, 0x0c, 0x9d, 0x01, 0x2e, 0xc3, 0x83, 0x49, 0x42, 0xee, 0x82, 0xf6, 0x02, 0xdf, 0x83, + 0x55, 0x03, 0x93, 0xab, 0xf9, 0x16, 0xcc, 0x8d, 0x11, 0x90, 0x4b, 0xa9, 0x94, 0x33, 0xf9, 0x19, + 0xa2, 0xc6, 0x6d, 0xc1, 0xf2, 0x7d, 0x9e, 0x3e, 0x08, 0x8f, 0x23, 0xd5, 0xd2, 0x8f, 0xab, 0xb0, + 0xa2, 0x21, 0xd9, 0xd0, 0x0d, 0x58, 0x09, 0xfa, 0x3c, 0x4c, 0x83, 0x74, 0xda, 0xb5, 0xce, 0x3f, + 0x79, 0x18, 0xfd, 0x33, 0x7f, 0x18, 0xf8, 0x89, 0xf4, 0x00, 0x44, 0x81, 0x6d, 0xc1, 0x3a, 0x1a, + 0x0f, 0x65, 0x0f, 0xf4, 0x16, 0x8b, 0x63, 0x58, 0x69, 0x1d, 0x8a, 0x37, 0xe2, 0x52, 0x7f, 0xeb, + 0x4f, 0x84, 0x9f, 0x52, 0x56, 0x85, 0xab, 0x26, 0x5a, 0xc2, 0x29, 0xcf, 0x09, 0x03, 0xa3, 0x81, + 0x42, 0x08, 0x68, 0x5e, 0x28, 0x9f, 0x7c, 0x08, 0xc8, 0x08, 0x23, 0x2d, 0x16, 0xc2, 0x48, 0xa8, + 0x9c, 0xa6, 0x61, 0x8f, 0xf7, 0xbb, 0x69, 0xd4, 0x25, 0x25, 0x4a, 0xbb, 0xb3, 0xe8, 0xe5, 0x61, + 0x0a, 0x78, 0xf1, 0x24, 0x0d, 0x79, 0x4a, 0x7a, 0x66, 0xd1, 0x53, 0x45, 0x94, 0x1f, 0x22, 0x11, + 0x26, 0xa1, 0xee, 0xc9, 0x12, 0x3a, 0x9a, 0x93, 0x38, 0x48, 0xda, 0x4d, 0x42, 0xe9, 0x37, 0xfb, + 0x34, 0x5c, 0x3c, 0xe2, 0x49, 0xda, 0x3d, 0xe1, 0x7e, 0x9f, 0xc7, 0xb4, 0xfb, 0x22, 0x3a, 0x25, + 0xec, 0x77, 0x79, 0x25, 0xf6, 0x7d, 0xca, 0xe3, 0x24, 0x88, 0x42, 0xb2, 0xdc, 0x75, 0x4f, 0x15, + 0xdd, 0x6f, 0x92, 0x3f, 0xac, 0xe3, 0x66, 0x4f, 0xc8, 0x98, 0xb3, 0xd7, 0xa0, 0x2e, 0xe6, 0x98, + 0x9c, 0xf8, 0xd2, 0x45, 0x5f, 0x24, 0xe0, 0xf0, 0xc4, 0x47, 0x8d, 0x60, 0x2d, 0x9b, 0x08, 0x44, + 0x36, 0x08, 0xdb, 0x13, 0xab, 0x76, 0x0d, 0x96, 0x55, 0x44, 0x2e, 0xe9, 0x0e, 0xf9, 0x71, 0xaa, + 0x8e, 0xd7, 0xe1, 0x64, 0x84, 0xdd, 0x25, 0xfb, 0xfc, 0x38, 0x75, 0x1f, 0xc2, 0xaa, 0x94, 0xe1, + 0x47, 0x63, 0xae, 0xba, 0xfe, 0x6c, 0x99, 0x75, 0x6b, 0x6c, 0xad, 0xd9, 0x42, 0x4f, 0x31, 0x82, + 0x9c, 0xc9, 0x73, 0x3d, 0x60, 0xa6, 0x4e, 0x90, 0x0d, 0x4a, 0x13, 0xa3, 0x0e, 0xf1, 0x72, 0x3a, + 0x16, 0x86, 0xeb, 0x93, 0x4c, 0x7a, 0x3d, 0xd4, 0x04, 0x42, 0x03, 0xaa, 0xa2, 0xfb, 0x7d, 0x07, + 0xd6, 0xa8, 0x35, 0x65, 0x9f, 0xf5, 0xc9, 0xef, 0xd5, 0x87, 0xd9, 0xec, 0x99, 0x81, 0x8d, 0x75, + 0x98, 0x33, 0x75, 0xad, 0x28, 0xfc, 0xe4, 0x67, 0xd9, 0x5a, 0xe1, 0x2c, 0xfb, 0x63, 0x07, 0x56, + 0x85, 0x32, 0x4c, 0xfd, 0x74, 0x92, 0xc8, 0xe9, 0xff, 0x32, 0x2c, 0x09, 0x3b, 0x25, 0xc5, 0x49, + 0x0e, 0x74, 0x5d, 0x4b, 0x3e, 0xa1, 0x82, 0x78, 0xef, 0x82, 0x67, 0x13, 0xb3, 0xcf, 0x43, 0xd3, + 0x0c, 0xab, 0xd2, 0x98, 0x1b, 0x5b, 0x97, 0xd5, 0x2c, 0x0b, 0x9c, 0xb3, 0x77, 0xc1, 0xb3, 0x3e, + 0x60, 0x1f, 0x92, 0xb3, 0x11, 0x76, 0xa9, 0x59, 0x19, 0x98, 0xba, 0x5c, 0xa2, 0xc0, 0xf5, 0xe7, + 0x06, 0xf9, 0xdd, 0x45, 0x98, 0x17, 0xde, 0xa5, 0x7b, 0x1f, 0x96, 0xac, 0x91, 0x5a, 0x67, 0xf4, + 0xa6, 0x38, 0xa3, 0x17, 0x42, 0x3a, 0x95, 0x62, 0x48, 0xc7, 0xfd, 0x76, 0x15, 0x18, 0x72, 0x5b, + 0x6e, 0x3b, 0xd1, 0xbd, 0x8d, 0xfa, 0xd6, 0x61, 0xa5, 0xe9, 0x99, 0x10, 0xbb, 0x09, 0xcc, 0x28, + 0xaa, 0xa8, 0x97, 0xb0, 0x1b, 0x25, 0x35, 0xa8, 0xe0, 0xa4, 0x61, 0x95, 0x26, 0x50, 0x1e, 0xcb, + 0xc4, 0xbe, 0x95, 0xd6, 0xa1, 0x69, 0x18, 0x4f, 0x92, 0x13, 0x74, 0xbf, 0xd5, 0x71, 0x46, 0x95, + 0xf3, 0x0c, 0x32, 0x7f, 0x2e, 0x83, 0x2c, 0xe4, 0x19, 0xc4, 0x74, 0xa8, 0x17, 0x2d, 0x87, 0x1a, + 0x1d, 0xb9, 0x11, 0xba, 0x7f, 0xe9, 0xb0, 0xd7, 0x1d, 0x61, 0xef, 0xf2, 0xf4, 0x62, 0x81, 0x6c, + 0x13, 0x5a, 0xd2, 0x15, 0xc8, 0xbc, 0x76, 0xa0, 0x35, 0x2e, 0xe0, 0xa8, 0x79, 0xf1, 0x63, 0xd2, + 0x00, 0x74, 0x82, 0x99, 0xf3, 0x32, 0xc0, 0xfd, 0x91, 0x03, 0x2d, 0xdc, 0x05, 0x8b, 0x53, 0x3f, + 0x00, 0x12, 0x94, 0x57, 0x64, 0x54, 0x8b, 0xf6, 0x67, 0xe7, 0xd3, 0xf7, 0xa1, 0x4e, 0x0d, 0x46, + 0x63, 0x1e, 0x4a, 0x36, 0x6d, 0xdb, 0x6c, 0x9a, 0xe9, 0xa8, 0xbd, 0x0b, 0x5e, 0x46, 0x6c, 0x30, + 0xe9, 0x3f, 0x39, 0xd0, 0x90, 0xc3, 0xfc, 0xa9, 0xcf, 0xe9, 0x1d, 0x58, 0x44, 0x7e, 0x35, 0x0e, + 0xc3, 0xba, 0x8c, 0xb6, 0x66, 0xe4, 0xa7, 0x93, 0x18, 0x8d, 0xab, 0x75, 0x46, 0xcf, 0xc3, 0x68, + 0x29, 0x49, 0x1d, 0x27, 0xdd, 0x34, 0x18, 0x76, 0x55, 0xad, 0xbc, 0xe3, 0x28, 0xab, 0x42, 0xad, + 0x94, 0xa4, 0xfe, 0x80, 0x4b, 0x23, 0x28, 0x0a, 0x6e, 0x1b, 0x36, 0xe4, 0x84, 0x72, 0x9e, 0xa5, + 0xfb, 0xb7, 0x4d, 0xb8, 0x54, 0xa8, 0xd2, 0x97, 0x84, 0xf2, 0xf0, 0x39, 0x0c, 0x46, 0x47, 0x91, + 0x76, 0xc3, 0x1d, 0xf3, 0x5c, 0x6a, 0x55, 0xb1, 0x01, 0x5c, 0x54, 0xd6, 0x1e, 0xd7, 0x34, 0xb3, + 0xed, 0x15, 0x72, 0x53, 0xde, 0xb5, 0x79, 0x20, 0xdf, 0xa1, 0xc2, 0x4d, 0xb9, 0x2e, 0x6f, 0x8f, + 0x9d, 0x40, 0x5b, 0xbb, 0x15, 0xd2, 0x00, 0x18, 0xae, 0x07, 0xf6, 0xf5, 0xce, 0x39, 0x7d, 0x59, + 0x6e, 0xaa, 0x37, 0xb3, 0x35, 0x36, 0x85, 0x2b, 0xaa, 0x8e, 0x34, 0x7c, 0xb1, 0xbf, 0xda, 0x2b, + 0xcd, 0x8d, 0x5c, 0x6c, 0xbb, 0xd3, 0x73, 0x1a, 0x66, 0x5f, 0x87, 0x8d, 0x33, 0x3f, 0x48, 0xd5, + 0xb0, 0x0c, 0x57, 0x69, 0x8e, 0xba, 0xdc, 0x3a, 0xa7, 0xcb, 0xa7, 0xe2, 0x63, 0xcb, 0xec, 0xcd, + 0x68, 0xb1, 0xf3, 0x0f, 0x0e, 0x2c, 0xdb, 0xed, 0x20, 0x9b, 0x4a, 0x75, 0xa0, 0xd4, 0xa2, 0x72, + 0x0d, 0x73, 0x70, 0xf1, 0x24, 0x5b, 0x29, 0x3b, 0xc9, 0x9a, 0xe7, 0xc7, 0xea, 0x79, 0x41, 0x9e, + 0xda, 0xab, 0x05, 0x79, 0xe6, 0xca, 0x82, 0x3c, 0x9d, 0xff, 0x76, 0x80, 0x15, 0x79, 0x89, 0xdd, + 0x17, 0x47, 0xe9, 0x90, 0x0f, 0xa5, 0x4e, 0xfa, 0xc5, 0x57, 0xe3, 0x47, 0xb5, 0x76, 0xea, 0x6b, + 0x14, 0x0c, 0x53, 0xe9, 0x98, 0x0e, 0xd4, 0x92, 0x57, 0x56, 0x95, 0x0b, 0x3b, 0xd5, 0xce, 0x0f, + 0x3b, 0xcd, 0x9d, 0x1f, 0x76, 0x9a, 0xcf, 0x87, 0x9d, 0x3a, 0xbf, 0xeb, 0xc0, 0x5a, 0xc9, 0xa6, + 0xff, 0xfc, 0x26, 0x8e, 0xdb, 0x64, 0xe9, 0x82, 0x8a, 0xdc, 0x26, 0x13, 0xec, 0xfc, 0x16, 0x2c, + 0x59, 0x8c, 0xfe, 0xf3, 0xeb, 0x3f, 0xef, 0x03, 0x0a, 0x3e, 0xb3, 0xb0, 0xce, 0x7f, 0x54, 0x80, + 0x15, 0x85, 0xed, 0xff, 0x75, 0x0c, 0xc5, 0x75, 0xaa, 0x96, 0xac, 0xd3, 0xff, 0xa9, 0x1d, 0x78, + 0x07, 0x56, 0x65, 0x46, 0x81, 0x11, 0x40, 0x11, 0x1c, 0x53, 0xac, 0x40, 0x2f, 0xd8, 0x8e, 0xf9, + 0x2d, 0x5a, 0x37, 0xd1, 0x86, 0x31, 0xcc, 0x85, 0xfe, 0xdc, 0x0d, 0x58, 0x17, 0x19, 0x0a, 0x77, + 0x45, 0x53, 0xca, 0xae, 0xfc, 0xa9, 0x03, 0x17, 0x73, 0x15, 0xd9, 0xbd, 0xa9, 0x30, 0x1d, 0xb6, + 0x3d, 0xb1, 0x41, 0x1c, 0xbf, 0x94, 0x23, 0x63, 0xfc, 0x82, 0xdb, 0x8a, 0x15, 0xb8, 0x3e, 0x93, + 0xb0, 0x48, 0x2f, 0x56, 0xbd, 0xac, 0xca, 0xbd, 0x24, 0xf2, 0x28, 0x42, 0x3e, 0xcc, 0x0d, 0xfc, + 0x58, 0x64, 0x3e, 0x98, 0x15, 0xd9, 0xc5, 0x8b, 0x3d, 0x64, 0x55, 0x44, 0x1f, 0xd1, 0x32, 0x53, + 0xf6, 0x78, 0x4b, 0xeb, 0xdc, 0xbf, 0x76, 0x80, 0x7d, 0x69, 0xc2, 0xe3, 0x29, 0xdd, 0x9f, 0xea, + 0x48, 0xcf, 0xa5, 0x7c, 0x94, 0x63, 0x7e, 0x3c, 0x39, 0xfa, 0x22, 0x9f, 0xaa, 0x5b, 0xf6, 0x4a, + 0x76, 0xcb, 0xfe, 0x06, 0x00, 0x1e, 0xce, 0xf4, 0xa5, 0x2c, 0xf9, 0x66, 0xe1, 0x64, 0x24, 0x1a, + 0x2c, 0xbd, 0x08, 0xaf, 0x9d, 0x7f, 0x11, 0x3e, 0x77, 0xde, 0x45, 0xf8, 0x87, 0xb0, 0x66, 0x8d, + 0x5b, 0x6f, 0xab, 0xba, 0x1e, 0x76, 0x5e, 0x72, 0x3d, 0xfc, 0x9f, 0x0e, 0x54, 0xf7, 0xa2, 0xb1, + 0x19, 0xd5, 0x74, 0xec, 0xa8, 0xa6, 0xb4, 0x25, 0x5d, 0x6d, 0x2a, 0xa4, 0x8a, 0xb1, 0x40, 0xb6, + 0x09, 0xcb, 0xfe, 0x28, 0xc5, 0x43, 0xf9, 0x71, 0x14, 0x9f, 0xf9, 0x71, 0x5f, 0xec, 0xf5, 0xdd, + 0x4a, 0xdb, 0xf1, 0x72, 0x35, 0x6c, 0x1d, 0xaa, 0x5a, 0xe9, 0x12, 0x01, 0x16, 0xd1, 0x71, 0xa3, + 0x1b, 0x91, 0xa9, 0x8c, 0x27, 0xc8, 0x12, 0xb2, 0x92, 0xfd, 0xbd, 0x70, 0xa4, 0x85, 0xe8, 0x94, + 0x55, 0xa1, 0x5d, 0xc3, 0xe5, 0x23, 0x32, 0x19, 0x08, 0x52, 0x65, 0xf7, 0xdf, 0x1c, 0x98, 0xa3, + 0x15, 0x40, 0x61, 0x17, 0x1c, 0xae, 0xc3, 0x97, 0x34, 0xf3, 0x25, 0x2f, 0x0f, 0x33, 0xd7, 0xca, + 0x46, 0xa9, 0xe8, 0x61, 0x9b, 0x19, 0x29, 0x57, 0xa1, 0x2e, 0x4a, 0x3a, 0xf3, 0x82, 0x48, 0x32, + 0x90, 0x5d, 0x81, 0xda, 0x49, 0x34, 0x56, 0xde, 0x09, 0xa8, 0xe8, 0x7d, 0x34, 0xf6, 0x08, 0xcf, + 0xc6, 0x83, 0xed, 0x89, 0xc1, 0x0b, 0x9b, 0x93, 0x87, 0xd1, 0xea, 0xea, 0x66, 0xcd, 0xc5, 0xc8, + 0xa1, 0xee, 0x26, 0xac, 0x3c, 0x8c, 0xfa, 0xdc, 0x88, 0x38, 0xcd, 0xe4, 0x66, 0xf7, 0x5b, 0x0e, + 0x2c, 0x2a, 0x62, 0x76, 0x03, 0x6a, 0xe8, 0x4a, 0xe4, 0x0e, 0x0a, 0xfa, 0xd6, 0x0e, 0xe9, 0x3c, + 0xa2, 0x40, 0xdd, 0x4b, 0xf1, 0x88, 0xcc, 0xad, 0x54, 0xd1, 0x88, 0xcc, 0x6b, 0xd2, 0xc3, 0xcd, + 0x39, 0x1b, 0x39, 0xd4, 0xfd, 0x4b, 0x07, 0x96, 0xac, 0x3e, 0xf0, 0xf0, 0x38, 0xf4, 0x93, 0x54, + 0xde, 0x84, 0xc8, 0xed, 0x31, 0x21, 0x33, 0x06, 0x59, 0xb1, 0x63, 0x90, 0x3a, 0x3a, 0x56, 0x35, + 0xa3, 0x63, 0xb7, 0xa1, 0x9e, 0xe5, 0x0c, 0xd5, 0x2c, 0x9d, 0x8a, 0x3d, 0xaa, 0xfb, 0xc8, 0x8c, + 0x08, 0xdb, 0xe9, 0x45, 0xc3, 0x28, 0x96, 0x21, 0x78, 0x51, 0x70, 0x3f, 0x84, 0x86, 0x41, 0x8f, + 0xc3, 0x08, 0x79, 0x7a, 0x16, 0xc5, 0xcf, 0x54, 0x28, 0x54, 0x16, 0xf5, 0xb5, 0x7b, 0x25, 0xbb, + 0x76, 0x77, 0x7f, 0xe8, 0xc0, 0x12, 0xf2, 0x60, 0x10, 0x0e, 0x0e, 0xa2, 0x61, 0xd0, 0x9b, 0xd2, + 0xde, 0x2b, 0x76, 0x93, 0x9a, 0x41, 0xf1, 0xa2, 0x0d, 0x23, 0x6f, 0xab, 0xb3, 0xa3, 0x14, 0x44, + 0x5d, 0x46, 0x49, 0x45, 0x3e, 0x3f, 0xf2, 0x13, 0xc9, 0xfc, 0xd2, 0xc8, 0x59, 0x20, 0xca, 0x13, + 0x02, 0xb1, 0x9f, 0xf2, 0xee, 0x28, 0x18, 0x0e, 0x03, 0x41, 0x2b, 0x5c, 0xa0, 0xb2, 0x2a, 0xec, + 0xb3, 0x1f, 0x24, 0xfe, 0x51, 0x16, 0x66, 0xd6, 0x65, 0xf7, 0x07, 0x15, 0x68, 0x48, 0xf5, 0xbc, + 0xdb, 0x1f, 0x70, 0x79, 0x07, 0x42, 0x4e, 0xa6, 0x56, 0x25, 0x06, 0xa2, 0xea, 0x2d, 0xb7, 0xd4, + 0x40, 0xf2, 0x5b, 0x5e, 0x2d, 0x6e, 0xf9, 0xeb, 0x50, 0x47, 0xd6, 0x7b, 0x97, 0xfc, 0x5f, 0x71, + 0x7f, 0x92, 0x01, 0xaa, 0x76, 0x8b, 0x6a, 0xe7, 0xb2, 0x5a, 0x02, 0x5e, 0x7a, 0x63, 0xf2, 0x3e, + 0x34, 0x65, 0x33, 0xb4, 0x27, 0xa4, 0x39, 0x32, 0xe6, 0xb7, 0xf6, 0xcb, 0xb3, 0x28, 0xd5, 0x97, + 0x5b, 0xea, 0xcb, 0xc5, 0xf3, 0xbe, 0x54, 0x94, 0x74, 0xbb, 0x2d, 0xd6, 0xe6, 0x7e, 0xec, 0x8f, + 0x4f, 0x94, 0xc9, 0xeb, 0xeb, 0x94, 0x1d, 0x82, 0xd9, 0x26, 0xcc, 0xe1, 0x67, 0x4a, 0x93, 0x97, + 0x0b, 0xa4, 0x20, 0x61, 0x37, 0x60, 0x8e, 0xf7, 0x07, 0x5c, 0x9d, 0xf0, 0x98, 0x7d, 0xd6, 0xc6, + 0x3d, 0xf2, 0x04, 0x01, 0xaa, 0x07, 0x44, 0x73, 0xea, 0xc1, 0xb6, 0x02, 0xf3, 0x58, 0x7c, 0xd0, + 0x77, 0xd7, 0x81, 0x3d, 0x14, 0x1c, 0x6d, 0xc6, 0xaf, 0xbf, 0x5d, 0x85, 0x86, 0x01, 0xa3, 0xa4, + 0x0f, 0x70, 0xc0, 0xdd, 0x7e, 0xe0, 0x8f, 0x78, 0xca, 0x63, 0xc9, 0xc5, 0x39, 0x14, 0xe9, 0xfc, + 0xd3, 0x41, 0x37, 0x9a, 0xa4, 0xdd, 0x3e, 0x1f, 0xc4, 0x5c, 0x18, 0x66, 0x34, 0x14, 0x16, 0x8a, + 0x74, 0x23, 0xff, 0xb9, 0x49, 0x27, 0xf8, 0x21, 0x87, 0xaa, 0x68, 0xb4, 0x58, 0xa3, 0x5a, 0x16, + 0x8d, 0x16, 0x2b, 0x92, 0xd7, 0x51, 0x73, 0x25, 0x3a, 0xea, 0x3d, 0xd8, 0x10, 0xda, 0x48, 0xca, + 0x6d, 0x37, 0xc7, 0x26, 0x33, 0x6a, 0xd9, 0x26, 0xb4, 0x70, 0xcc, 0x8a, 0xc1, 0x93, 0xe0, 0x9b, + 0x22, 0x3e, 0xe4, 0x78, 0x05, 0x1c, 0x69, 0x29, 0x50, 0x63, 0xd2, 0x8a, 0xfb, 0xb6, 0x02, 0x4e, + 0xb4, 0xfe, 0x73, 0x9b, 0xb6, 0x2e, 0x69, 0x73, 0xb8, 0xbb, 0x04, 0x8d, 0xc3, 0x34, 0x1a, 0xab, + 0x4d, 0x59, 0x86, 0xa6, 0x28, 0xca, 0xec, 0x86, 0xd7, 0xe0, 0x32, 0x71, 0xd1, 0xe3, 0x68, 0x1c, + 0x0d, 0xa3, 0xc1, 0xf4, 0x70, 0x72, 0x94, 0xf4, 0xe2, 0x60, 0x8c, 0xa7, 0x21, 0xf7, 0x1f, 0x1d, + 0x58, 0xb3, 0x6a, 0x65, 0xc8, 0xe8, 0xd3, 0x82, 0xa5, 0xf5, 0xb5, 0xb4, 0x60, 0xbc, 0x55, 0x43, + 0x55, 0x0a, 0x42, 0x11, 0xca, 0x7b, 0x22, 0x6f, 0xaa, 0xef, 0xc0, 0x8a, 0x1a, 0x99, 0xfa, 0x50, + 0x70, 0x61, 0xbb, 0xc8, 0x85, 0xf2, 0xfb, 0x65, 0xf9, 0x81, 0x6a, 0xe2, 0x57, 0xe4, 0xbd, 0x65, + 0x9f, 0xe6, 0xa8, 0x62, 0x07, 0xfa, 0x66, 0xca, 0x3c, 0x41, 0xa8, 0x11, 0xf4, 0x34, 0x98, 0xb8, + 0xbf, 0xef, 0x00, 0x64, 0xa3, 0x43, 0xc6, 0xc8, 0xd4, 0xbd, 0x48, 0xa5, 0x36, 0x54, 0xfb, 0x5b, + 0xd0, 0xd4, 0x77, 0x2a, 0x99, 0x05, 0x69, 0x28, 0x0c, 0x9d, 0xbc, 0xeb, 0xb0, 0x32, 0x18, 0x46, + 0x47, 0x64, 0x7e, 0x29, 0x5d, 0x26, 0x91, 0x39, 0x1e, 0xcb, 0x02, 0xbe, 0x27, 0xd1, 0xcc, 0xdc, + 0xd4, 0x0c, 0x73, 0xe3, 0x7e, 0xa7, 0xa2, 0x23, 0xf1, 0xd9, 0x9c, 0x67, 0x4a, 0x19, 0xdb, 0x2a, + 0x28, 0xc7, 0x19, 0x81, 0x6f, 0x8a, 0x92, 0x1d, 0x9c, 0x7b, 0x88, 0xff, 0x10, 0x96, 0x63, 0xa1, + 0x7d, 0x94, 0x6a, 0xaa, 0xbd, 0x44, 0x35, 0x2d, 0xc5, 0x96, 0x4d, 0xfa, 0x24, 0xb4, 0xfc, 0xfe, + 0x29, 0x8f, 0xd3, 0x80, 0x8e, 0x51, 0xe4, 0x10, 0x08, 0x85, 0xba, 0x62, 0xe0, 0x64, 0xa7, 0xaf, + 0xc3, 0x8a, 0xcc, 0xab, 0xd1, 0x94, 0x32, 0x17, 0x34, 0x83, 0x91, 0xd0, 0xfd, 0x73, 0x15, 0xf4, + 0xb7, 0xf7, 0x70, 0xf6, 0x8a, 0x98, 0xb3, 0xab, 0xe4, 0x66, 0xf7, 0x09, 0x19, 0x80, 0xef, 0xab, + 0xb3, 0x5a, 0xd5, 0xb8, 0xe3, 0xee, 0xcb, 0x0b, 0x13, 0x7b, 0x49, 0x6b, 0xaf, 0xb2, 0xa4, 0xee, + 0x8f, 0x1c, 0x58, 0xd8, 0x8b, 0xc6, 0x7b, 0xf2, 0xb6, 0x9f, 0x04, 0x41, 0x67, 0xad, 0xa9, 0xe2, + 0x4b, 0xf2, 0x00, 0x4a, 0xed, 0xf0, 0x52, 0xde, 0x0e, 0xff, 0x2a, 0xbc, 0x46, 0x91, 0x82, 0x38, + 0x1a, 0x47, 0x31, 0x0a, 0xa3, 0x3f, 0x14, 0x46, 0x37, 0x0a, 0xd3, 0x13, 0xa5, 0xc6, 0x5e, 0x46, + 0x42, 0x47, 0x32, 0x3c, 0x4a, 0x08, 0x47, 0x59, 0xfa, 0x0d, 0x42, 0xbb, 0x15, 0x2b, 0xdc, 0xcf, + 0x42, 0x9d, 0x1c, 0x5f, 0x9a, 0xd6, 0x3b, 0x50, 0x3f, 0x89, 0xc6, 0xdd, 0x93, 0x20, 0x4c, 0x95, + 0x70, 0x2f, 0x67, 0x1e, 0xe9, 0x1e, 0x2d, 0x88, 0x26, 0x70, 0x7f, 0x58, 0x83, 0x85, 0x07, 0xe1, + 0x69, 0x14, 0xf4, 0xe8, 0x7e, 0x60, 0xc4, 0x47, 0x91, 0xca, 0xe1, 0xc3, 0xdf, 0xb8, 0x14, 0x94, + 0xcf, 0x32, 0x4e, 0x65, 0x80, 0x5f, 0x15, 0xd1, 0xdc, 0xc7, 0x59, 0x9e, 0xad, 0x10, 0x1d, 0x03, + 0x41, 0xa7, 0x3f, 0x36, 0x53, 0x92, 0x65, 0x29, 0x4b, 0x82, 0x9c, 0x33, 0x92, 0x20, 0xe9, 0x36, + 0x49, 0x64, 0x26, 0x10, 0x7f, 0x2d, 0x7a, 0xaa, 0x48, 0x87, 0x94, 0x98, 0x8b, 0x08, 0x0f, 0x39, + 0x0e, 0x0b, 0xf2, 0x90, 0x62, 0x82, 0xe8, 0x5c, 0x88, 0x0f, 0x04, 0x8d, 0x50, 0xbe, 0x26, 0x84, + 0x8e, 0x58, 0x3e, 0xab, 0xb9, 0x2e, 0x78, 0x3e, 0x07, 0xa3, 0x86, 0xee, 0x73, 0xad, 0x48, 0xc5, + 0x1c, 0x40, 0xe4, 0x11, 0xe7, 0x71, 0xe3, 0x68, 0x23, 0x52, 0x8e, 0xd4, 0xd1, 0x06, 0x19, 0xc5, + 0x1f, 0x0e, 0x8f, 0xfc, 0xde, 0x33, 0x4a, 0x5a, 0xa7, 0x0c, 0xa3, 0xba, 0x67, 0x83, 0x94, 0x6b, + 0x90, 0xed, 0x26, 0xdd, 0x47, 0xd6, 0x3c, 0x13, 0x62, 0x5b, 0xd0, 0xa0, 0xe3, 0x9c, 0xdc, 0xcf, + 0x65, 0xda, 0xcf, 0x96, 0x79, 0xde, 0xa3, 0x1d, 0x35, 0x89, 0xcc, 0x3b, 0x8b, 0x15, 0xfb, 0xce, + 0x42, 0x28, 0x4d, 0x79, 0xd5, 0xd3, 0xa2, 0xde, 0x32, 0x00, 0xad, 0xa9, 0x5c, 0x30, 0x41, 0xb0, + 0x4a, 0x04, 0x16, 0x86, 0x52, 0x8b, 0x87, 0x90, 0xb1, 0x1f, 0xf4, 0xdb, 0x4c, 0x48, 0xad, 0x2a, + 0xbb, 0x29, 0xb0, 0x3b, 0xfd, 0xbe, 0xe4, 0x26, 0x7d, 0x58, 0xcd, 0xf8, 0xc0, 0xb1, 0xf8, 0xa0, + 0x64, 0x3f, 0x2a, 0xe5, 0xfb, 0xf1, 0xd2, 0x51, 0xbb, 0xbb, 0xd0, 0x38, 0x30, 0x52, 0xad, 0x89, + 0x2d, 0x55, 0x92, 0xb5, 0x64, 0x65, 0x03, 0x31, 0x86, 0x53, 0x31, 0x87, 0xe3, 0xfe, 0x12, 0xb0, + 0xfd, 0x20, 0x49, 0xf5, 0xe8, 0xb3, 0xdc, 0x6e, 0x15, 0x51, 0xc8, 0xd2, 0xa3, 0x1a, 0x12, 0xa3, + 0xb4, 0xa5, 0x3b, 0x22, 0xaf, 0x2a, 0x3f, 0xed, 0x4d, 0x58, 0x0c, 0x04, 0x94, 0x97, 0x42, 0x45, + 0xa9, 0xeb, 0xdd, 0xa7, 0xb0, 0x26, 0x41, 0xd3, 0x82, 0xdb, 0xf3, 0x76, 0xce, 0xdb, 0xad, 0x4a, + 0x71, 0xb7, 0xdc, 0x1f, 0x38, 0xb0, 0x20, 0x17, 0x07, 0xe9, 0x0b, 0x69, 0xea, 0x75, 0xcf, 0xc2, + 0xca, 0x13, 0x94, 0x8b, 0x12, 0x58, 0x2d, 0x93, 0x40, 0x06, 0xb5, 0xb1, 0x9f, 0x9e, 0xd0, 0xd1, + 0xab, 0xee, 0xd1, 0x6f, 0xd6, 0x12, 0xe1, 0x00, 0x21, 0xe9, 0x14, 0x0a, 0x28, 0xcb, 0xd1, 0x17, + 0x06, 0xa5, 0x80, 0xa3, 0x0b, 0x4d, 0xd9, 0x18, 0x02, 0xd7, 0xd7, 0x28, 0x32, 0x4f, 0x2c, 0x83, + 0xb3, 0x15, 0x97, 0x4d, 0xe4, 0x57, 0x5c, 0x92, 0x7a, 0xba, 0xde, 0xed, 0x40, 0x7b, 0x87, 0x0f, + 0x79, 0xca, 0xef, 0x0c, 0x87, 0xf9, 0xf6, 0x5f, 0x83, 0xcb, 0x25, 0x75, 0xd2, 0xe5, 0xba, 0x07, + 0xab, 0x3b, 0xfc, 0x68, 0x32, 0xd8, 0xe7, 0xa7, 0xd9, 0x4d, 0x28, 0x83, 0x5a, 0x72, 0x12, 0x9d, + 0x49, 0xee, 0xa0, 0xdf, 0xec, 0x0d, 0x80, 0x21, 0xd2, 0x74, 0x93, 0x31, 0xef, 0xa9, 0xd4, 0x5c, + 0x42, 0x0e, 0xc7, 0xbc, 0xe7, 0xbe, 0x07, 0xcc, 0x6c, 0x47, 0x4e, 0x01, 0xb5, 0xd8, 0xe4, 0xa8, + 0x9b, 0x4c, 0x93, 0x94, 0x8f, 0x54, 0xce, 0xb1, 0x09, 0xb9, 0xd7, 0xa1, 0x79, 0xe0, 0x4f, 0x3d, + 0xfe, 0x0d, 0xf9, 0x52, 0x00, 0x4f, 0xfd, 0xfe, 0x14, 0x45, 0x45, 0x9f, 0xfa, 0xa9, 0xda, 0xfd, + 0xaf, 0x0a, 0xcc, 0x0b, 0x4a, 0x6c, 0xb5, 0xcf, 0x93, 0x34, 0x08, 0xc5, 0x3d, 0x9f, 0x6c, 0xd5, + 0x80, 0x0a, 0xbc, 0x51, 0x29, 0xe1, 0x0d, 0xe9, 0x6b, 0xab, 0x34, 0x47, 0xc9, 0x04, 0x16, 0x86, + 0x1c, 0x9b, 0x65, 0x57, 0x88, 0x63, 0x67, 0x06, 0xe4, 0xc2, 0x40, 0x99, 0xae, 0x14, 0xe3, 0x53, + 0x6c, 0x2f, 0xd9, 0xc1, 0x84, 0x4a, 0x35, 0xf2, 0x82, 0xe0, 0x9a, 0x82, 0x46, 0x2e, 0x68, 0xde, + 0xc5, 0x57, 0xd0, 0xbc, 0xc2, 0x01, 0x7f, 0x99, 0xe6, 0x85, 0x57, 0xd0, 0xbc, 0x2e, 0x83, 0xd6, + 0x3d, 0xce, 0x3d, 0x8e, 0x36, 0x5d, 0xb1, 0xd3, 0x77, 0x1d, 0x68, 0x49, 0x77, 0x44, 0xd7, 0xb1, + 0xb7, 0x2c, 0xdf, 0xa5, 0x34, 0x19, 0xf1, 0x1a, 0x2c, 0x91, 0x47, 0xa1, 0xe3, 0x5d, 0x32, 0x38, + 0x67, 0x81, 0x38, 0x0f, 0x75, 0x29, 0x31, 0x0a, 0x86, 0x72, 0x53, 0x4c, 0x48, 0x85, 0xcc, 0xf0, + 0xe4, 0x4f, 0x5b, 0xe2, 0x78, 0xba, 0xec, 0xfe, 0x8d, 0x03, 0xab, 0xc6, 0x80, 0x25, 0x17, 0x7e, + 0x08, 0x2a, 0xfb, 0x42, 0x84, 0xc5, 0x84, 0x30, 0x5d, 0xb2, 0x5d, 0xab, 0xec, 0x33, 0x8b, 0x98, + 0x36, 0xd3, 0x9f, 0xd2, 0x00, 0x93, 0xc9, 0x48, 0x6a, 0x25, 0x13, 0x42, 0x46, 0x3a, 0xe3, 0xfc, + 0x99, 0x26, 0xa9, 0x0a, 0xc5, 0x65, 0x62, 0x74, 0xb9, 0x8e, 0x9e, 0x90, 0x26, 0x12, 0xf9, 0x64, + 0x36, 0xe8, 0xfe, 0xb3, 0x03, 0x6b, 0xc2, 0xa5, 0x95, 0x07, 0x06, 0x9d, 0x29, 0x3e, 0x2f, 0x7c, + 0x78, 0x21, 0x91, 0x7b, 0x17, 0x3c, 0x59, 0x66, 0x9f, 0x79, 0x45, 0x37, 0x5c, 0x27, 0x55, 0xcc, + 0xd8, 0x8b, 0x6a, 0xd9, 0x5e, 0xbc, 0x64, 0xa5, 0xcb, 0xc2, 0x40, 0x73, 0xa5, 0x61, 0xa0, 0xbb, + 0x0b, 0x30, 0x97, 0xf4, 0xa2, 0x31, 0x77, 0x37, 0x60, 0xdd, 0x9e, 0x9c, 0x54, 0x41, 0xdf, 0x73, + 0xa0, 0x7d, 0x4f, 0x04, 0x45, 0x83, 0x70, 0xb0, 0x17, 0x24, 0x69, 0x14, 0xeb, 0xa7, 0x31, 0x57, + 0x00, 0x92, 0xd4, 0x8f, 0x53, 0x91, 0xf4, 0x26, 0x83, 0x34, 0x19, 0x82, 0x63, 0xe4, 0x61, 0x5f, + 0xd4, 0x8a, 0xbd, 0xd1, 0x65, 0xdc, 0x18, 0x32, 0x1b, 0xdd, 0xe8, 0xf8, 0x38, 0xe1, 0xda, 0xe9, + 0x36, 0x31, 0x3c, 0xb7, 0xa3, 0xc4, 0xe3, 0x49, 0x95, 0x9f, 0x92, 0xaa, 0x15, 0xde, 0x6c, 0x0e, + 0x75, 0xff, 0xca, 0x81, 0x95, 0x6c, 0x90, 0xbb, 0x08, 0xda, 0xda, 0x41, 0xda, 0xb3, 0x4c, 0x3b, + 0xa8, 0xf0, 0x51, 0x80, 0x06, 0x4e, 0x8e, 0xcd, 0x40, 0x48, 0x62, 0x65, 0x29, 0x9a, 0xa8, 0x04, + 0x43, 0x13, 0x12, 0xf9, 0x01, 0x29, 0x7e, 0x2d, 0xb2, 0x0b, 0x65, 0x89, 0x72, 0x16, 0x47, 0x29, + 0x7d, 0x35, 0x2f, 0xdc, 0x79, 0x59, 0x54, 0xf6, 0x69, 0x81, 0x50, 0xfc, 0xe9, 0xfe, 0x81, 0x03, + 0x97, 0x4b, 0x16, 0x57, 0x4a, 0xc6, 0x0e, 0xac, 0x1e, 0xeb, 0x4a, 0xb5, 0x00, 0x42, 0x3c, 0x36, + 0x54, 0x14, 0xdf, 0x9e, 0xb4, 0x57, 0xfc, 0x00, 0x9d, 0x7b, 0x8a, 0x7a, 0x89, 0x25, 0xb5, 0x12, + 0x6f, 0x8a, 0x15, 0x5b, 0x7f, 0x58, 0x85, 0x65, 0x71, 0xbb, 0x23, 0x1e, 0xa9, 0xf2, 0x98, 0x7d, + 0x04, 0x0b, 0xf2, 0x91, 0x31, 0xbb, 0x28, 0xbb, 0xb5, 0x9f, 0x35, 0x77, 0x36, 0xf2, 0xb0, 0xe4, + 0x9d, 0xb5, 0xdf, 0xf9, 0xd1, 0xbf, 0xfe, 0x51, 0x65, 0x89, 0x35, 0x6e, 0x9d, 0xbe, 0x7b, 0x6b, + 0xc0, 0xc3, 0x04, 0xdb, 0xf8, 0x0d, 0x80, 0xec, 0xf9, 0x2d, 0x6b, 0x6b, 0x37, 0x25, 0xf7, 0xae, + 0xb8, 0x73, 0xb9, 0xa4, 0x46, 0xb6, 0x7b, 0x99, 0xda, 0x5d, 0x73, 0x97, 0xb1, 0xdd, 0x20, 0x0c, + 0x52, 0xf1, 0x16, 0xf7, 0x03, 0x67, 0x93, 0xf5, 0xa1, 0x69, 0xbe, 0xae, 0x65, 0xea, 0xc0, 0x5f, + 0xf2, 0xb6, 0xb7, 0xf3, 0x5a, 0x69, 0x9d, 0x8a, 0x76, 0x50, 0x1f, 0x17, 0xdd, 0x16, 0xf6, 0x31, + 0x21, 0x8a, 0xac, 0x97, 0x21, 0x2c, 0xdb, 0x8f, 0x68, 0xd9, 0xeb, 0x86, 0x58, 0x17, 0x9e, 0xf0, + 0x76, 0xde, 0x98, 0x51, 0x2b, 0xfb, 0x7a, 0x83, 0xfa, 0xba, 0xe4, 0x32, 0xec, 0xab, 0x47, 0x34, + 0xea, 0x09, 0xef, 0x07, 0xce, 0xe6, 0xd6, 0xb7, 0xae, 0x40, 0x5d, 0x87, 0xe8, 0xd8, 0xd7, 0x61, + 0xc9, 0xba, 0x7e, 0x63, 0x6a, 0x1a, 0x65, 0xb7, 0x75, 0x9d, 0xd7, 0xcb, 0x2b, 0x65, 0xc7, 0x57, + 0xa8, 0xe3, 0x36, 0xdb, 0xc0, 0x8e, 0xe5, 0xfd, 0xd5, 0x2d, 0xba, 0x74, 0x14, 0x19, 0x91, 0xcf, + 0xc4, 0x3c, 0xb3, 0x2b, 0x33, 0x6b, 0x9e, 0x85, 0x2b, 0x36, 0x6b, 0x9e, 0xc5, 0x7b, 0x36, 0xf7, + 0x75, 0xea, 0x6e, 0x83, 0xad, 0x9b, 0xdd, 0xe9, 0xd0, 0x19, 0xa7, 0x1c, 0x56, 0xf3, 0x8d, 0x2d, + 0x7b, 0x43, 0x33, 0x56, 0xd9, 0xdb, 0x5b, 0xcd, 0x22, 0xc5, 0x07, 0xb8, 0x6e, 0x9b, 0xba, 0x62, + 0x8c, 0xb6, 0xcf, 0x7c, 0x62, 0xcb, 0xbe, 0x0a, 0x75, 0xfd, 0xa0, 0x8c, 0x5d, 0x32, 0x5e, 0xf1, + 0x99, 0xaf, 0xdc, 0x3a, 0xed, 0x62, 0x45, 0x19, 0x63, 0x98, 0x2d, 0x23, 0x63, 0xec, 0xc3, 0x45, + 0xe9, 0x54, 0x1f, 0xf1, 0x9f, 0x64, 0x26, 0x25, 0x2f, 0x83, 0x6f, 0x3b, 0xec, 0x43, 0x58, 0x54, + 0xef, 0xf4, 0xd8, 0x46, 0xf9, 0x7b, 0xc3, 0xce, 0xa5, 0x02, 0x2e, 0xb5, 0xc7, 0x1d, 0x80, 0xec, + 0x8d, 0x99, 0x96, 0xb3, 0xc2, 0xcb, 0x37, 0xbd, 0x88, 0x25, 0x0f, 0xd2, 0x06, 0xf4, 0xa2, 0xce, + 0x7e, 0xc2, 0xc6, 0xde, 0xcc, 0xe8, 0x4b, 0x1f, 0xb7, 0xbd, 0xa4, 0x41, 0x77, 0x83, 0xd6, 0xae, + 0xc5, 0x48, 0x70, 0x43, 0x7e, 0xa6, 0xb2, 0xb9, 0x77, 0xa0, 0x61, 0xbc, 0x5b, 0x63, 0xaa, 0x85, + 0xe2, 0x9b, 0xb7, 0x4e, 0xa7, 0xac, 0x4a, 0x0e, 0xf7, 0x0b, 0xb0, 0x64, 0x3d, 0x40, 0xd3, 0x92, + 0x51, 0xf6, 0xbc, 0x4d, 0x4b, 0x46, 0xf9, 0x9b, 0xb5, 0xaf, 0x40, 0xc3, 0x78, 0x2e, 0xc6, 0x8c, + 0x3c, 0xb5, 0xdc, 0x43, 0x31, 0x3d, 0xa2, 0xb2, 0xd7, 0x65, 0xeb, 0x34, 0xdf, 0x65, 0xb7, 0x8e, + 0xf3, 0xa5, 0x94, 0x66, 0x64, 0x92, 0xaf, 0xc3, 0xb2, 0xfd, 0x80, 0x4c, 0x4b, 0x55, 0xe9, 0x53, + 0x34, 0x2d, 0x55, 0x33, 0x5e, 0x9d, 0x49, 0x86, 0xdc, 0x5c, 0xd3, 0x9d, 0xdc, 0xfa, 0x58, 0x5e, + 0x5e, 0xbd, 0x60, 0x5f, 0x42, 0xd5, 0x21, 0x73, 0xcc, 0x59, 0xf6, 0x6c, 0xce, 0xce, 0x44, 0xd7, + 0xdc, 0x5e, 0x48, 0x47, 0x77, 0x57, 0xa9, 0xf1, 0x06, 0xcb, 0x66, 0x20, 0xec, 0x01, 0xe5, 0x9a, + 0x1b, 0xf6, 0xc0, 0x4c, 0x47, 0x37, 0xec, 0x81, 0x95, 0x92, 0x9e, 0xb7, 0x07, 0x69, 0x80, 0x6d, + 0x84, 0xb0, 0x92, 0x4b, 0xd4, 0xd0, 0xc2, 0x52, 0x9e, 0xd9, 0xd6, 0xb9, 0xf2, 0xf2, 0xfc, 0x0e, + 0x5b, 0xcd, 0x28, 0xf5, 0x72, 0x4b, 0x25, 0x22, 0xfe, 0x26, 0x34, 0xcd, 0x87, 0x3f, 0xda, 0x42, + 0x94, 0x3c, 0x57, 0xd2, 0x16, 0xa2, 0xec, 0xa5, 0x90, 0xda, 0x5c, 0xd6, 0x34, 0xbb, 0xc1, 0xcd, + 0xb5, 0xdf, 0x49, 0x64, 0x2a, 0xb3, 0xec, 0x01, 0x48, 0xa6, 0x32, 0x4b, 0x1f, 0x57, 0xa8, 0xcd, + 0x65, 0x6b, 0xd6, 0x5c, 0x44, 0x64, 0x92, 0x7d, 0x05, 0x56, 0x8c, 0x2c, 0xa8, 0xc3, 0x69, 0xd8, + 0xd3, 0x8c, 0x5a, 0xcc, 0xa0, 0xed, 0x94, 0x79, 0x9e, 0xee, 0x25, 0x6a, 0x7f, 0xd5, 0xb5, 0x26, + 0x81, 0x4c, 0xba, 0x0d, 0x0d, 0x33, 0xc3, 0xea, 0x25, 0xed, 0x5e, 0x32, 0xaa, 0xcc, 0x74, 0xd1, + 0xdb, 0x0e, 0xfb, 0x13, 0x07, 0x9a, 0x56, 0xbe, 0x92, 0x15, 0x7f, 0xcf, 0xb5, 0xd3, 0x36, 0xeb, + 0xcc, 0x86, 0x5c, 0x8f, 0x06, 0xb9, 0xbf, 0xf9, 0x05, 0x6b, 0x11, 0x3e, 0xb6, 0x4e, 0x30, 0x37, + 0xf3, 0xef, 0xc7, 0x5f, 0xe4, 0x09, 0xcc, 0x2c, 0xe3, 0x17, 0xb7, 0x1d, 0xf6, 0x81, 0xf8, 0x87, + 0x04, 0x15, 0xb1, 0x60, 0x86, 0x22, 0xcd, 0x2f, 0x99, 0xf9, 0xf7, 0x00, 0x37, 0x9c, 0xdb, 0x0e, + 0xfb, 0x9a, 0x78, 0x26, 0x2e, 0xbf, 0xa5, 0x95, 0x7f, 0xd5, 0xef, 0xdd, 0x6b, 0x34, 0x9b, 0x2b, + 0xee, 0x65, 0x6b, 0x36, 0x79, 0x4b, 0x72, 0x47, 0x8c, 0x4e, 0xbe, 0xfe, 0xcf, 0x54, 0x62, 0xe1, + 0x1f, 0x01, 0x66, 0x0f, 0x72, 0x24, 0x06, 0x29, 0xc9, 0x2d, 0xf6, 0x78, 0xc5, 0x66, 0xdc, 0x4d, + 0x1a, 0xeb, 0x35, 0xf7, 0xcd, 0x99, 0x63, 0xbd, 0x45, 0x27, 0x52, 0x1c, 0xf1, 0x01, 0x40, 0x16, + 0x90, 0x63, 0xb9, 0xf8, 0x93, 0xb6, 0x0a, 0xc5, 0x98, 0x9d, 0xcd, 0x83, 0x2a, 0x4c, 0x85, 0x2d, + 0x7e, 0x55, 0x88, 0xaa, 0xa4, 0x4f, 0xf4, 0xe8, 0x8b, 0xa1, 0xb3, 0x4e, 0xa7, 0xac, 0xaa, 0x4c, + 0x50, 0x55, 0xfb, 0xec, 0x09, 0x2c, 0xed, 0x47, 0xd1, 0xb3, 0xc9, 0x58, 0x07, 0xa4, 0xed, 0xf8, + 0xcd, 0x9e, 0x9f, 0x9c, 0x74, 0x72, 0xb3, 0x70, 0xaf, 0x52, 0x53, 0x1d, 0xd6, 0x36, 0x9a, 0xba, + 0xf5, 0x71, 0x16, 0xf0, 0x7b, 0xc1, 0x7c, 0x58, 0xd5, 0x1e, 0x80, 0x1e, 0x78, 0xc7, 0x6e, 0xc6, + 0x8c, 0xbb, 0x15, 0xba, 0xb0, 0x7c, 0x32, 0x35, 0xda, 0x5b, 0x89, 0x6a, 0xf3, 0xb6, 0xc3, 0x0e, + 0xa0, 0xb9, 0xc3, 0x7b, 0x51, 0x9f, 0xcb, 0x88, 0xcb, 0x5a, 0x36, 0x70, 0x1d, 0xaa, 0xe9, 0x2c, + 0x59, 0xa0, 0xad, 0x13, 0xc7, 0xfe, 0x34, 0xe6, 0xdf, 0xb8, 0xf5, 0xb1, 0x8c, 0xe5, 0xbc, 0x50, + 0x3a, 0x51, 0xc5, 0x9f, 0x2c, 0x9d, 0x98, 0x0b, 0x58, 0x59, 0x3a, 0xb1, 0x10, 0xb0, 0xb2, 0x96, + 0x5a, 0xc5, 0xbf, 0xd8, 0x10, 0x56, 0x0b, 0x31, 0x2e, 0xed, 0x47, 0xcc, 0x8a, 0x8c, 0x75, 0xae, + 0xce, 0x26, 0xb0, 0x7b, 0xdb, 0xb4, 0x7b, 0x3b, 0x84, 0xa5, 0x1d, 0x2e, 0x16, 0x4b, 0xdc, 0x7a, + 0xe7, 0x9e, 0xa3, 0x99, 0x37, 0xe4, 0x79, 0xa5, 0x48, 0x75, 0xb6, 0xd1, 0xa3, 0x2b, 0x67, 0xf6, + 0x55, 0x68, 0xdc, 0xe7, 0xa9, 0xba, 0xe6, 0xd6, 0xde, 0x58, 0xee, 0xde, 0xbb, 0x53, 0x72, 0x4b, + 0x6e, 0xf3, 0x0c, 0xb5, 0x76, 0x8b, 0xf7, 0x07, 0x5c, 0xa8, 0xa7, 0x6e, 0xd0, 0x7f, 0xc1, 0x7e, + 0x8d, 0x1a, 0xd7, 0x59, 0x33, 0x1b, 0xc6, 0xed, 0xa8, 0xd9, 0xf8, 0x4a, 0x0e, 0x2f, 0x6b, 0x39, + 0x8c, 0xfa, 0xdc, 0x30, 0xff, 0x21, 0x34, 0x8c, 0x94, 0x2e, 0x2d, 0x40, 0xc5, 0xf4, 0x34, 0x2d, + 0x40, 0x25, 0x19, 0x60, 0xee, 0x0d, 0xea, 0xc7, 0x65, 0x57, 0xb3, 0x7e, 0x44, 0xd6, 0x57, 0xd6, + 0xd3, 0xad, 0x8f, 0xfd, 0x51, 0xfa, 0x82, 0x3d, 0xa5, 0xa7, 0x69, 0xe6, 0x55, 0x7e, 0xe6, 0x0d, + 0xe6, 0x6f, 0xfd, 0xf5, 0x62, 0x19, 0x55, 0xb6, 0x87, 0x28, 0xba, 0x22, 0x2f, 0xe1, 0x33, 0x00, + 0x87, 0x69, 0x34, 0xde, 0xf1, 0xf9, 0x28, 0x0a, 0x33, 0x5d, 0x9b, 0x5d, 0x57, 0x67, 0xfa, 0xcb, + 0xb8, 0xb3, 0x66, 0x4f, 0x0d, 0x7f, 0xdc, 0xca, 0x84, 0x50, 0xcc, 0x35, 0xf3, 0x46, 0x5b, 0x2f, + 0x48, 0xc9, 0xad, 0xf6, 0x6d, 0x07, 0xbd, 0xeb, 0x2c, 0xa2, 0xaa, 0xbd, 0xeb, 0x42, 0xb0, 0x56, + 0xab, 0xbd, 0x92, 0xf0, 0xeb, 0x01, 0xd4, 0xb3, 0x10, 0xdd, 0xa5, 0x2c, 0x2d, 0xcf, 0x0a, 0xe8, + 0x69, 0xab, 0x58, 0x08, 0x9c, 0xb9, 0x2d, 0x5a, 0x2a, 0x60, 0x8b, 0xb8, 0x54, 0x14, 0x0d, 0x0b, + 0x60, 0x4d, 0x0c, 0x50, 0x9b, 0x78, 0xba, 0x80, 0x55, 0x33, 0x29, 0x09, 0x5e, 0x69, 0x69, 0x2e, + 0x8d, 0xfd, 0x58, 0xe7, 0x6c, 0xe4, 0x56, 0x71, 0xf9, 0x8b, 0xaa, 0x79, 0x04, 0xab, 0x85, 0xc0, + 0x85, 0x16, 0xe9, 0x59, 0xf1, 0x22, 0x2d, 0xd2, 0x33, 0x63, 0x1e, 0xee, 0x45, 0xea, 0x72, 0xc5, + 0x05, 0xec, 0x32, 0x39, 0x0b, 0xd2, 0xde, 0xc9, 0x07, 0xce, 0xe6, 0xd1, 0x3c, 0xfd, 0xa3, 0xda, + 0xa7, 0xfe, 0x37, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xeb, 0x5e, 0xe3, 0x83, 0x4d, 0x00, 0x00, } diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index cc42faba..769b3fb9 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -1129,11 +1129,10 @@ message PendingUpdate { } message OpenChannelRequest { - /// The pubkey of the node to open a channel with bytes node_pubkey = 2 [json_name = "node_pubkey"]; - /// The hex encoded pubkey of the node to open a channel with + /// The hex encoded pubkey of the node to open a channel with string node_pubkey_string = 3 [json_name = "node_pubkey_string"]; /// The number of satoshis the wallet should commit to the channel @@ -1156,6 +1155,9 @@ message OpenChannelRequest { /// The delay we require on the remote's commitment transaction. If this is not set, it will be scaled automatically with the channel size. uint32 remote_csv_delay = 10 [json_name = "remote_csv_delay"]; + + /// The minimum number of confirmations each one of your outputs used for the funding transaction must satisfy. + int32 min_confs = 11 [json_name = "min_confs"]; } message OpenStatusUpdate { oneof update { diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index 117ca38c..bbaec3e5 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -2164,6 +2164,11 @@ "type": "integer", "format": "int64", "description": "/ The delay we require on the remote's commitment transaction. If this is not set, it will be scaled automatically with the channel size." + }, + "min_confs": { + "type": "integer", + "format": "int32", + "description": "/ The minimum number of confirmations each one of your outputs used for the funding transaction must satisfy." } } }, From 8e15b48a05723006f5d400219d44da804bd31805 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:40:11 -0700 Subject: [PATCH 06/16] cmd/lncli: expose min confs parameter for OpenChannel command --- cmd/lncli/commands.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index be63d4e7..fecca085 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -472,6 +472,12 @@ var openChannelCommand = cli.Command{ "not set, we will scale the value according to the " + "channel size", }, + cli.Uint64Flag{ + Name: "min_confs", + Usage: "(optional) the minimum number of confirmations " + + "each one of your outputs used for the funding " + + "transaction must satisfy", + }, }, Action: actionDecorator(openChannel), } @@ -496,6 +502,7 @@ func openChannel(ctx *cli.Context) error { SatPerByte: ctx.Int64("sat_per_byte"), MinHtlcMsat: ctx.Int64("min_htlc_msat"), RemoteCsvDelay: uint32(ctx.Uint64("remote_csv_delay")), + MinConfs: int32(ctx.Uint64("min_confs")), } switch { From a3cec7e036134cd4c0bc426e1f3777eb6c7a97e3 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:44:25 -0700 Subject: [PATCH 07/16] rpcserver: set minConfs field in openChanReq --- rpcserver.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rpcserver.go b/rpcserver.go index 6f4e8e6c..bcee345a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -770,6 +770,12 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, "size is: %v SAT", int64(minChanFundingSize)) } + // Ensure that the MinConfs parameter is non-negative. + if in.MinConfs < 0 { + return errors.New("minimum number of confirmations must be a " + + "non-negative number") + } + var ( nodePubKey *btcec.PublicKey nodePubKeyBytes []byte @@ -822,6 +828,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, fundingFeePerKw: feeRate, private: in.Private, remoteCsvDelay: remoteCsvDelay, + minConfs: in.MinConfs, } updateChan, errChan := r.server.OpenChannel(req) @@ -936,6 +943,12 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, "size is: %v SAT", int64(minChanFundingSize)) } + // Ensure that the MinConfs parameter is non-negative. + if in.MinConfs < 0 { + return nil, errors.New("minimum number of confirmations must " + + "be a non-negative number") + } + // Based on the passed fee related parameters, we'll determine an // appropriate fee rate for the funding transaction. feeRate, err := determineFeePerKw( @@ -957,6 +970,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, fundingFeePerKw: feeRate, private: in.Private, remoteCsvDelay: remoteCsvDelay, + minConfs: in.MinConfs, } updateChan, errChan := r.server.OpenChannel(req) From ae3e66dccccebe06587cae6d6bc62f817a14941e Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:24:12 -0700 Subject: [PATCH 08/16] fundingmanager+lnwallet: allow specifying number of min confs for coin selection --- fundingmanager.go | 2 ++ lnwallet/wallet.go | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index c31080ae..f1163fea 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1052,6 +1052,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { FundingFeePerKw: 0, PushMSat: msg.PushAmount, Flags: msg.ChannelFlags, + MinConfs: 1, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) @@ -2615,6 +2616,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { FundingFeePerKw: msg.fundingFeePerKw, PushMSat: msg.pushAmt, Flags: channelFlags, + MinConfs: msg.minConfs, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index cb03a16d..c0218957 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -93,6 +93,10 @@ type InitFundingReserveMsg struct { // open_channel message. Flags lnwire.FundingFlag + // MinConfs indicates the minimum number of confirmations that each + // output selected to fund the channel should satisfy. + MinConfs int32 + // err is a channel in which all errors will be sent across. Will be // nil if this initial set is successful. // @@ -473,7 +477,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg // Coin selection is done on the basis of sat/kw, so we'll use // the fee rate passed in to perform coin selection. err := l.selectCoinsAndChange( - req.FundingFeePerKw, req.FundingAmount, + req.FundingFeePerKw, req.FundingAmount, req.MinConfs, reservation.ourContribution, ) if err != nil { @@ -1256,9 +1260,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // selection is successful/possible, then the selected coins are available // within the passed contribution's inputs. If necessary, a change address will // also be generated. -// TODO(roasbeef): remove hardcoded fees and req'd confs for outputs. +// TODO(roasbeef): remove hardcoded fees. func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight, - amt btcutil.Amount, contribution *ChannelContribution) error { + amt btcutil.Amount, minConfs int32, + contribution *ChannelContribution) error { // We hold the coin select mutex while querying for outputs, and // performing coin selection in order to avoid inadvertent double @@ -1269,10 +1274,9 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight, walletLog.Infof("Performing funding tx coin selection using %v "+ "sat/kw as fee rate", int64(feeRate)) - // Find all unlocked unspent witness outputs with greater than 1 - // confirmation. - // TODO(roasbeef): make num confs a configuration parameter - coins, err := l.ListUnspentWitness(1) + // Find all unlocked unspent witness outputs that satisfy the minimum + // number of confirmations required. + coins, err := l.ListUnspentWitness(minConfs) if err != nil { return err } From 7724d868011b0717d51448aae491f4e006ae7db7 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 19:03:08 -0700 Subject: [PATCH 09/16] config+pilot: add flag to specify min confs for autopilot agent --- config.go | 7 +++++++ pilot.go | 13 ++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index 6e65afb7..f70785de 100644 --- a/config.go +++ b/config.go @@ -146,6 +146,7 @@ type autoPilotConfig struct { MinChannelSize int64 `long:"minchansize" description:"The smallest channel that the autopilot agent should create"` MaxChannelSize int64 `long:"maxchansize" description:"The largest channel that the autopilot agent should create"` Private bool `long:"private" description:"Whether the channels created by the autopilot agent should be private or not. Private channels won't be announced to the network."` + MinConfs int32 `long:"minconfs" description:"The minimum number of confirmations each of your inputs in funding transactions created by the autopilot agent must have."` } type torConfig struct { @@ -424,6 +425,12 @@ func loadConfig() (*config, error) { fmt.Fprintln(os.Stderr, err) return nil, err } + if cfg.Autopilot.MinConfs < 0 { + str := "%s: autopilot.minconfs must be non-negative" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + return nil, err + } // Ensure that the specified values for the min and max channel size // don't are within the bounds of the normal chan size constraints. diff --git a/pilot.go b/pilot.go index 86c8ef5a..fa4e77ef 100644 --- a/pilot.go +++ b/pilot.go @@ -17,8 +17,9 @@ import ( // chanController is an implementation of the autopilot.ChannelController // interface that's backed by a running lnd instance. type chanController struct { - server *server - private bool + server *server + private bool + minConfs int32 } // OpenChannel opens a channel to a target peer, with a capacity of the @@ -48,6 +49,7 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey, fundingFeePerKw: feePerKw, private: c.private, remoteCsvDelay: 0, + minConfs: c.minConfs, } updateStream, errChan := c.server.OpenChannel(req) @@ -110,11 +112,12 @@ func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error) Self: self, Heuristic: prefAttachment, ChanController: &chanController{ - server: svr, - private: cfg.Private, + server: svr, + private: cfg.Private, + minConfs: cfg.MinConfs, }, WalletBalance: func() (btcutil.Amount, error) { - return svr.cc.wallet.ConfirmedBalance(1) + return svr.cc.wallet.ConfirmedBalance(cfg.MinConfs) }, Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()), MaxPendingOpens: 10, From dd5b6394d92921088357f6e21e917bf4ac675996 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 6 Aug 2018 18:14:53 -0700 Subject: [PATCH 10/16] autopilot: read quit chan when sending state updates --- autopilot/agent.go | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/autopilot/agent.go b/autopilot/agent.go index da69a935..b2450523 100644 --- a/autopilot/agent.go +++ b/autopilot/agent.go @@ -206,9 +206,13 @@ type chanCloseUpdate struct { // OnBalanceChange is a callback that should be executed each time the balance of // the backing wallet changes. func (a *Agent) OnBalanceChange(delta btcutil.Amount) { + a.wg.Add(1) go func() { - a.stateUpdates <- &balanceUpdate{ - balanceDelta: delta, + defer a.wg.Done() + + select { + case a.stateUpdates <- &balanceUpdate{balanceDelta: delta}: + case <-a.quit: } }() } @@ -216,9 +220,13 @@ func (a *Agent) OnBalanceChange(delta btcutil.Amount) { // OnChannelOpen is a callback that should be executed each time a new channel // is manually opened by the user or any system outside the autopilot agent. func (a *Agent) OnChannelOpen(c Channel) { + a.wg.Add(1) go func() { - a.stateUpdates <- &chanOpenUpdate{ - newChan: c, + defer a.wg.Done() + + select { + case a.stateUpdates <- &chanOpenUpdate{newChan: c}: + case <-a.quit: } }() } @@ -227,8 +235,14 @@ func (a *Agent) OnChannelOpen(c Channel) { // autopilot has attempted to open a channel, but failed. In this case we can // retry channel creation with a different node. func (a *Agent) OnChannelOpenFailure() { + a.wg.Add(1) go func() { - a.stateUpdates <- &chanOpenFailureUpdate{} + defer a.wg.Done() + + select { + case a.stateUpdates <- &chanOpenFailureUpdate{}: + case <-a.quit: + } }() } @@ -236,9 +250,13 @@ func (a *Agent) OnChannelOpenFailure() { // channel has been closed for any reason. This includes regular // closes, force closes, and channel breaches. func (a *Agent) OnChannelClose(closedChans ...lnwire.ShortChannelID) { + a.wg.Add(1) go func() { - a.stateUpdates <- &chanCloseUpdate{ - closedChans: closedChans, + defer a.wg.Done() + + select { + case a.stateUpdates <- &chanCloseUpdate{closedChans: closedChans}: + case <-a.quit: } }() } From 956acdfa14b29c6ab4d221f6038e71133d8feee2 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 6 Aug 2018 18:17:38 -0700 Subject: [PATCH 11/16] autopilot/agent_test: pass in agent's quit chan to heuristic In this commit, we alter our mock heuristic to also take in a quit chan. It's possible that at the end of a test the agent is blocked on a NeedMoreChans/Select call as their mock implementations use channels. To prevent this, we use the agent's quit chan so that the heuristic can safely exit once the agent does. --- autopilot/agent_test.go | 67 ++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/autopilot/agent_test.go b/autopilot/agent_test.go index 9baf96eb..eb1009b3 100644 --- a/autopilot/agent_test.go +++ b/autopilot/agent_test.go @@ -2,13 +2,12 @@ package autopilot import ( "bytes" + "errors" "net" "sync" "testing" "time" - "errors" - "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" @@ -31,21 +30,32 @@ type mockHeuristic struct { directiveResps chan []AttachmentDirective directiveArgs chan directiveArg + + quit chan struct{} } func (m *mockHeuristic) NeedMoreChans(chans []Channel, balance btcutil.Amount) (btcutil.Amount, uint32, bool) { if m.moreChanArgs != nil { - m.moreChanArgs <- moreChanArg{ + moreChan := moreChanArg{ chans: chans, balance: balance, } + select { + case m.moreChanArgs <- moreChan: + case <-m.quit: + return 0, 0, false + } } - resp := <-m.moreChansResps - return resp.amt, resp.numMore, resp.needMore + select { + case resp := <-m.moreChansResps: + return resp.amt, resp.numMore, resp.needMore + case <-m.quit: + return 0, 0, false + } } type directiveArg struct { @@ -60,16 +70,26 @@ func (m *mockHeuristic) Select(self *btcec.PublicKey, graph ChannelGraph, skipChans map[NodeID]struct{}) ([]AttachmentDirective, error) { if m.directiveArgs != nil { - m.directiveArgs <- directiveArg{ + directive := directiveArg{ self: self, graph: graph, amt: amtToUse, skip: skipChans, } + + select { + case m.directiveArgs <- directive: + case <-m.quit: + return nil, errors.New("exiting") + } } - resp := <-m.directiveResps - return resp, nil + select { + case resp := <-m.directiveResps: + return resp, nil + case <-m.quit: + return nil, errors.New("exiting") + } } var _ AttachmentHeuristic = (*mockHeuristic)(nil) @@ -155,6 +175,10 @@ func TestAgentChannelOpenSignal(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll star the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -296,6 +320,10 @@ func TestAgentChannelFailureSignal(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll start the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -402,6 +430,10 @@ func TestAgentChannelCloseSignal(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll star the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -521,6 +553,10 @@ func TestAgentBalanceUpdate(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll star the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -643,6 +679,10 @@ func TestAgentImmediateAttach(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll star the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -781,6 +821,10 @@ func TestAgentPrivateChannels(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll star the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -914,6 +958,10 @@ func TestAgentPendingChannelState(t *testing.T) { t.Fatalf("unable to create agent: %v", err) } + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + // With the autopilot agent and all its dependencies we'll start the // primary controller goroutine. if err := agent.Start(); err != nil { @@ -966,7 +1014,6 @@ func TestAgentPendingChannelState(t *testing.T) { } select { case heuristic.directiveResps <- []AttachmentDirective{nodeDirective}: - return case <-time.After(time.Second * 10): t.Fatalf("heuristic wasn't queried in time") } @@ -994,8 +1041,6 @@ func TestAgentPendingChannelState(t *testing.T) { // heuristic. agent.OnBalanceChange(0.4 * btcutil.SatoshiPerBitcoin) - wg = sync.WaitGroup{} - // The heuristic should be queried, and the argument for the set of // channels passed in should include the pending channels that // should've been created above. From aa7c2e6d470c0cbf7b12e9f25b8506f361cfa284 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 6 Aug 2018 18:18:05 -0700 Subject: [PATCH 12/16] autopilot: add OnChannelPendingOpen state update --- autopilot/agent.go | 31 +++++++++++++- autopilot/agent_test.go | 94 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/autopilot/agent.go b/autopilot/agent.go index b2450523..6e521ba6 100644 --- a/autopilot/agent.go +++ b/autopilot/agent.go @@ -186,13 +186,18 @@ type balanceUpdate struct { balanceDelta btcutil.Amount } -// chanOpenUpdate is a type of external state update the indicates a new +// chanOpenUpdate is a type of external state update that indicates a new // channel has been opened, either by the Agent itself (within the main // controller loop), or by an external user to the system. type chanOpenUpdate struct { newChan Channel } +// chanPendingOpenUpdate is a type of external state update that indicates a new +// channel has been opened, either by the agent itself or an external subsystem, +// but is still pending. +type chanPendingOpenUpdate struct{} + // chanOpenFailureUpdate is a type of external state update that indicates // a previous channel open failed, and that it might be possible to try again. type chanOpenFailureUpdate struct{} @@ -231,6 +236,18 @@ func (a *Agent) OnChannelOpen(c Channel) { }() } +// OnChannelPendingOpen is a callback that should be executed each time a new +// channel is opened, either by the agent or an external subsystems, but is +// still pending. +func (a *Agent) OnChannelPendingOpen() { + go func() { + select { + case a.stateUpdates <- &chanPendingOpenUpdate{}: + case <-a.quit: + } + }() +} + // OnChannelOpenFailure is a callback that should be executed when the // autopilot has attempted to open a channel, but failed. In this case we can // retry channel creation with a different node. @@ -382,6 +399,12 @@ func (a *Agent) controller(startingBalance btcutil.Amount) { updateBalance() + // A new channel has been opened by the agent or an + // external subsystem, but is still pending + // confirmation. + case *chanPendingOpenUpdate: + updateBalance() + // A channel has been closed, this may free up an // available slot, triggering a new channel update. case *chanCloseUpdate: @@ -598,6 +621,12 @@ func (a *Agent) controller(startingBalance btcutil.Amount) { err) } } + + // Since the channel open was successful + // and is currently pending, we'll + // trigger the autopilot agent to query + // for more peers. + a.OnChannelPendingOpen() }(chanCandidate) } pendingMtx.Unlock() diff --git a/autopilot/agent_test.go b/autopilot/agent_test.go index eb1009b3..d89913bf 100644 --- a/autopilot/agent_test.go +++ b/autopilot/agent_test.go @@ -1089,3 +1089,97 @@ func TestAgentPendingChannelState(t *testing.T) { t.Fatalf("select wasn't queried in time") } } + +// TestAgentPendingOpenChannel ensures that the agent queries its heuristic once +// it detects a channel is pending open. This allows the agent to use its own +// change outputs that have yet to confirm for funding transactions. +func TestAgentPendingOpenChannel(t *testing.T) { + t.Parallel() + + // First, we'll create all the dependencies that we'll need in order to + // create the autopilot agent. + self, err := randKey() + if err != nil { + t.Fatalf("unable to generate key: %v", err) + } + heuristic := &mockHeuristic{ + moreChansResps: make(chan moreChansResp), + directiveResps: make(chan []AttachmentDirective), + } + chanController := &mockChanController{ + openChanSignals: make(chan openChanIntent), + } + memGraph, _, _ := newMemChanGraph() + + // The wallet will start with 6 BTC available. + const walletBalance = btcutil.SatoshiPerBitcoin * 6 + + // With the dependencies we created, we can now create the initial + // agent itself. + cfg := Config{ + Self: self, + Heuristic: heuristic, + ChanController: chanController, + WalletBalance: func() (btcutil.Amount, error) { + return walletBalance, nil + }, + Graph: memGraph, + MaxPendingOpens: 10, + } + agent, err := New(cfg, nil) + if err != nil { + t.Fatalf("unable to create agent: %v", err) + } + + // To ensure the heuristic doesn't block on quitting the agent, we'll + // use the agent's quit chan to signal when it should also stop. + heuristic.quit = agent.quit + + // With the autopilot agent and all its dependencies we'll start the + // primary controller goroutine. + if err := agent.Start(); err != nil { + t.Fatalf("unable to start agent: %v", err) + } + defer agent.Stop() + + // We'll send an initial "no" response to advance the agent past its + // initial check. + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + select { + case heuristic.moreChansResps <- moreChansResp{false, 0, 0}: + case <-time.After(time.Second * 10): + t.Fatalf("heuristic wasn't queried in time") + } + }() + + // Next, we'll signal that a new channel has been opened, but it is + // still pending. + agent.OnChannelPendingOpen() + + // The agent should now query the heuristic in order to determine its + // next action as its local state has now been modified. + wg.Add(1) + go func() { + defer wg.Done() + select { + case heuristic.moreChansResps <- moreChansResp{false, 0, 0}: + case <-time.After(time.Second * 10): + t.Fatalf("heuristic wasn't queried in time") + } + }() + + // We'll wait here for either the agent to query the heuristic to be + // queried, or for the timeout above to tick. + wg.Wait() + + // There shouldn't be a call to the Select method as we've returned + // "false" for NeedMoreChans above. + select { + case heuristic.directiveResps <- []AttachmentDirective{}: + t.Fatalf("Select was called but shouldn't have been") + default: + } +} From 0a335bbea2ead6d103fe28deb3dca2957522b04b Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 21:15:40 -0700 Subject: [PATCH 13/16] lntest: extract WaitForBalance into a method --- lntest/harness.go | 22 ++-------------------- lntest/node.go | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lntest/harness.go b/lntest/harness.go index 60c0f4ef..d31c5592 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -1171,24 +1171,6 @@ func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount, return err } - // Pause until the nodes current wallet balances reflects the amount - // sent to it above. - // TODO(roasbeef): factor out into helper func - balanceTicker := time.Tick(time.Millisecond * 50) - balanceTimeout := time.After(time.Second * 30) - for { - select { - case <-balanceTicker: - currentBal, err := target.WalletBalance(ctx, balReq) - if err != nil { - return err - } - - if currentBal.ConfirmedBalance == initialBalance.ConfirmedBalance+int64(amt) { - return nil - } - case <-balanceTimeout: - return fmt.Errorf("balances not synced after deadline") - } - } + expectedBalance := initialBalance.ConfirmedBalance + int64(amt) + return target.WaitForBalance(expectedBalance, true) } diff --git a/lntest/node.go b/lntest/node.go index c3c44590..aa965568 100644 --- a/lntest/node.go +++ b/lntest/node.go @@ -895,6 +895,33 @@ func (hn *HarnessNode) WaitForBlockchainSync(ctx context.Context) error { } } +// WaitForBalance waits until the node sees the expected confirmed/unconfirmed +// balance within their wallet. +func (hn *HarnessNode) WaitForBalance(expectedBalance int64, confirmed bool) error { + ctx := context.Background() + req := &lnrpc.WalletBalanceRequest{} + + doesBalanceMatch := func() bool { + balance, err := hn.WalletBalance(ctx, req) + if err != nil { + return false + } + + if confirmed { + return balance.ConfirmedBalance == expectedBalance + } + + return balance.UnconfirmedBalance == expectedBalance + } + + err := WaitPredicate(doesBalanceMatch, 30*time.Second) + if err != nil { + return errors.New("balances not synced after deadline") + } + + return nil +} + // fileExists reports whether the named file or directory exists. // This function is taken from https://github.com/btcsuite/btcd func fileExists(name string) bool { From 130124bb796a878052697a0f51814d4400e20175 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 21:16:20 -0700 Subject: [PATCH 14/16] lntest: add SendCoinsUnconfimed method In this commit, we add a new method to the network harness that allows us to send funds to another party without confirming the transaction. This will be useful for testing funding channels with outputs that have not been confirmed yet. --- lntest/harness.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/lntest/harness.go b/lntest/harness.go index d31c5592..1a6ed867 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -1104,12 +1104,26 @@ func (n *NetworkHarness) DumpLogs(node *HarnessNode) (string, error) { } // SendCoins attempts to send amt satoshis from the internal mining node to the -// targeted lightning node using a P2WKH address. +// targeted lightning node using a P2WKH address. 6 blocks are mined after in +// order to confirm the transaction. func (n *NetworkHarness) SendCoins(ctx context.Context, amt btcutil.Amount, target *HarnessNode) error { return n.sendCoins( ctx, amt, target, lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH, + true, + ) +} + +// SendCoinsUnconfirmed sends coins from the internal mining node to the target +// lightning node using a P2WPKH address. No blocks are mined after, so the +// transaction remains unconfirmed. +func (n *NetworkHarness) SendCoinsUnconfirmed(ctx context.Context, + amt btcutil.Amount, target *HarnessNode) error { + + return n.sendCoins( + ctx, amt, target, lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH, + false, ) } @@ -1120,14 +1134,16 @@ func (n *NetworkHarness) SendCoinsNP2WKH(ctx context.Context, return n.sendCoins( ctx, amt, target, lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH, + true, ) } // sendCoins attempts to send amt satoshis from the internal mining node to the -// targeted lightning node. +// targeted lightning node. The confirmed boolean indicates whether the +// transaction that pays to the target should confirm. func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount, - target *HarnessNode, - addrType lnrpc.NewAddressRequest_AddressType) error { + target *HarnessNode, addrType lnrpc.NewAddressRequest_AddressType, + confirmed bool) error { balReq := &lnrpc.WalletBalanceRequest{} initialBalance, err := target.WalletBalance(ctx, balReq) @@ -1165,8 +1181,17 @@ func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount, return err } - // Finally, generate 6 new blocks to ensure the output gains a - // sufficient number of confirmations. + // If the transaction should remain unconfirmed, then we'll wait until + // the target node's unconfirmed balance reflects the expected balance + // and exit. + if !confirmed { + expectedBalance := initialBalance.UnconfirmedBalance + int64(amt) + return target.WaitForBalance(expectedBalance, false) + } + + // Otherwise, we'll generate 6 new blocks to ensure the output gains a + // sufficient number of confirmations and wait for the balance to + // reflect what's expected. if _, err := n.Miner.Node.Generate(6); err != nil { return err } From 9ec643386394907970ae5489f12bb60708e9a08f Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 21:17:06 -0700 Subject: [PATCH 15/16] lntest: extend OpenChannel to specify whether confirmed funds should be used --- lnd_test.go | 16 ++++++++++------ lntest/harness.go | 12 ++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lnd_test.go b/lnd_test.go index 82dd6a6d..ec55e3f0 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -153,7 +153,7 @@ func openChannelAndAssert(ctx context.Context, t *harnessTest, private bool) *lnrpc.ChannelPoint { chanOpenUpdate, err := net.OpenChannel( - ctx, alice, bob, fundingAmt, pushAmt, private, + ctx, alice, bob, fundingAmt, pushAmt, private, true, ) if err != nil { t.Fatalf("unable to open channel: %v", err) @@ -3694,8 +3694,9 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) { if err := net.ConnectNodes(ctxb, carol, net.Alice); err != nil { t.Fatalf("unable to connect dave to alice: %v", err) } - chanOpenUpdate, err := net.OpenChannel(ctxb, carol, net.Alice, chanAmt, - 0, true) + chanOpenUpdate, err := net.OpenChannel( + ctxb, carol, net.Alice, chanAmt, 0, true, true, + ) if err != nil { t.Fatalf("unable to open channel: %v", err) } @@ -4576,8 +4577,9 @@ func testMaxPendingChannels(net *lntest.NetworkHarness, t *harnessTest) { openStreams := make([]lnrpc.Lightning_OpenChannelClient, maxPendingChannels) for i := 0; i < maxPendingChannels; i++ { ctx, _ = context.WithTimeout(context.Background(), timeout) - stream, err := net.OpenChannel(ctx, net.Alice, carol, amount, - 0, false) + stream, err := net.OpenChannel( + ctx, net.Alice, carol, amount, 0, false, true, + ) if err != nil { t.Fatalf("unable to open channel: %v", err) } @@ -4587,7 +4589,9 @@ func testMaxPendingChannels(net *lntest.NetworkHarness, t *harnessTest) { // Carol exhausted available amount of pending channels, next open // channel request should cause ErrorGeneric to be sent back to Alice. ctx, _ = context.WithTimeout(context.Background(), timeout) - _, err = net.OpenChannel(ctx, net.Alice, carol, amount, 0, false) + _, err = net.OpenChannel( + ctx, net.Alice, carol, amount, 0, false, true, + ) if err == nil { t.Fatalf("error wasn't received") } else if grpc.Code(err) != lnwire.ErrMaxPendingChannels.ToGrpcCode() { diff --git a/lntest/harness.go b/lntest/harness.go index 1a6ed867..fd0b4078 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -676,10 +676,12 @@ func (n *NetworkHarness) WaitForTxBroadcast(ctx context.Context, txid chainhash. // OpenChannel attempts to open a channel between srcNode and destNode with the // passed channel funding parameters. If the passed context has a timeout, then // if the timeout is reached before the channel pending notification is -// received, an error is returned. +// received, an error is returned. The confirmed boolean determines whether we +// should fund the channel with confirmed outputs or not. func (n *NetworkHarness) OpenChannel(ctx context.Context, srcNode, destNode *HarnessNode, amt btcutil.Amount, - pushAmt btcutil.Amount, private bool) (lnrpc.Lightning_OpenChannelClient, error) { + pushAmt btcutil.Amount, + private, confirmed bool) (lnrpc.Lightning_OpenChannelClient, error) { // Wait until srcNode and destNode have the latest chain synced. // Otherwise, we may run into a check within the funding manager that @@ -692,11 +694,17 @@ func (n *NetworkHarness) OpenChannel(ctx context.Context, return nil, fmt.Errorf("Unable to sync destNode chain: %v", err) } + minConfs := int32(0) + if confirmed { + minConfs = 1 + } + openReq := &lnrpc.OpenChannelRequest{ NodePubkey: destNode.PubKey[:], LocalFundingAmount: int64(amt), PushSat: int64(pushAmt), Private: private, + MinConfs: minConfs, } respStream, err := srcNode.OpenChannel(ctx, openReq) From 169ebad6c5369765e7d4bd96d8eeb2ea0018ae57 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Thu, 9 Aug 2018 21:14:31 -0700 Subject: [PATCH 16/16] lnd_test: add test for funding channels with unconfirmed outputs --- lnd_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/lnd_test.go b/lnd_test.go index ec55e3f0..207af566 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -677,6 +677,82 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) { closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false) } +// testUnconfirmedChannelFunding tests that unconfirmed outputs that pay to us +// can be used to fund channels. +func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) { + const ( + timeout = time.Duration(15 * time.Second) + chanAmt = maxBtcFundingAmount + pushAmt = btcutil.Amount(100000) + ) + + ctxb := context.Background() + + // We'll start off by creating a node for Carol. + carol, err := net.NewNode("Carol", nil) + if err != nil { + t.Fatalf("unable to create carol's node: %v", err) + } + defer shutdownAndAssert(net, t, carol) + + // We'll send her some funds that should not confirm. + ctxt, _ := context.WithTimeout(ctxb, timeout) + err = net.SendCoinsUnconfirmed(ctxt, 2*chanAmt, carol) + if err != nil { + t.Fatalf("unable to send coins to carol: %v", err) + } + + // Now, we'll connect her to Alice so that they can open a channel + // together. The funding flow should select Carol's unconfirmed output + // as she doesn't have any other funds since it's a new node. + ctxt, _ = context.WithTimeout(ctxb, timeout) + if err := net.ConnectNodes(ctxt, carol, net.Alice); err != nil { + t.Fatalf("unable to connect dave to alice: %v", err) + } + ctxt, _ = context.WithTimeout(ctxb, timeout) + chanOpenUpdate, err := net.OpenChannel( + ctxt, carol, net.Alice, chanAmt, pushAmt, false, false, + ) + if err != nil { + t.Fatalf("unable to open channel between carol and alice: %v", + err) + } + + // Confirm the channel and wait for it to be recognized by both parties. + mineBlocks(t, net, 6) + ctxt, _ = context.WithTimeout(ctxb, timeout) + chanPoint, err := net.WaitForChannelOpen(ctxt, chanOpenUpdate) + if err != nil { + t.Fatalf("error while waiting for channel open: %v", err) + } + + // With the channel open, we'll check the balances on each side of the + // channel as a sanity check to ensure things worked out as intended. + balReq := &lnrpc.ChannelBalanceRequest{} + ctxt, _ = context.WithTimeout(ctxb, timeout) + carolBal, err := carol.ChannelBalance(ctxt, balReq) + if err != nil { + t.Fatalf("unable to get carol's balance: %v", err) + } + ctxt, _ = context.WithTimeout(ctxb, timeout) + aliceBal, err := net.Alice.ChannelBalance(ctxt, balReq) + if err != nil { + t.Fatalf("unable to get alice's balance: %v", err) + } + if carolBal.Balance != int64(chanAmt-pushAmt-calcStaticFee(0)) { + t.Fatalf("carol's balance is incorrect: expected %v got %v", + chanAmt-pushAmt-calcStaticFee(0), carolBal) + } + if aliceBal.Balance != int64(pushAmt) { + t.Fatalf("alice's balance is incorrect: expected %v got %v", + pushAmt, aliceBal.Balance) + } + + // Now that we're done with the test, the channel can be closed. + ctxt, _ = context.WithTimeout(ctxb, timeout) + closeChannelAndAssert(ctxt, t, net, carol, chanPoint, false) +} + // txStr returns the string representation of the channel's funding transaction. func txStr(chanPoint *lnrpc.ChannelPoint) string { txidHash, err := getChanPointFundingTxid(chanPoint) @@ -10970,6 +11046,10 @@ var testsCases = []*testCase{ name: "basic funding flow", test: testBasicChannelFunding, }, + { + name: "unconfirmed channel funding", + test: testUnconfirmedChannelFunding, + }, { name: "update channel policy", test: testUpdateChannelPolicy,