Merge pull request #769 from Roasbeef/new-lightning-key-derivation

multi: modify key derivation to be fully deterministic, remove p2pkh, wallet now witness only
This commit is contained in:
Olaoluwa Osuntokun 2018-03-06 17:21:55 -05:00 committed by GitHub
commit 116406c7ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1936 additions and 995 deletions

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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()))

View File

@ -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

View File

@ -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()
@ -1047,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),
}
@ -1064,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

View File

@ -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())
}

View File

@ -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,
)
}

View File

@ -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"
@ -955,12 +956,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)
@ -988,11 +999,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)
@ -1069,11 +1080,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)
@ -1832,10 +1853,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 "+
@ -1941,10 +1963,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)
@ -2454,11 +2477,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,
}

View File

@ -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)
}

33
glide.lock generated
View File

@ -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

View File

@ -36,7 +36,7 @@ import:
- hdkeychain
- txsort
- package: github.com/roasbeef/btcwallet
version: 7447531cb6752ebf8680bc180e9a21c2c445ee7c
version: 97f2ef9c15d9a82a0e8b160df01157893f3853b5
subpackages:
- chain
- waddrmgr

View File

@ -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")
}

View File

@ -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

288
keychain/btcwallet.go Normal file
View File

@ -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
}

159
keychain/derivation.go Normal file
View File

@ -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 seed.
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

255
keychain/interface_test.go Normal file
View File

@ -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
}
}
}

27
lnd.go
View File

@ -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
@ -834,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,
)
@ -842,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[:],
)

View File

@ -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,
}

View File

@ -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

View File

@ -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

View File

@ -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"
]

View File

@ -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
}
@ -188,25 +206,16 @@ 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
}
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
for _, witnessOutput := range witnessOutputs {
balance += witnessOutput.Value
}
return balance, nil
@ -219,24 +228,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
@ -250,78 +257,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.
@ -765,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
}
@ -789,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
@ -804,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
}

View File

@ -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
}

View File

@ -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

View File

@ -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")
}

View File

@ -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)

View File

@ -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

View File

@ -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
@ -143,20 +144,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

View File

@ -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"
@ -101,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)
}
@ -159,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
}
@ -202,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
}
@ -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 {
@ -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 {
@ -1449,7 +1459,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 +1473,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 +1551,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 +1598,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
@ -1640,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)
}
@ -1656,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)
}
@ -1748,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)
}
@ -1966,6 +1981,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 +2026,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 +2086,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 +2155,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 +2172,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)
}

View File

@ -7,11 +7,9 @@ import (
"fmt"
"math/big"
"golang.org/x/crypto/hkdf"
"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 +310,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 +567,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 +1035,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: <sig> <pubkey>
sweepSig, err := signer.SignOutputRaw(sweepTx, signDesc)
@ -1037,7 +1054,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
@ -1218,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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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),

View File

@ -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
@ -570,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
@ -750,8 +722,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 +824,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 +869,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 +1012,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 +1043,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 +1145,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 +1172,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 +1197,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 +1216,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
}
signDesc := SignDescriptor{
WitnessScript: witnessScript,
PubKey: ourKey,
KeyDesc: ourKey,
Output: &wire.TxOut{
PkScript: p2wsh,
Value: channelValue,
@ -1349,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:
@ -1437,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)
@ -1449,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

41
mock.go
View File

@ -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")
}
@ -202,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
}
@ -218,16 +218,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 +262,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

View File

@ -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()),
}

View File

@ -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
}

View File

@ -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

View File

@ -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 {