Merge pull request #1005 from cfromknecht/wallet-recovery

Wallet Recovery
This commit is contained in:
Olaoluwa Osuntokun 2018-04-26 20:21:03 -07:00 committed by GitHub
commit 4ab2bba5c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1106 additions and 448 deletions

4
Gopkg.lock generated

@ -225,7 +225,7 @@
"walletdb/bdb", "walletdb/bdb",
"wtxmgr" "wtxmgr"
] ]
revision = "41f0e0145c5a4d1b90105dcccf0cbe5d22372eed" revision = "ccd48bb4720f2baeaa795cac81264f6ced2da4c7"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -359,6 +359,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "25f631ecffcdb9df5a8675cd1fee102ee89a39a64dd8a467667a257d35f816e0" inputs-digest = "7edcc418d8e2f483001838e05dc77e3fe613273418f8ddf7efd5cee6df68c6a8"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

@ -64,7 +64,7 @@
[[constraint]] [[constraint]]
name = "github.com/roasbeef/btcwallet" name = "github.com/roasbeef/btcwallet"
revision = "41f0e0145c5a4d1b90105dcccf0cbe5d22372eed" revision = "ccd48bb4720f2baeaa795cac81264f6ced2da4c7"
[[constraint]] [[constraint]]
name = "github.com/tv42/zbase32" name = "github.com/tv42/zbase32"

@ -126,12 +126,12 @@ var (
) )
var ( var (
// bitcoinGenesisDate is the timestamp of Bitcoin's genesis block. // BitcoinGenesisDate is the timestamp of Bitcoin's genesis block.
// We'll use this value in order to create a compact birthday for the // We'll use this value in order to create a compact birthday for the
// seed. The birthday will be interested as the number of days since // seed. The birthday will be interested as the number of days since
// the genesis date. We refer to this time period as ABE (after Bitcoin // the genesis date. We refer to this time period as ABE (after Bitcoin
// era). // era).
bitcoinGenesisDate = time.Unix(1231006505, 0) BitcoinGenesisDate = time.Unix(1231006505, 0)
) )
// CipherSeed is a fully decoded instance of the aezeed scheme. At a high // CipherSeed is a fully decoded instance of the aezeed scheme. At a high
@ -201,7 +201,7 @@ func New(internalVersion uint8, entropy *[EntropySize]byte,
// To compute our "birthday", we'll first use the current time, then // To compute our "birthday", we'll first use the current time, then
// subtract that from the Bitcoin Genesis Date. We'll then convert that // subtract that from the Bitcoin Genesis Date. We'll then convert that
// value to days. // value to days.
birthday := uint16(now.Sub(bitcoinGenesisDate) / (time.Hour * 24)) birthday := uint16(now.Sub(BitcoinGenesisDate) / (time.Hour * 24))
c := &CipherSeed{ c := &CipherSeed{
InternalVersion: internalVersion, InternalVersion: internalVersion,
@ -384,6 +384,13 @@ func (c *CipherSeed) Encipher(pass []byte) ([EncipheredCipherSeedSize]byte, erro
return c.encipher(pass) return c.encipher(pass)
} }
// BirthdayTime returns the cipher seed's internal birthday format as a native
// golang Time struct.
func (c *CipherSeed) BirthdayTime() time.Time {
offset := time.Duration(c.Birthday) * 24 * time.Hour
return BitcoinGenesisDate.Add(offset)
}
// Mnemonic is a 24-word passphrase as of CipherSeedVersion zero. This // Mnemonic is a 24-word passphrase as of CipherSeedVersion zero. This
// passphrase encodes an encrypted seed triple (version, birthday, entropy). // passphrase encodes an encrypted seed triple (version, birthday, entropy).
// Additionally, we also encode the salt used with scrypt to derive the key // Additionally, we also encode the salt used with scrypt to derive the key

@ -33,7 +33,7 @@ var (
version0TestVectors = []TestVector{ version0TestVectors = []TestVector{
{ {
version: 0, version: 0,
time: bitcoinGenesisDate, time: BitcoinGenesisDate,
entropy: testEntropy, entropy: testEntropy,
salt: testSalt, salt: testSalt,
password: []byte{}, password: []byte{},
@ -466,7 +466,7 @@ func TestSeedEncodeDecode(t *testing.T) {
now := time.Unix(nowInt, 0) now := time.Unix(nowInt, 0)
seed := CipherSeed{ seed := CipherSeed{
InternalVersion: version, InternalVersion: version,
Birthday: uint16(now.Sub(bitcoinGenesisDate) / (time.Hour * 24)), Birthday: uint16(now.Sub(BitcoinGenesisDate) / (time.Hour * 24)),
Entropy: entropy, Entropy: entropy,
} }

@ -112,7 +112,8 @@ type chainControl struct {
// branches of chainControl instances exist: one backed by a running btcd // branches of chainControl instances exist: one backed by a running btcd
// full-node, and the other backed by a running neutrino light client instance. // full-node, and the other backed by a running neutrino light client instance.
func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
privateWalletPw, publicWalletPw []byte) (*chainControl, func(), error) { privateWalletPw, publicWalletPw []byte, birthday time.Time,
recoveryWindow uint32) (*chainControl, func(), error) {
// Set the RPC config from the "home" chain. Multi-chain isn't yet // Set the RPC config from the "home" chain. Multi-chain isn't yet
// active, so we'll restrict usage to a particular chain for now. // active, so we'll restrict usage to a particular chain for now.
@ -152,12 +153,14 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
} }
walletConfig := &btcwallet.Config{ walletConfig := &btcwallet.Config{
PrivatePass: privateWalletPw, PrivatePass: privateWalletPw,
PublicPass: publicWalletPw, PublicPass: publicWalletPw,
DataDir: homeChainConfig.ChainDir, Birthday: birthday,
NetParams: activeNetParams.Params, RecoveryWindow: recoveryWindow,
FeeEstimator: cc.feeEstimator, DataDir: homeChainConfig.ChainDir,
CoinType: activeNetParams.CoinType, NetParams: activeNetParams.Params,
FeeEstimator: cc.feeEstimator,
CoinType: activeNetParams.CoinType,
} }
var ( var (
@ -243,7 +246,9 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// Finally, we'll set the chain source for btcwallet, and // Finally, we'll set the chain source for btcwallet, and
// create our clean up function which simply closes the // create our clean up function which simply closes the
// database. // database.
walletConfig.ChainSource = chain.NewNeutrinoClient(svc) walletConfig.ChainSource = chain.NewNeutrinoClient(
activeNetParams.Params, svc,
)
cleanUp = func() { cleanUp = func() {
svc.Stop() svc.Stop()
nodeDatabase.Close() nodeDatabase.Close()

@ -35,6 +35,8 @@ import (
// TODO(roasbeef): expose all fee conf targets // TODO(roasbeef): expose all fee conf targets
const defaultRecoveryWindow int32 = 250
func printJSON(resp interface{}) { func printJSON(resp interface{}) {
b, err := json.Marshal(resp) b, err := json.Marshal(resp)
if err != nil { if err != nil {
@ -1132,6 +1134,7 @@ mnemonicCheck:
var ( var (
cipherSeedMnemonic []string cipherSeedMnemonic []string
aezeedPass []byte aezeedPass []byte
recoveryWindow int32
) )
if hasMnemonic { if hasMnemonic {
// We'll now prompt the user to enter in their 24-word // We'll now prompt the user to enter in their 24-word
@ -1170,7 +1173,37 @@ mnemonicCheck:
aezeedPass = []byte(passphrase) aezeedPass = []byte(passphrase)
fmt.Println() for {
fmt.Println()
fmt.Printf("Input an optional address look-ahead "+
"used to scan for used keys (default %d): ",
defaultRecoveryWindow)
reader := bufio.NewReader(os.Stdin)
answer, err := reader.ReadString('\n')
if err != nil {
return err
}
fmt.Println()
answer = strings.TrimSpace(answer)
if len(answer) == 0 {
recoveryWindow = defaultRecoveryWindow
break
}
lookAhead, err := strconv.Atoi(answer)
if err != nil {
fmt.Println("Unable to parse recovery "+
"window: %v", err)
continue
}
recoveryWindow = int32(lookAhead)
break
}
} else { } else {
// Otherwise, if the user doesn't have a mnemonic that they // Otherwise, if the user doesn't have a mnemonic that they
// want to use, we'll generate a fresh one with the GenSeed // want to use, we'll generate a fresh one with the GenSeed
@ -1247,6 +1280,7 @@ mnemonicCheck:
WalletPassword: pw1, WalletPassword: pw1,
CipherSeedMnemonic: cipherSeedMnemonic, CipherSeedMnemonic: cipherSeedMnemonic,
AezeedPassphrase: aezeedPass, AezeedPassphrase: aezeedPass,
RecoveryWindow: recoveryWindow,
} }
if _, err := client.InitWallet(ctxb, req); err != nil { if _, err := client.InitWallet(ctxb, req); err != nil {
return err return err
@ -1265,6 +1299,16 @@ var unlockCommand = cli.Command{
able to carry out its duties. An exception is if a user is running with able to carry out its duties. An exception is if a user is running with
--noencryptwallet, then a default passphrase will be used. --noencryptwallet, then a default passphrase will be used.
`, `,
Flags: []cli.Flag{
cli.IntFlag{
Name: "recovery_window",
Usage: "address lookahead to resume recovery rescan, " +
"value should be non-zero -- To recover all " +
"funds, this should be greater than the " +
"maximum number of consecutive, unused " +
"addresses ever generated by the wallet.",
},
},
Action: actionDecorator(unlock), Action: actionDecorator(unlock),
} }
@ -1280,8 +1324,26 @@ func unlock(ctx *cli.Context) error {
} }
fmt.Println() fmt.Println()
args := ctx.Args()
// Parse the optional recovery window if it is specified. By default,
// the recovery window will be 0, indicating no lookahead should be
// used.
var recoveryWindow int32
switch {
case ctx.IsSet("recovery_window"):
recoveryWindow = int32(ctx.Int64("recovery_window"))
case args.Present():
window, err := strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return err
}
recoveryWindow = int32(window)
}
req := &lnrpc.UnlockWalletRequest{ req := &lnrpc.UnlockWalletRequest{
WalletPassword: pw, WalletPassword: pw,
RecoveryWindow: recoveryWindow,
} }
_, err = client.UnlockWallet(ctxb, req) _, err = client.UnlockWallet(ctxb, req)
if err != nil { if err != nil {

@ -6,6 +6,7 @@ import (
"math/rand" "math/rand"
"os" "os"
"testing" "testing"
"time"
"github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
@ -42,11 +43,13 @@ func createTestBtcWallet(coinType uint32) (func(), *wallet.Wallet, error) {
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
loader := wallet.NewLoader(&chaincfg.SimNetParams, tempDir) loader := wallet.NewLoader(&chaincfg.SimNetParams, tempDir, 0)
pass := []byte("test") pass := []byte("test")
baseWallet, err := loader.CreateNewWallet(pass, pass, testHDSeed[:]) baseWallet, err := loader.CreateNewWallet(
pass, pass, testHDSeed[:], time.Time{},
)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

98
lnd.go

@ -207,19 +207,35 @@ func lndMain() error {
defer macaroonService.Close() defer macaroonService.Close()
} }
var (
privateWalletPw = []byte("hello")
publicWalletPw = []byte("public")
birthday time.Time
recoveryWindow uint32
)
// We wait until the user provides a password over RPC. In case lnd is // We wait until the user provides a password over RPC. In case lnd is
// started with the --noencryptwallet flag, we use the default password // started with the --noencryptwallet flag, we use the default password
// "hello" for wallet encryption. // "hello" for wallet encryption.
privateWalletPw := []byte("hello")
publicWalletPw := []byte("public")
if !cfg.NoEncryptWallet { if !cfg.NoEncryptWallet {
privateWalletPw, publicWalletPw, err = waitForWalletPassword( walletInitParams, err := waitForWalletPassword(
cfg.RPCListeners, cfg.RESTListeners, serverOpts, proxyOpts, cfg.RPCListeners, cfg.RESTListeners, serverOpts,
tlsConf, macaroonService, proxyOpts, tlsConf, macaroonService,
) )
if err != nil { if err != nil {
return err return err
} }
privateWalletPw = walletInitParams.Password
publicWalletPw = walletInitParams.Password
birthday = walletInitParams.Birthday
recoveryWindow = walletInitParams.RecoveryWindow
if recoveryWindow > 0 {
ltndLog.Infof("Wallet recovery mode enabled with "+
"address lookahead of %d addresses",
recoveryWindow)
}
} }
if !cfg.NoMacaroons { if !cfg.NoMacaroons {
@ -251,8 +267,10 @@ func lndMain() error {
// With the information parsed from the configuration, create valid // With the information parsed from the configuration, create valid
// instances of the pertinent interfaces required to operate the // instances of the pertinent interfaces required to operate the
// Lightning Network Daemon. // Lightning Network Daemon.
activeChainControl, chainCleanUp, err := newChainControlFromConfig(cfg, activeChainControl, chainCleanUp, err := newChainControlFromConfig(
chanDB, privateWalletPw, publicWalletPw) cfg, chanDB, privateWalletPw, publicWalletPw, birthday,
recoveryWindow,
)
if err != nil { if err != nil {
fmt.Printf("unable to create chain control: %v\n", err) fmt.Printf("unable to create chain control: %v\n", err)
return err return err
@ -827,12 +845,30 @@ func genMacaroons(ctx context.Context, svc *macaroons.Service,
return nil return nil
} }
// WalletUnlockParams holds the variables used to parameterize the unlocking of
// lnd's wallet after it has already been created.
type WalletUnlockParams struct {
// Password is the public and private wallet passphrase.
Password []byte
// Birthday specifies the approximate time that this wallet was created.
// This is used to bound any rescans on startup.
Birthday time.Time
// RecoveryWindow specifies the address lookahead when entering recovery
// mode. A recovery will be attempted if this value is non-zero.
RecoveryWindow uint32
}
// waitForWalletPassword will spin up gRPC and REST endpoints for the // waitForWalletPassword will spin up gRPC and REST endpoints for the
// WalletUnlocker server, and block until a password is provided by // WalletUnlocker server, and block until a password is provided by
// the user to this RPC server. // the user to this RPC server.
func waitForWalletPassword(grpcEndpoints, restEndpoints []string, func waitForWalletPassword(
serverOpts []grpc.ServerOption, proxyOpts []grpc.DialOption, grpcEndpoints, restEndpoints []string,
tlsConf *tls.Config, macaroonService *macaroons.Service) ([]byte, []byte, error) { serverOpts []grpc.ServerOption,
proxyOpts []grpc.DialOption,
tlsConf *tls.Config,
macaroonService *macaroons.Service) (*WalletUnlockParams, error) {
// Set up a new PasswordService, which will listen // Set up a new PasswordService, which will listen
// for passwords provided over RPC. // for passwords provided over RPC.
@ -857,7 +893,7 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
if err != nil { if err != nil {
ltndLog.Errorf("password RPC server unable to listen on %s", ltndLog.Errorf("password RPC server unable to listen on %s",
grpcEndpoint) grpcEndpoint)
return nil, nil, err return nil, err
} }
defer lis.Close() defer lis.Close()
@ -879,7 +915,7 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
err := lnrpc.RegisterWalletUnlockerHandlerFromEndpoint(ctx, mux, err := lnrpc.RegisterWalletUnlockerHandlerFromEndpoint(ctx, mux,
grpcEndpoints[0], proxyOpts) grpcEndpoints[0], proxyOpts)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
srv := &http.Server{Handler: mux} srv := &http.Server{Handler: mux}
@ -889,7 +925,7 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
if err != nil { if err != nil {
ltndLog.Errorf("password gRPC proxy unable to listen on %s", ltndLog.Errorf("password gRPC proxy unable to listen on %s",
restEndpoint) restEndpoint)
return nil, nil, err return nil, err
} }
defer lis.Close() defer lis.Close()
@ -920,14 +956,15 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
case initMsg := <-pwService.InitMsgs: case initMsg := <-pwService.InitMsgs:
password := initMsg.Passphrase password := initMsg.Passphrase
cipherSeed := initMsg.WalletSeed cipherSeed := initMsg.WalletSeed
recoveryWindow := initMsg.RecoveryWindow
// Before we proceed, we'll check the internal version of the // Before we proceed, we'll check the internal version of the
// seed. If it's greater than the current key derivation // seed. If it's greater than the current key derivation
// version, then we'll return an error as we don't understand // version, then we'll return an error as we don't understand
// this. // this.
if cipherSeed.InternalVersion != keychain.KeyDerivationVersion { if cipherSeed.InternalVersion != keychain.KeyDerivationVersion {
return nil, nil, fmt.Errorf("invalid internal seed "+ return nil, fmt.Errorf("invalid internal seed version "+
"version %v, current version is %v", "%v, current version is %v",
cipherSeed.InternalVersion, cipherSeed.InternalVersion,
keychain.KeyDerivationVersion) keychain.KeyDerivationVersion)
} }
@ -935,31 +972,42 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
netDir := btcwallet.NetworkDir( netDir := btcwallet.NetworkDir(
chainConfig.ChainDir, activeNetParams.Params, chainConfig.ChainDir, activeNetParams.Params,
) )
loader := wallet.NewLoader(activeNetParams.Params, netDir) loader := wallet.NewLoader(
activeNetParams.Params, netDir, uint32(recoveryWindow),
)
// With the seed, we can now use the wallet loader to create // With the seed, we can now use the wallet loader to create
// the wallet, then unload it so it can be opened shortly // the wallet, then unload it so it can be opened shortly
// after. birthday := cipherSeed.BirthdayTime()
// TODO(roasbeef): extend loader to also accept birthday
_, err = loader.CreateNewWallet( _, err = loader.CreateNewWallet(
password, password, cipherSeed.Entropy[:], password, password, cipherSeed.Entropy[:], birthday,
) )
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
if err := loader.UnloadWallet(); err != nil { if err := loader.UnloadWallet(); err != nil {
return nil, nil, err return nil, err
} }
return password, password, nil walletInitParams := &WalletUnlockParams{
Password: password,
Birthday: birthday,
RecoveryWindow: recoveryWindow,
}
return walletInitParams, nil
// The wallet has already been created in the past, and is simply being // The wallet has already been created in the past, and is simply being
// unlocked. So we'll just return these passphrases. // unlocked. So we'll just return these passphrases.
case walletPw := <-pwService.UnlockPasswords: case unlockMsg := <-pwService.UnlockMsgs:
return walletPw, walletPw, nil walletInitParams := &WalletUnlockParams{
Password: unlockMsg.Passphrase,
RecoveryWindow: unlockMsg.RecoveryWindow,
}
return walletInitParams, nil
case <-shutdownChannel: case <-shutdownChannel:
return nil, nil, fmt.Errorf("shutting down") return nil, fmt.Errorf("shutting down")
} }
} }

@ -440,6 +440,180 @@ func completePaymentRequests(ctx context.Context, client lnrpc.LightningClient,
return nil return nil
} }
const (
AddrTypeWitnessPubkeyHash = lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH
AddrTypeNestedPubkeyHash = lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH
)
// testOnchainFundRecovery checks lnd's ability to rescan for onchain outputs
// when providing a valid aezeed that owns outputs on the chain. This test
// performs multiple restorations using the same seed and various recovery
// windows to ensure we detect funds properly.
func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
timeout := time.Duration(time.Second * 15)
ctxb := context.Background()
// First, create a new node with strong passphrase and grab the mnemonic
// used for key derivation. This will bring up Carol with an empty
// wallet, and such that she is synced up.
password := []byte("The Magic Words are Squeamish Ossifrage")
carol, mnemonic, err := net.NewNodeWithSeed(nil, password)
if err != nil {
t.Fatalf("unable to create node with seed; %v", err)
}
err = net.ShutdownNode(carol)
if err != nil {
t.Fatalf("unable to shutdown carol: %v", err)
}
// Create a closure for testing the recovery of Carol's wallet. This
// method takes the expected value of Carol's balance when using the
// given recovery window. Additionally, the caller can specify an action
// to perform on the restored node before the node is shutdown.
restoreCheckBalance := func(expAmount int64, recoveryWindow int32,
fn func(*lntest.HarnessNode)) {
// Restore Carol, passing in the password, mnemonic, and
// desired recovery window.
node, err := net.RestoreNodeWithSeed(
nil, password, mnemonic, recoveryWindow,
)
if err != nil {
t.Fatalf("unable to restore node: %v", err)
}
// Query carol for her current wallet balance.
var currBalance int64
err = lntest.WaitPredicate(func() bool {
req := &lnrpc.WalletBalanceRequest{}
ctxt, _ := context.WithTimeout(ctxb, timeout)
resp, err := node.WalletBalance(ctxt, req)
if err != nil {
t.Fatalf("unable to query wallet balance: %v",
err)
}
// Verify that Carol's balance matches our expected
// amount.
currBalance = resp.ConfirmedBalance
if expAmount != currBalance {
return false
}
return true
}, 15*time.Second)
if err != nil {
t.Fatalf("expected restored node to have %d satoshis, "+
"instead has %d satoshis", expAmount,
currBalance)
}
// If the user provided a callback, execute the commands against
// the restored Carol.
if fn != nil {
fn(node)
}
// Lastly, shutdown this Carol so we can move on to the next
// restoration.
err = net.ShutdownNode(node)
if err != nil {
t.Fatalf("unable to shutdown node: %v", err)
}
}
// Create a closure-factory for building closures that can generate and
// skip a configurable number of addresses, before finally sending coins
// to a next generated address. The returned closure will apply the same
// behavior to both default P2WKH and NP2WKH scopes.
skipAndSend := func(nskip int) func(*lntest.HarnessNode) {
return func(node *lntest.HarnessNode) {
newP2WKHAddrReq := &lnrpc.NewAddressRequest{
Type: AddrTypeWitnessPubkeyHash,
}
newNP2WKHAddrReq := &lnrpc.NewAddressRequest{
Type: AddrTypeNestedPubkeyHash,
}
// Generate and skip the number of addresses requested.
for i := 0; i < nskip; i++ {
ctxt, _ := context.WithTimeout(ctxb, timeout)
_, err = node.NewAddress(ctxt, newP2WKHAddrReq)
if err != nil {
t.Fatalf("unable to generate new "+
"p2wkh address: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
_, err = node.NewAddress(ctxt, newNP2WKHAddrReq)
if err != nil {
t.Fatalf("unable to generate new "+
"np2wkh address: %v", err)
}
}
// Send one BTC to the next P2WKH address.
ctxt, _ := context.WithTimeout(ctxb, timeout)
err = net.SendCoins(
ctxt, btcutil.SatoshiPerBitcoin, node,
)
if err != nil {
t.Fatalf("unable to send coins to node: %v",
err)
}
// And another to the next NP2WKH address.
ctxt, _ = context.WithTimeout(ctxb, timeout)
err = net.SendCoinsNP2WKH(
ctxt, btcutil.SatoshiPerBitcoin, node,
)
if err != nil {
t.Fatalf("unable to send coins to node: %v",
err)
}
}
}
// Restore Carol with a recovery window of 0. Since no coins have been
// sent, her balance should be zero.
//
// After, one BTC is sent to both her first external P2WKH and NP2WKH
// addresses.
restoreCheckBalance(0, 0, skipAndSend(0))
// Check that restoring without a look-ahead results in having no funds
// in the wallet, even though they exist on-chain.
restoreCheckBalance(0, 0, nil)
// Now, check that using a look-ahead of 1 recovers the balance from the
// two transactions above.
//
// After, we will generate and skip 9 P2WKH and NP2WKH addresses, and
// send another BTC to the subsequent 10th address in each derivation
// path.
restoreCheckBalance(2*btcutil.SatoshiPerBitcoin, 1, skipAndSend(9))
// Check that using a recovery window of 9 does not find the two most
// recent txns.
restoreCheckBalance(2*btcutil.SatoshiPerBitcoin, 9, nil)
// Extending our recovery window to 10 should find the most recent
// transactions, leaving the wallet with 4 BTC total.
//
// After, we will skip 19 more addrs, sending to the 20th address past
// our last found address, and repeat the same checks.
restoreCheckBalance(4*btcutil.SatoshiPerBitcoin, 10, skipAndSend(19))
// Check that recovering with a recovery window of 19 fails to find the
// most recent transactions.
restoreCheckBalance(4*btcutil.SatoshiPerBitcoin, 19, nil)
// Ensure that using a recovery window of 20 succeeds.
restoreCheckBalance(6*btcutil.SatoshiPerBitcoin, 20, nil)
}
// testBasicChannelFunding performs a test exercising expected behavior from a // testBasicChannelFunding performs a test exercising expected behavior from a
// basic funding workflow. The test creates a new channel between Alice and // basic funding workflow. The test creates a new channel between Alice and
// Bob, then immediately closes the channel after asserting some expected post // Bob, then immediately closes the channel after asserting some expected post
@ -8808,6 +8982,10 @@ type testCase struct {
} }
var testsCases = []*testCase{ var testsCases = []*testCase{
{
name: "onchain fund recovery",
test: testOnchainFundRecovery,
},
{ {
name: "basic funding flow", name: "basic funding flow",
test: testBasicChannelFunding, test: testBasicChannelFunding,

@ -232,6 +232,13 @@ type InitWalletRequest struct {
// aezeed_passphrase is an optional user provided passphrase that will be used // aezeed_passphrase is an optional user provided passphrase that will be used
// to encrypt the generated aezeed cipher seed. // to encrypt the generated aezeed cipher seed.
AezeedPassphrase []byte `protobuf:"bytes,3,opt,name=aezeed_passphrase,json=aezeedPassphrase,proto3" json:"aezeed_passphrase,omitempty"` AezeedPassphrase []byte `protobuf:"bytes,3,opt,name=aezeed_passphrase,json=aezeedPassphrase,proto3" json:"aezeed_passphrase,omitempty"`
// *
// recovery_window is an optional argument specifying the address lookahead
// when restoring a wallet seed. The recovery window applies to each
// invdividual branch of the BIP44 derivation paths. Supplying a recovery
// window of zero indicates that no addresses should be recovered, such after
// the first initialization of the wallet.
RecoveryWindow int32 `protobuf:"varint,4,opt,name=recovery_window,json=recoveryWindow" json:"recovery_window,omitempty"`
} }
func (m *InitWalletRequest) Reset() { *m = InitWalletRequest{} } func (m *InitWalletRequest) Reset() { *m = InitWalletRequest{} }
@ -260,6 +267,13 @@ func (m *InitWalletRequest) GetAezeedPassphrase() []byte {
return nil return nil
} }
func (m *InitWalletRequest) GetRecoveryWindow() int32 {
if m != nil {
return m.RecoveryWindow
}
return 0
}
type InitWalletResponse struct { type InitWalletResponse struct {
} }
@ -274,6 +288,13 @@ type UnlockWalletRequest struct {
// will be required to decrypt on-disk material that the daemon requires to // will be required to decrypt on-disk material that the daemon requires to
// function properly. // function properly.
WalletPassword []byte `protobuf:"bytes,1,opt,name=wallet_password,json=walletPassword,proto3" json:"wallet_password,omitempty"` WalletPassword []byte `protobuf:"bytes,1,opt,name=wallet_password,json=walletPassword,proto3" json:"wallet_password,omitempty"`
// *
// recovery_window is an optional argument specifying the address lookahead
// when restoring a wallet seed. The recovery window applies to each
// invdividual branch of the BIP44 derivation paths. Supplying a recovery
// window of zero indicates that no addresses should be recovered, such after
// the first initialization of the wallet.
RecoveryWindow int32 `protobuf:"varint,2,opt,name=recovery_window,json=recoveryWindow" json:"recovery_window,omitempty"`
} }
func (m *UnlockWalletRequest) Reset() { *m = UnlockWalletRequest{} } func (m *UnlockWalletRequest) Reset() { *m = UnlockWalletRequest{} }
@ -288,6 +309,13 @@ func (m *UnlockWalletRequest) GetWalletPassword() []byte {
return nil return nil
} }
func (m *UnlockWalletRequest) GetRecoveryWindow() int32 {
if m != nil {
return m.RecoveryWindow
}
return 0
}
type UnlockWalletResponse struct { type UnlockWalletResponse struct {
} }
@ -6351,356 +6379,358 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 5611 bytes of a gzipped FileDescriptorProto // 5640 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0xcb, 0x73, 0x1c, 0xc9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0x4d, 0x90, 0x1c, 0xc9,
0x71, 0x37, 0x7b, 0x30, 0x78, 0x4c, 0xce, 0x60, 0x00, 0x14, 0x40, 0x70, 0x38, 0xe4, 0x72, 0xb9, 0x55, 0xbf, 0xaa, 0xa7, 0xe7, 0xa3, 0x5f, 0xf7, 0xf4, 0xcc, 0xe4, 0x8c, 0x46, 0xad, 0x96, 0x56,
0xad, 0x0d, 0x91, 0x1f, 0xbe, 0x35, 0xc1, 0x85, 0xa4, 0xf5, 0x6a, 0x69, 0x4b, 0xe6, 0x1b, 0x2b, 0xab, 0x2d, 0x6f, 0x58, 0xfa, 0xcf, 0x7f, 0xd1, 0x68, 0xc7, 0xf6, 0xb2, 0x5e, 0x81, 0x8d, 0xbe,
0x71, 0x29, 0xa8, 0x41, 0x8a, 0xb6, 0x64, 0x7b, 0xd4, 0x98, 0x29, 0x0c, 0x7a, 0xd9, 0xd3, 0xdd, 0x67, 0x6d, 0xad, 0x3c, 0xae, 0x91, 0x2c, 0xb0, 0x81, 0x76, 0x4d, 0x77, 0x4e, 0x4f, 0xad, 0xaa,
0xdb, 0xdd, 0x03, 0x70, 0x76, 0xcd, 0x08, 0xbf, 0xc2, 0x27, 0x2b, 0x7c, 0xb0, 0x2f, 0xb2, 0xc3, 0xab, 0x6a, 0xab, 0xaa, 0x67, 0xd4, 0xbb, 0x28, 0x82, 0xaf, 0xe0, 0x84, 0x83, 0x03, 0x5c, 0x4c,
0xe1, 0x08, 0xe9, 0x62, 0x1f, 0x7c, 0xf4, 0x49, 0xf6, 0x3f, 0xe0, 0x08, 0x87, 0x0f, 0x7b, 0x52, 0x04, 0x41, 0x84, 0x7d, 0x81, 0x03, 0x47, 0x4e, 0x86, 0x1b, 0x27, 0x22, 0x08, 0x0e, 0x7b, 0x72,
0xf8, 0xe6, 0xc7, 0xc1, 0x56, 0xf8, 0xe2, 0x08, 0x5f, 0x7c, 0x70, 0x38, 0x32, 0xeb, 0xd1, 0x55, 0x70, 0xe3, 0xe3, 0x00, 0x0e, 0x2e, 0x44, 0x70, 0xe1, 0x40, 0x10, 0xef, 0xe5, 0x47, 0x65, 0x56,
0xdd, 0x0d, 0x92, 0x7a, 0xd8, 0xb7, 0xa9, 0x5f, 0x65, 0x67, 0xbd, 0x32, 0xb3, 0x32, 0xb3, 0xaa, 0xd5, 0x48, 0xf2, 0x07, 0xdc, 0x3a, 0x7f, 0xf9, 0xea, 0xe5, 0xd7, 0x7b, 0x2f, 0xdf, 0x7b, 0x99,
0x06, 0x5a, 0x69, 0x32, 0xbc, 0x96, 0xa4, 0x71, 0x1e, 0xb3, 0xf9, 0x30, 0x4a, 0x93, 0x61, 0xff, 0xd9, 0xd0, 0x4a, 0x93, 0xe1, 0xb5, 0x24, 0x8d, 0xf3, 0x98, 0xcd, 0x87, 0x51, 0x9a, 0x0c, 0xfb,
0xe2, 0x38, 0x8e, 0xc7, 0x21, 0xdf, 0xf6, 0x93, 0x60, 0xdb, 0x8f, 0xa2, 0x38, 0xf7, 0xf3, 0x20, 0x17, 0xc7, 0x71, 0x3c, 0x0e, 0xf9, 0xb6, 0x9f, 0x04, 0xdb, 0x7e, 0x14, 0xc5, 0xb9, 0x9f, 0x07,
0x8e, 0x32, 0x41, 0xe4, 0x7e, 0x1b, 0xba, 0xf7, 0x79, 0xb4, 0xcf, 0xf9, 0xc8, 0xe3, 0x1f, 0x4d, 0x71, 0x94, 0x09, 0x22, 0xf7, 0xdb, 0xd0, 0xbd, 0xcf, 0xa3, 0x7d, 0xce, 0x47, 0x1e, 0xff, 0x68,
0x79, 0x96, 0xb3, 0xff, 0x0f, 0x6b, 0x3e, 0xff, 0x98, 0xf3, 0xd1, 0x20, 0xf1, 0xb3, 0x2c, 0x39, 0xca, 0xb3, 0x9c, 0xfd, 0x7f, 0x58, 0xf3, 0xf9, 0xc7, 0x9c, 0x8f, 0x06, 0x89, 0x9f, 0x65, 0xc9,
0x4a, 0xfd, 0x8c, 0xf7, 0x9c, 0xcb, 0xce, 0xd5, 0x8e, 0xb7, 0x2a, 0x2a, 0xf6, 0x34, 0xce, 0xde, 0x51, 0xea, 0x67, 0xbc, 0xe7, 0x5c, 0x76, 0xae, 0x76, 0xbc, 0x55, 0x51, 0xb1, 0xa7, 0x71, 0xf6,
0x80, 0x4e, 0x86, 0xa4, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf5, 0x1a, 0x44, 0xd7, 0x46, 0xec, 0xae, 0x06, 0x74, 0x32, 0x24, 0xe5, 0x51, 0x9e, 0xc6, 0xc9, 0xac, 0xd7, 0x20, 0xba, 0x36, 0x62, 0x77,
0x80, 0xdc, 0x10, 0x56, 0x74, 0x0b, 0x59, 0x12, 0x47, 0x19, 0x67, 0xd7, 0x61, 0x63, 0x18, 0x24, 0x05, 0xe4, 0x86, 0xb0, 0xa2, 0x5b, 0xc8, 0x92, 0x38, 0xca, 0x38, 0xbb, 0x0e, 0x1b, 0xc3, 0x20,
0x47, 0x3c, 0x1d, 0xd0, 0xc7, 0x93, 0x88, 0x4f, 0xe2, 0x28, 0x18, 0xf6, 0x9c, 0xcb, 0x73, 0x57, 0x39, 0xe2, 0xe9, 0x80, 0x3e, 0x9e, 0x44, 0x7c, 0x12, 0x47, 0xc1, 0xb0, 0xe7, 0x5c, 0x9e, 0xbb,
0x5b, 0x1e, 0x13, 0x75, 0xf8, 0xc5, 0x07, 0xb2, 0x86, 0x5d, 0x81, 0x15, 0x1e, 0x09, 0x9c, 0x8f, 0xda, 0xf2, 0x98, 0xa8, 0xc3, 0x2f, 0x3e, 0x90, 0x35, 0xec, 0x0a, 0xac, 0xf0, 0x48, 0xe0, 0x7c,
0xe8, 0x2b, 0xd9, 0x54, 0xb7, 0x80, 0xf1, 0x03, 0xf7, 0x4f, 0x1c, 0x58, 0x7b, 0x3f, 0x0a, 0xf2, 0x44, 0x5f, 0xc9, 0xa6, 0xba, 0x05, 0x8c, 0x1f, 0xb8, 0x7f, 0xe3, 0xc0, 0xda, 0xfb, 0x51, 0x90,
0x27, 0x7e, 0x18, 0xf2, 0x5c, 0x8d, 0xe9, 0x0a, 0xac, 0x9c, 0x10, 0x40, 0x63, 0x3a, 0x89, 0xd3, 0x3f, 0xf1, 0xc3, 0x90, 0xe7, 0x6a, 0x4c, 0x57, 0x60, 0xe5, 0x84, 0x00, 0x1a, 0xd3, 0x49, 0x9c,
0x91, 0x1c, 0x51, 0x57, 0xc0, 0x7b, 0x12, 0x3d, 0xb5, 0x67, 0x8d, 0x53, 0x7b, 0x56, 0x3b, 0x5d, 0x8e, 0xe4, 0x88, 0xba, 0x02, 0xde, 0x93, 0xe8, 0xa9, 0x3d, 0x6b, 0x9c, 0xda, 0xb3, 0xda, 0xe9,
0x73, 0xf5, 0xd3, 0xe5, 0x6e, 0x00, 0x33, 0x3b, 0x27, 0xa6, 0xc3, 0xfd, 0x12, 0xac, 0x3f, 0x8e, 0x9a, 0x3b, 0x65, 0xba, 0xae, 0xc0, 0x4a, 0xca, 0x87, 0xf1, 0x31, 0x4f, 0x67, 0x83, 0x93, 0x20,
0xc2, 0x78, 0xf8, 0xf4, 0x27, 0xeb, 0xb4, 0xbb, 0x09, 0x1b, 0xf6, 0xf7, 0x92, 0xef, 0x77, 0x1b, 0x1a, 0xc5, 0x27, 0xbd, 0xe6, 0x65, 0xe7, 0xea, 0xbc, 0xd7, 0x55, 0xf0, 0x13, 0x42, 0xdd, 0x0d,
0xd0, 0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x71, 0xc9, 0x59, 0x0f, 0x16, 0xf3, 0x67, 0x83, 0x23, 0x60, 0xe6, 0x28, 0xc4, 0xbc, 0xb9, 0x63, 0x58, 0x7f, 0x1c, 0x85, 0xf1, 0xf0, 0xe9, 0x4f, 0x38,
0x3f, 0x3b, 0x22, 0x46, 0x2d, 0x4f, 0x15, 0xd9, 0x26, 0x2c, 0xf8, 0x93, 0x78, 0x1a, 0xe5, 0x34, 0xba, 0x9a, 0xe6, 0x1b, 0xb5, 0xcd, 0x6f, 0xc2, 0x86, 0xdd, 0x90, 0xec, 0xc0, 0x77, 0x1b, 0xd0,
0xab, 0x73, 0x9e, 0x2c, 0xb1, 0xb7, 0x60, 0x2d, 0x9a, 0x4e, 0x06, 0xc3, 0x38, 0x3a, 0x0c, 0xd2, 0x7e, 0x94, 0xfa, 0x51, 0xe6, 0x0f, 0x51, 0x88, 0x58, 0x0f, 0x16, 0xf3, 0x67, 0x83, 0x23, 0x3f,
0x89, 0x10, 0x1c, 0x1a, 0xdc, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, 0x3b, 0xa2, 0x16, 0x5b, 0x9e, 0x2a, 0xb2, 0x4d, 0x58, 0xf0, 0x27, 0xf1, 0x34, 0xca, 0xa9, 0x85,
0x26, 0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, 0x39, 0x4f, 0x96, 0xd8, 0x5b, 0xb0, 0x16, 0x4d, 0x27, 0x83, 0x61, 0x1c, 0x1d, 0x06, 0xe9, 0x44,
0x85, 0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, 0x88, 0x22, 0x4d, 0xd7, 0xbc, 0x57, 0xad, 0x60, 0x97, 0x00, 0x0e, 0xb0, 0x1b, 0xa2, 0x89, 0x26,
0xaa, 0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x67, 0xa1, 0x35, 0x61, 0x20, 0xcc, 0x85, 0x8e, 0x2c, 0xf1, 0x60, 0x7c, 0x94, 0xf7, 0xe6, 0x89, 0x91, 0x85,
0x3b, 0xe2, 0x59, 0x3e, 0xf0, 0x47, 0xa3, 0x94, 0x67, 0x19, 0xcf, 0x7a, 0x4b, 0xb4, 0x74, 0x25, 0x21, 0x8f, 0x3c, 0x98, 0xf0, 0x41, 0x96, 0xfb, 0x93, 0xa4, 0xb7, 0x40, 0xbd, 0x31, 0x10, 0xaa,
0xd4, 0xed, 0xc1, 0xe6, 0x7d, 0x9e, 0x1b, 0xb3, 0x93, 0xc9, 0x69, 0x77, 0x1f, 0x00, 0x33, 0xe0, 0x8f, 0x73, 0x3f, 0x1c, 0x1c, 0x72, 0x9e, 0xf5, 0x16, 0x65, 0xbd, 0x46, 0xd8, 0x67, 0xa1, 0x3b,
0x3b, 0x3c, 0xf7, 0x83, 0x30, 0x63, 0xef, 0x40, 0x27, 0x37, 0x88, 0x49, 0x54, 0xdb, 0x3b, 0xec, 0xe2, 0x59, 0x3e, 0xf0, 0x47, 0xa3, 0x94, 0x67, 0x19, 0xcf, 0x7a, 0x4b, 0x24, 0x0c, 0x25, 0xd4,
0x1a, 0xe9, 0xd8, 0x35, 0xe3, 0x03, 0xcf, 0xa2, 0x73, 0xff, 0xcb, 0x81, 0xf6, 0x3e, 0x8f, 0xb4, 0xed, 0xc1, 0xe6, 0x7d, 0x9e, 0x1b, 0xb3, 0x93, 0xc9, 0xf5, 0x71, 0x1f, 0x00, 0x33, 0xe0, 0x3b,
0x76, 0x31, 0x68, 0x62, 0x4f, 0xe4, 0x4a, 0xd2, 0x6f, 0xf6, 0x3a, 0xb4, 0xa9, 0x77, 0x59, 0x9e, 0x3c, 0xf7, 0x83, 0x30, 0x63, 0xef, 0x40, 0x27, 0x37, 0x88, 0x49, 0xf8, 0xdb, 0x3b, 0xec, 0x1a,
0x06, 0xd1, 0x98, 0x96, 0xa0, 0xe5, 0x01, 0x42, 0xfb, 0x84, 0xb0, 0x55, 0x98, 0xf3, 0x27, 0x39, 0x69, 0xed, 0x35, 0xe3, 0x03, 0xcf, 0xa2, 0x73, 0xff, 0xcb, 0x81, 0xf6, 0x3e, 0x8f, 0xb4, 0xbe,
0x4d, 0xfc, 0x9c, 0x87, 0x3f, 0x51, 0xef, 0x12, 0x7f, 0x36, 0xe1, 0x51, 0x5e, 0x4c, 0x76, 0xc7, 0x32, 0x68, 0x62, 0x4f, 0xe4, 0x92, 0xd3, 0x6f, 0xf6, 0x3a, 0xb4, 0xa9, 0x77, 0x59, 0x9e, 0x06,
0x6b, 0x4b, 0x6c, 0x17, 0x67, 0xfb, 0x1a, 0xac, 0x9b, 0x24, 0x8a, 0xfb, 0x3c, 0x71, 0x5f, 0x33, 0xd1, 0x98, 0x96, 0xa0, 0xe5, 0x01, 0x42, 0xfb, 0x84, 0xb0, 0x55, 0x98, 0xf3, 0x27, 0x39, 0x4d,
0x28, 0x65, 0x23, 0x57, 0x60, 0x45, 0xd1, 0xa7, 0xa2, 0xb3, 0x34, 0xfd, 0x2d, 0xaf, 0x2b, 0x61, 0xfc, 0x9c, 0x87, 0x3f, 0x51, 0x93, 0x13, 0x7f, 0x36, 0xe1, 0x51, 0x5e, 0x4c, 0x76, 0xc7, 0x6b,
0x35, 0x84, 0xab, 0xb0, 0x7a, 0x18, 0x44, 0x7e, 0x38, 0x18, 0x86, 0xf9, 0xf1, 0x60, 0xc4, 0xc3, 0x4b, 0x6c, 0x17, 0x67, 0xfb, 0x1a, 0xac, 0x9b, 0x24, 0x8a, 0xfb, 0x3c, 0x71, 0x5f, 0x33, 0x28,
0xdc, 0xa7, 0x85, 0x98, 0xf7, 0xba, 0x84, 0xdf, 0x0e, 0xf3, 0xe3, 0x3b, 0x88, 0xba, 0x7f, 0xe4, 0x65, 0x23, 0x57, 0x60, 0x45, 0xd1, 0xa7, 0xa2, 0xb3, 0x34, 0xfd, 0x2d, 0xaf, 0x2b, 0x61, 0x35,
0x40, 0x47, 0x0c, 0x5e, 0x2a, 0xfe, 0x9b, 0xb0, 0xac, 0xda, 0xe0, 0x69, 0x1a, 0xa7, 0x52, 0x0e, 0x84, 0xab, 0xb0, 0x7a, 0x18, 0x44, 0x7e, 0x38, 0x18, 0x86, 0xf9, 0xf1, 0x60, 0xc4, 0xc3, 0xdc,
0x6d, 0x90, 0x6d, 0xc1, 0xaa, 0x02, 0x92, 0x94, 0x07, 0x13, 0x7f, 0xcc, 0xa5, 0xb6, 0x57, 0x70, 0xa7, 0x85, 0x98, 0xf7, 0xba, 0x84, 0xdf, 0x0e, 0xf3, 0xe3, 0x3b, 0x88, 0xba, 0x7f, 0xe4, 0x40,
0xb6, 0x53, 0x70, 0x4c, 0xe3, 0x69, 0x2e, 0x54, 0xaf, 0xbd, 0xd3, 0x91, 0x0b, 0xe3, 0x21, 0xe6, 0x47, 0x0c, 0x5e, 0x9a, 0x92, 0x37, 0x61, 0x59, 0xb5, 0xc1, 0xd3, 0x34, 0x4e, 0xa5, 0x1c, 0xda,
0xd9, 0x24, 0xee, 0xf7, 0x1c, 0xe8, 0xdc, 0x3e, 0xf2, 0xa3, 0x88, 0x87, 0x7b, 0x71, 0x10, 0xe5, 0x20, 0xdb, 0x82, 0x55, 0x05, 0x24, 0x29, 0x0f, 0x26, 0xfe, 0x98, 0x4b, 0xfb, 0x51, 0xc1, 0xd9,
0xec, 0x3a, 0xb0, 0xc3, 0x69, 0x34, 0x0a, 0xa2, 0xf1, 0x20, 0x7f, 0x16, 0x8c, 0x06, 0x07, 0xb3, 0x4e, 0xc1, 0x31, 0x8d, 0xa7, 0xb9, 0x50, 0xe6, 0xf6, 0x4e, 0x47, 0x2e, 0x8c, 0x87, 0x98, 0x67,
0x9c, 0x67, 0x62, 0x89, 0x76, 0xcf, 0x78, 0x35, 0x75, 0xec, 0x2d, 0x58, 0xb5, 0xd0, 0x2c, 0x4f, 0x93, 0xb8, 0xdf, 0x73, 0xa0, 0x73, 0xfb, 0xc8, 0x8f, 0x22, 0x1e, 0xee, 0xc5, 0x41, 0x94, 0xb3,
0xc5, 0xba, 0xed, 0x9e, 0xf1, 0x2a, 0x35, 0x28, 0xf8, 0xf1, 0x34, 0x4f, 0xa6, 0xf9, 0x20, 0x88, 0xeb, 0xc0, 0x0e, 0xa7, 0xd1, 0x28, 0x88, 0xc6, 0x83, 0xfc, 0x59, 0x30, 0x1a, 0x1c, 0xcc, 0x72,
0x46, 0xfc, 0x19, 0xf5, 0x71, 0xd9, 0xb3, 0xb0, 0x5b, 0x5d, 0xe8, 0x98, 0xdf, 0xb9, 0x5f, 0x82, 0x9e, 0x89, 0x25, 0xda, 0x3d, 0xe3, 0xd5, 0xd4, 0xb1, 0xb7, 0x60, 0xd5, 0x42, 0xb3, 0x3c, 0x15,
0xd5, 0x07, 0xa8, 0x11, 0x51, 0x10, 0x8d, 0x6f, 0x0a, 0xb1, 0x45, 0x35, 0x4d, 0xa6, 0x07, 0x4f, 0xeb, 0xb6, 0x7b, 0xc6, 0xab, 0xd4, 0xa0, 0xe0, 0xc7, 0xd3, 0x3c, 0x99, 0xe6, 0x83, 0x20, 0x1a,
0xf9, 0x4c, 0xce, 0x9b, 0x2c, 0xa1, 0x50, 0x1d, 0xc5, 0x59, 0x2e, 0x25, 0x87, 0x7e, 0xbb, 0xff, 0xf1, 0x67, 0xd4, 0xc7, 0x65, 0xcf, 0xc2, 0x6e, 0x75, 0xa1, 0x63, 0x7e, 0xe7, 0x7e, 0x09, 0x56,
0xe4, 0xc0, 0x0a, 0xce, 0xfd, 0x07, 0x7e, 0x34, 0x53, 0x2b, 0xf7, 0x00, 0x3a, 0xc8, 0xea, 0x51, 0x1f, 0xa0, 0x46, 0x44, 0x41, 0x34, 0xbe, 0x29, 0xc4, 0x16, 0xd5, 0x34, 0x99, 0x1e, 0x3c, 0xe5,
0x7c, 0x53, 0x28, 0xbb, 0x10, 0xe2, 0xab, 0x72, 0xae, 0x4a, 0xd4, 0xd7, 0x4c, 0x52, 0x34, 0xe6, 0x33, 0x39, 0x6f, 0xb2, 0x84, 0x42, 0x75, 0x14, 0x67, 0xb9, 0x94, 0x1c, 0xfa, 0xed, 0xfe, 0x93,
0x33, 0xcf, 0xfa, 0x1a, 0xc5, 0x36, 0xf7, 0xd3, 0x31, 0xcf, 0xc9, 0x0c, 0x48, 0xb3, 0x00, 0x02, 0x03, 0x2b, 0x38, 0xf7, 0x1f, 0xf8, 0xd1, 0x4c, 0xad, 0xdc, 0x03, 0xe8, 0x20, 0xab, 0x47, 0xf1,
0xba, 0x1d, 0x47, 0x87, 0xec, 0x32, 0x74, 0x32, 0x3f, 0x1f, 0x24, 0x3c, 0xa5, 0x59, 0x23, 0xd1, 0x4d, 0xa1, 0xec, 0x42, 0x88, 0xaf, 0xca, 0xb9, 0x2a, 0x51, 0x5f, 0x33, 0x49, 0x71, 0x7b, 0x98,
0x9b, 0xf3, 0x20, 0xf3, 0xf3, 0x3d, 0x9e, 0xde, 0x9a, 0xe5, 0xbc, 0xff, 0x65, 0x58, 0xab, 0xb4, 0x79, 0xd6, 0xd7, 0x28, 0xb6, 0xb9, 0x9f, 0x8e, 0x79, 0x4e, 0x66, 0x40, 0x9a, 0x05, 0x10, 0xd0,
0x82, 0xd2, 0x5e, 0x0c, 0x11, 0x7f, 0xb2, 0x0d, 0x98, 0x3f, 0xf6, 0xc3, 0x29, 0x97, 0xd6, 0x49, 0xed, 0x38, 0x3a, 0x64, 0x97, 0xa1, 0x93, 0xf9, 0xf9, 0x20, 0xe1, 0x29, 0xcd, 0x1a, 0x89, 0xde,
0x14, 0xde, 0x6b, 0xbc, 0xeb, 0xb8, 0x9f, 0x85, 0xd5, 0xa2, 0xdb, 0x52, 0xc8, 0x18, 0x34, 0x71, 0x9c, 0x07, 0x99, 0x9f, 0xef, 0xf1, 0xf4, 0xd6, 0x2c, 0xe7, 0xfd, 0x2f, 0xc3, 0x5a, 0xa5, 0x15,
0x06, 0x25, 0x03, 0xfa, 0xed, 0xfe, 0x96, 0x23, 0x08, 0x6f, 0xc7, 0x81, 0xd6, 0x74, 0x24, 0x44, 0x94, 0xf6, 0x62, 0x88, 0xf8, 0x93, 0x6d, 0xc0, 0xfc, 0xb1, 0x1f, 0x4e, 0xb9, 0xb4, 0x4e, 0xa2,
0x83, 0xa0, 0x08, 0xf1, 0xf7, 0xa9, 0x96, 0xf0, 0xa7, 0x1f, 0xac, 0x7b, 0x05, 0xd6, 0x8c, 0x2e, 0xf0, 0x5e, 0xe3, 0x5d, 0xc7, 0xfd, 0x2c, 0xac, 0x16, 0xdd, 0x96, 0x42, 0xc6, 0xa0, 0x89, 0x33,
0xbc, 0xa0, 0xb3, 0xdf, 0x71, 0x60, 0xed, 0x21, 0x3f, 0x91, 0xab, 0xae, 0x7a, 0xfb, 0x2e, 0x34, 0x28, 0x19, 0xd0, 0x6f, 0xf7, 0xb7, 0x1c, 0x41, 0x78, 0x3b, 0x0e, 0xb4, 0xa6, 0x23, 0x21, 0x1a,
0xf3, 0x59, 0x22, 0xb6, 0xe2, 0xee, 0xce, 0x9b, 0x72, 0xd1, 0x2a, 0x74, 0xd7, 0x64, 0xf1, 0xd1, 0x04, 0x45, 0x88, 0xbf, 0x4f, 0xb5, 0x84, 0x3f, 0xfd, 0x60, 0xdd, 0x2b, 0xb0, 0x66, 0x74, 0xe1,
0x2c, 0xe1, 0x1e, 0x7d, 0xe1, 0x7e, 0x09, 0xda, 0x06, 0xc8, 0xce, 0xc1, 0xfa, 0x93, 0xf7, 0x1f, 0x05, 0x9d, 0xfd, 0x8e, 0x03, 0x6b, 0x0f, 0xf9, 0x89, 0x5c, 0x75, 0xd5, 0xdb, 0x77, 0xa1, 0x99,
0x3d, 0xbc, 0xbb, 0xbf, 0x3f, 0xd8, 0x7b, 0x7c, 0xeb, 0xab, 0x77, 0x7f, 0x65, 0xb0, 0x7b, 0x73, 0xcf, 0x12, 0xb1, 0xb9, 0x77, 0x77, 0xde, 0x94, 0x8b, 0x56, 0xa1, 0xbb, 0x26, 0x8b, 0x8f, 0x66,
0x7f, 0x77, 0xf5, 0x0c, 0xdb, 0x04, 0xf6, 0xf0, 0xee, 0xfe, 0xa3, 0xbb, 0x77, 0x2c, 0xdc, 0x71, 0x09, 0xf7, 0xe8, 0x0b, 0xf7, 0x4b, 0xd0, 0x36, 0x40, 0x76, 0x0e, 0xd6, 0x9f, 0xbc, 0xff, 0xe8,
0xfb, 0xd0, 0x7b, 0xc8, 0x4f, 0x9e, 0x04, 0x79, 0xc4, 0xb3, 0xcc, 0x6e, 0xcd, 0xbd, 0x06, 0xcc, 0xe1, 0xdd, 0xfd, 0xfd, 0xc1, 0xde, 0xe3, 0x5b, 0x5f, 0xbd, 0xfb, 0x2b, 0x83, 0xdd, 0x9b, 0xfb,
0xec, 0x82, 0x1c, 0x55, 0x0f, 0x16, 0xa5, 0xa9, 0x55, 0x3b, 0x8d, 0x2c, 0xba, 0x9f, 0x05, 0xb6, 0xbb, 0xab, 0x67, 0xd8, 0x26, 0xb0, 0x87, 0x77, 0xf7, 0x1f, 0xdd, 0xbd, 0x63, 0xe1, 0x8e, 0xdb,
0x1f, 0x8c, 0xa3, 0x0f, 0x78, 0x96, 0xf9, 0x63, 0xae, 0xc6, 0xb6, 0x0a, 0x73, 0x93, 0x6c, 0x2c, 0x87, 0xde, 0x43, 0x7e, 0xf2, 0x24, 0xc8, 0x23, 0x9e, 0x65, 0x76, 0x6b, 0xee, 0x35, 0x60, 0x66,
0x8d, 0x22, 0xfe, 0x74, 0x3f, 0x07, 0xeb, 0x16, 0x9d, 0x64, 0x7c, 0x11, 0x5a, 0x59, 0x30, 0x8e, 0x17, 0xe4, 0xa8, 0x7a, 0xb0, 0x28, 0x4d, 0xad, 0xda, 0x69, 0x64, 0xd1, 0xfd, 0x2c, 0xb0, 0xfd,
0xfc, 0x7c, 0x9a, 0x72, 0xc9, 0xba, 0x00, 0xdc, 0x7b, 0xb0, 0xf1, 0x0d, 0x9e, 0x06, 0x87, 0xb3, 0x60, 0x1c, 0x7d, 0xc0, 0xb3, 0xcc, 0x1f, 0x73, 0x35, 0xb6, 0x55, 0x98, 0x9b, 0x64, 0x63, 0x69,
0x97, 0xb1, 0xb7, 0xf9, 0x34, 0xca, 0x7c, 0xee, 0xc2, 0xd9, 0x12, 0x1f, 0xd9, 0xbc, 0x10, 0x44, 0x14, 0xf1, 0xa7, 0xfb, 0x39, 0x58, 0xb7, 0xe8, 0x24, 0xe3, 0x8b, 0xd0, 0xca, 0x82, 0x71, 0xe4,
0xb9, 0x5c, 0x4b, 0x9e, 0x28, 0x18, 0x6a, 0xd9, 0x30, 0xd5, 0xd2, 0x7d, 0x0c, 0xec, 0x76, 0x1c, 0xe7, 0xd3, 0x94, 0x4b, 0xd6, 0x05, 0xe0, 0xde, 0x83, 0x8d, 0x6f, 0xf0, 0x34, 0x38, 0x9c, 0xbd,
0x45, 0x7c, 0x98, 0xef, 0x71, 0x9e, 0x16, 0xfe, 0x55, 0x21, 0x75, 0xed, 0x9d, 0x73, 0x72, 0x1d, 0x8c, 0xbd, 0xcd, 0xa7, 0x51, 0xe6, 0x73, 0x17, 0xce, 0x96, 0xf8, 0xc8, 0xe6, 0x85, 0x20, 0xca,
0xcb, 0xba, 0x2e, 0xc5, 0x91, 0x41, 0x33, 0xe1, 0xe9, 0x84, 0x18, 0x2f, 0x79, 0xf4, 0xdb, 0x3d, 0xe5, 0x5a, 0xf2, 0x44, 0xc1, 0x50, 0xcb, 0x86, 0xa9, 0x96, 0xee, 0x63, 0x60, 0xb7, 0xe3, 0x28,
0x0b, 0xeb, 0x16, 0x5b, 0xb9, 0xdb, 0xbf, 0x0d, 0x67, 0xef, 0x04, 0xd9, 0xb0, 0xda, 0x60, 0x0f, 0xe2, 0xc3, 0x7c, 0x8f, 0xf3, 0xb4, 0xf0, 0xd8, 0x0a, 0xa9, 0x6b, 0xef, 0x9c, 0x93, 0xeb, 0x58,
0x16, 0x93, 0xe9, 0xc1, 0xa0, 0xd0, 0x29, 0x55, 0xc4, 0x4d, 0xb0, 0xfc, 0x89, 0x64, 0xf6, 0x7b, 0xd6, 0x75, 0x29, 0x8e, 0x0c, 0x9a, 0x09, 0x4f, 0x27, 0xc4, 0x78, 0xc9, 0xa3, 0xdf, 0xee, 0x59,
0x0e, 0x34, 0x77, 0x1f, 0x3d, 0xb8, 0xcd, 0xfa, 0xb0, 0x14, 0x44, 0xc3, 0x78, 0x82, 0x5b, 0x87, 0x58, 0xb7, 0xd8, 0xca, 0xdd, 0xfe, 0x6d, 0x38, 0x7b, 0x27, 0xc8, 0x86, 0xd5, 0x06, 0x7b, 0xb0,
0x18, 0xb4, 0x2e, 0x9f, 0xaa, 0x2b, 0x17, 0xa1, 0x45, 0x3b, 0x0e, 0xee, 0xeb, 0xd2, 0x15, 0x2a, 0x98, 0x4c, 0x0f, 0x06, 0x85, 0x4e, 0xa9, 0x22, 0x6e, 0x82, 0xe5, 0x4f, 0x24, 0xb3, 0xdf, 0x73,
0x00, 0xf4, 0x29, 0xf8, 0xb3, 0x24, 0x48, 0xc9, 0x69, 0x50, 0xae, 0x40, 0x93, 0x2c, 0x62, 0xb5, 0xa0, 0xb9, 0xfb, 0xe8, 0xc1, 0x6d, 0xd6, 0x87, 0xa5, 0x20, 0x1a, 0xc6, 0x13, 0xdc, 0x3a, 0xc4,
0xc2, 0xfd, 0xef, 0x26, 0x2c, 0x4a, 0x5b, 0x4d, 0xed, 0x0d, 0xf3, 0xe0, 0x98, 0xcb, 0x9e, 0xc8, 0xa0, 0x75, 0xf9, 0x54, 0x5d, 0xb9, 0x08, 0x2d, 0xda, 0x71, 0x70, 0x5f, 0x97, 0xce, 0x55, 0x01,
0x12, 0xee, 0x2a, 0x29, 0x9f, 0xc4, 0x39, 0x1f, 0x58, 0xcb, 0x60, 0x83, 0x48, 0x35, 0x14, 0x8c, 0xa0, 0x4f, 0xc1, 0x9f, 0x25, 0x41, 0x4a, 0x4e, 0x83, 0x72, 0x05, 0x9a, 0x64, 0x11, 0xab, 0x15,
0x06, 0x09, 0x5a, 0x7d, 0xea, 0x59, 0xcb, 0xb3, 0x41, 0x9c, 0x2c, 0x04, 0x06, 0xc1, 0x88, 0xfa, 0xee, 0x7f, 0x37, 0x61, 0x51, 0xda, 0x6a, 0x6a, 0x6f, 0x98, 0x07, 0xc7, 0x5c, 0xf6, 0x44, 0x96,
0xd4, 0xf4, 0x54, 0x11, 0x67, 0x62, 0xe8, 0x27, 0xfe, 0x30, 0xc8, 0x67, 0x52, 0xb9, 0x75, 0x19, 0x70, 0x57, 0x49, 0xf9, 0x24, 0xce, 0xf9, 0xc0, 0x5a, 0x06, 0x1b, 0x44, 0xaa, 0xa1, 0x60, 0x34,
0x79, 0x87, 0xf1, 0xd0, 0x0f, 0x07, 0x07, 0x7e, 0xe8, 0x47, 0x43, 0x2e, 0x1d, 0x17, 0x1b, 0x44, 0x48, 0xd0, 0xea, 0x53, 0xcf, 0x5a, 0x9e, 0x0d, 0xe2, 0x64, 0x21, 0x30, 0x08, 0x46, 0xd4, 0xa7,
0xdf, 0x44, 0x76, 0x49, 0x91, 0x09, 0xff, 0xa5, 0x84, 0xa2, 0x8f, 0x33, 0x8c, 0x27, 0x93, 0x20, 0xa6, 0xa7, 0x8a, 0x38, 0x13, 0x43, 0x3f, 0xf1, 0x87, 0x41, 0x3e, 0x93, 0xca, 0xad, 0xcb, 0xc8,
0x47, 0x97, 0xa6, 0xb7, 0x24, 0x0c, 0x49, 0x81, 0xd0, 0x48, 0x44, 0xe9, 0x44, 0xcc, 0x5e, 0x4b, 0x3b, 0x8c, 0x87, 0x7e, 0x38, 0x38, 0xf0, 0x43, 0x3f, 0x1a, 0x72, 0xe9, 0xb8, 0xd8, 0x20, 0xfa,
0xb4, 0x66, 0x81, 0xc8, 0xe5, 0x90, 0x73, 0x32, 0x48, 0x4f, 0x4f, 0x7a, 0x20, 0xb8, 0x14, 0x08, 0x26, 0xb2, 0x4b, 0x8a, 0x4c, 0xf8, 0x2f, 0x25, 0x14, 0x7d, 0x9c, 0x61, 0x3c, 0x99, 0x04, 0x39,
0xae, 0xc3, 0x34, 0xca, 0x78, 0x9e, 0x87, 0x7c, 0xa4, 0x3b, 0xd4, 0x26, 0xb2, 0x6a, 0x05, 0xbb, 0xba, 0x34, 0xbd, 0x25, 0x61, 0x48, 0x0a, 0x84, 0x46, 0x22, 0x4a, 0x27, 0x62, 0xf6, 0x5a, 0xa2,
0x0e, 0xeb, 0xc2, 0xcb, 0xca, 0xfc, 0x3c, 0xce, 0x8e, 0x82, 0x6c, 0x90, 0xf1, 0x28, 0xef, 0x75, 0x35, 0x0b, 0x44, 0x2e, 0x87, 0x9c, 0x93, 0x41, 0x7a, 0x7a, 0xd2, 0x03, 0xc1, 0xa5, 0x40, 0x70,
0x88, 0xbe, 0xae, 0x8a, 0xbd, 0x0b, 0xe7, 0x4a, 0x70, 0xca, 0x87, 0x3c, 0x38, 0xe6, 0xa3, 0xde, 0x1d, 0xa6, 0x51, 0xc6, 0xf3, 0x3c, 0xe4, 0x23, 0xdd, 0xa1, 0x36, 0x91, 0x55, 0x2b, 0xd8, 0x75,
0x32, 0x7d, 0x75, 0x5a, 0x35, 0xbb, 0x0c, 0x6d, 0x74, 0x2e, 0xa7, 0xc9, 0xc8, 0xc7, 0x7d, 0xb8, 0x58, 0x17, 0x5e, 0x56, 0xe6, 0xe7, 0x71, 0x76, 0x14, 0x64, 0x83, 0x8c, 0x47, 0x79, 0xaf, 0x43,
0x4b, 0xeb, 0x60, 0x42, 0xec, 0x6d, 0x58, 0x4e, 0xb8, 0xd8, 0x2c, 0x8f, 0xf2, 0x70, 0x98, 0xf5, 0xf4, 0x75, 0x55, 0xec, 0x5d, 0x38, 0x57, 0x82, 0x53, 0x3e, 0xe4, 0xc1, 0x31, 0x1f, 0xf5, 0x96,
0x56, 0x68, 0x27, 0x6b, 0x4b, 0x65, 0x42, 0xc9, 0xf5, 0x6c, 0x0a, 0x14, 0xca, 0x61, 0x46, 0xee, 0xe9, 0xab, 0xd3, 0xaa, 0xd9, 0x65, 0x68, 0xa3, 0x73, 0x39, 0x4d, 0x46, 0x3e, 0xee, 0xc3, 0x5d,
0x8a, 0x3f, 0xeb, 0xad, 0x92, 0xb8, 0x15, 0x00, 0xe9, 0x48, 0x1a, 0x1c, 0xfb, 0x39, 0xef, 0xad, 0x5a, 0x07, 0x13, 0x62, 0x6f, 0xc3, 0x72, 0xc2, 0xc5, 0x66, 0x79, 0x94, 0x87, 0xc3, 0xac, 0xb7,
0x91, 0x6c, 0xa9, 0xa2, 0xfb, 0x67, 0x0e, 0xac, 0x3f, 0x08, 0xb2, 0x5c, 0x0a, 0xa1, 0x36, 0xc7, 0x42, 0x3b, 0x59, 0x5b, 0x2a, 0x13, 0x4a, 0xae, 0x67, 0x53, 0xa0, 0x50, 0x0e, 0x33, 0x72, 0x57,
0xaf, 0x43, 0x5b, 0x88, 0xdf, 0x20, 0x8e, 0xc2, 0x99, 0x94, 0x48, 0x10, 0xd0, 0xd7, 0xa2, 0x70, 0xfc, 0x59, 0x6f, 0x95, 0xc4, 0xad, 0x00, 0x48, 0x47, 0xd2, 0xe0, 0xd8, 0xcf, 0x79, 0x6f, 0x8d,
0xc6, 0x3e, 0x03, 0xcb, 0x41, 0x64, 0x92, 0x08, 0x1d, 0xee, 0x28, 0x90, 0x88, 0x5e, 0x87, 0x76, 0x64, 0x4b, 0x15, 0xdd, 0x3f, 0x75, 0x60, 0xfd, 0x41, 0x90, 0xe5, 0x52, 0x08, 0xb5, 0x39, 0x7e,
0x32, 0x3d, 0x08, 0x83, 0xa1, 0x20, 0x99, 0x13, 0x5c, 0x04, 0x44, 0x04, 0xe8, 0xe8, 0x89, 0x9e, 0x1d, 0xda, 0x42, 0xfc, 0x06, 0x71, 0x14, 0xce, 0xa4, 0x44, 0x82, 0x80, 0xbe, 0x16, 0x85, 0x33,
0x08, 0x8a, 0x26, 0x51, 0xb4, 0x25, 0x86, 0x24, 0xee, 0x2d, 0xd8, 0xb0, 0x3b, 0x28, 0x8d, 0xd5, 0xf6, 0x19, 0x58, 0x0e, 0x22, 0x93, 0x44, 0xe8, 0x70, 0x47, 0x81, 0x44, 0xf4, 0x3a, 0xb4, 0x93,
0x16, 0x2c, 0x49, 0xd9, 0xce, 0x7a, 0x6d, 0x9a, 0x9f, 0xae, 0x9c, 0x1f, 0x49, 0xea, 0xe9, 0x7a, 0xe9, 0x41, 0x18, 0x0c, 0x05, 0xc9, 0x9c, 0xe0, 0x22, 0x20, 0x22, 0x40, 0x47, 0x4f, 0xf4, 0x44,
0xf7, 0xdf, 0x1c, 0x68, 0xa2, 0x01, 0x38, 0xdd, 0x58, 0x98, 0x36, 0x7d, 0xce, 0xb2, 0xe9, 0xe4, 0x50, 0x34, 0x89, 0xa2, 0x2d, 0x31, 0x24, 0x71, 0x6f, 0xc1, 0x86, 0xdd, 0x41, 0x69, 0xac, 0xb6,
0xf7, 0xa3, 0x57, 0x24, 0x44, 0x42, 0xa8, 0x8d, 0x81, 0x14, 0xf5, 0x29, 0x1f, 0x1e, 0x93, 0xee, 0x60, 0x49, 0xca, 0x76, 0xd6, 0x6b, 0xd3, 0xfc, 0x74, 0xe5, 0xfc, 0x48, 0x52, 0x4f, 0xd7, 0xbb,
0xe8, 0x7a, 0x44, 0x50, 0xb3, 0x70, 0xeb, 0xa4, 0xaf, 0x85, 0xe2, 0xe8, 0xb2, 0xaa, 0xa3, 0x2f, 0xff, 0xe6, 0x40, 0x13, 0x0d, 0xc0, 0xe9, 0xc6, 0xc2, 0xb4, 0xe9, 0x73, 0x96, 0x4d, 0x27, 0xbf,
0x17, 0x8b, 0x3a, 0xfa, 0xae, 0x07, 0x8b, 0x41, 0x74, 0x10, 0x4f, 0xa3, 0x11, 0x29, 0xc9, 0x92, 0x1f, 0xbd, 0x22, 0x21, 0x12, 0x42, 0x6d, 0x0c, 0xa4, 0xa8, 0x4f, 0xf9, 0xf0, 0x98, 0x74, 0x47,
0xa7, 0x8a, 0xb8, 0xd8, 0x09, 0x79, 0x52, 0xc1, 0x84, 0x4b, 0xed, 0x28, 0x00, 0x97, 0xa1, 0x6b, 0xd7, 0x23, 0x82, 0x9a, 0x85, 0x5b, 0x27, 0x7d, 0x2d, 0x14, 0x47, 0x97, 0x55, 0x1d, 0x7d, 0xb9,
0x95, 0x91, 0xc1, 0xd3, 0xfb, 0xd8, 0x3b, 0xb0, 0x66, 0x60, 0x72, 0x06, 0xdf, 0x80, 0xf9, 0x04, 0x58, 0xd4, 0xd1, 0x77, 0x3d, 0x58, 0x0c, 0xa2, 0x83, 0x78, 0x1a, 0x8d, 0x48, 0x49, 0x96, 0x3c,
0x01, 0xe9, 0x28, 0x29, 0xf1, 0x22, 0x4b, 0x29, 0x6a, 0xdc, 0x55, 0x8c, 0x9f, 0xf3, 0xf7, 0xa3, 0x55, 0xc4, 0xc5, 0x4e, 0xc8, 0x93, 0x0a, 0x26, 0x5c, 0x6a, 0x47, 0x01, 0xb8, 0x0c, 0x5d, 0xab,
0xc3, 0x58, 0x71, 0xfa, 0xe1, 0x1c, 0x06, 0xbc, 0x12, 0x92, 0x8c, 0xae, 0xc2, 0x4a, 0x30, 0xe2, 0x8c, 0x0c, 0x9e, 0xde, 0xc7, 0xde, 0x81, 0x35, 0x03, 0x93, 0x33, 0xf8, 0x06, 0xcc, 0x27, 0x08,
0x51, 0x1e, 0xe4, 0xb3, 0x81, 0xe5, 0xc1, 0x95, 0x61, 0xdc, 0x61, 0xfc, 0x30, 0xf0, 0x33, 0x69, 0x48, 0x47, 0x49, 0x89, 0x17, 0x59, 0x4a, 0x51, 0xe3, 0xae, 0x62, 0x44, 0x9e, 0xbf, 0x1f, 0x1d,
0xc3, 0x44, 0x81, 0xed, 0xc0, 0x06, 0x8a, 0xbf, 0x92, 0x68, 0xbd, 0xac, 0xc2, 0x91, 0xac, 0xad, 0xc6, 0x8a, 0xd3, 0x0f, 0xe7, 0x30, 0x84, 0x96, 0x90, 0x64, 0x74, 0x15, 0x56, 0x82, 0x11, 0x8f,
0x43, 0x8d, 0x45, 0x5c, 0x4a, 0xa0, 0xfe, 0x44, 0x58, 0xda, 0xba, 0x2a, 0x9c, 0x35, 0xc1, 0x09, 0xf2, 0x20, 0x9f, 0x0d, 0x2c, 0x0f, 0xae, 0x0c, 0xe3, 0x0e, 0xe3, 0x87, 0x81, 0x9f, 0x49, 0x1b,
0x87, 0x3c, 0x2f, 0x54, 0x44, 0x03, 0x95, 0xe8, 0x6d, 0x41, 0x38, 0xb1, 0xe5, 0xe8, 0xcd, 0x88, 0x26, 0x0a, 0x6c, 0x07, 0x36, 0x50, 0xfc, 0x95, 0x44, 0xeb, 0x65, 0x15, 0x8e, 0x64, 0x6d, 0x1d,
0x00, 0x97, 0x2a, 0x11, 0xe0, 0x55, 0x58, 0xc9, 0x66, 0xd1, 0x90, 0x8f, 0x06, 0x79, 0x8c, 0xed, 0x6a, 0x2c, 0xe2, 0x52, 0x02, 0xf5, 0x27, 0xc2, 0xd2, 0xd6, 0x55, 0xe1, 0xac, 0x09, 0x4e, 0x38,
0x06, 0x11, 0xad, 0xce, 0x92, 0x57, 0x86, 0x29, 0x56, 0xe5, 0x59, 0x1e, 0xf1, 0x9c, 0x4c, 0xd7, 0xe4, 0x79, 0xa1, 0x22, 0x1a, 0xa8, 0x44, 0x6f, 0x0b, 0xc2, 0x89, 0x2d, 0x47, 0x6f, 0x46, 0x04,
0x92, 0xa7, 0x8a, 0xb8, 0x0b, 0x10, 0x89, 0x10, 0xea, 0x96, 0x27, 0x4b, 0xb8, 0x55, 0x4e, 0xd3, 0xb8, 0x54, 0x89, 0x00, 0xaf, 0xc2, 0x4a, 0x36, 0x8b, 0x86, 0x7c, 0x34, 0xc8, 0x63, 0x6c, 0x37,
0x20, 0xeb, 0x75, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xf6, 0x00, 0x23, 0xab, 0x23, 0xee, 0x8f, 0x88, 0x68, 0x75, 0x96, 0xbc, 0x32, 0x4c, 0xb1, 0x2a, 0xcf, 0xf2, 0x88, 0xe7, 0x64, 0xba, 0x96,
0x78, 0x4a, 0xab, 0x2f, 0x02, 0x4b, 0x61, 0x81, 0xea, 0x2b, 0xb1, 0xed, 0x63, 0x9e, 0x66, 0x41, 0x3c, 0x55, 0xc4, 0x5d, 0x80, 0x48, 0x84, 0x50, 0xb7, 0x3c, 0x59, 0xc2, 0xad, 0x72, 0x9a, 0x06,
0x1c, 0x91, 0xed, 0x69, 0x79, 0xaa, 0xe8, 0x7e, 0x4c, 0x3b, 0xba, 0x0e, 0x79, 0x1f, 0x93, 0x39, 0x59, 0xaf, 0x43, 0x28, 0xfd, 0x66, 0x9f, 0x87, 0xb3, 0x07, 0x18, 0x59, 0x1d, 0x71, 0x7f, 0xc4,
0x62, 0x17, 0xa0, 0x25, 0xc6, 0x98, 0x1d, 0xf9, 0xd2, 0xc9, 0x58, 0x22, 0x60, 0xff, 0xc8, 0x47, 0x53, 0x5a, 0x7d, 0x11, 0x58, 0x0a, 0x0b, 0x54, 0x5f, 0x89, 0x6d, 0x1f, 0xf3, 0x34, 0x0b, 0xe2,
0x05, 0xb6, 0xa6, 0xad, 0x41, 0x9e, 0x63, 0x9b, 0xb0, 0x5d, 0x31, 0x6b, 0x6f, 0x42, 0x57, 0x05, 0x88, 0x6c, 0x4f, 0xcb, 0x53, 0x45, 0xf7, 0x63, 0xda, 0xd1, 0x75, 0xc8, 0xfb, 0x98, 0xcc, 0x11,
0xd3, 0xd9, 0x20, 0xe4, 0x87, 0xb9, 0x0a, 0x10, 0xa2, 0xe9, 0x04, 0x9b, 0xcb, 0x1e, 0xf0, 0xc3, 0xbb, 0x00, 0x2d, 0x31, 0xc6, 0xec, 0xc8, 0x97, 0x4e, 0xc6, 0x12, 0x01, 0xfb, 0x47, 0x3e, 0x2a,
0xdc, 0x7d, 0x08, 0x6b, 0x52, 0x6f, 0xbf, 0x96, 0x70, 0xd5, 0xf4, 0x17, 0xcb, 0x9b, 0x9a, 0xf0, 0xb0, 0x35, 0x6d, 0x22, 0x84, 0x6f, 0x13, 0xb6, 0x2b, 0x66, 0xed, 0x4d, 0xe8, 0xaa, 0x60, 0x3a,
0x2a, 0xd6, 0x6d, 0x45, 0xa7, 0x28, 0xa7, 0xb4, 0xd3, 0xb9, 0x1e, 0x30, 0x59, 0x7d, 0x3b, 0x8c, 0x1b, 0x84, 0xfc, 0x30, 0x57, 0x01, 0x42, 0x34, 0x9d, 0x60, 0x73, 0xd9, 0x03, 0x7e, 0x98, 0xbb,
0x33, 0x2e, 0x19, 0xba, 0xd0, 0x19, 0x86, 0x71, 0xa6, 0xc2, 0x10, 0x39, 0x1c, 0x0b, 0xc3, 0xf9, 0x0f, 0x61, 0x4d, 0xea, 0xed, 0xd7, 0x12, 0xae, 0x9a, 0xfe, 0x62, 0x79, 0x53, 0x13, 0x5e, 0xc5,
0xc9, 0xa6, 0xc3, 0x21, 0x5a, 0x02, 0x61, 0xd3, 0x54, 0xd1, 0xfd, 0x73, 0x07, 0xd6, 0x89, 0x9b, 0xba, 0xad, 0xe8, 0x14, 0xe5, 0x94, 0x76, 0x3a, 0xd7, 0x03, 0x26, 0xab, 0x6f, 0x87, 0x71, 0xc6,
0xb2, 0x30, 0xda, 0x77, 0x7d, 0xf5, 0x6e, 0x76, 0x86, 0x66, 0x68, 0xb6, 0x01, 0xf3, 0x87, 0x71, 0x25, 0x43, 0x17, 0x3a, 0xc3, 0x30, 0xce, 0x54, 0x18, 0x22, 0x87, 0x63, 0x61, 0x38, 0x3f, 0xd9,
0x3a, 0xe4, 0xb2, 0x25, 0x51, 0xf8, 0xf1, 0xbd, 0xf1, 0x66, 0xc5, 0x1b, 0xff, 0xa1, 0x03, 0x6b, 0x74, 0x38, 0x44, 0x4b, 0x20, 0x6c, 0x9a, 0x2a, 0xba, 0x7f, 0xe6, 0xc0, 0x3a, 0x71, 0x53, 0x16,
0xd4, 0xd5, 0xfd, 0xdc, 0xcf, 0xa7, 0x99, 0x1c, 0xfe, 0x2f, 0xc0, 0x32, 0x0e, 0x95, 0x2b, 0x75, 0x46, 0xfb, 0xae, 0xaf, 0xde, 0xcd, 0xce, 0xd0, 0x0c, 0xcd, 0x36, 0x60, 0xfe, 0x30, 0x4e, 0x87,
0x92, 0x1d, 0xdd, 0xd0, 0x9a, 0x4f, 0xa8, 0x20, 0xde, 0x3d, 0xe3, 0xd9, 0xc4, 0xec, 0xcb, 0xd0, 0x5c, 0xb6, 0x24, 0x0a, 0x3f, 0xbe, 0x37, 0xde, 0xac, 0x78, 0xe3, 0x3f, 0x74, 0x60, 0x8d, 0xba,
0x31, 0x33, 0x22, 0xd4, 0xe7, 0xf6, 0xce, 0x79, 0x35, 0xca, 0x8a, 0xe4, 0xec, 0x9e, 0xf1, 0xac, 0xba, 0x9f, 0xfb, 0xf9, 0x34, 0x93, 0xc3, 0xff, 0x05, 0x58, 0xc6, 0xa1, 0x72, 0xa5, 0x4e, 0xb2,
0x0f, 0xd8, 0x0d, 0x00, 0x72, 0x37, 0x88, 0xad, 0x0c, 0x65, 0xcf, 0xdb, 0x93, 0x64, 0x2c, 0xd6, 0xa3, 0x1b, 0x5a, 0xf3, 0x09, 0x15, 0xc4, 0xbb, 0x67, 0x3c, 0x9b, 0x98, 0x7d, 0x19, 0x3a, 0x66,
0xee, 0x19, 0xcf, 0x20, 0xbf, 0xb5, 0x04, 0x0b, 0x62, 0x7f, 0x74, 0xef, 0xc3, 0xb2, 0xd5, 0x53, 0x46, 0x84, 0xfa, 0xdc, 0xde, 0x39, 0xaf, 0x46, 0x59, 0x91, 0x9c, 0xdd, 0x33, 0x9e, 0xf5, 0x01,
0x2b, 0xca, 0xe8, 0x88, 0x28, 0xa3, 0x12, 0x94, 0x36, 0xaa, 0x41, 0xa9, 0xfb, 0x2f, 0x0d, 0x60, 0xbb, 0x01, 0x40, 0xee, 0x06, 0xb1, 0x95, 0xa1, 0xec, 0x79, 0x7b, 0x92, 0x8c, 0xc5, 0xda, 0x3d,
0x28, 0x6d, 0xa5, 0xe5, 0xc4, 0x0d, 0x3a, 0x1e, 0x59, 0xee, 0x56, 0xc7, 0x33, 0x21, 0x76, 0x0d, 0xe3, 0x19, 0xe4, 0xb7, 0x96, 0x60, 0x41, 0xec, 0x8f, 0xee, 0x7d, 0x58, 0xb6, 0x7a, 0x6a, 0x45,
0x98, 0x51, 0x54, 0xb9, 0x07, 0xb1, 0x6f, 0xd4, 0xd4, 0xa0, 0x81, 0x13, 0xbe, 0x92, 0x8a, 0x81, 0x19, 0x1d, 0x11, 0x65, 0x54, 0x82, 0xd2, 0x46, 0x35, 0x28, 0x75, 0xff, 0xa5, 0x01, 0x0c, 0xa5,
0xa5, 0x63, 0x29, 0xd6, 0xad, 0xb6, 0x0e, 0xb7, 0x86, 0x64, 0x9a, 0x1d, 0xa1, 0x03, 0xa1, 0x1c, 0xad, 0xb4, 0x9c, 0xb8, 0x41, 0xc7, 0x23, 0xcb, 0xdd, 0xea, 0x78, 0x26, 0xc4, 0xae, 0x01, 0x33,
0x32, 0x55, 0x2e, 0x0b, 0xc8, 0xc2, 0x4b, 0x05, 0x64, 0xb1, 0x2c, 0x20, 0xa6, 0x4b, 0xb0, 0x64, 0x8a, 0x2a, 0xf7, 0x20, 0xf6, 0x8d, 0x9a, 0x1a, 0x34, 0x70, 0xc2, 0x57, 0x52, 0x31, 0xb0, 0x74,
0xb9, 0x04, 0xe8, 0x7f, 0x4d, 0x82, 0x88, 0xfc, 0x8a, 0xc1, 0x04, 0x5b, 0x97, 0xfe, 0x97, 0x05, 0x2c, 0xc5, 0xba, 0xd5, 0xd6, 0xe1, 0xd6, 0x90, 0x4c, 0xb3, 0x23, 0x74, 0x20, 0x94, 0x43, 0xa6,
0xb2, 0x2d, 0x58, 0x95, 0x7e, 0x5d, 0xe1, 0x77, 0x00, 0xcd, 0x71, 0x05, 0x77, 0x3f, 0x75, 0x60, 0xca, 0x65, 0x01, 0x59, 0x78, 0xa9, 0x80, 0x2c, 0x96, 0x05, 0xc4, 0x74, 0x09, 0x96, 0x2c, 0x97,
0x15, 0xe7, 0xd9, 0x92, 0xc5, 0xf7, 0x80, 0x54, 0xe1, 0x15, 0x45, 0xd1, 0xa2, 0xfd, 0xe9, 0x25, 0x00, 0xfd, 0xaf, 0x49, 0x10, 0x91, 0x5f, 0x31, 0x98, 0x60, 0xeb, 0xd2, 0xff, 0xb2, 0x40, 0xb6,
0xf1, 0x5d, 0x68, 0x11, 0xc3, 0x38, 0xe1, 0x91, 0x14, 0xc4, 0x9e, 0x2d, 0x88, 0x85, 0x15, 0xda, 0x05, 0xab, 0xd2, 0xaf, 0x2b, 0xfc, 0x0e, 0xa0, 0x39, 0xae, 0xe0, 0xee, 0xa7, 0x0e, 0xac, 0xe2,
0x3d, 0xe3, 0x15, 0xc4, 0x86, 0x18, 0xfe, 0xbd, 0x03, 0x6d, 0xd9, 0xcd, 0x9f, 0x38, 0x96, 0xe8, 0x3c, 0x5b, 0xb2, 0xf8, 0x1e, 0x90, 0x2a, 0xbc, 0xa2, 0x28, 0x5a, 0xb4, 0x3f, 0xbd, 0x24, 0xbe,
0xc3, 0x12, 0x4a, 0xa4, 0xe1, 0xb0, 0xeb, 0x32, 0xee, 0x26, 0x13, 0x0c, 0xd8, 0x70, 0xfb, 0xb4, 0x0b, 0x2d, 0x62, 0x18, 0x27, 0x3c, 0x92, 0x82, 0xd8, 0xb3, 0x05, 0xb1, 0xb0, 0x42, 0xbb, 0x67,
0xe2, 0x88, 0x32, 0x8c, 0x7b, 0x21, 0x19, 0xdc, 0x6c, 0x90, 0x07, 0xe1, 0x40, 0xd5, 0xca, 0x04, 0xbc, 0x82, 0xd8, 0x10, 0xc3, 0xbf, 0x77, 0xa0, 0x2d, 0xbb, 0xf9, 0x13, 0xc7, 0x12, 0x7d, 0x58,
0x64, 0x5d, 0x15, 0xda, 0x9d, 0x2c, 0xf7, 0xc7, 0x5c, 0x6e, 0x73, 0xa2, 0x80, 0x01, 0x93, 0x1c, 0x42, 0x89, 0x34, 0x1c, 0x76, 0x5d, 0xc6, 0xdd, 0x64, 0x82, 0x01, 0x1b, 0x6e, 0x9f, 0x56, 0x1c,
0x50, 0xc9, 0x1d, 0x74, 0xff, 0xa6, 0x03, 0xe7, 0x2a, 0x55, 0x3a, 0xdd, 0x2d, 0x1d, 0xe4, 0x30, 0x51, 0x86, 0x71, 0x2f, 0x24, 0x83, 0x9b, 0x0d, 0xf2, 0x20, 0x1c, 0xa8, 0x5a, 0x99, 0x80, 0xac,
0x98, 0x1c, 0xc4, 0xda, 0xd7, 0x76, 0x4c, 0xdf, 0xd9, 0xaa, 0x62, 0x63, 0x38, 0xab, 0xf6, 0x73, 0xab, 0x42, 0xbb, 0x93, 0xe5, 0xfe, 0x98, 0xcb, 0x6d, 0x4e, 0x14, 0x30, 0x60, 0x92, 0x03, 0x2a,
0x9c, 0xd3, 0x62, 0xf7, 0x6e, 0x90, 0x23, 0xf2, 0xb6, 0x2d, 0x03, 0xe5, 0x06, 0x15, 0x6e, 0x6a, 0xb9, 0x83, 0xee, 0x5f, 0x77, 0xe0, 0x5c, 0xa5, 0x4a, 0x27, 0xd0, 0xa5, 0x83, 0x1c, 0x06, 0x93,
0x6e, 0x3d, 0x3f, 0x76, 0x04, 0x3d, 0xed, 0x38, 0x48, 0x13, 0x6f, 0x38, 0x17, 0xd8, 0xd6, 0x5b, 0x83, 0x58, 0xfb, 0xda, 0x8e, 0xe9, 0x3b, 0x5b, 0x55, 0x6c, 0x0c, 0x67, 0xd5, 0x7e, 0x8e, 0x73,
0x2f, 0x69, 0x8b, 0xec, 0xd1, 0x48, 0x35, 0x73, 0x2a, 0x37, 0x36, 0x83, 0x4b, 0xaa, 0x8e, 0x6c, 0x5a, 0xec, 0xde, 0x0d, 0x72, 0x44, 0xde, 0xb6, 0x65, 0xa0, 0xdc, 0xa0, 0xc2, 0x4d, 0xcd, 0xad,
0x78, 0xb5, 0xbd, 0xe6, 0x2b, 0x8d, 0xed, 0x1e, 0x7e, 0x6c, 0x37, 0xfa, 0x12, 0xc6, 0xec, 0x43, 0xe7, 0xc7, 0x8e, 0xa0, 0xa7, 0x1d, 0x07, 0x69, 0xe2, 0x0d, 0xe7, 0x02, 0xdb, 0x7a, 0xeb, 0x25,
0xd8, 0x3c, 0xf1, 0x83, 0x5c, 0x75, 0xcb, 0x70, 0x86, 0xe6, 0xa9, 0xc9, 0x9d, 0x97, 0x34, 0xf9, 0x6d, 0x91, 0x3d, 0x1a, 0xa9, 0x66, 0x4e, 0xe5, 0xc6, 0x66, 0x70, 0x49, 0xd5, 0x91, 0x0d, 0xaf,
0x44, 0x7c, 0x6c, 0x6d, 0x6c, 0xa7, 0x70, 0xec, 0xff, 0xad, 0x03, 0x5d, 0x9b, 0x0f, 0x8a, 0xa9, 0xb6, 0xd7, 0x7c, 0xa5, 0xb1, 0xdd, 0xc3, 0x8f, 0xed, 0x46, 0x5f, 0xc2, 0x98, 0x7d, 0x08, 0x9b,
0x54, 0x78, 0x65, 0xf8, 0x94, 0xf3, 0x57, 0x82, 0xab, 0x21, 0x6a, 0xa3, 0x2e, 0x44, 0x35, 0x03, 0x27, 0x7e, 0x90, 0xab, 0x6e, 0x19, 0xce, 0xd0, 0x3c, 0x35, 0xb9, 0xf3, 0x92, 0x26, 0x9f, 0x88,
0xd1, 0xb9, 0x97, 0x05, 0xa2, 0xcd, 0x57, 0x0b, 0x44, 0xe7, 0xeb, 0x02, 0xd1, 0xfe, 0x7f, 0x3a, 0x8f, 0xad, 0x8d, 0xed, 0x14, 0x8e, 0xfd, 0xbf, 0x75, 0xa0, 0x6b, 0xf3, 0x41, 0x31, 0x95, 0x0a,
0xc0, 0xaa, 0xb2, 0xc4, 0xee, 0x8b, 0x18, 0x39, 0xe2, 0xa1, 0xb4, 0x49, 0x3f, 0xf7, 0x6a, 0xf2, 0xaf, 0x0c, 0x9f, 0x72, 0xfe, 0x4a, 0x70, 0x35, 0x44, 0x6d, 0xd4, 0x85, 0xa8, 0x66, 0x20, 0x3a,
0xa8, 0xe6, 0x4e, 0x7d, 0x8d, 0x8a, 0x61, 0x1a, 0x1d, 0xd3, 0x45, 0x5a, 0xf6, 0xea, 0xaa, 0x4a, 0xf7, 0xb2, 0x40, 0xb4, 0xf9, 0x6a, 0x81, 0xe8, 0x7c, 0x5d, 0x20, 0xda, 0xff, 0x4f, 0x07, 0x58,
0xa1, 0x71, 0xf3, 0xe5, 0xa1, 0xf1, 0xfc, 0xcb, 0x43, 0xe3, 0x85, 0x72, 0x68, 0xdc, 0xff, 0x5d, 0x55, 0x96, 0xd8, 0x7d, 0x11, 0x23, 0x47, 0x3c, 0x94, 0x36, 0xe9, 0xe7, 0x5e, 0x4d, 0x1e, 0xd5,
0x07, 0xd6, 0x6b, 0x16, 0xfd, 0x67, 0x37, 0x70, 0x5c, 0x26, 0xcb, 0x16, 0x34, 0xe4, 0x32, 0x99, 0xdc, 0xa9, 0xaf, 0x51, 0x31, 0x4c, 0xa3, 0x63, 0xba, 0x48, 0xcb, 0x5e, 0x5d, 0x55, 0x29, 0x34,
0x60, 0xff, 0x37, 0x60, 0xd9, 0x12, 0xf4, 0x9f, 0x5d, 0xfb, 0x65, 0x2f, 0x4f, 0xc8, 0x99, 0x85, 0x6e, 0xbe, 0x3c, 0x34, 0x9e, 0x7f, 0x79, 0x68, 0xbc, 0x50, 0x0e, 0x8d, 0xfb, 0xbf, 0xeb, 0xc0,
0xf5, 0x7f, 0xd4, 0x00, 0x56, 0x55, 0xb6, 0xff, 0xd3, 0x3e, 0x54, 0xe7, 0x69, 0xae, 0x66, 0x9e, 0x7a, 0xcd, 0xa2, 0xff, 0xec, 0x06, 0x8e, 0xcb, 0x64, 0xd9, 0x82, 0x86, 0x5c, 0x26, 0x13, 0xec,
0xfe, 0x57, 0xf7, 0x81, 0xb7, 0x60, 0x2d, 0xe5, 0xc3, 0xf8, 0x98, 0xce, 0x1d, 0xed, 0xec, 0x4e, 0xff, 0x06, 0x2c, 0x5b, 0x82, 0xfe, 0xb3, 0x6b, 0xbf, 0xec, 0xe5, 0x09, 0x39, 0xb3, 0xb0, 0xfe,
0xb5, 0x02, 0xfd, 0x5c, 0x3b, 0x2f, 0xb1, 0x64, 0x1d, 0x13, 0x19, 0x9b, 0x61, 0x29, 0x3d, 0xe1, 0x8f, 0x1a, 0xc0, 0xaa, 0xca, 0xf6, 0x7f, 0xda, 0x87, 0xea, 0x3c, 0xcd, 0xd5, 0xcc, 0xd3, 0xff,
0x6e, 0xc2, 0x86, 0x38, 0xbd, 0xbb, 0x25, 0x58, 0xa9, 0x7d, 0xe5, 0x4f, 0x1d, 0x38, 0x5b, 0xaa, 0xea, 0x3e, 0xf0, 0x16, 0xac, 0xc9, 0xd3, 0x36, 0x23, 0x4b, 0x22, 0x24, 0xa6, 0x5a, 0x81, 0x7e,
0x28, 0xce, 0x52, 0xc4, 0xd6, 0x61, 0xef, 0x27, 0x36, 0x88, 0xfd, 0x97, 0x7a, 0x64, 0xf4, 0x5f, 0xae, 0x9d, 0x97, 0x58, 0xb2, 0x8e, 0x89, 0x8c, 0xcd, 0xb0, 0x94, 0x9e, 0x70, 0x37, 0x61, 0x43,
0x48, 0x5b, 0xb5, 0x02, 0xe7, 0x67, 0x1a, 0x55, 0xe9, 0xc5, 0xac, 0xd7, 0x55, 0xb9, 0xe7, 0xe0, 0x9c, 0xde, 0xdd, 0x12, 0xac, 0xd4, 0xbe, 0xf2, 0x27, 0x0e, 0x9c, 0x2d, 0x55, 0x14, 0x67, 0x29,
0xac, 0x5c, 0xd9, 0x52, 0xc7, 0x0f, 0x61, 0xb3, 0x5c, 0x51, 0x24, 0x87, 0xed, 0x2e, 0xab, 0x22, 0x62, 0xeb, 0xb0, 0xf7, 0x13, 0x1b, 0xc4, 0xfe, 0x4b, 0x3d, 0x32, 0xfa, 0x2f, 0xa4, 0xad, 0x5a,
0x7a, 0x81, 0xd6, 0x36, 0x65, 0xf7, 0xb7, 0xb6, 0xce, 0xfd, 0x75, 0x60, 0x5f, 0x9f, 0xf2, 0x74, 0x81, 0xf3, 0x33, 0x8d, 0xaa, 0xf4, 0x62, 0xd6, 0xeb, 0xaa, 0xdc, 0x73, 0x70, 0x56, 0xae, 0x6c,
0x46, 0x27, 0x3d, 0x3a, 0x3b, 0x73, 0xae, 0x9c, 0xc6, 0x58, 0x48, 0xa6, 0x07, 0x5f, 0xe5, 0x33, 0xa9, 0xe3, 0x87, 0xb0, 0x59, 0xae, 0x28, 0x92, 0xc3, 0x76, 0x97, 0x55, 0x11, 0xbd, 0x40, 0x6b,
0x75, 0x94, 0xd6, 0x28, 0x8e, 0xd2, 0x5e, 0x03, 0xc0, 0xe8, 0x8b, 0x8e, 0x86, 0xd4, 0xe1, 0x26, 0x9b, 0xb2, 0xfb, 0x5b, 0x5b, 0xe7, 0xfe, 0x3a, 0xb0, 0xaf, 0x4f, 0x79, 0x3a, 0xa3, 0x93, 0x1e,
0x86, 0xbd, 0x82, 0xa1, 0x7b, 0x03, 0xd6, 0x2d, 0xfe, 0x7a, 0xf6, 0x17, 0xe4, 0x17, 0x22, 0x37, 0x9d, 0x9d, 0x39, 0x57, 0x4e, 0x63, 0x2c, 0x24, 0xd3, 0x83, 0xaf, 0xf2, 0x99, 0x3a, 0x4a, 0x6b,
0x60, 0x1f, 0x38, 0xc9, 0x3a, 0xf7, 0xdf, 0x1d, 0x98, 0xdb, 0x8d, 0x13, 0x33, 0xab, 0xe8, 0xd8, 0x14, 0x47, 0x69, 0xaf, 0x01, 0x60, 0xf4, 0x45, 0x47, 0x43, 0xea, 0x70, 0x13, 0xc3, 0x5e, 0xc1,
0x59, 0x45, 0x69, 0xf2, 0x07, 0xda, 0xa2, 0x4b, 0x4b, 0x60, 0x81, 0x6c, 0x0b, 0xba, 0xfe, 0x24, 0xd0, 0xbd, 0x01, 0xeb, 0x16, 0x7f, 0x3d, 0xfb, 0x0b, 0xf2, 0x0b, 0x91, 0x1b, 0xb0, 0x0f, 0x9c,
0xc7, 0xe8, 0xf8, 0x30, 0x4e, 0x4f, 0xfc, 0x74, 0x24, 0x96, 0xe4, 0x56, 0xa3, 0xe7, 0x78, 0xa5, 0x64, 0x9d, 0xfb, 0xef, 0x0e, 0xcc, 0xed, 0xc6, 0x89, 0x99, 0x55, 0x74, 0xec, 0xac, 0xa2, 0x34,
0x1a, 0xb6, 0x01, 0x73, 0xda, 0x36, 0x12, 0x01, 0x16, 0xd1, 0xbf, 0xa2, 0xe4, 0xea, 0x4c, 0x06, 0xf9, 0x03, 0x6d, 0xd1, 0xa5, 0x25, 0xb0, 0x40, 0xb6, 0x05, 0x5d, 0x7f, 0x92, 0x63, 0x74, 0x7c,
0xf6, 0xb2, 0x84, 0x2b, 0x6e, 0x7f, 0x2f, 0x3c, 0x5a, 0x21, 0xe1, 0x75, 0x55, 0xb8, 0xfd, 0xa0, 0x18, 0xa7, 0x27, 0x7e, 0x3a, 0x12, 0x4b, 0x72, 0xab, 0xd1, 0x73, 0xbc, 0x52, 0x0d, 0xdb, 0x80,
0xa9, 0x24, 0x32, 0x99, 0x91, 0x51, 0x65, 0xf7, 0x5f, 0x1d, 0x98, 0xa7, 0x19, 0x40, 0x9d, 0x14, 0x39, 0x6d, 0x1b, 0x89, 0x00, 0x8b, 0xe8, 0x5f, 0x51, 0x72, 0x75, 0x26, 0x03, 0x7b, 0x59, 0xc2,
0x82, 0x48, 0x67, 0xb7, 0x94, 0x09, 0x76, 0x84, 0x4e, 0x96, 0x60, 0xe6, 0x5a, 0x27, 0xba, 0x0d, 0x15, 0xb7, 0xbf, 0x17, 0x1e, 0xad, 0x90, 0xf0, 0xba, 0x2a, 0xdc, 0x7e, 0xd0, 0x54, 0x12, 0x99,
0xdd, 0x6d, 0xf3, 0x54, 0xf7, 0x32, 0xb4, 0x44, 0x49, 0x1f, 0x83, 0x12, 0x49, 0x01, 0xb2, 0x4b, 0xcc, 0xc8, 0xa8, 0xb2, 0xfb, 0xaf, 0x0e, 0xcc, 0xd3, 0x0c, 0xa0, 0x4e, 0x0a, 0x41, 0xa4, 0xb3,
0xd0, 0x3c, 0x8a, 0x13, 0xe5, 0x44, 0x80, 0x4a, 0x04, 0xc6, 0x89, 0x47, 0x78, 0xd1, 0x1f, 0xe4, 0x5b, 0xca, 0x04, 0x3b, 0x42, 0x27, 0x4b, 0x30, 0x73, 0xad, 0x13, 0xdd, 0x86, 0xee, 0xb6, 0x79,
0x27, 0x3a, 0x2f, 0xb6, 0x86, 0x32, 0x8c, 0x9b, 0xa3, 0x66, 0x6b, 0x4e, 0x46, 0x09, 0x75, 0xb7, 0xaa, 0x7b, 0x19, 0x5a, 0xa2, 0xa4, 0x8f, 0x41, 0x89, 0xa4, 0x00, 0xd9, 0x25, 0x68, 0x1e, 0xc5,
0x60, 0xe5, 0x61, 0x3c, 0xe2, 0x46, 0xea, 0xe7, 0x54, 0xa9, 0x73, 0x7f, 0xd3, 0x81, 0x25, 0x45, 0x89, 0x72, 0x22, 0x40, 0x25, 0x02, 0xe3, 0xc4, 0x23, 0xbc, 0xe8, 0x0f, 0xf2, 0x13, 0x9d, 0x17,
0xcc, 0xae, 0x42, 0x13, 0x77, 0xfc, 0x92, 0x3f, 0xaf, 0x0f, 0x00, 0x90, 0xce, 0x23, 0x0a, 0x34, 0x5b, 0x43, 0x19, 0xc6, 0xcd, 0x51, 0xb3, 0x35, 0x27, 0xa3, 0x84, 0xba, 0x5b, 0xb0, 0xf2, 0x30,
0x91, 0x94, 0x18, 0x28, 0xbc, 0x3f, 0x95, 0x16, 0x28, 0x9c, 0x1b, 0xdd, 0xdd, 0x92, 0x4f, 0x50, 0x1e, 0x71, 0x23, 0xf5, 0x73, 0xaa, 0xd4, 0xb9, 0xbf, 0xe9, 0xc0, 0x92, 0x22, 0x66, 0x57, 0xa1,
0x42, 0xdd, 0xbf, 0x70, 0x60, 0xd9, 0x6a, 0x03, 0xa3, 0xb8, 0xd0, 0xcf, 0x72, 0x99, 0x54, 0x95, 0x89, 0x3b, 0x7e, 0xc9, 0x9f, 0xd7, 0x07, 0x00, 0x48, 0xe7, 0x11, 0x05, 0x9a, 0x48, 0x4a, 0x0c,
0xcb, 0x63, 0x42, 0x66, 0x32, 0xb0, 0x61, 0x27, 0x03, 0x75, 0x9a, 0x6a, 0xce, 0x4c, 0x53, 0x5d, 0x14, 0xde, 0x9f, 0x4a, 0x0b, 0x14, 0xce, 0x8d, 0xee, 0x6e, 0xc9, 0x27, 0x28, 0xa1, 0xee, 0x9f,
0x87, 0x56, 0x71, 0xee, 0xde, 0xb4, 0x4c, 0x1f, 0xb6, 0xa8, 0x8e, 0x36, 0x0a, 0x22, 0xe4, 0x33, 0x3b, 0xb0, 0x6c, 0xb5, 0x81, 0x51, 0x5c, 0xe8, 0x67, 0xb9, 0x4c, 0xaa, 0xca, 0xe5, 0x31, 0x21,
0x8c, 0xc3, 0x38, 0x95, 0xc7, 0xd2, 0xa2, 0xe0, 0xde, 0x80, 0xb6, 0x41, 0x8f, 0xdd, 0x88, 0x78, 0x33, 0x19, 0xd8, 0xb0, 0x93, 0x81, 0x3a, 0x4d, 0x35, 0x67, 0xa6, 0xa9, 0xae, 0x43, 0xab, 0x38,
0x7e, 0x12, 0xa7, 0x4f, 0x55, 0x4e, 0x52, 0x16, 0xf5, 0x09, 0x5e, 0xa3, 0x38, 0xc1, 0x73, 0xff, 0x77, 0x6f, 0x5a, 0xa6, 0x0f, 0x5b, 0x54, 0x47, 0x1b, 0x05, 0x11, 0xf2, 0x19, 0xc6, 0x61, 0x9c,
0xd2, 0x81, 0x65, 0x94, 0xc1, 0x20, 0x1a, 0xef, 0xc5, 0x61, 0x30, 0x9c, 0xd1, 0xda, 0x2b, 0x71, 0xca, 0x63, 0x69, 0x51, 0x70, 0x6f, 0x40, 0xdb, 0xa0, 0xc7, 0x6e, 0x44, 0x3c, 0x3f, 0x89, 0xd3,
0x93, 0xe7, 0xd5, 0x4a, 0x16, 0x6d, 0x18, 0x65, 0x5b, 0x05, 0x71, 0x52, 0x11, 0x75, 0x19, 0x35, 0xa7, 0x2a, 0x27, 0x29, 0x8b, 0xfa, 0x04, 0xaf, 0x51, 0x9c, 0xe0, 0xb9, 0x7f, 0xe1, 0xc0, 0x32,
0x15, 0xe5, 0xfc, 0xc0, 0xcf, 0xa4, 0xf0, 0xcb, 0xbd, 0xc8, 0x02, 0x51, 0x9f, 0x10, 0x48, 0xfd, 0xca, 0x60, 0x10, 0x8d, 0xf7, 0xe2, 0x30, 0x18, 0xce, 0x68, 0xed, 0x95, 0xb8, 0xc9, 0xf3, 0x6a,
0x9c, 0x0f, 0x26, 0x41, 0x18, 0x06, 0x82, 0x56, 0x78, 0x2a, 0x75, 0x55, 0xee, 0x0f, 0x1a, 0xd0, 0x25, 0x8b, 0x36, 0x8c, 0xb2, 0xad, 0x82, 0x38, 0xa9, 0x88, 0xba, 0x8c, 0x9a, 0x8a, 0x72, 0x7e,
0x96, 0x96, 0xf2, 0xee, 0x68, 0x2c, 0xb2, 0xff, 0xd2, 0xdf, 0xd3, 0xe6, 0xc2, 0x40, 0x54, 0xbd, 0xe0, 0x67, 0x52, 0xf8, 0xe5, 0x5e, 0x64, 0x81, 0xa8, 0x4f, 0x08, 0xa4, 0x7e, 0xce, 0x07, 0x93,
0xe5, 0x21, 0x1a, 0x48, 0x79, 0x59, 0xe7, 0xaa, 0xcb, 0x7a, 0x11, 0x5a, 0x28, 0x5e, 0x6f, 0x93, 0x20, 0x0c, 0x03, 0x41, 0x2b, 0x3c, 0x95, 0xba, 0x2a, 0xf7, 0x07, 0x0d, 0x68, 0x4b, 0x4b, 0x79,
0x2b, 0x2a, 0xae, 0x69, 0x14, 0x80, 0xaa, 0xdd, 0xa1, 0xda, 0xf9, 0xa2, 0x96, 0x00, 0xcb, 0xf9, 0x77, 0x34, 0x16, 0xd9, 0x7f, 0xe9, 0xef, 0x69, 0x73, 0x61, 0x20, 0xaa, 0xde, 0xf2, 0x10, 0x0d,
0x5c, 0x28, 0x39, 0x9f, 0xef, 0x42, 0x47, 0xb2, 0xa1, 0x79, 0x27, 0xeb, 0x50, 0x08, 0xb8, 0xb5, 0xa4, 0xbc, 0xac, 0x73, 0xd5, 0x65, 0xbd, 0x08, 0x2d, 0x14, 0xaf, 0xb7, 0xc9, 0x15, 0x15, 0xd7,
0x26, 0x9e, 0x45, 0xa9, 0xbe, 0xdc, 0x51, 0x5f, 0x2e, 0xbd, 0xec, 0x4b, 0x45, 0x49, 0x87, 0x61, 0x34, 0x0a, 0x40, 0xd5, 0xee, 0x50, 0xed, 0x7c, 0x51, 0x4b, 0x80, 0xe5, 0x7c, 0x2e, 0x94, 0x9c,
0x62, 0x6e, 0xee, 0xa7, 0x7e, 0x72, 0xa4, 0x76, 0x9f, 0x91, 0x3e, 0xe1, 0x27, 0x98, 0x6d, 0xc1, 0xcf, 0x77, 0xa1, 0x23, 0xd9, 0xd0, 0xbc, 0x93, 0x75, 0x28, 0x04, 0xdc, 0x5a, 0x13, 0xcf, 0xa2,
0x3c, 0x7e, 0xa6, 0xac, 0x75, 0xbd, 0xd2, 0x09, 0x12, 0x76, 0x15, 0xe6, 0xf9, 0x68, 0xcc, 0x55, 0x54, 0x5f, 0xee, 0xa8, 0x2f, 0x97, 0x5e, 0xf6, 0xa5, 0xa2, 0xa4, 0xc3, 0x30, 0x31, 0x37, 0xf7,
0xb0, 0xc5, 0xec, 0xb0, 0x17, 0xd7, 0xc8, 0x13, 0x04, 0x68, 0x02, 0x10, 0x2d, 0x99, 0x00, 0xdb, 0x53, 0x3f, 0x39, 0x52, 0xbb, 0xcf, 0x48, 0x9f, 0xf0, 0x13, 0xcc, 0xb6, 0x60, 0x1e, 0x3f, 0x53,
0xd2, 0x2f, 0x60, 0xf1, 0xfd, 0x91, 0xbb, 0x01, 0xec, 0xa1, 0x90, 0x5a, 0x33, 0x59, 0xfc, 0x3b, 0xd6, 0xba, 0x5e, 0xe9, 0x04, 0x09, 0xbb, 0x0a, 0xf3, 0x7c, 0x34, 0xe6, 0x2a, 0xd8, 0x62, 0x76,
0x73, 0xd0, 0x36, 0x60, 0xd4, 0xe6, 0x31, 0x76, 0x78, 0x30, 0x0a, 0xfc, 0x09, 0xcf, 0x79, 0x2a, 0xd8, 0x8b, 0x6b, 0xe4, 0x09, 0x02, 0x34, 0x01, 0x88, 0x96, 0x4c, 0x80, 0x6d, 0xe9, 0x17, 0xb0,
0x25, 0xb5, 0x84, 0x22, 0x9d, 0x7f, 0x3c, 0x1e, 0xc4, 0xd3, 0x7c, 0x30, 0xe2, 0xe3, 0x94, 0x8b, 0xf8, 0xfe, 0xc8, 0xdd, 0x00, 0xf6, 0x50, 0x48, 0xad, 0x99, 0x2c, 0xfe, 0x9d, 0x39, 0x68, 0x1b,
0x3d, 0x12, 0x37, 0x03, 0x0b, 0x45, 0xba, 0x89, 0xff, 0xcc, 0xa4, 0x13, 0xf2, 0x50, 0x42, 0x55, 0x30, 0x6a, 0xf3, 0x18, 0x3b, 0x3c, 0x18, 0x05, 0xfe, 0x84, 0xe7, 0x3c, 0x95, 0x92, 0x5a, 0x42,
0xea, 0x57, 0xcc, 0x51, 0xb3, 0x48, 0xfd, 0x8a, 0x19, 0x29, 0xdb, 0xa1, 0xf9, 0x1a, 0x3b, 0xf4, 0x91, 0xce, 0x3f, 0x1e, 0x0f, 0xe2, 0x69, 0x3e, 0x18, 0xf1, 0x71, 0xca, 0xc5, 0x1e, 0x89, 0x9b,
0x0e, 0x6c, 0x0a, 0x8b, 0x23, 0x75, 0x73, 0x50, 0x12, 0x93, 0x53, 0x6a, 0xd9, 0x16, 0xac, 0x62, 0x81, 0x85, 0x22, 0xdd, 0xc4, 0x7f, 0x66, 0xd2, 0x09, 0x79, 0x28, 0xa1, 0x2a, 0xf5, 0x2b, 0xe6,
0x9f, 0x95, 0x80, 0x67, 0xc1, 0xc7, 0x22, 0x19, 0xe3, 0x78, 0x15, 0x1c, 0x69, 0x51, 0x1d, 0x2d, 0xa8, 0x59, 0xa4, 0x7e, 0xc5, 0x8c, 0x94, 0xed, 0xd0, 0x7c, 0x8d, 0x1d, 0x7a, 0x07, 0x36, 0x85,
0x5a, 0x71, 0x3c, 0x56, 0xc1, 0x89, 0xd6, 0x7f, 0x66, 0xd3, 0xb6, 0x24, 0x6d, 0x09, 0x77, 0x97, 0xc5, 0x91, 0xba, 0x39, 0x28, 0x89, 0xc9, 0x29, 0xb5, 0x6c, 0x0b, 0x56, 0xb1, 0xcf, 0x4a, 0xc0,
0xa1, 0xbd, 0x9f, 0xc7, 0x89, 0x5a, 0x94, 0x2e, 0x74, 0x44, 0x51, 0x1e, 0x86, 0x5e, 0x80, 0xf3, 0xb3, 0xe0, 0x63, 0x91, 0x8c, 0x71, 0xbc, 0x0a, 0x8e, 0xb4, 0xa8, 0x8e, 0x16, 0xad, 0x38, 0x1e,
0x24, 0x45, 0x8f, 0xe2, 0x24, 0x0e, 0xe3, 0xf1, 0x6c, 0x7f, 0x7a, 0x90, 0x0d, 0xd3, 0x20, 0xc1, 0xab, 0xe0, 0x44, 0xeb, 0x3f, 0xb3, 0x69, 0x5b, 0x92, 0xb6, 0x84, 0xbb, 0xcb, 0xd0, 0xde, 0xcf,
0xc0, 0xc4, 0xfd, 0x3b, 0x07, 0xd6, 0xad, 0x5a, 0x99, 0xbd, 0xf9, 0xbc, 0x10, 0x69, 0x7d, 0x8a, 0xe3, 0x44, 0x2d, 0x4a, 0x17, 0x3a, 0xa2, 0x28, 0x0f, 0x43, 0x2f, 0xc0, 0x79, 0x92, 0xa2, 0x47,
0x25, 0x04, 0x6f, 0xcd, 0x30, 0x87, 0x82, 0x50, 0xe4, 0xcd, 0x1e, 0xcb, 0x83, 0xad, 0x9b, 0xb0, 0x71, 0x12, 0x87, 0xf1, 0x78, 0xb6, 0x3f, 0x3d, 0xc8, 0x86, 0x69, 0x90, 0x60, 0x60, 0xe2, 0xfe,
0xa2, 0x7a, 0xa6, 0x3e, 0x14, 0x52, 0xd8, 0xab, 0x4a, 0xa1, 0xfc, 0xbe, 0x2b, 0x3f, 0x50, 0x2c, 0x9d, 0x03, 0xeb, 0x56, 0xad, 0xcc, 0xde, 0x7c, 0x5e, 0x88, 0xb4, 0x3e, 0xc5, 0x12, 0x82, 0xb7,
0x7e, 0x51, 0xf8, 0xd5, 0x7c, 0x44, 0x63, 0x54, 0x61, 0x7c, 0x5f, 0x7d, 0x6f, 0x3a, 0xf3, 0xaa, 0x66, 0x98, 0x43, 0x41, 0x28, 0xf2, 0x66, 0x8f, 0xe5, 0xc1, 0xd6, 0x4d, 0x58, 0x51, 0x3d, 0x53,
0x07, 0x43, 0x0d, 0x66, 0xee, 0xef, 0x3b, 0x00, 0x45, 0xef, 0x50, 0x30, 0x0a, 0x93, 0x2e, 0xee, 0x1f, 0x0a, 0x29, 0xec, 0x55, 0xa5, 0x50, 0x7e, 0xdf, 0x95, 0x1f, 0x28, 0x16, 0xbf, 0x28, 0xfc,
0xe7, 0x19, 0xe6, 0xfb, 0x0d, 0xe8, 0xe8, 0x03, 0x8c, 0x62, 0x97, 0x68, 0x2b, 0x0c, 0x1d, 0xae, 0x6a, 0x3e, 0xa2, 0x31, 0xaa, 0x30, 0xbe, 0xaf, 0xbe, 0x37, 0x9d, 0x79, 0xd5, 0x83, 0xa1, 0x06,
0x2b, 0xb0, 0x32, 0x0e, 0xe3, 0x03, 0xda, 0x62, 0xe9, 0x74, 0x3d, 0x93, 0x47, 0xc2, 0x5d, 0x01, 0x33, 0xf7, 0xf7, 0x1d, 0x80, 0xa2, 0x77, 0x28, 0x18, 0x85, 0x49, 0x17, 0x37, 0xfe, 0x0c, 0xf3,
0xdf, 0x93, 0x68, 0xb1, 0xa5, 0x34, 0x8d, 0x2d, 0xc5, 0xfd, 0x4e, 0x43, 0xa7, 0xbd, 0x8b, 0x31, 0xfd, 0x06, 0x74, 0xf4, 0x01, 0x46, 0xb1, 0x4b, 0xb4, 0x15, 0x86, 0x0e, 0xd7, 0x15, 0x58, 0x19,
0x9f, 0xaa, 0x65, 0x6c, 0xa7, 0x62, 0x1c, 0x4f, 0xc9, 0x32, 0x53, 0xc2, 0x6a, 0xef, 0xa5, 0xf1, 0x87, 0xf1, 0x01, 0x6d, 0xb1, 0x74, 0xba, 0x9e, 0xc9, 0x23, 0xe1, 0xae, 0x80, 0xef, 0x49, 0xb4,
0xf4, 0x0d, 0xe8, 0xa6, 0xc2, 0xfa, 0x28, 0xd3, 0xd4, 0x7c, 0x81, 0x69, 0x5a, 0x4e, 0xad, 0x7d, 0xd8, 0x52, 0x9a, 0xc6, 0x96, 0xe2, 0x7e, 0xa7, 0xa1, 0xd3, 0xde, 0xc5, 0x98, 0x4f, 0xd5, 0x32,
0xe7, 0xff, 0xc1, 0xaa, 0x3f, 0x3a, 0xe6, 0x69, 0x1e, 0x50, 0x44, 0x43, 0x9b, 0xbe, 0x30, 0xa8, 0xb6, 0x53, 0x31, 0x8e, 0xa7, 0x64, 0x99, 0x29, 0x61, 0xb5, 0xf7, 0xd2, 0x78, 0xfa, 0x06, 0x74,
0x2b, 0x06, 0x4e, 0x7b, 0xf1, 0x15, 0x58, 0x91, 0xc7, 0xf0, 0x9a, 0x52, 0x5e, 0xbe, 0x2a, 0x60, 0x53, 0x61, 0x7d, 0x94, 0x69, 0x6a, 0xbe, 0xc0, 0x34, 0x2d, 0xa7, 0xd6, 0xbe, 0xf3, 0xff, 0x60,
0x24, 0x74, 0xbf, 0xaf, 0x32, 0xec, 0xf6, 0x1a, 0x9e, 0x3e, 0x23, 0xe6, 0xe8, 0x1a, 0xa5, 0xd1, 0xd5, 0x1f, 0x1d, 0xf3, 0x34, 0x0f, 0x28, 0xa2, 0xa1, 0x4d, 0x5f, 0x18, 0xd4, 0x15, 0x03, 0xa7,
0x7d, 0x46, 0x66, 0xbb, 0x47, 0x2a, 0x6c, 0x92, 0xe7, 0x0e, 0x02, 0x94, 0xa7, 0x13, 0xf6, 0x94, 0xbd, 0xf8, 0x0a, 0xac, 0xc8, 0x63, 0x78, 0x4d, 0x29, 0x2f, 0x5f, 0x15, 0x30, 0x12, 0xba, 0xdf,
0x36, 0x5f, 0x65, 0x4a, 0xdd, 0x4f, 0x1d, 0x58, 0xdc, 0x8d, 0x93, 0x5d, 0x79, 0xa2, 0x4e, 0x8a, 0x57, 0x19, 0x76, 0x7b, 0x0d, 0x4f, 0x9f, 0x11, 0x73, 0x74, 0x8d, 0xd2, 0xe8, 0x3e, 0x23, 0xb3,
0xa0, 0x2f, 0xb9, 0xa8, 0xa2, 0xe9, 0x15, 0x37, 0x2a, 0x5e, 0x71, 0x75, 0xaf, 0x5d, 0x2e, 0xef, 0xdd, 0x23, 0x15, 0x36, 0xc9, 0x73, 0x07, 0x01, 0xca, 0xd3, 0x09, 0x7b, 0x4a, 0x9b, 0xaf, 0x32,
0xb5, 0xbf, 0x04, 0x17, 0x28, 0x68, 0x4f, 0xe3, 0x24, 0x4e, 0x51, 0x19, 0xfd, 0x50, 0x6c, 0xac, 0xa5, 0xee, 0xa7, 0x0e, 0x2c, 0xee, 0xc6, 0xc9, 0xae, 0x3c, 0x51, 0x27, 0x45, 0xd0, 0x97, 0x5c,
0x71, 0x94, 0x1f, 0x29, 0x33, 0xf6, 0x22, 0x12, 0x8a, 0x8e, 0xc2, 0xfc, 0x78, 0x20, 0x9c, 0x61, 0x54, 0xd1, 0xf4, 0x8a, 0x1b, 0x15, 0xaf, 0xb8, 0xba, 0xd7, 0x2e, 0x97, 0xf7, 0xda, 0x5f, 0x82,
0xe9, 0x1b, 0x08, 0xeb, 0x56, 0xad, 0x70, 0xbf, 0x08, 0x2d, 0x72, 0x6e, 0x69, 0x58, 0x6f, 0x41, 0x0b, 0x14, 0xb4, 0xa7, 0x71, 0x12, 0xa7, 0xa8, 0x8c, 0x7e, 0x28, 0x36, 0xd6, 0x38, 0xca, 0x8f,
0xeb, 0x28, 0x4e, 0x06, 0x47, 0x41, 0x94, 0x2b, 0xe5, 0xee, 0x16, 0x5e, 0xe7, 0x2e, 0x4d, 0x88, 0x94, 0x19, 0x7b, 0x11, 0x09, 0x45, 0x47, 0x61, 0x7e, 0x3c, 0x10, 0xce, 0xb0, 0xf4, 0x0d, 0x84,
0x26, 0x70, 0x7f, 0x34, 0x07, 0x8b, 0xef, 0x47, 0xc7, 0x71, 0x30, 0xa4, 0x64, 0xfc, 0x84, 0x4f, 0x75, 0xab, 0x56, 0xb8, 0x5f, 0x84, 0x16, 0x39, 0xb7, 0x34, 0xac, 0xb7, 0xa0, 0x75, 0x14, 0x27,
0x62, 0x75, 0xe5, 0x07, 0x7f, 0xe3, 0x54, 0xd0, 0xf1, 0x77, 0x92, 0xcb, 0x6c, 0xba, 0x2a, 0xe2, 0x83, 0xa3, 0x20, 0xca, 0x95, 0x72, 0x77, 0x0b, 0xaf, 0x73, 0x97, 0x26, 0x44, 0x13, 0xb8, 0x3f,
0x76, 0x9f, 0x16, 0xd7, 0xe0, 0x84, 0xea, 0x18, 0x08, 0x3a, 0xf6, 0xa9, 0x79, 0x07, 0x50, 0x96, 0x9a, 0x83, 0xc5, 0xf7, 0xa3, 0xe3, 0x38, 0x18, 0x52, 0x32, 0x7e, 0xc2, 0x27, 0xb1, 0xba, 0xf2,
0x8a, 0x3b, 0x53, 0xf3, 0xc6, 0x9d, 0x29, 0x3a, 0xba, 0x11, 0x27, 0xfb, 0x24, 0x5f, 0x4b, 0x9e, 0x83, 0xbf, 0x71, 0x2a, 0xe8, 0xf8, 0x3b, 0xc9, 0x65, 0x36, 0x5d, 0x15, 0x71, 0xbb, 0x4f, 0x8b,
0x2a, 0x52, 0x20, 0x92, 0x72, 0x91, 0x6c, 0x21, 0xc7, 0x61, 0x51, 0x06, 0x22, 0x26, 0x88, 0xce, 0x6b, 0x70, 0x42, 0x75, 0x0c, 0x04, 0x1d, 0xfb, 0xd4, 0xbc, 0x03, 0x28, 0x4b, 0xc5, 0x9d, 0xa9,
0x85, 0xf8, 0x40, 0xd0, 0x08, 0xe3, 0x6b, 0x42, 0xe8, 0x6c, 0x95, 0xaf, 0x11, 0xb6, 0x84, 0xcc, 0x79, 0xe3, 0xce, 0x14, 0x1d, 0xdd, 0x88, 0x93, 0x7d, 0x92, 0xaf, 0x25, 0x4f, 0x15, 0x29, 0x10,
0x97, 0x60, 0xb4, 0xd0, 0x23, 0xae, 0x0d, 0xa9, 0x18, 0x03, 0x88, 0x6b, 0x7e, 0x65, 0xdc, 0x08, 0x49, 0xb9, 0x48, 0xb6, 0x90, 0xe3, 0xb0, 0x28, 0x03, 0x11, 0x13, 0x44, 0xe7, 0x42, 0x7c, 0x20,
0x5f, 0xc4, 0x0d, 0x05, 0x15, 0xbe, 0xa0, 0xa0, 0xf8, 0x61, 0x78, 0xe0, 0x0f, 0x9f, 0xd2, 0xe5, 0x68, 0x84, 0xf1, 0x35, 0x21, 0x74, 0xb6, 0xca, 0xd7, 0x08, 0x5b, 0x42, 0xe6, 0x4b, 0x30, 0x5a,
0x4e, 0xba, 0x90, 0xd0, 0xf2, 0x6c, 0x10, 0x7b, 0x6d, 0xac, 0x26, 0x1d, 0xfe, 0x35, 0x3d, 0x13, 0xe8, 0x11, 0xd7, 0x86, 0x54, 0x8c, 0x01, 0xc4, 0x35, 0xbf, 0x32, 0x6e, 0x84, 0x2f, 0xe2, 0x86,
0x62, 0x3b, 0xd0, 0xa6, 0x90, 0x4d, 0xae, 0x67, 0x97, 0xd6, 0x73, 0xd5, 0x8c, 0xe9, 0x68, 0x45, 0x82, 0x0a, 0x5f, 0x50, 0x50, 0xfc, 0x30, 0x3c, 0xf0, 0x87, 0x4f, 0xe9, 0x72, 0x27, 0x5d, 0x48,
0x4d, 0x22, 0xf3, 0x80, 0x60, 0xc5, 0xbe, 0x33, 0xf0, 0x0d, 0x60, 0x37, 0x47, 0x23, 0xb9, 0xde, 0x68, 0x79, 0x36, 0x88, 0xbd, 0x36, 0x56, 0x93, 0x0e, 0xff, 0x9a, 0x9e, 0x09, 0xb1, 0x1d, 0x68,
0x3a, 0x64, 0x2c, 0x56, 0xca, 0xb1, 0x56, 0xaa, 0x66, 0xc6, 0x1a, 0xb5, 0x33, 0xe6, 0xde, 0x85, 0x53, 0xc8, 0x26, 0xd7, 0xb3, 0x4b, 0xeb, 0xb9, 0x6a, 0xc6, 0x74, 0xb4, 0xa2, 0x26, 0x91, 0x79,
0xf6, 0x9e, 0x71, 0xc3, 0x93, 0x44, 0x43, 0xdd, 0xed, 0x94, 0xe2, 0x64, 0x20, 0x46, 0x83, 0x0d, 0x40, 0xb0, 0x62, 0xdf, 0x19, 0xf8, 0x06, 0xb0, 0x9b, 0xa3, 0x91, 0x5c, 0x6f, 0x1d, 0x32, 0x16,
0xb3, 0x41, 0xf7, 0xe7, 0x81, 0x3d, 0x08, 0xb2, 0x5c, 0xf7, 0x4f, 0x2c, 0xc7, 0x1b, 0xd0, 0xd1, 0x2b, 0xe5, 0x58, 0x2b, 0x55, 0x33, 0x63, 0x8d, 0xda, 0x19, 0x73, 0xef, 0x42, 0x7b, 0xcf, 0xb8,
0x01, 0x76, 0x71, 0xa3, 0xa1, 0x2d, 0x31, 0xba, 0x69, 0x70, 0x53, 0x5c, 0x85, 0x28, 0x0f, 0x6c, 0xe1, 0x49, 0xa2, 0xa1, 0xee, 0x76, 0x4a, 0x71, 0x32, 0x10, 0xa3, 0xc1, 0x86, 0xd9, 0xa0, 0xfb,
0x0b, 0x96, 0x02, 0x01, 0x95, 0x35, 0x41, 0x51, 0xea, 0x7a, 0xf4, 0xd7, 0x24, 0x68, 0xed, 0xa2, 0xf3, 0xc0, 0x1e, 0x04, 0x59, 0xae, 0xfb, 0x27, 0x96, 0xe3, 0x0d, 0xe8, 0xe8, 0x00, 0xbb, 0xb8,
0x3f, 0x70, 0x60, 0x51, 0x0e, 0x0d, 0xbd, 0x0d, 0xeb, 0x6e, 0xab, 0x18, 0x98, 0x85, 0xd5, 0xdf, 0xd1, 0xd0, 0x96, 0x18, 0xdd, 0x34, 0xb8, 0x29, 0xae, 0x42, 0x94, 0x07, 0xb6, 0x05, 0x4b, 0x81,
0x08, 0xac, 0xca, 0xf0, 0x5c, 0x9d, 0x0c, 0x33, 0x68, 0x26, 0x7e, 0x7e, 0x44, 0x01, 0x4a, 0xcb, 0x80, 0xca, 0x9a, 0xa0, 0x28, 0x75, 0x3d, 0xfa, 0x6b, 0x12, 0xb4, 0x76, 0xd1, 0x1f, 0x38, 0xb0,
0xa3, 0xdf, 0x6c, 0x55, 0x04, 0xcd, 0x42, 0x57, 0x28, 0x60, 0xae, 0xbb, 0x84, 0x2a, 0x4c, 0x72, 0x28, 0x87, 0x86, 0xde, 0x86, 0x75, 0xb7, 0x55, 0x0c, 0xcc, 0xc2, 0xea, 0x6f, 0x04, 0x56, 0x65,
0x05, 0xc7, 0x41, 0xd1, 0xe5, 0x01, 0x81, 0xeb, 0x33, 0x01, 0x79, 0x31, 0xa3, 0x80, 0x8b, 0xf9, 0x78, 0xae, 0x4e, 0x86, 0x19, 0x34, 0x13, 0x3f, 0x3f, 0xa2, 0x00, 0xa5, 0xe5, 0xd1, 0x6f, 0xb6,
0x92, 0x2c, 0xca, 0xf3, 0x25, 0x49, 0x3d, 0x5d, 0xef, 0xf6, 0xa1, 0x77, 0x87, 0x87, 0x3c, 0xe7, 0x2a, 0x82, 0x66, 0xa1, 0x2b, 0x14, 0x30, 0xd7, 0x5d, 0x42, 0x15, 0x26, 0xb9, 0x82, 0xe3, 0xa0,
0x37, 0xc3, 0xb0, 0xcc, 0xff, 0x02, 0x9c, 0xaf, 0xa9, 0x93, 0x4e, 0xcb, 0x3d, 0x58, 0xbb, 0xc3, 0xe8, 0xf2, 0x80, 0xc0, 0xf5, 0x99, 0x80, 0xbc, 0x98, 0x51, 0xc0, 0xc5, 0x7c, 0x49, 0x16, 0xe5,
0x0f, 0xa6, 0xe3, 0x07, 0xfc, 0xb8, 0x38, 0xb8, 0x63, 0xd0, 0xcc, 0x8e, 0xe2, 0x13, 0xb9, 0xb6, 0xf9, 0x92, 0xa4, 0x9e, 0xae, 0x77, 0xfb, 0xd0, 0xbb, 0xc3, 0x43, 0x9e, 0xf3, 0x9b, 0x61, 0x58,
0xf4, 0x9b, 0xbd, 0x06, 0x10, 0x22, 0xcd, 0x20, 0x4b, 0xf8, 0x50, 0xdd, 0x85, 0x23, 0x64, 0x3f, 0xe6, 0x7f, 0x01, 0xce, 0xd7, 0xd4, 0x49, 0xa7, 0xe5, 0x1e, 0xac, 0xdd, 0xe1, 0x07, 0xd3, 0xf1,
0xe1, 0x43, 0xf7, 0x1d, 0x60, 0x26, 0x1f, 0x39, 0x04, 0xb4, 0x03, 0xd3, 0x83, 0x41, 0x36, 0xcb, 0x03, 0x7e, 0x5c, 0x1c, 0xdc, 0x31, 0x68, 0x66, 0x47, 0xf1, 0x89, 0x5c, 0x5b, 0xfa, 0xcd, 0x5e,
0x72, 0x3e, 0x51, 0x97, 0xfc, 0x4c, 0xc8, 0xbd, 0x02, 0x9d, 0x3d, 0x7f, 0xe6, 0xf1, 0x8f, 0xe4, 0x03, 0x08, 0x91, 0x66, 0x90, 0x25, 0x7c, 0xa8, 0xee, 0xc2, 0x11, 0xb2, 0x9f, 0xf0, 0xa1, 0xfb,
0xf5, 0x62, 0x8c, 0x8d, 0xfd, 0x19, 0x8a, 0xb2, 0x8e, 0x8d, 0xa9, 0xda, 0xfd, 0x8f, 0x06, 0x2c, 0x0e, 0x30, 0x93, 0x8f, 0x1c, 0x02, 0xda, 0x81, 0xe9, 0xc1, 0x20, 0x9b, 0x65, 0x39, 0x9f, 0xa8,
0x08, 0x4a, 0xe4, 0x3a, 0xe2, 0x59, 0x1e, 0x44, 0xe2, 0xd0, 0x4a, 0x72, 0x35, 0xa0, 0x8a, 0x6c, 0x4b, 0x7e, 0x26, 0xe4, 0x5e, 0x81, 0xce, 0x9e, 0x3f, 0xf3, 0xf8, 0x47, 0xf2, 0x7a, 0x31, 0xc6,
0x34, 0x6a, 0x64, 0x43, 0x7a, 0xab, 0xea, 0x5e, 0x91, 0x14, 0x02, 0x0b, 0x43, 0xb7, 0xa6, 0xb8, 0xc6, 0xfe, 0x0c, 0x45, 0x59, 0xc7, 0xc6, 0x54, 0xed, 0xfe, 0x47, 0x03, 0x16, 0x04, 0x25, 0x72,
0x0c, 0x20, 0x82, 0xb3, 0x02, 0x28, 0x25, 0x4b, 0x0a, 0x6b, 0x23, 0xfa, 0xa7, 0x84, 0x56, 0x8a, 0x1d, 0xf1, 0x2c, 0x0f, 0x22, 0x71, 0x68, 0x25, 0xb9, 0x1a, 0x50, 0x45, 0x36, 0x1a, 0x35, 0xb2,
0x83, 0x09, 0xd5, 0xda, 0xb4, 0x45, 0x21, 0x35, 0x15, 0x9b, 0x56, 0xb1, 0x5d, 0x4b, 0xaf, 0x60, 0x21, 0xbd, 0x55, 0x75, 0xaf, 0x48, 0x0a, 0x81, 0x85, 0xa1, 0x5b, 0x53, 0x5c, 0x06, 0x10, 0xc1,
0xbb, 0x84, 0x0b, 0xfb, 0x22, 0xdb, 0x05, 0xaf, 0x60, 0xbb, 0x5c, 0x06, 0xab, 0xf7, 0x38, 0xf7, 0x59, 0x01, 0x94, 0x92, 0x25, 0x85, 0xb5, 0x11, 0xfd, 0x53, 0x42, 0x2b, 0xc5, 0xc1, 0x84, 0x6a,
0x38, 0xee, 0x8a, 0x4a, 0x9c, 0xbe, 0xeb, 0xc0, 0xaa, 0xdc, 0xd0, 0x75, 0x1d, 0x7b, 0xc3, 0xda, 0x6d, 0xda, 0xa2, 0x90, 0x9a, 0x8a, 0x4d, 0xab, 0xd8, 0xae, 0xa5, 0x57, 0xb0, 0x5d, 0xc2, 0x85,
0xfd, 0x9d, 0xba, 0xf3, 0x88, 0x37, 0x61, 0x99, 0xf6, 0x64, 0x9d, 0x15, 0x92, 0x29, 0x2c, 0x0b, 0x7d, 0x91, 0xed, 0x82, 0x57, 0xb0, 0x5d, 0x2e, 0x83, 0xd5, 0x7b, 0x9c, 0x7b, 0x1c, 0x77, 0x45,
0xc4, 0x71, 0xa8, 0x0c, 0xfb, 0x24, 0x08, 0xe5, 0xa2, 0x98, 0x90, 0x4a, 0x2c, 0x61, 0x7c, 0x4c, 0x25, 0x4e, 0xdf, 0x75, 0x60, 0x55, 0x6e, 0xe8, 0xba, 0x8e, 0xbd, 0x61, 0xed, 0xfe, 0x4e, 0xdd,
0x4b, 0xe2, 0x78, 0xba, 0xec, 0xfe, 0xb5, 0x03, 0x6b, 0x46, 0x87, 0xa5, 0x14, 0xde, 0x00, 0x75, 0x79, 0xc4, 0x9b, 0xb0, 0x4c, 0x7b, 0xb2, 0xce, 0x0a, 0xc9, 0x14, 0x96, 0x05, 0xe2, 0x38, 0x54,
0x59, 0x40, 0x24, 0x8f, 0x84, 0x32, 0x9d, 0xb3, 0x9d, 0x93, 0xe2, 0x33, 0x8b, 0x98, 0x16, 0xd3, 0x86, 0x7d, 0x12, 0x84, 0x72, 0x51, 0x4c, 0x48, 0x25, 0x96, 0x30, 0x3e, 0xa6, 0x25, 0x71, 0x3c,
0x9f, 0x51, 0x07, 0xb3, 0xe9, 0x44, 0x7a, 0x20, 0x26, 0x84, 0x82, 0x74, 0xc2, 0xf9, 0x53, 0x4d, 0x5d, 0x76, 0xff, 0xca, 0x81, 0x35, 0xa3, 0xc3, 0x52, 0x0a, 0x6f, 0x80, 0xba, 0x2c, 0x20, 0x92,
0x32, 0x47, 0x24, 0x16, 0x46, 0x67, 0xc1, 0xe8, 0x4b, 0x68, 0x22, 0x71, 0xfd, 0xc9, 0x06, 0xdd, 0x47, 0x42, 0x99, 0xce, 0xd9, 0xce, 0x49, 0xf1, 0x99, 0x45, 0x4c, 0x8b, 0xe9, 0xcf, 0xa8, 0x83,
0x7f, 0x70, 0x60, 0x5d, 0x38, 0x85, 0xd2, 0xe5, 0xd6, 0x57, 0x33, 0x17, 0x84, 0x17, 0x2c, 0x34, 0xd9, 0x74, 0x22, 0x3d, 0x10, 0x13, 0x42, 0x41, 0x3a, 0xe1, 0xfc, 0xa9, 0x26, 0x99, 0x23, 0x12,
0x72, 0xf7, 0x8c, 0x27, 0xcb, 0xec, 0x0b, 0xaf, 0xe8, 0xc8, 0xea, 0x3b, 0x00, 0xa7, 0xac, 0xc5, 0x0b, 0xa3, 0xb3, 0x60, 0xf4, 0x25, 0x34, 0x91, 0xb8, 0xfe, 0x64, 0x83, 0xee, 0x3f, 0x38, 0xb0,
0x5c, 0xdd, 0x5a, 0xbc, 0x60, 0xa6, 0xeb, 0x92, 0x25, 0xf3, 0xb5, 0xc9, 0x92, 0x5b, 0x8b, 0x30, 0x2e, 0x9c, 0x42, 0xe9, 0x72, 0xeb, 0xab, 0x99, 0x0b, 0xc2, 0x0b, 0x16, 0x1a, 0xb9, 0x7b, 0xc6,
0x9f, 0x0d, 0xe3, 0x84, 0xbb, 0x9b, 0xb0, 0x61, 0x0f, 0x4e, 0x9a, 0xa0, 0xef, 0x39, 0xd0, 0xbb, 0x93, 0x65, 0xf6, 0x85, 0x57, 0x74, 0x64, 0xf5, 0x1d, 0x80, 0x53, 0xd6, 0x62, 0xae, 0x6e, 0x2d,
0x27, 0x52, 0x87, 0x41, 0x34, 0xde, 0x0d, 0xb2, 0x3c, 0x4e, 0xf5, 0x5d, 0xf4, 0x4b, 0x00, 0x59, 0x5e, 0x30, 0xd3, 0x75, 0xc9, 0x92, 0xf9, 0xda, 0x64, 0xc9, 0xad, 0x45, 0x98, 0xcf, 0x86, 0x71,
0xee, 0xa7, 0xb9, 0xb8, 0xa3, 0x25, 0xd3, 0x1c, 0x05, 0x82, 0x7d, 0xe4, 0xd1, 0x48, 0xd4, 0x8a, 0xc2, 0xdd, 0x4d, 0xd8, 0xb0, 0x07, 0x27, 0x4d, 0xd0, 0xf7, 0x1c, 0xe8, 0xdd, 0x13, 0xa9, 0xc3,
0xb5, 0xd1, 0x65, 0x5c, 0x18, 0xba, 0x9f, 0x30, 0x88, 0x0f, 0x0f, 0x33, 0xae, 0xdd, 0x56, 0x13, 0x20, 0x1a, 0xef, 0x06, 0x59, 0x1e, 0xa7, 0xfa, 0x2e, 0xfa, 0x25, 0x80, 0x2c, 0xf7, 0xd3, 0x5c,
0xc3, 0xc8, 0x17, 0x35, 0x1e, 0x63, 0x3d, 0x7e, 0x4c, 0xa6, 0x56, 0xf8, 0x83, 0x25, 0xd4, 0xfd, 0xdc, 0xd1, 0x92, 0x69, 0x8e, 0x02, 0xc1, 0x3e, 0xf2, 0x68, 0x24, 0x6a, 0xc5, 0xda, 0xe8, 0x32,
0x2b, 0x07, 0x56, 0x8a, 0x4e, 0xde, 0x45, 0xd0, 0xb6, 0x0e, 0xa2, 0x6b, 0x86, 0x75, 0x50, 0x09, 0x2e, 0x0c, 0xdd, 0x4f, 0x18, 0xc4, 0x87, 0x87, 0x19, 0xd7, 0x6e, 0xab, 0x89, 0x61, 0xe4, 0x8b,
0x98, 0x60, 0x34, 0x08, 0x22, 0xd9, 0x37, 0x03, 0x21, 0x8d, 0x95, 0xa5, 0x78, 0xaa, 0xee, 0xc3, 0x1a, 0x8f, 0xb1, 0x1e, 0x3f, 0x26, 0x53, 0x2b, 0xfc, 0xc1, 0x12, 0xea, 0xfe, 0xa5, 0x03, 0x2b,
0x99, 0x90, 0x38, 0xec, 0xce, 0xf1, 0x6b, 0x71, 0x19, 0x4e, 0x96, 0xe8, 0x8a, 0xdd, 0x24, 0xa7, 0x45, 0x27, 0xef, 0x22, 0x68, 0x5b, 0x07, 0xd1, 0x35, 0xc3, 0x3a, 0xa8, 0x04, 0x4c, 0x30, 0x1a,
0xaf, 0x16, 0x84, 0x43, 0x2c, 0x8b, 0x6a, 0x7f, 0x5a, 0x24, 0x14, 0x7f, 0xba, 0x7f, 0xe0, 0xc0, 0x04, 0x91, 0xec, 0x9b, 0x81, 0x90, 0xc6, 0xca, 0x52, 0x3c, 0x55, 0xf7, 0xe1, 0x4c, 0x48, 0x1c,
0xf9, 0x9a, 0xc9, 0x95, 0x9a, 0x71, 0x07, 0xd6, 0x0e, 0x75, 0xa5, 0x9a, 0x00, 0xa1, 0x1e, 0x9b, 0x76, 0xe7, 0xf8, 0xb5, 0xb8, 0x0c, 0x27, 0x4b, 0x74, 0xc5, 0x6e, 0x92, 0xd3, 0x57, 0x0b, 0xc2,
0x52, 0x8a, 0x4a, 0x83, 0xf6, 0xaa, 0x1f, 0xa0, 0x7b, 0x4c, 0x79, 0x23, 0x31, 0xa5, 0xd6, 0x3d, 0x21, 0x96, 0x45, 0xb5, 0x3f, 0x2d, 0x12, 0x8a, 0x3f, 0xdd, 0x3f, 0x70, 0xe0, 0x7c, 0xcd, 0xe4,
0x91, 0x6a, 0xc5, 0xce, 0xf7, 0x1b, 0xd0, 0x15, 0x47, 0x15, 0xe2, 0x35, 0x12, 0x4f, 0xd9, 0x07, 0x4a, 0xcd, 0xb8, 0x03, 0x6b, 0x87, 0xba, 0x52, 0x4d, 0x80, 0x50, 0x8f, 0x4d, 0x29, 0x45, 0xa5,
0xb0, 0x28, 0xdf, 0x7e, 0xb1, 0xb3, 0xb2, 0x59, 0xfb, 0xb5, 0x59, 0x7f, 0xb3, 0x0c, 0x4b, 0xd9, 0x41, 0x7b, 0xd5, 0x0f, 0xd0, 0x3d, 0xa6, 0xbc, 0x91, 0x98, 0x52, 0xeb, 0x9e, 0x48, 0xb5, 0x62,
0x59, 0xff, 0xed, 0x4f, 0xff, 0xf9, 0x0f, 0x1b, 0xcb, 0xac, 0xbd, 0x7d, 0xfc, 0xf6, 0xf6, 0x98, 0xe7, 0xfb, 0x0d, 0xe8, 0x8a, 0xa3, 0x0a, 0xf1, 0x1a, 0x89, 0xa7, 0xec, 0x03, 0x58, 0x94, 0xaf,
0x47, 0x19, 0xf2, 0xf8, 0x55, 0x80, 0xe2, 0xf9, 0x14, 0xeb, 0x69, 0x27, 0xa3, 0xf4, 0xdc, 0xab, 0xc9, 0xd8, 0x59, 0xd9, 0xac, 0xfd, 0x7e, 0xad, 0xbf, 0x59, 0x86, 0xa5, 0xec, 0xac, 0xff, 0xf6,
0x7f, 0xbe, 0xa6, 0x46, 0xf2, 0x3d, 0x4f, 0x7c, 0xd7, 0xdd, 0x2e, 0xf2, 0x0d, 0xa2, 0x20, 0x17, 0xa7, 0xff, 0xfc, 0x87, 0x8d, 0x65, 0xd6, 0xde, 0x3e, 0x7e, 0x7b, 0x7b, 0xcc, 0xa3, 0x0c, 0x79,
0x6f, 0xa9, 0xde, 0x73, 0xb6, 0xd8, 0x08, 0x3a, 0xe6, 0x33, 0x2a, 0xa6, 0x42, 0xe6, 0x9a, 0xb7, 0xfc, 0x2a, 0x40, 0xf1, 0xce, 0x8a, 0xf5, 0xb4, 0x93, 0x51, 0x7a, 0x40, 0xd6, 0x3f, 0x5f, 0x53,
0x59, 0xfd, 0x0b, 0xb5, 0x75, 0x2a, 0x5f, 0x40, 0x6d, 0x9c, 0x75, 0x57, 0xb1, 0x8d, 0x29, 0x51, 0x23, 0xf9, 0x9e, 0x27, 0xbe, 0xeb, 0x6e, 0x17, 0xf9, 0x06, 0x51, 0x90, 0x8b, 0x47, 0x57, 0xef,
0xe8, 0x56, 0x76, 0xfe, 0xf1, 0x02, 0xb4, 0x74, 0xda, 0x89, 0x7d, 0x08, 0xcb, 0xd6, 0xe9, 0x0e, 0x39, 0x5b, 0x6c, 0x04, 0x1d, 0xf3, 0x19, 0x15, 0x53, 0x21, 0x73, 0xcd, 0x23, 0xae, 0xfe, 0x85,
0x53, 0x8c, 0xeb, 0x0e, 0x83, 0xfa, 0x17, 0xeb, 0x2b, 0x65, 0xb3, 0x97, 0xa8, 0xd9, 0x1e, 0xdb, 0xda, 0x3a, 0x95, 0x2f, 0xa0, 0x36, 0xce, 0xba, 0xab, 0xd8, 0xc6, 0x94, 0x28, 0x74, 0x2b, 0x3b,
0xc4, 0x66, 0xe5, 0xf1, 0xc8, 0x36, 0x9d, 0x69, 0x89, 0x2b, 0x75, 0x4f, 0xa1, 0x6b, 0x9f, 0xc8, 0xff, 0x78, 0x01, 0x5a, 0x3a, 0xed, 0xc4, 0x3e, 0x84, 0x65, 0xeb, 0x74, 0x87, 0x29, 0xc6, 0x75,
0xb0, 0x8b, 0xb6, 0x41, 0x29, 0xb5, 0xf6, 0xda, 0x29, 0xb5, 0xb2, 0xb9, 0x8b, 0xd4, 0xdc, 0x26, 0x87, 0x41, 0xfd, 0x8b, 0xf5, 0x95, 0xb2, 0xd9, 0x4b, 0xd4, 0x6c, 0x8f, 0x6d, 0x62, 0xb3, 0xf2,
0xdb, 0x30, 0x9b, 0xd3, 0xe9, 0x20, 0x4e, 0x97, 0x20, 0xcd, 0xf7, 0x55, 0xec, 0x35, 0xbd, 0xd4, 0x78, 0x64, 0x9b, 0xce, 0xb4, 0xc4, 0x95, 0xba, 0xa7, 0xd0, 0xb5, 0x4f, 0x64, 0xd8, 0x45, 0xdb,
0x75, 0xef, 0xae, 0xf4, 0xa2, 0x55, 0x1f, 0x5f, 0xb9, 0x3d, 0x6a, 0x8a, 0x31, 0x9a, 0x50, 0xf3, 0xa0, 0x94, 0x5a, 0x7b, 0xed, 0x94, 0x5a, 0xd9, 0xdc, 0x45, 0x6a, 0x6e, 0x93, 0x6d, 0x98, 0xcd,
0x79, 0x15, 0xfb, 0x16, 0xb4, 0xf4, 0x9b, 0x0a, 0x76, 0xce, 0x78, 0xc8, 0x62, 0x3e, 0xf4, 0xe8, 0xe9, 0x74, 0x10, 0xa7, 0x4b, 0x90, 0xe6, 0xfb, 0x2a, 0xf6, 0x9a, 0x5e, 0xea, 0xba, 0x77, 0x57,
0xf7, 0xaa, 0x15, 0x75, 0x4b, 0x65, 0x72, 0x46, 0x81, 0x78, 0x00, 0x67, 0xa5, 0x93, 0x7a, 0xc0, 0x7a, 0xd1, 0xaa, 0x8f, 0xaf, 0xdc, 0x1e, 0x35, 0xc5, 0x18, 0x4d, 0xa8, 0xf9, 0xbc, 0x8a, 0x7d,
0x7f, 0x9c, 0x91, 0xd4, 0xbc, 0x0a, 0xbb, 0xee, 0xb0, 0x1b, 0xb0, 0xa4, 0x9e, 0xaa, 0xb0, 0xcd, 0x0b, 0x5a, 0xfa, 0x4d, 0x05, 0x3b, 0x67, 0x3c, 0x64, 0x31, 0x1f, 0x7a, 0xf4, 0x7b, 0xd5, 0x8a,
0xfa, 0x27, 0x37, 0xfd, 0x73, 0x15, 0x5c, 0xea, 0xf3, 0x4d, 0x80, 0xe2, 0x99, 0x85, 0x96, 0xfc, 0xba, 0xa5, 0x32, 0x39, 0xa3, 0x40, 0x3c, 0x80, 0xb3, 0xd2, 0x49, 0x3d, 0xe0, 0x3f, 0xce, 0x48,
0xca, 0xe3, 0x0f, 0x3d, 0x89, 0x35, 0x6f, 0x32, 0xc6, 0xf4, 0xa8, 0xc4, 0x7e, 0xc5, 0xc1, 0x5e, 0x6a, 0x5e, 0x85, 0x5d, 0x77, 0xd8, 0x0d, 0x58, 0x52, 0x4f, 0x55, 0xd8, 0x66, 0xfd, 0x93, 0x9b,
0x2f, 0xe8, 0x6b, 0xdf, 0x77, 0xbc, 0x80, 0xa1, 0xbb, 0x49, 0x73, 0xb7, 0xca, 0x48, 0x95, 0x22, 0xfe, 0xb9, 0x0a, 0x2e, 0xf5, 0xf9, 0x26, 0x40, 0xf1, 0xcc, 0x42, 0x4b, 0x7e, 0xe5, 0xf1, 0x87,
0x7e, 0xa2, 0xae, 0x03, 0xdf, 0x81, 0xb6, 0xf1, 0x74, 0x83, 0x29, 0x0e, 0xd5, 0x67, 0x1f, 0xfd, 0x9e, 0xc4, 0x9a, 0x37, 0x19, 0x63, 0x7a, 0x54, 0x62, 0xbf, 0xe2, 0x60, 0xaf, 0x17, 0xf4, 0xb5,
0x7e, 0x5d, 0x95, 0xec, 0xee, 0x57, 0x60, 0xd9, 0x7a, 0x83, 0xa1, 0x35, 0xa3, 0xee, 0x85, 0x87, 0xef, 0x3b, 0x5e, 0xc0, 0xd0, 0xdd, 0xa4, 0xb9, 0x5b, 0x65, 0xa4, 0x4a, 0x11, 0x3f, 0x51, 0xd7,
0xd6, 0x8c, 0xfa, 0x67, 0x1b, 0xdf, 0x84, 0xb6, 0xf1, 0x62, 0x82, 0x19, 0xd7, 0xa0, 0x4a, 0x6f, 0x81, 0xef, 0x40, 0xdb, 0x78, 0xba, 0xc1, 0x14, 0x87, 0xea, 0xb3, 0x8f, 0x7e, 0xbf, 0xae, 0x4a,
0x25, 0x74, 0x8f, 0xea, 0x1e, 0x58, 0x6c, 0xd0, 0x78, 0xbb, 0x6e, 0x0b, 0xc7, 0x4b, 0x77, 0x62, 0x76, 0xf7, 0x2b, 0xb0, 0x6c, 0xbd, 0xc1, 0xd0, 0x9a, 0x51, 0xf7, 0xc2, 0x43, 0x6b, 0x46, 0xfd,
0x51, 0x48, 0x3e, 0x84, 0xae, 0xfd, 0x86, 0x42, 0x6b, 0x55, 0xed, 0x6b, 0x0c, 0xad, 0x55, 0xa7, 0xb3, 0x8d, 0x6f, 0x42, 0xdb, 0x78, 0x31, 0xc1, 0x8c, 0x6b, 0x50, 0xa5, 0xb7, 0x12, 0xba, 0x47,
0x3c, 0xbc, 0x90, 0x02, 0xb9, 0xb5, 0xae, 0x1b, 0xd9, 0xfe, 0x44, 0x1e, 0xba, 0x3c, 0x67, 0x5f, 0x75, 0x0f, 0x2c, 0x36, 0x68, 0xbc, 0x5d, 0xb7, 0x85, 0xe3, 0xa5, 0x3b, 0xb1, 0x28, 0x24, 0x1f,
0x47, 0xd3, 0x21, 0x2f, 0x29, 0xb3, 0xe2, 0xe5, 0x88, 0x7d, 0x95, 0x59, 0x4b, 0x7b, 0xe5, 0x3e, 0x42, 0xd7, 0x7e, 0x43, 0xa1, 0xb5, 0xaa, 0xf6, 0x35, 0x86, 0xd6, 0xaa, 0x53, 0x1e, 0x5e, 0x48,
0xb3, 0xbb, 0x46, 0xcc, 0xdb, 0xac, 0x18, 0x81, 0xb0, 0xd0, 0x74, 0x59, 0xd9, 0xb0, 0xd0, 0xe6, 0x81, 0xdc, 0x5a, 0xd7, 0x8d, 0x6c, 0x7f, 0x22, 0x0f, 0x5d, 0x9e, 0xb3, 0xaf, 0xa3, 0xe9, 0x90,
0x7d, 0x66, 0xc3, 0x42, 0x5b, 0x77, 0x9a, 0xcb, 0x16, 0x3a, 0x0f, 0x90, 0x47, 0x04, 0x2b, 0xa5, 0x97, 0x94, 0x59, 0xf1, 0x72, 0xc4, 0xbe, 0xca, 0xac, 0xa5, 0xbd, 0x72, 0x9f, 0xd9, 0x5d, 0x23,
0x7b, 0x00, 0x5a, 0x59, 0xea, 0x2f, 0x4e, 0xf5, 0x2f, 0xbd, 0xf8, 0xfa, 0x80, 0x6d, 0x66, 0x94, 0xe6, 0x6d, 0x56, 0x8c, 0x40, 0x58, 0x68, 0xba, 0xac, 0x6c, 0x58, 0x68, 0xf3, 0x3e, 0xb3, 0x61,
0x79, 0xd9, 0x56, 0xf7, 0xdc, 0x7e, 0x0d, 0x3a, 0xe6, 0xdd, 0x77, 0x6d, 0xb3, 0x6b, 0x6e, 0xec, 0xa1, 0xad, 0x3b, 0xcd, 0x65, 0x0b, 0x9d, 0x07, 0xc8, 0x23, 0x82, 0x95, 0xd2, 0x3d, 0x00, 0xad,
0x6b, 0x9b, 0x5d, 0x77, 0x59, 0x5e, 0x2d, 0x2e, 0xeb, 0x98, 0xcd, 0xb0, 0x6f, 0xc2, 0x8a, 0x71, 0x2c, 0xf5, 0x17, 0xa7, 0xfa, 0x97, 0x5e, 0x7c, 0x7d, 0xc0, 0x36, 0x33, 0xca, 0xbc, 0x6c, 0xab,
0xf1, 0x65, 0x7f, 0x16, 0x0d, 0xb5, 0xf0, 0x54, 0xaf, 0x45, 0xf6, 0xeb, 0xfc, 0x33, 0xf7, 0x1c, 0x7b, 0x6e, 0xbf, 0x06, 0x1d, 0xf3, 0xee, 0xbb, 0xb6, 0xd9, 0x35, 0x37, 0xf6, 0xb5, 0xcd, 0xae,
0x31, 0x5e, 0x73, 0x2d, 0xc6, 0x28, 0x38, 0xb7, 0xa1, 0x6d, 0x5e, 0xaa, 0x79, 0x01, 0xdf, 0x73, 0xbb, 0x2c, 0xaf, 0x16, 0x97, 0x75, 0xcc, 0x66, 0xd8, 0x37, 0x61, 0xc5, 0xb8, 0xf8, 0xb2, 0x3f,
0x46, 0x95, 0x79, 0x43, 0xf0, 0xba, 0xc3, 0xfe, 0xd8, 0x81, 0x8e, 0x75, 0x45, 0xc5, 0xca, 0xf3, 0x8b, 0x86, 0x5a, 0x78, 0xaa, 0xd7, 0x22, 0xfb, 0x75, 0xfe, 0x99, 0x7b, 0x8e, 0x18, 0xaf, 0xb9,
0x96, 0xf8, 0xf4, 0xcc, 0x3a, 0x93, 0x91, 0xeb, 0x51, 0x27, 0x1f, 0x6c, 0x7d, 0xc5, 0x9a, 0xe4, 0x16, 0x63, 0x14, 0x9c, 0xdb, 0xd0, 0x36, 0x2f, 0xd5, 0xbc, 0x80, 0xef, 0x39, 0xa3, 0xca, 0xbc,
0x4f, 0x2c, 0x3f, 0xff, 0x5a, 0xf9, 0x59, 0xe3, 0xf3, 0x32, 0x81, 0x79, 0x75, 0xf4, 0xf9, 0x75, 0x21, 0x78, 0xdd, 0x61, 0x7f, 0xec, 0x40, 0xc7, 0xba, 0xa2, 0x62, 0xe5, 0x79, 0x4b, 0x7c, 0x7a,
0x87, 0xbd, 0x27, 0x9e, 0xbe, 0xaa, 0xb8, 0x9e, 0x19, 0xc6, 0xad, 0x3c, 0x65, 0xe6, 0x2b, 0xd1, 0x66, 0x9d, 0xc9, 0xc8, 0xf5, 0xa8, 0x93, 0x0f, 0xb6, 0xbe, 0x62, 0x4d, 0xf2, 0x27, 0x96, 0x9f,
0xab, 0xce, 0x75, 0x87, 0x7d, 0x5b, 0xbc, 0x5e, 0x94, 0xdf, 0xd2, 0xcc, 0xbf, 0xea, 0xf7, 0xee, 0x7f, 0xad, 0xfc, 0xac, 0xf1, 0x79, 0x99, 0xc0, 0xbc, 0x3a, 0xfa, 0xfc, 0xba, 0xc3, 0xde, 0x13,
0x9b, 0x34, 0x9a, 0x4b, 0xee, 0x79, 0x6b, 0x34, 0x65, 0xeb, 0xbe, 0x07, 0x50, 0x24, 0x69, 0x58, 0x4f, 0x5f, 0x55, 0x5c, 0xcf, 0x0c, 0xe3, 0x56, 0x9e, 0x32, 0xf3, 0x95, 0xe8, 0x55, 0xe7, 0xba,
0x29, 0x63, 0xa1, 0xed, 0x5e, 0x35, 0x8f, 0x63, 0xaf, 0xa8, 0x4a, 0x6c, 0x20, 0xc7, 0x6f, 0x09, 0xc3, 0xbe, 0x2d, 0x5e, 0x2f, 0xca, 0x6f, 0x69, 0xe6, 0x5f, 0xf5, 0x7b, 0xf7, 0x4d, 0x1a, 0xcd,
0x61, 0x94, 0xf4, 0x99, 0x5e, 0xd2, 0x6a, 0xb2, 0xa5, 0xdf, 0xaf, 0xab, 0xaa, 0x13, 0x45, 0xc5, 0x25, 0xf7, 0xbc, 0x35, 0x9a, 0xb2, 0x75, 0xdf, 0x03, 0x28, 0x92, 0x34, 0xac, 0x94, 0xb1, 0xd0,
0x9f, 0x3d, 0x86, 0xe5, 0x07, 0x71, 0xfc, 0x74, 0x9a, 0xe8, 0x34, 0xa2, 0x9d, 0x33, 0xd8, 0xf5, 0x76, 0xaf, 0x9a, 0xc7, 0xb1, 0x57, 0x54, 0x25, 0x36, 0x90, 0xe3, 0xb7, 0x84, 0x30, 0x4a, 0xfa,
0xb3, 0xa3, 0x7e, 0x69, 0x14, 0xee, 0x65, 0x62, 0xd5, 0x67, 0x3d, 0x83, 0xd5, 0xf6, 0x27, 0x45, 0x4c, 0x2f, 0x69, 0x35, 0xd9, 0xd2, 0xef, 0xd7, 0x55, 0xd5, 0x89, 0xa2, 0xe2, 0xcf, 0x1e, 0xc3,
0x8a, 0xe8, 0x39, 0xf3, 0x61, 0x4d, 0xef, 0x71, 0xba, 0xe3, 0x7d, 0x9b, 0x8d, 0x99, 0xa9, 0xa9, 0xf2, 0x83, 0x38, 0x7e, 0x3a, 0x4d, 0x74, 0x1a, 0xd1, 0xce, 0x19, 0xec, 0xfa, 0xd9, 0x51, 0xbf,
0x34, 0x61, 0x79, 0x1d, 0xaa, 0xb7, 0xdb, 0x99, 0xe2, 0x79, 0xdd, 0x61, 0x7b, 0xd0, 0xb9, 0xc3, 0x34, 0x0a, 0xf7, 0x32, 0xb1, 0xea, 0xb3, 0x9e, 0xc1, 0x6a, 0xfb, 0x93, 0x22, 0x45, 0xf4, 0x9c,
0x87, 0xf1, 0x88, 0xcb, 0x28, 0x7f, 0xbd, 0xe8, 0xb8, 0x4e, 0x0f, 0xf4, 0x97, 0x2d, 0xd0, 0xd6, 0xf9, 0xb0, 0xa6, 0xf7, 0x38, 0xdd, 0xf1, 0xbe, 0xcd, 0xc6, 0xcc, 0xd4, 0x54, 0x9a, 0xb0, 0xbc,
0xfa, 0xc4, 0x9f, 0xa5, 0xfc, 0xa3, 0xed, 0x4f, 0x64, 0xfe, 0xe0, 0xb9, 0xd2, 0x7a, 0x95, 0xf3, 0x0e, 0xd5, 0xdb, 0xed, 0x4c, 0xf1, 0xbc, 0xee, 0xb0, 0x3d, 0xe8, 0xdc, 0xe1, 0xc3, 0x78, 0xc4,
0xb0, 0xb4, 0xbe, 0x94, 0x24, 0xb1, 0xb4, 0xbe, 0x92, 0x24, 0xb1, 0xa6, 0x5a, 0xe5, 0x5c, 0x58, 0x65, 0x94, 0xbf, 0x5e, 0x74, 0x5c, 0xa7, 0x07, 0xfa, 0xcb, 0x16, 0x68, 0x6b, 0x7d, 0xe2, 0xcf,
0x08, 0x6b, 0x95, 0xbc, 0x8a, 0xde, 0x29, 0x4f, 0xcb, 0xc6, 0xf4, 0x2f, 0x9f, 0x4e, 0x60, 0xb7, 0x52, 0xfe, 0xd1, 0xf6, 0x27, 0x32, 0x7f, 0xf0, 0x5c, 0x69, 0xbd, 0xca, 0x79, 0x58, 0x5a, 0x5f,
0xb6, 0x65, 0xb7, 0xb6, 0x0f, 0xcb, 0x77, 0xb8, 0x98, 0x2c, 0x71, 0x56, 0xd9, 0xb7, 0xcd, 0x88, 0x4a, 0x92, 0x58, 0x5a, 0x5f, 0x49, 0x92, 0x58, 0x53, 0xad, 0x72, 0x2e, 0x2c, 0x84, 0xb5, 0x4a,
0x79, 0xae, 0x59, 0x36, 0x31, 0x54, 0x67, 0x9b, 0x75, 0x3a, 0x28, 0x64, 0xdf, 0x82, 0xf6, 0x7d, 0x5e, 0x45, 0xef, 0x94, 0xa7, 0x65, 0x63, 0xfa, 0x97, 0x4f, 0x27, 0xb0, 0x5b, 0xdb, 0xb2, 0x5b,
0x9e, 0xab, 0xc3, 0x49, 0xed, 0x6f, 0x94, 0x4e, 0x2b, 0xfb, 0x35, 0x67, 0x9b, 0xb6, 0xcc, 0x10, 0xdb, 0x87, 0xe5, 0x3b, 0x5c, 0x4c, 0x96, 0x38, 0xab, 0xec, 0xdb, 0x66, 0xc4, 0x3c, 0xd7, 0x2c,
0xb7, 0x6d, 0x3e, 0x1a, 0x73, 0xa1, 0xec, 0x83, 0x60, 0xf4, 0x9c, 0xfd, 0x32, 0x31, 0xd7, 0xf7, 0x9b, 0x18, 0xaa, 0xb3, 0xcd, 0x3a, 0x1d, 0x14, 0xb2, 0x6f, 0x41, 0xfb, 0x3e, 0xcf, 0xd5, 0xe1,
0x19, 0x36, 0x8d, 0x33, 0x2d, 0x93, 0xf9, 0x4a, 0x09, 0xaf, 0xe3, 0x1c, 0xc5, 0x23, 0x6e, 0x6c, 0xa4, 0xf6, 0x37, 0x4a, 0xa7, 0x95, 0xfd, 0x9a, 0xb3, 0x4d, 0x5b, 0x66, 0x88, 0xdb, 0x36, 0x1f,
0x70, 0x11, 0xb4, 0x8d, 0xcb, 0x36, 0x5a, 0x81, 0xaa, 0x17, 0x7c, 0xb4, 0x02, 0xd5, 0xdc, 0xcd, 0x8d, 0xb9, 0x50, 0xf6, 0x41, 0x30, 0x7a, 0xce, 0x7e, 0x99, 0x98, 0xeb, 0xfb, 0x0c, 0x9b, 0xc6,
0x71, 0xaf, 0x52, 0x3b, 0x2e, 0xbb, 0x5c, 0xb4, 0x23, 0xee, 0xe3, 0x14, 0x2d, 0x6d, 0x7f, 0xe2, 0x99, 0x96, 0xc9, 0x7c, 0xa5, 0x84, 0xd7, 0x71, 0x8e, 0xe2, 0x11, 0x37, 0x36, 0xb8, 0x08, 0xda,
0x4f, 0xf2, 0xe7, 0xec, 0x09, 0xbd, 0xde, 0x31, 0x0f, 0x60, 0x0b, 0x7f, 0xa7, 0x7c, 0x56, 0xab, 0xc6, 0x65, 0x1b, 0xad, 0x40, 0xd5, 0x0b, 0x3e, 0x5a, 0x81, 0x6a, 0xee, 0xe6, 0xb8, 0x57, 0xa9,
0x27, 0xcb, 0xa8, 0xb2, 0x7d, 0x20, 0xd1, 0x14, 0xed, 0x83, 0x5f, 0x00, 0xd8, 0xcf, 0xe3, 0xe4, 0x1d, 0x97, 0x5d, 0x2e, 0xda, 0x11, 0xf7, 0x71, 0x8a, 0x96, 0xb6, 0x3f, 0xf1, 0x27, 0xf9, 0x73,
0x8e, 0xcf, 0x27, 0x71, 0x54, 0x58, 0xae, 0xe2, 0x90, 0xb1, 0xb0, 0x5c, 0xc6, 0x49, 0x23, 0x7b, 0xf6, 0x84, 0x5e, 0xef, 0x98, 0x07, 0xb0, 0x85, 0xbf, 0x53, 0x3e, 0xab, 0xd5, 0x93, 0x65, 0x54,
0x62, 0x78, 0x9c, 0xd6, 0xf9, 0xb5, 0x12, 0xae, 0x53, 0xcf, 0x21, 0xf5, 0x84, 0xd4, 0x9c, 0x45, 0xd9, 0x3e, 0x90, 0x68, 0x8a, 0xf6, 0xc1, 0x2f, 0x00, 0xec, 0xe7, 0x71, 0x72, 0xc7, 0xe7, 0x93,
0x5e, 0x77, 0xd0, 0x7f, 0x2c, 0xb2, 0x78, 0xda, 0x7f, 0xac, 0x24, 0x08, 0xb5, 0xd9, 0xab, 0x49, 0x38, 0x2a, 0x2c, 0x57, 0x71, 0xc8, 0x58, 0x58, 0x2e, 0xe3, 0xa4, 0x91, 0x3d, 0x31, 0x3c, 0x4e,
0xf9, 0xed, 0x41, 0xab, 0x48, 0x0b, 0xa9, 0x2d, 0xa9, 0x9c, 0x44, 0xd2, 0x7b, 0x4c, 0x25, 0x59, 0xeb, 0xfc, 0x5a, 0x09, 0xd7, 0xa9, 0xe7, 0x90, 0x7a, 0x42, 0x6a, 0xce, 0x22, 0xaf, 0x3b, 0xe8,
0xe3, 0xae, 0xd2, 0x54, 0x01, 0x5b, 0xc2, 0xa9, 0xa2, 0x0c, 0x4c, 0x00, 0xeb, 0xa2, 0x83, 0x7a, 0x3f, 0x16, 0x59, 0x3c, 0xed, 0x3f, 0x56, 0x12, 0x84, 0xda, 0xec, 0xd5, 0xa4, 0xfc, 0xf6, 0xa0,
0xc3, 0xa4, 0x63, 0x33, 0x35, 0x92, 0x9a, 0x84, 0x89, 0xd6, 0xe6, 0xda, 0x7c, 0x83, 0x15, 0xdb, 0x55, 0xa4, 0x85, 0xd4, 0x96, 0x54, 0x4e, 0x22, 0xe9, 0x3d, 0xa6, 0x92, 0xac, 0x71, 0x57, 0x69,
0xa1, 0xb4, 0x8a, 0x23, 0x3b, 0x34, 0xcd, 0x13, 0x58, 0xab, 0x04, 0xcb, 0x5a, 0xa5, 0x4f, 0xcb, 0xaa, 0x80, 0x2d, 0xe1, 0x54, 0x51, 0x06, 0x26, 0x80, 0x75, 0xd1, 0x41, 0xbd, 0x61, 0xd2, 0xb1,
0x51, 0x68, 0x95, 0x3e, 0x35, 0xce, 0x76, 0xcf, 0x52, 0x93, 0x2b, 0x2e, 0x60, 0x93, 0xd9, 0x49, 0x99, 0x1a, 0x49, 0x4d, 0xc2, 0x44, 0x6b, 0x73, 0x6d, 0xbe, 0xc1, 0x8a, 0xed, 0x50, 0x5a, 0xc5,
0x90, 0x0f, 0x8f, 0xde, 0x73, 0xb6, 0x0e, 0x16, 0xe8, 0xcf, 0x55, 0x3e, 0xf7, 0x3f, 0x01, 0x00, 0x91, 0x1d, 0x9a, 0xe6, 0x09, 0xac, 0x55, 0x82, 0x65, 0xad, 0xd2, 0xa7, 0xe5, 0x28, 0xb4, 0x4a,
0x00, 0xff, 0xff, 0x8f, 0xec, 0x5b, 0xef, 0x8e, 0x45, 0x00, 0x00, 0x9f, 0x1a, 0x67, 0xbb, 0x67, 0xa9, 0xc9, 0x15, 0x17, 0xb0, 0xc9, 0xec, 0x24, 0xc8, 0x87, 0x47,
0xef, 0x39, 0x5b, 0x07, 0x0b, 0xf4, 0x77, 0x2d, 0x9f, 0xfb, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff,
0x5c, 0x78, 0xf8, 0x82, 0xe0, 0x45, 0x00, 0x00,
} }

@ -592,7 +592,15 @@ func RegisterWalletUnlockerHandlerFromEndpoint(ctx context.Context, mux *runtime
// RegisterWalletUnlockerHandler registers the http handlers for service WalletUnlocker to "mux". // RegisterWalletUnlockerHandler registers the http handlers for service WalletUnlocker to "mux".
// The handlers forward requests to the grpc endpoint over "conn". // The handlers forward requests to the grpc endpoint over "conn".
func RegisterWalletUnlockerHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { func RegisterWalletUnlockerHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := NewWalletUnlockerClient(conn) return RegisterWalletUnlockerHandlerClient(ctx, mux, NewWalletUnlockerClient(conn))
}
// RegisterWalletUnlockerHandler registers the http handlers for service WalletUnlocker to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "WalletUnlockerClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "WalletUnlockerClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "WalletUnlockerClient" to call the correct interceptors.
func RegisterWalletUnlockerHandlerClient(ctx context.Context, mux *runtime.ServeMux, client WalletUnlockerClient) error {
mux.Handle("GET", pattern_WalletUnlocker_GenSeed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle("GET", pattern_WalletUnlocker_GenSeed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
@ -728,7 +736,15 @@ func RegisterLightningHandlerFromEndpoint(ctx context.Context, mux *runtime.Serv
// RegisterLightningHandler registers the http handlers for service Lightning to "mux". // RegisterLightningHandler registers the http handlers for service Lightning to "mux".
// The handlers forward requests to the grpc endpoint over "conn". // The handlers forward requests to the grpc endpoint over "conn".
func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := NewLightningClient(conn) return RegisterLightningHandlerClient(ctx, mux, NewLightningClient(conn))
}
// RegisterLightningHandler registers the http handlers for service Lightning to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "LightningClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "LightningClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "LightningClient" to call the correct interceptors.
func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux, client LightningClient) error {
mux.Handle("GET", pattern_Lightning_WalletBalance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle("GET", pattern_Lightning_WalletBalance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)

@ -127,6 +127,15 @@ message InitWalletRequest {
to encrypt the generated aezeed cipher seed. to encrypt the generated aezeed cipher seed.
*/ */
bytes aezeed_passphrase = 3; bytes aezeed_passphrase = 3;
/**
recovery_window is an optional argument specifying the address lookahead
when restoring a wallet seed. The recovery window applies to each
invdividual branch of the BIP44 derivation paths. Supplying a recovery
window of zero indicates that no addresses should be recovered, such after
the first initialization of the wallet.
*/
int32 recovery_window = 4;
} }
message InitWalletResponse { message InitWalletResponse {
} }
@ -138,6 +147,15 @@ message UnlockWalletRequest {
function properly. function properly.
*/ */
bytes wallet_password = 1; bytes wallet_password = 1;
/**
recovery_window is an optional argument specifying the address lookahead
when restoring a wallet seed. The recovery window applies to each
invdividual branch of the BIP44 derivation paths. Supplying a recovery
window of zero indicates that no addresses should be recovered, such after
the first initialization of the wallet.
*/
int32 recovery_window = 2;
} }
message UnlockWalletResponse {} message UnlockWalletResponse {}

@ -1569,6 +1569,11 @@
"type": "string", "type": "string",
"format": "byte", "format": "byte",
"description": "*\naezeed_passphrase is an optional user provided passphrase that will be used\nto encrypt the generated aezeed cipher seed." "description": "*\naezeed_passphrase is an optional user provided passphrase that will be used\nto encrypt the generated aezeed cipher seed."
},
"recovery_window": {
"type": "integer",
"format": "int32",
"description": "*\nrecovery_window is an optional argument specifying the address lookahead\nwhen restoring a wallet seed. The recovery window applies to each\ninvdividual branch of the BIP44 derivation paths. Supplying a recovery\nwindow of zero indicates that no addresses should be recovered, such after\nthe first initialization of the wallet."
} }
} }
}, },
@ -2390,6 +2395,11 @@
"type": "string", "type": "string",
"format": "byte", "format": "byte",
"description": "*\nwallet_password should be the current valid passphrase for the daemon. This\nwill be required to decrypt on-disk material that the daemon requires to\nfunction properly." "description": "*\nwallet_password should be the current valid passphrase for the daemon. This\nwill be required to decrypt on-disk material that the daemon requires to\nfunction properly."
},
"recovery_window": {
"type": "integer",
"format": "int32",
"description": "*\nrecovery_window is an optional argument specifying the address lookahead\nwhen restoring a wallet seed. The recovery window applies to each\ninvdividual branch of the BIP44 derivation paths. Supplying a recovery\nwindow of zero indicates that no addresses should be recovered, such after\nthe first initialization of the wallet."
} }
} }
}, },

@ -238,7 +238,97 @@ func (n *NetworkHarness) TearDownAll() error {
// current instance of the network harness. The created node is running, but // current instance of the network harness. The created node is running, but
// not yet connected to other nodes within the network. // not yet connected to other nodes within the network.
func (n *NetworkHarness) NewNode(extraArgs []string) (*HarnessNode, error) { func (n *NetworkHarness) NewNode(extraArgs []string) (*HarnessNode, error) {
return n.newNode(extraArgs, false)
}
// NewNodeWithSeed fully initializes a new HarnessNode after creating a fresh
// aezeed. The provided password is used as both the aezeed password and the
// wallet password. The generated mnemonic is returned along with the
// initialized harness node.
func (n *NetworkHarness) NewNodeWithSeed(extraArgs []string,
password []byte) (*HarnessNode, []string, error) {
node, err := n.newNode(extraArgs, true)
if err != nil {
return nil, nil, err
}
timeout := time.Duration(time.Second * 15)
ctxb := context.Background()
// Create a request to generate a new aezeed. The new seed will have the
// same password as the internal wallet.
genSeedReq := &lnrpc.GenSeedRequest{
AezeedPassphrase: password,
}
ctxt, _ := context.WithTimeout(ctxb, timeout)
genSeedResp, err := node.GenSeed(ctxt, genSeedReq)
if err != nil {
return nil, nil, err
}
// With the seed created, construct the init request to the node,
// including the newly generated seed.
initReq := &lnrpc.InitWalletRequest{
WalletPassword: password,
CipherSeedMnemonic: genSeedResp.CipherSeedMnemonic,
AezeedPassphrase: password,
}
// Pass the init request via rpc to finish unlocking the node. This will
// also initialize the macaroon-authenticated LightningClient.
err = node.Init(ctxb, initReq)
if err != nil {
return nil, nil, err
}
// With the node started, we can now record its public key within the
// global mapping.
n.RegisterNode(node)
return node, genSeedResp.CipherSeedMnemonic, nil
}
// RestoreNodeWithSeed fully initializes a HarnessNode using a chosen mnemonic,
// password, and recovery window. After providing the initialization request to
// unlock the node, this method will finish initializing the LightningClient
// such that the HarnessNode can be used for regular rpc operations.
func (n *NetworkHarness) RestoreNodeWithSeed(extraArgs []string,
password []byte, mnemonic []string,
recoveryWindow int32) (*HarnessNode, error) {
node, err := n.newNode(extraArgs, true)
if err != nil {
return nil, err
}
initReq := &lnrpc.InitWalletRequest{
WalletPassword: password,
CipherSeedMnemonic: mnemonic,
AezeedPassphrase: password,
RecoveryWindow: recoveryWindow,
}
err = node.Init(context.Background(), initReq)
if err != nil {
return nil, err
}
// With the node started, we can now record its public key within the
// global mapping.
n.RegisterNode(node)
return node, nil
}
// newNode initializes a new HarnessNode, supporting the ability to initialize a
// wallet with or without a seed. If hasSeed is false, the returned harness node
// can be used immediately. Otherwise, the node will require an additional
// initialization phase where the wallet is either created or restored.
func (n *NetworkHarness) newNode(extraArgs []string, hasSeed bool) (*HarnessNode, error) {
node, err := newNode(nodeConfig{ node, err := newNode(nodeConfig{
HasSeed: hasSeed,
RPCConfig: &n.rpcConfig, RPCConfig: &n.rpcConfig,
NetParams: n.netParams, NetParams: n.netParams,
ExtraArgs: extraArgs, ExtraArgs: extraArgs,
@ -257,13 +347,27 @@ func (n *NetworkHarness) NewNode(extraArgs []string) (*HarnessNode, error) {
return nil, err return nil, err
} }
// If this node is to have a seed, it will need to be unlocked or
// initialized via rpc. Delay registering it with the network until it
// can be driven via an unlocked rpc connection.
if node.cfg.HasSeed {
return node, nil
}
// With the node started, we can now record its public key within the // With the node started, we can now record its public key within the
// global mapping. // global mapping.
n.RegisterNode(node)
return node, nil
}
// RegisterNode records a new HarnessNode in the NetworkHarnesses map of known
// nodes. This method should only be called with nodes that have successfully
// retrieved their public keys via FetchNodeInfo.
func (n *NetworkHarness) RegisterNode(node *HarnessNode) {
n.mtx.Lock() n.mtx.Lock()
n.nodesByPub[node.PubKeyStr] = node n.nodesByPub[node.PubKeyStr] = node
n.mtx.Unlock() n.mtx.Unlock()
return node, nil
} }
// EnsureConnected will try to connect to two nodes, returning no error if they // EnsureConnected will try to connect to two nodes, returning no error if they
@ -986,10 +1090,31 @@ func (n *NetworkHarness) DumpLogs(node *HarnessNode) (string, error) {
} }
// SendCoins attempts to send amt satoshis from the internal mining node to the // SendCoins attempts to send amt satoshis from the internal mining node to the
// targeted lightning node. // targeted lightning node using a P2WKH address.
func (n *NetworkHarness) SendCoins(ctx context.Context, amt btcutil.Amount, func (n *NetworkHarness) SendCoins(ctx context.Context, amt btcutil.Amount,
target *HarnessNode) error { target *HarnessNode) error {
return n.sendCoins(
ctx, amt, target, lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH,
)
}
// SendCoinsNP2WKH attempts to send amt satoshis from the internal mining node
// to the targeted lightning node using a NP2WKH address.
func (n *NetworkHarness) SendCoinsNP2WKH(ctx context.Context,
amt btcutil.Amount, target *HarnessNode) error {
return n.sendCoins(
ctx, amt, target, lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH,
)
}
// sendCoins attempts to send amt satoshis from the internal mining node to the
// targeted lightning node.
func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount,
target *HarnessNode,
addrType lnrpc.NewAddressRequest_AddressType) error {
balReq := &lnrpc.WalletBalanceRequest{} balReq := &lnrpc.WalletBalanceRequest{}
initialBalance, err := target.WalletBalance(ctx, balReq) initialBalance, err := target.WalletBalance(ctx, balReq)
if err != nil { if err != nil {
@ -1000,7 +1125,7 @@ func (n *NetworkHarness) SendCoins(ctx context.Context, amt btcutil.Amount,
// to receive a p2wkh address s.t the output can immediately be used as // to receive a p2wkh address s.t the output can immediately be used as
// an input to a funding transaction. // an input to a funding transaction.
addrReq := &lnrpc.NewAddressRequest{ addrReq := &lnrpc.NewAddressRequest{
Type: lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH, Type: addrType,
} }
resp, err := target.NewAddress(ctx, addrReq) resp, err := target.NewAddress(ctx, addrReq)
if err != nil { if err != nil {

@ -98,6 +98,8 @@ type nodeConfig struct {
ReadMacPath string ReadMacPath string
InvoiceMacPath string InvoiceMacPath string
HasSeed bool
P2PPort int P2PPort int
RPCPort int RPCPort int
RESTPort int RESTPort int
@ -136,7 +138,6 @@ func (cfg nodeConfig) genArgs() []string {
encodedCert := hex.EncodeToString(cfg.RPCConfig.Certificates) encodedCert := hex.EncodeToString(cfg.RPCConfig.Certificates)
args = append(args, "--bitcoin.active") args = append(args, "--bitcoin.active")
args = append(args, "--nobootstrap") args = append(args, "--nobootstrap")
args = append(args, "--noencryptwallet")
args = append(args, "--debuglevel=debug") args = append(args, "--debuglevel=debug")
args = append(args, "--bitcoin.defaultchanconfs=1") args = append(args, "--bitcoin.defaultchanconfs=1")
args = append(args, "--bitcoin.defaultremotedelay=4") args = append(args, "--bitcoin.defaultremotedelay=4")
@ -159,6 +160,10 @@ func (cfg nodeConfig) genArgs() []string {
args = append(args, fmt.Sprintf("--externalip=%s", cfg.P2PAddr())) args = append(args, fmt.Sprintf("--externalip=%s", cfg.P2PAddr()))
args = append(args, fmt.Sprintf("--trickledelay=%v", trickleDelay)) args = append(args, fmt.Sprintf("--trickledelay=%v", trickleDelay))
if !cfg.HasSeed {
args = append(args, "--noencryptwallet")
}
if cfg.ExtraArgs != nil { if cfg.ExtraArgs != nil {
args = append(args, cfg.ExtraArgs...) args = append(args, cfg.ExtraArgs...)
} }
@ -195,10 +200,13 @@ type HarnessNode struct {
wg sync.WaitGroup wg sync.WaitGroup
lnrpc.LightningClient lnrpc.LightningClient
lnrpc.WalletUnlockerClient
} }
// Assert *HarnessNode implements the lnrpc.LightningClient interface. // Assert *HarnessNode implements the lnrpc.LightningClient interface.
var _ lnrpc.LightningClient = (*HarnessNode)(nil) var _ lnrpc.LightningClient = (*HarnessNode)(nil)
var _ lnrpc.WalletUnlockerClient = (*HarnessNode)(nil)
// newNode creates a new test lightning node instance from the passed config. // newNode creates a new test lightning node instance from the passed config.
func newNode(cfg nodeConfig) (*HarnessNode, error) { func newNode(cfg nodeConfig) (*HarnessNode, error) {
@ -301,13 +309,78 @@ func (hn *HarnessNode) start(lndError chan<- error) error {
// Since Stop uses the LightningClient to stop the node, if we fail to get a // Since Stop uses the LightningClient to stop the node, if we fail to get a
// connected client, we have to kill the process. // connected client, we have to kill the process.
conn, err := hn.connectRPC() useMacaroons := !hn.cfg.HasSeed
conn, err := hn.ConnectRPC(useMacaroons)
if err != nil { if err != nil {
hn.cmd.Process.Kill() hn.cmd.Process.Kill()
return err return err
} }
// If the node was created with a seed, we will need to perform an
// additional step to unlock the wallet. The connection returned will
// only use the TLS certs, and can only perform operations necessary to
// unlock the daemon.
if hn.cfg.HasSeed {
hn.WalletUnlockerClient = lnrpc.NewWalletUnlockerClient(conn)
return nil
}
return hn.initLightningClient(conn)
}
// Init initializes a harness node by passing the init request via rpc. After
// the request is submitted, this method will block until an
// macaroon-authenticated rpc connection can be established to the harness node.
// Once established, the new connection is used to initialize the
// LightningClient and subscribes the HarnessNode to topology changes.
func (hn *HarnessNode) Init(ctx context.Context,
initReq *lnrpc.InitWalletRequest) error {
timeout := time.Duration(time.Second * 15)
ctxt, _ := context.WithTimeout(ctx, timeout)
_, err := hn.InitWallet(ctxt, initReq)
if err != nil {
return err
}
// Wait for the wallet to finish unlocking, such that we can connect to
// it via a macaroon-authenticated rpc connection.
var conn *grpc.ClientConn
if err = WaitPredicate(func() bool {
conn, err = hn.ConnectRPC(true)
return err == nil
}, 5*time.Second); err != nil {
return err
}
return hn.initLightningClient(conn)
}
// initLightningClient constructs the grpc LightningClient from the given client
// connection and subscribes the harness node to graph topology updates.
func (hn *HarnessNode) initLightningClient(conn *grpc.ClientConn) error {
// Construct the LightningClient that will allow us to use the
// HarnessNode directly for normal rpc operations.
hn.LightningClient = lnrpc.NewLightningClient(conn) hn.LightningClient = lnrpc.NewLightningClient(conn)
// Set the harness node's pubkey to what the node claims in GetInfo.
err := hn.FetchNodeInfo()
if err != nil {
return err
}
// Launch the watcher that will hook into graph related topology change
// from the PoV of this node.
hn.wg.Add(1)
go hn.lightningNetworkWatcher()
return nil
}
// FetchNodeInfo queries an unlocked node to retrieve its public key. This
// method also spawns a lightning network watcher for this node, which watches
// for topology changes.
func (hn *HarnessNode) FetchNodeInfo() error {
// Obtain the lnid of this node for quick identification purposes. // Obtain the lnid of this node for quick identification purposes.
ctxb := context.Background() ctxb := context.Background()
info, err := hn.GetInfo(ctxb, &lnrpc.GetInfoRequest{}) info, err := hn.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
@ -323,11 +396,6 @@ func (hn *HarnessNode) start(lndError chan<- error) error {
} }
copy(hn.PubKey[:], pubkey) copy(hn.PubKey[:], pubkey)
// Launch the watcher that will hook into graph related topology change
// from the PoV of this node.
hn.wg.Add(1)
go hn.lightningNetworkWatcher()
return nil return nil
} }
@ -365,24 +433,45 @@ func (hn *HarnessNode) writePidFile() error {
// connectRPC uses the TLS certificate and admin macaroon files written by the // connectRPC uses the TLS certificate and admin macaroon files written by the
// lnd node to create a gRPC client connection. // lnd node to create a gRPC client connection.
func (hn *HarnessNode) connectRPC() (*grpc.ClientConn, error) { func (hn *HarnessNode) ConnectRPC(useMacs bool) (*grpc.ClientConn, error) {
// Wait until TLS certificate and admin macaroon are created before // Wait until TLS certificate and admin macaroon are created before
// using them, up to 20 sec. // using them, up to 20 sec.
tlsTimeout := time.After(30 * time.Second) tlsTimeout := time.After(30 * time.Second)
for !fileExists(hn.cfg.TLSCertPath) || !fileExists(hn.cfg.AdminMacPath) { for !fileExists(hn.cfg.TLSCertPath) {
select { select {
case <-tlsTimeout: case <-tlsTimeout:
return nil, fmt.Errorf("timeout waiting for TLS cert file " + return nil, fmt.Errorf("timeout waiting for TLS cert " +
"and admin macaroon file to be created after " + "file to be created after 30 seconds")
"20 seconds")
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
} }
} }
opts := []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTimeout(time.Second * 20),
}
tlsCreds, err := credentials.NewClientTLSFromFile(hn.cfg.TLSCertPath, "") tlsCreds, err := credentials.NewClientTLSFromFile(hn.cfg.TLSCertPath, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
opts = append(opts, grpc.WithTransportCredentials(tlsCreds))
if !useMacs {
return grpc.Dial(hn.cfg.RPCAddr(), opts...)
}
macTimeout := time.After(30 * time.Second)
for !fileExists(hn.cfg.AdminMacPath) {
select {
case <-macTimeout:
return nil, fmt.Errorf("timeout waiting for admin " +
"macaroon file to be created after 30 seconds")
case <-time.After(100 * time.Millisecond):
}
}
macBytes, err := ioutil.ReadFile(hn.cfg.AdminMacPath) macBytes, err := ioutil.ReadFile(hn.cfg.AdminMacPath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -391,12 +480,10 @@ func (hn *HarnessNode) connectRPC() (*grpc.ClientConn, error) {
if err = mac.UnmarshalBinary(macBytes); err != nil { if err = mac.UnmarshalBinary(macBytes); err != nil {
return nil, err return nil, err
} }
opts := []grpc.DialOption{
grpc.WithTransportCredentials(tlsCreds), macCred := macaroons.NewMacaroonCredential(mac)
grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)), opts = append(opts, grpc.WithPerRPCCredentials(macCred))
grpc.WithBlock(),
grpc.WithTimeout(time.Second * 20),
}
return grpc.Dial(hn.cfg.RPCAddr(), opts...) return grpc.Dial(hn.cfg.RPCAddr(), opts...)
} }
@ -441,6 +528,7 @@ func (hn *HarnessNode) stop() error {
hn.quit = nil hn.quit = nil
hn.processExit = nil hn.processExit = nil
hn.LightningClient = nil hn.LightningClient = nil
hn.WalletUnlockerClient = nil
return nil return nil
} }

@ -88,7 +88,7 @@ func New(cfg Config) (*BtcWallet, error) {
pubPass = cfg.PublicPass pubPass = cfg.PublicPass
} }
loader := base.NewLoader(cfg.NetParams, netDir) loader := base.NewLoader(cfg.NetParams, netDir, cfg.RecoveryWindow)
walletExists, err := loader.WalletExists() walletExists, err := loader.WalletExists()
if err != nil { if err != nil {
return nil, err return nil, err
@ -98,7 +98,7 @@ func New(cfg Config) (*BtcWallet, error) {
if !walletExists { if !walletExists {
// Wallet has never been created, perform initial set up. // Wallet has never been created, perform initial set up.
wallet, err = loader.CreateNewWallet( wallet, err = loader.CreateNewWallet(
pubPass, cfg.PrivatePass, cfg.HdSeed, pubPass, cfg.PrivatePass, cfg.HdSeed, cfg.Birthday,
) )
if err != nil { if err != nil {
return nil, err return nil, err

@ -2,6 +2,7 @@ package btcwallet
import ( import (
"path/filepath" "path/filepath"
"time"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg"
@ -63,6 +64,15 @@ type Config struct {
// unspecified, a new seed will be generated. // unspecified, a new seed will be generated.
HdSeed []byte HdSeed []byte
// Birthday specifies the time at which this wallet was initially
// created. It is used to bound rescans for used addresses.
Birthday time.Time
// RecoveryWindow specifies the address look-ahead for which to scan
// when restoring a wallet. The recovery window will apply to all
// default BIP44 derivation paths.
RecoveryWindow uint32
// ChainSource is the primary chain interface. This is used to operate // ChainSource is the primary chain interface. This is used to operate
// the wallet and do things such as rescanning, sending transactions, // the wallet and do things such as rescanning, sending transactions,
// notifications for received funds, etc. // notifications for received funds, etc.

@ -2083,7 +2083,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
} }
aliceChain.Start() aliceChain.Start()
defer aliceChain.Stop() defer aliceChain.Stop()
aliceClient = chain.NewNeutrinoClient(aliceChain) aliceClient = chain.NewNeutrinoClient(
netParams, aliceChain,
)
// Start Bob - open a database, start a neutrino // Start Bob - open a database, start a neutrino
// instance, and initialize a btcwallet driver for it. // instance, and initialize a btcwallet driver for it.
@ -2108,7 +2110,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
} }
bobChain.Start() bobChain.Start()
defer bobChain.Stop() defer bobChain.Stop()
bobClient = chain.NewNeutrinoClient(bobChain) bobClient = chain.NewNeutrinoClient(
netParams, bobChain,
)
case "bitcoind": case "bitcoind":
feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( feeEstimator, err = lnwallet.NewBitcoindFeeEstimator(

@ -14,7 +14,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// WalletInitMsg is a message sent to the UnlockerService when a user wishes to // WalletInitMsg is a message sent by the UnlockerService when a user wishes to
// set up the internal wallet for the first time. The user MUST provide a // set up the internal wallet for the first time. The user MUST provide a
// passphrase, but is also able to provide their own source of entropy. If // passphrase, but is also able to provide their own source of entropy. If
// provided, then this source of entropy will be used to generate the wallet's // provided, then this source of entropy will be used to generate the wallet's
@ -27,6 +27,28 @@ type WalletInitMsg struct {
// WalletSeed is the deciphered cipher seed that the wallet should use // WalletSeed is the deciphered cipher seed that the wallet should use
// to initialize itself. // to initialize itself.
WalletSeed *aezeed.CipherSeed WalletSeed *aezeed.CipherSeed
// RecoveryWindow is the address look-ahead used when restoring a seed
// with existing funds. A recovery window zero indicates that no
// recovery should be attempted, such as after the wallet's initial
// creation.
RecoveryWindow uint32
}
// WalletUnlockMsg is a message sent by the UnlockerService when a user wishes
// to unlock the internal wallet after initial setup. The user can optionally
// specify a recovery window, which will resume an interrupted rescan for used
// addresses.
type WalletUnlockMsg struct {
// Passphrase is the passphrase that will be used to encrypt the wallet
// itself. This MUST be at least 8 characters.
Passphrase []byte
// RecoveryWindow is the address look-ahead used when restoring a seed
// with existing funds. A recovery window zero indicates that no
// recovery should be attempted, such as after the wallet's initial
// creation, but before any addresses have been created.
RecoveryWindow uint32
} }
// UnlockerService implements the WalletUnlocker service used to provide lnd // UnlockerService implements the WalletUnlocker service used to provide lnd
@ -37,10 +59,10 @@ type UnlockerService struct {
// InitMsgs is a channel that carries all wallet init messages. // InitMsgs is a channel that carries all wallet init messages.
InitMsgs chan *WalletInitMsg InitMsgs chan *WalletInitMsg
// UnlockPasswords is a channel where passwords provided by the rpc // UnlockMsgs is a channel where unlock parameters provided by the rpc
// client to be used to unlock and decrypt an existing wallet will be // client to be used to unlock and decrypt an existing wallet will be
// sent. // sent.
UnlockPasswords chan []byte UnlockMsgs chan *WalletUnlockMsg
chainDir string chainDir string
netParams *chaincfg.Params netParams *chaincfg.Params
@ -52,10 +74,10 @@ func New(authSvc *macaroons.Service, chainDir string,
params *chaincfg.Params) *UnlockerService { params *chaincfg.Params) *UnlockerService {
return &UnlockerService{ return &UnlockerService{
InitMsgs: make(chan *WalletInitMsg, 1), InitMsgs: make(chan *WalletInitMsg, 1),
UnlockPasswords: make(chan []byte, 1), UnlockMsgs: make(chan *WalletUnlockMsg, 1),
chainDir: chainDir, chainDir: chainDir,
netParams: params, netParams: params,
} }
} }
@ -73,7 +95,7 @@ func (u *UnlockerService) GenSeed(ctx context.Context,
// Before we start, we'll ensure that the wallet hasn't already created // Before we start, we'll ensure that the wallet hasn't already created
// so we don't show a *new* seed to the user if one already exists. // so we don't show a *new* seed to the user if one already exists.
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams) netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir) loader := wallet.NewLoader(u.netParams, netDir, 0)
walletExists, err := loader.WalletExists() walletExists, err := loader.WalletExists()
if err != nil { if err != nil {
return nil, err return nil, err
@ -156,10 +178,17 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
"at least 8 characters") "at least 8 characters")
} }
// Require that the recovery window be non-negative.
recoveryWindow := in.RecoveryWindow
if recoveryWindow < 0 {
return nil, fmt.Errorf("recovery window %d must be "+
"non-negative", recoveryWindow)
}
// We'll then open up the directory that will be used to store the // We'll then open up the directory that will be used to store the
// wallet's files so we can check if the wallet already exists. // wallet's files so we can check if the wallet already exists.
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams) netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir) loader := wallet.NewLoader(u.netParams, netDir, uint32(recoveryWindow))
walletExists, err := loader.WalletExists() walletExists, err := loader.WalletExists()
if err != nil { if err != nil {
@ -198,8 +227,9 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
// now send over the wallet password and the seed. This will allow the // now send over the wallet password and the seed. This will allow the
// daemon to initialize itself and startup. // daemon to initialize itself and startup.
initMsg := &WalletInitMsg{ initMsg := &WalletInitMsg{
Passphrase: password, Passphrase: password,
WalletSeed: cipherSeed, WalletSeed: cipherSeed,
RecoveryWindow: uint32(recoveryWindow),
} }
u.InitMsgs <- initMsg u.InitMsgs <- initMsg
@ -208,13 +238,16 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
} }
// UnlockWallet sends the password provided by the incoming UnlockWalletRequest // UnlockWallet sends the password provided by the incoming UnlockWalletRequest
// over the UnlockPasswords channel in case it successfully decrypts an // over the UnlockMsgs channel in case it successfully decrypts an existing
// existing wallet found in the chain's wallet database directory. // wallet found in the chain's wallet database directory.
func (u *UnlockerService) UnlockWallet(ctx context.Context, func (u *UnlockerService) UnlockWallet(ctx context.Context,
in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) { in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) {
password := in.WalletPassword
recoveryWindow := uint32(in.RecoveryWindow)
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams) netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir) loader := wallet.NewLoader(u.netParams, netDir, recoveryWindow)
// Check if wallet already exists. // Check if wallet already exists.
walletExists, err := loader.WalletExists() walletExists, err := loader.WalletExists()
@ -228,7 +261,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
} }
// Try opening the existing wallet with the provided password. // Try opening the existing wallet with the provided password.
_, err = loader.OpenExistingWallet(in.WalletPassword, false) _, err = loader.OpenExistingWallet(password, false)
if err != nil { if err != nil {
// Could not open wallet, most likely this means that provided // Could not open wallet, most likely this means that provided
// password was incorrect. // password was incorrect.
@ -244,17 +277,22 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
// Attempt to create a password for the macaroon service. // Attempt to create a password for the macaroon service.
if u.authSvc != nil { if u.authSvc != nil {
err = u.authSvc.CreateUnlock(&in.WalletPassword) err = u.authSvc.CreateUnlock(&password)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create/unlock "+ return nil, fmt.Errorf("unable to create/unlock "+
"macaroon store: %v", err) "macaroon store: %v", err)
} }
} }
walletUnlockMsg := &WalletUnlockMsg{
Passphrase: password,
RecoveryWindow: recoveryWindow,
}
// At this point we was able to open the existing wallet with the // At this point we was able to open the existing wallet with the
// provided password. We send the password over the UnlockPasswords // provided password. We send the password over the UnlockMsgs
// channel, such that it can be used by lnd to open the wallet. // channel, such that it can be used by lnd to open the wallet.
u.UnlockPasswords <- in.WalletPassword u.UnlockMsgs <- walletUnlockMsg
return &lnrpc.UnlockWalletResponse{}, nil return &lnrpc.UnlockWalletResponse{}, nil
} }

@ -33,12 +33,16 @@ var (
} }
testNetParams = &chaincfg.MainNetParams testNetParams = &chaincfg.MainNetParams
testRecoveryWindow uint32 = 150
) )
func createTestWallet(t *testing.T, dir string, netParams *chaincfg.Params) { func createTestWallet(t *testing.T, dir string, netParams *chaincfg.Params) {
netDir := btcwallet.NetworkDir(dir, netParams) netDir := btcwallet.NetworkDir(dir, netParams)
loader := wallet.NewLoader(netParams, netDir) loader := wallet.NewLoader(netParams, netDir, 0)
_, err := loader.CreateNewWallet(testPassword, testPassword, testSeed) _, err := loader.CreateNewWallet(
testPassword, testPassword, testSeed, time.Time{},
)
if err != nil { if err != nil {
t.Fatalf("failed creating wallet: %v", err) t.Fatalf("failed creating wallet: %v", err)
} }
@ -207,6 +211,7 @@ func TestInitWallet(t *testing.T) {
WalletPassword: testPassword, WalletPassword: testPassword,
CipherSeedMnemonic: []string(mnemonic[:]), CipherSeedMnemonic: []string(mnemonic[:]),
AezeedPassphrase: pass, AezeedPassphrase: pass,
RecoveryWindow: int32(testRecoveryWindow),
} }
_, err = service.InitWallet(ctx, req) _, err = service.InitWallet(ctx, req)
if err != nil { if err != nil {
@ -236,6 +241,11 @@ func TestInitWallet(t *testing.T) {
"got %x", cipherSeed.Entropy[:], "got %x", cipherSeed.Entropy[:],
msg.WalletSeed.Entropy[:]) msg.WalletSeed.Entropy[:])
} }
if msg.RecoveryWindow != testRecoveryWindow {
t.Fatalf("mismatched recovery window: expected %v,"+
"got %v", testRecoveryWindow,
msg.RecoveryWindow)
}
case <-time.After(3 * time.Second): case <-time.After(3 * time.Second):
t.Fatalf("password not received") t.Fatalf("password not received")
@ -312,6 +322,7 @@ func TestUnlockWallet(t *testing.T) {
ctx := context.Background() ctx := context.Background()
req := &lnrpc.UnlockWalletRequest{ req := &lnrpc.UnlockWalletRequest{
WalletPassword: testPassword, WalletPassword: testPassword,
RecoveryWindow: int32(testRecoveryWindow),
} }
// Should fail to unlock non-existing wallet. // Should fail to unlock non-existing wallet.
@ -338,12 +349,17 @@ func TestUnlockWallet(t *testing.T) {
t.Fatalf("unable to unlock wallet: %v", err) t.Fatalf("unable to unlock wallet: %v", err)
} }
// Password should be sent over the channel. // Password and recovery window should be sent over the channel.
select { select {
case pw := <-service.UnlockPasswords: case unlockMsg := <-service.UnlockMsgs:
if !bytes.Equal(pw, testPassword) { if !bytes.Equal(unlockMsg.Passphrase, testPassword) {
t.Fatalf("expected to receive password %x, got %x", t.Fatalf("expected to receive password %x, got %x",
testPassword, pw) testPassword, unlockMsg.Passphrase)
}
if unlockMsg.RecoveryWindow != testRecoveryWindow {
t.Fatalf("expected to receive recovery window %d, "+
"got %d", testRecoveryWindow,
unlockMsg.RecoveryWindow)
} }
case <-time.After(3 * time.Second): case <-time.After(3 * time.Second):
t.Fatalf("password not received") t.Fatalf("password not received")