From a1a9834a534cb75df3b91f34a02006426b2cf397 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 15 Jan 2018 23:26:35 +0100 Subject: [PATCH 01/19] lnwallet: add PublishTransaction error types --- lnwallet/interface.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lnwallet/interface.go b/lnwallet/interface.go index c80d4479..87d1ea81 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -35,6 +35,11 @@ const ( PubKeyHash ) +// ErrDoubleSpend is returned from PublishTransaction in case the +// tx being published is spending an output spent by a conflicting +// transaction. +var ErrDoubleSpend = errors.New("Transaction rejected: output already spent") + // Utxo is an unspent output denoted by its outpoint, and output value of the // original output. type Utxo struct { @@ -183,6 +188,11 @@ type WalletController interface { // PublishTransaction performs cursory validation (dust checks, etc), // then finally broadcasts the passed transaction to the Bitcoin network. + // If the transaction is rejected because it is conflicting with an + // already known transaction, ErrDoubleSpend is returned. If the + // transaction is already known (published already), no error will be + // returned. Other error returned depends on the currently active chain + // backend. PublishTransaction(tx *wire.MsgTx) error // SubscribeTransactions returns a TransactionSubscription client which From 1dcc89cca9d3c45479b319444735a4881b5180db Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 15 Jan 2018 23:27:27 +0100 Subject: [PATCH 02/19] lnwallet/btcwallet: return concrete error type from PublishTransaction --- lnwallet/btcwallet/btcwallet.go | 85 ++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index c6229edc..4c4f50f9 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "math" + "strings" "sync" "time" @@ -399,9 +400,89 @@ func (b *BtcWallet) ListUnspentWitness(minConfs int32) ([]*lnwallet.Utxo, error) } // PublishTransaction performs cursory validation (dust checks, etc), then -// finally broadcasts the passed transaction to the Bitcoin network. +// finally broadcasts the passed transaction to the Bitcoin network. If +// publishing the transaction fails, an error describing the reason is +// returned (currently ErrDoubleSpend). If the transaction is already +// published to the network (either in the mempool or chain) no error +// will be returned. func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx) error { - return b.wallet.PublishTransaction(tx) + if err := b.wallet.PublishTransaction(tx); err != nil { + switch b.chain.(type) { + case *chain.RPCClient: + if strings.Contains(err.Error(), "already have") { + // Transaction was already in the mempool, do + // not treat as an error. We do this to mimic + // the behaviour of bitcoind, which will not + // return an error if a transaction in the + // mempool is sent again using the + // sendrawtransaction RPC call. + return nil + } + if strings.Contains(err.Error(), "already exists") { + // Transaction was already mined, we don't + // consider this an error. + return nil + } + if strings.Contains(err.Error(), "already spent") { + // Output was already spent. + return lnwallet.ErrDoubleSpend + } + if strings.Contains(err.Error(), "orphan transaction") { + // Transaction is spending either output that + // is missing or already spent. + return lnwallet.ErrDoubleSpend + } + + case *chain.BitcoindClient: + if strings.Contains(err.Error(), "txn-already-in-mempool") { + // Transaction in mempool, treat as non-error. + return nil + } + if strings.Contains(err.Error(), "txn-already-known") { + // Transaction in mempool, treat as non-error. + return nil + } + if strings.Contains(err.Error(), "already in block") { + // Transaction was already mined, we don't + // consider this an error. + return nil + } + if strings.Contains(err.Error(), "txn-mempool-conflict") { + // Output was spent by other transaction + // already in the mempool. + return lnwallet.ErrDoubleSpend + } + if strings.Contains(err.Error(), "insufficient fee") { + // RBF enabled transaction did not have enough fee. + return lnwallet.ErrDoubleSpend + } + if strings.Contains(err.Error(), "Missing inputs") { + // Transaction is spending either output that + // is missing or already spent. + return lnwallet.ErrDoubleSpend + } + + case *chain.NeutrinoClient: + if strings.Contains(err.Error(), "already have") { + // Transaction was already in the mempool, do + // not treat as an error. + return nil + } + if strings.Contains(err.Error(), "already exists") { + // Transaction was already mined, we don't + // consider this an error. + return nil + } + if strings.Contains(err.Error(), "already spent") { + // Output was already spent. + return lnwallet.ErrDoubleSpend + } + + default: + } + return err + } + return nil } // extractBalanceDelta extracts the net balance delta from the PoV of the From d96b5b62eb843901a2b2d238aa6e34e9bd66eb5e Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 19 Jan 2018 14:24:30 +0100 Subject: [PATCH 03/19] lnwallet test: add test for PublishTransaction return errors --- lnwallet/interface_test.go | 324 +++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 612be9c7..5e002eb6 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -1114,6 +1114,326 @@ func testTransactionSubscriptions(miner *rpctest.Harness, } } +// testPublishTransaction checks that PublishTransaction returns the +// expected error types in case the transaction being published +// conflicts with the current mempool or chain. +func testPublishTransaction(r *rpctest.Harness, + alice, _ *lnwallet.LightningWallet, t *testing.T) { + + // mineAndAssert mines a block and ensures the passed TX + // is part of that block. + mineAndAssert := func(tx *wire.MsgTx) error { + blockHashes, err := r.Node.Generate(1) + if err != nil { + return fmt.Errorf("unable to generate block: %v", err) + } + + block, err := r.Node.GetBlock(blockHashes[0]) + if err != nil { + return fmt.Errorf("unable to find block: %v", err) + } + + if len(block.Transactions) != 2 { + return fmt.Errorf("expected 2 txs in block, got %d", + len(block.Transactions)) + } + + blockTx := block.Transactions[1] + if blockTx.TxHash() != tx.TxHash() { + return fmt.Errorf("incorrect transaction was mined") + } + + // Sleep for a second before returning, to make sure the + // block has propagated. + time.Sleep(1 * time.Second) + return nil + } + + // Generate a pubkey, and pay-to-addr script. + pubKey, err := alice.NewRawKey() + if err != nil { + t.Fatalf("unable to obtain public key: %v", err) + } + pubkeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash, + &chaincfg.RegressionNetParams) + if err != nil { + t.Fatalf("unable to create addr: %v", err) + } + keyScript, err := txscript.PayToAddrScript(keyAddr) + if err != nil { + t.Fatalf("unable to generate script: %v", err) + } + + // txFromOutput takes a tx, and creates a new tx that spends + // the output from this tx, to an address derived from payToPubKey. + // NB: assumes that the output from tx is paid to pubKey. + txFromOutput := func(tx *wire.MsgTx, payToPubKey *btcec.PublicKey, + txFee btcutil.Amount) *wire.MsgTx { + // Create a script to pay to. + payToPubkeyHash := btcutil.Hash160(payToPubKey.SerializeCompressed()) + payToKeyAddr, err := btcutil.NewAddressWitnessPubKeyHash(payToPubkeyHash, + &chaincfg.RegressionNetParams) + if err != nil { + t.Fatalf("unable to create addr: %v", err) + } + payToScript, err := txscript.PayToAddrScript(payToKeyAddr) + if err != nil { + t.Fatalf("unable to generate script: %v", err) + } + + // We assume the output was paid to the keyScript made earlier. + var outputIndex uint32 + if len(tx.TxOut) == 1 || bytes.Equal(tx.TxOut[0].PkScript, keyScript) { + outputIndex = 0 + } else { + outputIndex = 1 + } + outputValue := tx.TxOut[outputIndex].Value + + // With the index located, we can create a transaction spending + // the referenced output. + tx1 := wire.NewMsgTx(2) + tx1.AddTxIn(&wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: tx.TxHash(), + Index: outputIndex, + }, + // We don't support RBF, so set sequence to max. + Sequence: wire.MaxTxInSequenceNum, + }) + tx1.AddTxOut(&wire.TxOut{ + Value: outputValue - int64(txFee), + PkScript: payToScript, + }) + + // Now we can populate the sign descriptor which we'll use to + // generate the signature. + signDesc := &lnwallet.SignDescriptor{ + PubKey: pubKey, + WitnessScript: keyScript, + Output: tx.TxOut[outputIndex], + HashType: txscript.SigHashAll, + SigHashes: txscript.NewTxSigHashes(tx1), + InputIndex: 0, // Has only one input. + } + + // With the descriptor created, we use it to generate a + // signature, then manually create a valid witness stack we'll + // use for signing. + spendSig, err := alice.Cfg.Signer.SignOutputRaw(tx1, signDesc) + if err != nil { + t.Fatalf("unable to generate signature: %v", err) + } + witness := make([][]byte, 2) + witness[0] = append(spendSig, byte(txscript.SigHashAll)) + witness[1] = pubKey.SerializeCompressed() + tx1.TxIn[0].Witness = witness + + // Finally, attempt to validate the completed transaction. This + // should succeed if the wallet was able to properly generate + // the proper private key. + vm, err := txscript.NewEngine(keyScript, + tx1, 0, txscript.StandardVerifyFlags, nil, + nil, outputValue) + if err != nil { + t.Fatalf("unable to create engine: %v", err) + } + if err := vm.Execute(); err != nil { + t.Fatalf("spend is invalid: %v", err) + } + return tx1 + } + + // newTx sends coins from Alice's wallet, mines this transaction, + // and creates a new, unconfirmed tx that spends this output to + // pubKey. + newTx := func() *wire.MsgTx { + + // With the script fully assembled, instruct the wallet to fund + // the output with a newly created transaction. + newOutput := &wire.TxOut{ + Value: btcutil.SatoshiPerBitcoin, + PkScript: keyScript, + } + txid, err := alice.SendOutputs([]*wire.TxOut{newOutput}, 10) + if err != nil { + t.Fatalf("unable to create output: %v", err) + } + + // Query for the transaction generated above so we can located + // the index of our output. + err = waitForMempoolTx(r, txid) + if err != nil { + t.Fatalf("tx not relayed to miner: %v", err) + } + tx, err := r.Node.GetRawTransaction(txid) + if err != nil { + t.Fatalf("unable to query for tx: %v", err) + } + + if err := mineAndAssert(tx.MsgTx()); err != nil { + t.Fatalf("unable to mine tx: %v", err) + } + txFee := btcutil.Amount(0.1 * btcutil.SatoshiPerBitcoin) + tx1 := txFromOutput(tx.MsgTx(), pubKey, txFee) + + return tx1 + } + + // We will first check that publishing a transaction already + // in the mempool does NOT return an error. Create the tx. + tx1 := newTx() + + // Publish the transaction. + if err := alice.PublishTransaction(tx1); err != nil { + t.Fatalf("unable to publish: %v", err) + } + + txid1 := tx1.TxHash() + err = waitForMempoolTx(r, &txid1) + if err != nil { + t.Fatalf("tx not relayed to miner: %v", err) + } + + // Publish the exact same transaction again. This should + // not return an error, even though the transaction is + // already in the mempool. + if err := alice.PublishTransaction(tx1); err != nil { + t.Fatalf("unable to publish: %v", err) + } + + // Mine the transaction. + if _, err := r.Node.Generate(1); err != nil { + t.Fatalf("unable to generate block: %v", err) + } + + // We'll now test that we don't get an error if we try + // to publish a transaction that is already mined. + // + // Create a new transaction. We must do this to properly + // test the reject messages from our peers. They might + // only send us a reject message for a given tx once, + // so we create a new to make sure it is not just + // immediately rejected. + tx2 := newTx() + + // Publish this tx. + if err := alice.PublishTransaction(tx2); err != nil { + t.Fatalf("unable to publish: %v", err) + } + + txid2 := tx2.TxHash() + err = waitForMempoolTx(r, &txid2) + if err != nil { + t.Fatalf("tx not relayed to miner: %v", err) + } + + // Mine the transaction. + if err := mineAndAssert(tx2); err != nil { + t.Fatalf("unable to mine tx: %v", err) + } + + // Publish the transaction again. It is already mined, + // and we don't expect this to return an error. + if err := alice.PublishTransaction(tx2); err != nil { + t.Fatalf("unable to publish: %v", err) + } + + // Now we'll try to double spend an output with a different + // transaction. Create a new tx and publish it. This is + // the output we'll try to double spend. + tx3 := newTx() + if err := alice.PublishTransaction(tx3); err != nil { + t.Fatalf("unable to publish: %v", err) + } + + txid3 := tx3.TxHash() + err = waitForMempoolTx(r, &txid3) + if err != nil { + t.Fatalf("tx not relayed to miner: %v", err) + } + + // Mine the transaction. + if err := mineAndAssert(tx3); err != nil { + t.Fatalf("unable to mine tx: %v", err) + } + + // Now we create a transaction that spends the output + // from the tx just mined. This should be accepted + // into the mempool. + txFee := btcutil.Amount(0.05 * btcutil.SatoshiPerBitcoin) + tx4 := txFromOutput(tx3, pubKey, txFee) + if err := alice.PublishTransaction(tx4); err != nil { + t.Fatalf("unable to publish: %v", err) + } + + txid4 := tx4.TxHash() + err = waitForMempoolTx(r, &txid4) + if err != nil { + t.Fatalf("tx not relayed to miner: %v", err) + } + + // Create a new key we'll pay to, to ensure we create + // a unique transaction. + pubKey2, err := alice.NewRawKey() + if err != nil { + t.Fatalf("unable to obtain public key: %v", err) + } + + // Create a new transaction that spends the output from + // tx3, and that pays to a different address. We expect + // this to be rejected because it is a double spend. + tx5 := txFromOutput(tx3, pubKey2, txFee) + if err := alice.PublishTransaction(tx5); err != lnwallet.ErrDoubleSpend { + t.Fatalf("expected ErrDoubleSpend, got: %v", err) + } + + // Create another transaction that spends the same output, + // but has a higher fee. We expect also this tx to be + // rejected, since the sequence number of tx3 is set to Max, + // indicating it is not replacable. + pubKey3, err := alice.NewRawKey() + if err != nil { + t.Fatalf("unable to obtain public key: %v", err) + } + tx6 := txFromOutput(tx3, pubKey3, 3*txFee) + + // Expect rejection. + if err := alice.PublishTransaction(tx6); err != lnwallet.ErrDoubleSpend { + t.Fatalf("expected ErrDoubleSpend, got: %v", err) + } + + // At last we try to spend an output already spent by a + // confirmed transaction. + // TODO(halseth): we currently skip this test for neutrino, + // as the backing btcd node will consider the tx being an + // orphan, and will accept it. Should look into if this is + // the behavior also for bitcoind, and update test + // accordingly. + if alice.BackEnd() != "neutrino" { + // Mine the tx spending tx3. + if err := mineAndAssert(tx4); err != nil { + t.Fatalf("unable to mine tx: %v", err) + } + + // Create another tx spending tx3. + pubKey4, err := alice.NewRawKey() + if err != nil { + t.Fatalf("unable to obtain public key: %v", err) + } + tx7 := txFromOutput(tx3, pubKey4, txFee) + + // Expect rejection. + if err := alice.PublishTransaction(tx7); err != lnwallet.ErrDoubleSpend { + t.Fatalf("expected ErrDoubleSpend, got: %v", err) + } + } + + // TODO(halseth): test replaceable transactions when btcd + // gets RBF support. +} + func testSignOutputUsingTweaks(r *rpctest.Harness, alice, _ *lnwallet.LightningWallet, t *testing.T) { @@ -1469,6 +1789,10 @@ var walletTests = []walletTestCase{ name: "transaction details", test: testListTransactionDetails, }, + { + name: "publish transaction", + test: testPublishTransaction, + }, { name: "signed with tweaked pubkeys", test: testSignOutputUsingTweaks, From db0928fa6f3a3e309cc7860ae525e13d0502107e Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 12 Jan 2018 15:29:46 +0100 Subject: [PATCH 04/19] chancloser: don't check error returned from broadcastTx This commit removes the inspection of the return error from broadcastTx. This is done since the new error checking added to PublishTransaction will return a nil error in case the transaction already exists in the mempool. --- chancloser.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/chancloser.go b/chancloser.go index 46cafe91..24dc18e5 100644 --- a/chancloser.go +++ b/chancloser.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "strings" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" @@ -439,19 +438,7 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b return spew.Sdump(closeTx) })) if err := c.cfg.broadcastTx(closeTx); err != nil { - // TODO(halseth): add relevant error types to the - // WalletController interface as this is quite fragile. - switch { - case strings.Contains(err.Error(), "already exists"): - fallthrough - case strings.Contains(err.Error(), "already have"): - peerLog.Debugf("channel close tx from "+ - "ChannelPoint(%v) already exist, "+ - "probably broadcast by peer: %v", - c.chanPoint, err) - default: - return nil, false, err - } + return nil, false, err } // Clear out the current channel state, marking the channel as From 7aaa15b8b553f4dfbab72fad1c1ab6fc3116fa69 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 12 Jan 2018 15:31:02 +0100 Subject: [PATCH 05/19] utxonursery: don't ignore any returned error from PublishTransaction --- utxonursery.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/utxonursery.go b/utxonursery.go index 3c01967b..1f0045d7 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -5,7 +5,6 @@ import ( "encoding/binary" "fmt" "io" - "strings" "sync" "sync/atomic" @@ -1125,9 +1124,7 @@ func (u *utxoNursery) sweepMatureOutputs(classHeight uint32, finalTx *wire.MsgTx // With the sweep transaction fully signed, broadcast the transaction // to the network. Additionally, we can stop tracking these outputs as // they've just been swept. - // TODO(conner): handle concrete error types returned from publication - err := u.cfg.PublishTransaction(finalTx) - if err != nil && !strings.Contains(err.Error(), "TX rejected:") { + if err := u.cfg.PublishTransaction(finalTx); err != nil { utxnLog.Errorf("unable to broadcast sweep tx: %v, %v", err, spew.Sdump(finalTx)) return err @@ -1233,14 +1230,9 @@ func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) erro // We'll now broadcast the HTLC transaction, then wait for it to be // confirmed before transitioning it to kindergarten. - // - // TODO(conner): handle concrete error types returned from publication - err := u.cfg.PublishTransaction(baby.timeoutTx) - if err != nil && - !strings.Contains(err.Error(), "TX rejected:") { + if err := u.cfg.PublishTransaction(baby.timeoutTx); err != nil { utxnLog.Errorf("Unable to broadcast baby tx: "+ - "%v, %v", err, - spew.Sdump(baby.timeoutTx)) + "%v, %v", err, spew.Sdump(baby.timeoutTx)) return err } From 3fd7f28b39c2e7bd92cc5afb284c71d9d827fda6 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 12 Jan 2018 15:31:43 +0100 Subject: [PATCH 06/19] lnwallet: don't ignore any returned error from PublishTransaction --- lnwallet/wallet.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 8adce2c3..8a1924ca 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -5,7 +5,6 @@ import ( "crypto/sha256" "fmt" "net" - "strings" "sync" "sync/atomic" @@ -1114,12 +1113,9 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Broadcast the finalized funding transaction to the network. if err := l.PublishTransaction(fundingTx); err != nil { - // TODO(roasbeef): need to make this into a concrete error - if !strings.Contains(err.Error(), "already have") { - msg.err <- err - msg.completeChan <- nil - return - } + msg.err <- err + msg.completeChan <- nil + return } msg.completeChan <- res.partialState From 2ae1b7dbbe5750acc46ed43e354d96cbe7977ccb Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 5 Feb 2018 17:26:35 -0500 Subject: [PATCH 07/19] contractcourt: remove TODO for checking double spends from PublishTx --- contractcourt/channel_arbitrator.go | 1 - contractcourt/contract_resolvers.go | 1 - 2 files changed, 2 deletions(-) diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index eb786d49..668309c1 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -449,7 +449,6 @@ func (c *ChannelArbitrator) stateStep(bestHeight uint32, bestHash *chainhash.Has // At this point, we'll now broadcast the commitment // transaction itself. if err := c.cfg.PublishTx(closeTx); err != nil { - // TODO(roasbeef): need to check for errors (duplicate) log.Errorf("ChannelArbitrator(%v): unable to broadcast "+ "close tx: %v", c.cfg.ChanPoint, err) return StateError, closeTx, err diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index 2fa4e2c7..5cc9ffa6 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -582,7 +582,6 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { // // TODO(roasbeef): after changing sighashes send to tx bundler if err := h.PublishTx(h.htlcResolution.SignedSuccessTx); err != nil { - // TODO(roasbeef): detect double spends return nil, err } From cabc07ea7d8e49e05edb9baa619ed767a2257c45 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 14 Feb 2018 12:28:19 +0100 Subject: [PATCH 08/19] breacharbiter: check ErrDoubleSpend from PublishTransaction --- breacharbiter.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/breacharbiter.go b/breacharbiter.go index 0093fb41..1771b81f 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -5,7 +5,6 @@ import ( "encoding/binary" "errors" "io" - "strings" "sync" "sync/atomic" @@ -580,7 +579,7 @@ secondLevelCheck: if err != nil { brarLog.Errorf("unable to broadcast "+ "justice tx: %v", err) - if strings.Contains(err.Error(), "already been spent") { + if err == lnwallet.ErrDoubleSpend { brarLog.Infof("Attempting to transfer HTLC revocations " + "to the second level") finalTx = nil From 39f9ae7cace142a2310a6a50f7ef7366f73b4198 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 17:28:14 -0800 Subject: [PATCH 09/19] build: update to version of golang fork with golang 1.10 bug fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we update the commit of my btcd fork as the saltiest commit includes a fix for a breaking change (or bug fix). After this commit, we’ll able to run the set of integration tests with golfing 1.10. --- glide.lock | 6 +++--- glide.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/glide.lock b/glide.lock index 9fc1c19b..5d13ca20 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 63c0706e71ebdd4664e6a452bf58035571a8799b24e517ea0d8aa2fb8dd94155 -updated: 2018-02-08T15:26:31.423935935-08:00 +hash: ccd5caad2b8fa46f76338964fe06f5d6a35b9ed36dd5df8da0748e3e3ab99e6a +updated: 2018-02-19T17:31:51.88595122-08:00 imports: - name: github.com/aead/chacha20 version: d31a916ded42d1640b9d89a26f8abd53cc96790c @@ -90,7 +90,7 @@ imports: subpackages: - internal/socket - name: github.com/roasbeef/btcd - version: 9978b939c33973be19b932fa7b936079bb7ba38d + version: e6807bc4dd5ddbb95b4ab163f6dd61e4ad79463a subpackages: - addrmgr - blockchain diff --git a/glide.yaml b/glide.yaml index 9367b6de..8e6462dd 100644 --- a/glide.yaml +++ b/glide.yaml @@ -15,7 +15,7 @@ import: - proto - package: github.com/howeyc/gopass - package: github.com/roasbeef/btcd - version: 9978b939c33973be19b932fa7b936079bb7ba38d + version: e6807bc4dd5ddbb95b4ab163f6dd61e4ad79463a subpackages: - blockchain - btcec From 7f0dd8f28fa438871c159600004d098451c0b31a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 17:30:51 -0800 Subject: [PATCH 10/19] build: update travis to build against golang 1.10+1.9.4 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6594bb60..65c9dc69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.8.5 - - 1.9.2 + - 1.9.4 + - "1.10" sudo: required install: - sudo add-apt-repository -y ppa:bitcoin/bitcoin -y From 236d53785d1b7dae39207edb6256a96f5b75e858 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 17:44:01 -0800 Subject: [PATCH 11/19] docs: update docs to recommend golang 1.10 --- docs/INSTALL.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 082dbe62..782868a5 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -7,14 +7,16 @@ * **Go:** `lnd` is written in Go. To install, run one of the following commands: - **Note**: The minimum version of Go supported is Go 1.8. + **Note**: The minimum version of Go supported is Go 1.8. We recommend that + users use the latest version of Go, which at the time of writing is + [`1.10`](https://blog.golang.org/go1.10). On Linux: ``` - sudo apt-get install golang-1.8-go + sudo apt-get install golang-1.10-go ``` - > Note that golang-1.8-go puts binaries in /usr/lib/go-1.8/bin. If you want them on your PATH, you need to make that change yourself. + > Note that golang-1.10-go puts binaries in /usr/lib/go-1.8/bin. If you want them on your PATH, you need to make that change yourself. On Mac OS X ``` From 4ed5ba0d26d006b36cf9c9e356d7f5d545be3be7 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Wed, 14 Feb 2018 18:23:01 +1300 Subject: [PATCH 12/19] multi: Remove peer_id from RPC commands --- cmd/lncli/commands.go | 15 +-------------- docker/README.md | 2 -- lnrpc/rpc.pb.go | 6 ------ lnrpc/rpc.proto | 8 -------- lnrpc/rpc.swagger.json | 19 +------------------ 5 files changed, 2 insertions(+), 48 deletions(-) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index 841601da..d1d860ce 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -388,15 +388,9 @@ var openChannelCommand = cli.Command{ of the funding output is returned. One can manually set the fee to be used for the funding transaction via either - the --conf_target or --sat_per_byte arguments. This is optional. - - NOTE: peer_id and node_key are mutually exclusive, only one should be used, not both.`, + the --conf_target or --sat_per_byte arguments. This is optional.`, ArgsUsage: "node-key local-amt push-amt", Flags: []cli.Flag{ - cli.IntFlag{ - Name: "peer_id", - Usage: "the relative id of the peer to open a channel with", - }, cli.StringFlag{ Name: "node_key", Usage: "the identity public key of the target node/peer " + @@ -463,11 +457,6 @@ func openChannel(ctx *cli.Context) error { return nil } - if ctx.IsSet("peer_id") && ctx.IsSet("node_key") { - return fmt.Errorf("both peer_id and lightning_id cannot be set " + - "at the same time, only one can be specified") - } - req := &lnrpc.OpenChannelRequest{ TargetConf: int32(ctx.Int64("conf_target")), SatPerByte: ctx.Int64("sat_per_byte"), @@ -475,8 +464,6 @@ func openChannel(ctx *cli.Context) error { } switch { - case ctx.IsSet("peer_id"): - req.TargetPeerId = int32(ctx.Int("peer_id")) case ctx.IsSet("node_key"): nodePubHex, err := hex.DecodeString(ctx.String("node_key")) if err != nil { diff --git a/docker/README.md b/docker/README.md index 983f0a55..dcdcffff 100644 --- a/docker/README.md +++ b/docker/README.md @@ -123,7 +123,6 @@ alice$ lncli listpeers "peers": [ { "pub_key": "0343bc80b914aebf8e50eb0b8e445fc79b9e6e8e5e018fa8c5f85c7d429c117b38", - "peer_id": 1, "address": "172.19.0.4:9735", "bytes_sent": "357", "bytes_recv": "357", @@ -141,7 +140,6 @@ bob$ lncli listpeers "peers": [ { "pub_key": "03d0cd35b761f789983f3cfe82c68170cd1c3266b39220c24f7dd72ef4be0883eb", - "peer_id": 1, "address": "172.19.0.3:51932", "bytes_sent": "357", "bytes_recv": "357", diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index 5078de13..822caa59 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -832,8 +832,6 @@ func (m *ConnectPeerRequest) GetPerm() bool { } type ConnectPeerResponse struct { - // / The id of the newly connected peer - PeerId int32 `protobuf:"varint,1,opt,name=peer_id" json:"peer_id,omitempty"` } func (m *ConnectPeerResponse) Reset() { *m = ConnectPeerResponse{} } @@ -1113,8 +1111,6 @@ func (m *ListChannelsResponse) GetChannels() []*ActiveChannel { type Peer struct { // / The identity pubkey of the peer PubKey string `protobuf:"bytes,1,opt,name=pub_key" json:"pub_key,omitempty"` - // / The peer's id from the local point of view - PeerId int32 `protobuf:"varint,2,opt,name=peer_id" json:"peer_id,omitempty"` // / Network address of the peer; eg `127.0.0.1:10011` Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` // / Bytes of data transmitted to this peer @@ -1635,8 +1631,6 @@ func (m *PendingUpdate) GetOutputIndex() uint32 { } type OpenChannelRequest struct { - // / The peer_id of the node to open a channel with - TargetPeerId int32 `protobuf:"varint,1,opt,name=target_peer_id" json:"target_peer_id,omitempty"` // / The pubkey of the node to open a channel with NodePubkey []byte `protobuf:"bytes,2,opt,name=node_pubkey,proto3" json:"node_pubkey,omitempty"` // / The hex encoded pubkey of the node to open a channel with diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index 512ffc00..52f46516 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -630,8 +630,6 @@ message ConnectPeerRequest { bool perm = 2; } message ConnectPeerResponse { - /// The id of the newly connected peer - int32 peer_id = 1 [json_name = "peer_id"]; } message DisconnectPeerRequest { @@ -738,9 +736,6 @@ message Peer { /// The identity pubkey of the peer string pub_key = 1 [json_name = "pub_key"]; - /// The peer's id from the local point of view - int32 peer_id = 2 [json_name = "peer_id"]; - /// Network address of the peer; eg `127.0.0.1:10011` string address = 3 [json_name = "address"]; @@ -858,9 +853,6 @@ message PendingUpdate { message OpenChannelRequest { - /// The peer_id of the node to open a channel with - int32 target_peer_id = 1 [json_name = "target_peer_id"]; - /// The pubkey of the node to open a channel with bytes node_pubkey = 2 [json_name = "node_pubkey"]; diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index fa262c3e..e904ff53 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -1127,14 +1127,7 @@ } }, "lnrpcConnectPeerResponse": { - "type": "object", - "properties": { - "peer_id": { - "type": "integer", - "format": "int32", - "title": "/ The id of the newly connected peer" - } - } + "type": "object" }, "lnrpcCreateWalletRequest": { "type": "object", @@ -1559,11 +1552,6 @@ "lnrpcOpenChannelRequest": { "type": "object", "properties": { - "target_peer_id": { - "type": "integer", - "format": "int32", - "title": "/ The peer_id of the node to open a channel with" - }, "node_pubkey": { "type": "string", "format": "byte", @@ -1697,11 +1685,6 @@ "type": "string", "title": "/ The identity pubkey of the peer" }, - "peer_id": { - "type": "integer", - "format": "int32", - "title": "/ The peer's id from the local point of view" - }, "address": { "type": "string", "title": "/ Network address of the peer; eg `127.0.0.1:10011`" From 915c4201b9a9d446cc5e53f06783a3d6a70f0bc4 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Wed, 14 Feb 2018 18:48:42 +1300 Subject: [PATCH 13/19] multi: remove internal peer_id usage --- fundingmanager_test.go | 1 - lnrpc/rpc.pb.go | 21 ---------------- peer.go | 10 +++----- pilot.go | 2 +- rpcserver.go | 56 +++++++++++++++++++----------------------- server.go | 21 +++++----------- 6 files changed, 36 insertions(+), 75 deletions(-) diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 972e0205..38e83f49 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -434,7 +434,6 @@ func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt, // Create a funding request and start the workflow. errChan := make(chan error, 1) initReq := &openChanReq{ - targetPeerID: int32(1), targetPubkey: bob.privKey.PubKey(), chainHash: *activeNetParams.GenesisHash, localFundingAmt: localFundingAmt, diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index 822caa59..15568d40 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -839,13 +839,6 @@ func (m *ConnectPeerResponse) String() string { return proto.CompactT func (*ConnectPeerResponse) ProtoMessage() {} func (*ConnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } -func (m *ConnectPeerResponse) GetPeerId() int32 { - if m != nil { - return m.PeerId - } - return 0 -} - type DisconnectPeerRequest struct { // / The pubkey of the node to disconnect from PubKey string `protobuf:"bytes,1,opt,name=pub_key" json:"pub_key,omitempty"` @@ -1139,13 +1132,6 @@ func (m *Peer) GetPubKey() string { return "" } -func (m *Peer) GetPeerId() int32 { - if m != nil { - return m.PeerId - } - return 0 -} - func (m *Peer) GetAddress() string { if m != nil { return m.Address @@ -1654,13 +1640,6 @@ func (m *OpenChannelRequest) String() string { return proto.CompactTe func (*OpenChannelRequest) ProtoMessage() {} func (*OpenChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } -func (m *OpenChannelRequest) GetTargetPeerId() int32 { - if m != nil { - return m.TargetPeerId - } - return 0 -} - func (m *OpenChannelRequest) GetNodePubkey() []byte { if m != nil { return m.NodePubkey diff --git a/peer.go b/peer.go index eb7d7e3a..4bfa73da 100644 --- a/peer.go +++ b/peer.go @@ -106,7 +106,6 @@ type peer struct { pubKeyBytes [33]byte inbound bool - id int32 // This mutex protects all the stats below it. sync.RWMutex @@ -179,7 +178,6 @@ func newPeer(conn net.Conn, connReq *connmgr.ConnReq, server *server, conn: conn, addr: addr, - id: atomic.AddInt32(&numNodes, 1), inbound: inbound, connReq: connReq, @@ -276,7 +274,7 @@ func (p *peer) Start() error { // registering them with the switch and launching the necessary // goroutines required to operate them. peerLog.Debugf("Loaded %v active channels from database with "+ - "peerID(%v)", len(activeChans), p.id) + "peerIDKey(%x)", len(activeChans), p.PubKey()) if err := p.loadActiveChannels(activeChans); err != nil { return fmt.Errorf("unable to load channels: %v", err) } @@ -310,7 +308,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error { p.activeChannels[chanID] = lnChan p.activeChanMtx.Unlock() - peerLog.Infof("peerID(%v) loading ChannelPoint(%v)", p.id, chanPoint) + peerLog.Infof("peerIDKey(%x) loading ChannelPoint(%v)", p.PubKey(), chanPoint) // Skip adding any permanently irreconcilable channels to the // htlcswitch. @@ -1247,7 +1245,7 @@ out: p.activeChanMtx.Unlock() peerLog.Infof("New channel active ChannelPoint(%v) "+ - "with peerId(%v)", chanPoint, p.id) + "with peerIDKey(%x)", chanPoint, p.PubKey()) // Next, we'll assemble a ChannelLink along with the // necessary items it needs to function. @@ -1304,7 +1302,7 @@ out: // local payments and also passively forward payments. if err := p.server.htlcSwitch.AddLink(link); err != nil { peerLog.Errorf("can't register new channel "+ - "link(%v) with peerId(%v)", chanPoint, p.id) + "link(%v) with peerIdKey(%x)", chanPoint, p.PubKey()) } close(newChanReq.done) diff --git a/pilot.go b/pilot.go index 82abe7ef..220caf97 100644 --- a/pilot.go +++ b/pilot.go @@ -93,7 +93,7 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey, // TODO(halseth): make configurable? minHtlc := lnwire.NewMSatFromSatoshis(1) - updateStream, errChan := c.server.OpenChannel(-1, target, amt, 0, + updateStream, errChan := c.server.OpenChannel(target, amt, 0, minHtlc, feePerWeight, false) select { diff --git a/rpcserver.go b/rpcserver.go index ac74c932..2b577c57 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -668,8 +668,8 @@ func (r *rpcServer) DisconnectPeer(ctx context.Context, func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, updateStream lnrpc.Lightning_OpenChannelServer) error { - rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ - "allocation(us=%v, them=%v)", in.TargetPeerId, + rpcsLog.Tracef("[openchannel] request to identityPub(%v) "+ + "allocation(us=%v, them=%v)", in.NodePubkeyString, in.LocalFundingAmount, in.PushSat) if !r.server.Started() { @@ -720,24 +720,21 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, // TODO(roasbeef): also return channel ID? - // If the node key is set, then we'll parse the raw bytes into a pubkey - // object so we can easily manipulate it. If this isn't set, then we - // expected the TargetPeerId to be set accordingly. - if len(in.NodePubkey) != 0 { - nodePubKey, err = btcec.ParsePubKey(in.NodePubkey, btcec.S256()) - if err != nil { - return err - } - - // Making a channel to ourselves wouldn't be of any use, so we - // explicitly disallow them. - if nodePubKey.IsEqual(r.server.identityPriv.PubKey()) { - return fmt.Errorf("cannot open channel to self") - } - - nodePubKeyBytes = nodePubKey.SerializeCompressed() + // Parse the raw bytes of the node key into a pubkey object so we + // can easily manipulate it. + nodePubKey, err = btcec.ParsePubKey(in.NodePubkey, btcec.S256()) + if err != nil { + return err } + // Making a channel to ourselves wouldn't be of any use, so we + // explicitly disallow them. + if nodePubKey.IsEqual(r.server.identityPriv.PubKey()) { + return fmt.Errorf("cannot open channel to self") + } + + nodePubKeyBytes = nodePubKey.SerializeCompressed() + // Based on the passed fee related parameters, we'll determine an // appropriate fee rate for the funding transaction. feePerByte, err := determineFeePerByte( @@ -754,7 +751,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, // 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( - in.TargetPeerId, nodePubKey, localFundingAmt, + nodePubKey, localFundingAmt, lnwire.NewMSatFromSatoshis(remoteInitialBalance), minHtlc, feePerByte, in.Private, ) @@ -764,9 +761,8 @@ out: for { select { case err := <-errChan: - rpcsLog.Errorf("unable to open channel to "+ - "identityPub(%x) nor peerID(%v): %v", - nodePubKeyBytes, in.TargetPeerId, err) + rpcsLog.Errorf("unable to open channel to identityPub(%x): %v", + nodePubKeyBytes, err) return err case fundingUpdate := <-updateChan: rpcsLog.Tracef("[openchannel] sending update: %v", @@ -802,8 +798,8 @@ out: } } - rpcsLog.Tracef("[openchannel] success peerid(%v), ChannelPoint(%v)", - in.TargetPeerId, outpoint) + rpcsLog.Tracef("[openchannel] success identityPub(%x), ChannelPoint(%v)", + nodePubKeyBytes, outpoint) return nil } @@ -814,8 +810,8 @@ out: func (r *rpcServer) OpenChannelSync(ctx context.Context, in *lnrpc.OpenChannelRequest) (*lnrpc.ChannelPoint, error) { - rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ - "allocation(us=%v, them=%v)", in.TargetPeerId, + rpcsLog.Tracef("[openchannel] request to identityPub(%v) "+ + "allocation(us=%v, them=%v)", in.NodePubkeyString, in.LocalFundingAmount, in.PushSat) // We don't allow new channels to be open while the server is still @@ -874,7 +870,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, int64(feePerByte)) updateChan, errChan := r.server.OpenChannel( - in.TargetPeerId, nodepubKey, localFundingAmt, + nodepubKey, localFundingAmt, lnwire.NewMSatFromSatoshis(remoteInitialBalance), minHtlc, feePerByte, in.Private, ) @@ -882,9 +878,8 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, select { // If an error occurs them immediately return the error to the client. case err := <-errChan: - rpcsLog.Errorf("unable to open channel to "+ - "identityPub(%x) nor peerID(%v): %v", - nodepubKey, in.TargetPeerId, err) + rpcsLog.Errorf("unable to open channel to identityPub(%x): %v", + nodepubKey, err) return nil, err // Otherwise, wait for the first channel update. The first update sent @@ -1237,7 +1232,6 @@ func (r *rpcServer) ListPeers(ctx context.Context, nodePub := serverPeer.addr.IdentityKey.SerializeCompressed() peer := &lnrpc.Peer{ PubKey: hex.EncodeToString(nodePub), - PeerId: serverPeer.id, Address: serverPeer.conn.RemoteAddr().String(), Inbound: !serverPeer.inbound, // Flip for display BytesRecv: atomic.LoadUint64(&serverPeer.bytesReceived), diff --git a/server.go b/server.go index edcff429..0f0547c9 100644 --- a/server.go +++ b/server.go @@ -75,7 +75,6 @@ type server struct { lightningID [32]byte mu sync.RWMutex - peersByID map[int32]*peer peersByPub map[string]*peer inboundPeers map[string]*peer @@ -173,7 +172,6 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl, persistentConnReqs: make(map[string][]*connmgr.ConnReq), ignorePeerTermination: make(map[*peer]struct{}), - peersByID: make(map[int32]*peer), peersByPub: make(map[string]*peer), inboundPeers: make(map[string]*peer), outboundPeers: make(map[string]*peer), @@ -1575,7 +1573,6 @@ func (s *server) addPeer(p *peer) { pubStr := string(p.addr.IdentityKey.SerializeCompressed()) - s.peersByID[p.id] = p s.peersByPub[pubStr] = p if p.inbound { @@ -1632,7 +1629,6 @@ func (s *server) removePeer(p *peer) { pubStr := string(p.addr.IdentityKey.SerializeCompressed()) - delete(s.peersByID, p.id) delete(s.peersByPub, pubStr) if p.inbound { @@ -1646,7 +1642,6 @@ func (s *server) removePeer(p *peer) { // initiation of a channel funding workflow to the peer with either the // specified relative peer ID, or a global lightning ID. type openChanReq struct { - targetPeerID int32 targetPubkey *btcec.PublicKey chainHash chainhash.Hash @@ -1778,10 +1773,10 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error { } // OpenChannel sends a request to the server to open a channel to the specified -// peer identified by ID with the passed channel funding parameters. +// peer identified by Public Key with the passed channel funding parameters. // // NOTE: This function is safe for concurrent access. -func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey, +func (s *server) OpenChannel(nodeKey *btcec.PublicKey, localAmt btcutil.Amount, pushAmt lnwire.MilliSatoshi, minHtlc lnwire.MilliSatoshi, fundingFeePerByte btcutil.Amount, @@ -1806,16 +1801,13 @@ func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey, // 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. s.mu.RLock() - if peer, ok := s.peersByID[peerID]; ok { - targetPeer = peer - } else if peer, ok := s.peersByPub[string(pubKeyBytes)]; ok { + if peer, ok := s.peersByPub[string(pubKeyBytes)]; ok { targetPeer = peer } s.mu.RUnlock() if targetPeer == nil { - errChan <- fmt.Errorf("unable to find peer nodeID(%x), "+ - "peerID(%v)", pubKeyBytes, peerID) + errChan <- fmt.Errorf("unable to find peer nodeID(%x)", pubKeyBytes) return updateChan, errChan } @@ -1839,7 +1831,6 @@ func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey, // instead of blocking on this request which is exported as a // synchronous request to the outside world. req := &openChanReq{ - targetPeerID: peerID, targetPubkey: nodeKey, chainHash: *activeNetParams.GenesisHash, localFundingAmt: localAmt, @@ -1865,8 +1856,8 @@ func (s *server) Peers() []*peer { s.mu.RLock() defer s.mu.RUnlock() - peers := make([]*peer, 0, len(s.peersByID)) - for _, peer := range s.peersByID { + peers := make([]*peer, 0, len(s.peersByPub)) + for _, peer := range s.peersByPub { peers = append(peers, peer) } From 4c4207943617442a9209d9d2600f27785f0fc1f5 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 20 Feb 2018 11:55:22 +1300 Subject: [PATCH 14/19] multi: ensure NodeKey is set in rpc/cli --- rpcserver.go | 5 +++++ server.go | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rpcserver.go b/rpcserver.go index 2b577c57..c76b1dda 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -720,6 +720,11 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, // TODO(roasbeef): also return channel ID? + // Ensure that the NodePubKey is set before attempting to use it + if len(in.NodePubkey) == 0 { + return fmt.Errorf("NodePubKey is not set") + } + // Parse the raw bytes of the node key into a pubkey object so we // can easily manipulate it. nodePubKey, err = btcec.ParsePubKey(in.NodePubkey, btcec.S256()) diff --git a/server.go b/server.go index 0f0547c9..00fdee28 100644 --- a/server.go +++ b/server.go @@ -1773,7 +1773,7 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error { } // OpenChannel sends a request to the server to open a channel to the specified -// peer identified by Public Key with the passed channel funding parameters. +// 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, From 2c2ed3c6a90b755af7741b13187ceccb363e26df Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Tue, 20 Feb 2018 12:01:23 +1300 Subject: [PATCH 15/19] multi: Unify use of NodeKey in log messages --- peer.go | 8 ++++---- rpcserver.go | 10 +++++----- server.go | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/peer.go b/peer.go index 4bfa73da..2a5de8bd 100644 --- a/peer.go +++ b/peer.go @@ -274,7 +274,7 @@ func (p *peer) Start() error { // registering them with the switch and launching the necessary // goroutines required to operate them. peerLog.Debugf("Loaded %v active channels from database with "+ - "peerIDKey(%x)", len(activeChans), p.PubKey()) + "NodeKey(%x)", len(activeChans), p.PubKey()) if err := p.loadActiveChannels(activeChans); err != nil { return fmt.Errorf("unable to load channels: %v", err) } @@ -308,7 +308,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error { p.activeChannels[chanID] = lnChan p.activeChanMtx.Unlock() - peerLog.Infof("peerIDKey(%x) loading ChannelPoint(%v)", p.PubKey(), chanPoint) + peerLog.Infof("NodeKey(%x) loading ChannelPoint(%v)", p.PubKey(), chanPoint) // Skip adding any permanently irreconcilable channels to the // htlcswitch. @@ -1245,7 +1245,7 @@ out: p.activeChanMtx.Unlock() peerLog.Infof("New channel active ChannelPoint(%v) "+ - "with peerIDKey(%x)", chanPoint, p.PubKey()) + "with NodeKey(%x)", chanPoint, p.PubKey()) // Next, we'll assemble a ChannelLink along with the // necessary items it needs to function. @@ -1302,7 +1302,7 @@ out: // local payments and also passively forward payments. if err := p.server.htlcSwitch.AddLink(link); err != nil { peerLog.Errorf("can't register new channel "+ - "link(%v) with peerIdKey(%x)", chanPoint, p.PubKey()) + "link(%v) with NodeKey(%x)", chanPoint, p.PubKey()) } close(newChanReq.done) diff --git a/rpcserver.go b/rpcserver.go index c76b1dda..2d72aa4a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -668,7 +668,7 @@ func (r *rpcServer) DisconnectPeer(ctx context.Context, func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, updateStream lnrpc.Lightning_OpenChannelServer) error { - rpcsLog.Tracef("[openchannel] request to identityPub(%v) "+ + rpcsLog.Tracef("[openchannel] request to NodeKey(%v) "+ "allocation(us=%v, them=%v)", in.NodePubkeyString, in.LocalFundingAmount, in.PushSat) @@ -766,7 +766,7 @@ out: for { select { case err := <-errChan: - rpcsLog.Errorf("unable to open channel to identityPub(%x): %v", + rpcsLog.Errorf("unable to open channel to NodeKey(%x): %v", nodePubKeyBytes, err) return err case fundingUpdate := <-updateChan: @@ -803,7 +803,7 @@ out: } } - rpcsLog.Tracef("[openchannel] success identityPub(%x), ChannelPoint(%v)", + rpcsLog.Tracef("[openchannel] success NodeKey(%x), ChannelPoint(%v)", nodePubKeyBytes, outpoint) return nil } @@ -815,7 +815,7 @@ out: func (r *rpcServer) OpenChannelSync(ctx context.Context, in *lnrpc.OpenChannelRequest) (*lnrpc.ChannelPoint, error) { - rpcsLog.Tracef("[openchannel] request to identityPub(%v) "+ + rpcsLog.Tracef("[openchannel] request to NodeKey(%v) "+ "allocation(us=%v, them=%v)", in.NodePubkeyString, in.LocalFundingAmount, in.PushSat) @@ -883,7 +883,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, select { // If an error occurs them immediately return the error to the client. case err := <-errChan: - rpcsLog.Errorf("unable to open channel to identityPub(%x): %v", + rpcsLog.Errorf("unable to open channel to NodeKey(%x): %v", nodepubKey, err) return nil, err diff --git a/server.go b/server.go index 00fdee28..176ab57e 100644 --- a/server.go +++ b/server.go @@ -1807,7 +1807,7 @@ func (s *server) OpenChannel(nodeKey *btcec.PublicKey, s.mu.RUnlock() if targetPeer == nil { - errChan <- fmt.Errorf("unable to find peer nodeID(%x)", pubKeyBytes) + errChan <- fmt.Errorf("unable to find peer NodeKey(%x)", pubKeyBytes) return updateChan, errChan } From 2a61ccec962a1328d1ca6587fe9feecc3bbc5a29 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 18:03:25 -0800 Subject: [PATCH 16/19] chainntnfs: fix new golang 1.10 vet/test warning --- chainntnfs/interface_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chainntnfs/interface_test.go b/chainntnfs/interface_test.go index e4521726..0fc02ac6 100644 --- a/chainntnfs/interface_test.go +++ b/chainntnfs/interface_test.go @@ -1116,7 +1116,7 @@ func testReorgConf(miner *rpctest.Harness, notifier chainntnfs.ChainNotifier, } if nodeHeight1 != nodeHeight2 { - t.Fatalf("expected both miners to be on the same height", + t.Fatalf("expected both miners to be on the same height: %v vs %v", nodeHeight1, nodeHeight2) } @@ -1190,7 +1190,7 @@ func testReorgConf(miner *rpctest.Harness, notifier chainntnfs.ChainNotifier, } if nodeHeight1 != nodeHeight2 { - t.Fatalf("expected both miners to be on the same height", + t.Fatalf("expected both miners to be on the same height: %v vs %v", nodeHeight1, nodeHeight2) } From 84551c616f4968a27e66806cab7d55006a48487c Mon Sep 17 00:00:00 2001 From: Jason Dufair Date: Sun, 10 Dec 2017 02:42:46 -0500 Subject: [PATCH 17/19] lnwallet/lnrpc: Expose sync status to gRPC interface This commit adds wallet_best_block_timestamp to the gRPC interface. This is done in order to allow clients to calculate progress while lnd syncs to the blockchain. wallet_best_block_timestamp is exposed via the GetInfo() rpc call. Additionally, IsSynced() returns the WalletBestBlockTimestamp as the second value in the tuple that is returned, providing additional detail when querying about the status of the sync. The BtcWallet interface has also been updated accordingly. This commit was created to support the issue to [Add progress bar for chain sync] (lightninglabs/lightning-app#10) in lightning-app --- fundingmanager.go | 2 +- lnd.go | 2 +- lnrpc/rpc.pb.go | 629 ++++++++++++++++---------------- lnrpc/rpc.proto | 3 + lnrpc/rpc.swagger.json | 7 +- lnwallet/btcwallet/btcwallet.go | 19 +- lnwallet/interface.go | 4 +- mock.go | 4 +- rpcserver.go | 27 +- 9 files changed, 361 insertions(+), 336 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index 50e47714..13a154cc 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -823,7 +823,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { // We'll also reject any requests to create channels until we're fully // synced to the network as we won't be able to properly validate the // confirmation of the funding transaction. - isSynced, err := f.cfg.Wallet.IsSynced() + isSynced, _, err := f.cfg.Wallet.IsSynced() if err != nil { fndgLog.Errorf("unable to query wallet: %v", err) return diff --git a/lnd.go b/lnd.go index 9f4e10f8..10d1e004 100644 --- a/lnd.go +++ b/lnd.go @@ -490,7 +490,7 @@ func lndMain() error { "start_height=%v", bestHeight) for { - synced, err := activeChainControl.wallet.IsSynced() + synced, _, err := activeChainControl.wallet.IsSynced() if err != nil { return err } diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index 15568d40..6b9551a3 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -1237,6 +1237,8 @@ type GetInfoResponse struct { Chains []string `protobuf:"bytes,11,rep,name=chains" json:"chains,omitempty"` // / The URIs of the current node. Uris []string `protobuf:"bytes,12,rep,name=uris" json:"uris,omitempty"` + // / Timestamp of the block best known to the wallet + BestHeaderTimestamp int64 `protobuf:"varint,13,opt,name=best_header_timestamp" json:"best_header_timestamp,omitempty"` } func (m *GetInfoResponse) Reset() { *m = GetInfoResponse{} } @@ -1321,6 +1323,13 @@ func (m *GetInfoResponse) GetUris() []string { return nil } +func (m *GetInfoResponse) GetBestHeaderTimestamp() int64 { + if m != nil { + return m.BestHeaderTimestamp + } + return 0 +} + type ConfirmationUpdate struct { BlockSha []byte `protobuf:"bytes,1,opt,name=block_sha,json=blockSha,proto3" json:"block_sha,omitempty"` BlockHeight int32 `protobuf:"varint,2,opt,name=block_height,json=blockHeight" json:"block_height,omitempty"` @@ -5713,314 +5722,314 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 4940 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x3b, 0x4d, 0x73, 0x1c, 0x49, - 0x56, 0xae, 0x56, 0xb7, 0xa4, 0x7e, 0xfd, 0x21, 0x29, 0x25, 0x4b, 0xed, 0x1e, 0xaf, 0xd7, 0x93, - 0x4c, 0xcc, 0x08, 0x33, 0x58, 0xb6, 0x96, 0x1d, 0x66, 0xc7, 0xc0, 0x86, 0x6d, 0x79, 0xac, 0xd9, - 0xd5, 0x78, 0xb4, 0x25, 0x7b, 0x07, 0x76, 0x02, 0x9a, 0x52, 0x77, 0xaa, 0x55, 0xeb, 0xea, 0xaa, - 0x9e, 0xaa, 0x6c, 0xc9, 0xbd, 0xc6, 0x11, 0x7c, 0x05, 0x11, 0x44, 0x40, 0x70, 0x80, 0x08, 0x62, - 0x89, 0x58, 0x0e, 0x70, 0x81, 0x03, 0xbf, 0x60, 0x23, 0xf8, 0x01, 0x1b, 0x41, 0x70, 0xd8, 0x13, - 0x01, 0x17, 0x02, 0x4e, 0x70, 0x24, 0xb8, 0x70, 0x22, 0x5e, 0x7e, 0x55, 0x66, 0x55, 0xc9, 0xf6, - 0xb0, 0xc0, 0xad, 0xf3, 0xe5, 0xab, 0x97, 0x99, 0x2f, 0x5f, 0xbe, 0xef, 0x86, 0x66, 0x3a, 0x1d, - 0xde, 0x9c, 0xa6, 0x09, 0x4f, 0x48, 0x23, 0x8a, 0xd3, 0xe9, 0xb0, 0x7f, 0x75, 0x9c, 0x24, 0xe3, - 0x88, 0xed, 0x04, 0xd3, 0x70, 0x27, 0x88, 0xe3, 0x84, 0x07, 0x3c, 0x4c, 0xe2, 0x4c, 0x22, 0xd1, - 0xdb, 0xb0, 0x7e, 0x3f, 0x65, 0x01, 0x67, 0x9f, 0x06, 0x51, 0xc4, 0xb8, 0xcf, 0x3e, 0x9f, 0xb1, - 0x8c, 0x93, 0x3e, 0x2c, 0x4f, 0x83, 0x2c, 0x3b, 0x4f, 0xd2, 0x51, 0xcf, 0xbb, 0xee, 0x6d, 0xb7, - 0x7d, 0x33, 0xa6, 0x9b, 0xb0, 0xe1, 0x7e, 0x92, 0x4d, 0x93, 0x38, 0x63, 0x48, 0xea, 0x49, 0x1c, - 0x25, 0xc3, 0xa7, 0x5f, 0x88, 0x94, 0xfb, 0x89, 0x22, 0xf5, 0xfd, 0x1a, 0xb4, 0x1e, 0xa7, 0x41, - 0x9c, 0x05, 0x43, 0xdc, 0x2c, 0xe9, 0xc1, 0x12, 0x7f, 0x36, 0x38, 0x0d, 0xb2, 0x53, 0x41, 0xa2, - 0xe9, 0xeb, 0x21, 0xd9, 0x84, 0xc5, 0x60, 0x92, 0xcc, 0x62, 0xde, 0xab, 0x5d, 0xf7, 0xb6, 0x17, - 0x7c, 0x35, 0x22, 0xef, 0xc2, 0x5a, 0x3c, 0x9b, 0x0c, 0x86, 0x49, 0x7c, 0x12, 0xa6, 0x13, 0x79, - 0xe4, 0xde, 0xc2, 0x75, 0x6f, 0xbb, 0xe1, 0x97, 0x27, 0xc8, 0x35, 0x80, 0x63, 0xdc, 0x86, 0x5c, - 0xa2, 0x2e, 0x96, 0xb0, 0x20, 0x84, 0x42, 0x5b, 0x8d, 0x58, 0x38, 0x3e, 0xe5, 0xbd, 0x86, 0x20, - 0xe4, 0xc0, 0x90, 0x06, 0x0f, 0x27, 0x6c, 0x90, 0xf1, 0x60, 0x32, 0xed, 0x2d, 0x8a, 0xdd, 0x58, - 0x10, 0x31, 0x9f, 0xf0, 0x20, 0x1a, 0x9c, 0x30, 0x96, 0xf5, 0x96, 0xd4, 0xbc, 0x81, 0x90, 0xb7, - 0xa1, 0x3b, 0x62, 0x19, 0x1f, 0x04, 0xa3, 0x51, 0xca, 0xb2, 0x8c, 0x65, 0xbd, 0xe5, 0xeb, 0x0b, - 0xdb, 0x4d, 0xbf, 0x00, 0xa5, 0x3d, 0xd8, 0x7c, 0xc8, 0xb8, 0xc5, 0x9d, 0x4c, 0x71, 0x9a, 0x1e, - 0x00, 0xb1, 0xc0, 0x7b, 0x8c, 0x07, 0x61, 0x94, 0x91, 0xf7, 0xa0, 0xcd, 0x2d, 0xe4, 0x9e, 0x77, - 0x7d, 0x61, 0xbb, 0xb5, 0x4b, 0x6e, 0x0a, 0xe9, 0xb8, 0x69, 0x7d, 0xe0, 0x3b, 0x78, 0xf4, 0xbf, - 0x3c, 0x68, 0x1d, 0xb1, 0x78, 0xa4, 0xef, 0x91, 0x40, 0x1d, 0x77, 0xa2, 0xee, 0x50, 0xfc, 0x26, - 0x5f, 0x86, 0x96, 0xd8, 0x5d, 0xc6, 0xd3, 0x30, 0x1e, 0x8b, 0x2b, 0x68, 0xfa, 0x80, 0xa0, 0x23, - 0x01, 0x21, 0xab, 0xb0, 0x10, 0x4c, 0xb8, 0x60, 0xfc, 0x82, 0x8f, 0x3f, 0xc9, 0x9b, 0xd0, 0x9e, - 0x06, 0xf3, 0x09, 0x8b, 0x79, 0xce, 0xec, 0xb6, 0xdf, 0x52, 0xb0, 0x7d, 0xe4, 0xf6, 0x4d, 0x58, - 0xb7, 0x51, 0x34, 0xf5, 0x86, 0xa0, 0xbe, 0x66, 0x61, 0xaa, 0x45, 0xde, 0x81, 0x15, 0x8d, 0x9f, - 0xca, 0xcd, 0x0a, 0xf6, 0x37, 0xfd, 0xae, 0x02, 0xeb, 0x23, 0x6c, 0xc3, 0xea, 0x49, 0x18, 0x07, - 0xd1, 0x60, 0x18, 0xf1, 0xb3, 0xc1, 0x88, 0x45, 0x3c, 0x10, 0x17, 0xd1, 0xf0, 0xbb, 0x02, 0x7e, - 0x3f, 0xe2, 0x67, 0x7b, 0x08, 0xa5, 0x7f, 0xe2, 0x41, 0x5b, 0x1e, 0x5e, 0x4a, 0x24, 0x79, 0x0b, - 0x3a, 0x7a, 0x0d, 0x96, 0xa6, 0x49, 0xaa, 0xe4, 0xd0, 0x05, 0x92, 0x1b, 0xb0, 0xaa, 0x01, 0xd3, - 0x94, 0x85, 0x93, 0x60, 0xcc, 0x04, 0x53, 0xda, 0x7e, 0x09, 0x4e, 0x76, 0x73, 0x8a, 0x69, 0x32, - 0xe3, 0x4c, 0x30, 0xa9, 0xb5, 0xdb, 0x56, 0x17, 0xe3, 0x23, 0xcc, 0x77, 0x51, 0xe8, 0x5f, 0x78, - 0xd0, 0xbe, 0x7f, 0x1a, 0xc4, 0x31, 0x8b, 0x0e, 0x93, 0x30, 0xe6, 0xe4, 0x16, 0x90, 0x93, 0x59, - 0x3c, 0x0a, 0xe3, 0xf1, 0x80, 0x3f, 0x0b, 0x47, 0x83, 0xe3, 0x39, 0x67, 0x99, 0xbc, 0xa2, 0xfd, - 0x4b, 0x7e, 0xc5, 0x1c, 0x79, 0x17, 0x56, 0x1d, 0x68, 0xc6, 0x53, 0x79, 0x6f, 0xfb, 0x97, 0xfc, - 0xd2, 0x0c, 0x0a, 0x7e, 0x32, 0xe3, 0xd3, 0x19, 0x1f, 0x84, 0xf1, 0x88, 0x3d, 0x13, 0x7b, 0xec, - 0xf8, 0x0e, 0xec, 0x5e, 0x17, 0xda, 0xf6, 0x77, 0xf4, 0x97, 0x60, 0xf5, 0x00, 0x5f, 0x44, 0x1c, - 0xc6, 0xe3, 0xbb, 0x52, 0x6c, 0xf1, 0x99, 0x4e, 0x67, 0xc7, 0x4f, 0xd9, 0x5c, 0xf1, 0x4d, 0x8d, - 0x50, 0xa8, 0x4e, 0x93, 0x8c, 0x2b, 0xc9, 0x11, 0xbf, 0xe9, 0xbf, 0x78, 0xb0, 0x82, 0xbc, 0xff, - 0x38, 0x88, 0xe7, 0xfa, 0xe6, 0x0e, 0xa0, 0x8d, 0xa4, 0x1e, 0x27, 0x77, 0xe5, 0x63, 0x97, 0x42, - 0xbc, 0xad, 0x78, 0x55, 0xc0, 0xbe, 0x69, 0xa3, 0x3e, 0x88, 0x79, 0x3a, 0xf7, 0x9d, 0xaf, 0x51, - 0x6c, 0x79, 0x90, 0x8e, 0x19, 0x17, 0x6a, 0x40, 0xa9, 0x05, 0x90, 0xa0, 0xfb, 0x49, 0x7c, 0x42, - 0xae, 0x43, 0x3b, 0x0b, 0xf8, 0x60, 0xca, 0x52, 0xc1, 0x35, 0x21, 0x7a, 0x0b, 0x3e, 0x64, 0x01, - 0x3f, 0x64, 0xe9, 0xbd, 0x39, 0x67, 0xfd, 0xaf, 0xc3, 0x5a, 0x69, 0x15, 0x94, 0xf6, 0xfc, 0x88, - 0xf8, 0x93, 0x6c, 0x40, 0xe3, 0x2c, 0x88, 0x66, 0x4c, 0x69, 0x27, 0x39, 0xf8, 0xa0, 0xf6, 0xbe, - 0x47, 0xdf, 0x86, 0xd5, 0x7c, 0xdb, 0x4a, 0xc8, 0x08, 0xd4, 0x91, 0x83, 0x8a, 0x80, 0xf8, 0x4d, - 0x7f, 0xcb, 0x93, 0x88, 0xf7, 0x93, 0xd0, 0xbc, 0x74, 0x44, 0x44, 0x85, 0xa0, 0x11, 0xf1, 0xf7, - 0x85, 0x9a, 0xf0, 0x27, 0x3f, 0x2c, 0x7d, 0x07, 0xd6, 0xac, 0x2d, 0xbc, 0x64, 0xb3, 0x7f, 0xee, - 0xc1, 0xda, 0x23, 0x76, 0xae, 0x6e, 0x5d, 0xef, 0xf6, 0x7d, 0xa8, 0xf3, 0xf9, 0x94, 0x09, 0xcc, - 0xee, 0xee, 0x5b, 0xea, 0xd2, 0x4a, 0x78, 0x37, 0xd5, 0xf0, 0xf1, 0x7c, 0xca, 0x7c, 0xf1, 0x05, - 0xfd, 0x04, 0x5a, 0x16, 0x90, 0x6c, 0xc1, 0xfa, 0xa7, 0x1f, 0x3d, 0x7e, 0xf4, 0xe0, 0xe8, 0x68, - 0x70, 0xf8, 0xe4, 0xde, 0x37, 0x1f, 0xfc, 0xca, 0x60, 0xff, 0xee, 0xd1, 0xfe, 0xea, 0x25, 0xb2, - 0x09, 0xe4, 0xd1, 0x83, 0xa3, 0xc7, 0x0f, 0xf6, 0x1c, 0xb8, 0x47, 0x56, 0xa0, 0x65, 0x03, 0x6a, - 0xb4, 0x0f, 0xbd, 0x47, 0xec, 0xfc, 0xd3, 0x90, 0xc7, 0x2c, 0xcb, 0xdc, 0xe5, 0xe9, 0x4d, 0x20, - 0xf6, 0x9e, 0xd4, 0x31, 0x7b, 0xb0, 0xa4, 0x74, 0xaf, 0x36, 0x3d, 0x6a, 0x48, 0xdf, 0x06, 0x72, - 0x14, 0x8e, 0xe3, 0x8f, 0x59, 0x96, 0x05, 0x63, 0xa6, 0x0f, 0xbb, 0x0a, 0x0b, 0x93, 0x6c, 0xac, - 0xb4, 0x24, 0xfe, 0xa4, 0x5f, 0x81, 0x75, 0x07, 0x4f, 0x11, 0xbe, 0x0a, 0xcd, 0x2c, 0x1c, 0xc7, - 0x01, 0x9f, 0xa5, 0x4c, 0x91, 0xce, 0x01, 0xf4, 0x43, 0xd8, 0xf8, 0x36, 0x4b, 0xc3, 0x93, 0xf9, - 0xab, 0xc8, 0xbb, 0x74, 0x6a, 0x45, 0x3a, 0x0f, 0xe0, 0x72, 0x81, 0x8e, 0x5a, 0x5e, 0x4a, 0xa6, - 0xba, 0xbf, 0x65, 0x5f, 0x0e, 0xac, 0x77, 0x5a, 0xb3, 0xdf, 0x29, 0x7d, 0x02, 0xe4, 0x7e, 0x12, - 0xc7, 0x6c, 0xc8, 0x0f, 0x19, 0x4b, 0xf5, 0x66, 0x7e, 0xc6, 0x12, 0xc3, 0xd6, 0xee, 0x96, 0xba, - 0xd8, 0xe2, 0xe3, 0x57, 0xf2, 0x49, 0xa0, 0x3e, 0x65, 0xe9, 0x44, 0x10, 0x5e, 0xf6, 0xc5, 0x6f, - 0xba, 0x03, 0xeb, 0x0e, 0xd9, 0x9c, 0xe7, 0x53, 0xc6, 0xd2, 0x81, 0xda, 0x5d, 0xc3, 0xd7, 0x43, - 0x7a, 0x1b, 0x2e, 0xef, 0x85, 0xd9, 0xb0, 0xbc, 0x15, 0xfc, 0x64, 0x76, 0x3c, 0xc8, 0x9f, 0x9f, - 0x1e, 0xa2, 0xbd, 0x2c, 0x7e, 0xa2, 0xbc, 0x8c, 0xdf, 0xf3, 0xa0, 0xbe, 0xff, 0xf8, 0xe0, 0x3e, - 0xba, 0x28, 0x61, 0x3c, 0x4c, 0x26, 0x68, 0x65, 0x24, 0x3b, 0xcc, 0xf8, 0xc2, 0x67, 0x75, 0x15, - 0x9a, 0xc2, 0x38, 0xa1, 0x0b, 0x20, 0x1e, 0x55, 0xdb, 0xcf, 0x01, 0xe8, 0x7e, 0xb0, 0x67, 0xd3, - 0x30, 0x15, 0xfe, 0x85, 0xf6, 0x1a, 0xea, 0x42, 0x79, 0x96, 0x27, 0xe8, 0xbf, 0xd5, 0xa1, 0x73, - 0x77, 0xc8, 0xc3, 0x33, 0xa6, 0x94, 0xbb, 0x58, 0x55, 0x00, 0xd4, 0x7e, 0xd4, 0x08, 0xcd, 0x50, - 0xca, 0x26, 0x09, 0x67, 0x03, 0xe7, 0x9a, 0x5c, 0x20, 0x62, 0x0d, 0x25, 0xa1, 0xc1, 0x14, 0xcd, - 0x84, 0xd8, 0x5f, 0xd3, 0x77, 0x81, 0xc8, 0x32, 0x04, 0x20, 0x97, 0x71, 0x67, 0x75, 0x5f, 0x0f, - 0x91, 0x1f, 0xc3, 0x60, 0x1a, 0x0c, 0x43, 0x3e, 0x57, 0xda, 0xc0, 0x8c, 0x91, 0x76, 0x94, 0x0c, - 0x83, 0x68, 0x70, 0x1c, 0x44, 0x41, 0x3c, 0x64, 0xca, 0xd3, 0x71, 0x81, 0xe8, 0xcc, 0xa8, 0x2d, - 0x69, 0x34, 0xe9, 0xf0, 0x14, 0xa0, 0xe8, 0x14, 0x0d, 0x93, 0xc9, 0x24, 0xe4, 0xe8, 0x03, 0xf5, - 0x96, 0xa5, 0xe6, 0xc9, 0x21, 0xe2, 0x24, 0x72, 0x74, 0x2e, 0x79, 0xd8, 0x94, 0xab, 0x39, 0x40, - 0xa4, 0x72, 0xc2, 0x98, 0xd0, 0x60, 0x4f, 0xcf, 0x7b, 0x20, 0xa9, 0xe4, 0x10, 0xbc, 0x8d, 0x59, - 0x9c, 0x31, 0xce, 0x23, 0x36, 0x32, 0x1b, 0x6a, 0x09, 0xb4, 0xf2, 0x04, 0xb9, 0x05, 0xeb, 0xd2, - 0x2d, 0xcb, 0x02, 0x9e, 0x64, 0xa7, 0x61, 0x36, 0xc8, 0x58, 0xcc, 0x7b, 0x6d, 0x81, 0x5f, 0x35, - 0x45, 0xde, 0x87, 0xad, 0x02, 0x38, 0x65, 0x43, 0x16, 0x9e, 0xb1, 0x51, 0xaf, 0x23, 0xbe, 0xba, - 0x68, 0x9a, 0x5c, 0x87, 0x16, 0x7a, 0xa3, 0xb3, 0xe9, 0x28, 0x40, 0xc3, 0xdd, 0x15, 0xf7, 0x60, - 0x83, 0xc8, 0x6d, 0xe8, 0x4c, 0x99, 0xb4, 0xae, 0xa7, 0x3c, 0x1a, 0x66, 0xbd, 0x15, 0x61, 0xfa, - 0x5a, 0xea, 0xb1, 0xa1, 0xfc, 0xfa, 0x2e, 0x06, 0x8a, 0xe6, 0x30, 0x13, 0xfe, 0x4d, 0x30, 0xef, - 0xad, 0x0a, 0xa1, 0xcb, 0x01, 0xf4, 0x32, 0xac, 0x1f, 0x84, 0x19, 0x57, 0x92, 0x66, 0xb4, 0xdf, - 0x3e, 0x6c, 0xb8, 0x60, 0xf5, 0x16, 0x6f, 0xc1, 0xb2, 0x12, 0x9b, 0xac, 0xd7, 0x12, 0x4b, 0x6f, - 0xa8, 0xa5, 0x1d, 0x89, 0xf5, 0x0d, 0x16, 0xfd, 0xdd, 0x1a, 0xd4, 0xf1, 0x9d, 0x5d, 0xfc, 0x26, - 0xed, 0x07, 0x5e, 0x73, 0x1e, 0xb8, 0xad, 0x6e, 0x17, 0x1c, 0x75, 0x2b, 0x7c, 0x74, 0xf4, 0x60, - 0xe4, 0x6d, 0x48, 0x89, 0xb5, 0x20, 0xf9, 0x7c, 0xca, 0x86, 0x67, 0x42, 0x6c, 0xcd, 0x3c, 0x42, - 0x50, 0xa8, 0xd1, 0xcc, 0x89, 0xaf, 0xa5, 0xcc, 0x9a, 0xb1, 0x9e, 0x13, 0x5f, 0x2e, 0xe5, 0x73, - 0xe2, 0xbb, 0x1e, 0x2c, 0x85, 0xf1, 0x71, 0x32, 0x8b, 0x47, 0x42, 0x3e, 0x97, 0x7d, 0x3d, 0x44, - 0x3e, 0x4f, 0x85, 0xd7, 0x13, 0x4e, 0x98, 0x12, 0xcc, 0x1c, 0x40, 0x09, 0xba, 0x41, 0x99, 0xd0, - 0x38, 0x86, 0xc9, 0xef, 0xc1, 0x9a, 0x05, 0x53, 0x1c, 0x7e, 0x13, 0x1a, 0x78, 0x7a, 0xed, 0x99, - 0xeb, 0x9b, 0x15, 0xaa, 0x4a, 0xce, 0xd0, 0x55, 0xe8, 0x3e, 0x64, 0xfc, 0xa3, 0xf8, 0x24, 0xd1, - 0x94, 0x7e, 0x7f, 0x01, 0x56, 0x0c, 0x48, 0x11, 0xda, 0x86, 0x95, 0x70, 0xc4, 0x62, 0x1e, 0xf2, - 0xf9, 0xc0, 0xf1, 0xb6, 0x8a, 0x60, 0x54, 0xfe, 0x41, 0x14, 0x06, 0x99, 0x52, 0x1f, 0x72, 0x40, - 0x76, 0x61, 0x03, 0x25, 0x4f, 0x0b, 0x93, 0xb9, 0x76, 0xe9, 0xf4, 0x55, 0xce, 0xe1, 0x63, 0x41, - 0xb8, 0x54, 0x4f, 0xf9, 0x27, 0x52, 0xd5, 0x55, 0x4d, 0x21, 0xd7, 0x24, 0x25, 0x3c, 0x72, 0x43, - 0x4a, 0xa7, 0x01, 0x94, 0x22, 0xad, 0x45, 0xe9, 0x70, 0x16, 0x23, 0x2d, 0x2b, 0x5a, 0x5b, 0x2e, - 0x45, 0x6b, 0xdb, 0xb0, 0x92, 0xcd, 0xe3, 0x21, 0x1b, 0x0d, 0x78, 0x82, 0xeb, 0x86, 0xb1, 0xb8, - 0x9d, 0x65, 0xbf, 0x08, 0x16, 0x71, 0x25, 0xcb, 0x78, 0xcc, 0xb8, 0xd0, 0x1a, 0xcb, 0xbe, 0x1e, - 0xa2, 0x02, 0x16, 0x28, 0x52, 0xe8, 0x9b, 0xbe, 0x1a, 0xa1, 0x15, 0x9b, 0xa5, 0x61, 0xd6, 0x6b, - 0x0b, 0xa8, 0xf8, 0x4d, 0xbf, 0x27, 0x8c, 0xa3, 0x09, 0x27, 0x9f, 0x88, 0x97, 0x4b, 0xde, 0x80, - 0xa6, 0xdc, 0x53, 0x76, 0x1a, 0xe8, 0xc0, 0x57, 0x00, 0x8e, 0x4e, 0x03, 0x8c, 0x82, 0x9c, 0x63, - 0xca, 0x57, 0xd0, 0x12, 0xb0, 0x7d, 0x79, 0xca, 0xb7, 0xa0, 0xab, 0x03, 0xd5, 0x6c, 0x10, 0xb1, - 0x13, 0xae, 0x9d, 0xef, 0x78, 0x36, 0xc1, 0xe5, 0xb2, 0x03, 0x76, 0xc2, 0xe9, 0x23, 0x58, 0x53, - 0x2f, 0xf0, 0x93, 0x29, 0xd3, 0x4b, 0x7f, 0xad, 0xa8, 0xff, 0xa5, 0x81, 0x5e, 0x57, 0x92, 0x65, - 0x47, 0x10, 0x05, 0xa3, 0x40, 0x7d, 0x20, 0x6a, 0xfa, 0x7e, 0x94, 0x64, 0x4c, 0x11, 0xa4, 0xd0, - 0x1e, 0x46, 0x49, 0xa6, 0x5d, 0x7c, 0x75, 0x1c, 0x07, 0x86, 0xbc, 0xcc, 0x66, 0xc3, 0x21, 0xbe, - 0x5c, 0x69, 0xe2, 0xf5, 0x90, 0xfe, 0x95, 0x07, 0xeb, 0x82, 0x9a, 0xd6, 0x15, 0xc6, 0x2f, 0x7c, - 0xfd, 0x6d, 0xb6, 0x87, 0x76, 0xd8, 0xb3, 0x01, 0x8d, 0x93, 0x24, 0x1d, 0x32, 0xb5, 0x92, 0x1c, - 0x7c, 0x71, 0x4f, 0xb7, 0x5e, 0xf2, 0x74, 0xff, 0xc1, 0x83, 0x35, 0xb1, 0xd5, 0x23, 0x1e, 0xf0, - 0x59, 0xa6, 0x8e, 0xff, 0x0b, 0xd0, 0xc1, 0xa3, 0x32, 0x2d, 0xfe, 0x6a, 0xa3, 0x1b, 0xe6, 0xa5, - 0x0a, 0xa8, 0x44, 0xde, 0xbf, 0xe4, 0xbb, 0xc8, 0xe4, 0xeb, 0xd0, 0xb6, 0xb3, 0x0d, 0x62, 0xcf, - 0xad, 0xdd, 0x2b, 0xfa, 0x94, 0x25, 0xc9, 0xd9, 0xbf, 0xe4, 0x3b, 0x1f, 0x90, 0x3b, 0x00, 0xc2, - 0x32, 0x0b, 0xb2, 0x2a, 0x4c, 0xbc, 0xe2, 0x32, 0xc9, 0xba, 0xac, 0xfd, 0x4b, 0xbe, 0x85, 0x7e, - 0x6f, 0x19, 0x16, 0xa5, 0x29, 0xa1, 0x0f, 0xa1, 0xe3, 0xec, 0xd4, 0xf1, 0xe0, 0xdb, 0xd2, 0x83, - 0x2f, 0x05, 0x7c, 0xb5, 0x72, 0xc0, 0x47, 0xff, 0xb9, 0x06, 0x04, 0xa5, 0xad, 0x70, 0x9d, 0x6f, - 0x43, 0x57, 0xb1, 0xdf, 0x75, 0xde, 0x0a, 0x50, 0x61, 0xf3, 0x92, 0x91, 0xe3, 0xc1, 0xb4, 0x7d, - 0x1b, 0x44, 0x6e, 0x02, 0xb1, 0x86, 0x3a, 0xfe, 0x97, 0xf6, 0xa0, 0x62, 0x06, 0x15, 0x97, 0x74, - 0x3f, 0x74, 0x1c, 0xaa, 0x3c, 0x36, 0x79, 0xbf, 0x95, 0x73, 0x22, 0x2d, 0x35, 0xcb, 0x4e, 0xd1, - 0x26, 0x6b, 0x1f, 0x47, 0x8f, 0x8b, 0x82, 0xb4, 0xf8, 0x4a, 0x41, 0x5a, 0x2a, 0x0a, 0x92, 0xb0, - 0x70, 0x69, 0x78, 0x16, 0x70, 0xa6, 0xad, 0x86, 0x1a, 0xa2, 0x4b, 0x33, 0x09, 0x63, 0x61, 0xaa, - 0x07, 0x13, 0x5c, 0x5d, 0xb9, 0x34, 0x0e, 0x90, 0xfe, 0xd8, 0x83, 0x55, 0xe4, 0xb1, 0x23, 0x87, - 0x1f, 0x80, 0x78, 0x06, 0xaf, 0x29, 0x86, 0x0e, 0xee, 0x4f, 0x2e, 0x85, 0xef, 0x43, 0x53, 0x10, - 0x4c, 0xa6, 0x2c, 0x56, 0x42, 0xd8, 0x73, 0x85, 0x30, 0xd7, 0x40, 0xfb, 0x97, 0xfc, 0x1c, 0xd9, - 0x12, 0xc1, 0xbf, 0xf7, 0xa0, 0xa5, 0xb6, 0xf9, 0x3f, 0x76, 0xbc, 0xfb, 0xb0, 0x8c, 0xd2, 0x68, - 0xf9, 0xb5, 0x66, 0x8c, 0x9a, 0x7f, 0x82, 0x71, 0x0f, 0x9a, 0x3a, 0xc7, 0xe9, 0x2e, 0x82, 0xd1, - 0x6e, 0x09, 0x65, 0x9b, 0x0d, 0x78, 0x18, 0x0d, 0xf4, 0xac, 0x4a, 0xec, 0x55, 0x4d, 0xa1, 0xce, - 0xc9, 0x78, 0x30, 0x66, 0xca, 0x24, 0xc9, 0x01, 0x46, 0x17, 0xea, 0x40, 0x45, 0x87, 0xea, 0x47, - 0x00, 0x5b, 0xa5, 0x29, 0xe3, 0x54, 0x29, 0x3f, 0x32, 0x0a, 0x27, 0xc7, 0x89, 0x71, 0x49, 0x3d, - 0xdb, 0xc5, 0x74, 0xa6, 0xc8, 0x18, 0x2e, 0x6b, 0xdb, 0x8b, 0x3c, 0xcd, 0x2d, 0x6d, 0x4d, 0x38, - 0x0d, 0xb7, 0x5d, 0x19, 0x28, 0x2e, 0xa8, 0xe1, 0xf6, 0xab, 0xad, 0xa6, 0x47, 0x4e, 0xa1, 0x67, - 0x8c, 0xbc, 0x52, 0xef, 0x96, 0x23, 0x80, 0x6b, 0xbd, 0xfb, 0x8a, 0xb5, 0x84, 0x2e, 0x1a, 0xe9, - 0x65, 0x2e, 0xa4, 0x46, 0xe6, 0x70, 0x4d, 0xcf, 0x09, 0xfd, 0x5d, 0x5e, 0xaf, 0xfe, 0x5a, 0x67, - 0xfb, 0x10, 0x3f, 0x76, 0x17, 0x7d, 0x05, 0xe1, 0xfe, 0x8f, 0x3c, 0xe8, 0xba, 0xe4, 0x50, 0x74, - 0x54, 0x6c, 0xa2, 0x15, 0x8c, 0x76, 0x9e, 0x0a, 0xe0, 0x72, 0x74, 0x55, 0xab, 0x8a, 0xae, 0xec, - 0x18, 0x6a, 0xe1, 0x55, 0x31, 0x54, 0xfd, 0xf5, 0x62, 0xa8, 0x46, 0x55, 0x0c, 0xd5, 0xff, 0x4f, - 0x0f, 0x48, 0xf9, 0x7e, 0xc9, 0x43, 0x19, 0xde, 0xc5, 0x2c, 0x52, 0x7a, 0xe2, 0x67, 0x5f, 0x4f, - 0x46, 0x34, 0x0f, 0xf5, 0xd7, 0x28, 0xac, 0xb6, 0x22, 0xb0, 0x5d, 0x96, 0x8e, 0x5f, 0x35, 0x55, - 0x88, 0xea, 0xea, 0xaf, 0x8e, 0xea, 0x1a, 0xaf, 0x8e, 0xea, 0x16, 0x8b, 0x51, 0x5d, 0xff, 0x37, - 0xa0, 0xe3, 0xdc, 0xfa, 0xff, 0xde, 0x89, 0x8b, 0xee, 0x8e, 0xbc, 0x60, 0x07, 0xd6, 0xff, 0xf7, - 0x1a, 0x90, 0xb2, 0xe4, 0xfd, 0xbf, 0xee, 0x41, 0xc8, 0x91, 0xa3, 0x40, 0x16, 0x94, 0x1c, 0x39, - 0xaa, 0xe3, 0xff, 0x52, 0x29, 0xbe, 0x0b, 0x6b, 0x29, 0x1b, 0x26, 0x67, 0x2c, 0xb5, 0x22, 0x6b, - 0x79, 0x55, 0xe5, 0x09, 0x74, 0xf8, 0xdc, 0x58, 0x76, 0xd9, 0xa9, 0x45, 0x58, 0x96, 0xa1, 0x10, - 0xd2, 0xd2, 0xaf, 0xc1, 0x86, 0x2c, 0x11, 0xdd, 0x93, 0xa4, 0xb4, 0xcf, 0xf1, 0x26, 0xb4, 0xcf, - 0x65, 0x32, 0x6f, 0x90, 0xc4, 0xd1, 0x5c, 0x19, 0x91, 0x96, 0x82, 0x7d, 0x12, 0x47, 0x73, 0xfa, - 0x03, 0x0f, 0x2e, 0x17, 0xbe, 0xcd, 0x73, 0xfa, 0x52, 0xd5, 0xba, 0xfa, 0xd7, 0x05, 0xe2, 0x11, - 0x95, 0x8c, 0x5b, 0x47, 0x94, 0x26, 0xa9, 0x3c, 0x81, 0x2c, 0x9c, 0xc5, 0x65, 0x7c, 0x79, 0x31, - 0x55, 0x53, 0x74, 0x0b, 0x2e, 0xab, 0xcb, 0x77, 0xcf, 0x46, 0x77, 0x61, 0xb3, 0x38, 0x91, 0xe7, - 0xc7, 0xdc, 0x2d, 0xeb, 0x21, 0xfd, 0x35, 0x20, 0xdf, 0x9a, 0xb1, 0x74, 0x2e, 0xaa, 0x07, 0x26, - 0x01, 0xbb, 0x55, 0x0c, 0xc4, 0x17, 0xa7, 0xb3, 0xe3, 0x6f, 0xb2, 0xb9, 0x2e, 0xcf, 0xd4, 0xf2, - 0xf2, 0xcc, 0x97, 0x00, 0x30, 0xea, 0x10, 0xe5, 0x06, 0x5d, 0x30, 0xc3, 0xf0, 0x4c, 0x12, 0xa4, - 0x77, 0x60, 0xdd, 0xa1, 0x6f, 0x38, 0xb9, 0xa8, 0xbe, 0x90, 0x31, 0xac, 0x5b, 0xc4, 0x50, 0x73, - 0xf4, 0x4f, 0x3d, 0x58, 0xd8, 0x4f, 0xa6, 0x76, 0xe2, 0xc9, 0x73, 0x13, 0x4f, 0x4a, 0xb5, 0x0e, - 0x8c, 0xe6, 0xac, 0x29, 0xc5, 0x60, 0x03, 0x51, 0x31, 0x06, 0x13, 0x8e, 0x51, 0xdc, 0x49, 0x92, - 0x9e, 0x07, 0xe9, 0x48, 0xb1, 0xb7, 0x00, 0xc5, 0xd3, 0xe5, 0xfa, 0x07, 0x7f, 0xa2, 0x4f, 0x21, - 0xb2, 0x6f, 0x73, 0x15, 0x78, 0xaa, 0x11, 0xfd, 0x23, 0x0f, 0x1a, 0x62, 0xaf, 0xf8, 0x58, 0xe4, - 0xf5, 0x8b, 0xca, 0x9d, 0x48, 0xee, 0x79, 0xf2, 0xb1, 0x14, 0xc0, 0x85, 0x7a, 0x5e, 0xad, 0x54, - 0xcf, 0xbb, 0x0a, 0x4d, 0x39, 0xca, 0x0b, 0x60, 0x39, 0x80, 0x5c, 0x83, 0xfa, 0x69, 0x32, 0xd5, - 0x26, 0x0e, 0x74, 0x36, 0x27, 0x99, 0xfa, 0x02, 0x4e, 0x6f, 0xc0, 0xca, 0xa3, 0x64, 0xc4, 0xac, - 0x90, 0xff, 0xc2, 0x5b, 0xa4, 0xbf, 0xe9, 0xc1, 0xb2, 0x46, 0x26, 0xdb, 0x50, 0x47, 0x4b, 0x55, - 0xf0, 0x0d, 0x4d, 0x4e, 0x16, 0xf1, 0x7c, 0x81, 0x81, 0x1a, 0x46, 0x04, 0x98, 0xb9, 0x27, 0xa1, - 0xc3, 0xcb, 0xdc, 0x46, 0xa3, 0x4f, 0x2f, 0xf6, 0x5c, 0xb0, 0x65, 0x05, 0x28, 0xfd, 0x6b, 0x0f, - 0x3a, 0xce, 0x1a, 0xe8, 0xe5, 0x47, 0x41, 0xc6, 0x55, 0x1e, 0x4b, 0x31, 0xd1, 0x06, 0xd9, 0xe9, - 0xa1, 0x9a, 0x9b, 0x1e, 0x32, 0xe9, 0x89, 0x05, 0x3b, 0x3d, 0x71, 0x0b, 0x9a, 0x79, 0x6d, 0xb4, - 0xee, 0x68, 0x0e, 0x5c, 0x51, 0x67, 0x9b, 0x73, 0x24, 0xa4, 0x33, 0x4c, 0xa2, 0x24, 0x55, 0xa5, - 0x43, 0x39, 0xa0, 0x77, 0xa0, 0x65, 0xe1, 0xe3, 0x36, 0x62, 0xc6, 0xcf, 0x93, 0xf4, 0xa9, 0xce, - 0x52, 0xa9, 0xa1, 0xa9, 0xb2, 0xd4, 0xf2, 0x2a, 0x0b, 0xfd, 0x1b, 0x0f, 0x3a, 0x28, 0x29, 0x61, - 0x3c, 0x3e, 0x4c, 0xa2, 0x70, 0x38, 0x17, 0x12, 0xa3, 0x85, 0x42, 0xd5, 0x14, 0xb5, 0xc4, 0xb8, - 0x60, 0x74, 0x09, 0xb4, 0x93, 0xaf, 0xe4, 0xc5, 0x8c, 0x51, 0xf2, 0xd1, 0xb4, 0x1d, 0x07, 0x19, - 0x93, 0x51, 0x81, 0x52, 0xe5, 0x0e, 0x10, 0xb5, 0x0b, 0x02, 0xd2, 0x80, 0xb3, 0xc1, 0x24, 0x8c, - 0xa2, 0x50, 0xe2, 0x4a, 0x09, 0xaf, 0x9a, 0xa2, 0x3f, 0xac, 0x41, 0x4b, 0x69, 0x91, 0x07, 0xa3, - 0xb1, 0x4c, 0xb8, 0x2a, 0x3f, 0xc5, 0x3c, 0x3f, 0x0b, 0xa2, 0xe7, 0x1d, 0xcf, 0xc6, 0x82, 0x14, - 0xaf, 0x75, 0xa1, 0x7c, 0xad, 0x57, 0xa1, 0x89, 0xe2, 0x75, 0x5b, 0xb8, 0x50, 0xb2, 0x94, 0x9e, - 0x03, 0xf4, 0xec, 0xae, 0x98, 0x6d, 0xe4, 0xb3, 0x02, 0xe0, 0x38, 0x4d, 0x8b, 0x05, 0xa7, 0xe9, - 0x7d, 0x68, 0x2b, 0x32, 0x82, 0xef, 0x22, 0xe6, 0xca, 0x05, 0xdc, 0xb9, 0x13, 0xdf, 0xc1, 0xd4, - 0x5f, 0xee, 0xea, 0x2f, 0x97, 0x5f, 0xf5, 0xa5, 0xc6, 0xa4, 0x97, 0x61, 0x5d, 0x31, 0xef, 0x61, - 0x1a, 0x4c, 0x4f, 0xb5, 0x66, 0x1e, 0x99, 0x2a, 0xac, 0x00, 0x93, 0x1b, 0xd0, 0xc0, 0xcf, 0xb4, - 0xf6, 0xab, 0x7e, 0x74, 0x12, 0x85, 0x6c, 0x43, 0x83, 0x8d, 0xc6, 0x4c, 0x3b, 0xee, 0xc4, 0x0d, - 0xa1, 0xf0, 0x8e, 0x7c, 0x89, 0x80, 0x2a, 0x00, 0xa1, 0x05, 0x15, 0xe0, 0x6a, 0xce, 0x45, 0x1c, - 0x7e, 0x34, 0xa2, 0x1b, 0x40, 0x1e, 0x49, 0xa9, 0xb5, 0x93, 0x84, 0xbf, 0xb3, 0x00, 0x2d, 0x0b, - 0x8c, 0xaf, 0x79, 0x8c, 0x1b, 0x1e, 0x8c, 0xc2, 0x60, 0xc2, 0x38, 0x4b, 0x95, 0xa4, 0x16, 0xa0, - 0x42, 0xc1, 0x9e, 0x8d, 0x07, 0xc9, 0x8c, 0x0f, 0x46, 0x6c, 0x9c, 0x32, 0x69, 0xef, 0x3c, 0xbf, - 0x00, 0x45, 0xbc, 0x49, 0xf0, 0xcc, 0xc6, 0x93, 0xf2, 0x50, 0x80, 0xea, 0x94, 0x9f, 0xe4, 0x51, - 0x3d, 0x4f, 0xf9, 0x49, 0x8e, 0x14, 0xf5, 0x50, 0xa3, 0x42, 0x0f, 0xbd, 0x07, 0x9b, 0x52, 0xe3, - 0xa8, 0xb7, 0x39, 0x28, 0x88, 0xc9, 0x05, 0xb3, 0xe4, 0x06, 0xac, 0xe2, 0x9e, 0xb5, 0x80, 0x67, - 0xe1, 0xf7, 0x64, 0xb0, 0xee, 0xf9, 0x25, 0x38, 0xe2, 0xe2, 0x73, 0x74, 0x70, 0x65, 0x45, 0xa2, - 0x04, 0x17, 0xb8, 0xc1, 0x33, 0x17, 0xb7, 0xa9, 0x70, 0x0b, 0x70, 0xda, 0x81, 0xd6, 0x11, 0x4f, - 0xa6, 0xfa, 0x52, 0xba, 0xd0, 0x96, 0x43, 0x55, 0x85, 0x7a, 0x03, 0xae, 0x08, 0x29, 0x7a, 0x9c, - 0x4c, 0x93, 0x28, 0x19, 0xcf, 0x8f, 0x66, 0xc7, 0xd9, 0x30, 0x0d, 0xa7, 0xe8, 0x50, 0xd3, 0xbf, - 0xf3, 0x60, 0xdd, 0x99, 0x55, 0x99, 0x80, 0x9f, 0x93, 0x22, 0x6d, 0x0a, 0x07, 0x52, 0xf0, 0xd6, - 0x2c, 0x75, 0x28, 0x11, 0x65, 0x5e, 0xe5, 0x89, 0xaa, 0x25, 0xdc, 0x85, 0x15, 0xbd, 0x33, 0xfd, - 0xa1, 0x94, 0xc2, 0x5e, 0x59, 0x0a, 0xd5, 0xf7, 0x5d, 0xf5, 0x81, 0x26, 0xf1, 0x8b, 0xd2, 0x2d, - 0x65, 0x23, 0x71, 0x46, 0x1d, 0x12, 0xf6, 0xf5, 0xf7, 0xb6, 0x2f, 0xac, 0x77, 0x30, 0x34, 0xc0, - 0x8c, 0xfe, 0x81, 0x07, 0x90, 0xef, 0x0e, 0x05, 0x23, 0x57, 0xe9, 0x9e, 0x48, 0xa9, 0x5a, 0xea, - 0xfb, 0x4d, 0x68, 0x9b, 0xc4, 0x75, 0x6e, 0x25, 0x5a, 0x1a, 0x86, 0x0e, 0xcc, 0x3b, 0xb0, 0x32, - 0x8e, 0x92, 0x63, 0x61, 0x73, 0x45, 0xc1, 0x33, 0x53, 0xb5, 0xb8, 0xae, 0x04, 0x7f, 0xa8, 0xa0, - 0xb9, 0x49, 0xa9, 0x5b, 0x26, 0x85, 0xfe, 0x61, 0xcd, 0xa4, 0x4f, 0xf3, 0x33, 0x5f, 0xf8, 0xca, - 0xc8, 0x6e, 0x49, 0x39, 0x5e, 0x90, 0xad, 0x14, 0xc9, 0x8f, 0xc3, 0x57, 0xc6, 0x81, 0x77, 0xa0, - 0x9b, 0x4a, 0xed, 0xa3, 0x55, 0x53, 0xfd, 0x25, 0xaa, 0xa9, 0x93, 0x3a, 0x76, 0xe7, 0xa7, 0x61, - 0x35, 0x18, 0x9d, 0xb1, 0x94, 0x87, 0x22, 0x20, 0x10, 0x46, 0x5f, 0x2a, 0xd4, 0x15, 0x0b, 0x2e, - 0x6c, 0xf1, 0x3b, 0xb0, 0xa2, 0xea, 0x9f, 0x06, 0x53, 0x35, 0xc8, 0xe4, 0x60, 0x44, 0xa4, 0x7f, - 0xa9, 0x33, 0xb5, 0xee, 0x1d, 0x5e, 0xcc, 0x11, 0xfb, 0x74, 0xb5, 0xc2, 0xe9, 0x7e, 0x4a, 0x65, - 0x4d, 0x47, 0x3a, 0xea, 0x50, 0xf9, 0x6b, 0x09, 0x54, 0x59, 0x6e, 0x97, 0xa5, 0xf5, 0xd7, 0x61, - 0x29, 0xfd, 0xc1, 0x02, 0x2c, 0x7d, 0x14, 0x9f, 0x25, 0xe1, 0x50, 0xe4, 0x30, 0x27, 0x6c, 0x92, - 0xe8, 0x2e, 0x04, 0xfc, 0x8d, 0x16, 0x5d, 0x14, 0xd8, 0xa6, 0x5c, 0x25, 0x17, 0xf5, 0x10, 0xad, - 0x5b, 0x9a, 0x77, 0xe6, 0x48, 0x49, 0xb1, 0x20, 0xe8, 0x1f, 0xa6, 0x76, 0x5b, 0x92, 0x1a, 0xe5, - 0x6d, 0x1c, 0x0d, 0xab, 0x8d, 0x43, 0x64, 0xbc, 0x65, 0xed, 0x50, 0xb0, 0x73, 0xd9, 0xd7, 0x43, - 0xe1, 0xc7, 0xa6, 0x4c, 0xc6, 0xc4, 0xc2, 0x4e, 0x2e, 0x29, 0x3f, 0xd6, 0x06, 0xa2, 0x2d, 0x95, - 0x1f, 0x48, 0x1c, 0xa9, 0x6b, 0x6c, 0x10, 0xfa, 0x16, 0xc5, 0xce, 0xa6, 0xa6, 0xbc, 0xe2, 0x02, - 0x18, 0x15, 0xd2, 0x88, 0x19, 0xbd, 0x21, 0xcf, 0x00, 0xb2, 0xf3, 0xa8, 0x08, 0xb7, 0xbc, 0x60, - 0x59, 0x03, 0x55, 0x23, 0xe1, 0x83, 0x04, 0x51, 0x74, 0x1c, 0x0c, 0x9f, 0x8a, 0x7e, 0x33, 0x51, - 0xf2, 0x6c, 0xfa, 0x2e, 0x10, 0x77, 0x2d, 0xda, 0xa7, 0x14, 0x89, 0x8e, 0x2c, 0x59, 0x5a, 0x20, - 0xfa, 0x6d, 0x20, 0x77, 0x47, 0x23, 0x75, 0x43, 0x26, 0x46, 0xc8, 0x79, 0xeb, 0x39, 0xbc, 0xad, - 0x38, 0x63, 0xad, 0xf2, 0x8c, 0xf4, 0x01, 0xb4, 0x0e, 0xad, 0x36, 0x31, 0x71, 0x99, 0xba, 0x41, - 0x4c, 0x09, 0x80, 0x05, 0xb1, 0x16, 0xac, 0xd9, 0x0b, 0xd2, 0x9f, 0x07, 0x72, 0x10, 0x66, 0xdc, - 0xec, 0xcf, 0x44, 0x92, 0x26, 0x21, 0x66, 0x45, 0x92, 0x0a, 0x26, 0x22, 0xc9, 0xbb, 0xb2, 0x72, - 0x5a, 0x3c, 0xd8, 0x0d, 0x58, 0x0e, 0x25, 0x48, 0xeb, 0xe1, 0xae, 0x12, 0x60, 0x8d, 0x69, 0xe6, - 0xd1, 0xa1, 0x50, 0x40, 0x47, 0xcd, 0xff, 0xd0, 0x83, 0x25, 0x75, 0x34, 0x34, 0x87, 0x4e, 0x83, - 0x9c, 0x3c, 0x98, 0x03, 0xab, 0x6e, 0x2b, 0x2a, 0x4b, 0xdd, 0x42, 0x95, 0xd4, 0x11, 0xa8, 0x4f, - 0x03, 0x7e, 0x2a, 0x3c, 0xe8, 0xa6, 0x2f, 0x7e, 0xeb, 0x48, 0xa9, 0x91, 0x47, 0x4a, 0x55, 0x9d, - 0x6c, 0x52, 0x67, 0x94, 0xe0, 0xba, 0xa2, 0xac, 0x0e, 0x60, 0x12, 0xa0, 0xf7, 0x64, 0x45, 0x39, - 0x07, 0xe7, 0xfc, 0x52, 0x24, 0x8a, 0xfc, 0x52, 0xa8, 0xbe, 0x99, 0xa7, 0x7d, 0xe8, 0xed, 0xb1, - 0x88, 0x71, 0x76, 0x37, 0x8a, 0x8a, 0xf4, 0xdf, 0x80, 0x2b, 0x15, 0x73, 0xca, 0xaa, 0x7e, 0x08, - 0x6b, 0x7b, 0xec, 0x78, 0x36, 0x3e, 0x60, 0x67, 0x79, 0x85, 0x82, 0x40, 0x3d, 0x3b, 0x4d, 0xce, - 0xd5, 0xdd, 0x8a, 0xdf, 0x18, 0xf0, 0x46, 0x88, 0x33, 0xc8, 0xa6, 0x6c, 0xa8, 0xfb, 0x67, 0x04, - 0xe4, 0x68, 0xca, 0x86, 0xf4, 0x3d, 0x20, 0x36, 0x1d, 0x75, 0x04, 0x7c, 0xb9, 0xb3, 0xe3, 0x41, - 0x36, 0xcf, 0x38, 0x9b, 0xe8, 0xc6, 0x20, 0x1b, 0x44, 0xdf, 0x81, 0xf6, 0x61, 0x30, 0xf7, 0xd9, - 0xe7, 0xaa, 0x47, 0x11, 0x83, 0xb7, 0x60, 0x8e, 0xa2, 0x6c, 0x82, 0x37, 0x31, 0x4d, 0xff, 0xb6, - 0x06, 0x8b, 0x12, 0x13, 0xa9, 0x8e, 0x58, 0xc6, 0xc3, 0x58, 0x66, 0xe8, 0x15, 0x55, 0x0b, 0x54, - 0x92, 0x8d, 0x5a, 0x85, 0x6c, 0x28, 0x77, 0x4a, 0xf7, 0x1a, 0x28, 0x21, 0x70, 0x60, 0x22, 0x36, - 0x0d, 0x27, 0x4c, 0xb6, 0xaa, 0xd6, 0x55, 0x6c, 0xaa, 0x01, 0x85, 0x28, 0x39, 0xd7, 0x0f, 0x72, - 0x7f, 0x5a, 0x68, 0x95, 0x38, 0xd8, 0xa0, 0x4a, 0x2d, 0xb4, 0x24, 0xa5, 0xa6, 0xa4, 0x85, 0x4a, - 0xda, 0x66, 0xf9, 0x35, 0xb4, 0x8d, 0xf4, 0xb1, 0x1c, 0x6d, 0x43, 0x60, 0xf5, 0x43, 0xc6, 0x7c, - 0x36, 0x4d, 0x52, 0xdd, 0xe8, 0x49, 0xbf, 0xef, 0xc1, 0xaa, 0xb2, 0x1e, 0x66, 0x8e, 0xbc, 0xe9, - 0x98, 0x1a, 0xaf, 0x2a, 0x69, 0xfb, 0x16, 0x74, 0x44, 0xb0, 0x85, 0x91, 0x94, 0x88, 0xac, 0x54, - 0xfe, 0xc1, 0x01, 0xe2, 0x9e, 0x74, 0x1a, 0x72, 0x12, 0x46, 0x8a, 0xc1, 0x36, 0x08, 0xcd, 0xa2, - 0x0e, 0xc6, 0x04, 0x7b, 0x3d, 0xdf, 0x8c, 0xe9, 0x21, 0xac, 0x59, 0xfb, 0x55, 0x02, 0x75, 0x07, - 0x74, 0x81, 0x53, 0xa6, 0x13, 0xe4, 0xbb, 0xd8, 0x72, 0x0d, 0x61, 0xfe, 0x99, 0x83, 0x4c, 0xff, - 0xd1, 0x83, 0x75, 0xe9, 0x14, 0x28, 0x97, 0xcb, 0xf4, 0x44, 0x2d, 0x4a, 0x2f, 0x48, 0x0a, 0xfc, - 0xfe, 0x25, 0x5f, 0x8d, 0xc9, 0x57, 0x5f, 0xd3, 0x91, 0x31, 0xb5, 0xc4, 0x0b, 0xd8, 0xb3, 0x50, - 0xc5, 0x9e, 0x97, 0x1c, 0xbe, 0x2a, 0x58, 0x6e, 0x54, 0x06, 0xcb, 0xf7, 0x96, 0xa0, 0x91, 0x0d, - 0x93, 0x29, 0xa3, 0x9b, 0xb0, 0xe1, 0x1e, 0x4e, 0xb2, 0x6c, 0xf7, 0x9f, 0x3c, 0xe8, 0xca, 0xbc, - 0x9e, 0x6c, 0x21, 0x67, 0x29, 0xc1, 0xb8, 0xcc, 0xea, 0x4c, 0x27, 0xc6, 0x2d, 0x2d, 0x77, 0xb8, - 0xf7, 0xdf, 0xa8, 0x9c, 0xd3, 0x3e, 0xf9, 0x6f, 0xff, 0xf8, 0x5f, 0xff, 0xb8, 0x76, 0x99, 0xae, - 0xee, 0x9c, 0xdd, 0xde, 0x11, 0xea, 0x93, 0x9d, 0x0b, 0x8c, 0x0f, 0xbc, 0x1b, 0xb8, 0x8a, 0xdd, - 0xb4, 0x6e, 0x56, 0xa9, 0x68, 0x7e, 0x37, 0xab, 0x54, 0x76, 0xb9, 0x3b, 0xab, 0xcc, 0x04, 0x86, - 0x59, 0x65, 0xf7, 0x3f, 0xfa, 0xd0, 0x34, 0x01, 0x24, 0xf9, 0x2e, 0x74, 0x9c, 0x1c, 0x26, 0xd1, - 0x84, 0xab, 0xb2, 0xa2, 0xfd, 0xab, 0xd5, 0x93, 0x6a, 0xd9, 0x6b, 0x62, 0xd9, 0x1e, 0xd9, 0xc4, - 0x65, 0x55, 0xe2, 0x70, 0x47, 0x24, 0x77, 0x65, 0x53, 0xc4, 0x53, 0xe8, 0xba, 0x79, 0x47, 0x72, - 0xd5, 0x15, 0x8d, 0xc2, 0x6a, 0x5f, 0xba, 0x60, 0x56, 0x2d, 0x77, 0x55, 0x2c, 0xb7, 0x49, 0x36, - 0xec, 0xe5, 0x4c, 0x60, 0xc7, 0x44, 0x1b, 0x8b, 0xdd, 0xcd, 0x4e, 0x34, 0xbd, 0xea, 0x2e, 0xf7, - 0xfe, 0x95, 0x72, 0xe7, 0xba, 0x6a, 0x75, 0xa7, 0x3d, 0xb1, 0x14, 0x21, 0x82, 0xa1, 0x76, 0x33, - 0x3b, 0xf9, 0x0c, 0x9a, 0xa6, 0x83, 0x95, 0x6c, 0x59, 0x6d, 0xc3, 0x76, 0x5b, 0x6d, 0xbf, 0x57, - 0x9e, 0xa8, 0xba, 0x2a, 0x9b, 0x32, 0x0a, 0xc4, 0x01, 0x5c, 0x56, 0xd6, 0xfc, 0x98, 0x7d, 0x91, - 0x93, 0x54, 0xf4, 0xe0, 0xdf, 0xf2, 0xc8, 0x1d, 0x58, 0xd6, 0x8d, 0xc1, 0x64, 0xb3, 0xba, 0xc1, - 0xb9, 0xbf, 0x55, 0x82, 0x2b, 0x3d, 0x72, 0x17, 0x20, 0xef, 0x61, 0x25, 0xbd, 0x8b, 0x5a, 0x6d, - 0x0d, 0x13, 0x2b, 0x1a, 0x5e, 0xc7, 0xa2, 0x85, 0xd7, 0x6d, 0x91, 0x25, 0x5f, 0xce, 0xf1, 0x2b, - 0x9b, 0x67, 0x5f, 0x42, 0x90, 0x6e, 0x0a, 0xde, 0xad, 0x92, 0x2e, 0xf2, 0x2e, 0x66, 0xe7, 0xba, - 0xa1, 0x6b, 0x0f, 0x5a, 0x56, 0x5f, 0x2c, 0xd1, 0x14, 0xca, 0x3d, 0xb5, 0xfd, 0x7e, 0xd5, 0x94, - 0xda, 0xee, 0x37, 0xa0, 0xe3, 0x34, 0xb8, 0x9a, 0x97, 0x51, 0xd5, 0x3e, 0x6b, 0x5e, 0x46, 0x75, - 0x4f, 0xec, 0x77, 0xa0, 0x65, 0xb5, 0xa3, 0x12, 0xab, 0x38, 0x5e, 0x68, 0x37, 0x35, 0x3b, 0xaa, - 0xe8, 0x5e, 0xa5, 0x1b, 0xe2, 0xbc, 0x5d, 0xda, 0xc4, 0xf3, 0x8a, 0xae, 0x26, 0x14, 0x92, 0xef, - 0x42, 0xd7, 0x6d, 0x43, 0x35, 0xaf, 0xaa, 0xb2, 0xa1, 0xd5, 0xbc, 0xaa, 0x0b, 0x7a, 0x57, 0x95, - 0x40, 0xde, 0x58, 0x37, 0x8b, 0xec, 0x3c, 0x57, 0xe9, 0xd3, 0x17, 0xe4, 0x5b, 0xa8, 0x3a, 0x54, - 0x9b, 0x19, 0xc9, 0xdb, 0x72, 0xdd, 0x66, 0x34, 0x23, 0xed, 0xa5, 0x8e, 0x34, 0xba, 0x26, 0x88, - 0xb7, 0x48, 0x7e, 0x02, 0xf2, 0x31, 0x2c, 0xa9, 0x76, 0x33, 0x72, 0x39, 0x97, 0x6a, 0x2b, 0xd9, - 0xd4, 0xdf, 0x2c, 0x82, 0x15, 0xb1, 0x75, 0x41, 0xac, 0x43, 0x5a, 0x48, 0x6c, 0xcc, 0x78, 0x88, - 0x34, 0x62, 0x58, 0x29, 0x14, 0xc4, 0xcc, 0x63, 0xa9, 0x2e, 0xa7, 0xf7, 0xaf, 0xbd, 0xbc, 0x8e, - 0xe6, 0xaa, 0x19, 0xad, 0x5e, 0x76, 0x74, 0xf7, 0xc3, 0xaf, 0x42, 0xdb, 0xee, 0x6e, 0x34, 0x3a, - 0xbb, 0xa2, 0x13, 0xd2, 0xe8, 0xec, 0xaa, 0x76, 0x48, 0x7d, 0xb9, 0xa4, 0x6d, 0x2f, 0x43, 0xbe, - 0x03, 0x2b, 0x56, 0xe9, 0xf5, 0x68, 0x1e, 0x0f, 0x8d, 0xf0, 0x94, 0x1b, 0x65, 0xfa, 0x55, 0x96, - 0x96, 0x6e, 0x09, 0xc2, 0x6b, 0xd4, 0x21, 0x8c, 0x82, 0x73, 0x1f, 0x5a, 0x76, 0x59, 0xf7, 0x25, - 0x74, 0xb7, 0xac, 0x29, 0xbb, 0x6f, 0xe4, 0x96, 0x47, 0xfe, 0xcc, 0x83, 0xb6, 0xdd, 0x82, 0x45, - 0x9c, 0x8c, 0x4d, 0x81, 0x4e, 0xcf, 0x9e, 0xb3, 0x09, 0x51, 0x5f, 0x6c, 0xf2, 0xe0, 0xc6, 0x37, - 0x1c, 0x26, 0x3f, 0x77, 0x9c, 0xa8, 0x9b, 0xc5, 0x3f, 0x91, 0xbc, 0x28, 0x22, 0xd8, 0xcd, 0x44, - 0x2f, 0x6e, 0x79, 0xe4, 0x03, 0xf9, 0x47, 0x23, 0x1d, 0x00, 0x11, 0x4b, 0xb9, 0x15, 0x59, 0x66, - 0xff, 0x27, 0x67, 0xdb, 0xbb, 0xe5, 0x91, 0x5f, 0x97, 0xff, 0x15, 0x51, 0xdf, 0x0a, 0xce, 0xbf, - 0xee, 0xf7, 0xf4, 0x2d, 0x71, 0x9a, 0x6b, 0xf4, 0x8a, 0x73, 0x9a, 0xa2, 0x76, 0x3f, 0x04, 0xc8, - 0xa3, 0x59, 0x52, 0x08, 0xed, 0x8c, 0xde, 0x2b, 0x07, 0xbc, 0xee, 0x8d, 0xea, 0x08, 0x10, 0x29, - 0x7e, 0x26, 0x85, 0x51, 0xe1, 0x67, 0xe6, 0x4a, 0xcb, 0x51, 0x69, 0xbf, 0x5f, 0x35, 0x55, 0x25, - 0x8a, 0x9a, 0x3e, 0x79, 0x02, 0x9d, 0x83, 0x24, 0x79, 0x3a, 0x9b, 0x9a, 0x0c, 0x89, 0x1b, 0x5c, - 0x61, 0xe8, 0xdc, 0x2f, 0x9c, 0x82, 0x5e, 0x17, 0xa4, 0xfa, 0xa4, 0x67, 0x91, 0xda, 0x79, 0x9e, - 0xc7, 0xd2, 0x2f, 0x48, 0x00, 0x6b, 0xc6, 0xc6, 0x99, 0x8d, 0xf7, 0x5d, 0x32, 0x76, 0x48, 0x5b, - 0x5a, 0xc2, 0xf1, 0x3a, 0xf4, 0x6e, 0x77, 0x32, 0x4d, 0xf3, 0x96, 0x47, 0x0e, 0xa1, 0xbd, 0xc7, - 0x86, 0xc9, 0x88, 0xa9, 0x70, 0x68, 0x3d, 0xdf, 0xb8, 0x89, 0xa3, 0xfa, 0x1d, 0x07, 0xe8, 0xbe, - 0xfa, 0x69, 0x30, 0x4f, 0xd9, 0xe7, 0x3b, 0xcf, 0x55, 0xa0, 0xf5, 0x42, 0xbf, 0x7a, 0x1d, 0x1c, - 0x3a, 0xaf, 0xbe, 0x10, 0x4d, 0x3a, 0xaf, 0xbe, 0x14, 0x4d, 0x3a, 0xac, 0xd6, 0xc1, 0x29, 0x89, - 0x30, 0xc6, 0x2c, 0x04, 0xa0, 0xc6, 0x52, 0x5e, 0x14, 0xb6, 0xf6, 0xaf, 0x5f, 0x8c, 0xe0, 0xae, - 0x76, 0xc3, 0x5d, 0xed, 0x08, 0x3a, 0x7b, 0x4c, 0x32, 0x4b, 0x56, 0x1d, 0xfa, 0xae, 0x1a, 0xb1, - 0x2b, 0x14, 0x45, 0x15, 0x23, 0xe6, 0x5c, 0xb5, 0x2e, 0x52, 0xfe, 0xe4, 0x33, 0x68, 0x3d, 0x64, - 0x5c, 0x97, 0x19, 0x8c, 0xbf, 0x51, 0xa8, 0x3b, 0xf4, 0x2b, 0xaa, 0x14, 0xae, 0xcc, 0x08, 0x6a, - 0x3b, 0x6c, 0x34, 0x66, 0xf2, 0xb1, 0x0f, 0xc2, 0xd1, 0x0b, 0xf2, 0xcb, 0x82, 0xb8, 0xa9, 0x4c, - 0x6e, 0x5a, 0xd9, 0x69, 0x9b, 0xf8, 0x4a, 0x01, 0x5e, 0x45, 0x39, 0x4e, 0x46, 0xcc, 0x32, 0x70, - 0x31, 0xb4, 0xac, 0x32, 0xb4, 0x79, 0x40, 0xe5, 0xd2, 0xb7, 0x79, 0x40, 0x15, 0x55, 0x6b, 0xba, - 0x2d, 0xd6, 0xa1, 0xe4, 0x7a, 0xbe, 0x8e, 0xac, 0x54, 0xe7, 0x2b, 0xed, 0x3c, 0x0f, 0x26, 0xfc, - 0x05, 0xf9, 0x54, 0xf4, 0x5f, 0xdb, 0xa5, 0x94, 0xdc, 0xdf, 0x29, 0x56, 0x5d, 0x0c, 0xb3, 0xac, - 0x29, 0xd7, 0x07, 0x92, 0x4b, 0x09, 0x3b, 0xf8, 0x55, 0x80, 0x23, 0x9e, 0x4c, 0xf7, 0x02, 0x36, - 0x49, 0xe2, 0x5c, 0x73, 0xe5, 0xe5, 0x82, 0x5c, 0x73, 0x59, 0x35, 0x03, 0xf2, 0xa9, 0xe5, 0x71, - 0x3a, 0x95, 0x28, 0x2d, 0x5c, 0x17, 0x56, 0x14, 0x0c, 0x43, 0x2a, 0xaa, 0x0a, 0xb7, 0x3c, 0xf4, - 0x1f, 0xf3, 0x74, 0x87, 0xf1, 0x1f, 0x4b, 0x99, 0x14, 0xa3, 0xf6, 0x2a, 0x72, 0x23, 0x87, 0xd0, - 0xcc, 0x63, 0x6e, 0x6d, 0x92, 0x8a, 0x11, 0xba, 0xb1, 0x31, 0xa5, 0x50, 0x98, 0xae, 0x0a, 0x56, - 0x01, 0x59, 0x46, 0x56, 0x89, 0x4a, 0x7a, 0x08, 0xeb, 0x72, 0x83, 0xc6, 0x60, 0x8a, 0x04, 0xb8, - 0x3e, 0x49, 0x45, 0xe8, 0x6b, 0x5e, 0x73, 0x55, 0xe4, 0x48, 0xaf, 0x88, 0x15, 0xd6, 0x69, 0x57, - 0xeb, 0x7d, 0x99, 0x7c, 0xff, 0xc0, 0xbb, 0x71, 0xbc, 0x28, 0xfe, 0x15, 0xfd, 0x95, 0xff, 0x0e, - 0x00, 0x00, 0xff, 0xff, 0xdf, 0xa6, 0x48, 0x8e, 0x47, 0x3d, 0x00, 0x00, + // 4934 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7b, 0xcd, 0x6f, 0x24, 0x49, + 0x56, 0x78, 0x67, 0xb9, 0xfc, 0x51, 0xaf, 0x3e, 0x6c, 0x87, 0xdd, 0x76, 0x75, 0x4d, 0x6f, 0x6f, + 0x4f, 0xfe, 0x46, 0x33, 0xfe, 0x35, 0x43, 0xbb, 0xc7, 0xbb, 0x3b, 0xcc, 0x4e, 0x03, 0xab, 0xfe, + 0x9a, 0xf6, 0xec, 0x7a, 0x7a, 0xbc, 0xe9, 0xee, 0x1d, 0xd8, 0x11, 0x14, 0xe9, 0xaa, 0x70, 0x39, + 0xb7, 0xb3, 0x32, 0x73, 0x32, 0xb3, 0xec, 0xae, 0x1d, 0x5a, 0xe2, 0x43, 0xe2, 0x04, 0xe2, 0x00, + 0x12, 0x5a, 0xa4, 0xe5, 0x00, 0x17, 0x38, 0xf0, 0x07, 0xa0, 0x95, 0xe0, 0xbe, 0x12, 0xe2, 0xb0, + 0x27, 0x04, 0x37, 0x38, 0x2d, 0x47, 0xc4, 0x85, 0x13, 0x7a, 0x2f, 0x3e, 0x32, 0x22, 0x33, 0xdd, + 0xdd, 0xc3, 0x02, 0xb7, 0x8a, 0x17, 0x2f, 0x5f, 0x44, 0xbc, 0x78, 0xf1, 0xbe, 0x0b, 0x5a, 0x69, + 0x32, 0xba, 0x99, 0xa4, 0x71, 0x1e, 0xb3, 0xc5, 0x30, 0x4a, 0x93, 0xd1, 0xe0, 0xea, 0x24, 0x8e, + 0x27, 0x21, 0xdf, 0xf5, 0x93, 0x60, 0xd7, 0x8f, 0xa2, 0x38, 0xf7, 0xf3, 0x20, 0x8e, 0x32, 0x81, + 0xe4, 0xbe, 0x03, 0x1b, 0xf7, 0x52, 0xee, 0xe7, 0xfc, 0x13, 0x3f, 0x0c, 0x79, 0xee, 0xf1, 0xcf, + 0x66, 0x3c, 0xcb, 0xd9, 0x00, 0x56, 0x12, 0x3f, 0xcb, 0xce, 0xe3, 0x74, 0xdc, 0x77, 0xae, 0x3b, + 0x3b, 0x1d, 0x4f, 0x8f, 0xdd, 0x2d, 0xd8, 0xb4, 0x3f, 0xc9, 0x92, 0x38, 0xca, 0x38, 0x92, 0x7a, + 0x12, 0x85, 0xf1, 0xe8, 0xe9, 0x17, 0x22, 0x65, 0x7f, 0x22, 0x49, 0xfd, 0xa0, 0x01, 0xed, 0xc7, + 0xa9, 0x1f, 0x65, 0xfe, 0x08, 0x37, 0xcb, 0xfa, 0xb0, 0x9c, 0x3f, 0x1b, 0x9e, 0xfa, 0xd9, 0x29, + 0x91, 0x68, 0x79, 0x6a, 0xc8, 0xb6, 0x60, 0xc9, 0x9f, 0xc6, 0xb3, 0x28, 0xef, 0x37, 0xae, 0x3b, + 0x3b, 0x0b, 0x9e, 0x1c, 0xb1, 0xb7, 0x61, 0x3d, 0x9a, 0x4d, 0x87, 0xa3, 0x38, 0x3a, 0x09, 0xd2, + 0xa9, 0x38, 0x72, 0x7f, 0xe1, 0xba, 0xb3, 0xb3, 0xe8, 0x55, 0x27, 0xd8, 0x35, 0x80, 0x63, 0xdc, + 0x86, 0x58, 0xa2, 0x49, 0x4b, 0x18, 0x10, 0xe6, 0x42, 0x47, 0x8e, 0x78, 0x30, 0x39, 0xcd, 0xfb, + 0x8b, 0x44, 0xc8, 0x82, 0x21, 0x8d, 0x3c, 0x98, 0xf2, 0x61, 0x96, 0xfb, 0xd3, 0xa4, 0xbf, 0x44, + 0xbb, 0x31, 0x20, 0x34, 0x1f, 0xe7, 0x7e, 0x38, 0x3c, 0xe1, 0x3c, 0xeb, 0x2f, 0xcb, 0x79, 0x0d, + 0x61, 0x6f, 0x42, 0x6f, 0xcc, 0xb3, 0x7c, 0xe8, 0x8f, 0xc7, 0x29, 0xcf, 0x32, 0x9e, 0xf5, 0x57, + 0xae, 0x2f, 0xec, 0xb4, 0xbc, 0x12, 0xd4, 0xed, 0xc3, 0xd6, 0x43, 0x9e, 0x1b, 0xdc, 0xc9, 0x24, + 0xa7, 0xdd, 0x03, 0x60, 0x06, 0xf8, 0x3e, 0xcf, 0xfd, 0x20, 0xcc, 0xd8, 0xbb, 0xd0, 0xc9, 0x0d, + 0xe4, 0xbe, 0x73, 0x7d, 0x61, 0xa7, 0xbd, 0xc7, 0x6e, 0x92, 0x74, 0xdc, 0x34, 0x3e, 0xf0, 0x2c, + 0x3c, 0xf7, 0x3f, 0x1d, 0x68, 0x1f, 0xf1, 0x68, 0xac, 0xee, 0x91, 0x41, 0x13, 0x77, 0x22, 0xef, + 0x90, 0x7e, 0xb3, 0x2f, 0x43, 0x9b, 0x76, 0x97, 0xe5, 0x69, 0x10, 0x4d, 0xe8, 0x0a, 0x5a, 0x1e, + 0x20, 0xe8, 0x88, 0x20, 0x6c, 0x0d, 0x16, 0xfc, 0x69, 0x4e, 0x8c, 0x5f, 0xf0, 0xf0, 0x27, 0x7b, + 0x1d, 0x3a, 0x89, 0x3f, 0x9f, 0xf2, 0x28, 0x2f, 0x98, 0xdd, 0xf1, 0xda, 0x12, 0xb6, 0x8f, 0xdc, + 0xbe, 0x09, 0x1b, 0x26, 0x8a, 0xa2, 0xbe, 0x48, 0xd4, 0xd7, 0x0d, 0x4c, 0xb9, 0xc8, 0x5b, 0xb0, + 0xaa, 0xf0, 0x53, 0xb1, 0x59, 0x62, 0x7f, 0xcb, 0xeb, 0x49, 0xb0, 0x3a, 0xc2, 0x0e, 0xac, 0x9d, + 0x04, 0x91, 0x1f, 0x0e, 0x47, 0x61, 0x7e, 0x36, 0x1c, 0xf3, 0x30, 0xf7, 0xe9, 0x22, 0x16, 0xbd, + 0x1e, 0xc1, 0xef, 0x85, 0xf9, 0xd9, 0x7d, 0x84, 0xba, 0x7f, 0xec, 0x40, 0x47, 0x1c, 0x5e, 0x48, + 0x24, 0x7b, 0x03, 0xba, 0x6a, 0x0d, 0x9e, 0xa6, 0x71, 0x2a, 0xe5, 0xd0, 0x06, 0xb2, 0x1b, 0xb0, + 0xa6, 0x00, 0x49, 0xca, 0x83, 0xa9, 0x3f, 0xe1, 0xc4, 0x94, 0x8e, 0x57, 0x81, 0xb3, 0xbd, 0x82, + 0x62, 0x1a, 0xcf, 0x72, 0x4e, 0x4c, 0x6a, 0xef, 0x75, 0xe4, 0xc5, 0x78, 0x08, 0xf3, 0x6c, 0x14, + 0xf7, 0xcf, 0x1d, 0xe8, 0xdc, 0x3b, 0xf5, 0xa3, 0x88, 0x87, 0x87, 0x71, 0x10, 0xe5, 0xec, 0x16, + 0xb0, 0x93, 0x59, 0x34, 0x0e, 0xa2, 0xc9, 0x30, 0x7f, 0x16, 0x8c, 0x87, 0xc7, 0xf3, 0x9c, 0x67, + 0xe2, 0x8a, 0xf6, 0x2f, 0x79, 0x35, 0x73, 0xec, 0x6d, 0x58, 0xb3, 0xa0, 0x59, 0x9e, 0x8a, 0x7b, + 0xdb, 0xbf, 0xe4, 0x55, 0x66, 0x50, 0xf0, 0xe3, 0x59, 0x9e, 0xcc, 0xf2, 0x61, 0x10, 0x8d, 0xf9, + 0x33, 0xda, 0x63, 0xd7, 0xb3, 0x60, 0x77, 0x7b, 0xd0, 0x31, 0xbf, 0x73, 0x7f, 0x19, 0xd6, 0x0e, + 0xf0, 0x45, 0x44, 0x41, 0x34, 0xb9, 0x23, 0xc4, 0x16, 0x9f, 0x69, 0x32, 0x3b, 0x7e, 0xca, 0xe7, + 0x92, 0x6f, 0x72, 0x84, 0x42, 0x75, 0x1a, 0x67, 0xb9, 0x94, 0x1c, 0xfa, 0xed, 0xfe, 0x8b, 0x03, + 0xab, 0xc8, 0xfb, 0x8f, 0xfc, 0x68, 0xae, 0x6e, 0xee, 0x00, 0x3a, 0x48, 0xea, 0x71, 0x7c, 0x47, + 0x3c, 0x76, 0x21, 0xc4, 0x3b, 0x92, 0x57, 0x25, 0xec, 0x9b, 0x26, 0xea, 0x83, 0x28, 0x4f, 0xe7, + 0x9e, 0xf5, 0x35, 0x8a, 0x6d, 0xee, 0xa7, 0x13, 0x9e, 0x93, 0x1a, 0x90, 0x6a, 0x01, 0x04, 0xe8, + 0x5e, 0x1c, 0x9d, 0xb0, 0xeb, 0xd0, 0xc9, 0xfc, 0x7c, 0x98, 0xf0, 0x94, 0xb8, 0x46, 0xa2, 0xb7, + 0xe0, 0x41, 0xe6, 0xe7, 0x87, 0x3c, 0xbd, 0x3b, 0xcf, 0xf9, 0xe0, 0x1b, 0xb0, 0x5e, 0x59, 0x05, + 0xa5, 0xbd, 0x38, 0x22, 0xfe, 0x64, 0x9b, 0xb0, 0x78, 0xe6, 0x87, 0x33, 0x2e, 0xb5, 0x93, 0x18, + 0xbc, 0xdf, 0x78, 0xcf, 0x71, 0xdf, 0x84, 0xb5, 0x62, 0xdb, 0x52, 0xc8, 0x18, 0x34, 0x91, 0x83, + 0x92, 0x00, 0xfd, 0x76, 0x7f, 0xdb, 0x11, 0x88, 0xf7, 0xe2, 0x40, 0xbf, 0x74, 0x44, 0x44, 0x85, + 0xa0, 0x10, 0xf1, 0xf7, 0x85, 0x9a, 0xf0, 0x67, 0x3f, 0xac, 0xfb, 0x16, 0xac, 0x1b, 0x5b, 0x78, + 0xc1, 0x66, 0xff, 0xcc, 0x81, 0xf5, 0x47, 0xfc, 0x5c, 0xde, 0xba, 0xda, 0xed, 0x7b, 0xd0, 0xcc, + 0xe7, 0x09, 0x27, 0xcc, 0xde, 0xde, 0x1b, 0xf2, 0xd2, 0x2a, 0x78, 0x37, 0xe5, 0xf0, 0xf1, 0x3c, + 0xe1, 0x1e, 0x7d, 0xe1, 0x7e, 0x0c, 0x6d, 0x03, 0xc8, 0xb6, 0x61, 0xe3, 0x93, 0x0f, 0x1f, 0x3f, + 0x7a, 0x70, 0x74, 0x34, 0x3c, 0x7c, 0x72, 0xf7, 0x5b, 0x0f, 0x7e, 0x75, 0xb8, 0x7f, 0xe7, 0x68, + 0x7f, 0xed, 0x12, 0xdb, 0x02, 0xf6, 0xe8, 0xc1, 0xd1, 0xe3, 0x07, 0xf7, 0x2d, 0xb8, 0xc3, 0x56, + 0xa1, 0x6d, 0x02, 0x1a, 0xee, 0x00, 0xfa, 0x8f, 0xf8, 0xf9, 0x27, 0x41, 0x1e, 0xf1, 0x2c, 0xb3, + 0x97, 0x77, 0x6f, 0x02, 0x33, 0xf7, 0x24, 0x8f, 0xd9, 0x87, 0x65, 0xa9, 0x7b, 0x95, 0xe9, 0x91, + 0x43, 0xf7, 0x4d, 0x60, 0x47, 0xc1, 0x24, 0xfa, 0x88, 0x67, 0x99, 0x3f, 0xe1, 0xea, 0xb0, 0x6b, + 0xb0, 0x30, 0xcd, 0x26, 0x52, 0x4b, 0xe2, 0x4f, 0xf7, 0x2b, 0xb0, 0x61, 0xe1, 0x49, 0xc2, 0x57, + 0xa1, 0x95, 0x05, 0x93, 0xc8, 0xcf, 0x67, 0x29, 0x97, 0xa4, 0x0b, 0x80, 0xfb, 0x01, 0x6c, 0x7e, + 0x87, 0xa7, 0xc1, 0xc9, 0xfc, 0x65, 0xe4, 0x6d, 0x3a, 0x8d, 0x32, 0x9d, 0x07, 0x70, 0xb9, 0x44, + 0x47, 0x2e, 0x2f, 0x24, 0x53, 0xde, 0xdf, 0x8a, 0x27, 0x06, 0xc6, 0x3b, 0x6d, 0x98, 0xef, 0xd4, + 0x7d, 0x02, 0xec, 0x5e, 0x1c, 0x45, 0x7c, 0x94, 0x1f, 0x72, 0x9e, 0xaa, 0xcd, 0xfc, 0x9c, 0x21, + 0x86, 0xed, 0xbd, 0x6d, 0x79, 0xb1, 0xe5, 0xc7, 0x2f, 0xe5, 0x93, 0x41, 0x33, 0xe1, 0xe9, 0x94, + 0x08, 0xaf, 0x78, 0xf4, 0xdb, 0xbd, 0x0c, 0x1b, 0x16, 0x59, 0xed, 0x49, 0x5c, 0xbe, 0x1f, 0x64, + 0xa3, 0xea, 0x82, 0x7d, 0x58, 0x4e, 0x66, 0xc7, 0xc3, 0xe2, 0x91, 0xa9, 0x21, 0x5a, 0xc5, 0xf2, + 0x27, 0x92, 0xd8, 0xef, 0x39, 0xd0, 0xdc, 0x7f, 0x7c, 0x70, 0x0f, 0x1d, 0x91, 0x20, 0x1a, 0xc5, + 0x53, 0xb4, 0x25, 0xe2, 0xd0, 0x7a, 0x7c, 0xe1, 0xe3, 0xb9, 0x0a, 0x2d, 0x32, 0x41, 0x68, 0xe8, + 0xe9, 0xe9, 0x74, 0xbc, 0x02, 0x80, 0x4e, 0x06, 0x7f, 0x96, 0x04, 0x29, 0x79, 0x11, 0xca, 0x37, + 0x68, 0x92, 0x8a, 0xac, 0x4e, 0xb8, 0x3f, 0x6d, 0x42, 0xf7, 0xce, 0x28, 0x0f, 0xce, 0xb8, 0x54, + 0xe1, 0xb4, 0x2a, 0x01, 0xe4, 0x7e, 0xe4, 0x08, 0x8d, 0x4d, 0xca, 0xa7, 0x71, 0xce, 0x87, 0xd6, + 0x65, 0xd8, 0x40, 0xc4, 0x1a, 0x09, 0x42, 0xc3, 0x04, 0x8d, 0x01, 0xed, 0xaf, 0xe5, 0xd9, 0x40, + 0x64, 0x19, 0x02, 0x86, 0xc1, 0x98, 0x76, 0xd6, 0xf4, 0xd4, 0x10, 0xf9, 0x31, 0xf2, 0x13, 0x7f, + 0x14, 0xe4, 0x73, 0xf9, 0xe6, 0xf5, 0x18, 0x69, 0x87, 0xf1, 0xc8, 0x0f, 0x87, 0xc7, 0x7e, 0xe8, + 0x47, 0x23, 0x2e, 0xfd, 0x19, 0x1b, 0x88, 0x2e, 0x8b, 0xdc, 0x92, 0x42, 0x13, 0x6e, 0x4d, 0x09, + 0x8a, 0xae, 0xcf, 0x28, 0x9e, 0x4e, 0x83, 0x1c, 0x3d, 0x9d, 0xfe, 0x8a, 0xd0, 0x2f, 0x05, 0x84, + 0x4e, 0x22, 0x46, 0xe7, 0x82, 0x87, 0x2d, 0xb1, 0x9a, 0x05, 0x44, 0x2a, 0x27, 0x9c, 0x93, 0x9e, + 0x7a, 0x7a, 0xde, 0x07, 0x41, 0xa5, 0x80, 0xe0, 0x6d, 0xcc, 0xa2, 0x8c, 0xe7, 0x79, 0xc8, 0xc7, + 0x7a, 0x43, 0x6d, 0x42, 0xab, 0x4e, 0xb0, 0x5b, 0xb0, 0x21, 0x9c, 0xaf, 0xcc, 0xcf, 0xe3, 0xec, + 0x34, 0xc8, 0x86, 0x19, 0x8f, 0xf2, 0x7e, 0x87, 0xf0, 0xeb, 0xa6, 0xd8, 0x7b, 0xb0, 0x5d, 0x02, + 0xa7, 0x7c, 0xc4, 0x83, 0x33, 0x3e, 0xee, 0x77, 0xe9, 0xab, 0x8b, 0xa6, 0xd9, 0x75, 0x68, 0xa3, + 0xcf, 0x39, 0x4b, 0xc6, 0x3e, 0x9a, 0xe7, 0x1e, 0xdd, 0x83, 0x09, 0x62, 0xef, 0x40, 0x37, 0xe1, + 0xc2, 0x86, 0x9e, 0xe6, 0xe1, 0x28, 0xeb, 0xaf, 0x92, 0x81, 0x6b, 0xcb, 0x27, 0x85, 0xf2, 0xeb, + 0xd9, 0x18, 0x28, 0x9a, 0xa3, 0x8c, 0xbc, 0x18, 0x7f, 0xde, 0x5f, 0x23, 0xa1, 0x2b, 0x00, 0xf8, + 0xb2, 0x0e, 0x82, 0x2c, 0x97, 0x92, 0xa6, 0x75, 0xdc, 0x3e, 0x6c, 0xda, 0x60, 0xa9, 0x0d, 0x6e, + 0xc1, 0x8a, 0x14, 0x9b, 0xac, 0xdf, 0xa6, 0xa5, 0x37, 0xe5, 0xd2, 0x96, 0xc4, 0x7a, 0x1a, 0xcb, + 0xfd, 0xa9, 0x03, 0x4d, 0x7c, 0x67, 0x17, 0xbf, 0x49, 0x53, 0x75, 0x2e, 0x58, 0xaa, 0x93, 0xfc, + 0x6d, 0xf4, 0x46, 0x04, 0xcf, 0x85, 0x5c, 0x1a, 0x90, 0x62, 0x3e, 0xe5, 0xa3, 0x33, 0x12, 0x4e, + 0x3d, 0x8f, 0x10, 0x14, 0x5d, 0x34, 0x59, 0xf4, 0xb5, 0x90, 0x4c, 0x3d, 0x56, 0x73, 0xf4, 0xe5, + 0x72, 0x31, 0x47, 0xdf, 0xf5, 0x61, 0x39, 0x88, 0x8e, 0xe3, 0x59, 0x34, 0x26, 0x29, 0x5c, 0xf1, + 0xd4, 0x10, 0xb9, 0x99, 0x90, 0x07, 0x13, 0x4c, 0xb9, 0x14, 0xbf, 0x02, 0xe0, 0x32, 0x74, 0x69, + 0x32, 0xd2, 0x2b, 0x9a, 0x95, 0xef, 0xc2, 0xba, 0x01, 0x93, 0x7c, 0x7c, 0x1d, 0x16, 0x13, 0x04, + 0x48, 0x07, 0x45, 0xdd, 0x1f, 0x29, 0x24, 0x31, 0xe3, 0xae, 0x41, 0xef, 0x21, 0xcf, 0x3f, 0x8c, + 0x4e, 0x62, 0x45, 0xe9, 0xef, 0x16, 0x60, 0x55, 0x83, 0x24, 0xa1, 0x1d, 0x58, 0x0d, 0xc6, 0x3c, + 0xca, 0x83, 0x7c, 0x3e, 0xb4, 0x3c, 0xa7, 0x32, 0x18, 0x15, 0xb9, 0x1f, 0x06, 0x7e, 0x26, 0x95, + 0x84, 0x18, 0xb0, 0x3d, 0xd8, 0x44, 0xf9, 0x52, 0x22, 0xa3, 0x2f, 0x57, 0x38, 0x70, 0xb5, 0x73, + 0xf8, 0x24, 0x10, 0x2e, 0x94, 0x50, 0xf1, 0x89, 0x50, 0x68, 0x75, 0x53, 0xc8, 0x35, 0x41, 0x09, + 0x8f, 0xbc, 0x28, 0x64, 0x50, 0x03, 0x2a, 0x51, 0xd3, 0x92, 0x70, 0x1e, 0xcb, 0x51, 0x93, 0x11, + 0x79, 0xad, 0x54, 0x22, 0xaf, 0x1d, 0x58, 0xcd, 0xe6, 0xd1, 0x88, 0x8f, 0x87, 0x79, 0x8c, 0xeb, + 0x06, 0x11, 0xdd, 0xce, 0x8a, 0x57, 0x06, 0x53, 0x8c, 0xc8, 0xb3, 0x3c, 0xe2, 0x39, 0xe9, 0x86, + 0x15, 0x4f, 0x0d, 0x51, 0xcd, 0x12, 0x8a, 0x10, 0xed, 0x96, 0x27, 0x47, 0x68, 0x91, 0x66, 0x69, + 0x90, 0xf5, 0x3b, 0x04, 0xa5, 0xdf, 0xec, 0xab, 0x70, 0xf9, 0x18, 0x23, 0x9a, 0x53, 0xee, 0x8f, + 0x79, 0x4a, 0xb7, 0x2f, 0x02, 0x3a, 0xf1, 0xc4, 0xeb, 0x27, 0xdd, 0xef, 0x93, 0x79, 0xd4, 0x01, + 0xe5, 0x13, 0x7a, 0xd5, 0xec, 0x35, 0x68, 0x89, 0x93, 0x64, 0xa7, 0xbe, 0x0a, 0x7d, 0x09, 0x70, + 0x74, 0xea, 0x63, 0x1c, 0x64, 0x31, 0xa7, 0x41, 0x7e, 0x59, 0x9b, 0x60, 0xfb, 0x82, 0x37, 0x6f, + 0x40, 0x4f, 0x85, 0xaa, 0xd9, 0x30, 0xe4, 0x27, 0xb9, 0x72, 0xbf, 0xa3, 0xd9, 0x14, 0x97, 0xcb, + 0x0e, 0xf8, 0x49, 0xee, 0x3e, 0x82, 0x75, 0xf9, 0x3a, 0x3f, 0x4e, 0xb8, 0x5a, 0xfa, 0xeb, 0x65, + 0xdb, 0x20, 0x4c, 0xf4, 0x86, 0x94, 0x47, 0x33, 0x86, 0x28, 0x19, 0x0c, 0xd7, 0x03, 0x26, 0xa7, + 0xef, 0x85, 0x71, 0xc6, 0x25, 0x41, 0x17, 0x3a, 0xa3, 0x30, 0xce, 0x94, 0x93, 0x2f, 0x8f, 0x63, + 0xc1, 0xf0, 0x06, 0xb2, 0xd9, 0x68, 0x84, 0xef, 0x5d, 0x18, 0x79, 0x35, 0x74, 0xff, 0xd2, 0x81, + 0x0d, 0xa2, 0xa6, 0xf4, 0x88, 0xf6, 0x0c, 0x5f, 0x7d, 0x9b, 0x9d, 0x91, 0x19, 0xf8, 0x6c, 0xc2, + 0xe2, 0x49, 0x9c, 0x8e, 0xb8, 0x5c, 0x49, 0x0c, 0xbe, 0xb8, 0xaf, 0xdb, 0xac, 0xf8, 0xba, 0xff, + 0xe8, 0xc0, 0x3a, 0x6d, 0xf5, 0x28, 0xf7, 0xf3, 0x59, 0x26, 0x8f, 0xff, 0x8b, 0xd0, 0xc5, 0xa3, + 0x72, 0xf5, 0x68, 0xe4, 0x46, 0x37, 0xf5, 0xfb, 0x26, 0xa8, 0x40, 0xde, 0xbf, 0xe4, 0xd9, 0xc8, + 0xec, 0x1b, 0xd0, 0x31, 0xf3, 0x0d, 0xb4, 0xe7, 0xf6, 0xde, 0x15, 0x75, 0xca, 0x8a, 0xe4, 0xec, + 0x5f, 0xf2, 0xac, 0x0f, 0xd8, 0x6d, 0x00, 0xb2, 0xda, 0x44, 0x56, 0x06, 0x8a, 0x57, 0x6c, 0x26, + 0x19, 0x97, 0xb5, 0x7f, 0xc9, 0x33, 0xd0, 0xef, 0xae, 0xc0, 0x92, 0x30, 0x33, 0xee, 0x43, 0xe8, + 0x5a, 0x3b, 0xb5, 0x7c, 0xf8, 0x8e, 0xf0, 0xe1, 0x2b, 0x21, 0x5f, 0xa3, 0x1a, 0xf2, 0xb9, 0x7f, + 0xd3, 0x00, 0x86, 0xd2, 0x56, 0xba, 0x4e, 0xb4, 0x73, 0xf1, 0xd8, 0xf2, 0x5a, 0x3a, 0x9e, 0x09, + 0x62, 0x37, 0x81, 0x19, 0x43, 0x15, 0xd9, 0x0b, 0xeb, 0x50, 0x33, 0x83, 0x6a, 0x4c, 0xb8, 0x1c, + 0x2a, 0xc2, 0x94, 0x5e, 0x9a, 0xb8, 0xb7, 0xda, 0x39, 0x4a, 0x38, 0xcd, 0xb2, 0x53, 0xb4, 0xc3, + 0xca, 0xaf, 0x51, 0xe3, 0xb2, 0x80, 0x2c, 0xbd, 0x54, 0x40, 0x96, 0xcb, 0x02, 0x42, 0xf6, 0x2e, + 0x0d, 0xce, 0xfc, 0x9c, 0x2b, 0x1b, 0x22, 0x87, 0xe8, 0xc6, 0x4c, 0x83, 0x88, 0xcc, 0xf3, 0x70, + 0x8a, 0xab, 0x4b, 0x37, 0xc6, 0x02, 0xba, 0x3f, 0x71, 0x60, 0x0d, 0x79, 0x67, 0xc9, 0xd7, 0xfb, + 0x40, 0xe2, 0xfd, 0x8a, 0xe2, 0x65, 0xe1, 0xfe, 0xec, 0xd2, 0xf5, 0x1e, 0xb4, 0x88, 0x60, 0x9c, + 0xf0, 0x48, 0x0a, 0x57, 0xdf, 0x16, 0xae, 0x42, 0xb3, 0xec, 0x5f, 0xf2, 0x0a, 0x64, 0x43, 0xb4, + 0xfe, 0xc1, 0x81, 0xb6, 0xdc, 0xe6, 0x7f, 0xdb, 0xd9, 0x1e, 0xc0, 0x0a, 0x4a, 0x99, 0xe1, 0xcb, + 0xea, 0x31, 0xda, 0x81, 0x29, 0x46, 0x34, 0x68, 0xf8, 0x2c, 0x47, 0xbb, 0x0c, 0x46, 0x2b, 0x46, + 0x4a, 0x34, 0x1b, 0xe6, 0x41, 0x38, 0x54, 0xb3, 0x32, 0x65, 0x57, 0x37, 0x85, 0xba, 0x24, 0xcb, + 0xfd, 0x09, 0x97, 0x06, 0x4a, 0x0c, 0x30, 0xa2, 0x90, 0x07, 0x2a, 0x3b, 0x51, 0x3f, 0x06, 0xd8, + 0xae, 0x4c, 0x69, 0x47, 0x4a, 0xfa, 0x8e, 0x61, 0x30, 0x3d, 0x8e, 0xb5, 0x1b, 0xea, 0x98, 0x6e, + 0xa5, 0x35, 0xc5, 0x26, 0x70, 0x59, 0x59, 0x62, 0xe4, 0x69, 0x61, 0x77, 0x1b, 0xe4, 0x42, 0xbc, + 0x63, 0xcb, 0x40, 0x79, 0x41, 0x05, 0x37, 0x5f, 0x63, 0x3d, 0x3d, 0x76, 0x0a, 0x7d, 0x6d, 0xf2, + 0xa5, 0xda, 0x36, 0xdc, 0x02, 0x5c, 0xeb, 0xed, 0x97, 0xac, 0x45, 0x3a, 0x66, 0xac, 0x96, 0xb9, + 0x90, 0x1a, 0x9b, 0xc3, 0x35, 0x35, 0x47, 0x7a, 0xb9, 0xba, 0x5e, 0xf3, 0x95, 0xce, 0xf6, 0x01, + 0x7e, 0x6c, 0x2f, 0xfa, 0x12, 0xc2, 0x83, 0x1f, 0x3b, 0xd0, 0xb3, 0xc9, 0xa1, 0xe8, 0xc8, 0x78, + 0x44, 0x29, 0x18, 0xe5, 0x4a, 0x95, 0xc0, 0xd5, 0x88, 0xaa, 0x51, 0x17, 0x51, 0x99, 0x71, 0xd3, + 0xc2, 0xcb, 0xe2, 0xa6, 0xe6, 0xab, 0xc5, 0x4d, 0x8b, 0x75, 0x71, 0xd3, 0xe0, 0x3f, 0x1c, 0x60, + 0xd5, 0xfb, 0x65, 0x0f, 0x45, 0x48, 0x17, 0xf1, 0x50, 0xea, 0x89, 0x9f, 0x7f, 0x35, 0x19, 0x51, + 0x3c, 0x54, 0x5f, 0xa3, 0xb0, 0x9a, 0x8a, 0xc0, 0x74, 0x45, 0xba, 0x5e, 0xdd, 0x54, 0x29, 0x92, + 0x6b, 0xbe, 0x3c, 0x92, 0x5b, 0x7c, 0x79, 0x24, 0xb7, 0x54, 0x8e, 0xe4, 0x06, 0xbf, 0x09, 0x5d, + 0xeb, 0xd6, 0xff, 0xe7, 0x4e, 0x5c, 0x76, 0x63, 0xc4, 0x05, 0x5b, 0xb0, 0xc1, 0xbf, 0x35, 0x80, + 0x55, 0x25, 0xef, 0xff, 0x74, 0x0f, 0x24, 0x47, 0x96, 0x02, 0x59, 0x90, 0x72, 0x64, 0xa9, 0x8e, + 0xff, 0x4d, 0xa5, 0xf8, 0x36, 0xac, 0xa7, 0x7c, 0x14, 0x9f, 0xf1, 0xd4, 0x88, 0xa6, 0xc5, 0x55, + 0x55, 0x27, 0xd0, 0x91, 0xb3, 0xe3, 0xd7, 0x15, 0xab, 0xca, 0x60, 0x58, 0x86, 0x52, 0x18, 0xeb, + 0x7e, 0x1d, 0x36, 0x45, 0xf1, 0xe7, 0xae, 0x20, 0xa5, 0x7c, 0x89, 0xd7, 0xa1, 0x73, 0x2e, 0xd2, + 0x74, 0xc3, 0x38, 0x0a, 0xe7, 0xd2, 0x88, 0xb4, 0x25, 0xec, 0xe3, 0x28, 0x9c, 0xbb, 0x3f, 0x74, + 0xe0, 0x72, 0xe9, 0xdb, 0x22, 0x5b, 0x2f, 0x54, 0xad, 0xad, 0x7f, 0x6d, 0x20, 0x1e, 0x51, 0xca, + 0xb8, 0x71, 0x44, 0x61, 0x92, 0xaa, 0x13, 0xc8, 0xc2, 0x59, 0x54, 0xc5, 0x17, 0x17, 0x53, 0x37, + 0xe5, 0x6e, 0xc3, 0x65, 0x79, 0xf9, 0xf6, 0xd9, 0xdc, 0x3d, 0xd8, 0x2a, 0x4f, 0x14, 0xd9, 0x46, + 0x7b, 0xcb, 0x6a, 0xe8, 0xfe, 0x3a, 0xb0, 0x6f, 0xcf, 0x78, 0x3a, 0xa7, 0xba, 0x80, 0x4e, 0xad, + 0x6e, 0x97, 0x83, 0xef, 0xa5, 0x64, 0x76, 0xfc, 0x2d, 0x3e, 0x57, 0x85, 0x97, 0x46, 0x51, 0x78, + 0xf9, 0x12, 0x00, 0x46, 0x13, 0x54, 0x48, 0x50, 0xa5, 0x30, 0x0c, 0xd6, 0x04, 0x41, 0xf7, 0x36, + 0x6c, 0x58, 0xf4, 0x35, 0x27, 0x97, 0xe4, 0x17, 0x22, 0xa2, 0xb5, 0xcb, 0x13, 0x72, 0xce, 0xfd, + 0x13, 0x07, 0x16, 0xf6, 0xe3, 0xc4, 0x4c, 0x36, 0x39, 0x76, 0xb2, 0x49, 0xaa, 0xd6, 0xa1, 0xd6, + 0x9c, 0x0d, 0xa9, 0x18, 0x4c, 0x20, 0x2a, 0x46, 0x7f, 0x9a, 0x63, 0x4c, 0x77, 0x12, 0xa7, 0xe7, + 0x7e, 0x3a, 0x96, 0xec, 0x2d, 0x41, 0xf1, 0x74, 0x85, 0xfe, 0xc1, 0x9f, 0xe8, 0x53, 0x50, 0xc6, + 0x6d, 0x2e, 0xc3, 0x50, 0x39, 0x72, 0xff, 0xd0, 0x81, 0x45, 0xda, 0x2b, 0x3e, 0x16, 0x71, 0xfd, + 0x54, 0x93, 0xa3, 0x84, 0x9e, 0x23, 0x1e, 0x4b, 0x09, 0x5c, 0xaa, 0xd4, 0x35, 0x2a, 0x95, 0xba, + 0xab, 0xd0, 0x12, 0xa3, 0xa2, 0xb4, 0x55, 0x00, 0xd8, 0x35, 0x68, 0x9e, 0xc6, 0x89, 0x32, 0x71, + 0xa0, 0x32, 0x38, 0x71, 0xe2, 0x11, 0xdc, 0xbd, 0x01, 0xab, 0x8f, 0xe2, 0x31, 0x37, 0x12, 0x00, + 0x17, 0xde, 0xa2, 0xfb, 0x5b, 0x0e, 0xac, 0x28, 0x64, 0xb6, 0x03, 0x4d, 0xb4, 0x54, 0x25, 0xdf, + 0x50, 0x67, 0x5b, 0x11, 0xcf, 0x23, 0x0c, 0xd4, 0x30, 0x14, 0x38, 0x16, 0x9e, 0x84, 0x0a, 0x1b, + 0x0b, 0x1b, 0xfd, 0x26, 0xf4, 0xc4, 0x9e, 0x4b, 0xb6, 0xac, 0x04, 0x75, 0xff, 0xca, 0x81, 0xae, + 0xb5, 0x06, 0x7a, 0xf9, 0xa1, 0x9f, 0xe5, 0x32, 0x77, 0x25, 0x99, 0x68, 0x82, 0xcc, 0x94, 0x50, + 0xc3, 0x4e, 0x09, 0xe9, 0x64, 0xc5, 0x82, 0x99, 0xac, 0xb8, 0x05, 0xad, 0xa2, 0xea, 0xd9, 0xb4, + 0x34, 0x07, 0xae, 0xa8, 0xf2, 0xc8, 0x05, 0x12, 0xd2, 0x19, 0xc5, 0x61, 0x9c, 0xca, 0xa2, 0xa0, + 0x18, 0xb8, 0xb7, 0xa1, 0x6d, 0xe0, 0xe3, 0x36, 0x22, 0x9e, 0x9f, 0xc7, 0xe9, 0x53, 0x95, 0x99, + 0x92, 0x43, 0x5d, 0x3f, 0x69, 0x14, 0xf5, 0x13, 0xf7, 0xaf, 0x1d, 0xe8, 0xa2, 0xa4, 0x04, 0xd1, + 0xe4, 0x30, 0x0e, 0x83, 0xd1, 0x9c, 0x24, 0x46, 0x09, 0x85, 0xac, 0x16, 0x2a, 0x89, 0xb1, 0xc1, + 0xe8, 0x12, 0x28, 0x27, 0x5f, 0xca, 0x8b, 0x1e, 0xa3, 0xe4, 0xa3, 0x69, 0x3b, 0xf6, 0x33, 0x2e, + 0xa2, 0x02, 0xa9, 0xca, 0x2d, 0x20, 0x6a, 0x17, 0x04, 0xa4, 0x7e, 0xce, 0x87, 0xd3, 0x20, 0x0c, + 0x03, 0x81, 0x2b, 0x24, 0xbc, 0x6e, 0xca, 0xfd, 0x51, 0x03, 0xda, 0x52, 0x8b, 0x3c, 0x18, 0x4f, + 0x44, 0x92, 0x55, 0xfa, 0x29, 0xfa, 0xf9, 0x19, 0x10, 0x35, 0x6f, 0x79, 0x36, 0x06, 0xa4, 0x7c, + 0xad, 0x0b, 0xd5, 0x6b, 0xbd, 0x0a, 0x2d, 0x14, 0xaf, 0x77, 0xc8, 0x85, 0x12, 0x45, 0xf2, 0x02, + 0xa0, 0x66, 0xf7, 0x68, 0x76, 0xb1, 0x98, 0x25, 0x80, 0xe5, 0x34, 0x2d, 0x95, 0x9c, 0xa6, 0xf7, + 0xa0, 0x23, 0xc9, 0x10, 0xdf, 0x29, 0xe6, 0x2a, 0x04, 0xdc, 0xba, 0x13, 0xcf, 0xc2, 0x54, 0x5f, + 0xee, 0xa9, 0x2f, 0x57, 0x5e, 0xf6, 0xa5, 0xc2, 0xa4, 0xca, 0x83, 0xe0, 0xcd, 0xc3, 0xd4, 0x4f, + 0x4e, 0x95, 0x66, 0x1e, 0xeb, 0xfa, 0x2a, 0x81, 0xd9, 0x0d, 0x58, 0xc4, 0xcf, 0x94, 0xf6, 0xab, + 0x7f, 0x74, 0x02, 0x85, 0xed, 0xc0, 0x22, 0x1f, 0x4f, 0xb8, 0x72, 0xdc, 0x99, 0x1d, 0x42, 0xe1, + 0x1d, 0x79, 0x02, 0x01, 0x55, 0x00, 0x42, 0x4b, 0x2a, 0xc0, 0xd6, 0x9c, 0x4b, 0x38, 0xfc, 0x70, + 0xec, 0x6e, 0x02, 0x7b, 0x24, 0xa4, 0xd6, 0x4c, 0x19, 0xfe, 0xee, 0x02, 0xb4, 0x0d, 0x30, 0xbe, + 0xe6, 0x09, 0x6e, 0x78, 0x38, 0x0e, 0xfc, 0x29, 0xcf, 0x79, 0x2a, 0x25, 0xb5, 0x04, 0x25, 0x05, + 0x7b, 0x36, 0x19, 0xc6, 0xb3, 0x7c, 0x38, 0xe6, 0x93, 0x94, 0x0b, 0x7b, 0xe7, 0x78, 0x25, 0x28, + 0xe2, 0x4d, 0xfd, 0x67, 0x26, 0x9e, 0x90, 0x87, 0x12, 0x54, 0x25, 0x00, 0x05, 0x8f, 0x9a, 0x45, + 0x02, 0x50, 0x70, 0xa4, 0xac, 0x87, 0x16, 0x6b, 0xf4, 0xd0, 0xbb, 0xb0, 0x25, 0x34, 0x8e, 0x7c, + 0x9b, 0xc3, 0x92, 0x98, 0x5c, 0x30, 0xcb, 0x6e, 0xc0, 0x1a, 0xee, 0x59, 0x09, 0x78, 0x16, 0x7c, + 0x5f, 0x04, 0xeb, 0x8e, 0x57, 0x81, 0x23, 0x2e, 0x3e, 0x47, 0x0b, 0x57, 0x54, 0x21, 0x2a, 0x70, + 0xc2, 0xf5, 0x9f, 0xd9, 0xb8, 0x2d, 0x89, 0x5b, 0x82, 0xbb, 0x5d, 0x68, 0x1f, 0xe5, 0x71, 0xa2, + 0x2e, 0xa5, 0x07, 0x1d, 0x31, 0x94, 0x95, 0xa7, 0xd7, 0xe0, 0x0a, 0x49, 0xd1, 0xe3, 0x38, 0x89, + 0xc3, 0x78, 0x32, 0x3f, 0x9a, 0x1d, 0x67, 0xa3, 0x34, 0x48, 0xd0, 0xa1, 0x76, 0xff, 0xde, 0x81, + 0x0d, 0x6b, 0x56, 0x66, 0x02, 0xbe, 0x2a, 0x44, 0x5a, 0x17, 0x0b, 0x84, 0xe0, 0xad, 0x1b, 0xea, + 0x50, 0x20, 0x8a, 0xbc, 0xca, 0x13, 0x59, 0x3f, 0xb8, 0x03, 0xab, 0x6a, 0x67, 0xea, 0x43, 0x21, + 0x85, 0xfd, 0xaa, 0x14, 0xca, 0xef, 0x7b, 0xf2, 0x03, 0x45, 0xe2, 0x97, 0x84, 0x5b, 0xca, 0xc7, + 0x74, 0x46, 0x15, 0x12, 0x0e, 0xd4, 0xf7, 0xa6, 0x2f, 0xac, 0x76, 0x30, 0xd2, 0xc0, 0xcc, 0xfd, + 0x7d, 0x07, 0xa0, 0xd8, 0x1d, 0x0a, 0x46, 0xa1, 0xd2, 0x1d, 0x4a, 0xb0, 0x1a, 0xea, 0xfb, 0x75, + 0xe8, 0xe8, 0x34, 0x76, 0x61, 0x25, 0xda, 0x0a, 0x86, 0x0e, 0xcc, 0x5b, 0xb0, 0x3a, 0x09, 0xe3, + 0x63, 0xb2, 0xb9, 0x54, 0xca, 0xcc, 0x64, 0xfd, 0xad, 0x27, 0xc0, 0x1f, 0x48, 0x68, 0x61, 0x52, + 0x9a, 0x86, 0x49, 0x71, 0xff, 0xa0, 0xa1, 0xd3, 0xa2, 0xc5, 0x99, 0x2f, 0x7c, 0x65, 0x6c, 0xaf, + 0xa2, 0x1c, 0x2f, 0xc8, 0x42, 0x52, 0xf2, 0xe3, 0xf0, 0xa5, 0x71, 0xe0, 0x6d, 0xe8, 0xa5, 0x42, + 0xfb, 0x28, 0xd5, 0xd4, 0x7c, 0x81, 0x6a, 0xea, 0xa6, 0x96, 0xdd, 0xf9, 0xff, 0xb0, 0xe6, 0x8f, + 0xcf, 0x78, 0x9a, 0x07, 0x14, 0x10, 0x90, 0xd1, 0x17, 0x0a, 0x75, 0xd5, 0x80, 0x93, 0x2d, 0x7e, + 0x0b, 0x56, 0x65, 0xcd, 0x53, 0x63, 0xca, 0xd6, 0x97, 0x02, 0x8c, 0x88, 0xee, 0x5f, 0xa8, 0x0c, + 0xac, 0x7d, 0x87, 0x17, 0x73, 0xc4, 0x3c, 0x5d, 0xa3, 0x74, 0xba, 0xff, 0x27, 0xb3, 0xa1, 0x63, + 0x15, 0x75, 0xc8, 0xbc, 0xb4, 0x00, 0xca, 0xec, 0xb5, 0xcd, 0xd2, 0xe6, 0xab, 0xb0, 0xd4, 0xfd, + 0xe1, 0x02, 0x2c, 0x7f, 0x18, 0x9d, 0xc5, 0xc1, 0x88, 0x72, 0x93, 0x53, 0x3e, 0x8d, 0x55, 0x7f, + 0x01, 0xfe, 0x46, 0x8b, 0x4e, 0x45, 0xb5, 0x24, 0x97, 0xc9, 0x45, 0x35, 0x44, 0xeb, 0x96, 0x16, + 0x3d, 0x37, 0x42, 0x52, 0x0c, 0x08, 0xfa, 0x87, 0xa9, 0xd9, 0x70, 0x24, 0x47, 0x45, 0x83, 0xc6, + 0xa2, 0xd1, 0xa0, 0x41, 0x99, 0x6c, 0x51, 0x2f, 0x24, 0x76, 0xae, 0x78, 0x6a, 0x48, 0x7e, 0x6c, + 0xca, 0x45, 0x4c, 0x4c, 0x76, 0x72, 0x59, 0xfa, 0xb1, 0x26, 0x10, 0x6d, 0xa9, 0xf8, 0x40, 0xe0, + 0x08, 0x5d, 0x63, 0x82, 0xd0, 0xb7, 0x28, 0xf7, 0x2c, 0xb5, 0xc4, 0x15, 0x97, 0xc0, 0xa8, 0x90, + 0xc6, 0x5c, 0xeb, 0x0d, 0x71, 0x06, 0x10, 0x3d, 0x45, 0x65, 0xb8, 0xe1, 0x05, 0x8b, 0xba, 0xa7, + 0x1c, 0x91, 0x0f, 0xe2, 0x87, 0xe1, 0xb1, 0x3f, 0x7a, 0x4a, 0x9d, 0x64, 0x54, 0xe6, 0x6c, 0x79, + 0x36, 0x10, 0x77, 0x4d, 0x8d, 0x51, 0x92, 0x44, 0x57, 0x94, 0x29, 0x0d, 0x90, 0xfb, 0x1d, 0x60, + 0x77, 0xc6, 0x63, 0x79, 0x43, 0x3a, 0x46, 0x28, 0x78, 0xeb, 0x58, 0xbc, 0xad, 0x39, 0x63, 0xa3, + 0xf6, 0x8c, 0xee, 0x03, 0x68, 0x1f, 0x1a, 0x0d, 0x60, 0x74, 0x99, 0xaa, 0xf5, 0x4b, 0x0a, 0x80, + 0x01, 0x31, 0x16, 0x6c, 0x98, 0x0b, 0xba, 0xbf, 0x00, 0xec, 0x20, 0xc8, 0x72, 0xbd, 0x3f, 0x1d, + 0x49, 0xea, 0x84, 0x98, 0x11, 0x49, 0x4a, 0x18, 0x45, 0x92, 0x77, 0x44, 0xb5, 0xb4, 0x7c, 0xb0, + 0x1b, 0xb0, 0x12, 0x08, 0x90, 0xd2, 0xc3, 0x3d, 0x29, 0xc0, 0x0a, 0x53, 0xcf, 0xa3, 0x43, 0x21, + 0x81, 0x96, 0x9a, 0xff, 0x91, 0x03, 0xcb, 0xf2, 0x68, 0x68, 0x0e, 0xad, 0xd6, 0x37, 0x71, 0x30, + 0x0b, 0x56, 0xdf, 0x30, 0x54, 0x95, 0xba, 0x85, 0x3a, 0xa9, 0x63, 0xd0, 0x4c, 0xfc, 0xfc, 0x94, + 0x3c, 0xe8, 0x96, 0x47, 0xbf, 0x55, 0xa4, 0xb4, 0x58, 0x44, 0x4a, 0x75, 0x3d, 0x6a, 0x42, 0x67, + 0x54, 0xe0, 0xaa, 0x8a, 0x2c, 0x0f, 0xa0, 0x13, 0xa0, 0x77, 0x45, 0x15, 0xb9, 0x00, 0x17, 0xfc, + 0x92, 0x24, 0xca, 0xfc, 0x92, 0xa8, 0x9e, 0x9e, 0x77, 0x07, 0xd0, 0xbf, 0xcf, 0x43, 0x9e, 0xf3, + 0x3b, 0x61, 0x58, 0xa6, 0xff, 0x1a, 0x5c, 0xa9, 0x99, 0x93, 0x56, 0xf5, 0x03, 0x58, 0xbf, 0xcf, + 0x8f, 0x67, 0x93, 0x03, 0x7e, 0x56, 0x54, 0x1e, 0x18, 0x34, 0xb3, 0xd3, 0xf8, 0x5c, 0xde, 0x2d, + 0xfd, 0xc6, 0x80, 0x37, 0x44, 0x9c, 0x61, 0x96, 0xf0, 0x91, 0xea, 0x8c, 0x21, 0xc8, 0x51, 0xc2, + 0x47, 0xee, 0xbb, 0xc0, 0x4c, 0x3a, 0xf2, 0x08, 0xf8, 0x72, 0x67, 0xc7, 0xc3, 0x6c, 0x9e, 0xe5, + 0x7c, 0xaa, 0x5a, 0x7e, 0x4c, 0x90, 0xfb, 0x16, 0x74, 0x0e, 0xfd, 0xb9, 0xc7, 0x3f, 0x93, 0xdd, + 0x87, 0x18, 0xbc, 0xf9, 0x73, 0x14, 0x65, 0x1d, 0xbc, 0xd1, 0xb4, 0xfb, 0xb7, 0x0d, 0x58, 0x12, + 0x98, 0x48, 0x75, 0xcc, 0xb3, 0x3c, 0x88, 0x44, 0x86, 0x5e, 0x52, 0x35, 0x40, 0x15, 0xd9, 0x68, + 0xd4, 0xc8, 0x86, 0x74, 0xa7, 0x54, 0x7f, 0x81, 0x14, 0x02, 0x0b, 0x46, 0xb1, 0xa9, 0xae, 0x59, + 0x36, 0x65, 0x6c, 0xaa, 0x00, 0xa5, 0x28, 0xb9, 0xd0, 0x0f, 0x62, 0x7f, 0x4a, 0x68, 0xa5, 0x38, + 0x98, 0xa0, 0x5a, 0x2d, 0xb4, 0x2c, 0xa4, 0xa6, 0xa2, 0x85, 0x2a, 0xda, 0x66, 0xe5, 0x15, 0xb4, + 0x8d, 0xf0, 0xb1, 0x2c, 0x6d, 0xc3, 0x60, 0xed, 0x03, 0xce, 0x3d, 0x9e, 0xc4, 0xa9, 0x6a, 0xe1, + 0x74, 0x7f, 0xe0, 0xc0, 0x9a, 0xb4, 0x1e, 0x7a, 0x8e, 0xbd, 0x6e, 0x99, 0x1a, 0xa7, 0x2e, 0x69, + 0xfb, 0x06, 0x74, 0x29, 0xd8, 0xc2, 0x48, 0x8a, 0x22, 0x2b, 0x99, 0x7f, 0xb0, 0x80, 0xb8, 0x27, + 0x95, 0x86, 0x9c, 0x06, 0xa1, 0x64, 0xb0, 0x09, 0x42, 0xb3, 0xa8, 0x82, 0x31, 0x62, 0xaf, 0xe3, + 0xe9, 0xb1, 0x7b, 0x08, 0xeb, 0xc6, 0x7e, 0xa5, 0x40, 0xdd, 0x06, 0x55, 0xb8, 0x14, 0xe9, 0x04, + 0xf1, 0x2e, 0xb6, 0x6d, 0x43, 0x58, 0x7c, 0x66, 0x21, 0xbb, 0xff, 0xe4, 0xc0, 0x86, 0x70, 0x0a, + 0xa4, 0xcb, 0xa5, 0xfb, 0xa0, 0x96, 0x84, 0x17, 0x24, 0x04, 0x7e, 0xff, 0x92, 0x27, 0xc7, 0xec, + 0x6b, 0xaf, 0xe8, 0xc8, 0xe8, 0x1a, 0xe1, 0x05, 0xec, 0x59, 0xa8, 0x63, 0xcf, 0x0b, 0x0e, 0x5f, + 0x17, 0x2c, 0x2f, 0xd6, 0x06, 0xcb, 0x77, 0x97, 0x61, 0x31, 0x1b, 0xc5, 0x09, 0x77, 0xb7, 0x60, + 0xd3, 0x3e, 0x9c, 0x60, 0xd9, 0xde, 0x3f, 0x3b, 0xd0, 0x13, 0x79, 0x3d, 0xd1, 0x1c, 0xce, 0x53, + 0x86, 0x71, 0x99, 0xd1, 0x73, 0xce, 0xb4, 0x5b, 0x5a, 0xed, 0x5d, 0x1f, 0xbc, 0x56, 0x3b, 0xa7, + 0x7c, 0xf2, 0xdf, 0xf9, 0xc9, 0xbf, 0xfe, 0x51, 0xe3, 0xb2, 0xbb, 0xb6, 0x7b, 0xf6, 0xce, 0x2e, + 0xa9, 0x4f, 0x7e, 0x4e, 0x18, 0xef, 0x3b, 0x37, 0x70, 0x15, 0xb3, 0x1d, 0x5d, 0xaf, 0x52, 0xd3, + 0xd6, 0xae, 0x57, 0xa9, 0xed, 0x5f, 0xb7, 0x56, 0x99, 0x11, 0x86, 0x5e, 0x65, 0xef, 0xdf, 0x07, + 0xd0, 0xd2, 0x01, 0x24, 0xfb, 0x1e, 0x74, 0xad, 0x1c, 0x26, 0x53, 0x84, 0xeb, 0xb2, 0xa2, 0x83, + 0xab, 0xf5, 0x93, 0x72, 0xd9, 0x6b, 0xb4, 0x6c, 0x9f, 0x6d, 0xe1, 0xb2, 0x32, 0x71, 0xb8, 0x4b, + 0xc9, 0x5d, 0xd1, 0x22, 0xf1, 0x14, 0x7a, 0x76, 0xde, 0x91, 0x5d, 0xb5, 0x45, 0xa3, 0xb4, 0xda, + 0x97, 0x2e, 0x98, 0x95, 0xcb, 0x5d, 0xa5, 0xe5, 0xb6, 0xd8, 0xa6, 0xb9, 0x9c, 0x0e, 0xec, 0x38, + 0x35, 0xb5, 0x98, 0x7d, 0xea, 0x4c, 0xd1, 0xab, 0xef, 0x5f, 0x1f, 0x5c, 0xa9, 0xf6, 0xa4, 0xcb, + 0x26, 0x76, 0xb7, 0x4f, 0x4b, 0x31, 0x46, 0x0c, 0x35, 0xdb, 0xd4, 0xd9, 0xa7, 0xd0, 0xd2, 0xbd, + 0xa9, 0x6c, 0xdb, 0x68, 0x08, 0x36, 0x1b, 0x66, 0x07, 0xfd, 0xea, 0x44, 0xdd, 0x55, 0x99, 0x94, + 0x51, 0x20, 0x0e, 0xe0, 0xb2, 0xb4, 0xe6, 0xc7, 0xfc, 0x8b, 0x9c, 0xa4, 0xa6, 0xbb, 0xfe, 0x96, + 0xc3, 0x6e, 0xc3, 0x8a, 0x6a, 0xf9, 0x65, 0x5b, 0xf5, 0xad, 0xcb, 0x83, 0xed, 0x0a, 0x5c, 0xea, + 0x91, 0x3b, 0x00, 0x45, 0x77, 0x2a, 0xeb, 0x5f, 0xd4, 0x44, 0xab, 0x99, 0x58, 0xd3, 0xca, 0x3a, + 0xa1, 0xe6, 0x5c, 0xbb, 0xf9, 0x95, 0x7d, 0xb9, 0xc0, 0xaf, 0x6d, 0x8b, 0x7d, 0x01, 0x41, 0x77, + 0x8b, 0x78, 0xb7, 0xc6, 0x7a, 0xc8, 0xbb, 0x88, 0x9f, 0xab, 0xf6, 0xae, 0xfb, 0xd0, 0x36, 0x3a, + 0x5e, 0x99, 0xa2, 0x50, 0xed, 0x96, 0x1d, 0x0c, 0xea, 0xa6, 0xe4, 0x76, 0xbf, 0x09, 0x5d, 0xab, + 0x75, 0x55, 0xbf, 0x8c, 0xba, 0xc6, 0x58, 0xfd, 0x32, 0xea, 0xbb, 0x5d, 0xbf, 0x0b, 0x6d, 0xa3, + 0xd1, 0x94, 0x19, 0xc5, 0xf1, 0x52, 0x8b, 0xa9, 0xde, 0x51, 0x5d, 0x5f, 0xea, 0x26, 0x9d, 0xb7, + 0xe7, 0xb6, 0xf0, 0xbc, 0xd4, 0xe3, 0x84, 0x42, 0xf2, 0x3d, 0xe8, 0xd9, 0xad, 0xa7, 0xfa, 0x55, + 0xd5, 0x36, 0xb1, 0xea, 0x57, 0x75, 0x41, 0xbf, 0xaa, 0x14, 0xc8, 0x1b, 0x1b, 0x7a, 0x91, 0xdd, + 0xcf, 0x65, 0xfa, 0xf4, 0x39, 0xfb, 0x36, 0xaa, 0x0e, 0xd9, 0x74, 0xc6, 0x8a, 0x86, 0x5b, 0xbb, + 0x35, 0x4d, 0x4b, 0x7b, 0xa5, 0x3f, 0xcd, 0x5d, 0x27, 0xe2, 0x6d, 0x56, 0x9c, 0x80, 0x7d, 0x04, + 0xcb, 0xb2, 0xf9, 0x8c, 0x5d, 0x2e, 0xa4, 0xda, 0x48, 0x36, 0x0d, 0xb6, 0xca, 0x60, 0x49, 0x6c, + 0x83, 0x88, 0x75, 0x59, 0x1b, 0x89, 0x4d, 0x78, 0x1e, 0x20, 0x8d, 0x08, 0x56, 0x4b, 0x05, 0x31, + 0xfd, 0x58, 0xea, 0xcb, 0xe9, 0x83, 0x6b, 0x2f, 0xae, 0xa3, 0xd9, 0x6a, 0x46, 0xa9, 0x97, 0x5d, + 0xd5, 0xfd, 0xf0, 0x6b, 0xd0, 0x31, 0x3b, 0x1a, 0xb5, 0xce, 0xae, 0xe9, 0x7e, 0xd4, 0x3a, 0xbb, + 0xae, 0x05, 0x52, 0x5d, 0x2e, 0xeb, 0x98, 0xcb, 0xb0, 0xef, 0xc2, 0xaa, 0x51, 0x7a, 0x3d, 0x9a, + 0x47, 0x23, 0x2d, 0x3c, 0xd5, 0x06, 0x98, 0x41, 0x9d, 0xa5, 0x75, 0xb7, 0x89, 0xf0, 0xba, 0x6b, + 0x11, 0x46, 0xc1, 0xb9, 0x07, 0x6d, 0xb3, 0xac, 0xfb, 0x02, 0xba, 0xdb, 0xc6, 0x94, 0xd9, 0x37, + 0x72, 0xcb, 0x61, 0x7f, 0xea, 0x40, 0xc7, 0x6c, 0xad, 0x62, 0x56, 0xc6, 0xa6, 0x44, 0xa7, 0x6f, + 0xce, 0x99, 0x84, 0x5c, 0x8f, 0x36, 0x79, 0x70, 0xe3, 0x9b, 0x16, 0x93, 0x3f, 0xb7, 0x9c, 0xa8, + 0x9b, 0xe5, 0xbf, 0x87, 0x3c, 0x2f, 0x23, 0x98, 0x4d, 0x42, 0xcf, 0x6f, 0x39, 0xec, 0x7d, 0xf1, + 0x17, 0x22, 0x15, 0x00, 0x31, 0x43, 0xb9, 0x95, 0x59, 0x66, 0xfe, 0xdb, 0x66, 0xc7, 0xb9, 0xe5, + 0xb0, 0xdf, 0x10, 0xff, 0x02, 0x91, 0xdf, 0x12, 0xe7, 0x5f, 0xf5, 0x7b, 0xf7, 0x0d, 0x3a, 0xcd, + 0x35, 0xf7, 0x8a, 0x75, 0x9a, 0xb2, 0x76, 0x3f, 0x04, 0x28, 0xa2, 0x59, 0x56, 0x0a, 0xed, 0xb4, + 0xde, 0xab, 0x06, 0xbc, 0xf6, 0x8d, 0xaa, 0x08, 0x10, 0x29, 0x7e, 0x2a, 0x84, 0x51, 0xe2, 0x67, + 0xfa, 0x4a, 0xab, 0x51, 0xe9, 0x60, 0x50, 0x37, 0x55, 0x27, 0x8a, 0x8a, 0x3e, 0x7b, 0x02, 0xdd, + 0x83, 0x38, 0x7e, 0x3a, 0x4b, 0x74, 0x86, 0xc4, 0x0e, 0xae, 0x30, 0x74, 0x1e, 0x94, 0x4e, 0xe1, + 0x5e, 0x27, 0x52, 0x03, 0xd6, 0x37, 0x48, 0xed, 0x7e, 0x5e, 0xc4, 0xd2, 0xcf, 0x99, 0x0f, 0xeb, + 0xda, 0xc6, 0xe9, 0x8d, 0x0f, 0x6c, 0x32, 0x66, 0x48, 0x5b, 0x59, 0xc2, 0xf2, 0x3a, 0xd4, 0x6e, + 0x77, 0x33, 0x45, 0xf3, 0x96, 0xc3, 0x0e, 0xa1, 0x73, 0x9f, 0x8f, 0xe2, 0x31, 0x97, 0xe1, 0xd0, + 0x46, 0xb1, 0x71, 0x1d, 0x47, 0x0d, 0xba, 0x16, 0xd0, 0x7e, 0xf5, 0x89, 0x3f, 0x4f, 0xf9, 0x67, + 0xbb, 0x9f, 0xcb, 0x40, 0xeb, 0xb9, 0x7a, 0xf5, 0x2a, 0x38, 0xb4, 0x5e, 0x7d, 0x29, 0x9a, 0xb4, + 0x5e, 0x7d, 0x25, 0x9a, 0xb4, 0x58, 0xad, 0x82, 0x53, 0x16, 0x62, 0x8c, 0x59, 0x0a, 0x40, 0xb5, + 0xa5, 0xbc, 0x28, 0x6c, 0x1d, 0x5c, 0xbf, 0x18, 0xc1, 0x5e, 0xed, 0x86, 0xbd, 0xda, 0x11, 0x74, + 0xef, 0x73, 0xc1, 0x2c, 0x51, 0x75, 0x18, 0xd8, 0x6a, 0xc4, 0xac, 0x50, 0x94, 0x55, 0x0c, 0xcd, + 0xd9, 0x6a, 0x9d, 0x52, 0xfe, 0xec, 0x53, 0x68, 0x3f, 0xe4, 0xb9, 0x2a, 0x33, 0x68, 0x7f, 0xa3, + 0x54, 0x77, 0x18, 0xd4, 0x54, 0x29, 0x6c, 0x99, 0x21, 0x6a, 0xbb, 0x7c, 0x3c, 0xe1, 0xe2, 0xb1, + 0x0f, 0x83, 0xf1, 0x73, 0xf6, 0x2b, 0x44, 0x5c, 0x57, 0x26, 0xb7, 0x8c, 0xec, 0xb4, 0x49, 0x7c, + 0xb5, 0x04, 0xaf, 0xa3, 0x1c, 0xc5, 0x63, 0x6e, 0x18, 0xb8, 0x08, 0xda, 0x46, 0x19, 0x5a, 0x3f, + 0xa0, 0x6a, 0xe9, 0x5b, 0x3f, 0xa0, 0x9a, 0xaa, 0xb5, 0xbb, 0x43, 0xeb, 0xb8, 0xec, 0x7a, 0xb1, + 0x8e, 0xa8, 0x54, 0x17, 0x2b, 0xed, 0x7e, 0xee, 0x4f, 0xf3, 0xe7, 0xec, 0x13, 0xea, 0xc6, 0x36, + 0x4b, 0x29, 0x85, 0xbf, 0x53, 0xae, 0xba, 0x68, 0x66, 0x19, 0x53, 0xb6, 0x0f, 0x24, 0x96, 0x22, + 0x3b, 0xf8, 0x35, 0x80, 0xa3, 0x3c, 0x4e, 0xee, 0xfb, 0x7c, 0x1a, 0x47, 0x85, 0xe6, 0x2a, 0xca, + 0x05, 0x85, 0xe6, 0x32, 0x6a, 0x06, 0xec, 0x13, 0xc3, 0xe3, 0xb4, 0x2a, 0x51, 0x4a, 0xb8, 0x2e, + 0xac, 0x28, 0x68, 0x86, 0xd4, 0x54, 0x15, 0x6e, 0x39, 0xe8, 0x3f, 0x16, 0xe9, 0x0e, 0xed, 0x3f, + 0x56, 0x32, 0x29, 0x5a, 0xed, 0xd5, 0xe4, 0x46, 0x0e, 0xa1, 0x55, 0xc4, 0xdc, 0xca, 0x24, 0x95, + 0x23, 0x74, 0x6d, 0x63, 0x2a, 0xa1, 0xb0, 0xbb, 0x46, 0xac, 0x02, 0xb6, 0x82, 0xac, 0xa2, 0x4a, + 0x7a, 0x00, 0x1b, 0x62, 0x83, 0xda, 0x60, 0x52, 0x02, 0x5c, 0x9d, 0xa4, 0x26, 0xf4, 0xd5, 0xaf, + 0xb9, 0x2e, 0x72, 0x74, 0xaf, 0xd0, 0x0a, 0x1b, 0x6e, 0x4f, 0xe9, 0x7d, 0x91, 0x7c, 0x7f, 0xdf, + 0xb9, 0x71, 0xbc, 0x44, 0xff, 0x77, 0xfe, 0xca, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x23, + 0xfe, 0x34, 0x21, 0x3d, 0x00, 0x00, } diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index 52f46516..9e057c60 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -801,6 +801,9 @@ message GetInfoResponse { /// The URIs of the current node. repeated string uris = 12 [json_name = "uris"]; + + /// Timestamp of the block best known to the wallet + int64 best_header_timestamp = 13 [ json_name = "best_header_timestamp" ]; } message ConfirmationUpdate { diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index e904ff53..93f83995 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -1225,6 +1225,11 @@ "type": "string" }, "description": "/ The URIs of the current node." + }, + "best_header_timestamp": { + "type": "string", + "format": "int64", + "title": "/ Timestamp of the block best known to the wallet" } } }, @@ -2007,7 +2012,7 @@ "amount": { "type": "string", "format": "int64", - "title": "/ The transaction amount, denominated in satoshis" + "title": "/ The transaction ammount, denominated in satoshis" }, "num_confirmations": { "type": "integer", diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index c6229edc..d317146e 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -667,27 +667,32 @@ func (b *BtcWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, e // it has fully synced to the current best block in the main chain. // // This is a part of the WalletController interface. -func (b *BtcWallet) IsSynced() (bool, error) { - // Grab the best chain state the wallet is currently aware of. +func (b *BtcWallet) IsSynced() (bool, int64, error) { + // Grab the best chain state the wallet is currently aware of. We'll + // also grab the timestamp of the best block to return for determining + // sync progress. syncState := b.wallet.Manager.SyncedTo() + walletBestHeader, err := b.chain.GetBlockHeader(&syncState.Hash) + if err != nil { + return false, 0, err + } var ( bestHash *chainhash.Hash bestHeight int32 - err error ) // Next, query the chain backend to grab the info about the tip of the // main chain. bestHash, bestHeight, err = b.cfg.ChainSource.GetBestBlock() if err != nil { - return false, err + return false, 0, err } // If the wallet hasn't yet fully synced to the node's best chain tip, // then we're not yet fully synced. if syncState.Height < bestHeight { - return false, nil + return false, walletBestHeader.Timestamp.Unix(), nil } // If the wallet is on par with the current best chain tip, then we @@ -696,11 +701,11 @@ func (b *BtcWallet) IsSynced() (bool, error) { // order to make a guess based on the current time stamp. blockHeader, err := b.cfg.ChainSource.GetBlockHeader(bestHash) if err != nil { - return false, err + return false, 0, err } // If the timestamp no the best header is more than 2 hours in the // past, then we're not yet synced. minus24Hours := time.Now().Add(-2 * time.Hour) - return !blockHeader.Timestamp.Before(minus24Hours), nil + return !blockHeader.Timestamp.Before(minus24Hours), walletBestHeader.Timestamp.Unix(), nil } diff --git a/lnwallet/interface.go b/lnwallet/interface.go index c80d4479..1f7efe7d 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -198,7 +198,9 @@ type WalletController interface { // IsSynced returns a boolean indicating if from the PoV of the wallet, // it has fully synced to the current best block in the main chain. - IsSynced() (bool, error) + // It also returns an int64 indicating the timestamp of the best block + // known to the wallet, expressed in Unix epoch time + IsSynced() (bool, int64, error) // Start initializes the wallet, making any necessary connections, // starting up required goroutines etc. diff --git a/mock.go b/mock.go index 0d9f64c9..25528042 100644 --- a/mock.go +++ b/mock.go @@ -262,8 +262,8 @@ func (m *mockWalletController) PublishTransaction(tx *wire.MsgTx) error { func (*mockWalletController) SubscribeTransactions() (lnwallet.TransactionSubscription, error) { return nil, nil } -func (*mockWalletController) IsSynced() (bool, error) { - return true, nil +func (*mockWalletController) IsSynced() (bool, int64, error) { + return true, int64(0), nil } func (*mockWalletController) Start() error { return nil diff --git a/rpcserver.go b/rpcserver.go index 2d72aa4a..ae332f1f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -829,7 +829,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, // Creation of channels before the wallet syncs up is currently // disallowed. - isSynced, err := r.server.cc.wallet.IsSynced() + isSynced, _, err := r.server.cc.wallet.IsSynced() if err != nil { return nil, err } @@ -1166,7 +1166,7 @@ func (r *rpcServer) GetInfo(ctx context.Context, return nil, fmt.Errorf("unable to get best block info: %v", err) } - isSynced, err := r.server.cc.wallet.IsSynced() + isSynced, bestHeaderTimestamp, err := r.server.cc.wallet.IsSynced() if err != nil { return nil, fmt.Errorf("unable to sync PoV of the wallet "+ "with current best block in the main chain: %v", err) @@ -1192,17 +1192,18 @@ func (r *rpcServer) GetInfo(ctx context.Context, // TODO(roasbeef): add synced height n stuff return &lnrpc.GetInfoResponse{ - IdentityPubkey: encodedIDPub, - NumPendingChannels: nPendingChannels, - NumActiveChannels: activeChannels, - NumPeers: uint32(len(serverPeers)), - BlockHeight: uint32(bestHeight), - BlockHash: bestHash.String(), - SyncedToChain: isSynced, - Testnet: activeNetParams.Params == &chaincfg.TestNet3Params, - Chains: activeChains, - Uris: uris, - Alias: nodeAnn.Alias.String(), + IdentityPubkey: encodedIDPub, + NumPendingChannels: nPendingChannels, + NumActiveChannels: activeChannels, + NumPeers: uint32(len(serverPeers)), + BlockHeight: uint32(bestHeight), + BlockHash: bestHash.String(), + SyncedToChain: isSynced, + Testnet: activeNetParams.Params == &chaincfg.TestNet3Params, + Chains: activeChains, + Uris: uris, + Alias: nodeAnn.Alias.String(), + BestHeaderTimestamp: int64(bestHeaderTimestamp), }, nil } From 34efb380beab9ccdabecd202b0174c39c66f6526 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 20 Feb 2018 19:15:40 -0800 Subject: [PATCH 18/19] lnwallet: update interface tests due to recent API change --- lnwallet/interface_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 23592c55..94ac3825 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -211,7 +211,7 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet, } select { case <-timeout: - synced, err := w.IsSynced() + synced, _, err := w.IsSynced() if err != nil { return err } @@ -1886,7 +1886,7 @@ func waitForWalletSync(r *rpctest.Harness, w *lnwallet.LightningWallet) error { } // Check for synchronization. - synced, err = w.IsSynced() + synced, _, err = w.IsSynced() if err != nil { return err } From 4449038ae550f9bb6c346deeb1bd2a60c467b39d Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 20 Feb 2018 20:48:53 -0800 Subject: [PATCH 19/19] lnwallet/btcwallet/btcwallet: adds extra double spend case Adds an extra case to the select statement to catch an error produced by btcd. The error is meant to signal that an output was previously spent, which can appear under certain race conditions in spending/broadcasting. This caused our final itest to fail because it would not try to recraft the justice txn. --- lnwallet/btcwallet/btcwallet.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 51a69edd..5ba512db 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -427,6 +427,10 @@ func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx) error { // Output was already spent. return lnwallet.ErrDoubleSpend } + if strings.Contains(err.Error(), "already been spent") { + // Output was already spent. + return lnwallet.ErrDoubleSpend + } if strings.Contains(err.Error(), "orphan transaction") { // Transaction is spending either output that // is missing or already spent.