From aebe7f35dc1df18787cbfe67d41a330bf3728a09 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 14:55:05 -0800 Subject: [PATCH 01/29] keychain: introduce new package for deterministic key derivation + seed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we add a new package to lnd: the keychain package. The role of this package is to house all the interfaces that lnd will use to generate the various keys it needs to create contracts and operate within the network. Additionally, we also use this package to define a deterministic key derivation scheme, that can be implemented by any software/hardware that partially understands BIP43-like derivation. The first version (v0) of the keychain schema is very simple. We re-use BIP43, with a slight twist. Re-using BIP43 lets us leverage all the existing libraries out there as far as compatibility. The purpose used is 1017, and the coin type, the particular coin type of the chain where the keys are meant to be used. Within our schema, we define multiple “key families”. Each key family is actually just manifested as a new “account” within the BIP44/43 family. With this schema, if we have static description of the keys used within a channel, given the seed, and the “KeyLocator”, we can re-derive all keys necessary be able to re-sign for the channel. --- keychain/derivation.go | 159 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 keychain/derivation.go diff --git a/keychain/derivation.go b/keychain/derivation.go new file mode 100644 index 00000000..b2ffd167 --- /dev/null +++ b/keychain/derivation.go @@ -0,0 +1,159 @@ +package keychain + +import "github.com/roasbeef/btcd/btcec" + +const ( + // KeyDerivationVersion is the version of the key derivation schema + // defined below. We use a version as this means that we'll be able to + // accept new seed in the future and be able to discern if the software + // is compatible with the version of the weed. + KeyDerivationVersion = 0 + + // BIP0043Purpose is the "purpose" value that we'll use for the first + // version or our key derivation scheme. All keys are expected to be + // derived from this purpose, then the particular coin type of the + // chain where the keys are to be used. Slightly adhering to BIP0043 + // allows us to not deviate too far from a widely used standard, and + // also fits into existing implementations of the BIP's template. + // + // NOTE: BRICK SQUUUUUAD. + BIP0043Purpose = 1017 +) + +// KeyFamily represents a "family" of keys that will be used within various +// contracts created by lnd. These families are meant to be distinct branches +// within the HD key chain of the backing wallet. Usage of key families within +// the interface below are strict in order to promote integrability and the +// ability to restore all keys given a user master seed backup. +// +// The key derivation in this file follows the following hierarchy based on +// BIP43: +// +// * m/1017'/coinType'/keyFamily/0/index +type KeyFamily uint32 + +const ( + // KeyFamilyMultiSig are keys to be used within multi-sig scripts. + KeyFamilyMultiSig KeyFamily = 0 + + // KeyFamilyRevocationBase are keys that are used within channels to + // create revocation basepoints that the remote party will use to + // create revocation keys for us. + KeyFamilyRevocationBase = 1 + + // KeyFamilyHtlcBase are keys used within channels that will be + // combined with per-state randomness to produce public keys that will + // be used in HTLC scripts. + KeyFamilyHtlcBase KeyFamily = 2 + + // KeyFamilyPaymentBase are keys used within channels that will be + // combined with per-state randomness to produce public keys that will + // be used in scripts that pay directly to us without any delay. + KeyFamilyPaymentBase KeyFamily = 3 + + // KeyFamilyDelayBase are keys used within channels that will be + // combined with per-state randomness to produce public keys that will + // be used in scripts that pay to us, but require a CSV delay before we + // can sweep the funds. + KeyFamilyDelayBase KeyFamily = 4 + + // KeyFamilyRevocationRoot is a family of keys which will be used to + // derive the root of a revocation tree for a particular channel. + KeyFamilyRevocationRoot KeyFamily = 5 + + // KeyFamilyNodeKey is a family of keys that will be used to derive + // keys that will be advertised on the network to represent our current + // "identity" within the network. Peers will need our latest node key + // in order to establish a transport session with us on the Lightning + // p2p level (BOLT-0008). + KeyFamilyNodeKey KeyFamily = 6 +) + +// KeyLocator is a two-tuple that can be used to derive *any* key that has ever +// been used under the key derivation mechanisms described in this file. +// Version 0 of our key derivation schema uses the following BIP43-like +// derivation: +// +// * m/201'/coinType'/keyFamily/0/index +// +// Our purpose is 201 (chosen arbitrary for now), and the coin type will vary +// based on which coin/chain the channels are being created on. The key family +// are actually just individual "accounts" in the nomenclature of BIP43. By +// default we assume a branch of 0 (external). Finally, the key index (which +// will vary per channel and use case) is the final element which allows us to +// deterministically derive keys. +type KeyLocator struct { + // TODO(roasbeef); add the key scope as well?? + + // Family is the family of key being identified. + Family KeyFamily + + // Index is the precise index of the key being identified. + Index uint32 +} + +// IsEmpty returns true if a KeyLocator is "empty". This may be the case where we +// learn of a key from a remote party for a contract, but don't know the +// precise details of its derivation (as we don't know the private key!). +func (k KeyLocator) IsEmpty() bool { + return k.Family == 0 && k.Index == 0 +} + +// KeyDescriptor wraps a KeyLocator and also optionally includes a public key. +// Either the KeyLocator must be non-empty, or the public key pointer be +// non-nil. This will be used by the KeyRing interface to lookup arbitrary +// private keys, and also within the SignDescriptor struct to locate precisely +// which keys should be used for signing. +type KeyDescriptor struct { + // KeyLocator is the internal KeyLocator of the descriptor. + KeyLocator + + // PubKey is an optional public key that fully describes a target key. + // If this is nil, the KeyLocator MUST NOT be empty. + PubKey *btcec.PublicKey +} + +// KeyRing is the primary interface that will be used to perform public +// derivation of various keys used within the peer-to-peer network, and also +// within any created contracts. All derivation required by the KeyRing is +// based off of public derivation, so a system with only an extended public key +// (for the particular purpose+family) can derive this set of keys. +type KeyRing interface { + // DeriveNextKey attempts to derive the *next* key within the key + // family (account in BIP43) specified. This method should return the + // next external child within this branch. + DeriveNextKey(keyFam KeyFamily) (KeyDescriptor, error) + + // DeriveKey attempts to derive an arbitrary key specified by the + // passed KeyLocator. This may be used in several recovery scenarios, + // or when manually rotating something like our current default node + // key. + DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error) +} + +// SecretKeyRing is a similar to the regular KeyRing interface, but it is also +// able to derive *private keys*. As this is a super-set of the regular +// KeyRing, we also expect the SecretKeyRing to implement the fully KeyRing +// interface. The methods in this struct may be used to extract the node key in +// order to accept inbound network connections, or to do manual signing for +// recovery purposes. +type SecretKeyRing interface { + KeyRing + + // DerivePrivKey attempts to derive the private key that corresponds to + // the passed key descriptor. + DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error) + + // ScalarMult performs a scalar multiplication (ECDH-like operation) + // between the target key descriptor and remote public key. The output + // returned will be the sha256 of the resulting shared point serialized + // in compressed format. If k is our private key, and P is the public + // key, we perform the following operation: + // + // sx := k*P + // s := sha256(sx.SerializeCompressed()) + ScalarMult(keyDesc KeyDescriptor, pubKey *btcec.PublicKey) ([]byte, error) +} + +// TODO(roasbeef): extend to actually support scalar mult of key? +// * would allow to push in initial handshake auth into interface as well From d6f54b30fc33079321d06ca4d7a07cb37688c89b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 14:55:56 -0800 Subject: [PATCH 02/29] keychain: add a btcwallet implementation of the KeyRing and SecretKeyRing interfaces --- keychain/btcwallet.go | 288 +++++++++++++++++++++++++++++++++++++++++ keychain/derivation.go | 2 +- 2 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 keychain/btcwallet.go diff --git a/keychain/btcwallet.go b/keychain/btcwallet.go new file mode 100644 index 00000000..749f09ec --- /dev/null +++ b/keychain/btcwallet.go @@ -0,0 +1,288 @@ +package keychain + +import ( + "crypto/sha256" + "fmt" + + "github.com/roasbeef/btcd/btcec" + "github.com/roasbeef/btcwallet/waddrmgr" + "github.com/roasbeef/btcwallet/wallet" + "github.com/roasbeef/btcwallet/walletdb" +) + +var ( + // lightningKeyScope is the key scope that will be used within the + // waddrmgr to create an HD chain for deriving all of our required + // keys. + lightningKeyScope = waddrmgr.KeyScope{ + Purpose: BIP0043Purpose, + Coin: 0, + } + + // lightningAddrSchema is the scope addr schema for all keys that we + // derive. We'll treat them all as p2wkh addresses, as atm we must + // specify a particular type. + lightningAddrSchema = waddrmgr.ScopeAddrSchema{ + ExternalAddrType: waddrmgr.WitnessPubKey, + InternalAddrType: waddrmgr.WitnessPubKey, + } + + // waddrmgrNamespaceKey is the namespace key that the waddrmgr state is + // stored within the top-level waleltdb buckets of btcwallet. + waddrmgrNamespaceKey = []byte("waddrmgr") +) + +// BtcWalletKeyRing is an implementation of both the KeyRing and SecretKeyRing +// interfaces backed by btcwallet's internal root waddrmgr. Internally, we'll +// be using a ScopedKeyManager to do all of our derivations, using the key +// scope and scope addr scehma defined above. Re-using the existing key scope +// construction means that all key derivation will be protected under the root +// seed of the wallet, making each derived key fully deterministic. +type BtcWalletKeyRing struct { + // wallet is a pointer to the active instance of the btcwallet core. + // This is required as we'll need to manually open database + // transactions in order to derive addresses and lookup relevant keys + wallet *wallet.Wallet + + // lightningScope is a pointer to the scope that we'll be using as a + // sub key manager to derive all the keys that we require. + lightningScope *waddrmgr.ScopedKeyManager +} + +// NewBtcWalletKeyRing creates a new implementation of the +// keychain.SecretKeyRing interface backed by btcwallet. +// +// NOTE: The passed waddrmgr.Manager MUST be unlocked in order for the keychain +// to function. +func NewBtcWalletKeyRing(w *wallet.Wallet) SecretKeyRing { + return &BtcWalletKeyRing{ + wallet: w, + } +} + +// keyScope attempts to return the key scope that we'll use to derive all of +// our keys. If the scope has already been fetched from the database, then a +// cached version will be returned. Otherwise, we'll fetch it from the database +// and cache it for subsequent accesses. +func (b *BtcWalletKeyRing) keyScope() (*waddrmgr.ScopedKeyManager, error) { + // If the scope has already been populated, then we'll return it + // directly. + if b.lightningScope != nil { + return b.lightningScope, nil + } + + // Otherwise, we'll first do a check to ensure that the root manager + // isn't locked, as otherwise we won't be able to *use* the scope. + if b.wallet.Manager.Locked() { + return nil, fmt.Errorf("cannot create BtcWalletKeyRing with " + + "locked waddrmgr.Manager") + } + + // If the manager is indeed unlocked, then we'll fetch the scope, cache + // it, and return to the caller. + lnScope, err := b.wallet.Manager.FetchScopedKeyManager( + lightningKeyScope, + ) + if err != nil { + return nil, err + } + + b.lightningScope = lnScope + + return lnScope, nil +} + +// createAccountIfNotExists will create the corresponding account for a key +// family if it doesn't already exist in the database. +func (b *BtcWalletKeyRing) createAccountIfNotExists( + addrmgrNs walletdb.ReadWriteBucket, keyFam KeyFamily, + scope *waddrmgr.ScopedKeyManager) error { + + // If this is the multi-sig key family, then we can return early as + // this is the default account that's created. + if keyFam == KeyFamilyMultiSig { + return nil + } + + // Otherwise, we'll check if the account already exists, if so, we can + // once again bail early. + _, err := scope.AccountName(addrmgrNs, uint32(keyFam)) + if err == nil { + return nil + } + + // If we reach this point, then the account hasn't yet been created, so + // we'll need to create it before we can proceed. + return scope.NewRawAccount(addrmgrNs, uint32(keyFam)) +} + +// DeriveNextKey attempts to derive the *next* key within the key family +// (account in BIP43) specified. This method should return the next external +// child within this branch. +// +// NOTE: This is part of the keychain.KeyRing interface. +func (b *BtcWalletKeyRing) DeriveNextKey(keyFam KeyFamily) (KeyDescriptor, error) { + var pubKey *btcec.PublicKey + + db := b.wallet.Database() + err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) + + scope, err := b.keyScope() + if err != nil { + return err + } + + // If the account doesn't exist, then we may need to create it + // for the first time in order to derive the keys that we + // require. + err = b.createAccountIfNotExists(addrmgrNs, keyFam, scope) + if err != nil { + return err + } + + addrs, err := scope.NextExternalAddresses( + addrmgrNs, uint32(keyFam), 1, + ) + if err != nil { + return err + } + + pubKey = addrs[0].(waddrmgr.ManagedPubKeyAddress).PubKey() + + return nil + }) + if err != nil { + return KeyDescriptor{}, err + } + + return KeyDescriptor{ + PubKey: pubKey, + }, nil +} + +// DeriveKey attempts to derive an arbitrary key specified by the passed +// KeyLocator. This may be used in several recovery scenarios, or when manually +// rotating something like our current default node key. +// +// NOTE: This is part of the keychain.KeyRing interface. +func (b *BtcWalletKeyRing) DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error) { + var keyDesc KeyDescriptor + + db := b.wallet.Database() + err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) + + scope, err := b.keyScope() + if err != nil { + return err + } + + // If the account doesn't exist, then we may need to create it + // for the first time in order to derive the keys that we + // require. + err = b.createAccountIfNotExists(addrmgrNs, keyLoc.Family, scope) + if err != nil { + return err + } + + path := waddrmgr.DerivationPath{ + Account: uint32(keyLoc.Family), + Branch: 0, + Index: uint32(keyLoc.Index), + } + addr, err := scope.DeriveFromKeyPath(addrmgrNs, path) + if err != nil { + return err + } + + keyDesc.KeyLocator = keyLoc + keyDesc.PubKey = addr.(waddrmgr.ManagedPubKeyAddress).PubKey() + + return nil + }) + if err != nil { + return keyDesc, err + } + + return keyDesc, nil +} + +// DerivePrivKey attempts to derive the private key that corresponds to the +// passed key descriptor. +// +// NOTE: This is part of the keychain.SecretKeyRing interface. +func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error) { + var key *btcec.PrivateKey + + db := b.wallet.Database() + err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) + + scope, err := b.keyScope() + if err != nil { + return err + } + + // If the account doesn't exist, then we may need to create it + // for the first time in order to derive the keys that we + // require. + err = b.createAccountIfNotExists( + addrmgrNs, keyDesc.Family, scope, + ) + if err != nil { + return err + } + + // Now that we know the account exists, we can safely derive + // the full private key from the given path. + path := waddrmgr.DerivationPath{ + Account: uint32(keyDesc.Family), + Branch: 0, + Index: uint32(keyDesc.Index), + } + addr, err := scope.DeriveFromKeyPath(addrmgrNs, path) + if err != nil { + return err + } + + key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey() + if err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, err + } + + return key, nil +} + +// ScalarMult performs a scalar multiplication (ECDH-like operation) between +// the target key descriptor and remote public key. The output returned will be +// the sha256 of the resulting shared point serialized in compressed format. If +// k is our private key, and P is the public key, we perform the following +// operation: +// +// sx := k*P s := sha256(sx.SerializeCompressed()) +// +// NOTE: This is part of the keychain.SecretKeyRing interface. +func (b *BtcWalletKeyRing) ScalarMult(keyDesc KeyDescriptor, + pub *btcec.PublicKey) ([]byte, error) { + + privKey, err := b.DerivePrivKey(keyDesc) + if err != nil { + return nil, err + } + + s := &btcec.PublicKey{} + x, y := btcec.S256().ScalarMult(pub.X, pub.Y, privKey.D.Bytes()) + s.X = x + s.Y = y + + h := sha256.Sum256(s.SerializeCompressed()) + + return h[:], nil +} diff --git a/keychain/derivation.go b/keychain/derivation.go index b2ffd167..cc619892 100644 --- a/keychain/derivation.go +++ b/keychain/derivation.go @@ -6,7 +6,7 @@ const ( // KeyDerivationVersion is the version of the key derivation schema // defined below. We use a version as this means that we'll be able to // accept new seed in the future and be able to discern if the software - // is compatible with the version of the weed. + // is compatible with the version of the seed. KeyDerivationVersion = 0 // BIP0043Purpose is the "purpose" value that we'll use for the first From cd068d88887074c172b0340eb6f60aed44c446f1 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 14:56:21 -0800 Subject: [PATCH 03/29] keychain: add initial set up interface-level tests --- keychain/interface_test.go | 255 +++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 keychain/interface_test.go diff --git a/keychain/interface_test.go b/keychain/interface_test.go new file mode 100644 index 00000000..8d0cf247 --- /dev/null +++ b/keychain/interface_test.go @@ -0,0 +1,255 @@ +package keychain + +import ( + "fmt" + "io/ioutil" + "math/rand" + "os" + "testing" + + "github.com/roasbeef/btcd/chaincfg" + "github.com/roasbeef/btcd/chaincfg/chainhash" + "github.com/roasbeef/btcwallet/wallet" + "github.com/roasbeef/btcwallet/walletdb" + + _ "github.com/roasbeef/btcwallet/walletdb/bdb" // Required in order to create the default database. +) + +// versionZeroKeyFamilies is a slice of all the known key families for first +// version of the key derivation schema defined in this package. +var versionZeroKeyFamilies = []KeyFamily{ + KeyFamilyMultiSig, + KeyFamilyRevocationBase, + KeyFamilyHtlcBase, + KeyFamilyPaymentBase, + KeyFamilyDelayBase, + KeyFamilyRevocationRoot, + KeyFamilyNodeKey, +} + +var ( + testHDSeed = chainhash.Hash{ + 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, + 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, + 0x4f, 0x2f, 0x6f, 0x25, 0x98, 0xa3, 0xef, 0xb9, + 0x69, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, + } +) + +func createTestBtcWallet() (func(), *wallet.Wallet, error) { + tempDir, err := ioutil.TempDir("", "keyring-lnwallet") + if err != nil { + return nil, nil, err + } + loader := wallet.NewLoader(&chaincfg.SimNetParams, tempDir) + + pass := []byte("test") + + baseWallet, err := loader.CreateNewWallet(pass, pass, testHDSeed[:]) + if err != nil { + return nil, nil, err + } + + if err := baseWallet.Unlock(pass, nil); err != nil { + return nil, nil, err + } + + // We'll now ensure that the KeyScope: (201, 1) exists within the + // internal waddrmgr. We'll need this in order to properly generate the + // keys required for signing various contracts. + _, err = baseWallet.Manager.FetchScopedKeyManager(lightningKeyScope) + if err != nil { + err := walletdb.Update(baseWallet.Database(), func(tx walletdb.ReadWriteTx) error { + addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) + + _, err := baseWallet.Manager.NewScopedKeyManager( + addrmgrNs, lightningKeyScope, lightningAddrSchema, + ) + return err + }) + if err != nil { + return nil, nil, err + } + } + + cleanUp := func() { + baseWallet.Lock() + os.RemoveAll(tempDir) + } + + return cleanUp, baseWallet, nil +} + +// secretKeyRingConstructor is a function signature that's used as a generic +// constructor for various implementations of the KeyRing interface. A string +// naming the returned interface, a function closure that cleans up any +// resources, and the clean up interface itself are to be returned. +type keyRingConstructor func() (string, func(), KeyRing, error) + +// TestKeyRingDerivation tests that each known KeyRing implementation properly +// adheres to the expected behavior of the set of interfaces. +func TestKeyRingDerivation(t *testing.T) { + t.Parallel() + + keyRingImplementations := []keyRingConstructor{ + func() (string, func(), KeyRing, error) { + cleanUp, wallet, err := createTestBtcWallet() + if err != nil { + t.Fatalf("unable to create wallet: %v", err) + } + + keyRing := NewBtcWalletKeyRing(wallet) + + return "btcwallet", cleanUp, keyRing, nil + }, + } + + // For each implementation constructor registered above, we'll execute + // an identical set of tests in order to ensure that the interface + // adheres to our nominal specification. + for _, keyRingConstructor := range keyRingImplementations { + keyRingName, cleanUp, keyRing, err := keyRingConstructor() + if err != nil { + t.Fatalf("unable to create key ring %v: %v", keyRingName, + err) + } + defer cleanUp() + + success := t.Run(fmt.Sprintf("%v", keyRingName), func(t *testing.T) { + // First, we'll ensure that we're able to derive keys + // from each of the known key families. + for _, keyFam := range versionZeroKeyFamilies { + // First, we'll ensure that we can derive the + // *next* key in the keychain. + keyDesc, err := keyRing.DeriveNextKey(keyFam) + if err != nil { + t.Fatalf("unable to derive next for "+ + "keyFam=%v: %v", keyFam, err) + } + + // If we now try to manually derive the *first* + // key, then we should get an identical public + // key back. + keyLoc := KeyLocator{ + Family: keyFam, + Index: 0, + } + firstKeyDesc, err := keyRing.DeriveKey(keyLoc) + if err != nil { + t.Fatalf("unable to derive first key for "+ + "keyFam=%v: %v", keyFam, err) + } + + if !keyDesc.PubKey.IsEqual(firstKeyDesc.PubKey) { + t.Fatalf("mismatched keys: expected %v, "+ + "got %x", + keyDesc.PubKey.SerializeCompressed(), + firstKeyDesc.PubKey.SerializeCompressed()) + } + + // If this succeeds, then we'll also try to + // derive a random index within the range. + randKeyIndex := uint32(rand.Int31()) + keyLoc = KeyLocator{ + Family: keyFam, + Index: randKeyIndex, + } + _, err = keyRing.DeriveKey(keyLoc) + if err != nil { + t.Fatalf("unable to derive key_index=%v "+ + "for keyFam=%v: %v", + randKeyIndex, keyFam, err) + } + } + }) + if !success { + break + } + } +} + +// secretKeyRingConstructor is a function signature that's used as a generic +// constructor for various implementations of the SecretKeyRing interface. A +// string naming the returned interface, a function closure that cleans up any +// resources, and the clean up interface itself are to be returned. +type secretKeyRingConstructor func() (string, func(), SecretKeyRing, error) + +// TestSecretKeyRingDerivation tests that each known SecretKeyRing +// implementation properly adheres to the expected behavior of the set of +// interface. +func TestSecretKeyRingDerivation(t *testing.T) { + t.Parallel() + + secretKeyRingImplementations := []secretKeyRingConstructor{ + func() (string, func(), SecretKeyRing, error) { + cleanUp, wallet, err := createTestBtcWallet() + if err != nil { + t.Fatalf("unable to create wallet: %v", err) + } + + keyRing := NewBtcWalletKeyRing(wallet) + + return "btcwallet", cleanUp, keyRing, nil + }, + } + + // For each implementation constructor registered above, we'll execute + // an identical set of tests in order to ensure that the interface + // adheres to our nominal specification. + for _, secretKeyRingConstructor := range secretKeyRingImplementations { + keyRingName, cleanUp, secretKeyRing, err := secretKeyRingConstructor() + if err != nil { + t.Fatalf("unable to create secret key ring %v: %v", + keyRingName, err) + } + defer cleanUp() + + success := t.Run(fmt.Sprintf("%v", keyRingName), func(t *testing.T) { + // First, each key family, we'll ensure that we're able + // to obtain the private key of a randomly select child + // index within the key family. + for _, keyFam := range versionZeroKeyFamilies { + randKeyIndex := uint32(rand.Int31()) + keyLoc := KeyLocator{ + Family: keyFam, + Index: randKeyIndex, + } + + // First, we'll query for the public key for + // this target key locator. + pubKeyDesc, err := secretKeyRing.DeriveKey(keyLoc) + if err != nil { + t.Fatalf("unable to derive pubkey "+ + "(fam=%v, index=%v): %v", + keyLoc.Family, + keyLoc.Index, err) + } + + // With the public key derive, ensure that + // we're able to obtain the corresponding + // private key correctly. + privKey, err := secretKeyRing.DerivePrivKey(KeyDescriptor{ + KeyLocator: keyLoc, + }) + if err != nil { + t.Fatalf("unable to derive priv "+ + "(fam=%v, index=%v): %v", keyLoc.Family, + keyLoc.Index, err) + } + + // Finally, ensure that the keys match up + // properly. + if !pubKeyDesc.PubKey.IsEqual(privKey.PubKey()) { + t.Fatalf("pubkeys mismatched: expected %x, got %x", + pubKeyDesc.PubKey.SerializeCompressed(), + privKey.PubKey().SerializeCompressed()) + } + + // TODO(roasbeef): scalar mult once integrated + } + }) + if !success { + break + } + } +} From a26367f3cb2f0c151dbdc20b9f3b04fc920d55f7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 14:57:53 -0800 Subject: [PATCH 04/29] lnwallet: remove NewRawKey and FetchRootKey from the WalletController interface In this commit, we remove two methods from the WalletController interface which were previously used by the funding reservation process (NewRawKey) and the p2p network (FetchRootKey) in order to derive various keys for operation. This methods are no longer necessary as the KeyRing interface implements the functionality in a deterministic manner. --- lnwallet/interface.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 17fec68c..58af4fd2 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -143,20 +143,6 @@ type WalletController interface { // error should be returned. GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error) - // NewRawKey returns a raw private key controlled by the wallet. These - // keys are used for the 2-of-2 multi-sig outputs for funding - // transactions, as well as the pub key used for commitment transactions. - // - // NOTE: The wallet MUST watch for on-chain outputs created to a p2wpkh - // script using keys returned by this function. - NewRawKey() (*btcec.PublicKey, error) - - // FetchRootKey returns a root key which will be used by the - // LightningWallet to deterministically generate secrets. The private - // key returned by this method should remain constant in-between - // WalletController restarts. - FetchRootKey() (*btcec.PrivateKey, error) - // SendOutputs funds, signs, and broadcasts a Bitcoin transaction // paying out to the specified outputs. In the case the wallet has // insufficient funds, or the outputs are non-standard, an error should From 6290a520fd94ed8812f43bde8cf389f64c5fa62e Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:02:16 -0800 Subject: [PATCH 05/29] lnwallet/btcwallet: remove pruned interface methods These methods are no longer a part of the interface. As a result, we can safely delete them. --- lnwallet/btcwallet/btcwallet.go | 72 --------------------------------- 1 file changed, 72 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 2855d319..b446974b 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -250,78 +250,6 @@ func (b *BtcWallet) GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error) { return b.wallet.PrivKeyForAddress(a) } -// NewRawKey retrieves the next key within our HD key-chain for use within as a -// multi-sig key within the funding transaction, or within the commitment -// transaction's outputs. -// -// This is a part of the WalletController interface. -func (b *BtcWallet) NewRawKey() (*btcec.PublicKey, error) { - addr, err := b.wallet.NewAddress(defaultAccount, - waddrmgr.WitnessPubKey) - if err != nil { - return nil, err - } - - return b.wallet.PubKeyForAddress(addr) -} - -// FetchRootKey returns a root key which is intended to be used as an initial -// seed/salt to generate any Lightning specific secrets. -// -// This is a part of the WalletController interface. -func (b *BtcWallet) FetchRootKey() (*btcec.PrivateKey, error) { - // Fetch the root address hash from the database, this is persisted - // locally within the database, then used to obtain the key from the - // wallet based on the address hash. - var rootAddrHash []byte - if err := walletdb.View(b.db, func(tx walletdb.ReadTx) error { - lnBucket := tx.ReadBucket(lnNamespace) - - rootAddrHash = lnBucket.Get(rootKey) - return nil - }); err != nil { - return nil, err - } - - if rootAddrHash == nil { - // Otherwise, we need to generate a fresh address from the - // wallet, then stores its hash160 within the database so we - // can look up the exact key later. - if err := walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error { - addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) - addrs, err := b.wallet.Manager.NextExternalAddresses(addrmgrNs, - defaultAccount, 1, waddrmgr.WitnessPubKey) - if err != nil { - return err - } - rootAddr := addrs[0].Address() - - lnBucket := tx.ReadWriteBucket(lnNamespace) - - rootAddrHash = rootAddr.ScriptAddress() - return lnBucket.Put(rootKey, rootAddrHash) - }); err != nil { - return nil, err - } - } - - // With the root address hash obtained, generate the corresponding - // address, then retrieve the managed address from the wallet which - // will allow us to obtain the private key. - rootAddr, err := btcutil.NewAddressWitnessPubKeyHash(rootAddrHash, - b.netParams) - if err != nil { - return nil, err - } - - priv, err := b.wallet.PrivKeyForAddress(rootAddr) - if err != nil { - return nil, err - } - - return priv, nil -} - // SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to // the specified outputs. In the case the wallet has insufficient funds, or the // outputs are non-standard, a non-nil error will be be returned. From 93280ad60af9edde8a6b175b92543539c9429ce8 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:03:59 -0800 Subject: [PATCH 06/29] lnwallet/btcwallet: update implementation to account for recent API changes The new version of the internal core of btcwallet now uses KeyScopes rather than address types to derive particular addresses. As a result, in this commit, we update our API usage to ensure that proper addresses are still derived. --- lnwallet/btcwallet/btcwallet.go | 104 +++++++++++++++++++------------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index b446974b..f9039ea7 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg" @@ -27,9 +28,25 @@ const ( ) var ( - lnNamespace = []byte("ln") - rootKey = []byte("ln-root") + // waddrmgrNamespaceKey is the namespace key that the waddrmgr state is + // stored within the top-level waleltdb buckets of btcwallet. waddrmgrNamespaceKey = []byte("waddrmgr") + + // lightningKeyScope is the key scope that will be used within the + // waddrmgr to create an HD chain for deriving all of our required + // keys. We'll ensure this this scope is created upon start. + lightningKeyScope = waddrmgr.KeyScope{ + Purpose: keychain.BIP0043Purpose, + Coin: 0, + } + + // lightningAddrSchema is the scope addr schema for all keys that we + // derive. We'll treat them all as p2wkh addresses, as atm we must + // specify a particular type. + lightningAddrSchema = waddrmgr.ScopeAddrSchema{ + ExternalAddrType: waddrmgr.WitnessPubKey, + InternalAddrType: waddrmgr.WitnessPubKey, + } ) // BtcWallet is an implementation of the lnwallet.WalletController interface @@ -83,49 +100,23 @@ func New(cfg Config) (*BtcWallet, error) { wallet, err = loader.CreateNewWallet( pubPass, cfg.PrivatePass, cfg.HdSeed, ) - - switch { - // If the wallet already exists, then we'll ignore this error - // and proceed directly to opening the wallet. - case err == base.ErrExists: - - // Otherwise, there's a greater error here, and we'll return - // early. - case err != nil: + if err != nil { return nil, err } - - if err := loader.UnloadWallet(); err != nil { + } else { + // Wallet has been created and been initialized at this point, + // open it along with all the required DB namespaces, and the + // DB itself. + wallet, err = loader.OpenExistingWallet(pubPass, false) + if err != nil { return nil, err } } - // Wallet has been created and been initialized at this point, open it - // along with all the required DB namepsaces, and the DB itself. - wallet, err = loader.OpenExistingWallet(pubPass, false) - if err != nil { - return nil, err - } - - // Create a bucket within the wallet's database dedicated to storing - // our LN specific data. - db := wallet.Database() - err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { - _, err := tx.CreateTopLevelBucket(lnNamespace) - if err != nil && err != walletdb.ErrBucketExists { - return err - } - - return nil - }) - if err != nil { - return nil, err - } - return &BtcWallet{ cfg: &cfg, wallet: wallet, - db: db, + db: wallet.Database(), chain: cfg.ChainSource, netParams: cfg.NetParams, utxoCache: make(map[wire.OutPoint]*wire.TxOut), @@ -143,6 +134,12 @@ func (b *BtcWallet) BackEnd() string { return "" } +// InternalWallet returns a pointer to the internal base wallet which is the +// core of btcwallet. +func (b *BtcWallet) InternalWallet() *base.Wallet { + return b.wallet +} + // Start initializes the underlying rpc connection, the wallet itself, and // begins syncing to the current available blockchain state. // @@ -165,6 +162,27 @@ func (b *BtcWallet) Start() error { return err } + // We'll now ensure that the KeyScope: (1017, 1) exists within the + // internal waddrmgr. We'll need this in order to properly generate the + // keys required for signing various contracts. + _, err := b.wallet.Manager.FetchScopedKeyManager(lightningKeyScope) + if err != nil { + // If the scope hasn't yet been created (it wouldn't been + // loaded by default if it was), then we'll manually create the + // scope for the first time ourselves. + err := walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error { + addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) + + _, err := b.wallet.Manager.NewScopedKeyManager( + addrmgrNs, lightningKeyScope, lightningAddrSchema, + ) + return err + }) + if err != nil { + return err + } + } + return nil } @@ -209,6 +227,8 @@ func (b *BtcWallet) ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, balance = outputSum } + // TODO(roasbeef): remove witness only distinction? + return balance, nil } @@ -219,24 +239,22 @@ func (b *BtcWallet) ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, // // This is a part of the WalletController interface. func (b *BtcWallet) NewAddress(t lnwallet.AddressType, change bool) (btcutil.Address, error) { - var addrType waddrmgr.AddressType + var keyScope waddrmgr.KeyScope switch t { case lnwallet.WitnessPubKey: - addrType = waddrmgr.WitnessPubKey + keyScope = waddrmgr.KeyScopeBIP0084 case lnwallet.NestedWitnessPubKey: - addrType = waddrmgr.NestedWitnessPubKey - case lnwallet.PubKeyHash: - addrType = waddrmgr.PubKeyHash + keyScope = waddrmgr.KeyScopeBIP0049Plus default: return nil, fmt.Errorf("unknown address type") } if change { - return b.wallet.NewChangeAddress(defaultAccount, addrType) + return b.wallet.NewChangeAddress(defaultAccount, keyScope) } - return b.wallet.NewAddress(defaultAccount, addrType) + return b.wallet.NewAddress(defaultAccount, keyScope) } // GetPrivKey retrieves the underlying private key associated with the passed From 9ccdcaf74719a5fbae438fcff21dcb9598c95803 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:08:21 -0800 Subject: [PATCH 07/29] lnwallet: update the SignDescriptor struct to use keychain.KeyDescriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we update the SignDescriptor struct to instead use the keychain.KeyDescriptor struct inplace of a raw public key. We do this as this allows for a recovery mode, where the caller doesn’t know what key was used exactly, but remembers the precise KeyDescriptor. A stateless signer is still able to re-derive the proper key, as it’s given the full path. --- lnwallet/signdescriptor.go | 53 ++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/lnwallet/signdescriptor.go b/lnwallet/signdescriptor.go index f2057d2a..d1ae1834 100644 --- a/lnwallet/signdescriptor.go +++ b/lnwallet/signdescriptor.go @@ -5,6 +5,7 @@ import ( "errors" "io" + "github.com/lightningnetwork/lnd/keychain" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" @@ -20,10 +21,11 @@ var ( // a given output. This struct is used by the Signer interface in order to gain // access to critical data needed to generate a valid signature. type SignDescriptor struct { - // Pubkey is the public key to which the signature should be generated - // over. The Signer should then generate a signature with the private - // key corresponding to this public key. - PubKey *btcec.PublicKey + // KeyDesc is a descriptor that precisely describes *which* key to use + // for signing. This may provide the raw public key directly, or + // require the Signer to re-derive the key according to the populated + // derivation path. + KeyDesc keychain.KeyDescriptor // SingleTweak is a scalar value that will be added to the private key // corresponding to the above public key to obtain the private key to @@ -83,10 +85,26 @@ type SignDescriptor struct { // yet, since that is usually done just before broadcast by the witness // generator. func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error { - serializedPubKey := sd.PubKey.SerializeCompressed() - if err := wire.WriteVarBytes(w, 0, serializedPubKey); err != nil { + err := binary.Write(w, binary.BigEndian, sd.KeyDesc.Family) + if err != nil { return err } + err = binary.Write(w, binary.BigEndian, sd.KeyDesc.Index) + if err != nil { + return err + } + + err = binary.Write(w, binary.BigEndian, sd.KeyDesc.PubKey != nil) + if err != nil { + return err + } + + if sd.KeyDesc.PubKey != nil { + serializedPubKey := sd.KeyDesc.PubKey.SerializeCompressed() + if err := wire.WriteVarBytes(w, 0, serializedPubKey); err != nil { + return err + } + } if err := wire.WriteVarBytes(w, 0, sd.SingleTweak); err != nil { return err @@ -120,15 +138,34 @@ func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error { // ReadSignDescriptor deserializes a SignDescriptor struct from the passed // io.Reader stream. func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error { - pubKeyBytes, err := wire.ReadVarBytes(r, 0, 34, "pubkey") + err := binary.Read(r, binary.BigEndian, &sd.KeyDesc.Family) if err != nil { return err } - sd.PubKey, err = btcec.ParsePubKey(pubKeyBytes, btcec.S256()) + err = binary.Read(r, binary.BigEndian, &sd.KeyDesc.Index) if err != nil { return err } + var hasKey bool + err = binary.Read(r, binary.BigEndian, &hasKey) + if err != nil { + return err + } + + if hasKey { + pubKeyBytes, err := wire.ReadVarBytes(r, 0, 34, "pubkey") + if err != nil { + return err + } + sd.KeyDesc.PubKey, err = btcec.ParsePubKey( + pubKeyBytes, btcec.S256(), + ) + if err != nil { + return err + } + } + singleTweak, err := wire.ReadVarBytes(r, 0, 32, "singleTweak") if err != nil { return err From 73dd1536ed1b49eb435c93843eda899026232b0c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:09:06 -0800 Subject: [PATCH 08/29] lnwallet: update signdescriptor_test.go due to latest API changes --- lnwallet/signdescriptor_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lnwallet/signdescriptor_test.go b/lnwallet/signdescriptor_test.go index 4225665f..7a4be0fc 100644 --- a/lnwallet/signdescriptor_test.go +++ b/lnwallet/signdescriptor_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/lightningnetwork/lnd/keychain" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" @@ -99,7 +100,13 @@ func TestSignDescriptorSerialization(t *testing.T) { if err != nil { t.Fatalf("unable to parse pubkey: %v", err) } - sd.PubKey = pubkey + sd.KeyDesc = keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: 50, + Index: 99, + }, + PubKey: pubkey, + } // Test that serialize -> deserialize yields same result as original. var buf bytes.Buffer From a0dc06b954a1b115e1fb18d424b9183bc5d27786 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:09:51 -0800 Subject: [PATCH 09/29] lnwallet: update script_utils and tests due to SignDescriptor API change --- lnwallet/script_utils.go | 30 ++++++++++--- lnwallet/script_utils_test.go | 84 ++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/lnwallet/script_utils.go b/lnwallet/script_utils.go index 2ce51a3a..14266412 100644 --- a/lnwallet/script_utils.go +++ b/lnwallet/script_utils.go @@ -11,7 +11,6 @@ import ( "golang.org/x/crypto/ripemd160" "github.com/roasbeef/btcd/btcec" - "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" @@ -312,10 +311,17 @@ func senderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, func SenderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, sweepTx *wire.MsgTx) (wire.TxWitness, error) { + if signDesc.KeyDesc.PubKey == nil { + return nil, fmt.Errorf("cannot generate witness with nil " + + "KeyDesc pubkey") + } + // Derive the revocation key using the local revocation base point and // commitment point. - revokeKey := DeriveRevocationPubkey(signDesc.PubKey, - signDesc.DoubleTweak.PubKey()) + revokeKey := DeriveRevocationPubkey( + signDesc.KeyDesc.PubKey, + signDesc.DoubleTweak.PubKey(), + ) return senderHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx) } @@ -562,10 +568,17 @@ func receiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, func ReceiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, sweepTx *wire.MsgTx) (wire.TxWitness, error) { + if signDesc.KeyDesc.PubKey == nil { + return nil, fmt.Errorf("cannot generate witness with nil " + + "KeyDesc pubkey") + } + // Derive the revocation key using the local revocation base point and // commitment point. - revokeKey := DeriveRevocationPubkey(signDesc.PubKey, - signDesc.DoubleTweak.PubKey()) + revokeKey := DeriveRevocationPubkey( + signDesc.KeyDesc.PubKey, + signDesc.DoubleTweak.PubKey(), + ) return receiverHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx) } @@ -1023,6 +1036,11 @@ func CommitSpendRevoke(signer Signer, signDesc *SignDescriptor, func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor, sweepTx *wire.MsgTx) (wire.TxWitness, error) { + if signDesc.KeyDesc.PubKey == nil { + return nil, fmt.Errorf("cannot generate witness with nil " + + "KeyDesc pubkey") + } + // This is just a regular p2wkh spend which looks something like: // * witness: sweepSig, err := signer.SignOutputRaw(sweepTx, signDesc) @@ -1037,7 +1055,7 @@ func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor, witness := make([][]byte, 2) witness[0] = append(sweepSig, byte(signDesc.HashType)) witness[1] = TweakPubKeyWithTweak( - signDesc.PubKey, signDesc.SingleTweak, + signDesc.KeyDesc.PubKey, signDesc.SingleTweak, ).SerializeCompressed() return witness, nil diff --git a/lnwallet/script_utils_test.go b/lnwallet/script_utils_test.go index 66288486..921e0377 100644 --- a/lnwallet/script_utils_test.go +++ b/lnwallet/script_utils_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/lightningnetwork/lnd/keychain" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/txscript" @@ -112,9 +113,11 @@ func TestCommitmentSpendValidation(t *testing.T) { sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, csvTimeout) signDesc := &SignDescriptor{ WitnessScript: delayScript, - PubKey: aliceKeyPub, - SingleTweak: aliceCommitTweak, - SigHashes: txscript.NewTxSigHashes(sweepTx), + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + SingleTweak: aliceCommitTweak, + SigHashes: txscript.NewTxSigHashes(sweepTx), Output: &wire.TxOut{ Value: int64(channelBalance), }, @@ -143,7 +146,9 @@ func TestCommitmentSpendValidation(t *testing.T) { // simulate the scenario when Alice broadcasts this commitment // transaction after it's been revoked. signDesc = &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, DoubleTweak: commitSecret, WitnessScript: delayScript, SigHashes: txscript.NewTxSigHashes(sweepTx), @@ -186,7 +191,9 @@ func TestCommitmentSpendValidation(t *testing.T) { t.Fatalf("unable to create bob p2wkh script: %v", err) } signDesc = &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: bobCommitTweak, WitnessScript: bobScriptP2WKH, SigHashes: txscript.NewTxSigHashes(sweepTx), @@ -394,7 +401,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) { // that will act as Bob's signature to Alice for the second level HTLC // transaction. bobSignDesc := SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: bobCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -416,7 +425,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) { // TODO(roasbeef): test invalid revoke makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, DoubleTweak: commitSecret, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -434,7 +445,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) { // HTLC with invalid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: bobCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -455,7 +468,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) { // TODO(roasbeef): invalid preimage makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: bobCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -475,7 +490,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) { // transaction. makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, SingleTweak: aliceCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -491,6 +508,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) { }, } + // TODO(roasbeef): set of cases to ensure able to sign w/ keypath and + // not + for i, testCase := range testCases { sweepTx.TxIn[0].Witness = testCase.witness() @@ -639,7 +659,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { // that will act as Alice's signature to Bob for the second level HTLC // transaction. aliceSignDesc := SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, SingleTweak: aliceCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -661,7 +683,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { // HTLC redemption w/ invalid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: bobCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -681,7 +705,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { // HTLC redemption w/ valid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: bobCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -700,7 +726,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { // revoke w/ sig makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, DoubleTweak: commitSecret, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -718,7 +746,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { // refund w/ invalid lock time makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, SingleTweak: aliceCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -736,7 +766,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) { // refund w/ valid lock time makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, SingleTweak: aliceCommitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -881,7 +913,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) { // use the double tweak in this case). makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, WitnessScript: htlcWitnessScript, Output: htlcOutput, HashType: txscript.SigHashAll, @@ -898,7 +932,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) { // Sender of HTLC activates the revocation clause. makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: aliceKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, DoubleTweak: commitSecret, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -918,7 +954,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) { // blocks instead of 5 blocks). makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: commitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, @@ -938,7 +976,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) { // tweak). makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, WitnessScript: htlcWitnessScript, Output: htlcOutput, HashType: txscript.SigHashAll, @@ -956,7 +996,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) { // delay, and the correct key. makeWitnessTestCase(t, func() (wire.TxWitness, error) { signDesc := &SignDescriptor{ - PubKey: bobKeyPub, + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, SingleTweak: commitTweak, WitnessScript: htlcWitnessScript, Output: htlcOutput, From af1fd65655602594686ace08873563a48215ec8c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:10:51 -0800 Subject: [PATCH 10/29] lnwallet/btcwallet: update Signer implementation to use keychain.KeyDescriptor --- lnwallet/btcwallet/signer.go | 54 ++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/lnwallet/btcwallet/signer.go b/lnwallet/btcwallet/signer.go index 4634a668..b3c92d2a 100644 --- a/lnwallet/btcwallet/signer.go +++ b/lnwallet/btcwallet/signer.go @@ -2,6 +2,7 @@ package btcwallet import ( "github.com/go-errors/errors" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -10,6 +11,7 @@ import ( "github.com/roasbeef/btcutil" "github.com/roasbeef/btcwallet/waddrmgr" base "github.com/roasbeef/btcwallet/wallet" + "github.com/roasbeef/btcwallet/walletdb" ) // FetchInputInfo queries for the WalletController's knowledge of the passed @@ -74,11 +76,44 @@ func (b *BtcWallet) fetchOutputAddr(script []byte) (waddrmgr.ManagedAddress, err } // fetchPrivKey attempts to retrieve the raw private key corresponding to the -// passed public key. -// TODO(roasbeef): alternatively can extract all the data pushes within the -// script, then attempt to match keys one by one -func (b *BtcWallet) fetchPrivKey(pub *btcec.PublicKey) (*btcec.PrivateKey, error) { - hash160 := btcutil.Hash160(pub.SerializeCompressed()) +// passed public key if populated, or the key descriptor path (if non-empty). +func (b *BtcWallet) fetchPrivKey(keyDesc *keychain.KeyDescriptor) (*btcec.PrivateKey, error) { + // If the key locator within the descriptor *isn't* empty, then we can + // directly derive the keys raw. + if !keyDesc.KeyLocator.IsEmpty() { + // We'll assume the special lightning key scope in this case. + scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager( + lightningKeyScope, + ) + if err != nil { + return nil, err + } + + var key *btcec.PrivateKey + err = walletdb.View(b.db, func(tx walletdb.ReadTx) error { + addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey) + + path := waddrmgr.DerivationPath{ + Account: uint32(keyDesc.Family), + Branch: 0, + Index: uint32(keyDesc.Index), + } + addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path) + if err != nil { + return err + } + + key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey() + return err + }) + if err != nil { + return nil, err + } + + return key, nil + } + + hash160 := btcutil.Hash160(keyDesc.PubKey.SerializeCompressed()) addr, err := btcutil.NewAddressWitnessPubKeyHash(hash160, b.netParams) if err != nil { return nil, err @@ -117,11 +152,12 @@ func maybeTweakPrivKey(signDesc *lnwallet.SignDescriptor, // This is a part of the WalletController interface. func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx, signDesc *lnwallet.SignDescriptor) ([]byte, error) { + witnessScript := signDesc.WitnessScript // First attempt to fetch the private key which corresponds to the // specified public key. - privKey, err := b.fetchPrivKey(signDesc.PubKey) + privKey, err := b.fetchPrivKey(&signDesc.KeyDesc) if err != nil { return nil, err } @@ -176,7 +212,7 @@ func (b *BtcWallet) ComputeInputScript(tx *wire.MsgTx, // If we're spending p2wkh output nested within a p2sh output, then // we'll need to attach a sigScript in addition to witness data. - case pka.IsNestedWitness(): + case pka.AddrType() == waddrmgr.NestedWitnessPubKey: pubKey := privKey.PubKey() pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) @@ -249,7 +285,9 @@ func (b *BtcWallet) SignMessage(pubKey *btcec.PublicKey, // First attempt to fetch the private key which corresponds to the // specified public key. - privKey, err := b.fetchPrivKey(pubKey) + privKey, err := b.fetchPrivKey(&keychain.KeyDescriptor{ + PubKey: pubKey, + }) if err != nil { return nil, err } From 705661a39eee9d858b53a4fdb73c162de3d54eb7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:14:07 -0800 Subject: [PATCH 11/29] channeldb: replace raw keys in ChannelConfig with keychain.KeyDescriptor In this commit, we remove references to raw keys from the main ChannelConfig struct and instead replace it with usage of keychain.KeyDescriptor. We do this, as the ChannelConfig as it stands is a near complete static description of a channel. In the future, it will be possible to export these static descriptions as backups. We prefer the KeyDescriptor of a plain PublicKey, as the KeyLocator portion of the struct allows a stateless signer to re-derive the keys as needed when signing. --- channeldb/channel.go | 11 +++++----- channeldb/channel_test.go | 45 ++++++++++++++++++++++++++++----------- channeldb/codec.go | 34 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 23469596..0153f3d7 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/boltdb/bolt" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" @@ -170,33 +171,33 @@ type ChannelConfig struct { // MultiSigKey is the key to be used within the 2-of-2 output script // for the owner of this channel config. - MultiSigKey *btcec.PublicKey + MultiSigKey keychain.KeyDescriptor // RevocationBasePoint is the base public key to be used when deriving // revocation keys for the remote node's commitment transaction. This // will be combined along with a per commitment secret to derive a // unique revocation key for each state. - RevocationBasePoint *btcec.PublicKey + RevocationBasePoint keychain.KeyDescriptor // PaymentBasePoint is the base public key to be used when deriving // the key used within the non-delayed pay-to-self output on the // commitment transaction for a node. This will be combined with a // tweak derived from the per-commitment point to ensure unique keys // for each commitment transaction. - PaymentBasePoint *btcec.PublicKey + PaymentBasePoint keychain.KeyDescriptor // DelayBasePoint is the base public key to be used when deriving the // key used within the delayed pay-to-self output on the commitment // transaction for a node. This will be combined with a tweak derived // from the per-commitment point to ensure unique keys for each // commitment transaction. - DelayBasePoint *btcec.PublicKey + DelayBasePoint keychain.KeyDescriptor // HtlcBasePoint is the base public key to be used when deriving the // local HTLC key. The derived key (combined with the tweak derived // from the per-commitment point) is used within the "to self" clause // within any HTLC output scripts. - HtlcBasePoint *btcec.PublicKey + HtlcBasePoint keychain.KeyDescriptor } // ChannelCommitment is a snapshot of the commitment state at a particular diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index b2643a93..072d24a0 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" @@ -136,12 +137,22 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) { MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MaxAcceptedHtlcs: uint16(rand.Int31()), }, - CsvDelay: uint16(rand.Int31()), - MultiSigKey: privKey.PubKey(), - RevocationBasePoint: privKey.PubKey(), - PaymentBasePoint: privKey.PubKey(), - DelayBasePoint: privKey.PubKey(), - HtlcBasePoint: privKey.PubKey(), + CsvDelay: uint16(rand.Int31()), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, } remoteCfg := ChannelConfig{ ChannelConstraints: ChannelConstraints{ @@ -151,12 +162,22 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) { MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MaxAcceptedHtlcs: uint16(rand.Int31()), }, - CsvDelay: uint16(rand.Int31()), - MultiSigKey: privKey.PubKey(), - RevocationBasePoint: privKey.PubKey(), - PaymentBasePoint: privKey.PubKey(), - DelayBasePoint: privKey.PubKey(), - HtlcBasePoint: privKey.PubKey(), + CsvDelay: uint16(rand.Int31()), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: privKey.PubKey(), + }, } chanID := lnwire.NewShortChanIDFromInt(uint64(rand.Int63())) diff --git a/channeldb/codec.go b/channeldb/codec.go index 5db6dd90..14665e0c 100644 --- a/channeldb/codec.go +++ b/channeldb/codec.go @@ -5,6 +5,7 @@ import ( "fmt" "io" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" @@ -48,6 +49,22 @@ func readOutpoint(r io.Reader, o *wire.OutPoint) error { // to dynamically expand to accommodate additional data. func writeElement(w io.Writer, element interface{}) error { switch e := element.(type) { + case keychain.KeyDescriptor: + if err := binary.Write(w, byteOrder, e.Family); err != nil { + return err + } + if err := binary.Write(w, byteOrder, e.Index); err != nil { + return err + } + + if e.PubKey != nil { + if err := binary.Write(w, byteOrder, true); err != nil { + } + + return writeElement(w, e.PubKey) + } + + return binary.Write(w, byteOrder, false) case ChannelType: if err := binary.Write(w, byteOrder, e); err != nil { return err @@ -163,6 +180,23 @@ func writeElements(w io.Writer, elements ...interface{}) error { // encoded using the serialization format of the database. func readElement(r io.Reader, element interface{}) error { switch e := element.(type) { + case *keychain.KeyDescriptor: + if err := binary.Read(r, byteOrder, &e.Family); err != nil { + return err + } + if err := binary.Read(r, byteOrder, &e.Index); err != nil { + return err + } + + var hasPubKey bool + if err := binary.Read(r, byteOrder, &hasPubKey); err != nil { + return err + } + + if hasPubKey { + return readElement(r, &e.PubKey) + } + case *ChannelType: if err := binary.Read(r, byteOrder, e); err != nil { return err From 22ee0a757660cd14553de934fae01770b641ba13 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:17:40 -0800 Subject: [PATCH 12/29] lnwallet: update state machine due to channeldb and SignDescriptor changes --- lnwallet/channel.go | 101 ++++++++++++++++++++------------------- lnwallet/channel_test.go | 73 ++++++++++++++++++---------- 2 files changed, 101 insertions(+), 73 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 650de152..66036dfa 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -835,15 +835,18 @@ func deriveCommitmentKeys(commitPoint *btcec.PublicKey, isOurCommit bool, keyRing := &CommitmentKeyRing{ CommitPoint: commitPoint, - LocalCommitKeyTweak: SingleTweakBytes(commitPoint, - localChanCfg.PaymentBasePoint), - LocalHtlcKeyTweak: SingleTweakBytes(commitPoint, - localChanCfg.HtlcBasePoint), - - LocalHtlcKey: TweakPubKey(localChanCfg.HtlcBasePoint, - commitPoint), - RemoteHtlcKey: TweakPubKey(remoteChanCfg.HtlcBasePoint, - commitPoint), + LocalCommitKeyTweak: SingleTweakBytes( + commitPoint, localChanCfg.PaymentBasePoint.PubKey, + ), + LocalHtlcKeyTweak: SingleTweakBytes( + commitPoint, localChanCfg.HtlcBasePoint.PubKey, + ), + LocalHtlcKey: TweakPubKey( + localChanCfg.HtlcBasePoint.PubKey, commitPoint, + ), + RemoteHtlcKey: TweakPubKey( + remoteChanCfg.HtlcBasePoint.PubKey, commitPoint, + ), } // We'll now compute the delay, no delay, and revocation key based on @@ -857,13 +860,13 @@ func deriveCommitmentKeys(commitPoint *btcec.PublicKey, isOurCommit bool, revocationBasePoint *btcec.PublicKey ) if isOurCommit { - delayBasePoint = localChanCfg.DelayBasePoint - noDelayBasePoint = remoteChanCfg.PaymentBasePoint - revocationBasePoint = remoteChanCfg.RevocationBasePoint + delayBasePoint = localChanCfg.DelayBasePoint.PubKey + noDelayBasePoint = remoteChanCfg.PaymentBasePoint.PubKey + revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey } else { - delayBasePoint = remoteChanCfg.DelayBasePoint - noDelayBasePoint = localChanCfg.PaymentBasePoint - revocationBasePoint = localChanCfg.RevocationBasePoint + delayBasePoint = remoteChanCfg.DelayBasePoint.PubKey + noDelayBasePoint = localChanCfg.PaymentBasePoint.PubKey + revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey } // With the base points assigned, we can now derive the actual keys @@ -1241,8 +1244,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache, remoteUpdateLog: remoteUpdateLog, ChanPoint: &state.FundingOutpoint, Capacity: state.Capacity, - LocalFundingKey: state.LocalChanCfg.MultiSigKey, - RemoteFundingKey: state.RemoteChanCfg.MultiSigKey, + LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey, + RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey, quit: make(chan struct{}), } @@ -1277,8 +1280,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache, // createSignDesc derives the SignDescriptor for commitment transactions from // other fields on the LightningChannel. func (lc *LightningChannel) createSignDesc() error { - localKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() - remoteKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() + localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() + remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() multiSigScript, err := genMultiSigScript(localKey, remoteKey) if err != nil { @@ -1290,7 +1293,7 @@ func (lc *LightningChannel) createSignDesc() error { return err } lc.signDesc = &SignDescriptor{ - PubKey: lc.localChanCfg.MultiSigKey, + KeyDesc: lc.localChanCfg.MultiSigKey, WitnessScript: multiSigScript, Output: &wire.TxOut{ PkScript: fundingPkScript, @@ -1310,13 +1313,13 @@ func (lc *LightningChannel) createStateHintObfuscator() { state := lc.channelState if state.IsInitiator { lc.stateHintObfuscator = DeriveStateHintObfuscator( - state.LocalChanCfg.PaymentBasePoint, - state.RemoteChanCfg.PaymentBasePoint, + state.LocalChanCfg.PaymentBasePoint.PubKey, + state.RemoteChanCfg.PaymentBasePoint.PubKey, ) } else { lc.stateHintObfuscator = DeriveStateHintObfuscator( - state.RemoteChanCfg.PaymentBasePoint, - state.LocalChanCfg.PaymentBasePoint, + state.RemoteChanCfg.PaymentBasePoint.PubKey, + state.LocalChanCfg.PaymentBasePoint.PubKey, ) } } @@ -1820,7 +1823,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, if localAmt >= chanState.RemoteChanCfg.DustLimit { localSignDesc = &SignDescriptor{ SingleTweak: keyRing.LocalCommitKeyTweak, - PubKey: chanState.LocalChanCfg.PaymentBasePoint, + KeyDesc: chanState.LocalChanCfg.PaymentBasePoint, WitnessScript: localPkScript, Output: &wire.TxOut{ PkScript: localPkScript, @@ -1834,7 +1837,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // limit, assemble the remote sign descriptor. if remoteAmt >= chanState.RemoteChanCfg.DustLimit { remoteSignDesc = &SignDescriptor{ - PubKey: chanState.LocalChanCfg.RevocationBasePoint, + KeyDesc: chanState.LocalChanCfg.RevocationBasePoint, DoubleTweak: commitmentSecret, WitnessScript: remotePkScript, Output: &wire.TxOut{ @@ -1894,7 +1897,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, htlcRetributions[i] = HtlcRetribution{ SignDesc: SignDescriptor{ - PubKey: chanState.LocalChanCfg.RevocationBasePoint, + KeyDesc: chanState.LocalChanCfg.RevocationBasePoint, DoubleTweak: commitmentSecret, WitnessScript: htlcScript, Output: &wire.TxOut{ @@ -2462,7 +2465,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // signature to give to the remote party for this commitment // transaction. Note we use the raw HTLC amount. sigJob.signDesc = SignDescriptor{ - PubKey: localChanCfg.HtlcBasePoint, + KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, WitnessScript: htlc.theirWitnessScript, Output: &wire.TxOut{ @@ -2512,7 +2515,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // signature to give to the remote party for this commitment // transaction. Note we use the raw HTLC amount. sigJob.signDesc = SignDescriptor{ - PubKey: localChanCfg.HtlcBasePoint, + KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, WitnessScript: htlc.theirWitnessScript, Output: &wire.TxOut{ @@ -3532,8 +3535,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, // we'll ensure that the newly constructed commitment state has a valid // signature. verifyKey := btcec.PublicKey{ - X: lc.remoteChanCfg.MultiSigKey.X, - Y: lc.remoteChanCfg.MultiSigKey.Y, + X: lc.remoteChanCfg.MultiSigKey.PubKey.X, + Y: lc.remoteChanCfg.MultiSigKey.PubKey.Y, Curve: btcec.S256(), } cSig, err := commitSig.ToSignature() @@ -4145,8 +4148,8 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { // With the final signature generated, create the witness stack // required to spend from the multi-sig output. - ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() - theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() + ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() + theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() commitTx.TxIn[0].Witness = SpendMultiSig( lc.signDesc.WitnessScript, ourKey, @@ -4267,7 +4270,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer, commitResolution = &CommitOutputResolution{ SelfOutPoint: *selfPoint, SelfOutputSignDesc: SignDescriptor{ - PubKey: localPayBase, + KeyDesc: localPayBase, SingleTweak: keyRing.LocalCommitKeyTweak, WitnessScript: selfP2WKH, Output: &wire.TxOut{ @@ -4435,7 +4438,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon Expiry: htlc.RefundTimeout, ClaimOutpoint: op, SweepSignDesc: SignDescriptor{ - PubKey: localChanCfg.HtlcBasePoint, + KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, WitnessScript: htlcReceiverScript, Output: &wire.TxOut{ @@ -4475,7 +4478,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon return nil, err } timeoutSignDesc := SignDescriptor{ - PubKey: localChanCfg.HtlcBasePoint, + KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, WitnessScript: htlcCreationScript, Output: &wire.TxOut{ @@ -4509,8 +4512,9 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon return nil, err } - localDelayTweak := SingleTweakBytes(keyRing.CommitPoint, - localChanCfg.DelayBasePoint) + localDelayTweak := SingleTweakBytes( + keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey, + ) return &OutgoingHtlcResolution{ Expiry: htlc.RefundTimeout, SignedTimeoutTx: timeoutTx, @@ -4520,7 +4524,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon Index: 0, }, SweepSignDesc: SignDescriptor{ - PubKey: localChanCfg.DelayBasePoint, + KeyDesc: localChanCfg.DelayBasePoint, SingleTweak: localDelayTweak, WitnessScript: htlcSweepScript, Output: &wire.TxOut{ @@ -4573,7 +4577,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon ClaimOutpoint: op, CsvDelay: csvDelay, SweepSignDesc: SignDescriptor{ - PubKey: localChanCfg.HtlcBasePoint, + KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, WitnessScript: htlcSenderScript, Output: &wire.TxOut{ @@ -4609,7 +4613,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon return nil, err } successSignDesc := SignDescriptor{ - PubKey: localChanCfg.HtlcBasePoint, + KeyDesc: localChanCfg.HtlcBasePoint, SingleTweak: keyRing.LocalHtlcKeyTweak, WitnessScript: htlcCreationScript, Output: &wire.TxOut{ @@ -4645,7 +4649,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon } localDelayTweak := SingleTweakBytes( - keyRing.CommitPoint, localChanCfg.DelayBasePoint, + keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey, ) return &IncomingHtlcResolution{ Preimage: preimage, @@ -4656,7 +4660,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon Index: 0, }, SweepSignDesc: SignDescriptor{ - PubKey: localChanCfg.DelayBasePoint, + KeyDesc: localChanCfg.DelayBasePoint, SingleTweak: localDelayTweak, WitnessScript: htlcSweepScript, Output: &wire.TxOut{ @@ -4844,8 +4848,9 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) { // nil. var commitResolution *CommitOutputResolution if len(delayScript) != 0 { - singleTweak := SingleTweakBytes(commitPoint, - lc.localChanCfg.DelayBasePoint) + singleTweak := SingleTweakBytes( + commitPoint, lc.localChanCfg.DelayBasePoint.PubKey, + ) localBalance := localCommitment.LocalBalance commitResolution = &CommitOutputResolution{ SelfOutPoint: wire.OutPoint{ @@ -4853,7 +4858,7 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) { Index: delayIndex, }, SelfOutputSignDesc: SignDescriptor{ - PubKey: lc.localChanCfg.DelayBasePoint, + KeyDesc: lc.localChanCfg.DelayBasePoint, SingleTweak: singleTweak, WitnessScript: selfScript, Output: &wire.TxOut{ @@ -5012,8 +5017,8 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte, // Finally, construct the witness stack minding the order of the // pubkeys+sigs on the stack. - ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() - theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() + ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() + theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() witness := SpendMultiSig(lc.signDesc.WitnessScript, ourKey, localSig, theirKey, remoteSig) closeTx.TxIn[0].Witness = witness diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 50b6f9a3..3ea70f15 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -13,6 +13,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/blockchain" @@ -168,12 +169,22 @@ func createTestChannels(revocationWindow int) (*LightningChannel, MinHTLC: 0, MaxAcceptedHtlcs: MaxHTLCNumber / 2, }, - CsvDelay: uint16(csvTimeoutAlice), - MultiSigKey: aliceKeys[0].PubKey(), - RevocationBasePoint: aliceKeys[1].PubKey(), - PaymentBasePoint: aliceKeys[2].PubKey(), - DelayBasePoint: aliceKeys[3].PubKey(), - HtlcBasePoint: aliceKeys[4].PubKey(), + CsvDelay: uint16(csvTimeoutAlice), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: aliceKeys[0].PubKey(), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[1].PubKey(), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[2].PubKey(), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[3].PubKey(), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[4].PubKey(), + }, } bobCfg := channeldb.ChannelConfig{ ChannelConstraints: channeldb.ChannelConstraints{ @@ -183,28 +194,40 @@ func createTestChannels(revocationWindow int) (*LightningChannel, MinHTLC: 0, MaxAcceptedHtlcs: MaxHTLCNumber / 2, }, - CsvDelay: uint16(csvTimeoutBob), - MultiSigKey: bobKeys[0].PubKey(), - RevocationBasePoint: bobKeys[1].PubKey(), - PaymentBasePoint: bobKeys[2].PubKey(), - DelayBasePoint: bobKeys[3].PubKey(), - HtlcBasePoint: bobKeys[4].PubKey(), + CsvDelay: uint16(csvTimeoutBob), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: bobKeys[0].PubKey(), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[1].PubKey(), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[2].PubKey(), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[3].PubKey(), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[4].PubKey(), + }, } - bobRoot := DeriveRevocationRoot( - bobKeys[0], testHdSeed, aliceKeys[0].PubKey(), - ) - bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) + bobRoot, err := chainhash.NewHash(bobKeys[0].Serialize()) + if err != nil { + return nil, nil, nil, err + } + bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, err } bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:]) - aliceRoot := DeriveRevocationRoot( - aliceKeys[0], testHdSeed, bobKeys[0].PubKey(), - ) - alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) + aliceRoot, err := chainhash.NewHash(aliceKeys[0].Serialize()) + if err != nil { + return nil, nil, nil, err + } + alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, err @@ -880,8 +903,8 @@ func TestForceClose(t *testing.T) { // The rest of the close summary should have been populated properly. aliceDelayPoint := aliceChannel.channelState.LocalChanCfg.DelayBasePoint - if !aliceCommitResolution.SelfOutputSignDesc.PubKey.IsEqual( - aliceDelayPoint, + if !aliceCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual( + aliceDelayPoint.PubKey, ) { t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc") } @@ -1036,7 +1059,7 @@ func TestForceClose(t *testing.T) { t.Fatalf("bob fails to include to-self output in ForceCloseSummary") } bobDelayPoint := bobChannel.channelState.LocalChanCfg.DelayBasePoint - if !bobCommitResolution.SelfOutputSignDesc.PubKey.IsEqual(bobDelayPoint) { + if !bobCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(bobDelayPoint.PubKey) { t.Fatalf("bob incorrect pubkey in SelfOutputSignDesc") } if bobCommitResolution.SelfOutputSignDesc.Output.Value != @@ -1156,8 +1179,8 @@ func TestForceCloseDustOutput(t *testing.T) { t.Fatalf("alice fails to include to-self output in " + "ForceCloseSummary") } - if !commitResolution.SelfOutputSignDesc.PubKey.IsEqual( - aliceChannel.channelState.LocalChanCfg.DelayBasePoint, + if !commitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual( + aliceChannel.channelState.LocalChanCfg.DelayBasePoint.PubKey, ) { t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc") } From fe12c908f830e43cd053f6b3e6d29eae2823a067 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:18:35 -0800 Subject: [PATCH 13/29] lnwallet: add an instance of keychain.SecretKeyRing to the Config struct --- lnwallet/config.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lnwallet/config.go b/lnwallet/config.go index 6dd69033..8570f5fe 100644 --- a/lnwallet/config.go +++ b/lnwallet/config.go @@ -3,6 +3,7 @@ package lnwallet import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/roasbeef/btcd/chaincfg" ) @@ -22,6 +23,12 @@ type Config struct { // counterparty's broadcasting revoked commitment states. Notifier chainntnfs.ChainNotifier + // SecretKeyRing is used by the wallet during the funding workflow + // process to obtain keys to be used directly within contracts. Usage + // of this interface ensures that all key derivation is itself fully + // deterministic. + SecretKeyRing keychain.SecretKeyRing + // WalletController is the core wallet, all non Lightning Network // specific interaction is proxied to the internal wallet. WalletController WalletController From a41f00e2d63c075d0f008b804ecf918ebfe504c0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:20:41 -0800 Subject: [PATCH 14/29] lnwallet: update funding flow to utilize keychain.KeyRing In this commit, we modify the funding flow process to obtain all keys necessary from the keychain.KeyRing interface. This ensure that all keys we generate are fully deterministic. --- lnwallet/wallet.go | 126 +++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index c394ff2d..563c6a59 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -10,6 +10,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -27,24 +28,6 @@ const ( // The size of the buffered queue of requests to the wallet from the // outside word. msgBufferSize = 100 - - // revocationRootIndex is the top level HD key index from which secrets - // used to generate producer roots should be derived from. - revocationRootIndex = hdkeychain.HardenedKeyStart + 1 - - // identityKeyIndex is the top level HD key index which is used to - // generate/rotate identity keys. - // - // TODO(roasbeef): should instead be child to make room for future - // rotations, etc. - identityKeyIndex = hdkeychain.HardenedKeyStart + 2 -) - -var ( - // Namespace bucket keys. - lightningNamespaceKey = []byte("ln-wallet") - waddrmgrNamespaceKey = []byte("waddrmgr") - wtxmgrNamespaceKey = []byte("wtxmgr") ) // ErrInsufficientFunds is a type matching the error interface which is @@ -248,6 +231,11 @@ type LightningWallet struct { // specific interaction is proxied to the internal wallet. WalletController + // SecretKeyRing is the interface we'll use to derive any keys related + // to our purpose within the network including: multi-sig keys, node + // keys, revocation keys, etc. + keychain.SecretKeyRing + // This mutex is to be held when generating external keys to be used as // multi-sig, and commitment keys within the channel. keyGenMtx sync.RWMutex @@ -297,6 +285,7 @@ func NewLightningWallet(Cfg Config) (*LightningWallet, error) { return &LightningWallet{ Cfg: Cfg, + SecretKeyRing: Cfg.SecretKeyRing, WalletController: Cfg.WalletController, msgChan: make(chan interface{}, msgBufferSize), nextFundingID: 0, @@ -319,20 +308,6 @@ func (l *LightningWallet) Startup() error { return err } - // Fetch the root derivation key from the wallet's HD chain. We'll use - // this to generate specific Lightning related secrets on the fly. - rootKey, err := l.FetchRootKey() - if err != nil { - return err - } - - // TODO(roasbeef): always re-derive on the fly? - rootKeyRaw := rootKey.Serialize() - l.rootKey, err = hdkeychain.NewMaster(rootKeyRaw, &l.Cfg.NetParams) - if err != nil { - return err - } - l.wg.Add(1) // TODO(roasbeef): multiple request handlers? go l.requestHandler() @@ -390,17 +365,6 @@ func (l *LightningWallet) ActiveReservations() []*ChannelReservation { return reservations } -// GetIdentitykey returns the identity private key of the wallet. -// TODO(roasbeef): should be moved elsewhere -func (l *LightningWallet) GetIdentitykey() (*btcec.PrivateKey, error) { - identityKey, err := l.rootKey.Child(identityKeyIndex) - if err != nil { - return nil, err - } - - return identityKey.ECPrivKey() -} - // requestHandler is the primary goroutine(s) responsible for handling, and // dispatching relies to all messages. func (l *LightningWallet) requestHandler() { @@ -534,35 +498,42 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // key, the base revocation key, the base htlc key,the base payment // key, and the delayed payment key. // - // TODO(roasbeef): special derivaiton? - reservation.ourContribution.MultiSigKey, err = l.NewRawKey() + // TODO(roasbeef): "salt" each key as well? + reservation.ourContribution.MultiSigKey, err = l.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { req.err <- err req.resp <- nil return } - reservation.ourContribution.RevocationBasePoint, err = l.NewRawKey() + reservation.ourContribution.RevocationBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyRevocationBase, + ) if err != nil { req.err <- err req.resp <- nil return } - reservation.ourContribution.HtlcBasePoint, err = l.NewRawKey() + reservation.ourContribution.HtlcBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyHtlcBase, + ) if err != nil { req.err <- err req.resp <- nil return } - // TODO(roasbeef); allow for querying to extract key distinct from HD - // chain - // * allows for offline commitment keys - reservation.ourContribution.PaymentBasePoint, err = l.NewRawKey() + reservation.ourContribution.PaymentBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyPaymentBase, + ) if err != nil { req.err <- err req.resp <- nil return } - reservation.ourContribution.DelayBasePoint, err = l.NewRawKey() + reservation.ourContribution.DelayBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyDelayBase, + ) if err != nil { req.err <- err req.resp <- nil @@ -750,8 +721,10 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Finally, add the 2-of-2 multi-sig output which will set up the lightning // channel. channelCapacity := int64(pendingReservation.partialState.Capacity) - witnessScript, multiSigOut, err := GenFundingPkScript(ourKey.SerializeCompressed(), - theirKey.SerializeCompressed(), channelCapacity) + witnessScript, multiSigOut, err := GenFundingPkScript( + ourKey.PubKey.SerializeCompressed(), + theirKey.PubKey.SerializeCompressed(), channelCapacity, + ) if err != nil { req.err <- err return @@ -850,22 +823,22 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { var stateObfuscator [StateHintSize]byte if chanState.ChanType == channeldb.SingleFunder { stateObfuscator = DeriveStateHintObfuscator( - ourContribution.PaymentBasePoint, - theirContribution.PaymentBasePoint, + ourContribution.PaymentBasePoint.PubKey, + theirContribution.PaymentBasePoint.PubKey, ) } else { - ourSer := ourContribution.PaymentBasePoint.SerializeCompressed() - theirSer := theirContribution.PaymentBasePoint.SerializeCompressed() + ourSer := ourContribution.PaymentBasePoint.PubKey.SerializeCompressed() + theirSer := theirContribution.PaymentBasePoint.PubKey.SerializeCompressed() switch bytes.Compare(ourSer, theirSer) { case -1: stateObfuscator = DeriveStateHintObfuscator( - ourContribution.PaymentBasePoint, - theirContribution.PaymentBasePoint, + ourContribution.PaymentBasePoint.PubKey, + theirContribution.PaymentBasePoint.PubKey, ) default: stateObfuscator = DeriveStateHintObfuscator( - theirContribution.PaymentBasePoint, - ourContribution.PaymentBasePoint, + theirContribution.PaymentBasePoint.PubKey, + ourContribution.PaymentBasePoint.PubKey, ) } } @@ -895,7 +868,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // transaction. signDesc = SignDescriptor{ WitnessScript: witnessScript, - PubKey: ourKey, + KeyDesc: ourKey, Output: multiSigOut, HashType: txscript.SigHashAll, SigHashes: txscript.NewTxSigHashes(theirCommitTx), @@ -1038,8 +1011,11 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Re-generate both the witnessScript and p2sh output. We sign the // witnessScript script, but include the p2sh output as the subscript // for verification. - witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), - theirKey.SerializeCompressed(), int64(res.partialState.Capacity)) + witnessScript, _, err := GenFundingPkScript( + ourKey.PubKey.SerializeCompressed(), + theirKey.PubKey.SerializeCompressed(), + int64(res.partialState.Capacity), + ) if err != nil { msg.err <- err msg.completeChan <- nil @@ -1066,7 +1042,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs msg.err <- err msg.completeChan <- nil return - } else if !sig.Verify(sigHash, theirKey) { + } else if !sig.Verify(sigHash, theirKey.PubKey) { msg.err <- fmt.Errorf("counterparty's commitment signature is invalid") msg.completeChan <- nil return @@ -1168,8 +1144,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // generator state obfuscator to encode the current state number within // both commitment transactions. stateObfuscator := DeriveStateHintObfuscator( - pendingReservation.theirContribution.PaymentBasePoint, - pendingReservation.ourContribution.PaymentBasePoint) + pendingReservation.theirContribution.PaymentBasePoint.PubKey, + pendingReservation.ourContribution.PaymentBasePoint.PubKey, + ) err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator) if err != nil { req.err <- err @@ -1194,8 +1171,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { hashCache := txscript.NewTxSigHashes(ourCommitTx) theirKey := pendingReservation.theirContribution.MultiSigKey ourKey := pendingReservation.ourContribution.MultiSigKey - witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), - theirKey.SerializeCompressed(), channelValue) + witnessScript, _, err := GenFundingPkScript( + ourKey.PubKey.SerializeCompressed(), + theirKey.PubKey.SerializeCompressed(), channelValue, + ) if err != nil { req.err <- err req.completeChan <- nil @@ -1217,8 +1196,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { req.err <- err req.completeChan <- nil return - } else if !sig.Verify(sigHash, theirKey) { - req.err <- fmt.Errorf("counterparty's commitment signature is invalid") + } else if !sig.Verify(sigHash, theirKey.PubKey) { + req.err <- fmt.Errorf("counterparty's commitment signature " + + "is invalid") req.completeChan <- nil return } @@ -1235,7 +1215,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { } signDesc := SignDescriptor{ WitnessScript: witnessScript, - PubKey: ourKey, + KeyDesc: ourKey, Output: &wire.TxOut{ PkScript: p2wsh, Value: channelValue, From 5b063a0691583632ddc23a5a526bc6e80ba1fe30 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:24:43 -0800 Subject: [PATCH 15/29] lnwallet: modify the way we derive revocation roots to be deterministic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we modify the way we generate the secrets for revocation roots to be fully deterministic. Rather than use a special key and derive all sub-roots from that (mixing in some “salts”), we’ll use the proper keychain.KeyFamily instead. This ensures that given a static description of the channel, we’re able to re-derive our revocation root properly. --- lnwallet/script_utils.go | 28 --------------------------- lnwallet/wallet.go | 41 +++++++++++++++------------------------- 2 files changed, 15 insertions(+), 54 deletions(-) diff --git a/lnwallet/script_utils.go b/lnwallet/script_utils.go index 14266412..a7c3c4c5 100644 --- a/lnwallet/script_utils.go +++ b/lnwallet/script_utils.go @@ -7,7 +7,6 @@ import ( "fmt" "math/big" - "golang.org/x/crypto/hkdf" "golang.org/x/crypto/ripemd160" "github.com/roasbeef/btcd/btcec" @@ -1236,33 +1235,6 @@ func DeriveRevocationPrivKey(revokeBasePriv *btcec.PrivateKey, return priv } -// DeriveRevocationRoot derives an root unique to a channel given the -// derivation root, and the blockhash that the funding process began at and the -// remote node's identity public key. The seed is derived using the HKDF[1][2] -// instantiated with sha-256. With this schema, once we know the block hash of -// the funding transaction, and who we funded the channel with, we can -// reconstruct all of our revocation state. -// -// [1]: https://eprint.iacr.org/2010/264.pdf -// [2]: https://tools.ietf.org/html/rfc5869 -func DeriveRevocationRoot(derivationRoot *btcec.PrivateKey, - blockSalt chainhash.Hash, nodePubKey *btcec.PublicKey) chainhash.Hash { - - secret := derivationRoot.Serialize() - salt := blockSalt[:] - info := nodePubKey.SerializeCompressed() - - seedReader := hkdf.New(sha256.New, secret, salt, info) - - // It's safe to ignore the error her as we know for sure that we won't - // be draining the HKDF past its available entropy horizon. - // TODO(roasbeef): revisit... - var root chainhash.Hash - seedReader.Read(root[:]) - - return root -} - // SetStateNumHint encodes the current state number within the passed // commitment transaction by re-purposing the locktime and sequence fields in // the commitment transaction to encode the obfuscated state number. The state diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 563c6a59..536b62ea 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -541,30 +541,31 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg } // With the above keys created, we'll also need to initialization our - // initial revocation tree state. In order to do so in a deterministic - // manner (for recovery purposes), we'll use the current block hash - // along with the identity public key of the node we're creating the - // channel with. In the event of a recovery, given these two items and - // the initialize wallet HD seed, we can derive all of our revocation - // secrets. - masterElkremRoot, err := l.deriveMasterRevocationRoot() + // initial revocation tree state. + nextRevocationKeyDesc, err := l.DeriveNextKey( + keychain.KeyFamilyRevocationRoot, + ) if err != nil { req.err <- err req.resp <- nil return } - bestHash, _, err := l.Cfg.ChainIO.GetBestBlock() + revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc) if err != nil { req.err <- err req.resp <- nil return } - revocationRoot := DeriveRevocationRoot(masterElkremRoot, *bestHash, - req.nodeID) // Once we have the root, we can then generate our shachain producer // and from that generate the per-commitment point. - producer := shachain.NewRevocationProducer(revocationRoot) + revRoot, err := chainhash.NewHash(revocationRoot.Serialize()) + if err != nil { + req.err <- err + req.resp <- nil + return + } + producer := shachain.NewRevocationProducer(*revRoot) firstPreimage, err := producer.AtIndex(0) if err != nil { req.err <- err @@ -1329,18 +1330,6 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte, return nil } -// deriveMasterRevocationRoot derives the private key which serves as the master -// producer root. This master secret is used as the secret input to a HKDF to -// generate revocation secrets based on random, but public data. -func (l *LightningWallet) deriveMasterRevocationRoot() (*btcec.PrivateKey, error) { - masterElkremRoot, err := l.rootKey.Child(revocationRootIndex) - if err != nil { - return nil, err - } - - return masterElkremRoot.ECPrivKey() -} - // DeriveStateHintObfuscator derives the bytes to be used for obfuscating the // state hints from the root to be used for a new channel. The obfuscator is // generated via the following computation: @@ -1417,8 +1406,6 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount, weightEstimate.AddP2WKHInput() case NestedWitnessPubKey: weightEstimate.AddNestedP2WKHInput() - case PubKeyHash: - weightEstimate.AddP2PKHInput() default: return nil, 0, fmt.Errorf("Unsupported address type: %v", utxo.AddressType) @@ -1429,7 +1416,9 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount, weightEstimate.AddP2WSHOutput() // Assume that change output is a P2WKH output. - // TODO: Handle wallets that generate non-witness change addresses. + // + // TODO: Handle wallets that generate non-witness change + // addresses. weightEstimate.AddP2WKHOutput() // The difference between the selected amount and the amount From 0d1a40fb46cc05838da97588d86efab287ecbd2c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:25:55 -0800 Subject: [PATCH 16/29] lnwallet: update tests due to recent API changes --- lnwallet/common_test.go | 2 +- lnwallet/interface_test.go | 60 ++++++++++++++++++++++++----------- lnwallet/transactions_test.go | 33 +++++++++++++------ 3 files changed, 67 insertions(+), 28 deletions(-) diff --git a/lnwallet/common_test.go b/lnwallet/common_test.go index e21d89ef..424d34da 100644 --- a/lnwallet/common_test.go +++ b/lnwallet/common_test.go @@ -23,7 +23,7 @@ type mockSigner struct { } func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) { - pubkey := signDesc.PubKey + pubkey := signDesc.KeyDesc.PubKey switch { case signDesc.SingleTweak != nil: pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index ed15c8d7..76b71629 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -27,6 +27,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -230,8 +231,8 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet, // available for funding channels. func createTestWallet(tempTestDir string, miningNode *rpctest.Harness, netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier, - wc lnwallet.WalletController, signer lnwallet.Signer, - bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) { + wc lnwallet.WalletController, keyRing keychain.SecretKeyRing, + signer lnwallet.Signer, bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) { dbDir := filepath.Join(tempTestDir, "cdb") cdb, err := channeldb.Open(dbDir) @@ -242,6 +243,7 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness, cfg := lnwallet.Config{ Database: cdb, Notifier: notifier, + SecretKeyRing: keyRing, WalletController: wc, Signer: signer, ChainIO: bio, @@ -360,7 +362,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, t.Fatalf("bob's commit signatures not populated") } - // To concludes, we'll consume first Alice's signatures with Bob, and + // To conclude, we'll consume first Alice's signatures with Bob, and // then the other way around. _, err = aliceChanReservation.CompleteReservation( bobFundingSigs, bobCommitSig, @@ -597,16 +599,16 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi t.Fatalf("line #%v: csv delay not set", line) } - if c.MultiSigKey == nil { + if c.MultiSigKey.PubKey == nil { t.Fatalf("line #%v: multi-sig key not set", line) } - if c.RevocationBasePoint == nil { + if c.RevocationBasePoint.PubKey == nil { t.Fatalf("line #%v: revocation key not set", line) } - if c.PaymentBasePoint == nil { + if c.PaymentBasePoint.PubKey == nil { t.Fatalf("line #%v: payment key not set", line) } - if c.DelayBasePoint == nil { + if c.DelayBasePoint.PubKey == nil { t.Fatalf("line #%v: delay key not set", line) } @@ -617,8 +619,6 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi t.Fatalf("line #%v: max pending amt not set", line) } if c.ChanReserve == 0 { - // TODO(roasbeef): need to follow up and ensure reserve set to - // fraction t.Fatalf("line #%v: chan reserve not set", line) } if c.MinHTLC == 0 { @@ -1449,7 +1449,9 @@ func testSignOutputUsingTweaks(r *rpctest.Harness, // First, generate a new public key under the control of the wallet, // then generate a revocation key using it. - pubKey, err := alice.NewRawKey() + pubKey, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { t.Fatalf("unable to obtain public key: %v", err) } @@ -1461,10 +1463,10 @@ func testSignOutputUsingTweaks(r *rpctest.Harness, commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(), commitPreimage) - revocationKey := lnwallet.DeriveRevocationPubkey(pubKey, commitPoint) - commitTweak := lnwallet.SingleTweakBytes(commitPoint, pubKey) + revocationKey := lnwallet.DeriveRevocationPubkey(pubKey.PubKey, commitPoint) + commitTweak := lnwallet.SingleTweakBytes(commitPoint, pubKey.PubKey) - tweakedPub := lnwallet.TweakPubKey(pubKey, commitPoint) + tweakedPub := lnwallet.TweakPubKey(pubKey.PubKey, commitPoint) // As we'd like to test both single and double tweaks, we'll repeat // the same set up twice. The first will use a regular single tweak, @@ -1539,7 +1541,9 @@ func testSignOutputUsingTweaks(r *rpctest.Harness, // based on this tweak value and the key we originally // generated above. signDesc := &lnwallet.SignDescriptor{ - PubKey: baseKey, + KeyDesc: keychain.KeyDescriptor{ + PubKey: baseKey.PubKey, + }, WitnessScript: keyScript, Output: newOutput, HashType: txscript.SigHashAll, @@ -1584,6 +1588,7 @@ func testSignOutputUsingTweaks(r *rpctest.Harness, func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet, _ *lnwallet.LightningWallet, t *testing.T) { + // We first mine a few blocks to ensure any transactions still in the // mempool confirm, and then get the original balance, before a // reorganization that doesn't invalidate any existing transactions or @@ -1966,6 +1971,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, aliceSigner lnwallet.Signer bobSigner lnwallet.Signer + aliceKeyRing keychain.SecretKeyRing + bobKeyRing keychain.SecretKeyRing + aliceWalletController lnwallet.WalletController bobWalletController lnwallet.WalletController @@ -2008,14 +2016,17 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, if err != nil { t.Fatalf("unable to make chain rpc: %v", err) } + case "neutrino": feeEstimator = lnwallet.StaticFeeEstimator{FeeRate: 250} + // Set some package-level variable to speed up // operation for tests. neutrino.WaitForMoreCFHeaders = time.Millisecond * 100 neutrino.BanDuration = time.Millisecond * 100 neutrino.QueryTimeout = time.Millisecond * 500 neutrino.QueryNumRetries = 2 + // Start Alice - open a database, start a neutrino // instance, and initialize a btcwallet driver for it. aliceDB, err := walletdb.Create("bdb", @@ -2065,6 +2076,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, bobChain.Start() defer bobChain.Stop() bobClient = chain.NewNeutrinoClient(bobChain) + case "bitcoind": feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( rpcConfig, 250) @@ -2133,6 +2145,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, t.Fatalf("unable to create btcwallet: %v", err) } aliceSigner = aliceWalletController.(*btcwallet.BtcWallet) + aliceKeyRing = keychain.NewBtcWalletKeyRing( + aliceWalletController.(*btcwallet.BtcWallet).InternalWallet(), + ) bobWalletConfig := &btcwallet.Config{ PrivatePass: []byte("bob-pass"), @@ -2147,21 +2162,30 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, t.Fatalf("unable to create btcwallet: %v", err) } bobSigner = bobWalletController.(*btcwallet.BtcWallet) + bobKeyRing = keychain.NewBtcWalletKeyRing( + bobWalletController.(*btcwallet.BtcWallet).InternalWallet(), + ) bio = bobWalletController.(*btcwallet.BtcWallet) default: t.Fatalf("unknown wallet driver: %v", walletType) } // Funding via 20 outputs with 4BTC each. - alice, err := createTestWallet(tempTestDirAlice, miningNode, netParams, - chainNotifier, aliceWalletController, aliceSigner, bio) + alice, err := createTestWallet( + tempTestDirAlice, miningNode, netParams, + chainNotifier, aliceWalletController, aliceKeyRing, + aliceSigner, bio, + ) if err != nil { t.Fatalf("unable to create test ln wallet: %v", err) } defer alice.Shutdown() - bob, err := createTestWallet(tempTestDirBob, miningNode, netParams, - chainNotifier, bobWalletController, bobSigner, bio) + bob, err := createTestWallet( + tempTestDirBob, miningNode, netParams, + chainNotifier, bobWalletController, bobKeyRing, + bobSigner, bio, + ) if err != nil { t.Fatalf("unable to create test ln wallet: %v", err) } diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index ecd4bf23..47ee6d81 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" @@ -325,7 +326,7 @@ func (tc *testContext) extractFundingInput() (*Utxo, *wire.TxOut, error) { } block1Utxo := Utxo{ - AddressType: PubKeyHash, + AddressType: WitnessPubKey, Value: btcutil.Amount(txout.Value), OutPoint: wire.OutPoint{ Hash: *tx.Hash(), @@ -378,16 +379,30 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { MaxPendingAmount: lnwire.NewMSatFromSatoshis(tc.fundingAmount), MaxAcceptedHtlcs: MaxHTLCNumber, }, - CsvDelay: tc.localCsvDelay, - MultiSigKey: tc.localFundingPubKey, - PaymentBasePoint: tc.localPaymentBasePoint, - HtlcBasePoint: tc.localPaymentBasePoint, - DelayBasePoint: localDelayBasePoint, + CsvDelay: tc.localCsvDelay, + MultiSigKey: keychain.KeyDescriptor{ + PubKey: tc.localFundingPubKey, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: tc.localPaymentBasePoint, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: tc.localPaymentBasePoint, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: localDelayBasePoint, + }, }, RemoteChanCfg: channeldb.ChannelConfig{ - MultiSigKey: tc.remoteFundingPubKey, - PaymentBasePoint: tc.remotePaymentBasePoint, - HtlcBasePoint: tc.remotePaymentBasePoint, + MultiSigKey: keychain.KeyDescriptor{ + PubKey: tc.remoteFundingPubKey, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: tc.remotePaymentBasePoint, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: tc.remotePaymentBasePoint, + }, }, Capacity: tc.fundingAmount, RevocationProducer: shachain.NewRevocationProducer(zeroHash), From 4b20e805fe8e8b2c02f7af37bf5363f0cd83db11 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:29:01 -0800 Subject: [PATCH 17/29] multi: update packages due to recent SignDescriptor and WalletController changes --- breacharbiter_test.go | 61 ++++++++++++++++++------- contractcourt/chain_watcher.go | 8 ++-- fundingmanager.go | 81 ++++++++++++++++++++++------------ fundingmanager_test.go | 15 +++++-- htlcswitch/mock.go | 2 +- htlcswitch/test_utils.go | 63 ++++++++++++++++++-------- mock.go | 38 +++++++++++----- test_utils.go | 59 ++++++++++++++++++------- utxonursery_test.go | 2 +- 9 files changed, 229 insertions(+), 100 deletions(-) diff --git a/breacharbiter_test.go b/breacharbiter_test.go index 0fd093ee..6ce445db 100644 --- a/breacharbiter_test.go +++ b/breacharbiter_test.go @@ -20,6 +20,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -405,7 +406,7 @@ func initBreachedOutputs() error { return fmt.Errorf("unable to parse pubkey: %v", breachKeys[i]) } - bo.signDesc.PubKey = pubkey + bo.signDesc.KeyDesc.PubKey = pubkey } return nil @@ -1272,12 +1273,22 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa MinHTLC: 0, MaxAcceptedHtlcs: uint16(rand.Int31()), }, - CsvDelay: uint16(csvTimeoutAlice), - MultiSigKey: aliceKeyPub, - RevocationBasePoint: aliceKeyPub, - PaymentBasePoint: aliceKeyPub, - DelayBasePoint: aliceKeyPub, - HtlcBasePoint: aliceKeyPub, + CsvDelay: uint16(csvTimeoutAlice), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, } bobCfg := channeldb.ChannelConfig{ ChannelConstraints: channeldb.ChannelConstraints{ @@ -1287,24 +1298,40 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa MinHTLC: 0, MaxAcceptedHtlcs: uint16(rand.Int31()), }, - CsvDelay: uint16(csvTimeoutBob), - MultiSigKey: bobKeyPub, - RevocationBasePoint: bobKeyPub, - PaymentBasePoint: bobKeyPub, - DelayBasePoint: bobKeyPub, - HtlcBasePoint: bobKeyPub, + CsvDelay: uint16(csvTimeoutBob), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, } - bobRoot := lnwallet.DeriveRevocationRoot(bobKeyPriv, testHdSeed, aliceKeyPub) - bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) + bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, err + } + bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, err } bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:]) - aliceRoot := lnwallet.DeriveRevocationRoot(aliceKeyPriv, testHdSeed, bobKeyPub) - alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) + aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, err + } + alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, err diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 067a265b..efdeddb7 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -133,13 +133,13 @@ func newChainWatcher(chanState *channeldb.OpenChannel, var stateHint [lnwallet.StateHintSize]byte if chanState.IsInitiator { stateHint = lnwallet.DeriveStateHintObfuscator( - chanState.LocalChanCfg.PaymentBasePoint, - chanState.RemoteChanCfg.PaymentBasePoint, + chanState.LocalChanCfg.PaymentBasePoint.PubKey, + chanState.RemoteChanCfg.PaymentBasePoint.PubKey, ) } else { stateHint = lnwallet.DeriveStateHintObfuscator( - chanState.RemoteChanCfg.PaymentBasePoint, - chanState.LocalChanCfg.PaymentBasePoint, + chanState.RemoteChanCfg.PaymentBasePoint.PubKey, + chanState.LocalChanCfg.PaymentBasePoint.PubKey, ) } diff --git a/fundingmanager.go b/fundingmanager.go index 4614c18b..8762da98 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -940,12 +941,22 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { MinHTLC: msg.HtlcMinimum, MaxAcceptedHtlcs: maxHtlcs, }, - CsvDelay: remoteCsvDelay, - MultiSigKey: copyPubKey(msg.FundingKey), - RevocationBasePoint: copyPubKey(msg.RevocationPoint), - PaymentBasePoint: copyPubKey(msg.PaymentPoint), - DelayBasePoint: copyPubKey(msg.DelayedPaymentPoint), - HtlcBasePoint: copyPubKey(msg.HtlcPoint), + CsvDelay: remoteCsvDelay, + MultiSigKey: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.FundingKey), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.RevocationPoint), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.PaymentPoint), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.DelayedPaymentPoint), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.HtlcPoint), + }, }, } err = reservation.ProcessSingleContribution(remoteContribution) @@ -974,11 +985,11 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { HtlcMinimum: ourContribution.MinHTLC, CsvDelay: uint16(remoteCsvDelay), MaxAcceptedHTLCs: maxHtlcs, - FundingKey: ourContribution.MultiSigKey, - RevocationPoint: ourContribution.RevocationBasePoint, - PaymentPoint: ourContribution.PaymentBasePoint, - DelayedPaymentPoint: ourContribution.DelayBasePoint, - HtlcPoint: ourContribution.HtlcBasePoint, + FundingKey: ourContribution.MultiSigKey.PubKey, + RevocationPoint: ourContribution.RevocationBasePoint.PubKey, + PaymentPoint: ourContribution.PaymentBasePoint.PubKey, + DelayedPaymentPoint: ourContribution.DelayBasePoint.PubKey, + HtlcPoint: ourContribution.HtlcBasePoint.PubKey, FirstCommitmentPoint: ourContribution.FirstCommitmentPoint, } err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, &fundingAccept) @@ -1057,11 +1068,21 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) { MinHTLC: msg.HtlcMinimum, MaxAcceptedHtlcs: maxHtlcs, }, - MultiSigKey: copyPubKey(msg.FundingKey), - RevocationBasePoint: copyPubKey(msg.RevocationPoint), - PaymentBasePoint: copyPubKey(msg.PaymentPoint), - DelayBasePoint: copyPubKey(msg.DelayedPaymentPoint), - HtlcBasePoint: copyPubKey(msg.HtlcPoint), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.FundingKey), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.RevocationPoint), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.PaymentPoint), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.DelayedPaymentPoint), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: copyPubKey(msg.HtlcPoint), + }, }, } remoteContribution.CsvDelay = f.cfg.RequiredRemoteDelay(resCtx.chanAmt) @@ -1818,10 +1839,11 @@ func (f *fundingManager) addToRouterGraph(completeChan *channeldb.OpenChannel, // will be the one that's carrying the HTLC towards us. remoteMinHTLC := completeChan.RemoteChanCfg.MinHTLC - ann, err := f.newChanAnnouncement(f.cfg.IDKey, completeChan.IdentityPub, - completeChan.LocalChanCfg.MultiSigKey, - completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID, - remoteMinHTLC, + ann, err := f.newChanAnnouncement( + f.cfg.IDKey, completeChan.IdentityPub, + completeChan.LocalChanCfg.MultiSigKey.PubKey, + completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, + chanID, remoteMinHTLC, ) if err != nil { return fmt.Errorf("error generating channel "+ @@ -1927,10 +1949,11 @@ func (f *fundingManager) annAfterSixConfs(completeChan *channeldb.OpenChannel, // Create and broadcast the proofs required to make this channel // public and usable for other nodes for routing. - err = f.announceChannel(f.cfg.IDKey, completeChan.IdentityPub, - completeChan.LocalChanCfg.MultiSigKey, - completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID, - remoteMinHTLC, + err = f.announceChannel( + f.cfg.IDKey, completeChan.IdentityPub, + completeChan.LocalChanCfg.MultiSigKey.PubKey, + completeChan.RemoteChanCfg.MultiSigKey.PubKey, + *shortChanID, chanID, remoteMinHTLC, ) if err != nil { return fmt.Errorf("channel announcement failed: %v", err) @@ -2440,11 +2463,11 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { FeePerKiloWeight: uint32(commitFeePerKw), CsvDelay: uint16(remoteCsvDelay), MaxAcceptedHTLCs: maxHtlcs, - FundingKey: ourContribution.MultiSigKey, - RevocationPoint: ourContribution.RevocationBasePoint, - PaymentPoint: ourContribution.PaymentBasePoint, - HtlcPoint: ourContribution.HtlcBasePoint, - DelayedPaymentPoint: ourContribution.DelayBasePoint, + FundingKey: ourContribution.MultiSigKey.PubKey, + RevocationPoint: ourContribution.RevocationBasePoint.PubKey, + PaymentPoint: ourContribution.PaymentBasePoint.PubKey, + HtlcPoint: ourContribution.HtlcBasePoint.PubKey, + DelayedPaymentPoint: ourContribution.DelayBasePoint.PubKey, FirstCommitmentPoint: ourContribution.FirstCommitmentPoint, ChannelFlags: channelFlags, } diff --git a/fundingmanager_test.go b/fundingmanager_test.go index dc575074..451287ca 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -150,12 +151,14 @@ func init() { func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController, - signer lnwallet.Signer, bio lnwallet.BlockChainIO, + signer lnwallet.Signer, keyRing keychain.SecretKeyRing, + bio lnwallet.BlockChainIO, estimator lnwallet.FeeEstimator) (*lnwallet.LightningWallet, error) { wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{ Database: cdb, Notifier: notifier, + SecretKeyRing: keyRing, WalletController: wc, Signer: signer, ChainIO: bio, @@ -212,8 +215,14 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, return nil, err } - lnw, err := createTestWallet(cdb, netParams, - chainNotifier, wc, signer, bio, estimator) + keyRing := &mockSecretKeyRing{ + rootKey: alicePrivKey, + } + + lnw, err := createTestWallet( + cdb, netParams, chainNotifier, wc, signer, keyRing, bio, + estimator, + ) if err != nil { t.Fatalf("unable to create test ln wallet: %v", err) } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 7b6e5803..d3704e95 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -557,7 +557,7 @@ func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *lnwallet.SignDescri witnessScript := signDesc.WitnessScript privKey := m.key - if !privKey.PubKey().IsEqual(signDesc.PubKey) { + if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) { return nil, fmt.Errorf("incorrect key passed") } diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 7549568d..70418fb0 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -132,34 +133,60 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, fundingTxIn := wire.NewTxIn(prevOut, nil, nil) aliceCfg := channeldb.ChannelConfig{ - ChannelConstraints: *aliceConstraints, - CsvDelay: uint16(csvTimeoutAlice), - MultiSigKey: aliceKeyPub, - RevocationBasePoint: aliceKeyPub, - PaymentBasePoint: aliceKeyPub, - DelayBasePoint: aliceKeyPub, - HtlcBasePoint: aliceKeyPub, + ChannelConstraints: *aliceConstraints, + CsvDelay: uint16(csvTimeoutAlice), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, } bobCfg := channeldb.ChannelConfig{ - ChannelConstraints: *bobConstraints, - CsvDelay: uint16(csvTimeoutBob), - MultiSigKey: bobKeyPub, - RevocationBasePoint: bobKeyPub, - PaymentBasePoint: bobKeyPub, - DelayBasePoint: bobKeyPub, - HtlcBasePoint: bobKeyPub, + ChannelConstraints: *bobConstraints, + CsvDelay: uint16(csvTimeoutBob), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, } - bobRoot := lnwallet.DeriveRevocationRoot(bobKeyPriv, hash, aliceKeyPub) - bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) + bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, nil, err + } + bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, nil, err } bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:]) - aliceRoot := lnwallet.DeriveRevocationRoot(aliceKeyPriv, hash, bobKeyPub) - alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) + aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, nil, err + } + alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, nil, err diff --git a/mock.go b/mock.go index 39a2eba2..2e47532c 100644 --- a/mock.go +++ b/mock.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg" @@ -28,7 +29,7 @@ func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, witnessScript := signDesc.WitnessScript privKey := m.key - if !privKey.PubKey().IsEqual(signDesc.PubKey) { + if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) { return nil, fmt.Errorf("incorrect key passed") } @@ -218,16 +219,6 @@ func (*mockWalletController) GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, e return nil, nil } -// NewRawKey will be called to get keys to be used for the funding tx and the -// commitment tx. -func (m *mockWalletController) NewRawKey() (*btcec.PublicKey, error) { - return m.rootKey.PubKey(), nil -} - -// FetchRootKey will be called to provide the wallet with a root key. -func (m *mockWalletController) FetchRootKey() (*btcec.PrivateKey, error) { - return m.rootKey, nil -} func (*mockWalletController) SendOutputs(outputs []*wire.TxOut, _ lnwallet.SatPerVByte) (*chainhash.Hash, error) { @@ -272,6 +263,31 @@ func (*mockWalletController) Stop() error { return nil } +type mockSecretKeyRing struct { + rootKey *btcec.PrivateKey +} + +func (m *mockSecretKeyRing) DeriveNextKey(keyFam keychain.KeyFamily) (keychain.KeyDescriptor, error) { + return keychain.KeyDescriptor{ + PubKey: m.rootKey.PubKey(), + }, nil +} + +func (m *mockSecretKeyRing) DeriveKey(keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) { + return keychain.KeyDescriptor{ + PubKey: m.rootKey.PubKey(), + }, nil +} + +func (m *mockSecretKeyRing) DerivePrivKey(keyDesc keychain.KeyDescriptor) (*btcec.PrivateKey, error) { + return m.rootKey, nil +} + +func (m *mockSecretKeyRing) ScalarMult(keyDesc keychain.KeyDescriptor, + pubKey *btcec.PublicKey) ([]byte, error) { + return nil, nil +} + type mockPreimageCache struct { sync.Mutex preimageMap map[[32]byte][]byte diff --git a/test_utils.go b/test_utils.go index 2f279c30..891afebe 100644 --- a/test_utils.go +++ b/test_utils.go @@ -11,6 +11,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -79,12 +80,22 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MaxAcceptedHtlcs: uint16(rand.Int31()), }, - CsvDelay: uint16(csvTimeoutAlice), - MultiSigKey: aliceKeyPub, - RevocationBasePoint: aliceKeyPub, - PaymentBasePoint: aliceKeyPub, - DelayBasePoint: aliceKeyPub, - HtlcBasePoint: aliceKeyPub, + CsvDelay: uint16(csvTimeoutAlice), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, } bobCfg := channeldb.ChannelConfig{ ChannelConstraints: channeldb.ChannelConstraints{ @@ -94,24 +105,40 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MaxAcceptedHtlcs: uint16(rand.Int31()), }, - CsvDelay: uint16(csvTimeoutBob), - MultiSigKey: bobKeyPub, - RevocationBasePoint: bobKeyPub, - PaymentBasePoint: bobKeyPub, - DelayBasePoint: bobKeyPub, - HtlcBasePoint: bobKeyPub, + CsvDelay: uint16(csvTimeoutBob), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, } - bobRoot := lnwallet.DeriveRevocationRoot(bobKeyPriv, testHdSeed, aliceKeyPub) - bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) + bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, nil, err + } + bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, nil, err } bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:]) - aliceRoot := lnwallet.DeriveRevocationRoot(aliceKeyPriv, testHdSeed, bobKeyPub) - alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) + aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) + if err != nil { + return nil, nil, nil, nil, err + } + alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, nil, err diff --git a/utxonursery_test.go b/utxonursery_test.go index 3727e0ce..e950aed1 100644 --- a/utxonursery_test.go +++ b/utxonursery_test.go @@ -319,7 +319,7 @@ func init() { if err != nil { panic(fmt.Sprintf("unable to parse pub key during init: %v", err)) } - signDescriptors[i].PubKey = pk + signDescriptors[i].KeyDesc.PubKey = pk } for i := range kidOutputs { From a144018e9812a971b74db4102cc5150778f05270 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:36:53 -0800 Subject: [PATCH 18/29] lnwallet: remove p2pkh as an address type, wallet is now pure segwit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we modify the mechanics of the wallet to only allow derivation of segwit-like addresses. Additionally, the ConfirmedBalance method on the WalletController now only has a single argument, as it’s assumed that the wallet is itself only concerned with segwit outputs. --- lnwallet/btcwallet/btcwallet.go | 25 +++++++------------------ lnwallet/interface.go | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index f9039ea7..9554f6f7 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -206,28 +206,17 @@ func (b *BtcWallet) Stop() error { // final sum. // // This is a part of the WalletController interface. -func (b *BtcWallet) ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, error) { +func (b *BtcWallet) ConfirmedBalance(confs int32) (btcutil.Amount, error) { var balance btcutil.Amount - if witness { - witnessOutputs, err := b.ListUnspentWitness(confs) - if err != nil { - return 0, err - } - - for _, witnessOutput := range witnessOutputs { - balance += witnessOutput.Value - } - } else { - outputSum, err := b.wallet.CalculateBalance(confs) - if err != nil { - return 0, err - } - - balance = outputSum + witnessOutputs, err := b.ListUnspentWitness(confs) + if err != nil { + return 0, err } - // TODO(roasbeef): remove witness only distinction? + for _, witnessOutput := range witnessOutputs { + balance += witnessOutput.Value + } return balance, nil } diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 58af4fd2..e5ad9bb8 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -20,19 +20,16 @@ var ErrNotMine = errors.New("the passed output doesn't belong to the wallet") type AddressType uint8 const ( - // UnknownAddressType represents an output with an unknown or non-standard - // script. - UnknownAddressType AddressType = iota - // WitnessPubKey represents a p2wkh address. - WitnessPubKey + WitnessPubKey AddressType = iota // NestedWitnessPubKey represents a p2sh output which is itself a // nested p2wkh output. NestedWitnessPubKey - // PubKeyHash represents a regular p2pkh output. - PubKeyHash + // UnknownAddressType represents an output with an unknown or non-standard + // script. + UnknownAddressType ) // ErrDoubleSpend is returned from PublishTransaction in case the @@ -127,14 +124,18 @@ type WalletController interface { // that have at least confs confirmations. If confs is set to zero, // then all unspent outputs, including those currently in the mempool // will be included in the final sum. - ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, error) + // + // NOTE: Only witness outputs should be included in the computation of + // the total spendable balance of the wallet. We require this as only + // witness inputs can be used for funding channels. + ConfirmedBalance(confs int32) (btcutil.Amount, error) // NewAddress returns the next external or internal address for the // wallet dictated by the value of the `change` parameter. If change is // true, then an internal address should be used, otherwise an external // address should be returned. The type of address returned is dictated - // by the wallet's capabilities, and may be of type: p2sh, p2pkh, - // p2wkh, p2wsh, etc. + // by the wallet's capabilities, and may be of type: p2sh, p2wkh, + // p2wsh, etc. NewAddress(addrType AddressType, change bool) (btcutil.Address, error) // GetPrivKey retrieves the underlying private key associated with the From 525cca02e2bc8bec0bdeddc548fa15cf9965dd29 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:37:32 -0800 Subject: [PATCH 19/29] lnrpc: remove the PUBKEY_HASH adds type, WalletBalance is segwit by default --- lnrpc/rpc.pb.go | 683 ++++++++++++++++++++--------------------- lnrpc/rpc.pb.gw.go | 8 - lnrpc/rpc.proto | 6 +- lnrpc/rpc.swagger.json | 12 +- 4 files changed, 336 insertions(+), 373 deletions(-) diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index 8970c9f0..057b216c 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -134,18 +134,15 @@ type NewAddressRequest_AddressType int32 const ( NewAddressRequest_WITNESS_PUBKEY_HASH NewAddressRequest_AddressType = 0 NewAddressRequest_NESTED_PUBKEY_HASH NewAddressRequest_AddressType = 1 - NewAddressRequest_PUBKEY_HASH NewAddressRequest_AddressType = 2 ) var NewAddressRequest_AddressType_name = map[int32]string{ 0: "WITNESS_PUBKEY_HASH", 1: "NESTED_PUBKEY_HASH", - 2: "PUBKEY_HASH", } var NewAddressRequest_AddressType_value = map[string]int32{ "WITNESS_PUBKEY_HASH": 0, "NESTED_PUBKEY_HASH": 1, - "PUBKEY_HASH": 2, } func (x NewAddressRequest_AddressType) String() string { @@ -2300,8 +2297,6 @@ func (m *PendingChannelsResponse_ForceClosedChannel) GetPendingHtlcs() []*Pendin } type WalletBalanceRequest struct { - // / If only witness outputs should be considered when calculating the wallet's balance - WitnessOnly bool `protobuf:"varint,1,opt,name=witness_only,json=witnessOnly" json:"witness_only,omitempty"` } func (m *WalletBalanceRequest) Reset() { *m = WalletBalanceRequest{} } @@ -2309,13 +2304,6 @@ func (m *WalletBalanceRequest) String() string { return proto.Compact func (*WalletBalanceRequest) ProtoMessage() {} func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} } -func (m *WalletBalanceRequest) GetWitnessOnly() bool { - if m != nil { - return m.WitnessOnly - } - return false -} - type WalletBalanceResponse struct { // / The balance of the wallet TotalBalance int64 `protobuf:"varint,1,opt,name=total_balance" json:"total_balance,omitempty"` @@ -4294,8 +4282,7 @@ type LightningClient interface { // * lncli: `walletbalance` // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all // confirmed unspent outputs and all unconfirmed unspent outputs under control - // by the wallet. This method can be modified by having the request specify - // only witness outputs should be factored into the final output sum. + // of the wallet. WalletBalance(ctx context.Context, in *WalletBalanceRequest, opts ...grpc.CallOption) (*WalletBalanceResponse, error) // * lncli: `channelbalance` // ChannelBalance returns the total funds available across all open channels @@ -5002,8 +4989,7 @@ type LightningServer interface { // * lncli: `walletbalance` // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all // confirmed unspent outputs and all unconfirmed unspent outputs under control - // by the wallet. This method can be modified by having the request specify - // only witness outputs should be factored into the final output sum. + // of the wallet. WalletBalance(context.Context, *WalletBalanceRequest) (*WalletBalanceResponse, error) // * lncli: `channelbalance` // ChannelBalance returns the total funds available across all open channels @@ -6113,337 +6099,336 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 5312 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0x4b, 0x90, 0x1c, 0xc9, - 0x59, 0x56, 0xf5, 0xf4, 0x3c, 0xfa, 0xef, 0x9e, 0x57, 0xce, 0x68, 0xd4, 0xea, 0x95, 0x65, 0x6d, - 0xb1, 0xb1, 0x12, 0x62, 0xd1, 0x68, 0xc7, 0xf6, 0xb2, 0x5e, 0x81, 0x1d, 0x7a, 0xcf, 0xda, 0xb3, - 0xf2, 0xb8, 0x46, 0xf2, 0x82, 0x17, 0x68, 0xd7, 0x74, 0xe7, 0xf4, 0x94, 0xd5, 0x5d, 0x55, 0x5b, - 0x95, 0x3d, 0xa3, 0xde, 0x45, 0x11, 0x3c, 0x22, 0x38, 0x41, 0xf8, 0x00, 0x11, 0x84, 0x21, 0xcc, - 0xc1, 0xbe, 0xc0, 0x81, 0x23, 0x07, 0xc2, 0x04, 0xdc, 0x1d, 0x41, 0x70, 0xf0, 0x89, 0xe0, 0x06, - 0x9c, 0xcc, 0x99, 0x0b, 0x27, 0xe2, 0xff, 0xf3, 0x51, 0x99, 0x55, 0x35, 0x92, 0x6c, 0x03, 0xb7, - 0xce, 0x2f, 0x33, 0xff, 0x7c, 0xfd, 0xf9, 0xbf, 0xf2, 0xaf, 0x86, 0x56, 0x96, 0x0e, 0x6e, 0xa4, - 0x59, 0x22, 0x12, 0x36, 0x3f, 0x8e, 0xb3, 0x74, 0xd0, 0xbb, 0x34, 0x4a, 0x92, 0xd1, 0x98, 0x6f, - 0x87, 0x69, 0xb4, 0x1d, 0xc6, 0x71, 0x22, 0x42, 0x11, 0x25, 0x71, 0x2e, 0x1b, 0xf9, 0xdf, 0x82, - 0x95, 0x87, 0x3c, 0x3e, 0xe0, 0x7c, 0x18, 0xf0, 0x8f, 0xa7, 0x3c, 0x17, 0xec, 0x97, 0x60, 0x3d, - 0xe4, 0x9f, 0x70, 0x3e, 0xec, 0xa7, 0x61, 0x9e, 0xa7, 0xc7, 0x59, 0x98, 0xf3, 0xae, 0x77, 0xc5, - 0xbb, 0xd6, 0x09, 0xd6, 0x64, 0xc5, 0xbe, 0xc1, 0xd9, 0xeb, 0xd0, 0xc9, 0xb1, 0x29, 0x8f, 0x45, - 0x96, 0xa4, 0xb3, 0x6e, 0x83, 0xda, 0xb5, 0x11, 0xbb, 0x2f, 0x21, 0x7f, 0x0c, 0xab, 0x66, 0x84, - 0x3c, 0x4d, 0xe2, 0x9c, 0xb3, 0x9b, 0xb0, 0x39, 0x88, 0xd2, 0x63, 0x9e, 0xf5, 0xa9, 0xf3, 0x24, - 0xe6, 0x93, 0x24, 0x8e, 0x06, 0x5d, 0xef, 0xca, 0xdc, 0xb5, 0x56, 0xc0, 0x64, 0x1d, 0xf6, 0xf8, - 0x40, 0xd5, 0xb0, 0xab, 0xb0, 0xca, 0x63, 0x89, 0xf3, 0x21, 0xf5, 0x52, 0x43, 0xad, 0x14, 0x30, - 0x76, 0xf0, 0xff, 0xc2, 0x83, 0xf5, 0xf7, 0xe3, 0x48, 0x7c, 0x18, 0x8e, 0xc7, 0x5c, 0xe8, 0x35, - 0x5d, 0x85, 0xd5, 0x53, 0x02, 0x68, 0x4d, 0xa7, 0x49, 0x36, 0x54, 0x2b, 0x5a, 0x91, 0xf0, 0xbe, - 0x42, 0xcf, 0x9c, 0x59, 0xe3, 0xcc, 0x99, 0xd5, 0x6e, 0xd7, 0x5c, 0xfd, 0x76, 0xf9, 0x9b, 0xc0, - 0xec, 0xc9, 0xc9, 0xed, 0xf0, 0xbf, 0x04, 0x1b, 0x4f, 0xe2, 0x71, 0x32, 0x78, 0xfa, 0xb3, 0x4d, - 0xda, 0xdf, 0x82, 0x4d, 0xb7, 0xbf, 0xa2, 0xfb, 0xdd, 0x06, 0xb4, 0x1f, 0x67, 0x61, 0x9c, 0x87, - 0x03, 0x3c, 0x72, 0xd6, 0x85, 0x45, 0xf1, 0xac, 0x7f, 0x1c, 0xe6, 0xc7, 0x44, 0xa8, 0x15, 0xe8, - 0x22, 0xdb, 0x82, 0x85, 0x70, 0x92, 0x4c, 0x63, 0x41, 0xbb, 0x3a, 0x17, 0xa8, 0x12, 0x7b, 0x0b, - 0xd6, 0xe3, 0xe9, 0xa4, 0x3f, 0x48, 0xe2, 0xa3, 0x28, 0x9b, 0x48, 0xc6, 0xa1, 0xc5, 0xcd, 0x07, - 0xd5, 0x0a, 0x76, 0x19, 0xe0, 0x10, 0xa7, 0x21, 0x87, 0x68, 0xd2, 0x10, 0x16, 0xc2, 0x7c, 0xe8, - 0xa8, 0x12, 0x8f, 0x46, 0xc7, 0xa2, 0x3b, 0x4f, 0x84, 0x1c, 0x0c, 0x69, 0x88, 0x68, 0xc2, 0xfb, - 0xb9, 0x08, 0x27, 0x69, 0x77, 0x81, 0x66, 0x63, 0x21, 0x54, 0x9f, 0x88, 0x70, 0xdc, 0x3f, 0xe2, - 0x3c, 0xef, 0x2e, 0xaa, 0x7a, 0x83, 0xb0, 0x37, 0x61, 0x65, 0xc8, 0x73, 0xd1, 0x0f, 0x87, 0xc3, - 0x8c, 0xe7, 0x39, 0xcf, 0xbb, 0x4b, 0x74, 0x74, 0x25, 0xd4, 0xef, 0xc2, 0xd6, 0x43, 0x2e, 0xac, - 0xdd, 0xc9, 0xd5, 0xb6, 0xfb, 0x7b, 0xc0, 0x2c, 0xf8, 0x1e, 0x17, 0x61, 0x34, 0xce, 0xd9, 0x3b, - 0xd0, 0x11, 0x56, 0x63, 0x62, 0xd5, 0xf6, 0x0e, 0xbb, 0x41, 0x77, 0xec, 0x86, 0xd5, 0x21, 0x70, - 0xda, 0xf9, 0xff, 0xed, 0x41, 0xfb, 0x80, 0xc7, 0xe6, 0x76, 0x31, 0x68, 0xe2, 0x4c, 0xd4, 0x49, - 0xd2, 0x6f, 0xf6, 0x59, 0x68, 0xd3, 0xec, 0x72, 0x91, 0x45, 0xf1, 0x88, 0x8e, 0xa0, 0x15, 0x00, - 0x42, 0x07, 0x84, 0xb0, 0x35, 0x98, 0x0b, 0x27, 0x82, 0x36, 0x7e, 0x2e, 0xc0, 0x9f, 0x78, 0xef, - 0xd2, 0x70, 0x36, 0xe1, 0xb1, 0x28, 0x36, 0xbb, 0x13, 0xb4, 0x15, 0xb6, 0x8b, 0xbb, 0x7d, 0x03, - 0x36, 0xec, 0x26, 0x9a, 0xfa, 0x3c, 0x51, 0x5f, 0xb7, 0x5a, 0xaa, 0x41, 0xae, 0xc2, 0xaa, 0x6e, - 0x9f, 0xc9, 0xc9, 0xd2, 0xf6, 0xb7, 0x82, 0x15, 0x05, 0xeb, 0x25, 0x5c, 0x83, 0xb5, 0xa3, 0x28, - 0x0e, 0xc7, 0xfd, 0xc1, 0x58, 0x9c, 0xf4, 0x87, 0x7c, 0x2c, 0x42, 0x3a, 0x88, 0xf9, 0x60, 0x85, - 0xf0, 0xbb, 0x63, 0x71, 0x72, 0x0f, 0x51, 0xff, 0x4f, 0x3d, 0xe8, 0xc8, 0xc5, 0xab, 0x8b, 0xff, - 0x06, 0x2c, 0xeb, 0x31, 0x78, 0x96, 0x25, 0x99, 0xe2, 0x43, 0x17, 0x64, 0xd7, 0x61, 0x4d, 0x03, - 0x69, 0xc6, 0xa3, 0x49, 0x38, 0xe2, 0xea, 0xb6, 0x57, 0x70, 0xb6, 0x53, 0x50, 0xcc, 0x92, 0xa9, - 0x90, 0x57, 0xaf, 0xbd, 0xd3, 0x51, 0x07, 0x13, 0x20, 0x16, 0xb8, 0x4d, 0xfc, 0xef, 0x7b, 0xd0, - 0xb9, 0x7b, 0x1c, 0xc6, 0x31, 0x1f, 0xef, 0x27, 0x51, 0x2c, 0xd8, 0x4d, 0x60, 0x47, 0xd3, 0x78, - 0x18, 0xc5, 0xa3, 0xbe, 0x78, 0x16, 0x0d, 0xfb, 0x87, 0x33, 0xc1, 0x73, 0x79, 0x44, 0xbb, 0xe7, - 0x82, 0x9a, 0x3a, 0xf6, 0x16, 0xac, 0x39, 0x68, 0x2e, 0x32, 0x79, 0x6e, 0xbb, 0xe7, 0x82, 0x4a, - 0x0d, 0x32, 0x7e, 0x32, 0x15, 0xe9, 0x54, 0xf4, 0xa3, 0x78, 0xc8, 0x9f, 0xd1, 0x1c, 0x97, 0x03, - 0x07, 0xbb, 0xb3, 0x02, 0x1d, 0xbb, 0x9f, 0xff, 0x25, 0x58, 0xdb, 0xc3, 0x1b, 0x11, 0x47, 0xf1, - 0xe8, 0xb6, 0x64, 0x5b, 0xbc, 0xa6, 0xe9, 0xf4, 0xf0, 0x29, 0x9f, 0xa9, 0x7d, 0x53, 0x25, 0x64, - 0xaa, 0xe3, 0x24, 0x17, 0x8a, 0x73, 0xe8, 0xb7, 0xff, 0xef, 0x1e, 0xac, 0xe2, 0xde, 0x7f, 0x10, - 0xc6, 0x33, 0x7d, 0x72, 0x7b, 0xd0, 0x41, 0x52, 0x8f, 0x93, 0xdb, 0xf2, 0xb2, 0x4b, 0x26, 0xbe, - 0xa6, 0xf6, 0xaa, 0xd4, 0xfa, 0x86, 0xdd, 0x14, 0x85, 0xf9, 0x2c, 0x70, 0x7a, 0x23, 0xdb, 0x8a, - 0x30, 0x1b, 0x71, 0x41, 0x62, 0x40, 0x89, 0x05, 0x90, 0xd0, 0xdd, 0x24, 0x3e, 0x62, 0x57, 0xa0, - 0x93, 0x87, 0xa2, 0x9f, 0xf2, 0x8c, 0x76, 0x8d, 0x58, 0x6f, 0x2e, 0x80, 0x3c, 0x14, 0xfb, 0x3c, - 0xbb, 0x33, 0x13, 0xbc, 0xf7, 0x65, 0x58, 0xaf, 0x8c, 0x82, 0xdc, 0x5e, 0x2c, 0x11, 0x7f, 0xb2, - 0x4d, 0x98, 0x3f, 0x09, 0xc7, 0x53, 0xae, 0xa4, 0x93, 0x2c, 0xbc, 0xd7, 0x78, 0xd7, 0xf3, 0xdf, - 0x84, 0xb5, 0x62, 0xda, 0x8a, 0xc9, 0x18, 0x34, 0x71, 0x07, 0x15, 0x01, 0xfa, 0xed, 0xff, 0x9e, - 0x27, 0x1b, 0xde, 0x4d, 0x22, 0x73, 0xd3, 0xb1, 0x21, 0x0a, 0x04, 0xdd, 0x10, 0x7f, 0x9f, 0x29, - 0x09, 0x7f, 0xfe, 0xc5, 0xfa, 0x57, 0x61, 0xdd, 0x9a, 0xc2, 0x0b, 0x26, 0xfb, 0x97, 0x1e, 0xac, - 0x3f, 0xe2, 0xa7, 0xea, 0xd4, 0xf5, 0x6c, 0xdf, 0x85, 0xa6, 0x98, 0xa5, 0x52, 0x15, 0xaf, 0xec, - 0xbc, 0xa1, 0x0e, 0xad, 0xd2, 0xee, 0x86, 0x2a, 0x3e, 0x9e, 0xa5, 0x3c, 0xa0, 0x1e, 0xfe, 0xd7, - 0xa0, 0x6d, 0x81, 0xec, 0x02, 0x6c, 0x7c, 0xf8, 0xfe, 0xe3, 0x47, 0xf7, 0x0f, 0x0e, 0xfa, 0xfb, - 0x4f, 0xee, 0x7c, 0xf5, 0xfe, 0x6f, 0xf4, 0x77, 0x6f, 0x1f, 0xec, 0xae, 0x9d, 0x63, 0x5b, 0xc0, - 0x1e, 0xdd, 0x3f, 0x78, 0x7c, 0xff, 0x9e, 0x83, 0x7b, 0x6c, 0x15, 0xda, 0x36, 0xd0, 0xf0, 0x7b, - 0xd0, 0x7d, 0xc4, 0x4f, 0x3f, 0x8c, 0x44, 0xcc, 0xf3, 0xdc, 0x1d, 0xde, 0xbf, 0x01, 0xcc, 0x9e, - 0x93, 0x5a, 0x66, 0x17, 0x16, 0x95, 0xec, 0xd5, 0xaa, 0x47, 0x15, 0xfd, 0x37, 0x81, 0x1d, 0x44, - 0xa3, 0xf8, 0x03, 0x9e, 0xe7, 0xe1, 0x88, 0xeb, 0xc5, 0xae, 0xc1, 0xdc, 0x24, 0x1f, 0x29, 0x29, - 0x89, 0x3f, 0xfd, 0xcf, 0xc1, 0x86, 0xd3, 0x4e, 0x11, 0xbe, 0x04, 0xad, 0x3c, 0x1a, 0xc5, 0xa1, - 0x98, 0x66, 0x5c, 0x91, 0x2e, 0x00, 0xff, 0x01, 0x6c, 0x7e, 0x83, 0x67, 0xd1, 0xd1, 0xec, 0x65, - 0xe4, 0x5d, 0x3a, 0x8d, 0x32, 0x9d, 0xfb, 0x70, 0xbe, 0x44, 0x47, 0x0d, 0x2f, 0x39, 0x53, 0x9d, - 0xdf, 0x52, 0x20, 0x0b, 0xd6, 0x3d, 0x6d, 0xd8, 0xf7, 0xd4, 0x7f, 0x02, 0xec, 0x6e, 0x12, 0xc7, - 0x7c, 0x20, 0xf6, 0x39, 0xcf, 0x0a, 0x83, 0xab, 0x60, 0xc3, 0xf6, 0xce, 0x05, 0x75, 0xb0, 0xe5, - 0xcb, 0xaf, 0xf8, 0x93, 0x41, 0x33, 0xe5, 0xd9, 0x84, 0x08, 0x2f, 0x05, 0xf4, 0xdb, 0x3f, 0x0f, - 0x1b, 0x0e, 0x59, 0xa5, 0xfe, 0xdf, 0x86, 0xf3, 0xf7, 0xa2, 0x7c, 0x50, 0x1d, 0xb0, 0x0b, 0x8b, - 0xe9, 0xf4, 0xb0, 0x5f, 0x5c, 0x32, 0x5d, 0x44, 0xad, 0x58, 0xee, 0xa2, 0x88, 0xfd, 0xa1, 0x07, - 0xcd, 0xdd, 0xc7, 0x7b, 0x77, 0x59, 0x0f, 0x96, 0xa2, 0x78, 0x90, 0x4c, 0x50, 0x97, 0xc8, 0x45, - 0x9b, 0xf2, 0x99, 0x97, 0xe7, 0x12, 0xb4, 0x48, 0x05, 0xa1, 0xa2, 0x57, 0xb6, 0x51, 0x01, 0xa0, - 0x91, 0xc1, 0x9f, 0xa5, 0x51, 0x46, 0x56, 0x84, 0xb6, 0x0d, 0x9a, 0x24, 0x22, 0xab, 0x15, 0xfe, - 0x4f, 0x9a, 0xb0, 0x7c, 0x7b, 0x20, 0xa2, 0x13, 0xae, 0x44, 0x38, 0x8d, 0x4a, 0x80, 0x9a, 0x8f, - 0x2a, 0xa1, 0xb2, 0xc9, 0xf8, 0x24, 0x11, 0xbc, 0xef, 0x1c, 0x86, 0x0b, 0x62, 0xab, 0x81, 0x24, - 0xd4, 0x4f, 0x51, 0x19, 0xd0, 0xfc, 0x5a, 0x81, 0x0b, 0xe2, 0x96, 0x21, 0xd0, 0x8f, 0x86, 0x34, - 0xb3, 0x66, 0xa0, 0x8b, 0xb8, 0x1f, 0x83, 0x30, 0x0d, 0x07, 0x91, 0x98, 0xa9, 0x3b, 0x6f, 0xca, - 0x48, 0x7b, 0x9c, 0x0c, 0xc2, 0x71, 0xff, 0x30, 0x1c, 0x87, 0xf1, 0x80, 0x2b, 0x7b, 0xc6, 0x05, - 0xd1, 0x64, 0x51, 0x53, 0xd2, 0xcd, 0xa4, 0x59, 0x53, 0x42, 0xd1, 0xf4, 0x19, 0x24, 0x93, 0x49, - 0x24, 0xd0, 0xd2, 0xe9, 0x2e, 0x49, 0xf9, 0x52, 0x20, 0xb4, 0x12, 0x59, 0x3a, 0x95, 0x7b, 0xd8, - 0x92, 0xa3, 0x39, 0x20, 0x52, 0x39, 0xe2, 0x9c, 0xe4, 0xd4, 0xd3, 0xd3, 0x2e, 0x48, 0x2a, 0x05, - 0x82, 0xa7, 0x31, 0x8d, 0x73, 0x2e, 0xc4, 0x98, 0x0f, 0xcd, 0x84, 0xda, 0xd4, 0xac, 0x5a, 0xc1, - 0x6e, 0xc2, 0x86, 0x34, 0xbe, 0xf2, 0x50, 0x24, 0xf9, 0x71, 0x94, 0xf7, 0x73, 0x1e, 0x8b, 0x6e, - 0x87, 0xda, 0xd7, 0x55, 0xb1, 0x77, 0xe1, 0x42, 0x09, 0xce, 0xf8, 0x80, 0x47, 0x27, 0x7c, 0xd8, - 0x5d, 0xa6, 0x5e, 0x67, 0x55, 0xb3, 0x2b, 0xd0, 0x46, 0x9b, 0x73, 0x9a, 0x0e, 0x43, 0x54, 0xcf, - 0x2b, 0x74, 0x0e, 0x36, 0xc4, 0xde, 0x86, 0xe5, 0x94, 0x4b, 0x1d, 0x7a, 0x2c, 0xc6, 0x83, 0xbc, - 0xbb, 0x4a, 0x0a, 0xae, 0xad, 0xae, 0x14, 0xf2, 0x6f, 0xe0, 0xb6, 0x40, 0xd6, 0x1c, 0xe4, 0x64, - 0xc5, 0x84, 0xb3, 0xee, 0x1a, 0x31, 0x5d, 0x01, 0xe0, 0xcd, 0xda, 0x8b, 0x72, 0xa1, 0x38, 0xcd, - 0xc8, 0xb8, 0x5d, 0xd8, 0x74, 0x61, 0xe3, 0xd7, 0x2c, 0x29, 0xb6, 0xc9, 0xbb, 0x6d, 0x1a, 0x7a, - 0x53, 0x0d, 0xed, 0x70, 0x6c, 0x60, 0x5a, 0xf9, 0x3f, 0xf1, 0xa0, 0x89, 0xf7, 0xec, 0xec, 0x3b, - 0x69, 0x8b, 0xce, 0x39, 0x47, 0x74, 0x92, 0xbd, 0x8d, 0xd6, 0x88, 0xdc, 0x73, 0xc9, 0x97, 0x16, - 0x52, 0xd4, 0x67, 0x7c, 0x70, 0x42, 0xcc, 0x69, 0xea, 0x11, 0x41, 0xd6, 0x45, 0x95, 0x45, 0xbd, - 0x25, 0x67, 0x9a, 0xb2, 0xae, 0xa3, 0x9e, 0x8b, 0x45, 0x1d, 0xf5, 0xeb, 0xc2, 0x62, 0x14, 0x1f, - 0x26, 0xd3, 0x78, 0x48, 0x5c, 0xb8, 0x14, 0xe8, 0x22, 0xee, 0x66, 0x4a, 0x16, 0x4c, 0x34, 0xe1, - 0x8a, 0xfd, 0x0a, 0xc0, 0x67, 0x68, 0xd2, 0xe4, 0x24, 0x57, 0xcc, 0x56, 0xbe, 0x03, 0xeb, 0x16, - 0xa6, 0xf6, 0xf1, 0x75, 0x98, 0x4f, 0x11, 0x50, 0x06, 0x8a, 0x3e, 0x3f, 0x12, 0x48, 0xb2, 0xc6, - 0x5f, 0x43, 0xbf, 0x55, 0xbc, 0x1f, 0x1f, 0x25, 0x9a, 0xd2, 0x3f, 0xce, 0xa1, 0xa3, 0xa9, 0x20, - 0x45, 0xe8, 0x1a, 0xac, 0x46, 0x43, 0x1e, 0x8b, 0x48, 0xcc, 0xfa, 0x8e, 0xe5, 0x54, 0x86, 0x51, - 0x90, 0x87, 0xe3, 0x28, 0xcc, 0x95, 0x90, 0x90, 0x05, 0xb6, 0x03, 0x9b, 0xc8, 0x5f, 0x9a, 0x65, - 0xcc, 0xe1, 0x4a, 0x03, 0xae, 0xb6, 0x0e, 0xaf, 0x04, 0xe2, 0x52, 0x08, 0x15, 0x5d, 0xa4, 0x40, - 0xab, 0xab, 0xc2, 0x5d, 0x93, 0x94, 0x70, 0xc9, 0xf3, 0x92, 0x07, 0x0d, 0x50, 0xf1, 0x9a, 0x16, - 0xa4, 0xf1, 0x58, 0xf6, 0x9a, 0x2c, 0xcf, 0x6b, 0xa9, 0xe2, 0x79, 0x5d, 0x83, 0xd5, 0x7c, 0x16, - 0x0f, 0xf8, 0xb0, 0x2f, 0x12, 0x1c, 0x37, 0x8a, 0xe9, 0x74, 0x96, 0x82, 0x32, 0x4c, 0x3e, 0x22, - 0xcf, 0x45, 0xcc, 0x05, 0xc9, 0x86, 0xa5, 0x40, 0x17, 0x51, 0xcc, 0x52, 0x13, 0xc9, 0xda, 0xad, - 0x40, 0x95, 0x50, 0x23, 0x4d, 0xb3, 0x28, 0xef, 0x76, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xfe, - 0x10, 0x3d, 0x9a, 0x63, 0x1e, 0x0e, 0x79, 0x46, 0xa7, 0x2f, 0x1d, 0x3a, 0x79, 0xc5, 0xeb, 0x2b, - 0xfd, 0x4f, 0x48, 0x3d, 0x1a, 0x87, 0xf2, 0x09, 0xdd, 0x6a, 0xf6, 0x1a, 0xb4, 0xe4, 0x4a, 0xf2, - 0xe3, 0x50, 0x69, 0xec, 0x25, 0x02, 0x0e, 0x8e, 0x43, 0xf4, 0x83, 0x9c, 0xcd, 0x69, 0x90, 0x5d, - 0xd6, 0x26, 0x6c, 0x57, 0xee, 0xcd, 0x1b, 0xb0, 0xa2, 0x5d, 0xd5, 0xbc, 0x3f, 0xe6, 0x47, 0x42, - 0x9b, 0xdf, 0xf1, 0x74, 0x82, 0xc3, 0xe5, 0x7b, 0xfc, 0x48, 0xf8, 0x8f, 0x60, 0x5d, 0xdd, 0xce, - 0xaf, 0xa5, 0x5c, 0x0f, 0xfd, 0xc5, 0xb2, 0x6e, 0x90, 0x2a, 0x7a, 0x43, 0xf1, 0xa3, 0xed, 0x43, - 0x94, 0x14, 0x86, 0x1f, 0x00, 0x53, 0xd5, 0x77, 0xc7, 0x49, 0xce, 0x15, 0x41, 0x1f, 0x3a, 0x83, - 0x71, 0x92, 0x6b, 0x23, 0x5f, 0x2d, 0xc7, 0xc1, 0xf0, 0x04, 0xf2, 0xe9, 0x60, 0x80, 0xf7, 0x5d, - 0x2a, 0x79, 0x5d, 0xf4, 0xff, 0xca, 0x83, 0x0d, 0xa2, 0xa6, 0xe5, 0x88, 0xb1, 0x0c, 0x5f, 0x7d, - 0x9a, 0x9d, 0x81, 0xed, 0xf8, 0x6c, 0xc2, 0xfc, 0x51, 0x92, 0x0d, 0xb8, 0x1a, 0x49, 0x16, 0x7e, - 0x7a, 0x5b, 0xb7, 0x59, 0xb1, 0x75, 0xff, 0xc5, 0x83, 0x75, 0x9a, 0xea, 0x81, 0x08, 0xc5, 0x34, - 0x57, 0xcb, 0xff, 0x55, 0x58, 0xc6, 0xa5, 0x72, 0x7d, 0x69, 0xd4, 0x44, 0x37, 0xcd, 0xfd, 0x26, - 0x54, 0x36, 0xde, 0x3d, 0x17, 0xb8, 0x8d, 0xd9, 0x97, 0xa1, 0x63, 0xc7, 0x1b, 0x68, 0xce, 0xed, - 0x9d, 0x8b, 0x7a, 0x95, 0x15, 0xce, 0xd9, 0x3d, 0x17, 0x38, 0x1d, 0xd8, 0x2d, 0x00, 0xd2, 0xda, - 0x44, 0x56, 0x39, 0x8a, 0x17, 0xdd, 0x4d, 0xb2, 0x0e, 0x6b, 0xf7, 0x5c, 0x60, 0x35, 0xbf, 0xb3, - 0x04, 0x0b, 0x52, 0xcd, 0xf8, 0x0f, 0x61, 0xd9, 0x99, 0xa9, 0x63, 0xc3, 0x77, 0xa4, 0x0d, 0x5f, - 0x71, 0xf9, 0x1a, 0x55, 0x97, 0xcf, 0xff, 0xbb, 0x06, 0x30, 0xe4, 0xb6, 0xd2, 0x71, 0xa2, 0x9e, - 0x4b, 0x86, 0x8e, 0xd5, 0xd2, 0x09, 0x6c, 0x88, 0xdd, 0x00, 0x66, 0x15, 0xb5, 0x67, 0x2f, 0xb5, - 0x43, 0x4d, 0x0d, 0x8a, 0x31, 0x69, 0x72, 0x68, 0x0f, 0x53, 0x59, 0x69, 0xf2, 0xdc, 0x6a, 0xeb, - 0x50, 0x01, 0xa4, 0xd3, 0xfc, 0x18, 0xf5, 0xb0, 0xb6, 0x6b, 0x74, 0xb9, 0xcc, 0x20, 0x0b, 0x2f, - 0x65, 0x90, 0xc5, 0x32, 0x83, 0x90, 0xbe, 0xcb, 0xa2, 0x93, 0x50, 0x70, 0xad, 0x43, 0x54, 0x11, - 0xcd, 0x98, 0x49, 0x14, 0x93, 0x7a, 0xee, 0x4f, 0x70, 0x74, 0x65, 0xc6, 0x38, 0xa0, 0xff, 0x63, - 0x0f, 0xd6, 0x70, 0xef, 0x1c, 0xfe, 0x7a, 0x0f, 0x88, 0xbd, 0x5f, 0x91, 0xbd, 0x9c, 0xb6, 0x3f, - 0x3f, 0x77, 0xbd, 0x0b, 0x2d, 0x22, 0x98, 0xa4, 0x3c, 0x56, 0xcc, 0xd5, 0x75, 0x99, 0xab, 0x90, - 0x2c, 0xbb, 0xe7, 0x82, 0xa2, 0xb1, 0xc5, 0x5a, 0xff, 0xec, 0x41, 0x5b, 0x4d, 0xf3, 0x67, 0x36, - 0xb6, 0x7b, 0xb0, 0x84, 0x5c, 0x66, 0xd9, 0xb2, 0xa6, 0x8c, 0x7a, 0x60, 0x82, 0x1e, 0x0d, 0x2a, - 0x3e, 0xc7, 0xd0, 0x2e, 0xc3, 0xa8, 0xc5, 0x48, 0x88, 0xe6, 0x7d, 0x11, 0x8d, 0xfb, 0xba, 0x56, - 0x85, 0xec, 0xea, 0xaa, 0x50, 0x96, 0xe4, 0x22, 0x1c, 0x71, 0xa5, 0xa0, 0x64, 0x01, 0x3d, 0x0a, - 0xb5, 0xa0, 0xb2, 0x11, 0xf5, 0x23, 0x80, 0x0b, 0x95, 0x2a, 0x63, 0x48, 0x29, 0xdb, 0x71, 0x1c, - 0x4d, 0x0e, 0x13, 0x63, 0x86, 0x7a, 0xb6, 0x59, 0xe9, 0x54, 0xb1, 0x11, 0x9c, 0xd7, 0x9a, 0x18, - 0xf7, 0xb4, 0xd0, 0xbb, 0x0d, 0x32, 0x21, 0xde, 0x76, 0x79, 0xa0, 0x3c, 0xa0, 0xc6, 0xed, 0xdb, - 0x58, 0x4f, 0x8f, 0x1d, 0x43, 0xd7, 0xa8, 0x7c, 0x25, 0xb6, 0x2d, 0xb3, 0x00, 0xc7, 0x7a, 0xeb, - 0x25, 0x63, 0x91, 0x8c, 0x19, 0xea, 0x61, 0xce, 0xa4, 0xc6, 0x66, 0x70, 0x59, 0xd7, 0x91, 0x5c, - 0xae, 0x8e, 0xd7, 0x7c, 0xa5, 0xb5, 0x3d, 0xc0, 0xce, 0xee, 0xa0, 0x2f, 0x21, 0xdc, 0xfb, 0x91, - 0x07, 0x2b, 0x2e, 0x39, 0x64, 0x1d, 0xe5, 0x8f, 0x68, 0x01, 0xa3, 0x4d, 0xa9, 0x12, 0x5c, 0xf5, - 0xa8, 0x1a, 0x75, 0x1e, 0x95, 0xed, 0x37, 0xcd, 0xbd, 0xcc, 0x6f, 0x6a, 0xbe, 0x9a, 0xdf, 0x34, - 0x5f, 0xe7, 0x37, 0xf5, 0xfe, 0xcb, 0x03, 0x56, 0x3d, 0x5f, 0xf6, 0x50, 0xba, 0x74, 0x31, 0x1f, - 0x2b, 0x39, 0xf1, 0xcb, 0xaf, 0xc6, 0x23, 0x7a, 0x0f, 0x75, 0x6f, 0x64, 0x56, 0x5b, 0x10, 0xd8, - 0xa6, 0xc8, 0x72, 0x50, 0x57, 0x55, 0xf2, 0xe4, 0x9a, 0x2f, 0xf7, 0xe4, 0xe6, 0x5f, 0xee, 0xc9, - 0x2d, 0x94, 0x3d, 0xb9, 0xde, 0xef, 0xc0, 0xb2, 0x73, 0xea, 0xff, 0x7b, 0x2b, 0x2e, 0x9b, 0x31, - 0xf2, 0x80, 0x1d, 0xac, 0xf7, 0x9f, 0x0d, 0x60, 0x55, 0xce, 0xfb, 0x7f, 0x9d, 0x03, 0xf1, 0x91, - 0x23, 0x40, 0xe6, 0x14, 0x1f, 0x39, 0xa2, 0xe3, 0xff, 0x52, 0x28, 0xbe, 0x05, 0xeb, 0x19, 0x1f, - 0x24, 0x27, 0xf4, 0x6c, 0xe5, 0x46, 0x01, 0xaa, 0x15, 0x68, 0xc8, 0xb9, 0xfe, 0xeb, 0x92, 0xf3, - 0xca, 0x60, 0x69, 0x86, 0x92, 0x1b, 0xeb, 0x7f, 0x11, 0x36, 0xe5, 0xe3, 0xcf, 0x1d, 0x49, 0x4a, - 0xdb, 0x12, 0xaf, 0x43, 0xe7, 0x54, 0x86, 0xe9, 0xfa, 0x49, 0x3c, 0x9e, 0x29, 0x25, 0xd2, 0x56, - 0xd8, 0xd7, 0xe2, 0xf1, 0xcc, 0xff, 0x9e, 0x07, 0xe7, 0x4b, 0x7d, 0x8b, 0x68, 0xbd, 0x14, 0xb5, - 0xae, 0xfc, 0x75, 0x41, 0x5c, 0xa2, 0xe2, 0x71, 0x6b, 0x89, 0x52, 0x25, 0x55, 0x2b, 0x70, 0x0b, - 0xa7, 0x71, 0xb5, 0xbd, 0x3c, 0x98, 0xba, 0x2a, 0xff, 0x02, 0x9c, 0x57, 0x87, 0xef, 0xae, 0xcd, - 0xdf, 0x81, 0xad, 0x72, 0x45, 0x11, 0x6d, 0x74, 0xa7, 0xac, 0x8b, 0xfe, 0x6f, 0x03, 0xfb, 0xfa, - 0x94, 0x67, 0x33, 0x7a, 0x17, 0x30, 0xa1, 0xd5, 0x0b, 0x65, 0xe7, 0x7b, 0x21, 0x9d, 0x1e, 0x7e, - 0x95, 0xcf, 0xf4, 0xc3, 0x4b, 0xa3, 0x78, 0x78, 0xf9, 0x0c, 0x00, 0x7a, 0x13, 0xf4, 0x90, 0xa0, - 0x9f, 0xc2, 0xd0, 0x59, 0x93, 0x04, 0xfd, 0x5b, 0xb0, 0xe1, 0xd0, 0x37, 0x3b, 0xb9, 0xa0, 0x7a, - 0x48, 0x8f, 0xd6, 0x7d, 0x9e, 0x50, 0x75, 0xfe, 0x9f, 0x79, 0x30, 0xb7, 0x9b, 0xa4, 0x76, 0xb0, - 0xc9, 0x73, 0x83, 0x4d, 0x4a, 0xb4, 0xf6, 0x8d, 0xe4, 0x6c, 0x28, 0xc1, 0x60, 0x83, 0x28, 0x18, - 0xc3, 0x89, 0x40, 0x9f, 0xee, 0x28, 0xc9, 0x4e, 0xc3, 0x6c, 0xa8, 0xb6, 0xb7, 0x84, 0xe2, 0xea, - 0x0a, 0xf9, 0x83, 0x3f, 0xd1, 0xa6, 0xa0, 0x88, 0xdb, 0x4c, 0xb9, 0xa1, 0xaa, 0xe4, 0x7f, 0xc7, - 0x83, 0x79, 0x9a, 0x2b, 0x5e, 0x16, 0x79, 0xfc, 0xf4, 0x26, 0x47, 0x01, 0x3d, 0x4f, 0x5e, 0x96, - 0x12, 0x5c, 0x7a, 0xa9, 0x6b, 0x54, 0x5e, 0xea, 0x2e, 0x41, 0x4b, 0x96, 0x8a, 0xa7, 0xad, 0x02, - 0x60, 0x97, 0xa1, 0x79, 0x9c, 0xa4, 0x5a, 0xc5, 0x81, 0x8e, 0xe0, 0x24, 0x69, 0x40, 0xb8, 0x7f, - 0x1d, 0x56, 0x1f, 0x25, 0x43, 0x6e, 0x05, 0x00, 0xce, 0x3c, 0x45, 0xff, 0x77, 0x3d, 0x58, 0xd2, - 0x8d, 0xd9, 0x35, 0x68, 0xa2, 0xa6, 0x2a, 0xd9, 0x86, 0x26, 0xda, 0x8a, 0xed, 0x02, 0x6a, 0x81, - 0x12, 0x86, 0x1c, 0xc7, 0xc2, 0x92, 0xd0, 0x6e, 0x63, 0xa1, 0xa3, 0xdf, 0x84, 0x15, 0x39, 0xe7, - 0x92, 0x2e, 0x2b, 0xa1, 0xfe, 0x5f, 0x7b, 0xb0, 0xec, 0x8c, 0x81, 0x56, 0xfe, 0x38, 0xcc, 0x85, - 0x8a, 0x5d, 0xa9, 0x4d, 0xb4, 0x21, 0x3b, 0x24, 0xd4, 0x70, 0x43, 0x42, 0x26, 0x58, 0x31, 0x67, - 0x07, 0x2b, 0x6e, 0x42, 0xab, 0x78, 0xf5, 0x6c, 0x3a, 0x92, 0x03, 0x47, 0xd4, 0x71, 0xe4, 0xa2, - 0x11, 0xd2, 0x19, 0x24, 0xe3, 0x24, 0x53, 0x8f, 0x82, 0xb2, 0xe0, 0xdf, 0x82, 0xb6, 0xd5, 0x1e, - 0xa7, 0x11, 0x73, 0x71, 0x9a, 0x64, 0x4f, 0x75, 0x64, 0x4a, 0x15, 0xcd, 0xfb, 0x49, 0xa3, 0x78, - 0x3f, 0xf1, 0xff, 0xc6, 0x83, 0x65, 0xe4, 0x94, 0x28, 0x1e, 0xed, 0x27, 0xe3, 0x68, 0x30, 0x23, - 0x8e, 0xd1, 0x4c, 0xa1, 0x5e, 0x0b, 0x35, 0xc7, 0xb8, 0x30, 0x9a, 0x04, 0xda, 0xc8, 0x57, 0xfc, - 0x62, 0xca, 0xc8, 0xf9, 0xa8, 0xda, 0x0e, 0xc3, 0x9c, 0x4b, 0xaf, 0x40, 0x89, 0x72, 0x07, 0x44, - 0xe9, 0x82, 0x40, 0x16, 0x0a, 0xde, 0x9f, 0x44, 0xe3, 0x71, 0x24, 0xdb, 0x4a, 0x0e, 0xaf, 0xab, - 0xf2, 0x7f, 0xd8, 0x80, 0xb6, 0x92, 0x22, 0xf7, 0x87, 0x23, 0x19, 0x64, 0x55, 0x76, 0x8a, 0xb9, - 0x7e, 0x16, 0xa2, 0xeb, 0x1d, 0xcb, 0xc6, 0x42, 0xca, 0xc7, 0x3a, 0x57, 0x3d, 0xd6, 0x4b, 0xd0, - 0x42, 0xf6, 0x7a, 0x9b, 0x4c, 0x28, 0xf9, 0x48, 0x5e, 0x00, 0xba, 0x76, 0x87, 0x6a, 0xe7, 0x8b, - 0x5a, 0x02, 0x1c, 0xa3, 0x69, 0xa1, 0x64, 0x34, 0xbd, 0x0b, 0x1d, 0x45, 0x86, 0xf6, 0x9d, 0x7c, - 0xae, 0x82, 0xc1, 0x9d, 0x33, 0x09, 0x9c, 0x96, 0xba, 0xe7, 0x8e, 0xee, 0xb9, 0xf4, 0xb2, 0x9e, - 0xba, 0x25, 0xbd, 0x3c, 0xc8, 0xbd, 0x79, 0x98, 0x85, 0xe9, 0xb1, 0x96, 0xcc, 0x43, 0xf3, 0xbe, - 0x4a, 0x30, 0xbb, 0x0e, 0xf3, 0xd8, 0x4d, 0x4b, 0xbf, 0xfa, 0x4b, 0x27, 0x9b, 0xb0, 0x6b, 0x30, - 0xcf, 0x87, 0x23, 0xae, 0x0d, 0x77, 0xe6, 0xba, 0x50, 0x78, 0x46, 0x81, 0x6c, 0x80, 0x22, 0x00, - 0xd1, 0x92, 0x08, 0x70, 0x25, 0xe7, 0x02, 0x16, 0xdf, 0x1f, 0xfa, 0x9b, 0xc0, 0x1e, 0x49, 0xae, - 0xb5, 0x43, 0x86, 0x7f, 0x30, 0x07, 0x6d, 0x0b, 0xc6, 0xdb, 0x3c, 0xc2, 0x09, 0xf7, 0x87, 0x51, - 0x38, 0xe1, 0x82, 0x67, 0x8a, 0x53, 0x4b, 0x28, 0x09, 0xd8, 0x93, 0x51, 0x3f, 0x99, 0x8a, 0xfe, - 0x90, 0x8f, 0x32, 0x2e, 0xf5, 0x9d, 0x17, 0x94, 0x50, 0x6c, 0x37, 0x09, 0x9f, 0xd9, 0xed, 0x24, - 0x3f, 0x94, 0x50, 0x1d, 0x00, 0x94, 0x7b, 0xd4, 0x2c, 0x02, 0x80, 0x72, 0x47, 0xca, 0x72, 0x68, - 0xbe, 0x46, 0x0e, 0xbd, 0x03, 0x5b, 0x52, 0xe2, 0xa8, 0xbb, 0xd9, 0x2f, 0xb1, 0xc9, 0x19, 0xb5, - 0xec, 0x3a, 0xac, 0xe1, 0x9c, 0x35, 0x83, 0xe7, 0xd1, 0x27, 0xd2, 0x59, 0xf7, 0x82, 0x0a, 0x8e, - 0x6d, 0xf1, 0x3a, 0x3a, 0x6d, 0xe5, 0x2b, 0x44, 0x05, 0xa7, 0xb6, 0xe1, 0x33, 0xb7, 0x6d, 0x4b, - 0xb5, 0x2d, 0xe1, 0xfe, 0x32, 0xb4, 0x0f, 0x44, 0x92, 0xea, 0x43, 0x59, 0x81, 0x8e, 0x2c, 0xaa, - 0x97, 0xa7, 0xd7, 0xe0, 0x22, 0x71, 0xd1, 0xe3, 0x24, 0x4d, 0xc6, 0xc9, 0x68, 0x76, 0x30, 0x3d, - 0xcc, 0x07, 0x59, 0x94, 0xa2, 0x41, 0xed, 0xff, 0x93, 0x07, 0x1b, 0x4e, 0xad, 0x8a, 0x04, 0x7c, - 0x5e, 0xb2, 0xb4, 0x79, 0x2c, 0x90, 0x8c, 0xb7, 0x6e, 0x89, 0x43, 0xd9, 0x50, 0xc6, 0x55, 0x9e, - 0xa8, 0xf7, 0x83, 0xdb, 0xb0, 0xaa, 0x67, 0xa6, 0x3b, 0x4a, 0x2e, 0xec, 0x56, 0xb9, 0x50, 0xf5, - 0x5f, 0x51, 0x1d, 0x34, 0x89, 0x5f, 0x93, 0x66, 0x29, 0x1f, 0xd2, 0x1a, 0xb5, 0x4b, 0xd8, 0xd3, - 0xfd, 0x6d, 0x5b, 0x58, 0xcf, 0x60, 0x60, 0xc0, 0xdc, 0xff, 0x23, 0x0f, 0xa0, 0x98, 0x1d, 0x32, - 0x46, 0x21, 0xd2, 0x65, 0x76, 0x94, 0x25, 0xbe, 0x5f, 0x87, 0x8e, 0x09, 0x63, 0x17, 0x5a, 0xa2, - 0xad, 0x31, 0x34, 0x60, 0xae, 0xc2, 0xea, 0x68, 0x9c, 0x1c, 0x92, 0xce, 0xa5, 0xa7, 0xcc, 0x5c, - 0xbd, 0xbf, 0xad, 0x48, 0xf8, 0x81, 0x42, 0x0b, 0x95, 0xd2, 0xb4, 0x54, 0x8a, 0xff, 0xc7, 0x0d, - 0x13, 0x16, 0x2d, 0xd6, 0x7c, 0xe6, 0x2d, 0x63, 0x3b, 0x15, 0xe1, 0x78, 0x46, 0x14, 0x92, 0x82, - 0x1f, 0xfb, 0x2f, 0xf5, 0x03, 0x6f, 0xc1, 0x4a, 0x26, 0xa5, 0x8f, 0x16, 0x4d, 0xcd, 0x17, 0x88, - 0xa6, 0xe5, 0xcc, 0xd1, 0x3b, 0xbf, 0x08, 0x6b, 0xe1, 0xf0, 0x84, 0x67, 0x22, 0x22, 0x87, 0x80, - 0x94, 0xbe, 0x14, 0xa8, 0xab, 0x16, 0x4e, 0xba, 0xf8, 0x2a, 0xac, 0xaa, 0x37, 0x4f, 0xd3, 0x52, - 0xa5, 0xbe, 0x14, 0x30, 0x36, 0xf4, 0x7f, 0xa0, 0x23, 0xb0, 0xee, 0x19, 0x9e, 0xbd, 0x23, 0xf6, - 0xea, 0x1a, 0xa5, 0xd5, 0xfd, 0x82, 0x8a, 0x86, 0x0e, 0xb5, 0xd7, 0xa1, 0xe2, 0xd2, 0x12, 0x54, - 0xd1, 0x6b, 0x77, 0x4b, 0x9b, 0xaf, 0xb2, 0xa5, 0xfe, 0xf7, 0xe6, 0x60, 0xf1, 0xfd, 0xf8, 0x24, - 0x89, 0x06, 0x14, 0x9b, 0x9c, 0xf0, 0x49, 0xa2, 0xf3, 0x0b, 0xf0, 0x37, 0x6a, 0x74, 0x7a, 0x54, - 0x4b, 0x85, 0x0a, 0x2e, 0xea, 0x22, 0x6a, 0xb7, 0xac, 0xc8, 0xb9, 0x91, 0x9c, 0x62, 0x21, 0x68, - 0x1f, 0x66, 0x76, 0xc2, 0x91, 0x2a, 0x15, 0x09, 0x1a, 0xf3, 0x56, 0x82, 0x06, 0x45, 0xb2, 0xe5, - 0x7b, 0x21, 0x6d, 0xe7, 0x52, 0xa0, 0x8b, 0x64, 0xc7, 0x66, 0x5c, 0xfa, 0xc4, 0xa4, 0x27, 0x17, - 0x95, 0x1d, 0x6b, 0x83, 0xa8, 0x4b, 0x65, 0x07, 0xd9, 0x46, 0xca, 0x1a, 0x1b, 0x42, 0xdb, 0xa2, - 0x9c, 0xb3, 0xd4, 0x92, 0x47, 0x5c, 0x82, 0x51, 0x20, 0x0d, 0xb9, 0x91, 0x1b, 0x72, 0x0d, 0x20, - 0x73, 0x8a, 0xca, 0xb8, 0x65, 0x05, 0xcb, 0x77, 0x4f, 0x55, 0x22, 0x1b, 0x24, 0x1c, 0x8f, 0x0f, - 0xc3, 0xc1, 0x53, 0xca, 0x24, 0xa3, 0x67, 0xce, 0x56, 0xe0, 0x82, 0x38, 0x6b, 0x4a, 0x8c, 0x52, - 0x24, 0x96, 0xe5, 0x33, 0xa5, 0x05, 0xf9, 0xdf, 0x00, 0x76, 0x7b, 0x38, 0x54, 0x27, 0x64, 0x7c, - 0x84, 0x62, 0x6f, 0x3d, 0x67, 0x6f, 0x6b, 0xd6, 0xd8, 0xa8, 0x5d, 0xa3, 0x7f, 0x1f, 0xda, 0xfb, - 0x56, 0x02, 0x18, 0x1d, 0xa6, 0x4e, 0xfd, 0x52, 0x0c, 0x60, 0x21, 0xd6, 0x80, 0x0d, 0x7b, 0x40, - 0xff, 0x57, 0x80, 0xed, 0x45, 0xb9, 0x30, 0xf3, 0x33, 0x9e, 0xa4, 0x09, 0x88, 0x59, 0x9e, 0xa4, - 0xc2, 0xc8, 0x93, 0xbc, 0x2d, 0x5f, 0x4b, 0xcb, 0x0b, 0xbb, 0x0e, 0x4b, 0x91, 0x84, 0xb4, 0x1c, - 0x5e, 0x51, 0x0c, 0xac, 0x5b, 0x9a, 0x7a, 0x34, 0x28, 0x14, 0xe8, 0x88, 0xf9, 0x1f, 0x7a, 0xb0, - 0xa8, 0x96, 0x86, 0xea, 0xd0, 0x49, 0x7d, 0x93, 0x0b, 0x73, 0xb0, 0xfa, 0x84, 0xa1, 0x2a, 0xd7, - 0xcd, 0xd5, 0x71, 0x1d, 0x83, 0x66, 0x1a, 0x8a, 0x63, 0xb2, 0xa0, 0x5b, 0x01, 0xfd, 0xd6, 0x9e, - 0xd2, 0x7c, 0xe1, 0x29, 0xd5, 0xe5, 0xa8, 0x49, 0x99, 0x51, 0xc1, 0xf5, 0x2b, 0xb2, 0x5a, 0x80, - 0x09, 0x80, 0xde, 0x91, 0xaf, 0xc8, 0x05, 0x5c, 0xec, 0x97, 0x22, 0x51, 0xde, 0x2f, 0xd5, 0x34, - 0x30, 0xf5, 0x7e, 0x0f, 0xba, 0xf7, 0xf8, 0x98, 0x0b, 0x7e, 0x7b, 0x3c, 0x2e, 0xd3, 0x7f, 0x0d, - 0x2e, 0xd6, 0xd4, 0x29, 0xad, 0xfa, 0x00, 0xd6, 0xef, 0xf1, 0xc3, 0xe9, 0x68, 0x8f, 0x9f, 0x14, - 0x2f, 0x0f, 0x0c, 0x9a, 0xf9, 0x71, 0x72, 0xaa, 0xce, 0x96, 0x7e, 0xa3, 0xc3, 0x3b, 0xc6, 0x36, - 0xfd, 0x3c, 0xe5, 0x03, 0x9d, 0x19, 0x43, 0xc8, 0x41, 0xca, 0x07, 0xfe, 0x3b, 0xc0, 0x6c, 0x3a, - 0x6a, 0x09, 0x78, 0x73, 0xa7, 0x87, 0xfd, 0x7c, 0x96, 0x0b, 0x3e, 0xd1, 0x29, 0x3f, 0x36, 0xe4, - 0x5f, 0x85, 0xce, 0x7e, 0x38, 0x0b, 0xf8, 0xc7, 0x2a, 0xfb, 0x10, 0x9d, 0xb7, 0x70, 0x86, 0xac, - 0x6c, 0x9c, 0x37, 0xaa, 0xf6, 0xff, 0xa1, 0x01, 0x0b, 0xb2, 0x25, 0x52, 0x1d, 0xf2, 0x5c, 0x44, - 0xb1, 0x8c, 0xd0, 0x2b, 0xaa, 0x16, 0x54, 0xe1, 0x8d, 0x46, 0x0d, 0x6f, 0x28, 0x73, 0x4a, 0xe7, - 0x17, 0x28, 0x26, 0x70, 0x30, 0xf2, 0x4d, 0xcd, 0x9b, 0x65, 0x53, 0xf9, 0xa6, 0x1a, 0x28, 0x79, - 0xc9, 0x85, 0x7c, 0x90, 0xf3, 0xd3, 0x4c, 0xab, 0xd8, 0xc1, 0x86, 0x6a, 0xa5, 0xd0, 0xa2, 0xe4, - 0x9a, 0x8a, 0x14, 0xaa, 0x48, 0x9b, 0xa5, 0x57, 0x90, 0x36, 0xd2, 0xc6, 0x72, 0xa4, 0x0d, 0x83, - 0xb5, 0x07, 0x9c, 0x07, 0x3c, 0x4d, 0x32, 0x9d, 0xc2, 0xe9, 0x7f, 0xd7, 0x83, 0x35, 0xa5, 0x3d, - 0x4c, 0x1d, 0x7b, 0xdd, 0x51, 0x35, 0x5e, 0x5d, 0xd0, 0xf6, 0x0d, 0x58, 0x26, 0x67, 0x0b, 0x3d, - 0x29, 0xf2, 0xac, 0x54, 0xfc, 0xc1, 0x01, 0x71, 0x4e, 0x3a, 0x0c, 0x39, 0x89, 0xc6, 0x6a, 0x83, - 0x6d, 0x08, 0xd5, 0xa2, 0x76, 0xc6, 0x68, 0x7b, 0xbd, 0xc0, 0x94, 0xfd, 0xbf, 0xf7, 0x60, 0xdd, - 0x9a, 0xb0, 0xe2, 0xa8, 0x5b, 0xa0, 0x5f, 0x2e, 0x65, 0x3c, 0x41, 0x5e, 0x8c, 0x0b, 0xae, 0x26, - 0x2c, 0xba, 0x39, 0x8d, 0xe9, 0x60, 0xc2, 0x19, 0x4d, 0x30, 0x9f, 0xca, 0xdc, 0xa9, 0x66, 0x60, - 0x43, 0xc8, 0x14, 0xa7, 0x9c, 0x3f, 0x35, 0x4d, 0xe6, 0xa8, 0x89, 0x83, 0xd1, 0xc3, 0x54, 0x12, - 0x8b, 0x63, 0xd3, 0x48, 0x66, 0x5c, 0xb8, 0xa0, 0xff, 0xaf, 0x1e, 0x6c, 0x48, 0x0b, 0x44, 0xd9, - 0x77, 0x26, 0xe9, 0x6a, 0x41, 0x9a, 0x5c, 0xf2, 0x76, 0xed, 0x9e, 0x0b, 0x54, 0x99, 0x7d, 0xe1, - 0x15, 0xad, 0x26, 0xf3, 0x20, 0x79, 0xc6, 0x59, 0xcc, 0xd5, 0x9d, 0xc5, 0x0b, 0x76, 0xba, 0xce, - 0x33, 0x9f, 0xaf, 0xf5, 0xcc, 0xef, 0x2c, 0xc2, 0x7c, 0x3e, 0x48, 0x52, 0xee, 0x6f, 0xc1, 0xa6, - 0xbb, 0x38, 0x25, 0x4e, 0xbe, 0xef, 0x41, 0xf7, 0x81, 0x0c, 0x2b, 0x45, 0xf1, 0x68, 0x37, 0xca, - 0x45, 0x92, 0x99, 0xb4, 0xd3, 0xcb, 0x00, 0xb9, 0x08, 0x33, 0x21, 0xd3, 0x42, 0x94, 0x4f, 0x5d, - 0x20, 0x38, 0x47, 0x1e, 0x0f, 0x65, 0xad, 0x3c, 0x1b, 0x53, 0xc6, 0x83, 0xa1, 0xc7, 0xd2, 0x7e, - 0x72, 0x74, 0x94, 0x73, 0x63, 0x23, 0xd9, 0x18, 0xba, 0x59, 0x78, 0x7b, 0xd1, 0xb1, 0xe0, 0x27, - 0x24, 0x36, 0xa5, 0x0f, 0x55, 0x42, 0xfd, 0xbf, 0xf5, 0x60, 0xb5, 0x98, 0xe4, 0x7d, 0x04, 0xdd, - 0x9b, 0x2e, 0xa7, 0x66, 0xdd, 0x74, 0xed, 0xed, 0x47, 0xc3, 0x7e, 0x14, 0xab, 0xb9, 0x59, 0x08, - 0xdd, 0x3e, 0x55, 0x4a, 0xa6, 0x3a, 0x05, 0xc7, 0x86, 0xe4, 0x2b, 0x9d, 0xc0, 0xde, 0x32, 0xff, - 0x46, 0x95, 0x28, 0xab, 0x67, 0x22, 0xa8, 0xd7, 0x82, 0x8c, 0xf1, 0xa9, 0xa2, 0xd6, 0x35, 0x8b, - 0x84, 0xe2, 0x4f, 0xff, 0x3b, 0x1e, 0x5c, 0xac, 0xd9, 0x5c, 0x75, 0x33, 0xee, 0xc1, 0xfa, 0x91, - 0xa9, 0xd4, 0x1b, 0x20, 0xaf, 0xc7, 0x96, 0xe2, 0xa2, 0xd2, 0xa2, 0x83, 0x6a, 0x07, 0xf6, 0x16, - 0xac, 0x53, 0x90, 0x42, 0x6e, 0xa9, 0xf3, 0x68, 0x5d, 0xad, 0xd8, 0xf9, 0x41, 0x03, 0x56, 0x64, - 0xcc, 0x58, 0x7e, 0x78, 0xc0, 0x33, 0xf6, 0x01, 0x2c, 0xaa, 0xcf, 0x3c, 0xd8, 0x79, 0x35, 0xac, - 0xfb, 0x61, 0x49, 0x6f, 0xab, 0x0c, 0x2b, 0xde, 0xd9, 0xf8, 0xfd, 0x1f, 0xff, 0xc7, 0x9f, 0x34, - 0x96, 0x59, 0x7b, 0xfb, 0xe4, 0xed, 0xed, 0x11, 0x8f, 0x73, 0xa4, 0xf1, 0x9b, 0x00, 0xc5, 0x97, - 0x12, 0xac, 0x6b, 0x0c, 0x86, 0xd2, 0x97, 0x1d, 0xbd, 0x8b, 0x35, 0x35, 0x8a, 0xee, 0x45, 0xa2, - 0xbb, 0xe1, 0xaf, 0x20, 0xdd, 0x28, 0x8e, 0x84, 0xfc, 0x6c, 0xe2, 0x3d, 0xef, 0x3a, 0x1b, 0x42, - 0xc7, 0xfe, 0x62, 0x82, 0x69, 0xff, 0xac, 0xe6, 0x33, 0x8c, 0xde, 0x6b, 0xb5, 0x75, 0xda, 0x39, - 0xa5, 0x31, 0xce, 0xfb, 0x6b, 0x38, 0xc6, 0x94, 0x5a, 0x98, 0x51, 0x76, 0xfe, 0xed, 0x35, 0x68, - 0x99, 0x18, 0x07, 0xfb, 0x36, 0x2c, 0x3b, 0x61, 0x76, 0xa6, 0x09, 0xd7, 0x05, 0xee, 0x7b, 0x97, - 0xea, 0x2b, 0xd5, 0xb0, 0x97, 0x69, 0xd8, 0x2e, 0xdb, 0xc2, 0x61, 0x55, 0x6c, 0x7b, 0x9b, 0xde, - 0x1f, 0x64, 0x16, 0xcf, 0x53, 0x58, 0x71, 0x43, 0xe3, 0xec, 0x92, 0x2b, 0x50, 0x4a, 0xa3, 0x7d, - 0xe6, 0x8c, 0x5a, 0x35, 0xdc, 0x25, 0x1a, 0x6e, 0x8b, 0x6d, 0xda, 0xc3, 0x99, 0xd8, 0x03, 0xa7, - 0xbc, 0x2b, 0xfb, 0x53, 0x0a, 0xf6, 0x19, 0x73, 0xd4, 0x75, 0x9f, 0x58, 0x98, 0x43, 0xab, 0x7e, - 0x67, 0xe1, 0x77, 0x69, 0x28, 0xc6, 0x68, 0x43, 0xed, 0x2f, 0x29, 0xd8, 0x47, 0xd0, 0x32, 0xe9, - 0xd3, 0xec, 0x82, 0x95, 0xb3, 0x6e, 0xe7, 0x74, 0xf7, 0xba, 0xd5, 0x8a, 0xba, 0xa3, 0xb2, 0x29, - 0x23, 0x43, 0xec, 0xc1, 0x79, 0x65, 0x70, 0x1e, 0xf2, 0x9f, 0x66, 0x25, 0x35, 0x1f, 0x80, 0xdc, - 0xf4, 0xd8, 0x2d, 0x58, 0xd2, 0x59, 0xe9, 0x6c, 0xab, 0x3e, 0xbb, 0xbe, 0x77, 0xa1, 0x82, 0xab, - 0xfb, 0x7c, 0x1b, 0xa0, 0x48, 0xa0, 0x36, 0x9c, 0x5f, 0xc9, 0xf3, 0x36, 0x9b, 0x58, 0x93, 0x6d, - 0x3d, 0xa2, 0xfc, 0x71, 0x37, 0x3f, 0x9b, 0x7d, 0xb6, 0x68, 0x5f, 0x9b, 0xb9, 0xfd, 0x02, 0x82, - 0xfe, 0x16, 0xed, 0xdd, 0x1a, 0xa3, 0xab, 0x14, 0xf3, 0x53, 0x9d, 0x81, 0x78, 0x0f, 0xda, 0x56, - 0x52, 0x36, 0xd3, 0x14, 0xaa, 0x09, 0xdd, 0xbd, 0x5e, 0x5d, 0x95, 0x9a, 0xee, 0x57, 0x60, 0xd9, - 0xc9, 0xae, 0x36, 0x37, 0xa3, 0x2e, 0x77, 0xdb, 0xdc, 0x8c, 0xfa, 0x84, 0xec, 0x6f, 0x42, 0xdb, - 0xca, 0x85, 0x66, 0x56, 0xfe, 0x46, 0x29, 0x0b, 0xda, 0xcc, 0xa8, 0x2e, 0x75, 0x7a, 0x93, 0xd6, - 0xbb, 0xe2, 0xb7, 0x70, 0xbd, 0x94, 0x86, 0x87, 0x4c, 0xf2, 0x6d, 0x58, 0x71, 0xb3, 0xa3, 0xcd, - 0xad, 0xaa, 0xcd, 0xb3, 0x36, 0xb7, 0xea, 0x8c, 0x94, 0x6a, 0xc5, 0x90, 0xd7, 0x37, 0xcc, 0x20, - 0xdb, 0x9f, 0xaa, 0x08, 0xff, 0x73, 0xf6, 0x75, 0x14, 0x1d, 0x2a, 0x2f, 0x92, 0x15, 0x39, 0xe1, - 0x6e, 0xf6, 0xa4, 0xe1, 0xf6, 0x4a, 0x0a, 0xa5, 0xbf, 0x4e, 0xc4, 0xdb, 0xac, 0x58, 0x81, 0x94, - 0xd0, 0x94, 0x1f, 0x69, 0x49, 0x68, 0x3b, 0x85, 0xd2, 0x92, 0xd0, 0x4e, 0x1a, 0x65, 0x59, 0x42, - 0x8b, 0x08, 0x69, 0xc4, 0xb0, 0x5a, 0x7a, 0xb3, 0x35, 0x97, 0xa5, 0x3e, 0xe3, 0xa3, 0x77, 0xf9, - 0xc5, 0x4f, 0xbd, 0xae, 0x98, 0xd1, 0xe2, 0x65, 0x5b, 0x27, 0xe8, 0xfc, 0x16, 0x74, 0xec, 0xa4, - 0x5b, 0x23, 0xb3, 0x6b, 0x12, 0x74, 0x8d, 0xcc, 0xae, 0xcb, 0xd2, 0xd5, 0x87, 0xcb, 0x3a, 0xf6, - 0x30, 0xec, 0x9b, 0xb0, 0x6a, 0x65, 0x07, 0x1c, 0xcc, 0xe2, 0x81, 0x61, 0x9e, 0x6a, 0x8e, 0x56, - 0xaf, 0xce, 0x3e, 0xf3, 0x2f, 0x10, 0xe1, 0x75, 0xdf, 0x21, 0x8c, 0x8c, 0x73, 0x17, 0xda, 0x76, - 0xe6, 0xc1, 0x0b, 0xe8, 0x5e, 0xb0, 0xaa, 0xec, 0xd4, 0xa6, 0x9b, 0x1e, 0xfb, 0x73, 0x0f, 0x3a, - 0x76, 0xf6, 0x1f, 0x73, 0x82, 0x8a, 0x25, 0x3a, 0x5d, 0xbb, 0xce, 0x26, 0xe4, 0x07, 0x34, 0xc9, - 0xbd, 0xeb, 0x5f, 0x71, 0x36, 0xf9, 0x53, 0xc7, 0xce, 0xbf, 0x51, 0xfe, 0x82, 0xe9, 0x79, 0xb9, - 0x81, 0x9d, 0xc7, 0xf6, 0xfc, 0xa6, 0xc7, 0xde, 0x93, 0x5f, 0xb9, 0x69, 0x1f, 0x9d, 0x59, 0xc2, - 0xad, 0xbc, 0x65, 0xf6, 0x07, 0x61, 0xd7, 0xbc, 0x9b, 0x1e, 0xfb, 0x96, 0xfc, 0x50, 0x49, 0xf5, - 0xa5, 0x9d, 0x7f, 0xd5, 0xfe, 0xfe, 0x1b, 0xb4, 0x9a, 0xcb, 0xfe, 0x45, 0x67, 0x35, 0x65, 0xe9, - 0xbe, 0x0f, 0x50, 0x04, 0x5c, 0x58, 0x29, 0xfa, 0x60, 0xe4, 0x5e, 0x35, 0x26, 0xe3, 0x9e, 0xa8, - 0x0e, 0x52, 0x20, 0xc5, 0x8f, 0x24, 0x33, 0xaa, 0xf6, 0xb9, 0x39, 0xd2, 0x6a, 0xe0, 0xa4, 0xd7, - 0xab, 0xab, 0xaa, 0x63, 0x45, 0x4d, 0x9f, 0x3d, 0x81, 0xe5, 0xbd, 0x24, 0x79, 0x3a, 0x4d, 0x4d, - 0x10, 0xcf, 0xf5, 0xff, 0x77, 0xc3, 0xfc, 0xb8, 0x57, 0x5a, 0x85, 0x7f, 0x85, 0x48, 0xf5, 0x58, - 0xd7, 0x22, 0xb5, 0xfd, 0x69, 0x11, 0xee, 0x79, 0xce, 0x42, 0x58, 0x37, 0x3a, 0xce, 0x4c, 0xbc, - 0xe7, 0x92, 0xb1, 0xa3, 0x2e, 0x95, 0x21, 0x1c, 0xab, 0x43, 0xcf, 0x76, 0x3b, 0xd7, 0x34, 0x6f, - 0x7a, 0x6c, 0x1f, 0x3a, 0xf7, 0xf8, 0x20, 0x19, 0x72, 0xe5, 0xb1, 0x6f, 0x14, 0x13, 0x37, 0xae, - 0x7e, 0x6f, 0xd9, 0x01, 0xdd, 0x5b, 0x9f, 0x86, 0xb3, 0x8c, 0x7f, 0xbc, 0xfd, 0xa9, 0x8a, 0x05, - 0x3c, 0xd7, 0xb7, 0x5e, 0xc7, 0x2f, 0x9c, 0x5b, 0x5f, 0x0a, 0x78, 0x38, 0xb7, 0xbe, 0x12, 0xf0, - 0x70, 0xb6, 0x5a, 0xc7, 0x4f, 0xd8, 0x18, 0xd6, 0x2b, 0x31, 0x12, 0xa3, 0x29, 0xcf, 0x8a, 0xac, - 0xf4, 0xae, 0x9c, 0xdd, 0xc0, 0x1d, 0xed, 0xba, 0x3b, 0xda, 0x01, 0x2c, 0xdf, 0xe3, 0x72, 0xb3, - 0xe4, 0xc3, 0x58, 0xcf, 0x15, 0x23, 0xf6, 0x23, 0x5a, 0x59, 0xc4, 0x50, 0x9d, 0x2b, 0xd6, 0xe9, - 0x55, 0x8a, 0x7d, 0x04, 0xed, 0x87, 0x5c, 0xe8, 0x97, 0x30, 0x63, 0x6f, 0x94, 0x9e, 0xc6, 0x7a, - 0x35, 0x0f, 0x69, 0x2e, 0xcf, 0x10, 0xb5, 0x6d, 0x3e, 0x1c, 0x71, 0x79, 0xd9, 0xfb, 0xd1, 0xf0, - 0x39, 0xfb, 0x75, 0x22, 0x6e, 0x1e, 0xcf, 0xb7, 0xac, 0x07, 0x14, 0x9b, 0xf8, 0x6a, 0x09, 0xaf, - 0xa3, 0x1c, 0x27, 0x43, 0x6e, 0x29, 0xb8, 0x18, 0xda, 0x56, 0xa6, 0x84, 0xb9, 0x40, 0xd5, 0xec, - 0x0c, 0x73, 0x81, 0x6a, 0x12, 0x2b, 0xfc, 0x6b, 0x34, 0x8e, 0xcf, 0xae, 0x14, 0xe3, 0xc8, 0x64, - 0x8a, 0x62, 0xa4, 0xed, 0x4f, 0xc3, 0x89, 0x78, 0xce, 0x3e, 0xa4, 0x0f, 0x06, 0xec, 0xd7, 0xbe, - 0xc2, 0xde, 0x29, 0x3f, 0x0c, 0x9a, 0xcd, 0xb2, 0xaa, 0x5c, 0x1b, 0x48, 0x0e, 0x45, 0x7a, 0xf0, - 0x0b, 0x00, 0x07, 0x22, 0x49, 0xef, 0x85, 0x7c, 0x92, 0xc4, 0x85, 0xe4, 0x2a, 0x5e, 0xb4, 0x0a, - 0xc9, 0x65, 0x3d, 0x6b, 0xb1, 0x0f, 0x2d, 0x8b, 0xd3, 0x79, 0x2c, 0xd5, 0xcc, 0x75, 0xe6, 0xa3, - 0x97, 0xd9, 0x90, 0x9a, 0x87, 0xaf, 0x9b, 0x1e, 0xda, 0x8f, 0x45, 0x44, 0xce, 0xd8, 0x8f, 0x95, - 0x60, 0x9f, 0x11, 0x7b, 0x35, 0xe1, 0xbb, 0x7d, 0x68, 0x15, 0x61, 0x21, 0xad, 0x92, 0xca, 0x41, - 0x24, 0xa3, 0x63, 0x2a, 0xc1, 0x1a, 0x7f, 0x8d, 0xb6, 0x0a, 0xd8, 0x12, 0x6e, 0x15, 0x45, 0x60, - 0x22, 0xd8, 0x90, 0x13, 0x34, 0x0a, 0x93, 0xde, 0x68, 0xf4, 0x4a, 0x6a, 0x02, 0x26, 0xe6, 0x36, - 0xd7, 0xc6, 0x1b, 0x1c, 0xdf, 0x0e, 0xb9, 0x55, 0xbe, 0x0f, 0xa1, 0x68, 0x9e, 0xc0, 0x7a, 0xc5, - 0x59, 0x36, 0x57, 0xfa, 0xac, 0x18, 0x85, 0xb9, 0xd2, 0x67, 0xfa, 0xd9, 0xfe, 0x79, 0x1a, 0x72, - 0xd5, 0x07, 0x1c, 0x32, 0x3f, 0x8d, 0xc4, 0xe0, 0xf8, 0x3d, 0xef, 0xfa, 0xe1, 0x02, 0xfd, 0x8f, - 0xc2, 0xe7, 0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, 0x2a, 0xe2, 0xf7, 0xd2, 0x79, 0x41, 0x00, 0x00, + // 5295 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0x4b, 0x90, 0x1c, 0xc9, + 0x59, 0xbf, 0xaa, 0xa7, 0xe7, 0xd1, 0x5f, 0xf7, 0xbc, 0x72, 0x46, 0xa3, 0x56, 0xaf, 0x2c, 0x6b, + 0xeb, 0xbf, 0xb1, 0xd2, 0x5f, 0x2c, 0x1a, 0xed, 0xd8, 0x5e, 0x96, 0x15, 0xac, 0x43, 0xef, 0x59, + 0x7b, 0x56, 0x1e, 0xd7, 0x48, 0x5e, 0xf0, 0x02, 0xed, 0x9a, 0xee, 0x9c, 0x9e, 0xb2, 0xba, 0xab, + 0x6a, 0xab, 0xb2, 0x67, 0xd4, 0xbb, 0x28, 0x82, 0x47, 0x04, 0x27, 0x1c, 0x3e, 0x40, 0x04, 0x61, + 0x08, 0x5f, 0xec, 0x0b, 0x1c, 0x38, 0x72, 0x20, 0x4c, 0xc0, 0xdd, 0x11, 0x04, 0x07, 0x9f, 0x08, + 0x6e, 0xc0, 0xc9, 0x9c, 0xb9, 0x70, 0x22, 0xbe, 0x2f, 0x1f, 0x95, 0x59, 0x55, 0x23, 0xc9, 0x36, + 0x70, 0xeb, 0xfc, 0x65, 0xe6, 0x97, 0xaf, 0x2f, 0xbf, 0x57, 0x7e, 0xd5, 0xd0, 0xca, 0xd2, 0xc1, + 0x8d, 0x34, 0x4b, 0x44, 0xc2, 0xe6, 0xc7, 0x71, 0x96, 0x0e, 0x7a, 0x97, 0x46, 0x49, 0x32, 0x1a, + 0xf3, 0xed, 0x30, 0x8d, 0xb6, 0xc3, 0x38, 0x4e, 0x44, 0x28, 0xa2, 0x24, 0xce, 0x65, 0x23, 0xff, + 0x5b, 0xb0, 0xf2, 0x90, 0xc7, 0x07, 0x9c, 0x0f, 0x03, 0xfe, 0xc9, 0x94, 0xe7, 0x82, 0xfd, 0x12, + 0xac, 0x87, 0xfc, 0x53, 0xce, 0x87, 0xfd, 0x34, 0xcc, 0xf3, 0xf4, 0x38, 0x0b, 0x73, 0xde, 0xf5, + 0xae, 0x78, 0xd7, 0x3a, 0xc1, 0x9a, 0xac, 0xd8, 0x37, 0x38, 0x7b, 0x1d, 0x3a, 0x39, 0x36, 0xe5, + 0xb1, 0xc8, 0x92, 0x74, 0xd6, 0x6d, 0x50, 0xbb, 0x36, 0x62, 0xf7, 0x25, 0xe4, 0x8f, 0x61, 0xd5, + 0x8c, 0x90, 0xa7, 0x49, 0x9c, 0x73, 0x76, 0x13, 0x36, 0x07, 0x51, 0x7a, 0xcc, 0xb3, 0x3e, 0x75, + 0x9e, 0xc4, 0x7c, 0x92, 0xc4, 0xd1, 0xa0, 0xeb, 0x5d, 0x99, 0xbb, 0xd6, 0x0a, 0x98, 0xac, 0xc3, + 0x1e, 0x1f, 0xaa, 0x1a, 0x76, 0x15, 0x56, 0x79, 0x2c, 0x71, 0x3e, 0xa4, 0x5e, 0x6a, 0xa8, 0x95, + 0x02, 0xc6, 0x0e, 0xfe, 0x5f, 0x78, 0xb0, 0xfe, 0x41, 0x1c, 0x89, 0x8f, 0xc2, 0xf1, 0x98, 0x0b, + 0xbd, 0xa6, 0xab, 0xb0, 0x7a, 0x4a, 0x00, 0xad, 0xe9, 0x34, 0xc9, 0x86, 0x6a, 0x45, 0x2b, 0x12, + 0xde, 0x57, 0xe8, 0x99, 0x33, 0x6b, 0x9c, 0x39, 0xb3, 0xda, 0xed, 0x9a, 0xab, 0xdf, 0x2e, 0x7f, + 0x13, 0x98, 0x3d, 0x39, 0xb9, 0x1d, 0xfe, 0xfb, 0xb0, 0xf1, 0x24, 0x1e, 0x27, 0x83, 0xa7, 0x3f, + 0xdf, 0xa4, 0xfd, 0x2d, 0xd8, 0x74, 0xfb, 0x2b, 0xba, 0xdf, 0x6b, 0x40, 0xfb, 0x71, 0x16, 0xc6, + 0x79, 0x38, 0xc0, 0x23, 0x67, 0x5d, 0x58, 0x14, 0xcf, 0xfa, 0xc7, 0x61, 0x7e, 0x4c, 0x84, 0x5a, + 0x81, 0x2e, 0xb2, 0x2d, 0x58, 0x08, 0x27, 0xc9, 0x34, 0x16, 0xb4, 0xab, 0x73, 0x81, 0x2a, 0xb1, + 0xb7, 0x60, 0x3d, 0x9e, 0x4e, 0xfa, 0x83, 0x24, 0x3e, 0x8a, 0xb2, 0x89, 0x64, 0x1c, 0x5a, 0xdc, + 0x7c, 0x50, 0xad, 0x60, 0x97, 0x01, 0x0e, 0x71, 0x1a, 0x72, 0x88, 0x26, 0x0d, 0x61, 0x21, 0xcc, + 0x87, 0x8e, 0x2a, 0xf1, 0x68, 0x74, 0x2c, 0xba, 0xf3, 0x44, 0xc8, 0xc1, 0x90, 0x86, 0x88, 0x26, + 0xbc, 0x9f, 0x8b, 0x70, 0x92, 0x76, 0x17, 0x68, 0x36, 0x16, 0x42, 0xf5, 0x89, 0x08, 0xc7, 0xfd, + 0x23, 0xce, 0xf3, 0xee, 0xa2, 0xaa, 0x37, 0x08, 0x7b, 0x13, 0x56, 0x86, 0x3c, 0x17, 0xfd, 0x70, + 0x38, 0xcc, 0x78, 0x9e, 0xf3, 0xbc, 0xbb, 0x44, 0x47, 0x57, 0x42, 0xfd, 0x2e, 0x6c, 0x3d, 0xe4, + 0xc2, 0xda, 0x9d, 0x5c, 0x6d, 0xbb, 0xbf, 0x07, 0xcc, 0x82, 0xef, 0x71, 0x11, 0x46, 0xe3, 0x9c, + 0xbd, 0x03, 0x1d, 0x61, 0x35, 0x26, 0x56, 0x6d, 0xef, 0xb0, 0x1b, 0x74, 0xc7, 0x6e, 0x58, 0x1d, + 0x02, 0xa7, 0x9d, 0xff, 0x5f, 0x1e, 0xb4, 0x0f, 0x78, 0x6c, 0x6e, 0x17, 0x83, 0x26, 0xce, 0x44, + 0x9d, 0x24, 0xfd, 0x66, 0x9f, 0x87, 0x36, 0xcd, 0x2e, 0x17, 0x59, 0x14, 0x8f, 0xe8, 0x08, 0x5a, + 0x01, 0x20, 0x74, 0x40, 0x08, 0x5b, 0x83, 0xb9, 0x70, 0x22, 0x68, 0xe3, 0xe7, 0x02, 0xfc, 0x89, + 0xf7, 0x2e, 0x0d, 0x67, 0x13, 0x1e, 0x8b, 0x62, 0xb3, 0x3b, 0x41, 0x5b, 0x61, 0xbb, 0xb8, 0xdb, + 0x37, 0x60, 0xc3, 0x6e, 0xa2, 0xa9, 0xcf, 0x13, 0xf5, 0x75, 0xab, 0xa5, 0x1a, 0xe4, 0x2a, 0xac, + 0xea, 0xf6, 0x99, 0x9c, 0x2c, 0x6d, 0x7f, 0x2b, 0x58, 0x51, 0xb0, 0x5e, 0xc2, 0x35, 0x58, 0x3b, + 0x8a, 0xe2, 0x70, 0xdc, 0x1f, 0x8c, 0xc5, 0x49, 0x7f, 0xc8, 0xc7, 0x22, 0xa4, 0x83, 0x98, 0x0f, + 0x56, 0x08, 0xbf, 0x3b, 0x16, 0x27, 0xf7, 0x10, 0xf5, 0xff, 0xd4, 0x83, 0x8e, 0x5c, 0xbc, 0xba, + 0xf8, 0x6f, 0xc0, 0xb2, 0x1e, 0x83, 0x67, 0x59, 0x92, 0x29, 0x3e, 0x74, 0x41, 0x76, 0x1d, 0xd6, + 0x34, 0x90, 0x66, 0x3c, 0x9a, 0x84, 0x23, 0xae, 0x6e, 0x7b, 0x05, 0x67, 0x3b, 0x05, 0xc5, 0x2c, + 0x99, 0x0a, 0x79, 0xf5, 0xda, 0x3b, 0x1d, 0x75, 0x30, 0x01, 0x62, 0x81, 0xdb, 0xc4, 0xff, 0x81, + 0x07, 0x9d, 0xbb, 0xc7, 0x61, 0x1c, 0xf3, 0xf1, 0x7e, 0x12, 0xc5, 0x82, 0xdd, 0x04, 0x76, 0x34, + 0x8d, 0x87, 0x51, 0x3c, 0xea, 0x8b, 0x67, 0xd1, 0xb0, 0x7f, 0x38, 0x13, 0x3c, 0x97, 0x47, 0xb4, + 0x7b, 0x2e, 0xa8, 0xa9, 0x63, 0x6f, 0xc1, 0x9a, 0x83, 0xe6, 0x22, 0x93, 0xe7, 0xb6, 0x7b, 0x2e, + 0xa8, 0xd4, 0x20, 0xe3, 0x27, 0x53, 0x91, 0x4e, 0x45, 0x3f, 0x8a, 0x87, 0xfc, 0x19, 0xcd, 0x71, + 0x39, 0x70, 0xb0, 0x3b, 0x2b, 0xd0, 0xb1, 0xfb, 0xf9, 0xef, 0xc3, 0xda, 0x1e, 0xde, 0x88, 0x38, + 0x8a, 0x47, 0xb7, 0x25, 0xdb, 0xe2, 0x35, 0x4d, 0xa7, 0x87, 0x4f, 0xf9, 0x4c, 0xed, 0x9b, 0x2a, + 0x21, 0x53, 0x1d, 0x27, 0xb9, 0x50, 0x9c, 0x43, 0xbf, 0xfd, 0x7f, 0xf3, 0x60, 0x15, 0xf7, 0xfe, + 0xc3, 0x30, 0x9e, 0xe9, 0x93, 0xdb, 0x83, 0x0e, 0x92, 0x7a, 0x9c, 0xdc, 0x96, 0x97, 0x5d, 0x32, + 0xf1, 0x35, 0xb5, 0x57, 0xa5, 0xd6, 0x37, 0xec, 0xa6, 0x28, 0xcc, 0x67, 0x81, 0xd3, 0x1b, 0xd9, + 0x56, 0x84, 0xd9, 0x88, 0x0b, 0x12, 0x03, 0x4a, 0x2c, 0x80, 0x84, 0xee, 0x26, 0xf1, 0x11, 0xbb, + 0x02, 0x9d, 0x3c, 0x14, 0xfd, 0x94, 0x67, 0xb4, 0x6b, 0xc4, 0x7a, 0x73, 0x01, 0xe4, 0xa1, 0xd8, + 0xe7, 0xd9, 0x9d, 0x99, 0xe0, 0xbd, 0x2f, 0xc3, 0x7a, 0x65, 0x14, 0xe4, 0xf6, 0x62, 0x89, 0xf8, + 0x93, 0x6d, 0xc2, 0xfc, 0x49, 0x38, 0x9e, 0x72, 0x25, 0x9d, 0x64, 0xe1, 0xbd, 0xc6, 0xbb, 0x9e, + 0xff, 0x26, 0xac, 0x15, 0xd3, 0x56, 0x4c, 0xc6, 0xa0, 0x89, 0x3b, 0xa8, 0x08, 0xd0, 0x6f, 0xff, + 0xf7, 0x3d, 0xd9, 0xf0, 0x6e, 0x12, 0x99, 0x9b, 0x8e, 0x0d, 0x51, 0x20, 0xe8, 0x86, 0xf8, 0xfb, + 0x4c, 0x49, 0xf8, 0x8b, 0x2f, 0xd6, 0xbf, 0x0a, 0xeb, 0xd6, 0x14, 0x5e, 0x30, 0xd9, 0xef, 0x78, + 0xb0, 0xfe, 0x88, 0x9f, 0xaa, 0x53, 0xd7, 0xb3, 0x7d, 0x17, 0x9a, 0x62, 0x96, 0x4a, 0x55, 0xbc, + 0xb2, 0xf3, 0x86, 0x3a, 0xb4, 0x4a, 0xbb, 0x1b, 0xaa, 0xf8, 0x78, 0x96, 0xf2, 0x80, 0x7a, 0xf8, + 0xef, 0x43, 0xdb, 0x02, 0xd9, 0x05, 0xd8, 0xf8, 0xe8, 0x83, 0xc7, 0x8f, 0xee, 0x1f, 0x1c, 0xf4, + 0xf7, 0x9f, 0xdc, 0xf9, 0xea, 0xfd, 0xdf, 0xec, 0xef, 0xde, 0x3e, 0xd8, 0x5d, 0x3b, 0xc7, 0xb6, + 0x80, 0x3d, 0xba, 0x7f, 0xf0, 0xf8, 0xfe, 0x3d, 0x07, 0xf7, 0xfc, 0x1e, 0x74, 0x1f, 0xf1, 0xd3, + 0x8f, 0x22, 0x11, 0xf3, 0x3c, 0x77, 0x47, 0xf3, 0x6f, 0x00, 0xb3, 0xa7, 0xa0, 0x56, 0xd5, 0x85, + 0x45, 0x25, 0x6a, 0xb5, 0xa6, 0x51, 0x45, 0xff, 0x4d, 0x60, 0x07, 0xd1, 0x28, 0xfe, 0x90, 0xe7, + 0x79, 0x38, 0xe2, 0x7a, 0x6d, 0x6b, 0x30, 0x37, 0xc9, 0x47, 0x4a, 0x28, 0xe2, 0x4f, 0xff, 0x0b, + 0xb0, 0xe1, 0xb4, 0x53, 0x84, 0x2f, 0x41, 0x2b, 0x8f, 0x46, 0x71, 0x28, 0xa6, 0x19, 0x57, 0xa4, + 0x0b, 0xc0, 0x7f, 0x00, 0x9b, 0xdf, 0xe0, 0x59, 0x74, 0x34, 0x7b, 0x19, 0x79, 0x97, 0x4e, 0xa3, + 0x4c, 0xe7, 0x3e, 0x9c, 0x2f, 0xd1, 0x51, 0xc3, 0x4b, 0x46, 0x54, 0xc7, 0xb5, 0x14, 0xc8, 0x82, + 0x75, 0x2d, 0x1b, 0xf6, 0xb5, 0xf4, 0x9f, 0x00, 0xbb, 0x9b, 0xc4, 0x31, 0x1f, 0x88, 0x7d, 0xce, + 0xb3, 0xc2, 0xbe, 0x2a, 0xb8, 0xae, 0xbd, 0x73, 0x41, 0x9d, 0x63, 0xf9, 0xae, 0x2b, 0x76, 0x64, + 0xd0, 0x4c, 0x79, 0x36, 0x21, 0xc2, 0x4b, 0x01, 0xfd, 0xf6, 0xcf, 0xc3, 0x86, 0x43, 0x56, 0x69, + 0xfb, 0xb7, 0xe1, 0xfc, 0xbd, 0x28, 0x1f, 0x54, 0x07, 0xec, 0xc2, 0x62, 0x3a, 0x3d, 0xec, 0x17, + 0x77, 0x4a, 0x17, 0x51, 0x09, 0x96, 0xbb, 0x28, 0x62, 0x7f, 0xe4, 0x41, 0x73, 0xf7, 0xf1, 0xde, + 0x5d, 0xd6, 0x83, 0xa5, 0x28, 0x1e, 0x24, 0x13, 0x54, 0x1d, 0x72, 0xd1, 0xa6, 0x7c, 0xe6, 0x5d, + 0xb9, 0x04, 0x2d, 0xd2, 0x38, 0xa8, 0xd7, 0x95, 0x29, 0x54, 0x00, 0x68, 0x53, 0xf0, 0x67, 0x69, + 0x94, 0x91, 0xd1, 0xa0, 0x4d, 0x81, 0x26, 0x49, 0xc4, 0x6a, 0x85, 0xff, 0xd3, 0x26, 0x2c, 0xdf, + 0x1e, 0x88, 0xe8, 0x84, 0x2b, 0x89, 0x4d, 0xa3, 0x12, 0xa0, 0xe6, 0xa3, 0x4a, 0xa8, 0x5b, 0x32, + 0x3e, 0x49, 0x04, 0xef, 0x3b, 0x87, 0xe1, 0x82, 0xd8, 0x6a, 0x20, 0x09, 0xf5, 0x53, 0x94, 0xfd, + 0x34, 0xbf, 0x56, 0xe0, 0x82, 0xb8, 0x65, 0x08, 0xf4, 0xa3, 0x21, 0xcd, 0xac, 0x19, 0xe8, 0x22, + 0xee, 0xc7, 0x20, 0x4c, 0xc3, 0x41, 0x24, 0x66, 0xea, 0x8a, 0x9b, 0x32, 0xd2, 0x1e, 0x27, 0x83, + 0x70, 0xdc, 0x3f, 0x0c, 0xc7, 0x61, 0x3c, 0xe0, 0xca, 0x7c, 0x71, 0x41, 0xb4, 0x50, 0xd4, 0x94, + 0x74, 0x33, 0x69, 0xc5, 0x94, 0x50, 0xb4, 0x74, 0x06, 0xc9, 0x64, 0x12, 0x09, 0x34, 0x6c, 0xba, + 0x4b, 0x52, 0x9c, 0x14, 0x08, 0xad, 0x44, 0x96, 0x4e, 0xe5, 0x1e, 0xb6, 0xe4, 0x68, 0x0e, 0x88, + 0x54, 0x8e, 0x38, 0x27, 0xb1, 0xf4, 0xf4, 0xb4, 0x0b, 0x92, 0x4a, 0x81, 0xe0, 0x69, 0x4c, 0xe3, + 0x9c, 0x0b, 0x31, 0xe6, 0x43, 0x33, 0xa1, 0x36, 0x35, 0xab, 0x56, 0xb0, 0x9b, 0xb0, 0x21, 0x6d, + 0xad, 0x3c, 0x14, 0x49, 0x7e, 0x1c, 0xe5, 0xfd, 0x9c, 0xc7, 0xa2, 0xdb, 0xa1, 0xf6, 0x75, 0x55, + 0xec, 0x5d, 0xb8, 0x50, 0x82, 0x33, 0x3e, 0xe0, 0xd1, 0x09, 0x1f, 0x76, 0x97, 0xa9, 0xd7, 0x59, + 0xd5, 0xec, 0x0a, 0xb4, 0xd1, 0xc4, 0x9c, 0xa6, 0xc3, 0x10, 0xb5, 0xf1, 0x0a, 0x9d, 0x83, 0x0d, + 0xb1, 0xb7, 0x61, 0x39, 0xe5, 0x52, 0x65, 0x1e, 0x8b, 0xf1, 0x20, 0xef, 0xae, 0x92, 0x3e, 0x6b, + 0xab, 0x2b, 0x85, 0xfc, 0x1b, 0xb8, 0x2d, 0x90, 0x35, 0x07, 0x39, 0x19, 0x2d, 0xe1, 0xac, 0xbb, + 0x46, 0x4c, 0x57, 0x00, 0x78, 0xb3, 0xf6, 0xa2, 0x5c, 0x28, 0x4e, 0x33, 0x32, 0x6e, 0x17, 0x36, + 0x5d, 0xd8, 0xb8, 0x31, 0x4b, 0x8a, 0x6d, 0xf2, 0x6e, 0x9b, 0x86, 0xde, 0x54, 0x43, 0x3b, 0x1c, + 0x1b, 0x98, 0x56, 0xfe, 0x4f, 0x3d, 0x68, 0xe2, 0x3d, 0x3b, 0xfb, 0x4e, 0xda, 0xa2, 0x73, 0xce, + 0x11, 0x9d, 0x64, 0x5e, 0xa3, 0xf1, 0x21, 0xf7, 0x5c, 0xf2, 0xa5, 0x85, 0x14, 0xf5, 0x19, 0x1f, + 0x9c, 0x10, 0x73, 0x9a, 0x7a, 0x44, 0x90, 0x75, 0x51, 0x43, 0x51, 0x6f, 0xc9, 0x99, 0xa6, 0xac, + 0xeb, 0xa8, 0xe7, 0x62, 0x51, 0x47, 0xfd, 0xba, 0xb0, 0x18, 0xc5, 0x87, 0xc9, 0x34, 0x1e, 0x12, + 0x17, 0x2e, 0x05, 0xba, 0x88, 0xbb, 0x99, 0x92, 0xc1, 0x12, 0x4d, 0xb8, 0x62, 0xbf, 0x02, 0xf0, + 0x19, 0x5a, 0x30, 0x39, 0xc9, 0x15, 0xb3, 0x95, 0xef, 0xc0, 0xba, 0x85, 0xa9, 0x7d, 0x7c, 0x1d, + 0xe6, 0x53, 0x04, 0x94, 0x3d, 0xa2, 0xcf, 0x8f, 0x04, 0x92, 0xac, 0xf1, 0xd7, 0xd0, 0x4d, 0x15, + 0x1f, 0xc4, 0x47, 0x89, 0xa6, 0xf4, 0x0f, 0x73, 0xe8, 0x57, 0x2a, 0x48, 0x11, 0xba, 0x06, 0xab, + 0xd1, 0x90, 0xc7, 0x22, 0x12, 0xb3, 0xbe, 0x63, 0x28, 0x95, 0x61, 0x14, 0xe4, 0xe1, 0x38, 0x0a, + 0x73, 0x25, 0x24, 0x64, 0x81, 0xed, 0xc0, 0x26, 0xf2, 0x97, 0x66, 0x19, 0x73, 0xb8, 0xd2, 0x5e, + 0xab, 0xad, 0xc3, 0x2b, 0x81, 0xb8, 0x14, 0x42, 0x45, 0x17, 0x29, 0xd0, 0xea, 0xaa, 0x70, 0xd7, + 0x24, 0x25, 0x5c, 0xf2, 0xbc, 0xe4, 0x41, 0x03, 0x54, 0x9c, 0xa4, 0x05, 0x69, 0x2b, 0x96, 0x9d, + 0x24, 0xcb, 0xd1, 0x5a, 0xaa, 0x38, 0x5a, 0xd7, 0x60, 0x35, 0x9f, 0xc5, 0x03, 0x3e, 0xec, 0x8b, + 0x04, 0xc7, 0x8d, 0x62, 0x3a, 0x9d, 0xa5, 0xa0, 0x0c, 0x93, 0x4b, 0xc8, 0x73, 0x11, 0x73, 0x41, + 0xb2, 0x61, 0x29, 0xd0, 0x45, 0x14, 0xb3, 0xd4, 0x44, 0xb2, 0x76, 0x2b, 0x50, 0x25, 0xd4, 0x48, + 0xd3, 0x2c, 0xca, 0xbb, 0x1d, 0x42, 0xe9, 0x37, 0xfb, 0x22, 0x9c, 0x3f, 0x44, 0x07, 0xe6, 0x98, + 0x87, 0x43, 0x9e, 0xd1, 0xe9, 0x4b, 0xff, 0x4d, 0x5e, 0xf1, 0xfa, 0x4a, 0xff, 0x53, 0x52, 0x8f, + 0xc6, 0x7f, 0x7c, 0x42, 0xb7, 0x9a, 0xbd, 0x06, 0x2d, 0xb9, 0x92, 0xfc, 0x38, 0x54, 0x1a, 0x7b, + 0x89, 0x80, 0x83, 0xe3, 0x10, 0xdd, 0x1e, 0x67, 0x73, 0x1a, 0x64, 0x86, 0xb5, 0x09, 0xdb, 0x95, + 0x7b, 0xf3, 0x06, 0xac, 0x68, 0xcf, 0x34, 0xef, 0x8f, 0xf9, 0x91, 0xd0, 0xd6, 0x76, 0x3c, 0x9d, + 0xe0, 0x70, 0xf9, 0x1e, 0x3f, 0x12, 0xfe, 0x23, 0x58, 0x57, 0xb7, 0xf3, 0x6b, 0x29, 0xd7, 0x43, + 0xff, 0x6a, 0x59, 0x37, 0x48, 0x15, 0xbd, 0xa1, 0xf8, 0xd1, 0x76, 0x19, 0x4a, 0x0a, 0xc3, 0x0f, + 0x80, 0xa9, 0xea, 0xbb, 0xe3, 0x24, 0xe7, 0x8a, 0xa0, 0x0f, 0x9d, 0xc1, 0x38, 0xc9, 0xb5, 0x4d, + 0xaf, 0x96, 0xe3, 0x60, 0x78, 0x02, 0xf9, 0x74, 0x30, 0xc0, 0xfb, 0x2e, 0x95, 0xbc, 0x2e, 0xfa, + 0x7f, 0xe9, 0xc1, 0x06, 0x51, 0xd3, 0x72, 0xc4, 0x18, 0x82, 0xaf, 0x3e, 0xcd, 0xce, 0xc0, 0xf6, + 0x73, 0x36, 0x61, 0xfe, 0x28, 0xc9, 0x06, 0x5c, 0x8d, 0x24, 0x0b, 0x3f, 0xbb, 0x69, 0xdb, 0xac, + 0x98, 0xb6, 0xff, 0xec, 0xc1, 0x3a, 0x4d, 0xf5, 0x40, 0x84, 0x62, 0x9a, 0xab, 0xe5, 0xff, 0x1a, + 0x2c, 0xe3, 0x52, 0xb9, 0xbe, 0x34, 0x6a, 0xa2, 0x9b, 0xe6, 0x7e, 0x13, 0x2a, 0x1b, 0xef, 0x9e, + 0x0b, 0xdc, 0xc6, 0xec, 0xcb, 0xd0, 0xb1, 0xc3, 0x0b, 0x34, 0xe7, 0xf6, 0xce, 0x45, 0xbd, 0xca, + 0x0a, 0xe7, 0xec, 0x9e, 0x0b, 0x9c, 0x0e, 0xec, 0x16, 0x00, 0x69, 0x6d, 0x22, 0xab, 0xfc, 0xc2, + 0x8b, 0xee, 0x26, 0x59, 0x87, 0xb5, 0x7b, 0x2e, 0xb0, 0x9a, 0xdf, 0x59, 0x82, 0x05, 0xa9, 0x66, + 0xfc, 0x87, 0xb0, 0xec, 0xcc, 0xd4, 0x31, 0xd9, 0x3b, 0xd2, 0x64, 0xaf, 0x78, 0x78, 0x8d, 0xaa, + 0x87, 0xe7, 0xff, 0x6d, 0x03, 0x18, 0x72, 0x5b, 0xe9, 0x38, 0x51, 0xcf, 0x25, 0x43, 0xc7, 0x6a, + 0xe9, 0x04, 0x36, 0xc4, 0x6e, 0x00, 0xb3, 0x8a, 0xda, 0x91, 0x97, 0xda, 0xa1, 0xa6, 0x06, 0xc5, + 0x98, 0x34, 0x39, 0xb4, 0x43, 0xa9, 0xac, 0x34, 0x79, 0x6e, 0xb5, 0x75, 0xa8, 0x00, 0xd2, 0x69, + 0x7e, 0x8c, 0x7a, 0x58, 0xdb, 0x35, 0xba, 0x5c, 0x66, 0x90, 0x85, 0x97, 0x32, 0xc8, 0x62, 0x99, + 0x41, 0x48, 0xdf, 0x65, 0xd1, 0x49, 0x28, 0xb8, 0xd6, 0x21, 0xaa, 0x88, 0x66, 0xcc, 0x24, 0x8a, + 0x49, 0x3d, 0xf7, 0x27, 0x38, 0xba, 0x32, 0x63, 0x1c, 0xd0, 0xff, 0x89, 0x07, 0x6b, 0xb8, 0x77, + 0x0e, 0x7f, 0xbd, 0x07, 0xc4, 0xde, 0xaf, 0xc8, 0x5e, 0x4e, 0xdb, 0x5f, 0x9c, 0xbb, 0xde, 0x85, + 0x16, 0x11, 0x4c, 0x52, 0x1e, 0x2b, 0xe6, 0xea, 0xba, 0xcc, 0x55, 0x48, 0x96, 0xdd, 0x73, 0x41, + 0xd1, 0xd8, 0x62, 0xad, 0x7f, 0xf2, 0xa0, 0xad, 0xa6, 0xf9, 0x73, 0x1b, 0xdb, 0x3d, 0x58, 0x42, + 0x2e, 0xb3, 0x6c, 0x59, 0x53, 0x46, 0x3d, 0x30, 0x41, 0x8f, 0x06, 0x15, 0x9f, 0x63, 0x68, 0x97, + 0x61, 0xd4, 0x62, 0x24, 0x44, 0xf3, 0xbe, 0x88, 0xc6, 0x7d, 0x5d, 0xab, 0x22, 0x74, 0x75, 0x55, + 0x28, 0x4b, 0x72, 0x11, 0x8e, 0xb8, 0x52, 0x50, 0xb2, 0x80, 0x1e, 0x85, 0x5a, 0x50, 0xd9, 0x88, + 0xfa, 0x31, 0xc0, 0x85, 0x4a, 0x95, 0x31, 0xa4, 0x94, 0xed, 0x38, 0x8e, 0x26, 0x87, 0x89, 0x31, + 0x43, 0x3d, 0xdb, 0xac, 0x74, 0xaa, 0xd8, 0x08, 0xce, 0x6b, 0x4d, 0x8c, 0x7b, 0x5a, 0xe8, 0xdd, + 0x06, 0x99, 0x10, 0x6f, 0xbb, 0x3c, 0x50, 0x1e, 0x50, 0xe3, 0xf6, 0x6d, 0xac, 0xa7, 0xc7, 0x8e, + 0xa1, 0x6b, 0x54, 0xbe, 0x12, 0xdb, 0x96, 0x59, 0x80, 0x63, 0xbd, 0xf5, 0x92, 0xb1, 0x48, 0xc6, + 0x0c, 0xf5, 0x30, 0x67, 0x52, 0x63, 0x33, 0xb8, 0xac, 0xeb, 0x48, 0x2e, 0x57, 0xc7, 0x6b, 0xbe, + 0xd2, 0xda, 0x1e, 0x60, 0x67, 0x77, 0xd0, 0x97, 0x10, 0xee, 0xfd, 0xd8, 0x83, 0x15, 0x97, 0x1c, + 0xb2, 0x8e, 0xf2, 0x47, 0xb4, 0x80, 0xd1, 0xa6, 0x54, 0x09, 0xae, 0x7a, 0x54, 0x8d, 0x3a, 0x8f, + 0xca, 0xf6, 0x9b, 0xe6, 0x5e, 0xe6, 0x37, 0x35, 0x5f, 0xcd, 0x6f, 0x9a, 0xaf, 0xf3, 0x9b, 0x7a, + 0xff, 0xe9, 0x01, 0xab, 0x9e, 0x2f, 0x7b, 0x28, 0x5d, 0xba, 0x98, 0x8f, 0x95, 0x9c, 0xf8, 0xe5, + 0x57, 0xe3, 0x11, 0xbd, 0x87, 0xba, 0x37, 0x32, 0xab, 0x2d, 0x08, 0x6c, 0x53, 0x64, 0x39, 0xa8, + 0xab, 0x2a, 0x79, 0x72, 0xcd, 0x97, 0x7b, 0x72, 0xf3, 0x2f, 0xf7, 0xe4, 0x16, 0xca, 0x9e, 0x5c, + 0xef, 0x77, 0x61, 0xd9, 0x39, 0xf5, 0xff, 0xb9, 0x15, 0x97, 0xcd, 0x18, 0x79, 0xc0, 0x0e, 0xd6, + 0xfb, 0x8f, 0x06, 0xb0, 0x2a, 0xe7, 0xfd, 0x9f, 0xce, 0x81, 0xf8, 0xc8, 0x11, 0x20, 0x73, 0x8a, + 0x8f, 0x1c, 0xd1, 0xf1, 0xbf, 0x29, 0x14, 0xdf, 0x82, 0xf5, 0x8c, 0x0f, 0x92, 0x13, 0x7a, 0xa5, + 0x72, 0xa3, 0x00, 0xd5, 0x0a, 0x34, 0xe4, 0x5c, 0xff, 0x75, 0xc9, 0x79, 0x54, 0xb0, 0x34, 0x43, + 0xc9, 0x8d, 0xf5, 0xb7, 0x60, 0x53, 0xbe, 0xf5, 0xdc, 0x91, 0xa4, 0xb4, 0x90, 0xfd, 0xbe, 0x07, + 0xe7, 0x4b, 0x15, 0x45, 0xe4, 0x5d, 0xca, 0x51, 0x57, 0xb8, 0xba, 0x20, 0xce, 0x5f, 0x31, 0xb0, + 0x35, 0x7f, 0xa9, 0x6f, 0xaa, 0x15, 0xb8, 0x3f, 0xd3, 0xb8, 0xda, 0x5e, 0xee, 0x7a, 0x5d, 0x95, + 0x7f, 0x01, 0xce, 0xab, 0x93, 0x2d, 0x4d, 0x7c, 0x07, 0xb6, 0xca, 0x15, 0x45, 0x28, 0xd1, 0x9d, + 0xb2, 0x2e, 0xfa, 0xbf, 0x03, 0xec, 0xeb, 0x53, 0x9e, 0xcd, 0x28, 0xc6, 0x6f, 0xc2, 0xa4, 0x17, + 0xca, 0x9e, 0xf5, 0x42, 0x3a, 0x3d, 0xfc, 0x2a, 0x9f, 0xe9, 0x47, 0x94, 0x46, 0xf1, 0x88, 0xf2, + 0x39, 0x00, 0x74, 0x15, 0xe8, 0x51, 0x40, 0x3f, 0x6b, 0xa1, 0x27, 0x26, 0x09, 0xfa, 0xb7, 0x60, + 0xc3, 0xa1, 0x6f, 0x76, 0x72, 0x41, 0xf5, 0x90, 0xee, 0xaa, 0xfb, 0xd4, 0xa0, 0xea, 0xfc, 0x3f, + 0xf3, 0x60, 0x6e, 0x37, 0x49, 0xed, 0x48, 0x92, 0xe7, 0x46, 0x92, 0x94, 0xdc, 0xec, 0x1b, 0xb1, + 0xd8, 0x50, 0xb7, 0xde, 0x06, 0x51, 0xea, 0x85, 0x13, 0x81, 0x0e, 0xdb, 0x51, 0x92, 0x9d, 0x86, + 0xd9, 0x50, 0x6d, 0x6f, 0x09, 0xc5, 0xd5, 0x15, 0xc2, 0x05, 0x7f, 0xa2, 0xc1, 0x40, 0xe1, 0xb4, + 0x99, 0xf2, 0x31, 0x55, 0xc9, 0xff, 0xae, 0x07, 0xf3, 0x34, 0x57, 0xbc, 0x09, 0xf2, 0xf8, 0xe9, + 0x7d, 0x8d, 0xa2, 0x75, 0x9e, 0xbc, 0x09, 0x25, 0xb8, 0xf4, 0xea, 0xd6, 0xa8, 0xbc, 0xba, 0x5d, + 0x82, 0x96, 0x2c, 0x15, 0xcf, 0x54, 0x05, 0xc0, 0x2e, 0x43, 0xf3, 0x38, 0x49, 0xb5, 0xfe, 0x02, + 0x1d, 0x9e, 0x49, 0xd2, 0x80, 0x70, 0xff, 0x3a, 0xac, 0x3e, 0x4a, 0x86, 0xdc, 0xf2, 0xee, 0xcf, + 0x3c, 0x45, 0xff, 0xf7, 0x3c, 0x58, 0xd2, 0x8d, 0xd9, 0x35, 0x68, 0xa2, 0x1a, 0x2a, 0x19, 0x7e, + 0x26, 0x94, 0x8a, 0xed, 0x02, 0x6a, 0x81, 0xe2, 0x83, 0xbc, 0xc2, 0xc2, 0x4c, 0xd0, 0x3e, 0x61, + 0xa1, 0x80, 0xdf, 0x84, 0x15, 0x39, 0xe7, 0x92, 0xa2, 0x2a, 0xa1, 0xfe, 0x5f, 0x79, 0xb0, 0xec, + 0x8c, 0x81, 0x26, 0xfc, 0x38, 0xcc, 0x85, 0x0a, 0x4c, 0xa9, 0x4d, 0xb4, 0x21, 0x3b, 0xde, 0xd3, + 0x70, 0xe3, 0x3d, 0x26, 0x12, 0x31, 0x67, 0x47, 0x22, 0x6e, 0x42, 0xab, 0x78, 0xc1, 0x6c, 0x3a, + 0x62, 0x01, 0x47, 0xd4, 0x41, 0xe2, 0xa2, 0x11, 0xd2, 0x19, 0x24, 0xe3, 0x24, 0x53, 0x0f, 0x7c, + 0xb2, 0xe0, 0xdf, 0x82, 0xb6, 0xd5, 0x1e, 0xa7, 0x11, 0x73, 0x71, 0x9a, 0x64, 0x4f, 0x75, 0xd8, + 0x49, 0x15, 0xcd, 0x5b, 0x48, 0xa3, 0x78, 0x0b, 0xf1, 0xff, 0xda, 0x83, 0x65, 0xe4, 0x94, 0x28, + 0x1e, 0xed, 0x27, 0xe3, 0x68, 0x30, 0x23, 0x8e, 0xd1, 0x4c, 0xa1, 0x5e, 0xfe, 0x34, 0xc7, 0xb8, + 0x30, 0xea, 0x7b, 0x6d, 0xc1, 0x2b, 0x7e, 0x31, 0x65, 0xe4, 0x7c, 0xd4, 0x5b, 0x87, 0x61, 0xce, + 0xa5, 0xc9, 0xaf, 0xe4, 0xb4, 0x03, 0xa2, 0x74, 0x41, 0x20, 0x0b, 0x05, 0xef, 0x4f, 0xa2, 0xf1, + 0x38, 0x92, 0x6d, 0x25, 0x87, 0xd7, 0x55, 0xf9, 0x3f, 0x6a, 0x40, 0x5b, 0x49, 0x91, 0xfb, 0xc3, + 0x91, 0x8c, 0xa0, 0x2a, 0x23, 0xc4, 0x5c, 0x3f, 0x0b, 0xd1, 0xf5, 0x8e, 0xd9, 0x62, 0x21, 0xe5, + 0x63, 0x9d, 0xab, 0x1e, 0xeb, 0x25, 0x68, 0x21, 0x7b, 0xbd, 0x4d, 0xf6, 0x91, 0x7c, 0xf0, 0x2e, + 0x00, 0x5d, 0xbb, 0x43, 0xb5, 0xf3, 0x45, 0x2d, 0x01, 0x8e, 0x45, 0xb4, 0x50, 0xb2, 0x88, 0xde, + 0x85, 0x8e, 0x22, 0x43, 0xfb, 0x4e, 0x0e, 0x55, 0xc1, 0xe0, 0xce, 0x99, 0x04, 0x4e, 0x4b, 0xdd, + 0x73, 0x47, 0xf7, 0x5c, 0x7a, 0x59, 0x4f, 0xdd, 0x92, 0x9e, 0x15, 0xe4, 0xde, 0x3c, 0xcc, 0xc2, + 0xf4, 0x58, 0x4b, 0xe6, 0xa1, 0x79, 0x2b, 0x25, 0x98, 0x5d, 0x87, 0x79, 0xec, 0xa6, 0xa5, 0x5f, + 0xfd, 0xa5, 0x93, 0x4d, 0xd8, 0x35, 0x98, 0xe7, 0xc3, 0x11, 0xd7, 0x56, 0x39, 0x73, 0xfd, 0x23, + 0x3c, 0xa3, 0x40, 0x36, 0x40, 0x11, 0x80, 0x68, 0x49, 0x04, 0xb8, 0x92, 0x73, 0x01, 0x8b, 0x1f, + 0x0c, 0xfd, 0x4d, 0x60, 0x8f, 0x24, 0xd7, 0xda, 0xf1, 0xc0, 0x3f, 0x9c, 0x83, 0xb6, 0x05, 0xe3, + 0x6d, 0x1e, 0xe1, 0x84, 0xfb, 0xc3, 0x28, 0x9c, 0x70, 0xc1, 0x33, 0xc5, 0xa9, 0x25, 0x94, 0x04, + 0xec, 0xc9, 0xa8, 0x9f, 0x4c, 0x45, 0x7f, 0xc8, 0x47, 0x19, 0x97, 0xfa, 0xce, 0x0b, 0x4a, 0x28, + 0xb6, 0x9b, 0x84, 0xcf, 0xec, 0x76, 0x92, 0x1f, 0x4a, 0xa8, 0x8e, 0xee, 0xc9, 0x3d, 0x6a, 0x16, + 0xd1, 0x3d, 0xb9, 0x23, 0x65, 0x39, 0x34, 0x5f, 0x23, 0x87, 0xde, 0x81, 0x2d, 0x29, 0x71, 0xd4, + 0xdd, 0xec, 0x97, 0xd8, 0xe4, 0x8c, 0x5a, 0x76, 0x1d, 0xd6, 0x70, 0xce, 0x9a, 0xc1, 0xf3, 0xe8, + 0x53, 0xe9, 0x89, 0x7b, 0x41, 0x05, 0xc7, 0xb6, 0x78, 0x1d, 0x9d, 0xb6, 0xf2, 0x89, 0xa1, 0x82, + 0x53, 0xdb, 0xf0, 0x99, 0xdb, 0xb6, 0xa5, 0xda, 0x96, 0x70, 0x7f, 0x19, 0xda, 0x07, 0x22, 0x49, + 0xf5, 0xa1, 0xac, 0x40, 0x47, 0x16, 0xd5, 0xb3, 0xd2, 0x6b, 0x70, 0x91, 0xb8, 0xe8, 0x71, 0x92, + 0x26, 0xe3, 0x64, 0x34, 0x3b, 0x98, 0x1e, 0xe6, 0x83, 0x2c, 0x4a, 0xd1, 0x5a, 0xf6, 0xff, 0xd1, + 0x83, 0x0d, 0xa7, 0x56, 0xb9, 0xf9, 0x5f, 0x94, 0x2c, 0x6d, 0x5e, 0x02, 0x24, 0xe3, 0xad, 0x5b, + 0xe2, 0x50, 0x36, 0x94, 0x41, 0x93, 0x27, 0xea, 0x71, 0xe0, 0x36, 0xac, 0xea, 0x99, 0xe9, 0x8e, + 0x92, 0x0b, 0xbb, 0x55, 0x2e, 0x54, 0xfd, 0x57, 0x54, 0x07, 0x4d, 0xe2, 0xd7, 0xa5, 0xcd, 0xc9, + 0x87, 0xb4, 0x46, 0xed, 0xef, 0xf5, 0x74, 0x7f, 0xdb, 0xd0, 0xd5, 0x33, 0x18, 0x18, 0x30, 0xf7, + 0xff, 0xd8, 0x03, 0x28, 0x66, 0x87, 0x8c, 0x51, 0x88, 0x74, 0x99, 0xe9, 0x64, 0x89, 0xef, 0xd7, + 0xa1, 0x63, 0x62, 0xd4, 0x85, 0x96, 0x68, 0x6b, 0x0c, 0x0d, 0x98, 0xab, 0xb0, 0x3a, 0x1a, 0x27, + 0x87, 0xa4, 0x73, 0xe9, 0x9d, 0x32, 0x57, 0x8f, 0x6b, 0x2b, 0x12, 0x7e, 0xa0, 0xd0, 0x42, 0xa5, + 0x34, 0x2d, 0x95, 0xe2, 0x7f, 0xa7, 0x61, 0x62, 0x9e, 0xc5, 0x9a, 0xcf, 0xbc, 0x65, 0x6c, 0xa7, + 0x22, 0x1c, 0xcf, 0x08, 0x31, 0x52, 0x64, 0x63, 0xff, 0xa5, 0x4e, 0xde, 0x2d, 0x58, 0xc9, 0xa4, + 0xf4, 0xd1, 0xa2, 0xa9, 0xf9, 0x02, 0xd1, 0xb4, 0x9c, 0x39, 0x7a, 0xe7, 0xff, 0xc3, 0x5a, 0x38, + 0x3c, 0xe1, 0x99, 0x88, 0xc8, 0xda, 0x27, 0xa5, 0x2f, 0x05, 0xea, 0xaa, 0x85, 0x93, 0x2e, 0xbe, + 0x0a, 0xab, 0xea, 0x41, 0xd3, 0xb4, 0x54, 0x69, 0x2c, 0x05, 0x8c, 0x0d, 0xfd, 0x1f, 0xea, 0xf0, + 0xaa, 0x7b, 0x86, 0x67, 0xef, 0x88, 0xbd, 0xba, 0x46, 0x69, 0x75, 0xff, 0x4f, 0x85, 0x3a, 0x87, + 0xda, 0xa5, 0x50, 0x41, 0x67, 0x09, 0xaa, 0xd0, 0xb4, 0xbb, 0xa5, 0xcd, 0x57, 0xd9, 0x52, 0xff, + 0xfb, 0x73, 0xb0, 0xf8, 0x41, 0x7c, 0x92, 0x44, 0x03, 0x0a, 0x3c, 0x4e, 0xf8, 0x24, 0xd1, 0xb9, + 0x02, 0xf8, 0x1b, 0x35, 0x3a, 0xbd, 0x98, 0xa5, 0x42, 0x45, 0x0e, 0x75, 0x11, 0xb5, 0x5b, 0x56, + 0xe4, 0xcf, 0x48, 0x4e, 0xb1, 0x10, 0xb4, 0x0f, 0x33, 0x3b, 0x79, 0x48, 0x95, 0x8a, 0x64, 0x8b, + 0x79, 0x2b, 0xd9, 0x82, 0xc2, 0xd4, 0xf2, 0x31, 0x90, 0xb6, 0x73, 0x29, 0xd0, 0x45, 0xb2, 0x63, + 0x33, 0x2e, 0x1d, 0x5e, 0xd2, 0x93, 0x8b, 0xca, 0x8e, 0xb5, 0x41, 0xd4, 0xa5, 0xb2, 0x83, 0x6c, + 0x23, 0x65, 0x8d, 0x0d, 0xa1, 0x6d, 0x51, 0xce, 0x3f, 0x6a, 0xc9, 0x23, 0x2e, 0xc1, 0x28, 0x90, + 0x86, 0xdc, 0xc8, 0x0d, 0xb9, 0x06, 0x90, 0xf9, 0x41, 0x65, 0xdc, 0xb2, 0x82, 0xe5, 0xa3, 0xa6, + 0x2a, 0x91, 0x0d, 0x12, 0x8e, 0xc7, 0x87, 0xe1, 0xe0, 0x29, 0x65, 0x85, 0xd1, 0x1b, 0x66, 0x2b, + 0x70, 0x41, 0x9c, 0x35, 0x25, 0x39, 0x29, 0x12, 0xcb, 0xf2, 0x0d, 0xd2, 0x82, 0xfc, 0x6f, 0x00, + 0xbb, 0x3d, 0x1c, 0xaa, 0x13, 0x32, 0x3e, 0x42, 0xb1, 0xb7, 0x9e, 0xb3, 0xb7, 0x35, 0x6b, 0x6c, + 0xd4, 0xae, 0xd1, 0xbf, 0x0f, 0xed, 0x7d, 0x2b, 0x99, 0x8b, 0x0e, 0x53, 0xa7, 0x71, 0x29, 0x06, + 0xb0, 0x10, 0x6b, 0xc0, 0x86, 0x3d, 0xa0, 0xff, 0x2b, 0xc0, 0xf6, 0xa2, 0x5c, 0x98, 0xf9, 0xc9, + 0x0d, 0x7c, 0x1d, 0x3a, 0x26, 0xda, 0x15, 0x8f, 0x67, 0x2a, 0xd6, 0xd8, 0x56, 0xd8, 0xd7, 0xe2, + 0xf1, 0xcc, 0xbf, 0x2d, 0x9f, 0x42, 0xcb, 0x0b, 0xbb, 0x0e, 0x4b, 0x91, 0x84, 0xb4, 0x1c, 0x5e, + 0x51, 0x0c, 0xac, 0x5b, 0x9a, 0x7a, 0x34, 0x28, 0x14, 0xe8, 0x88, 0xf9, 0x1f, 0x79, 0xb0, 0xa8, + 0x96, 0x86, 0xea, 0xd0, 0x49, 0x63, 0x93, 0x0b, 0x73, 0xb0, 0xfa, 0xe4, 0x9f, 0x2a, 0xd7, 0xcd, + 0xd5, 0x71, 0x1d, 0x83, 0x66, 0x1a, 0x8a, 0x63, 0xb2, 0xa0, 0x5b, 0x01, 0xfd, 0xd6, 0x9e, 0xd2, + 0x7c, 0xe1, 0x29, 0xd5, 0xe5, 0x9b, 0x49, 0x99, 0x51, 0xc1, 0xf5, 0x13, 0xb1, 0x5a, 0x80, 0x89, + 0x6e, 0xde, 0x91, 0x4f, 0xc4, 0x05, 0x5c, 0xec, 0x97, 0x22, 0x51, 0xde, 0x2f, 0xd5, 0x34, 0x30, + 0xf5, 0x7e, 0x0f, 0xba, 0xf7, 0xf8, 0x98, 0x0b, 0x7e, 0x7b, 0x3c, 0x2e, 0xd3, 0x7f, 0x0d, 0x2e, + 0xd6, 0xd4, 0x29, 0xad, 0xfa, 0x00, 0xd6, 0xef, 0xf1, 0xc3, 0xe9, 0x68, 0x8f, 0x9f, 0x14, 0xcf, + 0x0a, 0x0c, 0x9a, 0xf9, 0x71, 0x72, 0xaa, 0xce, 0x96, 0x7e, 0xa3, 0xc3, 0x3b, 0xc6, 0x36, 0xfd, + 0x3c, 0xe5, 0x03, 0x9d, 0xf6, 0x42, 0xc8, 0x41, 0xca, 0x07, 0xfe, 0x3b, 0xc0, 0x6c, 0x3a, 0x6a, + 0x09, 0x78, 0x73, 0xa7, 0x87, 0xfd, 0x7c, 0x96, 0x0b, 0x3e, 0xd1, 0xf9, 0x3c, 0x36, 0xe4, 0x5f, + 0x85, 0xce, 0x7e, 0x38, 0x0b, 0xf8, 0x27, 0x2a, 0x93, 0x10, 0x9d, 0xb7, 0x70, 0x86, 0xac, 0x6c, + 0x9c, 0x37, 0xaa, 0xf6, 0xff, 0xbe, 0x01, 0x0b, 0xb2, 0x25, 0x52, 0x1d, 0xf2, 0x5c, 0x44, 0xb1, + 0x0c, 0xbf, 0x2b, 0xaa, 0x16, 0x54, 0xe1, 0x8d, 0x46, 0x0d, 0x6f, 0x28, 0x73, 0x4a, 0x27, 0x0f, + 0x28, 0x26, 0x70, 0x30, 0xf2, 0x4d, 0xcd, 0x83, 0x64, 0x53, 0xf9, 0xa6, 0x1a, 0x28, 0x79, 0xc9, + 0x85, 0x7c, 0x90, 0xf3, 0xd3, 0x4c, 0xab, 0xd8, 0xc1, 0x86, 0x6a, 0xa5, 0xd0, 0xa2, 0xe4, 0x9a, + 0x8a, 0x14, 0xaa, 0x48, 0x9b, 0xa5, 0x57, 0x90, 0x36, 0xd2, 0xc6, 0x72, 0xa4, 0x0d, 0x83, 0xb5, + 0x07, 0x9c, 0x07, 0x3c, 0x4d, 0x32, 0x9d, 0x8e, 0xe9, 0x7f, 0xcf, 0x83, 0x35, 0xa5, 0x3d, 0x4c, + 0x1d, 0x7b, 0xdd, 0x51, 0x35, 0x5e, 0x5d, 0x44, 0xf6, 0x0d, 0x58, 0x26, 0x67, 0x0b, 0x3d, 0x29, + 0xf2, 0xac, 0x54, 0xfc, 0xc1, 0x01, 0x71, 0x4e, 0x3a, 0xc6, 0x38, 0x89, 0xc6, 0x6a, 0x83, 0x6d, + 0x08, 0xd5, 0xa2, 0x76, 0xc6, 0x68, 0x7b, 0xbd, 0xc0, 0x94, 0xfd, 0xbf, 0xf3, 0x60, 0xdd, 0x9a, + 0xb0, 0xe2, 0xa8, 0x5b, 0xa0, 0x9f, 0x25, 0x65, 0x3c, 0x41, 0x5e, 0x8c, 0x0b, 0xae, 0x26, 0x2c, + 0xba, 0x39, 0x8d, 0xe9, 0x60, 0xc2, 0x19, 0x4d, 0x30, 0x9f, 0xca, 0xc4, 0xa8, 0x66, 0x60, 0x43, + 0xc8, 0x14, 0xa7, 0x9c, 0x3f, 0x35, 0x4d, 0xe6, 0xa8, 0x89, 0x83, 0xd1, 0xab, 0x53, 0x12, 0x8b, + 0x63, 0xd3, 0x48, 0xa6, 0x53, 0xb8, 0xa0, 0xff, 0x2f, 0x1e, 0x6c, 0x48, 0x0b, 0x44, 0xd9, 0x77, + 0x26, 0xa3, 0x6a, 0x41, 0x9a, 0x5c, 0xf2, 0x76, 0xed, 0x9e, 0x0b, 0x54, 0x99, 0x7d, 0xe9, 0x15, + 0xad, 0x26, 0xf3, 0xda, 0x78, 0xc6, 0x59, 0xcc, 0xd5, 0x9d, 0xc5, 0x0b, 0x76, 0xba, 0xce, 0x33, + 0x9f, 0xaf, 0xf5, 0xcc, 0xef, 0x2c, 0xc2, 0x7c, 0x3e, 0x48, 0x52, 0xee, 0x6f, 0xc1, 0xa6, 0xbb, + 0x38, 0x25, 0x4e, 0x7e, 0xe0, 0x41, 0xf7, 0x81, 0x0c, 0x2b, 0x45, 0xf1, 0x68, 0x37, 0xca, 0x45, + 0x92, 0x99, 0x14, 0xd2, 0xcb, 0x00, 0xb9, 0x08, 0x33, 0x21, 0x73, 0x3e, 0x94, 0x4f, 0x5d, 0x20, + 0x38, 0x47, 0x1e, 0x0f, 0x65, 0xad, 0x3c, 0x1b, 0x53, 0xc6, 0x83, 0xa1, 0x97, 0xd0, 0x7e, 0x72, + 0x74, 0x94, 0x73, 0x63, 0x23, 0xd9, 0x18, 0xba, 0x59, 0x78, 0x7b, 0xd1, 0xb1, 0xe0, 0x27, 0x24, + 0x36, 0xa5, 0x0f, 0x55, 0x42, 0xfd, 0xbf, 0xf1, 0x60, 0xb5, 0x98, 0xe4, 0x7d, 0x04, 0xdd, 0x9b, + 0x2e, 0xa7, 0x66, 0xdd, 0x74, 0xed, 0xed, 0x47, 0xc3, 0x7e, 0x14, 0xab, 0xb9, 0x59, 0x08, 0xdd, + 0x3e, 0x55, 0x4a, 0xa6, 0x3a, 0xbf, 0xc6, 0x86, 0xe4, 0x13, 0x9c, 0xc0, 0xde, 0x32, 0xb9, 0x46, + 0x95, 0x28, 0x65, 0x67, 0x22, 0xa8, 0xd7, 0x82, 0x8c, 0xf1, 0xa9, 0xa2, 0xd6, 0x35, 0x8b, 0x84, + 0xe2, 0x4f, 0xff, 0xbb, 0x1e, 0x5c, 0xac, 0xd9, 0x5c, 0x75, 0x33, 0xee, 0xc1, 0xfa, 0x91, 0xa9, + 0xd4, 0x1b, 0x20, 0xaf, 0xc7, 0x96, 0xe2, 0xa2, 0xd2, 0xa2, 0x83, 0x6a, 0x07, 0xf6, 0x16, 0xac, + 0x53, 0x90, 0x42, 0x6e, 0xa9, 0xf3, 0x22, 0x5d, 0xad, 0xd8, 0xf9, 0x61, 0x03, 0x56, 0x64, 0xcc, + 0x58, 0x7e, 0x44, 0xc0, 0x33, 0xf6, 0x21, 0x2c, 0xaa, 0x4f, 0x36, 0xd8, 0x79, 0x35, 0xac, 0xfb, + 0x91, 0x48, 0x6f, 0xab, 0x0c, 0x2b, 0xde, 0xd9, 0xf8, 0x83, 0x9f, 0xfc, 0xfb, 0x9f, 0x34, 0x96, + 0x59, 0x7b, 0xfb, 0xe4, 0xed, 0xed, 0x11, 0x8f, 0x73, 0xa4, 0xf1, 0x5b, 0x00, 0xc5, 0x57, 0x0f, + 0xac, 0x6b, 0x0c, 0x86, 0xd2, 0x57, 0x1a, 0xbd, 0x8b, 0x35, 0x35, 0x8a, 0xee, 0x45, 0xa2, 0xbb, + 0xe1, 0xaf, 0x20, 0xdd, 0x28, 0x8e, 0x84, 0xfc, 0x04, 0xe2, 0x3d, 0xef, 0x3a, 0x1b, 0x42, 0xc7, + 0xfe, 0xfa, 0x81, 0x69, 0xff, 0xac, 0xe6, 0x93, 0x8a, 0xde, 0x6b, 0xb5, 0x75, 0xda, 0x39, 0xa5, + 0x31, 0xce, 0xfb, 0x6b, 0x38, 0xc6, 0x94, 0x5a, 0x98, 0x51, 0x76, 0xfe, 0xf5, 0x35, 0x68, 0x99, + 0x18, 0x07, 0xfb, 0x36, 0x2c, 0x3b, 0x61, 0x76, 0xa6, 0x09, 0xd7, 0x45, 0xe5, 0x7b, 0x97, 0xea, + 0x2b, 0xd5, 0xb0, 0x97, 0x69, 0xd8, 0x2e, 0xdb, 0xc2, 0x61, 0x55, 0x6c, 0x7b, 0x9b, 0x1e, 0x17, + 0x64, 0x8a, 0xce, 0x53, 0x58, 0x71, 0x43, 0xe3, 0xec, 0x92, 0x2b, 0x50, 0x4a, 0xa3, 0x7d, 0xee, + 0x8c, 0x5a, 0x35, 0xdc, 0x25, 0x1a, 0x6e, 0x8b, 0x6d, 0xda, 0xc3, 0x99, 0xd8, 0x03, 0xa7, 0xa4, + 0x2a, 0xfb, 0xb3, 0x08, 0xf6, 0x39, 0x73, 0xd4, 0x75, 0x9f, 0x4b, 0x98, 0x43, 0xab, 0x7e, 0x33, + 0xe1, 0x77, 0x69, 0x28, 0xc6, 0x68, 0x43, 0xed, 0xaf, 0x22, 0xd8, 0xc7, 0xd0, 0x32, 0xa9, 0xd0, + 0xec, 0x82, 0x95, 0x7f, 0x6e, 0xe7, 0x67, 0xf7, 0xba, 0xd5, 0x8a, 0xba, 0xa3, 0xb2, 0x29, 0x23, + 0x43, 0xec, 0xc1, 0x79, 0x65, 0x70, 0x1e, 0xf2, 0x9f, 0x65, 0x25, 0x35, 0x1f, 0x73, 0xdc, 0xf4, + 0xd8, 0x2d, 0x58, 0xd2, 0x19, 0xe6, 0x6c, 0xab, 0x3e, 0x53, 0xbe, 0x77, 0xa1, 0x82, 0xab, 0xfb, + 0x7c, 0x1b, 0xa0, 0xc8, 0x8e, 0x36, 0x9c, 0x5f, 0xc9, 0xd9, 0x36, 0x9b, 0x58, 0x93, 0x4a, 0x3d, + 0xa2, 0x5c, 0x70, 0x37, 0xf9, 0x9a, 0x7d, 0xbe, 0x68, 0x5f, 0x9b, 0x96, 0xfd, 0x02, 0x82, 0xfe, + 0x16, 0xed, 0xdd, 0x1a, 0xa3, 0xab, 0x14, 0xf3, 0x53, 0x9d, 0x5e, 0x78, 0x0f, 0xda, 0x56, 0xc6, + 0x35, 0xd3, 0x14, 0xaa, 0xd9, 0xda, 0xbd, 0x5e, 0x5d, 0x95, 0x9a, 0xee, 0x57, 0x60, 0xd9, 0x49, + 0x9d, 0x36, 0x37, 0xa3, 0x2e, 0x31, 0xdb, 0xdc, 0x8c, 0xfa, 0x6c, 0xeb, 0x6f, 0x42, 0xdb, 0x4a, + 0x74, 0x66, 0x56, 0x72, 0x46, 0x29, 0xc5, 0xd9, 0xcc, 0xa8, 0x2e, 0x2f, 0x7a, 0x93, 0xd6, 0xbb, + 0xe2, 0xb7, 0x70, 0xbd, 0x94, 0x63, 0x87, 0x4c, 0xf2, 0x6d, 0x58, 0x71, 0x53, 0x9f, 0xcd, 0xad, + 0xaa, 0x4d, 0xa2, 0x36, 0xb7, 0xea, 0x8c, 0x7c, 0x69, 0xc5, 0x90, 0xd7, 0x37, 0xcc, 0x20, 0xdb, + 0x9f, 0xa9, 0x08, 0xff, 0x73, 0xf6, 0x75, 0x14, 0x1d, 0x2a, 0xe9, 0x91, 0x15, 0x09, 0xdf, 0x6e, + 0x6a, 0xa4, 0xe1, 0xf6, 0x4a, 0x7e, 0xa4, 0xbf, 0x4e, 0xc4, 0xdb, 0xac, 0x58, 0x81, 0x94, 0xd0, + 0x94, 0xfc, 0x68, 0x49, 0x68, 0x3b, 0x3f, 0xd2, 0x92, 0xd0, 0x4e, 0x8e, 0x64, 0x59, 0x42, 0x8b, + 0x08, 0x69, 0xc4, 0xb0, 0x5a, 0x7a, 0x90, 0x35, 0x97, 0xa5, 0x3e, 0x9d, 0xa3, 0x77, 0xf9, 0xc5, + 0xef, 0xb8, 0xae, 0x98, 0xd1, 0xe2, 0x65, 0x5b, 0x67, 0xdf, 0xfc, 0x36, 0x74, 0xec, 0x8c, 0x5a, + 0x23, 0xb3, 0x6b, 0xb2, 0x6f, 0x8d, 0xcc, 0xae, 0x4b, 0xc1, 0xd5, 0x87, 0xcb, 0x3a, 0xf6, 0x30, + 0xec, 0x9b, 0xb0, 0x6a, 0x3d, 0xfd, 0x1f, 0xcc, 0xe2, 0x81, 0x61, 0x9e, 0x6a, 0x02, 0x56, 0xaf, + 0xce, 0x3e, 0xf3, 0x2f, 0x10, 0xe1, 0x75, 0xdf, 0x21, 0x8c, 0x8c, 0x73, 0x17, 0xda, 0x76, 0x5a, + 0xc1, 0x0b, 0xe8, 0x5e, 0xb0, 0xaa, 0xec, 0xbc, 0xa5, 0x9b, 0x1e, 0xfb, 0x73, 0x0f, 0x3a, 0x76, + 0x6a, 0x1f, 0x73, 0x82, 0x8a, 0x25, 0x3a, 0x5d, 0xbb, 0xce, 0x26, 0xe4, 0x07, 0x34, 0xc9, 0xbd, + 0xeb, 0x5f, 0x71, 0x36, 0xf9, 0x33, 0xc7, 0xce, 0xbf, 0x51, 0xfe, 0x1a, 0xe9, 0x79, 0xb9, 0x81, + 0x9d, 0xa4, 0xf6, 0xfc, 0xa6, 0xc7, 0xde, 0x93, 0x5f, 0xac, 0x69, 0x1f, 0x9d, 0x59, 0xc2, 0xad, + 0xbc, 0x65, 0xf6, 0xc7, 0x5d, 0xd7, 0xbc, 0x9b, 0x1e, 0xfb, 0x96, 0xfc, 0xe8, 0x48, 0xf5, 0xa5, + 0x9d, 0x7f, 0xd5, 0xfe, 0xfe, 0x1b, 0xb4, 0x9a, 0xcb, 0xfe, 0x45, 0x67, 0x35, 0x65, 0xe9, 0xbe, + 0x0f, 0x50, 0x04, 0x5c, 0x58, 0x29, 0xfa, 0x60, 0xe4, 0x5e, 0x35, 0x26, 0xe3, 0x9e, 0xa8, 0x0e, + 0x52, 0x20, 0xc5, 0x8f, 0x25, 0x33, 0xaa, 0xf6, 0xb9, 0x39, 0xd2, 0x6a, 0xe0, 0xa4, 0xd7, 0xab, + 0xab, 0xaa, 0x63, 0x45, 0x4d, 0x9f, 0x3d, 0x81, 0xe5, 0xbd, 0x24, 0x79, 0x3a, 0x4d, 0x4d, 0x10, + 0xcf, 0xf5, 0xff, 0x77, 0xc3, 0xfc, 0xb8, 0x57, 0x5a, 0x85, 0x7f, 0x85, 0x48, 0xf5, 0x58, 0xd7, + 0x22, 0xb5, 0xfd, 0x59, 0x11, 0xee, 0x79, 0xce, 0x42, 0x58, 0x37, 0x3a, 0xce, 0x4c, 0xbc, 0xe7, + 0x92, 0xb1, 0xa3, 0x2e, 0x95, 0x21, 0x1c, 0xab, 0x43, 0xcf, 0x76, 0x3b, 0xd7, 0x34, 0x6f, 0x7a, + 0x6c, 0x1f, 0x3a, 0xf7, 0xf8, 0x20, 0x19, 0x72, 0xe5, 0xb1, 0x6f, 0x14, 0x13, 0x37, 0xae, 0x7e, + 0x6f, 0xd9, 0x01, 0xdd, 0x5b, 0x9f, 0x86, 0xb3, 0x8c, 0x7f, 0xb2, 0xfd, 0x99, 0x8a, 0x05, 0x3c, + 0xd7, 0xb7, 0x5e, 0xc7, 0x2f, 0x9c, 0x5b, 0x5f, 0x0a, 0x78, 0x38, 0xb7, 0xbe, 0x12, 0xf0, 0x70, + 0xb6, 0x5a, 0xc7, 0x4f, 0xd8, 0x18, 0xd6, 0x2b, 0x31, 0x12, 0xa3, 0x29, 0xcf, 0x8a, 0xac, 0xf4, + 0xae, 0x9c, 0xdd, 0xc0, 0x1d, 0xed, 0xba, 0x3b, 0xda, 0x01, 0x2c, 0xdf, 0xe3, 0x72, 0xb3, 0xe4, + 0xc3, 0x58, 0xcf, 0x15, 0x23, 0xf6, 0x23, 0x5a, 0x59, 0xc4, 0x50, 0x9d, 0x2b, 0xd6, 0xe9, 0x55, + 0x8a, 0x7d, 0x0c, 0xed, 0x87, 0x5c, 0xe8, 0x97, 0x30, 0x63, 0x6f, 0x94, 0x9e, 0xc6, 0x7a, 0x35, + 0x0f, 0x69, 0x2e, 0xcf, 0x10, 0xb5, 0x6d, 0x3e, 0x1c, 0x71, 0x79, 0xd9, 0xfb, 0xd1, 0xf0, 0x39, + 0xfb, 0x0d, 0x22, 0x6e, 0x1e, 0xcf, 0xb7, 0xac, 0x07, 0x14, 0x9b, 0xf8, 0x6a, 0x09, 0xaf, 0xa3, + 0x1c, 0x27, 0x43, 0x6e, 0x29, 0xb8, 0x18, 0xda, 0x56, 0xa6, 0x84, 0xb9, 0x40, 0xd5, 0xec, 0x0c, + 0x73, 0x81, 0x6a, 0x12, 0x2b, 0xfc, 0x6b, 0x34, 0x8e, 0xcf, 0xae, 0x14, 0xe3, 0xc8, 0x64, 0x8a, + 0x62, 0xa4, 0xed, 0xcf, 0xc2, 0x89, 0x78, 0xce, 0x3e, 0xa2, 0xaf, 0x01, 0xec, 0xd7, 0xbe, 0xc2, + 0xde, 0x29, 0x3f, 0x0c, 0x9a, 0xcd, 0xb2, 0xaa, 0x5c, 0x1b, 0x48, 0x0e, 0x45, 0x7a, 0xf0, 0x4b, + 0x00, 0x07, 0x22, 0x49, 0xef, 0x85, 0x7c, 0x92, 0xc4, 0x85, 0xe4, 0x2a, 0x5e, 0xb4, 0x0a, 0xc9, + 0x65, 0x3d, 0x6b, 0xb1, 0x8f, 0x2c, 0x8b, 0xd3, 0x79, 0x2c, 0xd5, 0xcc, 0x75, 0xe6, 0xa3, 0x97, + 0xd9, 0x90, 0x9a, 0x87, 0xaf, 0x9b, 0x1e, 0xda, 0x8f, 0x45, 0x44, 0xce, 0xd8, 0x8f, 0x95, 0x60, + 0x9f, 0x11, 0x7b, 0x35, 0xe1, 0xbb, 0x7d, 0x68, 0x15, 0x61, 0x21, 0xad, 0x92, 0xca, 0x41, 0x24, + 0xa3, 0x63, 0x2a, 0xc1, 0x1a, 0x7f, 0x8d, 0xb6, 0x0a, 0xd8, 0x12, 0x6e, 0x15, 0x45, 0x60, 0x22, + 0xd8, 0x90, 0x13, 0x34, 0x0a, 0x93, 0xde, 0x68, 0xf4, 0x4a, 0x6a, 0x02, 0x26, 0xe6, 0x36, 0xd7, + 0xc6, 0x1b, 0x1c, 0xdf, 0x0e, 0xb9, 0x55, 0xbe, 0x0f, 0xa1, 0x68, 0x9e, 0xc0, 0x7a, 0xc5, 0x59, + 0x36, 0x57, 0xfa, 0xac, 0x18, 0x85, 0xb9, 0xd2, 0x67, 0xfa, 0xd9, 0xfe, 0x79, 0x1a, 0x72, 0xd5, + 0x07, 0x1c, 0x32, 0x3f, 0x8d, 0xc4, 0xe0, 0xf8, 0x3d, 0xef, 0xfa, 0xe1, 0x02, 0xfd, 0x27, 0xc2, + 0x17, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x34, 0xfb, 0x9b, 0x45, 0x41, 0x00, 0x00, } diff --git a/lnrpc/rpc.pb.gw.go b/lnrpc/rpc.pb.gw.go index e5116aa7..49a7de7b 100644 --- a/lnrpc/rpc.pb.gw.go +++ b/lnrpc/rpc.pb.gw.go @@ -71,18 +71,10 @@ func request_WalletUnlocker_UnlockWallet_0(ctx context.Context, marshaler runtim } -var ( - filter_Lightning_WalletBalance_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - func request_Lightning_WalletBalance_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq WalletBalanceRequest var metadata runtime.ServerMetadata - if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_Lightning_WalletBalance_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := client.WalletBalance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index 23f4cb12..97910f0f 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -145,8 +145,7 @@ service Lightning { /** lncli: `walletbalance` WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control - by the wallet. This method can be modified by having the request specify - only witness outputs should be factored into the final output sum. + of the wallet. */ rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) { option (google.api.http) = { @@ -681,7 +680,6 @@ message NewAddressRequest { enum AddressType { WITNESS_PUBKEY_HASH = 0; NESTED_PUBKEY_HASH = 1; - PUBKEY_HASH = 2; } /// The address type @@ -1099,8 +1097,6 @@ message PendingChannelsResponse { } message WalletBalanceRequest { - /// If only witness outputs should be considered when calculating the wallet's balance - bool witness_only = 1; } message WalletBalanceResponse { /// The balance of the wallet diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index 9eda8943..76797407 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -17,7 +17,7 @@ "paths": { "/v1/balance/blockchain": { "get": { - "summary": "* lncli: `walletbalance`\nWalletBalance returns total unspent outputs(confirmed and unconfirmed), all\nconfirmed unspent outputs and all unconfirmed unspent outputs under control\nby the wallet. This method can be modified by having the request specify\nonly witness outputs should be factored into the final output sum.", + "summary": "* lncli: `walletbalance`\nWalletBalance returns total unspent outputs(confirmed and unconfirmed), all\nconfirmed unspent outputs and all unconfirmed unspent outputs under control\nof the wallet.", "operationId": "WalletBalance", "responses": { "200": { @@ -27,16 +27,6 @@ } } }, - "parameters": [ - { - "name": "witness_only", - "description": "/ If only witness outputs should be considered when calculating the wallet's balance.", - "in": "query", - "required": false, - "type": "boolean", - "format": "boolean" - } - ], "tags": [ "Lightning" ] From b41222b3166469eb02d360d2c69d33e74fe56c9b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:38:06 -0800 Subject: [PATCH 20/29] rpc: update RPC server to disallow p2pkh adds, assume segwit for ConfirmedBalance --- rpcserver.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 2416a31c..663cad8b 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -463,8 +463,6 @@ func (r *rpcServer) NewAddress(ctx context.Context, addrType = lnwallet.WitnessPubKey case lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH: addrType = lnwallet.NestedWitnessPubKey - case lnrpc.NewAddressRequest_PUBKEY_HASH: - addrType = lnwallet.PubKeyHash } addr, err := r.server.cc.wallet.NewAddress(addrType, false) @@ -1264,13 +1262,13 @@ func (r *rpcServer) WalletBalance(ctx context.Context, in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) { // Get total balance, from txs that have >= 0 confirmations. - totalBal, err := r.server.cc.wallet.ConfirmedBalance(0, in.WitnessOnly) + totalBal, err := r.server.cc.wallet.ConfirmedBalance(0) if err != nil { return nil, err } // Get confirmed balance, from txs that have >= 1 confirmations. - confirmedBal, err := r.server.cc.wallet.ConfirmedBalance(1, in.WitnessOnly) + confirmedBal, err := r.server.cc.wallet.ConfirmedBalance(1) if err != nil { return nil, err } From 1669e3d5ec92d6fd6a01b59d91c96319cd8953f7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:40:10 -0800 Subject: [PATCH 21/29] lnd: use first key in keychain.KeyFamilyNodeKey as our nodeID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we update lnd to now access the deterministic keychain.KeyRing struct in order to obtain our identity public key. With this change, if the user recovers their wallet with the same seed, then they’ll have access to the same prior node identity. This change also makes it easy for us to support node key rotation in the future by bumping up our requested index. --- lnd.go | 14 +++++++++++--- mock.go | 3 +-- pilot.go | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lnd.go b/lnd.go index d88affda..0c269f65 100644 --- a/lnd.go +++ b/lnd.go @@ -37,6 +37,7 @@ import ( flags "github.com/jessevdk/go-flags" "github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" @@ -237,7 +238,13 @@ func lndMain() error { primaryChain := registeredChains.PrimaryChain() registeredChains.RegisterChain(primaryChain, activeChainControl) - idPrivKey, err := activeChainControl.wallet.GetIdentitykey() + // TODO(roasbeef): add rotation + idPrivKey, err := activeChainControl.wallet.DerivePrivKey(keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamilyNodeKey, + Index: 0, + }, + }) if err != nil { return err } @@ -252,8 +259,9 @@ func lndMain() error { // Set up the core server which will listen for incoming peer // connections. - server, err := newServer(cfg.Listeners, chanDB, activeChainControl, - idPrivKey) + server, err := newServer( + cfg.Listeners, chanDB, activeChainControl, idPrivKey, + ) if err != nil { srvrLog.Errorf("unable to create server: %v\n", err) return err diff --git a/mock.go b/mock.go index 2e47532c..5d2868d0 100644 --- a/mock.go +++ b/mock.go @@ -203,8 +203,7 @@ func (*mockWalletController) FetchInputInfo( } return txOut, nil } -func (*mockWalletController) ConfirmedBalance(confs int32, - witness bool) (btcutil.Amount, error) { +func (*mockWalletController) ConfirmedBalance(confs int32) (btcutil.Amount, error) { return 0, nil } diff --git a/pilot.go b/pilot.go index c82b0f17..b6eb3a78 100644 --- a/pilot.go +++ b/pilot.go @@ -146,7 +146,7 @@ func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error) Heuristic: prefAttachment, ChanController: &chanController{svr}, WalletBalance: func() (btcutil.Amount, error) { - return svr.cc.wallet.ConfirmedBalance(1, true) + return svr.cc.wallet.ConfirmedBalance(1) }, Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()), } From f2192f1b90f3b462b59dbb527af116bff6cbe123 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:41:34 -0800 Subject: [PATCH 22/29] cmd/lncli: remove p2pkh as an option for the new address command --- cmd/lncli/commands.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index 20936a44..201ec2d6 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -91,9 +91,8 @@ var newAddressCommand = cli.Command{ ArgsUsage: "address-type", Description: ` Generate a wallet new address. Address-types has to be one of: - - p2wkh: Push to witness key hash - - np2wkh: Push to nested witness key hash - - p2pkh: Push to public key hash (can't be used to fund channels)`, + - p2wkh: Pay to witness key hash + - np2wkh: Pay to nested witness key hash`, Action: actionDecorator(newAddress), } @@ -111,11 +110,9 @@ func newAddress(ctx *cli.Context) error { addrType = lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH case "np2wkh": addrType = lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH - case "p2pkh": - addrType = lnrpc.NewAddressRequest_PUBKEY_HASH default: return fmt.Errorf("invalid address type %v, support address type "+ - "are: p2wkh, np2wkh, p2pkh", stringAddrType) + "are: p2wkh and np2wkh", stringAddrType) } ctxb := context.Background() From 78ef09c9d35458fc7a84d5b60ccb4120d2b32d5b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:42:17 -0800 Subject: [PATCH 23/29] cmd/lncli: remove the --witness_only flag from walletbalance This flag is no longer needed, as the call will now only return the active segwit balance. --- cmd/lncli/commands.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index 201ec2d6..67123d36 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -1044,15 +1044,8 @@ func unlock(ctx *cli.Context) error { } var walletBalanceCommand = cli.Command{ - Name: "walletbalance", - Usage: "Compute and display the wallet's current balance", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "witness_only", - Usage: "if only witness outputs should be considered when " + - "calculating the wallet's balance", - }, - }, + Name: "walletbalance", + Usage: "Compute and display the wallet's current balance", Action: actionDecorator(walletBalance), } @@ -1061,9 +1054,7 @@ func walletBalance(ctx *cli.Context) error { client, cleanUp := getClient(ctx) defer cleanUp() - req := &lnrpc.WalletBalanceRequest{ - WitnessOnly: ctx.Bool("witness_only"), - } + req := &lnrpc.WalletBalanceRequest{} resp, err := client.WalletBalance(ctxb, req) if err != nil { return err From 9981c4ce38f20869a1d64fc3d4856670a4919d09 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 17:30:27 -0800 Subject: [PATCH 24/29] lnd: populate the secret key ring in the lnwallet config --- chainregistry.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chainregistry.go b/chainregistry.go index 909d12f7..ae35a901 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/routing/chainview" @@ -443,6 +444,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, WalletController: wc, Signer: cc.signer, FeeEstimator: cc.feeEstimator, + SecretKeyRing: keychain.NewBtcWalletKeyRing(wc.InternalWallet()), ChainIO: cc.chainIO, DefaultConstraints: defaultChannelConstraints, NetParams: *activeNetParams.Params, From 7edca37918ba8cdf41d43d86182b87a4565e8407 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 18:41:48 -0800 Subject: [PATCH 25/29] build: update to use latest btcwallet with scoped key managers --- glide.lock | 33 +++++++++++++++++++-------------- glide.yaml | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/glide.lock b/glide.lock index 2f8163cb..afe59531 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: ca248eaf0ddf2e5daab59a13c8a62df255721e11fd10b0169b608f9c3349ad58 -updated: 2018-03-04T21:24:12.266291-05:00 +hash: 75e7ae24914974ad34be24e5176b64e6d8cdc80aa65084714f1eb4d469b7830b +updated: 2018-03-06T13:12:10.827387022-05:00 imports: - name: git.schwanenlied.me/yawning/bsaes.git version: e06297f34865a50b8e473105e52cb64ad1b55da8 @@ -45,13 +45,13 @@ imports: - name: github.com/btcsuite/websocket version: 31079b6807923eb23992c421b114992b95131b55 - name: github.com/davecgh/go-spew - version: ecdeabc65495df2dec95d7c4a4c3e021903035e5 + version: 8991bc29aa16c548c550c7ff78260e27b9ab7c73 subpackages: - spew - name: github.com/go-errors/errors - version: 8fa88b06e5974e97fbf9899a7f86a344bfd1f105 + version: 3afebba5a48dbc89b574d890b6b34d9ee10b4785 - name: github.com/golang/protobuf - version: ab9f9a6dab164b7d1246e0e688b0ab7b94d8553e + version: bbd03ef6da3a115852eaf24c8a1c46aeb39aa175 subpackages: - jsonpb - proto @@ -93,9 +93,7 @@ imports: - chaincfg/chainhash - wire - name: github.com/miekg/dns - version: 946bd9fbed05568b0f3cd188353d8aa28f38b688 - subpackages: - - internal/socket + version: 5364553f1ee9cddc7ac8b62dce148309c386695b - name: github.com/roasbeef/btcd version: e6807bc4dd5ddbb95b4ab163f6dd61e4ad79463a subpackages: @@ -126,7 +124,7 @@ imports: - hdkeychain - txsort - name: github.com/roasbeef/btcwallet - version: 7447531cb6752ebf8680bc180e9a21c2c445ee7c + version: 97f2ef9c15d9a82a0e8b160df01157893f3853b5 subpackages: - chain - internal/helpers @@ -157,6 +155,8 @@ imports: - blake2b - chacha20poly1305 - curve25519 + - ed25519 + - ed25519/internal/edwards25519 - hkdf - internal/chacha20 - nacl/box @@ -169,30 +169,35 @@ imports: - scrypt - ssh/terminal - name: golang.org/x/net - version: 66aacef3dd8a676686c7ae3716979581e8b03c47 + version: cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb subpackages: + - bpf - context - http2 - http2/hpack - idna + - internal/iana + - internal/socket - internal/timeseries + - ipv4 + - ipv6 - lex/httplex - proxy - trace - name: golang.org/x/sys - version: ab9e364efd8b52800ff7ee48a9ffba4e0ed78dfb + version: 88d2dcc510266da9f7f8c7f34e1940716cab5f5c subpackages: - unix - windows - name: golang.org/x/text - version: 18c65dde6afd36dbc39197ca72008895b8dfbfb6 + version: 27420a1a391f5504f73155051cd274311bf70883 subpackages: - secure/bidirule - transform - unicode/bidi - unicode/norm - name: google.golang.org/genproto - version: ee236bd376b077c7a89f260c026c4735b195e459 + version: 2b5a72b8730b0b16380010cfe5286c42108d88e7 subpackages: - googleapis/api/annotations - googleapis/rpc/status @@ -216,7 +221,7 @@ imports: - name: gopkg.in/errgo.v1 version: 442357a80af5c6bf9b6d51ae791a39c3421004f3 - name: gopkg.in/macaroon-bakery.v2 - version: 04cf5ef151a211d929975161aea7c65f94c90446 + version: 22c04a94d902625448265ef041bb53e715452a40 subpackages: - bakery - bakery/checkers diff --git a/glide.yaml b/glide.yaml index 45e09a59..bcdbd18d 100644 --- a/glide.yaml +++ b/glide.yaml @@ -36,7 +36,7 @@ import: - hdkeychain - txsort - package: github.com/roasbeef/btcwallet - version: 7447531cb6752ebf8680bc180e9a21c2c445ee7c + version: 97f2ef9c15d9a82a0e8b160df01157893f3853b5 subpackages: - chain - waddrmgr From d98e7a730d575be614529e6eaa1bef3b424b3875 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 18:58:57 -0800 Subject: [PATCH 26/29] lnwallet: update integration tests due to recent API changes --- lnwallet/interface_test.go | 46 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 76b71629..d5fb64cd 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -102,7 +102,7 @@ var ( // within the wallet are *exactly* amount. If unable to retrieve the current // balance, or the assertion fails, the test will halt with a fatal error. func assertProperBalance(t *testing.T, lw *lnwallet.LightningWallet, numConfirms int32, amount int64) { - balance, err := lw.ConfirmedBalance(numConfirms, false) + balance, err := lw.ConfirmedBalance(numConfirms) if err != nil { t.Fatalf("unable to query for balance: %v", err) } @@ -160,7 +160,7 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet, // Using the mining node, spend from a coinbase output numOutputs to // give us btcPerOutput with each output. satoshiPerOutput := int64(btcPerOutput * 1e8) - expectedBalance, err := w.ConfirmedBalance(1, false) + expectedBalance, err := w.ConfirmedBalance(1) if err != nil { return err } @@ -203,7 +203,7 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet, timeout := time.After(30 * time.Second) for range ticker.C { - balance, err := w.ConfirmedBalance(1, false) + balance, err := w.ConfirmedBalance(1) if err != nil { return err } @@ -1154,11 +1154,13 @@ func testPublishTransaction(r *rpctest.Harness, } // Generate a pubkey, and pay-to-addr script. - pubKey, err := alice.NewRawKey() + pubKey, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { t.Fatalf("unable to obtain public key: %v", err) } - pubkeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + pubkeyHash := btcutil.Hash160(pubKey.PubKey.SerializeCompressed()) keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash, &chaincfg.RegressionNetParams) if err != nil { @@ -1214,7 +1216,9 @@ func testPublishTransaction(r *rpctest.Harness, // Now we can populate the sign descriptor which we'll use to // generate the signature. signDesc := &lnwallet.SignDescriptor{ - PubKey: pubKey, + KeyDesc: keychain.KeyDescriptor{ + PubKey: pubKey.PubKey, + }, WitnessScript: keyScript, Output: tx.TxOut[outputIndex], HashType: txscript.SigHashAll, @@ -1231,7 +1235,7 @@ func testPublishTransaction(r *rpctest.Harness, } witness := make([][]byte, 2) witness[0] = append(spendSig, byte(txscript.SigHashAll)) - witness[1] = pubKey.SerializeCompressed() + witness[1] = pubKey.PubKey.SerializeCompressed() tx1.TxIn[0].Witness = witness // Finally, attempt to validate the completed transaction. This @@ -1280,7 +1284,7 @@ func testPublishTransaction(r *rpctest.Harness, t.Fatalf("unable to mine tx: %v", err) } txFee := btcutil.Amount(0.1 * btcutil.SatoshiPerBitcoin) - tx1 := txFromOutput(tx.MsgTx(), pubKey, txFee) + tx1 := txFromOutput(tx.MsgTx(), pubKey.PubKey, txFee) return tx1 } @@ -1367,7 +1371,7 @@ func testPublishTransaction(r *rpctest.Harness, // from the tx just mined. This should be accepted // into the mempool. txFee := btcutil.Amount(0.05 * btcutil.SatoshiPerBitcoin) - tx4 := txFromOutput(tx3, pubKey, txFee) + tx4 := txFromOutput(tx3, pubKey.PubKey, txFee) if err := alice.PublishTransaction(tx4); err != nil { t.Fatalf("unable to publish: %v", err) } @@ -1380,7 +1384,9 @@ func testPublishTransaction(r *rpctest.Harness, // Create a new key we'll pay to, to ensure we create // a unique transaction. - pubKey2, err := alice.NewRawKey() + pubKey2, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { t.Fatalf("unable to obtain public key: %v", err) } @@ -1388,7 +1394,7 @@ func testPublishTransaction(r *rpctest.Harness, // Create a new transaction that spends the output from // tx3, and that pays to a different address. We expect // this to be rejected because it is a double spend. - tx5 := txFromOutput(tx3, pubKey2, txFee) + tx5 := txFromOutput(tx3, pubKey2.PubKey, txFee) if err := alice.PublishTransaction(tx5); err != lnwallet.ErrDoubleSpend { t.Fatalf("expected ErrDoubleSpend, got: %v", err) } @@ -1397,11 +1403,13 @@ func testPublishTransaction(r *rpctest.Harness, // but has a higher fee. We expect also this tx to be // rejected, since the sequence number of tx3 is set to Max, // indicating it is not replacable. - pubKey3, err := alice.NewRawKey() + pubKey3, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { t.Fatalf("unable to obtain public key: %v", err) } - tx6 := txFromOutput(tx3, pubKey3, 3*txFee) + tx6 := txFromOutput(tx3, pubKey3.PubKey, 3*txFee) // Expect rejection. if err := alice.PublishTransaction(tx6); err != lnwallet.ErrDoubleSpend { @@ -1422,11 +1430,13 @@ func testPublishTransaction(r *rpctest.Harness, } // Create another tx spending tx3. - pubKey4, err := alice.NewRawKey() + pubKey4, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { t.Fatalf("unable to obtain public key: %v", err) } - tx7 := txFromOutput(tx3, pubKey4, txFee) + tx7 := txFromOutput(tx3, pubKey4.PubKey, txFee) // Expect rejection. if err := alice.PublishTransaction(tx7); err != lnwallet.ErrDoubleSpend { @@ -1645,7 +1655,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet, } // Get the original balance. - origBalance, err := w.ConfirmedBalance(1, false) + origBalance, err := w.ConfirmedBalance(1) if err != nil { t.Fatalf("unable to query for balance: %v", err) } @@ -1661,7 +1671,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet, t.Fatalf("unable to set up mining node: %v", err) } defer r2.TearDown() - newBalance, err := w.ConfirmedBalance(1, false) + newBalance, err := w.ConfirmedBalance(1) if err != nil { t.Fatalf("unable to query for balance: %v", err) } @@ -1753,7 +1763,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet, } // Now we check that the wallet balance stays the same. - newBalance, err = w.ConfirmedBalance(1, false) + newBalance, err = w.ConfirmedBalance(1) if err != nil { t.Fatalf("unable to query for balance: %v", err) } From dc2404f28936a1224ec8813976e3bc0f11296b28 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 19 Feb 2018 19:13:03 -0800 Subject: [PATCH 27/29] contractcourt: update test due to recent SignDescriptor API changes --- contractcourt/briefcase_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contractcourt/briefcase_test.go b/contractcourt/briefcase_test.go index c8ae24b8..06ba2a52 100644 --- a/contractcourt/briefcase_test.go +++ b/contractcourt/briefcase_test.go @@ -796,7 +796,7 @@ func TestScopeIsolation(t *testing.T) { } func init() { - testSignDesc.PubKey, _ = btcec.ParsePubKey(key1, btcec.S256()) + testSignDesc.KeyDesc.PubKey, _ = btcec.ParsePubKey(key1, btcec.S256()) prand.Seed(time.Now().Unix()) } From 78cbe7a141d3e38a02c97ed84e1b5b6b6a7bb523 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 6 Mar 2018 11:55:10 -0500 Subject: [PATCH 28/29] lnd: verify proper keychain derivation version upon initial creation --- lnd.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lnd.go b/lnd.go index 0c269f65..9842b77d 100644 --- a/lnd.go +++ b/lnd.go @@ -842,6 +842,17 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string, password := initMsg.Passphrase cipherSeed := initMsg.WalletSeed + // Before we proceed, we'll check the internal version of the + // seed. If it's greater than the current key derivation + // version, then we'll return an error as we don't understand + // this. + if cipherSeed.InternalVersion != keychain.KeyDerivationVersion { + return nil, nil, fmt.Errorf("invalid internal seed "+ + "version %v, current version is %v", + cipherSeed.InternalVersion, + keychain.KeyDerivationVersion) + } + netDir := btcwallet.NetworkDir( chainConfig.ChainDir, activeNetParams.Params, ) @@ -850,9 +861,7 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string, // With the seed, we can now use the wallet loader to create // the wallet, then unload it so it can be opened shortly // after. - // // TODO(roasbeef): extend loader to also accept birthday - // * also check with keychain version _, err = loader.CreateNewWallet( password, password, cipherSeed.Entropy[:], ) From 18e9475a9a48877d742cb05c8602a0230c3a0605 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 6 Mar 2018 13:17:09 -0500 Subject: [PATCH 29/29] lnwallet/btcwallet: grab best header timestamp directly from wallet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we modify the way we obtain the current best header timestamp. In doing this, we fix an intermittent flake that would pop up at times on the integration tests. This could occur as if the wallet was lagging behind the chain backend for a re-org, then a hash that the backend knew of, may not be known by the wallet. To remedy this, we’ll take advantage of a recent change to btcwallet to actually include the timestamp in its sync state. --- lnwallet/btcwallet/btcwallet.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 9554f6f7..ee2674f0 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -700,23 +700,16 @@ func (b *BtcWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, e // // This is a part of the WalletController interface. func (b *BtcWallet) IsSynced() (bool, int64, error) { - // Grab the best chain state the wallet is currently aware of. We'll - // also grab the timestamp of the best block to return for determining - // sync progress. + // Grab the best chain state the wallet is currently aware of. syncState := b.wallet.Manager.SyncedTo() - walletBestHeader, err := b.chain.GetBlockHeader(&syncState.Hash) - if err != nil { - return false, 0, err - } - var ( - bestHash *chainhash.Hash - bestHeight int32 - ) + // We'll also extract the current best wallet timestamp so the caller + // can get an idea of where we are in the sync timeline. + bestTimestamp := syncState.Timestamp.Unix() // Next, query the chain backend to grab the info about the tip of the // main chain. - bestHash, bestHeight, err = b.cfg.ChainSource.GetBestBlock() + bestHash, bestHeight, err := b.cfg.ChainSource.GetBestBlock() if err != nil { return false, 0, err } @@ -724,7 +717,7 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) { // If the wallet hasn't yet fully synced to the node's best chain tip, // then we're not yet fully synced. if syncState.Height < bestHeight { - return false, walletBestHeader.Timestamp.Unix(), nil + return false, bestTimestamp, nil } // If the wallet is on par with the current best chain tip, then we @@ -739,5 +732,5 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) { // If the timestamp no the best header is more than 2 hours in the // past, then we're not yet synced. minus24Hours := time.Now().Add(-2 * time.Hour) - return !blockHeader.Timestamp.Before(minus24Hours), walletBestHeader.Timestamp.Unix(), nil + return !blockHeader.Timestamp.Before(minus24Hours), bestTimestamp, nil }