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/channeldb"
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
@ -405,7 +406,7 @@ func initBreachedOutputs() error {
return fmt.Errorf("unable to parse pubkey: %v", return fmt.Errorf("unable to parse pubkey: %v",
breachKeys[i]) breachKeys[i])
} }
bo.signDesc.PubKey = pubkey bo.signDesc.KeyDesc.PubKey = pubkey
} }
return nil return nil
@ -1272,12 +1273,22 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
MinHTLC: 0, MinHTLC: 0,
MaxAcceptedHtlcs: uint16(rand.Int31()), MaxAcceptedHtlcs: uint16(rand.Int31()),
}, },
CsvDelay: uint16(csvTimeoutAlice), CsvDelay: uint16(csvTimeoutAlice),
MultiSigKey: aliceKeyPub, MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: aliceKeyPub, PubKey: aliceKeyPub,
PaymentBasePoint: aliceKeyPub, },
DelayBasePoint: aliceKeyPub, RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: aliceKeyPub, PubKey: aliceKeyPub,
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
} }
bobCfg := channeldb.ChannelConfig{ bobCfg := channeldb.ChannelConfig{
ChannelConstraints: channeldb.ChannelConstraints{ ChannelConstraints: channeldb.ChannelConstraints{
@ -1287,24 +1298,40 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
MinHTLC: 0, MinHTLC: 0,
MaxAcceptedHtlcs: uint16(rand.Int31()), MaxAcceptedHtlcs: uint16(rand.Int31()),
}, },
CsvDelay: uint16(csvTimeoutBob), CsvDelay: uint16(csvTimeoutBob),
MultiSigKey: bobKeyPub, MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: bobKeyPub, PubKey: bobKeyPub,
PaymentBasePoint: bobKeyPub, },
DelayBasePoint: bobKeyPub, RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: bobKeyPub, PubKey: bobKeyPub,
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
} }
bobRoot := lnwallet.DeriveRevocationRoot(bobKeyPriv, testHdSeed, aliceKeyPub) bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) if err != nil {
return nil, nil, nil, err
}
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:]) bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:])
aliceRoot := lnwallet.DeriveRevocationRoot(aliceKeyPriv, testHdSeed, bobKeyPub) aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) if err != nil {
return nil, nil, nil, err
}
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

View File

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify" "github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
@ -443,6 +444,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
WalletController: wc, WalletController: wc,
Signer: cc.signer, Signer: cc.signer,
FeeEstimator: cc.feeEstimator, FeeEstimator: cc.feeEstimator,
SecretKeyRing: keychain.NewBtcWalletKeyRing(wc.InternalWallet()),
ChainIO: cc.chainIO, ChainIO: cc.chainIO,
DefaultConstraints: defaultChannelConstraints, DefaultConstraints: defaultChannelConstraints,
NetParams: *activeNetParams.Params, NetParams: *activeNetParams.Params,

View File

@ -9,6 +9,7 @@ import (
"sync" "sync"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
"github.com/roasbeef/btcd/btcec" "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 // MultiSigKey is the key to be used within the 2-of-2 output script
// for the owner of this channel config. // for the owner of this channel config.
MultiSigKey *btcec.PublicKey MultiSigKey keychain.KeyDescriptor
// RevocationBasePoint is the base public key to be used when deriving // RevocationBasePoint is the base public key to be used when deriving
// revocation keys for the remote node's commitment transaction. This // revocation keys for the remote node's commitment transaction. This
// will be combined along with a per commitment secret to derive a // will be combined along with a per commitment secret to derive a
// unique revocation key for each state. // unique revocation key for each state.
RevocationBasePoint *btcec.PublicKey RevocationBasePoint keychain.KeyDescriptor
// PaymentBasePoint is the base public key to be used when deriving // PaymentBasePoint is the base public key to be used when deriving
// the key used within the non-delayed pay-to-self output on the // the key used within the non-delayed pay-to-self output on the
// commitment transaction for a node. This will be combined with a // commitment transaction for a node. This will be combined with a
// tweak derived from the per-commitment point to ensure unique keys // tweak derived from the per-commitment point to ensure unique keys
// for each commitment transaction. // for each commitment transaction.
PaymentBasePoint *btcec.PublicKey PaymentBasePoint keychain.KeyDescriptor
// DelayBasePoint is the base public key to be used when deriving the // DelayBasePoint is the base public key to be used when deriving the
// key used within the delayed pay-to-self output on the commitment // key used within the delayed pay-to-self output on the commitment
// transaction for a node. This will be combined with a tweak derived // transaction for a node. This will be combined with a tweak derived
// from the per-commitment point to ensure unique keys for each // from the per-commitment point to ensure unique keys for each
// commitment transaction. // commitment transaction.
DelayBasePoint *btcec.PublicKey DelayBasePoint keychain.KeyDescriptor
// HtlcBasePoint is the base public key to be used when deriving the // HtlcBasePoint is the base public key to be used when deriving the
// local HTLC key. The derived key (combined with the tweak derived // local HTLC key. The derived key (combined with the tweak derived
// from the per-commitment point) is used within the "to self" clause // from the per-commitment point) is used within the "to self" clause
// within any HTLC output scripts. // within any HTLC output scripts.
HtlcBasePoint *btcec.PublicKey HtlcBasePoint keychain.KeyDescriptor
} }
// ChannelCommitment is a snapshot of the commitment state at a particular // ChannelCommitment is a snapshot of the commitment state at a particular

View File

@ -11,6 +11,7 @@ import (
"testing" "testing"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
@ -136,12 +137,22 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) {
MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MinHTLC: lnwire.MilliSatoshi(rand.Int63()),
MaxAcceptedHtlcs: uint16(rand.Int31()), MaxAcceptedHtlcs: uint16(rand.Int31()),
}, },
CsvDelay: uint16(rand.Int31()), CsvDelay: uint16(rand.Int31()),
MultiSigKey: privKey.PubKey(), MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: privKey.PubKey(), PubKey: privKey.PubKey(),
PaymentBasePoint: privKey.PubKey(), },
DelayBasePoint: privKey.PubKey(), RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: privKey.PubKey(), PubKey: privKey.PubKey(),
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: privKey.PubKey(),
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: privKey.PubKey(),
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: privKey.PubKey(),
},
} }
remoteCfg := ChannelConfig{ remoteCfg := ChannelConfig{
ChannelConstraints: ChannelConstraints{ ChannelConstraints: ChannelConstraints{
@ -151,12 +162,22 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) {
MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MinHTLC: lnwire.MilliSatoshi(rand.Int63()),
MaxAcceptedHtlcs: uint16(rand.Int31()), MaxAcceptedHtlcs: uint16(rand.Int31()),
}, },
CsvDelay: uint16(rand.Int31()), CsvDelay: uint16(rand.Int31()),
MultiSigKey: privKey.PubKey(), MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: privKey.PubKey(), PubKey: privKey.PubKey(),
PaymentBasePoint: privKey.PubKey(), },
DelayBasePoint: privKey.PubKey(), RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: privKey.PubKey(), 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())) chanID := lnwire.NewShortChanIDFromInt(uint64(rand.Int63()))

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
"github.com/roasbeef/btcd/btcec" "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. // to dynamically expand to accommodate additional data.
func writeElement(w io.Writer, element interface{}) error { func writeElement(w io.Writer, element interface{}) error {
switch e := element.(type) { 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: case ChannelType:
if err := binary.Write(w, byteOrder, e); err != nil { if err := binary.Write(w, byteOrder, e); err != nil {
return err return err
@ -163,6 +180,23 @@ func writeElements(w io.Writer, elements ...interface{}) error {
// encoded using the serialization format of the database. // encoded using the serialization format of the database.
func readElement(r io.Reader, element interface{}) error { func readElement(r io.Reader, element interface{}) error {
switch e := element.(type) { 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: case *ChannelType:
if err := binary.Read(r, byteOrder, e); err != nil { if err := binary.Read(r, byteOrder, e); err != nil {
return err return err

View File

@ -91,9 +91,8 @@ var newAddressCommand = cli.Command{
ArgsUsage: "address-type", ArgsUsage: "address-type",
Description: ` Description: `
Generate a wallet new address. Address-types has to be one of: Generate a wallet new address. Address-types has to be one of:
- p2wkh: Push to witness key hash - p2wkh: Pay to witness key hash
- np2wkh: Push to nested witness key hash - np2wkh: Pay to nested witness key hash`,
- p2pkh: Push to public key hash (can't be used to fund channels)`,
Action: actionDecorator(newAddress), Action: actionDecorator(newAddress),
} }
@ -111,11 +110,9 @@ func newAddress(ctx *cli.Context) error {
addrType = lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH addrType = lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH
case "np2wkh": case "np2wkh":
addrType = lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH addrType = lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH
case "p2pkh":
addrType = lnrpc.NewAddressRequest_PUBKEY_HASH
default: default:
return fmt.Errorf("invalid address type %v, support address type "+ return fmt.Errorf("invalid address type %v, support address type "+
"are: p2wkh, np2wkh, p2pkh", stringAddrType) "are: p2wkh and np2wkh", stringAddrType)
} }
ctxb := context.Background() ctxb := context.Background()
@ -1047,15 +1044,8 @@ func unlock(ctx *cli.Context) error {
} }
var walletBalanceCommand = cli.Command{ var walletBalanceCommand = cli.Command{
Name: "walletbalance", Name: "walletbalance",
Usage: "Compute and display the wallet's current balance", 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",
},
},
Action: actionDecorator(walletBalance), Action: actionDecorator(walletBalance),
} }
@ -1064,9 +1054,7 @@ func walletBalance(ctx *cli.Context) error {
client, cleanUp := getClient(ctx) client, cleanUp := getClient(ctx)
defer cleanUp() defer cleanUp()
req := &lnrpc.WalletBalanceRequest{ req := &lnrpc.WalletBalanceRequest{}
WitnessOnly: ctx.Bool("witness_only"),
}
resp, err := client.WalletBalance(ctxb, req) resp, err := client.WalletBalance(ctxb, req)
if err != nil { if err != nil {
return err return err

View File

@ -796,7 +796,7 @@ func TestScopeIsolation(t *testing.T) {
} }
func init() { func init() {
testSignDesc.PubKey, _ = btcec.ParsePubKey(key1, btcec.S256()) testSignDesc.KeyDesc.PubKey, _ = btcec.ParsePubKey(key1, btcec.S256())
prand.Seed(time.Now().Unix()) prand.Seed(time.Now().Unix())
} }

View File

@ -133,13 +133,13 @@ func newChainWatcher(chanState *channeldb.OpenChannel,
var stateHint [lnwallet.StateHintSize]byte var stateHint [lnwallet.StateHintSize]byte
if chanState.IsInitiator { if chanState.IsInitiator {
stateHint = lnwallet.DeriveStateHintObfuscator( stateHint = lnwallet.DeriveStateHintObfuscator(
chanState.LocalChanCfg.PaymentBasePoint, chanState.LocalChanCfg.PaymentBasePoint.PubKey,
chanState.RemoteChanCfg.PaymentBasePoint, chanState.RemoteChanCfg.PaymentBasePoint.PubKey,
) )
} else { } else {
stateHint = lnwallet.DeriveStateHintObfuscator( stateHint = lnwallet.DeriveStateHintObfuscator(
chanState.RemoteChanCfg.PaymentBasePoint, chanState.RemoteChanCfg.PaymentBasePoint.PubKey,
chanState.LocalChanCfg.PaymentBasePoint, chanState.LocalChanCfg.PaymentBasePoint.PubKey,
) )
} }

View File

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -955,12 +956,22 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
MinHTLC: msg.HtlcMinimum, MinHTLC: msg.HtlcMinimum,
MaxAcceptedHtlcs: maxHtlcs, MaxAcceptedHtlcs: maxHtlcs,
}, },
CsvDelay: remoteCsvDelay, CsvDelay: remoteCsvDelay,
MultiSigKey: copyPubKey(msg.FundingKey), MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: copyPubKey(msg.RevocationPoint), PubKey: copyPubKey(msg.FundingKey),
PaymentBasePoint: copyPubKey(msg.PaymentPoint), },
DelayBasePoint: copyPubKey(msg.DelayedPaymentPoint), RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: copyPubKey(msg.HtlcPoint), 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) err = reservation.ProcessSingleContribution(remoteContribution)
@ -988,11 +999,11 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
HtlcMinimum: ourContribution.MinHTLC, HtlcMinimum: ourContribution.MinHTLC,
CsvDelay: uint16(remoteCsvDelay), CsvDelay: uint16(remoteCsvDelay),
MaxAcceptedHTLCs: maxHtlcs, MaxAcceptedHTLCs: maxHtlcs,
FundingKey: ourContribution.MultiSigKey, FundingKey: ourContribution.MultiSigKey.PubKey,
RevocationPoint: ourContribution.RevocationBasePoint, RevocationPoint: ourContribution.RevocationBasePoint.PubKey,
PaymentPoint: ourContribution.PaymentBasePoint, PaymentPoint: ourContribution.PaymentBasePoint.PubKey,
DelayedPaymentPoint: ourContribution.DelayBasePoint, DelayedPaymentPoint: ourContribution.DelayBasePoint.PubKey,
HtlcPoint: ourContribution.HtlcBasePoint, HtlcPoint: ourContribution.HtlcBasePoint.PubKey,
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint, FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
} }
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, &fundingAccept) err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, &fundingAccept)
@ -1069,11 +1080,21 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
MinHTLC: msg.HtlcMinimum, MinHTLC: msg.HtlcMinimum,
MaxAcceptedHtlcs: maxHtlcs, MaxAcceptedHtlcs: maxHtlcs,
}, },
MultiSigKey: copyPubKey(msg.FundingKey), MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: copyPubKey(msg.RevocationPoint), PubKey: copyPubKey(msg.FundingKey),
PaymentBasePoint: copyPubKey(msg.PaymentPoint), },
DelayBasePoint: copyPubKey(msg.DelayedPaymentPoint), RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: copyPubKey(msg.HtlcPoint), 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) 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. // will be the one that's carrying the HTLC towards us.
remoteMinHTLC := completeChan.RemoteChanCfg.MinHTLC remoteMinHTLC := completeChan.RemoteChanCfg.MinHTLC
ann, err := f.newChanAnnouncement(f.cfg.IDKey, completeChan.IdentityPub, ann, err := f.newChanAnnouncement(
completeChan.LocalChanCfg.MultiSigKey, f.cfg.IDKey, completeChan.IdentityPub,
completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID, completeChan.LocalChanCfg.MultiSigKey.PubKey,
remoteMinHTLC, completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID,
chanID, remoteMinHTLC,
) )
if err != nil { if err != nil {
return fmt.Errorf("error generating channel "+ 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 // Create and broadcast the proofs required to make this channel
// public and usable for other nodes for routing. // public and usable for other nodes for routing.
err = f.announceChannel(f.cfg.IDKey, completeChan.IdentityPub, err = f.announceChannel(
completeChan.LocalChanCfg.MultiSigKey, f.cfg.IDKey, completeChan.IdentityPub,
completeChan.RemoteChanCfg.MultiSigKey, *shortChanID, chanID, completeChan.LocalChanCfg.MultiSigKey.PubKey,
remoteMinHTLC, completeChan.RemoteChanCfg.MultiSigKey.PubKey,
*shortChanID, chanID, remoteMinHTLC,
) )
if err != nil { if err != nil {
return fmt.Errorf("channel announcement failed: %v", err) return fmt.Errorf("channel announcement failed: %v", err)
@ -2454,11 +2477,11 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
FeePerKiloWeight: uint32(commitFeePerKw), FeePerKiloWeight: uint32(commitFeePerKw),
CsvDelay: uint16(remoteCsvDelay), CsvDelay: uint16(remoteCsvDelay),
MaxAcceptedHTLCs: maxHtlcs, MaxAcceptedHTLCs: maxHtlcs,
FundingKey: ourContribution.MultiSigKey, FundingKey: ourContribution.MultiSigKey.PubKey,
RevocationPoint: ourContribution.RevocationBasePoint, RevocationPoint: ourContribution.RevocationBasePoint.PubKey,
PaymentPoint: ourContribution.PaymentBasePoint, PaymentPoint: ourContribution.PaymentBasePoint.PubKey,
HtlcPoint: ourContribution.HtlcBasePoint, HtlcPoint: ourContribution.HtlcBasePoint.PubKey,
DelayedPaymentPoint: ourContribution.DelayBasePoint, DelayedPaymentPoint: ourContribution.DelayBasePoint.PubKey,
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint, FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
ChannelFlags: channelFlags, ChannelFlags: channelFlags,
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -150,12 +151,14 @@ func init() {
func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params, func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params,
notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController, 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) { estimator lnwallet.FeeEstimator) (*lnwallet.LightningWallet, error) {
wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{ wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{
Database: cdb, Database: cdb,
Notifier: notifier, Notifier: notifier,
SecretKeyRing: keyRing,
WalletController: wc, WalletController: wc,
Signer: signer, Signer: signer,
ChainIO: bio, ChainIO: bio,
@ -212,8 +215,14 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
return nil, err return nil, err
} }
lnw, err := createTestWallet(cdb, netParams, keyRing := &mockSecretKeyRing{
chainNotifier, wc, signer, bio, estimator) rootKey: alicePrivKey,
}
lnw, err := createTestWallet(
cdb, netParams, chainNotifier, wc, signer, keyRing, bio,
estimator,
)
if err != nil { if err != nil {
t.Fatalf("unable to create test ln wallet: %v", err) t.Fatalf("unable to create test ln wallet: %v", err)
} }

33
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: ca248eaf0ddf2e5daab59a13c8a62df255721e11fd10b0169b608f9c3349ad58 hash: 75e7ae24914974ad34be24e5176b64e6d8cdc80aa65084714f1eb4d469b7830b
updated: 2018-03-04T21:24:12.266291-05:00 updated: 2018-03-06T13:12:10.827387022-05:00
imports: imports:
- name: git.schwanenlied.me/yawning/bsaes.git - name: git.schwanenlied.me/yawning/bsaes.git
version: e06297f34865a50b8e473105e52cb64ad1b55da8 version: e06297f34865a50b8e473105e52cb64ad1b55da8
@ -45,13 +45,13 @@ imports:
- name: github.com/btcsuite/websocket - name: github.com/btcsuite/websocket
version: 31079b6807923eb23992c421b114992b95131b55 version: 31079b6807923eb23992c421b114992b95131b55
- name: github.com/davecgh/go-spew - name: github.com/davecgh/go-spew
version: ecdeabc65495df2dec95d7c4a4c3e021903035e5 version: 8991bc29aa16c548c550c7ff78260e27b9ab7c73
subpackages: subpackages:
- spew - spew
- name: github.com/go-errors/errors - name: github.com/go-errors/errors
version: 8fa88b06e5974e97fbf9899a7f86a344bfd1f105 version: 3afebba5a48dbc89b574d890b6b34d9ee10b4785
- name: github.com/golang/protobuf - name: github.com/golang/protobuf
version: ab9f9a6dab164b7d1246e0e688b0ab7b94d8553e version: bbd03ef6da3a115852eaf24c8a1c46aeb39aa175
subpackages: subpackages:
- jsonpb - jsonpb
- proto - proto
@ -93,9 +93,7 @@ imports:
- chaincfg/chainhash - chaincfg/chainhash
- wire - wire
- name: github.com/miekg/dns - name: github.com/miekg/dns
version: 946bd9fbed05568b0f3cd188353d8aa28f38b688 version: 5364553f1ee9cddc7ac8b62dce148309c386695b
subpackages:
- internal/socket
- name: github.com/roasbeef/btcd - name: github.com/roasbeef/btcd
version: e6807bc4dd5ddbb95b4ab163f6dd61e4ad79463a version: e6807bc4dd5ddbb95b4ab163f6dd61e4ad79463a
subpackages: subpackages:
@ -126,7 +124,7 @@ imports:
- hdkeychain - hdkeychain
- txsort - txsort
- name: github.com/roasbeef/btcwallet - name: github.com/roasbeef/btcwallet
version: 7447531cb6752ebf8680bc180e9a21c2c445ee7c version: 97f2ef9c15d9a82a0e8b160df01157893f3853b5
subpackages: subpackages:
- chain - chain
- internal/helpers - internal/helpers
@ -157,6 +155,8 @@ imports:
- blake2b - blake2b
- chacha20poly1305 - chacha20poly1305
- curve25519 - curve25519
- ed25519
- ed25519/internal/edwards25519
- hkdf - hkdf
- internal/chacha20 - internal/chacha20
- nacl/box - nacl/box
@ -169,30 +169,35 @@ imports:
- scrypt - scrypt
- ssh/terminal - ssh/terminal
- name: golang.org/x/net - name: golang.org/x/net
version: 66aacef3dd8a676686c7ae3716979581e8b03c47 version: cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb
subpackages: subpackages:
- bpf
- context - context
- http2 - http2
- http2/hpack - http2/hpack
- idna - idna
- internal/iana
- internal/socket
- internal/timeseries - internal/timeseries
- ipv4
- ipv6
- lex/httplex - lex/httplex
- proxy - proxy
- trace - trace
- name: golang.org/x/sys - name: golang.org/x/sys
version: ab9e364efd8b52800ff7ee48a9ffba4e0ed78dfb version: 88d2dcc510266da9f7f8c7f34e1940716cab5f5c
subpackages: subpackages:
- unix - unix
- windows - windows
- name: golang.org/x/text - name: golang.org/x/text
version: 18c65dde6afd36dbc39197ca72008895b8dfbfb6 version: 27420a1a391f5504f73155051cd274311bf70883
subpackages: subpackages:
- secure/bidirule - secure/bidirule
- transform - transform
- unicode/bidi - unicode/bidi
- unicode/norm - unicode/norm
- name: google.golang.org/genproto - name: google.golang.org/genproto
version: ee236bd376b077c7a89f260c026c4735b195e459 version: 2b5a72b8730b0b16380010cfe5286c42108d88e7
subpackages: subpackages:
- googleapis/api/annotations - googleapis/api/annotations
- googleapis/rpc/status - googleapis/rpc/status
@ -216,7 +221,7 @@ imports:
- name: gopkg.in/errgo.v1 - name: gopkg.in/errgo.v1
version: 442357a80af5c6bf9b6d51ae791a39c3421004f3 version: 442357a80af5c6bf9b6d51ae791a39c3421004f3
- name: gopkg.in/macaroon-bakery.v2 - name: gopkg.in/macaroon-bakery.v2
version: 04cf5ef151a211d929975161aea7c65f94c90446 version: 22c04a94d902625448265ef041bb53e715452a40
subpackages: subpackages:
- bakery - bakery
- bakery/checkers - bakery/checkers

View File

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

View File

@ -557,7 +557,7 @@ func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *lnwallet.SignDescri
witnessScript := signDesc.WitnessScript witnessScript := signDesc.WitnessScript
privKey := m.key privKey := m.key
if !privKey.PubKey().IsEqual(signDesc.PubKey) { if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) {
return nil, fmt.Errorf("incorrect key passed") return nil, fmt.Errorf("incorrect key passed")
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
@ -132,34 +133,60 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
fundingTxIn := wire.NewTxIn(prevOut, nil, nil) fundingTxIn := wire.NewTxIn(prevOut, nil, nil)
aliceCfg := channeldb.ChannelConfig{ aliceCfg := channeldb.ChannelConfig{
ChannelConstraints: *aliceConstraints, ChannelConstraints: *aliceConstraints,
CsvDelay: uint16(csvTimeoutAlice), CsvDelay: uint16(csvTimeoutAlice),
MultiSigKey: aliceKeyPub, MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: aliceKeyPub, PubKey: aliceKeyPub,
PaymentBasePoint: aliceKeyPub, },
DelayBasePoint: aliceKeyPub, RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: aliceKeyPub, PubKey: aliceKeyPub,
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
} }
bobCfg := channeldb.ChannelConfig{ bobCfg := channeldb.ChannelConfig{
ChannelConstraints: *bobConstraints, ChannelConstraints: *bobConstraints,
CsvDelay: uint16(csvTimeoutBob), CsvDelay: uint16(csvTimeoutBob),
MultiSigKey: bobKeyPub, MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: bobKeyPub, PubKey: bobKeyPub,
PaymentBasePoint: bobKeyPub, },
DelayBasePoint: bobKeyPub, RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: bobKeyPub, PubKey: bobKeyPub,
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
} }
bobRoot := lnwallet.DeriveRevocationRoot(bobKeyPriv, hash, aliceKeyPub) bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) if err != nil {
return nil, nil, nil, nil, err
}
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:]) bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:])
aliceRoot := lnwallet.DeriveRevocationRoot(aliceKeyPriv, hash, bobKeyPub) aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) if err != nil {
return nil, nil, nil, nil, err
}
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, nil, err 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" flags "github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
@ -237,7 +238,13 @@ func lndMain() error {
primaryChain := registeredChains.PrimaryChain() primaryChain := registeredChains.PrimaryChain()
registeredChains.RegisterChain(primaryChain, activeChainControl) 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 { if err != nil {
return err return err
} }
@ -252,8 +259,9 @@ func lndMain() error {
// Set up the core server which will listen for incoming peer // Set up the core server which will listen for incoming peer
// connections. // connections.
server, err := newServer(cfg.Listeners, chanDB, activeChainControl, server, err := newServer(
idPrivKey) cfg.Listeners, chanDB, activeChainControl, idPrivKey,
)
if err != nil { if err != nil {
srvrLog.Errorf("unable to create server: %v\n", err) srvrLog.Errorf("unable to create server: %v\n", err)
return err return err
@ -834,6 +842,17 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
password := initMsg.Passphrase password := initMsg.Passphrase
cipherSeed := initMsg.WalletSeed 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( netDir := btcwallet.NetworkDir(
chainConfig.ChainDir, activeNetParams.Params, 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 // With the seed, we can now use the wallet loader to create
// the wallet, then unload it so it can be opened shortly // the wallet, then unload it so it can be opened shortly
// after. // after.
//
// TODO(roasbeef): extend loader to also accept birthday // TODO(roasbeef): extend loader to also accept birthday
// * also check with keychain version
_, err = loader.CreateNewWallet( _, err = loader.CreateNewWallet(
password, password, cipherSeed.Entropy[:], password, password, cipherSeed.Entropy[:],
) )

View File

@ -134,18 +134,15 @@ type NewAddressRequest_AddressType int32
const ( const (
NewAddressRequest_WITNESS_PUBKEY_HASH NewAddressRequest_AddressType = 0 NewAddressRequest_WITNESS_PUBKEY_HASH NewAddressRequest_AddressType = 0
NewAddressRequest_NESTED_PUBKEY_HASH NewAddressRequest_AddressType = 1 NewAddressRequest_NESTED_PUBKEY_HASH NewAddressRequest_AddressType = 1
NewAddressRequest_PUBKEY_HASH NewAddressRequest_AddressType = 2
) )
var NewAddressRequest_AddressType_name = map[int32]string{ var NewAddressRequest_AddressType_name = map[int32]string{
0: "WITNESS_PUBKEY_HASH", 0: "WITNESS_PUBKEY_HASH",
1: "NESTED_PUBKEY_HASH", 1: "NESTED_PUBKEY_HASH",
2: "PUBKEY_HASH",
} }
var NewAddressRequest_AddressType_value = map[string]int32{ var NewAddressRequest_AddressType_value = map[string]int32{
"WITNESS_PUBKEY_HASH": 0, "WITNESS_PUBKEY_HASH": 0,
"NESTED_PUBKEY_HASH": 1, "NESTED_PUBKEY_HASH": 1,
"PUBKEY_HASH": 2,
} }
func (x NewAddressRequest_AddressType) String() string { func (x NewAddressRequest_AddressType) String() string {
@ -2300,8 +2297,6 @@ func (m *PendingChannelsResponse_ForceClosedChannel) GetPendingHtlcs() []*Pendin
} }
type WalletBalanceRequest struct { 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{} } func (m *WalletBalanceRequest) Reset() { *m = WalletBalanceRequest{} }
@ -2309,13 +2304,6 @@ func (m *WalletBalanceRequest) String() string { return proto.Compact
func (*WalletBalanceRequest) ProtoMessage() {} func (*WalletBalanceRequest) ProtoMessage() {}
func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} } 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 { type WalletBalanceResponse struct {
// / The balance of the wallet // / The balance of the wallet
TotalBalance int64 `protobuf:"varint,1,opt,name=total_balance" json:"total_balance,omitempty"` TotalBalance int64 `protobuf:"varint,1,opt,name=total_balance" json:"total_balance,omitempty"`
@ -4294,8 +4282,7 @@ type LightningClient interface {
// * lncli: `walletbalance` // * lncli: `walletbalance`
// WalletBalance returns total unspent outputs(confirmed and unconfirmed), all // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all
// confirmed unspent outputs and all unconfirmed unspent outputs under control // confirmed unspent outputs and all unconfirmed unspent outputs under control
// by the wallet. This method can be modified by having the request specify // of the wallet.
// only witness outputs should be factored into the final output sum.
WalletBalance(ctx context.Context, in *WalletBalanceRequest, opts ...grpc.CallOption) (*WalletBalanceResponse, error) WalletBalance(ctx context.Context, in *WalletBalanceRequest, opts ...grpc.CallOption) (*WalletBalanceResponse, error)
// * lncli: `channelbalance` // * lncli: `channelbalance`
// ChannelBalance returns the total funds available across all open channels // ChannelBalance returns the total funds available across all open channels
@ -5002,8 +4989,7 @@ type LightningServer interface {
// * lncli: `walletbalance` // * lncli: `walletbalance`
// WalletBalance returns total unspent outputs(confirmed and unconfirmed), all // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all
// confirmed unspent outputs and all unconfirmed unspent outputs under control // confirmed unspent outputs and all unconfirmed unspent outputs under control
// by the wallet. This method can be modified by having the request specify // of the wallet.
// only witness outputs should be factored into the final output sum.
WalletBalance(context.Context, *WalletBalanceRequest) (*WalletBalanceResponse, error) WalletBalance(context.Context, *WalletBalanceRequest) (*WalletBalanceResponse, error)
// * lncli: `channelbalance` // * lncli: `channelbalance`
// ChannelBalance returns the total funds available across all open channels // 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) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 5312 bytes of a gzipped FileDescriptorProto // 5295 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0x4b, 0x90, 0x1c, 0xc9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0x4b, 0x90, 0x1c, 0xc9,
0x59, 0x56, 0xf5, 0xf4, 0x3c, 0xfa, 0xef, 0x9e, 0x57, 0xce, 0x68, 0xd4, 0xea, 0x95, 0x65, 0x6d, 0x59, 0xbf, 0xaa, 0xa7, 0xe7, 0xd1, 0x5f, 0xf7, 0xbc, 0x72, 0x46, 0xa3, 0x56, 0xaf, 0x2c, 0x6b,
0xb1, 0xb1, 0x12, 0x62, 0xd1, 0x68, 0xc7, 0xf6, 0xb2, 0x5e, 0x81, 0x1d, 0x7a, 0xcf, 0xda, 0xb3, 0xeb, 0xbf, 0xb1, 0xd2, 0x5f, 0x2c, 0x1a, 0xed, 0xd8, 0x5e, 0x96, 0x15, 0xac, 0x43, 0xef, 0x59,
0xf2, 0xb8, 0x46, 0xf2, 0x82, 0x17, 0x68, 0xd7, 0x74, 0xe7, 0xf4, 0x94, 0xd5, 0x5d, 0x55, 0x5b, 0x7b, 0x56, 0x1e, 0xd7, 0x48, 0x5e, 0xf0, 0x02, 0xed, 0x9a, 0xee, 0x9c, 0x9e, 0xb2, 0xba, 0xab,
0x95, 0x3d, 0xa3, 0xde, 0x45, 0x11, 0x3c, 0x22, 0x38, 0x41, 0xf8, 0x00, 0x11, 0x84, 0x21, 0xcc, 0x6a, 0xab, 0xb2, 0x67, 0xd4, 0xbb, 0x28, 0x82, 0x47, 0x04, 0x27, 0x1c, 0x3e, 0x40, 0x04, 0x61,
0xc1, 0xbe, 0xc0, 0x81, 0x23, 0x07, 0xc2, 0x04, 0xdc, 0x1d, 0x41, 0x70, 0xf0, 0x89, 0xe0, 0x06, 0x08, 0x5f, 0xec, 0x0b, 0x1c, 0x38, 0x72, 0x20, 0x4c, 0xc0, 0xdd, 0x11, 0x04, 0x07, 0x9f, 0x08,
0x9c, 0xcc, 0x99, 0x0b, 0x27, 0xe2, 0xff, 0xf3, 0x51, 0x99, 0x55, 0x35, 0x92, 0x6c, 0x03, 0xb7, 0x6e, 0xc0, 0xc9, 0x9c, 0xb9, 0x70, 0x22, 0xbe, 0x2f, 0x1f, 0x95, 0x59, 0x55, 0x23, 0xc9, 0x36,
0xce, 0x2f, 0x33, 0xff, 0x7c, 0xfd, 0xf9, 0xbf, 0xf2, 0xaf, 0x86, 0x56, 0x96, 0x0e, 0x6e, 0xa4, 0x70, 0xeb, 0xfc, 0x65, 0xe6, 0x97, 0xaf, 0x2f, 0xbf, 0x57, 0x7e, 0xd5, 0xd0, 0xca, 0xd2, 0xc1,
0x59, 0x22, 0x12, 0x36, 0x3f, 0x8e, 0xb3, 0x74, 0xd0, 0xbb, 0x34, 0x4a, 0x92, 0xd1, 0x98, 0x6f, 0x8d, 0x34, 0x4b, 0x44, 0xc2, 0xe6, 0xc7, 0x71, 0x96, 0x0e, 0x7a, 0x97, 0x46, 0x49, 0x32, 0x1a,
0x87, 0x69, 0xb4, 0x1d, 0xc6, 0x71, 0x22, 0x42, 0x11, 0x25, 0x71, 0x2e, 0x1b, 0xf9, 0xdf, 0x82, 0xf3, 0xed, 0x30, 0x8d, 0xb6, 0xc3, 0x38, 0x4e, 0x44, 0x28, 0xa2, 0x24, 0xce, 0x65, 0x23, 0xff,
0x95, 0x87, 0x3c, 0x3e, 0xe0, 0x7c, 0x18, 0xf0, 0x8f, 0xa7, 0x3c, 0x17, 0xec, 0x97, 0x60, 0x3d, 0x5b, 0xb0, 0xf2, 0x90, 0xc7, 0x07, 0x9c, 0x0f, 0x03, 0xfe, 0xc9, 0x94, 0xe7, 0x82, 0xfd, 0x12,
0xe4, 0x9f, 0x70, 0x3e, 0xec, 0xa7, 0x61, 0x9e, 0xa7, 0xc7, 0x59, 0x98, 0xf3, 0xae, 0x77, 0xc5, 0xac, 0x87, 0xfc, 0x53, 0xce, 0x87, 0xfd, 0x34, 0xcc, 0xf3, 0xf4, 0x38, 0x0b, 0x73, 0xde, 0xf5,
0xbb, 0xd6, 0x09, 0xd6, 0x64, 0xc5, 0xbe, 0xc1, 0xd9, 0xeb, 0xd0, 0xc9, 0xb1, 0x29, 0x8f, 0x45, 0xae, 0x78, 0xd7, 0x3a, 0xc1, 0x9a, 0xac, 0xd8, 0x37, 0x38, 0x7b, 0x1d, 0x3a, 0x39, 0x36, 0xe5,
0x96, 0xa4, 0xb3, 0x6e, 0x83, 0xda, 0xb5, 0x11, 0xbb, 0x2f, 0x21, 0x7f, 0x0c, 0xab, 0x66, 0x84, 0xb1, 0xc8, 0x92, 0x74, 0xd6, 0x6d, 0x50, 0xbb, 0x36, 0x62, 0xf7, 0x25, 0xe4, 0x8f, 0x61, 0xd5,
0x3c, 0x4d, 0xe2, 0x9c, 0xb3, 0x9b, 0xb0, 0x39, 0x88, 0xd2, 0x63, 0x9e, 0xf5, 0xa9, 0xf3, 0x24, 0x8c, 0x90, 0xa7, 0x49, 0x9c, 0x73, 0x76, 0x13, 0x36, 0x07, 0x51, 0x7a, 0xcc, 0xb3, 0x3e, 0x75,
0xe6, 0x93, 0x24, 0x8e, 0x06, 0x5d, 0xef, 0xca, 0xdc, 0xb5, 0x56, 0xc0, 0x64, 0x1d, 0xf6, 0xf8, 0x9e, 0xc4, 0x7c, 0x92, 0xc4, 0xd1, 0xa0, 0xeb, 0x5d, 0x99, 0xbb, 0xd6, 0x0a, 0x98, 0xac, 0xc3,
0x40, 0xd5, 0xb0, 0xab, 0xb0, 0xca, 0x63, 0x89, 0xf3, 0x21, 0xf5, 0x52, 0x43, 0xad, 0x14, 0x30, 0x1e, 0x1f, 0xaa, 0x1a, 0x76, 0x15, 0x56, 0x79, 0x2c, 0x71, 0x3e, 0xa4, 0x5e, 0x6a, 0xa8, 0x95,
0x76, 0xf0, 0xff, 0xc2, 0x83, 0xf5, 0xf7, 0xe3, 0x48, 0x7c, 0x18, 0x8e, 0xc7, 0x5c, 0xe8, 0x35, 0x02, 0xc6, 0x0e, 0xfe, 0x5f, 0x78, 0xb0, 0xfe, 0x41, 0x1c, 0x89, 0x8f, 0xc2, 0xf1, 0x98, 0x0b,
0x5d, 0x85, 0xd5, 0x53, 0x02, 0x68, 0x4d, 0xa7, 0x49, 0x36, 0x54, 0x2b, 0x5a, 0x91, 0xf0, 0xbe, 0xbd, 0xa6, 0xab, 0xb0, 0x7a, 0x4a, 0x00, 0xad, 0xe9, 0x34, 0xc9, 0x86, 0x6a, 0x45, 0x2b, 0x12,
0x42, 0xcf, 0x9c, 0x59, 0xe3, 0xcc, 0x99, 0xd5, 0x6e, 0xd7, 0x5c, 0xfd, 0x76, 0xf9, 0x9b, 0xc0, 0xde, 0x57, 0xe8, 0x99, 0x33, 0x6b, 0x9c, 0x39, 0xb3, 0xda, 0xed, 0x9a, 0xab, 0xdf, 0x2e, 0x7f,
0xec, 0xc9, 0xc9, 0xed, 0xf0, 0xbf, 0x04, 0x1b, 0x4f, 0xe2, 0x71, 0x32, 0x78, 0xfa, 0xb3, 0x4d, 0x13, 0x98, 0x3d, 0x39, 0xb9, 0x1d, 0xfe, 0xfb, 0xb0, 0xf1, 0x24, 0x1e, 0x27, 0x83, 0xa7, 0x3f,
0xda, 0xdf, 0x82, 0x4d, 0xb7, 0xbf, 0xa2, 0xfb, 0xdd, 0x06, 0xb4, 0x1f, 0x67, 0x61, 0x9c, 0x87, 0xdf, 0xa4, 0xfd, 0x2d, 0xd8, 0x74, 0xfb, 0x2b, 0xba, 0xdf, 0x6b, 0x40, 0xfb, 0x71, 0x16, 0xc6,
0x03, 0x3c, 0x72, 0xd6, 0x85, 0x45, 0xf1, 0xac, 0x7f, 0x1c, 0xe6, 0xc7, 0x44, 0xa8, 0x15, 0xe8, 0x79, 0x38, 0xc0, 0x23, 0x67, 0x5d, 0x58, 0x14, 0xcf, 0xfa, 0xc7, 0x61, 0x7e, 0x4c, 0x84, 0x5a,
0x22, 0xdb, 0x82, 0x85, 0x70, 0x92, 0x4c, 0x63, 0x41, 0xbb, 0x3a, 0x17, 0xa8, 0x12, 0x7b, 0x0b, 0x81, 0x2e, 0xb2, 0x2d, 0x58, 0x08, 0x27, 0xc9, 0x34, 0x16, 0xb4, 0xab, 0x73, 0x81, 0x2a, 0xb1,
0xd6, 0xe3, 0xe9, 0xa4, 0x3f, 0x48, 0xe2, 0xa3, 0x28, 0x9b, 0x48, 0xc6, 0xa1, 0xc5, 0xcd, 0x07, 0xb7, 0x60, 0x3d, 0x9e, 0x4e, 0xfa, 0x83, 0x24, 0x3e, 0x8a, 0xb2, 0x89, 0x64, 0x1c, 0x5a, 0xdc,
0xd5, 0x0a, 0x76, 0x19, 0xe0, 0x10, 0xa7, 0x21, 0x87, 0x68, 0xd2, 0x10, 0x16, 0xc2, 0x7c, 0xe8, 0x7c, 0x50, 0xad, 0x60, 0x97, 0x01, 0x0e, 0x71, 0x1a, 0x72, 0x88, 0x26, 0x0d, 0x61, 0x21, 0xcc,
0xa8, 0x12, 0x8f, 0x46, 0xc7, 0xa2, 0x3b, 0x4f, 0x84, 0x1c, 0x0c, 0x69, 0x88, 0x68, 0xc2, 0xfb, 0x87, 0x8e, 0x2a, 0xf1, 0x68, 0x74, 0x2c, 0xba, 0xf3, 0x44, 0xc8, 0xc1, 0x90, 0x86, 0x88, 0x26,
0xb9, 0x08, 0x27, 0x69, 0x77, 0x81, 0x66, 0x63, 0x21, 0x54, 0x9f, 0x88, 0x70, 0xdc, 0x3f, 0xe2, 0xbc, 0x9f, 0x8b, 0x70, 0x92, 0x76, 0x17, 0x68, 0x36, 0x16, 0x42, 0xf5, 0x89, 0x08, 0xc7, 0xfd,
0x3c, 0xef, 0x2e, 0xaa, 0x7a, 0x83, 0xb0, 0x37, 0x61, 0x65, 0xc8, 0x73, 0xd1, 0x0f, 0x87, 0xc3, 0x23, 0xce, 0xf3, 0xee, 0xa2, 0xaa, 0x37, 0x08, 0x7b, 0x13, 0x56, 0x86, 0x3c, 0x17, 0xfd, 0x70,
0x8c, 0xe7, 0x39, 0xcf, 0xbb, 0x4b, 0x74, 0x74, 0x25, 0xd4, 0xef, 0xc2, 0xd6, 0x43, 0x2e, 0xac, 0x38, 0xcc, 0x78, 0x9e, 0xf3, 0xbc, 0xbb, 0x44, 0x47, 0x57, 0x42, 0xfd, 0x2e, 0x6c, 0x3d, 0xe4,
0xdd, 0xc9, 0xd5, 0xb6, 0xfb, 0x7b, 0xc0, 0x2c, 0xf8, 0x1e, 0x17, 0x61, 0x34, 0xce, 0xd9, 0x3b, 0xc2, 0xda, 0x9d, 0x5c, 0x6d, 0xbb, 0xbf, 0x07, 0xcc, 0x82, 0xef, 0x71, 0x11, 0x46, 0xe3, 0x9c,
0xd0, 0x11, 0x56, 0x63, 0x62, 0xd5, 0xf6, 0x0e, 0xbb, 0x41, 0x77, 0xec, 0x86, 0xd5, 0x21, 0x70, 0xbd, 0x03, 0x1d, 0x61, 0x35, 0x26, 0x56, 0x6d, 0xef, 0xb0, 0x1b, 0x74, 0xc7, 0x6e, 0x58, 0x1d,
0xda, 0xf9, 0xff, 0xed, 0x41, 0xfb, 0x80, 0xc7, 0xe6, 0x76, 0x31, 0x68, 0xe2, 0x4c, 0xd4, 0x49, 0x02, 0xa7, 0x9d, 0xff, 0x5f, 0x1e, 0xb4, 0x0f, 0x78, 0x6c, 0x6e, 0x17, 0x83, 0x26, 0xce, 0x44,
0xd2, 0x6f, 0xf6, 0x59, 0x68, 0xd3, 0xec, 0x72, 0x91, 0x45, 0xf1, 0x88, 0x8e, 0xa0, 0x15, 0x00, 0x9d, 0x24, 0xfd, 0x66, 0x9f, 0x87, 0x36, 0xcd, 0x2e, 0x17, 0x59, 0x14, 0x8f, 0xe8, 0x08, 0x5a,
0x42, 0x07, 0x84, 0xb0, 0x35, 0x98, 0x0b, 0x27, 0x82, 0x36, 0x7e, 0x2e, 0xc0, 0x9f, 0x78, 0xef, 0x01, 0x20, 0x74, 0x40, 0x08, 0x5b, 0x83, 0xb9, 0x70, 0x22, 0x68, 0xe3, 0xe7, 0x02, 0xfc, 0x89,
0xd2, 0x70, 0x36, 0xe1, 0xb1, 0x28, 0x36, 0xbb, 0x13, 0xb4, 0x15, 0xb6, 0x8b, 0xbb, 0x7d, 0x03, 0xf7, 0x2e, 0x0d, 0x67, 0x13, 0x1e, 0x8b, 0x62, 0xb3, 0x3b, 0x41, 0x5b, 0x61, 0xbb, 0xb8, 0xdb,
0x36, 0xec, 0x26, 0x9a, 0xfa, 0x3c, 0x51, 0x5f, 0xb7, 0x5a, 0xaa, 0x41, 0xae, 0xc2, 0xaa, 0x6e, 0x37, 0x60, 0xc3, 0x6e, 0xa2, 0xa9, 0xcf, 0x13, 0xf5, 0x75, 0xab, 0xa5, 0x1a, 0xe4, 0x2a, 0xac,
0x9f, 0xc9, 0xc9, 0xd2, 0xf6, 0xb7, 0x82, 0x15, 0x05, 0xeb, 0x25, 0x5c, 0x83, 0xb5, 0xa3, 0x28, 0xea, 0xf6, 0x99, 0x9c, 0x2c, 0x6d, 0x7f, 0x2b, 0x58, 0x51, 0xb0, 0x5e, 0xc2, 0x35, 0x58, 0x3b,
0x0e, 0xc7, 0xfd, 0xc1, 0x58, 0x9c, 0xf4, 0x87, 0x7c, 0x2c, 0x42, 0x3a, 0x88, 0xf9, 0x60, 0x85, 0x8a, 0xe2, 0x70, 0xdc, 0x1f, 0x8c, 0xc5, 0x49, 0x7f, 0xc8, 0xc7, 0x22, 0xa4, 0x83, 0x98, 0x0f,
0xf0, 0xbb, 0x63, 0x71, 0x72, 0x0f, 0x51, 0xff, 0x4f, 0x3d, 0xe8, 0xc8, 0xc5, 0xab, 0x8b, 0xff, 0x56, 0x08, 0xbf, 0x3b, 0x16, 0x27, 0xf7, 0x10, 0xf5, 0xff, 0xd4, 0x83, 0x8e, 0x5c, 0xbc, 0xba,
0x06, 0x2c, 0xeb, 0x31, 0x78, 0x96, 0x25, 0x99, 0xe2, 0x43, 0x17, 0x64, 0xd7, 0x61, 0x4d, 0x03, 0xf8, 0x6f, 0xc0, 0xb2, 0x1e, 0x83, 0x67, 0x59, 0x92, 0x29, 0x3e, 0x74, 0x41, 0x76, 0x1d, 0xd6,
0x69, 0xc6, 0xa3, 0x49, 0x38, 0xe2, 0xea, 0xb6, 0x57, 0x70, 0xb6, 0x53, 0x50, 0xcc, 0x92, 0xa9, 0x34, 0x90, 0x66, 0x3c, 0x9a, 0x84, 0x23, 0xae, 0x6e, 0x7b, 0x05, 0x67, 0x3b, 0x05, 0xc5, 0x2c,
0x90, 0x57, 0xaf, 0xbd, 0xd3, 0x51, 0x07, 0x13, 0x20, 0x16, 0xb8, 0x4d, 0xfc, 0xef, 0x7b, 0xd0, 0x99, 0x0a, 0x79, 0xf5, 0xda, 0x3b, 0x1d, 0x75, 0x30, 0x01, 0x62, 0x81, 0xdb, 0xc4, 0xff, 0x81,
0xb9, 0x7b, 0x1c, 0xc6, 0x31, 0x1f, 0xef, 0x27, 0x51, 0x2c, 0xd8, 0x4d, 0x60, 0x47, 0xd3, 0x78, 0x07, 0x9d, 0xbb, 0xc7, 0x61, 0x1c, 0xf3, 0xf1, 0x7e, 0x12, 0xc5, 0x82, 0xdd, 0x04, 0x76, 0x34,
0x18, 0xc5, 0xa3, 0xbe, 0x78, 0x16, 0x0d, 0xfb, 0x87, 0x33, 0xc1, 0x73, 0x79, 0x44, 0xbb, 0xe7, 0x8d, 0x87, 0x51, 0x3c, 0xea, 0x8b, 0x67, 0xd1, 0xb0, 0x7f, 0x38, 0x13, 0x3c, 0x97, 0x47, 0xb4,
0x82, 0x9a, 0x3a, 0xf6, 0x16, 0xac, 0x39, 0x68, 0x2e, 0x32, 0x79, 0x6e, 0xbb, 0xe7, 0x82, 0x4a, 0x7b, 0x2e, 0xa8, 0xa9, 0x63, 0x6f, 0xc1, 0x9a, 0x83, 0xe6, 0x22, 0x93, 0xe7, 0xb6, 0x7b, 0x2e,
0x0d, 0x32, 0x7e, 0x32, 0x15, 0xe9, 0x54, 0xf4, 0xa3, 0x78, 0xc8, 0x9f, 0xd1, 0x1c, 0x97, 0x03, 0xa8, 0xd4, 0x20, 0xe3, 0x27, 0x53, 0x91, 0x4e, 0x45, 0x3f, 0x8a, 0x87, 0xfc, 0x19, 0xcd, 0x71,
0x07, 0xbb, 0xb3, 0x02, 0x1d, 0xbb, 0x9f, 0xff, 0x25, 0x58, 0xdb, 0xc3, 0x1b, 0x11, 0x47, 0xf1, 0x39, 0x70, 0xb0, 0x3b, 0x2b, 0xd0, 0xb1, 0xfb, 0xf9, 0xef, 0xc3, 0xda, 0x1e, 0xde, 0x88, 0x38,
0xe8, 0xb6, 0x64, 0x5b, 0xbc, 0xa6, 0xe9, 0xf4, 0xf0, 0x29, 0x9f, 0xa9, 0x7d, 0x53, 0x25, 0x64, 0x8a, 0x47, 0xb7, 0x25, 0xdb, 0xe2, 0x35, 0x4d, 0xa7, 0x87, 0x4f, 0xf9, 0x4c, 0xed, 0x9b, 0x2a,
0xaa, 0xe3, 0x24, 0x17, 0x8a, 0x73, 0xe8, 0xb7, 0xff, 0xef, 0x1e, 0xac, 0xe2, 0xde, 0x7f, 0x10, 0x21, 0x53, 0x1d, 0x27, 0xb9, 0x50, 0x9c, 0x43, 0xbf, 0xfd, 0x7f, 0xf3, 0x60, 0x15, 0xf7, 0xfe,
0xc6, 0x33, 0x7d, 0x72, 0x7b, 0xd0, 0x41, 0x52, 0x8f, 0x93, 0xdb, 0xf2, 0xb2, 0x4b, 0x26, 0xbe, 0xc3, 0x30, 0x9e, 0xe9, 0x93, 0xdb, 0x83, 0x0e, 0x92, 0x7a, 0x9c, 0xdc, 0x96, 0x97, 0x5d, 0x32,
0xa6, 0xf6, 0xaa, 0xd4, 0xfa, 0x86, 0xdd, 0x14, 0x85, 0xf9, 0x2c, 0x70, 0x7a, 0x23, 0xdb, 0x8a, 0xf1, 0x35, 0xb5, 0x57, 0xa5, 0xd6, 0x37, 0xec, 0xa6, 0x28, 0xcc, 0x67, 0x81, 0xd3, 0x1b, 0xd9,
0x30, 0x1b, 0x71, 0x41, 0x62, 0x40, 0x89, 0x05, 0x90, 0xd0, 0xdd, 0x24, 0x3e, 0x62, 0x57, 0xa0, 0x56, 0x84, 0xd9, 0x88, 0x0b, 0x12, 0x03, 0x4a, 0x2c, 0x80, 0x84, 0xee, 0x26, 0xf1, 0x11, 0xbb,
0x93, 0x87, 0xa2, 0x9f, 0xf2, 0x8c, 0x76, 0x8d, 0x58, 0x6f, 0x2e, 0x80, 0x3c, 0x14, 0xfb, 0x3c, 0x02, 0x9d, 0x3c, 0x14, 0xfd, 0x94, 0x67, 0xb4, 0x6b, 0xc4, 0x7a, 0x73, 0x01, 0xe4, 0xa1, 0xd8,
0xbb, 0x33, 0x13, 0xbc, 0xf7, 0x65, 0x58, 0xaf, 0x8c, 0x82, 0xdc, 0x5e, 0x2c, 0x11, 0x7f, 0xb2, 0xe7, 0xd9, 0x9d, 0x99, 0xe0, 0xbd, 0x2f, 0xc3, 0x7a, 0x65, 0x14, 0xe4, 0xf6, 0x62, 0x89, 0xf8,
0x4d, 0x98, 0x3f, 0x09, 0xc7, 0x53, 0xae, 0xa4, 0x93, 0x2c, 0xbc, 0xd7, 0x78, 0xd7, 0xf3, 0xdf, 0x93, 0x6d, 0xc2, 0xfc, 0x49, 0x38, 0x9e, 0x72, 0x25, 0x9d, 0x64, 0xe1, 0xbd, 0xc6, 0xbb, 0x9e,
0x84, 0xb5, 0x62, 0xda, 0x8a, 0xc9, 0x18, 0x34, 0x71, 0x07, 0x15, 0x01, 0xfa, 0xed, 0xff, 0x9e, 0xff, 0x26, 0xac, 0x15, 0xd3, 0x56, 0x4c, 0xc6, 0xa0, 0x89, 0x3b, 0xa8, 0x08, 0xd0, 0x6f, 0xff,
0x27, 0x1b, 0xde, 0x4d, 0x22, 0x73, 0xd3, 0xb1, 0x21, 0x0a, 0x04, 0xdd, 0x10, 0x7f, 0x9f, 0x29, 0xf7, 0x3d, 0xd9, 0xf0, 0x6e, 0x12, 0x99, 0x9b, 0x8e, 0x0d, 0x51, 0x20, 0xe8, 0x86, 0xf8, 0xfb,
0x09, 0x7f, 0xfe, 0xc5, 0xfa, 0x57, 0x61, 0xdd, 0x9a, 0xc2, 0x0b, 0x26, 0xfb, 0x97, 0x1e, 0xac, 0x4c, 0x49, 0xf8, 0x8b, 0x2f, 0xd6, 0xbf, 0x0a, 0xeb, 0xd6, 0x14, 0x5e, 0x30, 0xd9, 0xef, 0x78,
0x3f, 0xe2, 0xa7, 0xea, 0xd4, 0xf5, 0x6c, 0xdf, 0x85, 0xa6, 0x98, 0xa5, 0x52, 0x15, 0xaf, 0xec, 0xb0, 0xfe, 0x88, 0x9f, 0xaa, 0x53, 0xd7, 0xb3, 0x7d, 0x17, 0x9a, 0x62, 0x96, 0x4a, 0x55, 0xbc,
0xbc, 0xa1, 0x0e, 0xad, 0xd2, 0xee, 0x86, 0x2a, 0x3e, 0x9e, 0xa5, 0x3c, 0xa0, 0x1e, 0xfe, 0xd7, 0xb2, 0xf3, 0x86, 0x3a, 0xb4, 0x4a, 0xbb, 0x1b, 0xaa, 0xf8, 0x78, 0x96, 0xf2, 0x80, 0x7a, 0xf8,
0xa0, 0x6d, 0x81, 0xec, 0x02, 0x6c, 0x7c, 0xf8, 0xfe, 0xe3, 0x47, 0xf7, 0x0f, 0x0e, 0xfa, 0xfb, 0xef, 0x43, 0xdb, 0x02, 0xd9, 0x05, 0xd8, 0xf8, 0xe8, 0x83, 0xc7, 0x8f, 0xee, 0x1f, 0x1c, 0xf4,
0x4f, 0xee, 0x7c, 0xf5, 0xfe, 0x6f, 0xf4, 0x77, 0x6f, 0x1f, 0xec, 0xae, 0x9d, 0x63, 0x5b, 0xc0, 0xf7, 0x9f, 0xdc, 0xf9, 0xea, 0xfd, 0xdf, 0xec, 0xef, 0xde, 0x3e, 0xd8, 0x5d, 0x3b, 0xc7, 0xb6,
0x1e, 0xdd, 0x3f, 0x78, 0x7c, 0xff, 0x9e, 0x83, 0x7b, 0x6c, 0x15, 0xda, 0x36, 0xd0, 0xf0, 0x7b, 0x80, 0x3d, 0xba, 0x7f, 0xf0, 0xf8, 0xfe, 0x3d, 0x07, 0xf7, 0xfc, 0x1e, 0x74, 0x1f, 0xf1, 0xd3,
0xd0, 0x7d, 0xc4, 0x4f, 0x3f, 0x8c, 0x44, 0xcc, 0xf3, 0xdc, 0x1d, 0xde, 0xbf, 0x01, 0xcc, 0x9e, 0x8f, 0x22, 0x11, 0xf3, 0x3c, 0x77, 0x47, 0xf3, 0x6f, 0x00, 0xb3, 0xa7, 0xa0, 0x56, 0xd5, 0x85,
0x93, 0x5a, 0x66, 0x17, 0x16, 0x95, 0xec, 0xd5, 0xaa, 0x47, 0x15, 0xfd, 0x37, 0x81, 0x1d, 0x44, 0x45, 0x25, 0x6a, 0xb5, 0xa6, 0x51, 0x45, 0xff, 0x4d, 0x60, 0x07, 0xd1, 0x28, 0xfe, 0x90, 0xe7,
0xa3, 0xf8, 0x03, 0x9e, 0xe7, 0xe1, 0x88, 0xeb, 0xc5, 0xae, 0xc1, 0xdc, 0x24, 0x1f, 0x29, 0x29, 0x79, 0x38, 0xe2, 0x7a, 0x6d, 0x6b, 0x30, 0x37, 0xc9, 0x47, 0x4a, 0x28, 0xe2, 0x4f, 0xff, 0x0b,
0x89, 0x3f, 0xfd, 0xcf, 0xc1, 0x86, 0xd3, 0x4e, 0x11, 0xbe, 0x04, 0xad, 0x3c, 0x1a, 0xc5, 0xa1, 0xb0, 0xe1, 0xb4, 0x53, 0x84, 0x2f, 0x41, 0x2b, 0x8f, 0x46, 0x71, 0x28, 0xa6, 0x19, 0x57, 0xa4,
0x98, 0x66, 0x5c, 0x91, 0x2e, 0x00, 0xff, 0x01, 0x6c, 0x7e, 0x83, 0x67, 0xd1, 0xd1, 0xec, 0x65, 0x0b, 0xc0, 0x7f, 0x00, 0x9b, 0xdf, 0xe0, 0x59, 0x74, 0x34, 0x7b, 0x19, 0x79, 0x97, 0x4e, 0xa3,
0xe4, 0x5d, 0x3a, 0x8d, 0x32, 0x9d, 0xfb, 0x70, 0xbe, 0x44, 0x47, 0x0d, 0x2f, 0x39, 0x53, 0x9d, 0x4c, 0xe7, 0x3e, 0x9c, 0x2f, 0xd1, 0x51, 0xc3, 0x4b, 0x46, 0x54, 0xc7, 0xb5, 0x14, 0xc8, 0x82,
0xdf, 0x52, 0x20, 0x0b, 0xd6, 0x3d, 0x6d, 0xd8, 0xf7, 0xd4, 0x7f, 0x02, 0xec, 0x6e, 0x12, 0xc7, 0x75, 0x2d, 0x1b, 0xf6, 0xb5, 0xf4, 0x9f, 0x00, 0xbb, 0x9b, 0xc4, 0x31, 0x1f, 0x88, 0x7d, 0xce,
0x7c, 0x20, 0xf6, 0x39, 0xcf, 0x0a, 0x83, 0xab, 0x60, 0xc3, 0xf6, 0xce, 0x05, 0x75, 0xb0, 0xe5, 0xb3, 0xc2, 0xbe, 0x2a, 0xb8, 0xae, 0xbd, 0x73, 0x41, 0x9d, 0x63, 0xf9, 0xae, 0x2b, 0x76, 0x64,
0xcb, 0xaf, 0xf8, 0x93, 0x41, 0x33, 0xe5, 0xd9, 0x84, 0x08, 0x2f, 0x05, 0xf4, 0xdb, 0x3f, 0x0f, 0xd0, 0x4c, 0x79, 0x36, 0x21, 0xc2, 0x4b, 0x01, 0xfd, 0xf6, 0xcf, 0xc3, 0x86, 0x43, 0x56, 0x69,
0x1b, 0x0e, 0x59, 0xa5, 0xfe, 0xdf, 0x86, 0xf3, 0xf7, 0xa2, 0x7c, 0x50, 0x1d, 0xb0, 0x0b, 0x8b, 0xfb, 0xb7, 0xe1, 0xfc, 0xbd, 0x28, 0x1f, 0x54, 0x07, 0xec, 0xc2, 0x62, 0x3a, 0x3d, 0xec, 0x17,
0xe9, 0xf4, 0xb0, 0x5f, 0x5c, 0x32, 0x5d, 0x44, 0xad, 0x58, 0xee, 0xa2, 0x88, 0xfd, 0xa1, 0x07, 0x77, 0x4a, 0x17, 0x51, 0x09, 0x96, 0xbb, 0x28, 0x62, 0x7f, 0xe4, 0x41, 0x73, 0xf7, 0xf1, 0xde,
0xcd, 0xdd, 0xc7, 0x7b, 0x77, 0x59, 0x0f, 0x96, 0xa2, 0x78, 0x90, 0x4c, 0x50, 0x97, 0xc8, 0x45, 0x5d, 0xd6, 0x83, 0xa5, 0x28, 0x1e, 0x24, 0x13, 0x54, 0x1d, 0x72, 0xd1, 0xa6, 0x7c, 0xe6, 0x5d,
0x9b, 0xf2, 0x99, 0x97, 0xe7, 0x12, 0xb4, 0x48, 0x05, 0xa1, 0xa2, 0x57, 0xb6, 0x51, 0x01, 0xa0, 0xb9, 0x04, 0x2d, 0xd2, 0x38, 0xa8, 0xd7, 0x95, 0x29, 0x54, 0x00, 0x68, 0x53, 0xf0, 0x67, 0x69,
0x91, 0xc1, 0x9f, 0xa5, 0x51, 0x46, 0x56, 0x84, 0xb6, 0x0d, 0x9a, 0x24, 0x22, 0xab, 0x15, 0xfe, 0x94, 0x91, 0xd1, 0xa0, 0x4d, 0x81, 0x26, 0x49, 0xc4, 0x6a, 0x85, 0xff, 0xd3, 0x26, 0x2c, 0xdf,
0x4f, 0x9a, 0xb0, 0x7c, 0x7b, 0x20, 0xa2, 0x13, 0xae, 0x44, 0x38, 0x8d, 0x4a, 0x80, 0x9a, 0x8f, 0x1e, 0x88, 0xe8, 0x84, 0x2b, 0x89, 0x4d, 0xa3, 0x12, 0xa0, 0xe6, 0xa3, 0x4a, 0xa8, 0x5b, 0x32,
0x2a, 0xa1, 0xb2, 0xc9, 0xf8, 0x24, 0x11, 0xbc, 0xef, 0x1c, 0x86, 0x0b, 0x62, 0xab, 0x81, 0x24, 0x3e, 0x49, 0x04, 0xef, 0x3b, 0x87, 0xe1, 0x82, 0xd8, 0x6a, 0x20, 0x09, 0xf5, 0x53, 0x94, 0xfd,
0xd4, 0x4f, 0x51, 0x19, 0xd0, 0xfc, 0x5a, 0x81, 0x0b, 0xe2, 0x96, 0x21, 0xd0, 0x8f, 0x86, 0x34, 0x34, 0xbf, 0x56, 0xe0, 0x82, 0xb8, 0x65, 0x08, 0xf4, 0xa3, 0x21, 0xcd, 0xac, 0x19, 0xe8, 0x22,
0xb3, 0x66, 0xa0, 0x8b, 0xb8, 0x1f, 0x83, 0x30, 0x0d, 0x07, 0x91, 0x98, 0xa9, 0x3b, 0x6f, 0xca, 0xee, 0xc7, 0x20, 0x4c, 0xc3, 0x41, 0x24, 0x66, 0xea, 0x8a, 0x9b, 0x32, 0xd2, 0x1e, 0x27, 0x83,
0x48, 0x7b, 0x9c, 0x0c, 0xc2, 0x71, 0xff, 0x30, 0x1c, 0x87, 0xf1, 0x80, 0x2b, 0x7b, 0xc6, 0x05, 0x70, 0xdc, 0x3f, 0x0c, 0xc7, 0x61, 0x3c, 0xe0, 0xca, 0x7c, 0x71, 0x41, 0xb4, 0x50, 0xd4, 0x94,
0xd1, 0x64, 0x51, 0x53, 0xd2, 0xcd, 0xa4, 0x59, 0x53, 0x42, 0xd1, 0xf4, 0x19, 0x24, 0x93, 0x49, 0x74, 0x33, 0x69, 0xc5, 0x94, 0x50, 0xb4, 0x74, 0x06, 0xc9, 0x64, 0x12, 0x09, 0x34, 0x6c, 0xba,
0x24, 0xd0, 0xd2, 0xe9, 0x2e, 0x49, 0xf9, 0x52, 0x20, 0xb4, 0x12, 0x59, 0x3a, 0x95, 0x7b, 0xd8, 0x4b, 0x52, 0x9c, 0x14, 0x08, 0xad, 0x44, 0x96, 0x4e, 0xe5, 0x1e, 0xb6, 0xe4, 0x68, 0x0e, 0x88,
0x92, 0xa3, 0x39, 0x20, 0x52, 0x39, 0xe2, 0x9c, 0xe4, 0xd4, 0xd3, 0xd3, 0x2e, 0x48, 0x2a, 0x05, 0x54, 0x8e, 0x38, 0x27, 0xb1, 0xf4, 0xf4, 0xb4, 0x0b, 0x92, 0x4a, 0x81, 0xe0, 0x69, 0x4c, 0xe3,
0x82, 0xa7, 0x31, 0x8d, 0x73, 0x2e, 0xc4, 0x98, 0x0f, 0xcd, 0x84, 0xda, 0xd4, 0xac, 0x5a, 0xc1, 0x9c, 0x0b, 0x31, 0xe6, 0x43, 0x33, 0xa1, 0x36, 0x35, 0xab, 0x56, 0xb0, 0x9b, 0xb0, 0x21, 0x6d,
0x6e, 0xc2, 0x86, 0x34, 0xbe, 0xf2, 0x50, 0x24, 0xf9, 0x71, 0x94, 0xf7, 0x73, 0x1e, 0x8b, 0x6e, 0xad, 0x3c, 0x14, 0x49, 0x7e, 0x1c, 0xe5, 0xfd, 0x9c, 0xc7, 0xa2, 0xdb, 0xa1, 0xf6, 0x75, 0x55,
0x87, 0xda, 0xd7, 0x55, 0xb1, 0x77, 0xe1, 0x42, 0x09, 0xce, 0xf8, 0x80, 0x47, 0x27, 0x7c, 0xd8, 0xec, 0x5d, 0xb8, 0x50, 0x82, 0x33, 0x3e, 0xe0, 0xd1, 0x09, 0x1f, 0x76, 0x97, 0xa9, 0xd7, 0x59,
0x5d, 0xa6, 0x5e, 0x67, 0x55, 0xb3, 0x2b, 0xd0, 0x46, 0x9b, 0x73, 0x9a, 0x0e, 0x43, 0x54, 0xcf, 0xd5, 0xec, 0x0a, 0xb4, 0xd1, 0xc4, 0x9c, 0xa6, 0xc3, 0x10, 0xb5, 0xf1, 0x0a, 0x9d, 0x83, 0x0d,
0x2b, 0x74, 0x0e, 0x36, 0xc4, 0xde, 0x86, 0xe5, 0x94, 0x4b, 0x1d, 0x7a, 0x2c, 0xc6, 0x83, 0xbc, 0xb1, 0xb7, 0x61, 0x39, 0xe5, 0x52, 0x65, 0x1e, 0x8b, 0xf1, 0x20, 0xef, 0xae, 0x92, 0x3e, 0x6b,
0xbb, 0x4a, 0x0a, 0xae, 0xad, 0xae, 0x14, 0xf2, 0x6f, 0xe0, 0xb6, 0x40, 0xd6, 0x1c, 0xe4, 0x64, 0xab, 0x2b, 0x85, 0xfc, 0x1b, 0xb8, 0x2d, 0x90, 0x35, 0x07, 0x39, 0x19, 0x2d, 0xe1, 0xac, 0xbb,
0xc5, 0x84, 0xb3, 0xee, 0x1a, 0x31, 0x5d, 0x01, 0xe0, 0xcd, 0xda, 0x8b, 0x72, 0xa1, 0x38, 0xcd, 0x46, 0x4c, 0x57, 0x00, 0x78, 0xb3, 0xf6, 0xa2, 0x5c, 0x28, 0x4e, 0x33, 0x32, 0x6e, 0x17, 0x36,
0xc8, 0xb8, 0x5d, 0xd8, 0x74, 0x61, 0xe3, 0xd7, 0x2c, 0x29, 0xb6, 0xc9, 0xbb, 0x6d, 0x1a, 0x7a, 0x5d, 0xd8, 0xb8, 0x31, 0x4b, 0x8a, 0x6d, 0xf2, 0x6e, 0x9b, 0x86, 0xde, 0x54, 0x43, 0x3b, 0x1c,
0x53, 0x0d, 0xed, 0x70, 0x6c, 0x60, 0x5a, 0xf9, 0x3f, 0xf1, 0xa0, 0x89, 0xf7, 0xec, 0xec, 0x3b, 0x1b, 0x98, 0x56, 0xfe, 0x4f, 0x3d, 0x68, 0xe2, 0x3d, 0x3b, 0xfb, 0x4e, 0xda, 0xa2, 0x73, 0xce,
0x69, 0x8b, 0xce, 0x39, 0x47, 0x74, 0x92, 0xbd, 0x8d, 0xd6, 0x88, 0xdc, 0x73, 0xc9, 0x97, 0x16, 0x11, 0x9d, 0x64, 0x5e, 0xa3, 0xf1, 0x21, 0xf7, 0x5c, 0xf2, 0xa5, 0x85, 0x14, 0xf5, 0x19, 0x1f,
0x52, 0xd4, 0x67, 0x7c, 0x70, 0x42, 0xcc, 0x69, 0xea, 0x11, 0x41, 0xd6, 0x45, 0x95, 0x45, 0xbd, 0x9c, 0x10, 0x73, 0x9a, 0x7a, 0x44, 0x90, 0x75, 0x51, 0x43, 0x51, 0x6f, 0xc9, 0x99, 0xa6, 0xac,
0x25, 0x67, 0x9a, 0xb2, 0xae, 0xa3, 0x9e, 0x8b, 0x45, 0x1d, 0xf5, 0xeb, 0xc2, 0x62, 0x14, 0x1f, 0xeb, 0xa8, 0xe7, 0x62, 0x51, 0x47, 0xfd, 0xba, 0xb0, 0x18, 0xc5, 0x87, 0xc9, 0x34, 0x1e, 0x12,
0x26, 0xd3, 0x78, 0x48, 0x5c, 0xb8, 0x14, 0xe8, 0x22, 0xee, 0x66, 0x4a, 0x16, 0x4c, 0x34, 0xe1, 0x17, 0x2e, 0x05, 0xba, 0x88, 0xbb, 0x99, 0x92, 0xc1, 0x12, 0x4d, 0xb8, 0x62, 0xbf, 0x02, 0xf0,
0x8a, 0xfd, 0x0a, 0xc0, 0x67, 0x68, 0xd2, 0xe4, 0x24, 0x57, 0xcc, 0x56, 0xbe, 0x03, 0xeb, 0x16, 0x19, 0x5a, 0x30, 0x39, 0xc9, 0x15, 0xb3, 0x95, 0xef, 0xc0, 0xba, 0x85, 0xa9, 0x7d, 0x7c, 0x1d,
0xa6, 0xf6, 0xf1, 0x75, 0x98, 0x4f, 0x11, 0x50, 0x06, 0x8a, 0x3e, 0x3f, 0x12, 0x48, 0xb2, 0xc6, 0xe6, 0x53, 0x04, 0x94, 0x3d, 0xa2, 0xcf, 0x8f, 0x04, 0x92, 0xac, 0xf1, 0xd7, 0xd0, 0x4d, 0x15,
0x5f, 0x43, 0xbf, 0x55, 0xbc, 0x1f, 0x1f, 0x25, 0x9a, 0xd2, 0x3f, 0xce, 0xa1, 0xa3, 0xa9, 0x20, 0x1f, 0xc4, 0x47, 0x89, 0xa6, 0xf4, 0x0f, 0x73, 0xe8, 0x57, 0x2a, 0x48, 0x11, 0xba, 0x06, 0xab,
0x45, 0xe8, 0x1a, 0xac, 0x46, 0x43, 0x1e, 0x8b, 0x48, 0xcc, 0xfa, 0x8e, 0xe5, 0x54, 0x86, 0x51, 0xd1, 0x90, 0xc7, 0x22, 0x12, 0xb3, 0xbe, 0x63, 0x28, 0x95, 0x61, 0x14, 0xe4, 0xe1, 0x38, 0x0a,
0x90, 0x87, 0xe3, 0x28, 0xcc, 0x95, 0x90, 0x90, 0x05, 0xb6, 0x03, 0x9b, 0xc8, 0x5f, 0x9a, 0x65, 0x73, 0x25, 0x24, 0x64, 0x81, 0xed, 0xc0, 0x26, 0xf2, 0x97, 0x66, 0x19, 0x73, 0xb8, 0xd2, 0x5e,
0xcc, 0xe1, 0x4a, 0x03, 0xae, 0xb6, 0x0e, 0xaf, 0x04, 0xe2, 0x52, 0x08, 0x15, 0x5d, 0xa4, 0x40, 0xab, 0xad, 0xc3, 0x2b, 0x81, 0xb8, 0x14, 0x42, 0x45, 0x17, 0x29, 0xd0, 0xea, 0xaa, 0x70, 0xd7,
0xab, 0xab, 0xc2, 0x5d, 0x93, 0x94, 0x70, 0xc9, 0xf3, 0x92, 0x07, 0x0d, 0x50, 0xf1, 0x9a, 0x16, 0x24, 0x25, 0x5c, 0xf2, 0xbc, 0xe4, 0x41, 0x03, 0x54, 0x9c, 0xa4, 0x05, 0x69, 0x2b, 0x96, 0x9d,
0xa4, 0xf1, 0x58, 0xf6, 0x9a, 0x2c, 0xcf, 0x6b, 0xa9, 0xe2, 0x79, 0x5d, 0x83, 0xd5, 0x7c, 0x16, 0x24, 0xcb, 0xd1, 0x5a, 0xaa, 0x38, 0x5a, 0xd7, 0x60, 0x35, 0x9f, 0xc5, 0x03, 0x3e, 0xec, 0x8b,
0x0f, 0xf8, 0xb0, 0x2f, 0x12, 0x1c, 0x37, 0x8a, 0xe9, 0x74, 0x96, 0x82, 0x32, 0x4c, 0x3e, 0x22, 0x04, 0xc7, 0x8d, 0x62, 0x3a, 0x9d, 0xa5, 0xa0, 0x0c, 0x93, 0x4b, 0xc8, 0x73, 0x11, 0x73, 0x41,
0xcf, 0x45, 0xcc, 0x05, 0xc9, 0x86, 0xa5, 0x40, 0x17, 0x51, 0xcc, 0x52, 0x13, 0xc9, 0xda, 0xad, 0xb2, 0x61, 0x29, 0xd0, 0x45, 0x14, 0xb3, 0xd4, 0x44, 0xb2, 0x76, 0x2b, 0x50, 0x25, 0xd4, 0x48,
0x40, 0x95, 0x50, 0x23, 0x4d, 0xb3, 0x28, 0xef, 0x76, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xfe, 0xd3, 0x2c, 0xca, 0xbb, 0x1d, 0x42, 0xe9, 0x37, 0xfb, 0x22, 0x9c, 0x3f, 0x44, 0x07, 0xe6, 0x98,
0x10, 0x3d, 0x9a, 0x63, 0x1e, 0x0e, 0x79, 0x46, 0xa7, 0x2f, 0x1d, 0x3a, 0x79, 0xc5, 0xeb, 0x2b, 0x87, 0x43, 0x9e, 0xd1, 0xe9, 0x4b, 0xff, 0x4d, 0x5e, 0xf1, 0xfa, 0x4a, 0xff, 0x53, 0x52, 0x8f,
0xfd, 0x4f, 0x48, 0x3d, 0x1a, 0x87, 0xf2, 0x09, 0xdd, 0x6a, 0xf6, 0x1a, 0xb4, 0xe4, 0x4a, 0xf2, 0xc6, 0x7f, 0x7c, 0x42, 0xb7, 0x9a, 0xbd, 0x06, 0x2d, 0xb9, 0x92, 0xfc, 0x38, 0x54, 0x1a, 0x7b,
0xe3, 0x50, 0x69, 0xec, 0x25, 0x02, 0x0e, 0x8e, 0x43, 0xf4, 0x83, 0x9c, 0xcd, 0x69, 0x90, 0x5d, 0x89, 0x80, 0x83, 0xe3, 0x10, 0xdd, 0x1e, 0x67, 0x73, 0x1a, 0x64, 0x86, 0xb5, 0x09, 0xdb, 0x95,
0xd6, 0x26, 0x6c, 0x57, 0xee, 0xcd, 0x1b, 0xb0, 0xa2, 0x5d, 0xd5, 0xbc, 0x3f, 0xe6, 0x47, 0x42, 0x7b, 0xf3, 0x06, 0xac, 0x68, 0xcf, 0x34, 0xef, 0x8f, 0xf9, 0x91, 0xd0, 0xd6, 0x76, 0x3c, 0x9d,
0x9b, 0xdf, 0xf1, 0x74, 0x82, 0xc3, 0xe5, 0x7b, 0xfc, 0x48, 0xf8, 0x8f, 0x60, 0x5d, 0xdd, 0xce, 0xe0, 0x70, 0xf9, 0x1e, 0x3f, 0x12, 0xfe, 0x23, 0x58, 0x57, 0xb7, 0xf3, 0x6b, 0x29, 0xd7, 0x43,
0xaf, 0xa5, 0x5c, 0x0f, 0xfd, 0xc5, 0xb2, 0x6e, 0x90, 0x2a, 0x7a, 0x43, 0xf1, 0xa3, 0xed, 0x43, 0xff, 0x6a, 0x59, 0x37, 0x48, 0x15, 0xbd, 0xa1, 0xf8, 0xd1, 0x76, 0x19, 0x4a, 0x0a, 0xc3, 0x0f,
0x94, 0x14, 0x86, 0x1f, 0x00, 0x53, 0xd5, 0x77, 0xc7, 0x49, 0xce, 0x15, 0x41, 0x1f, 0x3a, 0x83, 0x80, 0xa9, 0xea, 0xbb, 0xe3, 0x24, 0xe7, 0x8a, 0xa0, 0x0f, 0x9d, 0xc1, 0x38, 0xc9, 0xb5, 0x4d,
0x71, 0x92, 0x6b, 0x23, 0x5f, 0x2d, 0xc7, 0xc1, 0xf0, 0x04, 0xf2, 0xe9, 0x60, 0x80, 0xf7, 0x5d, 0xaf, 0x96, 0xe3, 0x60, 0x78, 0x02, 0xf9, 0x74, 0x30, 0xc0, 0xfb, 0x2e, 0x95, 0xbc, 0x2e, 0xfa,
0x2a, 0x79, 0x5d, 0xf4, 0xff, 0xca, 0x83, 0x0d, 0xa2, 0xa6, 0xe5, 0x88, 0xb1, 0x0c, 0x5f, 0x7d, 0x7f, 0xe9, 0xc1, 0x06, 0x51, 0xd3, 0x72, 0xc4, 0x18, 0x82, 0xaf, 0x3e, 0xcd, 0xce, 0xc0, 0xf6,
0x9a, 0x9d, 0x81, 0xed, 0xf8, 0x6c, 0xc2, 0xfc, 0x51, 0x92, 0x0d, 0xb8, 0x1a, 0x49, 0x16, 0x7e, 0x73, 0x36, 0x61, 0xfe, 0x28, 0xc9, 0x06, 0x5c, 0x8d, 0x24, 0x0b, 0x3f, 0xbb, 0x69, 0xdb, 0xac,
0x7a, 0x5b, 0xb7, 0x59, 0xb1, 0x75, 0xff, 0xc5, 0x83, 0x75, 0x9a, 0xea, 0x81, 0x08, 0xc5, 0x34, 0x98, 0xb6, 0xff, 0xec, 0xc1, 0x3a, 0x4d, 0xf5, 0x40, 0x84, 0x62, 0x9a, 0xab, 0xe5, 0xff, 0x1a,
0x57, 0xcb, 0xff, 0x55, 0x58, 0xc6, 0xa5, 0x72, 0x7d, 0x69, 0xd4, 0x44, 0x37, 0xcd, 0xfd, 0x26, 0x2c, 0xe3, 0x52, 0xb9, 0xbe, 0x34, 0x6a, 0xa2, 0x9b, 0xe6, 0x7e, 0x13, 0x2a, 0x1b, 0xef, 0x9e,
0x54, 0x36, 0xde, 0x3d, 0x17, 0xb8, 0x8d, 0xd9, 0x97, 0xa1, 0x63, 0xc7, 0x1b, 0x68, 0xce, 0xed, 0x0b, 0xdc, 0xc6, 0xec, 0xcb, 0xd0, 0xb1, 0xc3, 0x0b, 0x34, 0xe7, 0xf6, 0xce, 0x45, 0xbd, 0xca,
0x9d, 0x8b, 0x7a, 0x95, 0x15, 0xce, 0xd9, 0x3d, 0x17, 0x38, 0x1d, 0xd8, 0x2d, 0x00, 0xd2, 0xda, 0x0a, 0xe7, 0xec, 0x9e, 0x0b, 0x9c, 0x0e, 0xec, 0x16, 0x00, 0x69, 0x6d, 0x22, 0xab, 0xfc, 0xc2,
0x44, 0x56, 0x39, 0x8a, 0x17, 0xdd, 0x4d, 0xb2, 0x0e, 0x6b, 0xf7, 0x5c, 0x60, 0x35, 0xbf, 0xb3, 0x8b, 0xee, 0x26, 0x59, 0x87, 0xb5, 0x7b, 0x2e, 0xb0, 0x9a, 0xdf, 0x59, 0x82, 0x05, 0xa9, 0x66,
0x04, 0x0b, 0x52, 0xcd, 0xf8, 0x0f, 0x61, 0xd9, 0x99, 0xa9, 0x63, 0xc3, 0x77, 0xa4, 0x0d, 0x5f, 0xfc, 0x87, 0xb0, 0xec, 0xcc, 0xd4, 0x31, 0xd9, 0x3b, 0xd2, 0x64, 0xaf, 0x78, 0x78, 0x8d, 0xaa,
0x71, 0xf9, 0x1a, 0x55, 0x97, 0xcf, 0xff, 0xbb, 0x06, 0x30, 0xe4, 0xb6, 0xd2, 0x71, 0xa2, 0x9e, 0x87, 0xe7, 0xff, 0x6d, 0x03, 0x18, 0x72, 0x5b, 0xe9, 0x38, 0x51, 0xcf, 0x25, 0x43, 0xc7, 0x6a,
0x4b, 0x86, 0x8e, 0xd5, 0xd2, 0x09, 0x6c, 0x88, 0xdd, 0x00, 0x66, 0x15, 0xb5, 0x67, 0x2f, 0xb5, 0xe9, 0x04, 0x36, 0xc4, 0x6e, 0x00, 0xb3, 0x8a, 0xda, 0x91, 0x97, 0xda, 0xa1, 0xa6, 0x06, 0xc5,
0x43, 0x4d, 0x0d, 0x8a, 0x31, 0x69, 0x72, 0x68, 0x0f, 0x53, 0x59, 0x69, 0xf2, 0xdc, 0x6a, 0xeb, 0x98, 0x34, 0x39, 0xb4, 0x43, 0xa9, 0xac, 0x34, 0x79, 0x6e, 0xb5, 0x75, 0xa8, 0x00, 0xd2, 0x69,
0x50, 0x01, 0xa4, 0xd3, 0xfc, 0x18, 0xf5, 0xb0, 0xb6, 0x6b, 0x74, 0xb9, 0xcc, 0x20, 0x0b, 0x2f, 0x7e, 0x8c, 0x7a, 0x58, 0xdb, 0x35, 0xba, 0x5c, 0x66, 0x90, 0x85, 0x97, 0x32, 0xc8, 0x62, 0x99,
0x65, 0x90, 0xc5, 0x32, 0x83, 0x90, 0xbe, 0xcb, 0xa2, 0x93, 0x50, 0x70, 0xad, 0x43, 0x54, 0x11, 0x41, 0x48, 0xdf, 0x65, 0xd1, 0x49, 0x28, 0xb8, 0xd6, 0x21, 0xaa, 0x88, 0x66, 0xcc, 0x24, 0x8a,
0xcd, 0x98, 0x49, 0x14, 0x93, 0x7a, 0xee, 0x4f, 0x70, 0x74, 0x65, 0xc6, 0x38, 0xa0, 0xff, 0x63, 0x49, 0x3d, 0xf7, 0x27, 0x38, 0xba, 0x32, 0x63, 0x1c, 0xd0, 0xff, 0x89, 0x07, 0x6b, 0xb8, 0x77,
0x0f, 0xd6, 0x70, 0xef, 0x1c, 0xfe, 0x7a, 0x0f, 0x88, 0xbd, 0x5f, 0x91, 0xbd, 0x9c, 0xb6, 0x3f, 0x0e, 0x7f, 0xbd, 0x07, 0xc4, 0xde, 0xaf, 0xc8, 0x5e, 0x4e, 0xdb, 0x5f, 0x9c, 0xbb, 0xde, 0x85,
0x3f, 0x77, 0xbd, 0x0b, 0x2d, 0x22, 0x98, 0xa4, 0x3c, 0x56, 0xcc, 0xd5, 0x75, 0x99, 0xab, 0x90, 0x16, 0x11, 0x4c, 0x52, 0x1e, 0x2b, 0xe6, 0xea, 0xba, 0xcc, 0x55, 0x48, 0x96, 0xdd, 0x73, 0x41,
0x2c, 0xbb, 0xe7, 0x82, 0xa2, 0xb1, 0xc5, 0x5a, 0xff, 0xec, 0x41, 0x5b, 0x4d, 0xf3, 0x67, 0x36, 0xd1, 0xd8, 0x62, 0xad, 0x7f, 0xf2, 0xa0, 0xad, 0xa6, 0xf9, 0x73, 0x1b, 0xdb, 0x3d, 0x58, 0x42,
0xb6, 0x7b, 0xb0, 0x84, 0x5c, 0x66, 0xd9, 0xb2, 0xa6, 0x8c, 0x7a, 0x60, 0x82, 0x1e, 0x0d, 0x2a, 0x2e, 0xb3, 0x6c, 0x59, 0x53, 0x46, 0x3d, 0x30, 0x41, 0x8f, 0x06, 0x15, 0x9f, 0x63, 0x68, 0x97,
0x3e, 0xc7, 0xd0, 0x2e, 0xc3, 0xa8, 0xc5, 0x48, 0x88, 0xe6, 0x7d, 0x11, 0x8d, 0xfb, 0xba, 0x56, 0x61, 0xd4, 0x62, 0x24, 0x44, 0xf3, 0xbe, 0x88, 0xc6, 0x7d, 0x5d, 0xab, 0x22, 0x74, 0x75, 0x55,
0x85, 0xec, 0xea, 0xaa, 0x50, 0x96, 0xe4, 0x22, 0x1c, 0x71, 0xa5, 0xa0, 0x64, 0x01, 0x3d, 0x0a, 0x28, 0x4b, 0x72, 0x11, 0x8e, 0xb8, 0x52, 0x50, 0xb2, 0x80, 0x1e, 0x85, 0x5a, 0x50, 0xd9, 0x88,
0xb5, 0xa0, 0xb2, 0x11, 0xf5, 0x23, 0x80, 0x0b, 0x95, 0x2a, 0x63, 0x48, 0x29, 0xdb, 0x71, 0x1c, 0xfa, 0x31, 0xc0, 0x85, 0x4a, 0x95, 0x31, 0xa4, 0x94, 0xed, 0x38, 0x8e, 0x26, 0x87, 0x89, 0x31,
0x4d, 0x0e, 0x13, 0x63, 0x86, 0x7a, 0xb6, 0x59, 0xe9, 0x54, 0xb1, 0x11, 0x9c, 0xd7, 0x9a, 0x18, 0x43, 0x3d, 0xdb, 0xac, 0x74, 0xaa, 0xd8, 0x08, 0xce, 0x6b, 0x4d, 0x8c, 0x7b, 0x5a, 0xe8, 0xdd,
0xf7, 0xb4, 0xd0, 0xbb, 0x0d, 0x32, 0x21, 0xde, 0x76, 0x79, 0xa0, 0x3c, 0xa0, 0xc6, 0xed, 0xdb, 0x06, 0x99, 0x10, 0x6f, 0xbb, 0x3c, 0x50, 0x1e, 0x50, 0xe3, 0xf6, 0x6d, 0xac, 0xa7, 0xc7, 0x8e,
0x58, 0x4f, 0x8f, 0x1d, 0x43, 0xd7, 0xa8, 0x7c, 0x25, 0xb6, 0x2d, 0xb3, 0x00, 0xc7, 0x7a, 0xeb, 0xa1, 0x6b, 0x54, 0xbe, 0x12, 0xdb, 0x96, 0x59, 0x80, 0x63, 0xbd, 0xf5, 0x92, 0xb1, 0x48, 0xc6,
0x25, 0x63, 0x91, 0x8c, 0x19, 0xea, 0x61, 0xce, 0xa4, 0xc6, 0x66, 0x70, 0x59, 0xd7, 0x91, 0x5c, 0x0c, 0xf5, 0x30, 0x67, 0x52, 0x63, 0x33, 0xb8, 0xac, 0xeb, 0x48, 0x2e, 0x57, 0xc7, 0x6b, 0xbe,
0xae, 0x8e, 0xd7, 0x7c, 0xa5, 0xb5, 0x3d, 0xc0, 0xce, 0xee, 0xa0, 0x2f, 0x21, 0xdc, 0xfb, 0x91, 0xd2, 0xda, 0x1e, 0x60, 0x67, 0x77, 0xd0, 0x97, 0x10, 0xee, 0xfd, 0xd8, 0x83, 0x15, 0x97, 0x1c,
0x07, 0x2b, 0x2e, 0x39, 0x64, 0x1d, 0xe5, 0x8f, 0x68, 0x01, 0xa3, 0x4d, 0xa9, 0x12, 0x5c, 0xf5, 0xb2, 0x8e, 0xf2, 0x47, 0xb4, 0x80, 0xd1, 0xa6, 0x54, 0x09, 0xae, 0x7a, 0x54, 0x8d, 0x3a, 0x8f,
0xa8, 0x1a, 0x75, 0x1e, 0x95, 0xed, 0x37, 0xcd, 0xbd, 0xcc, 0x6f, 0x6a, 0xbe, 0x9a, 0xdf, 0x34, 0xca, 0xf6, 0x9b, 0xe6, 0x5e, 0xe6, 0x37, 0x35, 0x5f, 0xcd, 0x6f, 0x9a, 0xaf, 0xf3, 0x9b, 0x7a,
0x5f, 0xe7, 0x37, 0xf5, 0xfe, 0xcb, 0x03, 0x56, 0x3d, 0x5f, 0xf6, 0x50, 0xba, 0x74, 0x31, 0x1f, 0xff, 0xe9, 0x01, 0xab, 0x9e, 0x2f, 0x7b, 0x28, 0x5d, 0xba, 0x98, 0x8f, 0x95, 0x9c, 0xf8, 0xe5,
0x2b, 0x39, 0xf1, 0xcb, 0xaf, 0xc6, 0x23, 0x7a, 0x0f, 0x75, 0x6f, 0x64, 0x56, 0x5b, 0x10, 0xd8, 0x57, 0xe3, 0x11, 0xbd, 0x87, 0xba, 0x37, 0x32, 0xab, 0x2d, 0x08, 0x6c, 0x53, 0x64, 0x39, 0xa8,
0xa6, 0xc8, 0x72, 0x50, 0x57, 0x55, 0xf2, 0xe4, 0x9a, 0x2f, 0xf7, 0xe4, 0xe6, 0x5f, 0xee, 0xc9, 0xab, 0x2a, 0x79, 0x72, 0xcd, 0x97, 0x7b, 0x72, 0xf3, 0x2f, 0xf7, 0xe4, 0x16, 0xca, 0x9e, 0x5c,
0x2d, 0x94, 0x3d, 0xb9, 0xde, 0xef, 0xc0, 0xb2, 0x73, 0xea, 0xff, 0x7b, 0x2b, 0x2e, 0x9b, 0x31, 0xef, 0x77, 0x61, 0xd9, 0x39, 0xf5, 0xff, 0xb9, 0x15, 0x97, 0xcd, 0x18, 0x79, 0xc0, 0x0e, 0xd6,
0xf2, 0x80, 0x1d, 0xac, 0xf7, 0x9f, 0x0d, 0x60, 0x55, 0xce, 0xfb, 0x7f, 0x9d, 0x03, 0xf1, 0x91, 0xfb, 0x8f, 0x06, 0xb0, 0x2a, 0xe7, 0xfd, 0x9f, 0xce, 0x81, 0xf8, 0xc8, 0x11, 0x20, 0x73, 0x8a,
0x23, 0x40, 0xe6, 0x14, 0x1f, 0x39, 0xa2, 0xe3, 0xff, 0x52, 0x28, 0xbe, 0x05, 0xeb, 0x19, 0x1f, 0x8f, 0x1c, 0xd1, 0xf1, 0xbf, 0x29, 0x14, 0xdf, 0x82, 0xf5, 0x8c, 0x0f, 0x92, 0x13, 0x7a, 0xa5,
0x24, 0x27, 0xf4, 0x6c, 0xe5, 0x46, 0x01, 0xaa, 0x15, 0x68, 0xc8, 0xb9, 0xfe, 0xeb, 0x92, 0xf3, 0x72, 0xa3, 0x00, 0xd5, 0x0a, 0x34, 0xe4, 0x5c, 0xff, 0x75, 0xc9, 0x79, 0x54, 0xb0, 0x34, 0x43,
0xca, 0x60, 0x69, 0x86, 0x92, 0x1b, 0xeb, 0x7f, 0x11, 0x36, 0xe5, 0xe3, 0xcf, 0x1d, 0x49, 0x4a, 0xc9, 0x8d, 0xf5, 0xb7, 0x60, 0x53, 0xbe, 0xf5, 0xdc, 0x91, 0xa4, 0xb4, 0x90, 0xfd, 0xbe, 0x07,
0xdb, 0x12, 0xaf, 0x43, 0xe7, 0x54, 0x86, 0xe9, 0xfa, 0x49, 0x3c, 0x9e, 0x29, 0x25, 0xd2, 0x56, 0xe7, 0x4b, 0x15, 0x45, 0xe4, 0x5d, 0xca, 0x51, 0x57, 0xb8, 0xba, 0x20, 0xce, 0x5f, 0x31, 0xb0,
0xd8, 0xd7, 0xe2, 0xf1, 0xcc, 0xff, 0x9e, 0x07, 0xe7, 0x4b, 0x7d, 0x8b, 0x68, 0xbd, 0x14, 0xb5, 0x35, 0x7f, 0xa9, 0x6f, 0xaa, 0x15, 0xb8, 0x3f, 0xd3, 0xb8, 0xda, 0x5e, 0xee, 0x7a, 0x5d, 0x95,
0xae, 0xfc, 0x75, 0x41, 0x5c, 0xa2, 0xe2, 0x71, 0x6b, 0x89, 0x52, 0x25, 0x55, 0x2b, 0x70, 0x0b, 0x7f, 0x01, 0xce, 0xab, 0x93, 0x2d, 0x4d, 0x7c, 0x07, 0xb6, 0xca, 0x15, 0x45, 0x28, 0xd1, 0x9d,
0xa7, 0x71, 0xb5, 0xbd, 0x3c, 0x98, 0xba, 0x2a, 0xff, 0x02, 0x9c, 0x57, 0x87, 0xef, 0xae, 0xcd, 0xb2, 0x2e, 0xfa, 0xbf, 0x03, 0xec, 0xeb, 0x53, 0x9e, 0xcd, 0x28, 0xc6, 0x6f, 0xc2, 0xa4, 0x17,
0xdf, 0x81, 0xad, 0x72, 0x45, 0x11, 0x6d, 0x74, 0xa7, 0xac, 0x8b, 0xfe, 0x6f, 0x03, 0xfb, 0xfa, 0xca, 0x9e, 0xf5, 0x42, 0x3a, 0x3d, 0xfc, 0x2a, 0x9f, 0xe9, 0x47, 0x94, 0x46, 0xf1, 0x88, 0xf2,
0x94, 0x67, 0x33, 0x7a, 0x17, 0x30, 0xa1, 0xd5, 0x0b, 0x65, 0xe7, 0x7b, 0x21, 0x9d, 0x1e, 0x7e, 0x39, 0x00, 0x74, 0x15, 0xe8, 0x51, 0x40, 0x3f, 0x6b, 0xa1, 0x27, 0x26, 0x09, 0xfa, 0xb7, 0x60,
0x95, 0xcf, 0xf4, 0xc3, 0x4b, 0xa3, 0x78, 0x78, 0xf9, 0x0c, 0x00, 0x7a, 0x13, 0xf4, 0x90, 0xa0, 0xc3, 0xa1, 0x6f, 0x76, 0x72, 0x41, 0xf5, 0x90, 0xee, 0xaa, 0xfb, 0xd4, 0xa0, 0xea, 0xfc, 0x3f,
0x9f, 0xc2, 0xd0, 0x59, 0x93, 0x04, 0xfd, 0x5b, 0xb0, 0xe1, 0xd0, 0x37, 0x3b, 0xb9, 0xa0, 0x7a, 0xf3, 0x60, 0x6e, 0x37, 0x49, 0xed, 0x48, 0x92, 0xe7, 0x46, 0x92, 0x94, 0xdc, 0xec, 0x1b, 0xb1,
0x48, 0x8f, 0xd6, 0x7d, 0x9e, 0x50, 0x75, 0xfe, 0x9f, 0x79, 0x30, 0xb7, 0x9b, 0xa4, 0x76, 0xb0, 0xd8, 0x50, 0xb7, 0xde, 0x06, 0x51, 0xea, 0x85, 0x13, 0x81, 0x0e, 0xdb, 0x51, 0x92, 0x9d, 0x86,
0xc9, 0x73, 0x83, 0x4d, 0x4a, 0xb4, 0xf6, 0x8d, 0xe4, 0x6c, 0x28, 0xc1, 0x60, 0x83, 0x28, 0x18, 0xd9, 0x50, 0x6d, 0x6f, 0x09, 0xc5, 0xd5, 0x15, 0xc2, 0x05, 0x7f, 0xa2, 0xc1, 0x40, 0xe1, 0xb4,
0xc3, 0x89, 0x40, 0x9f, 0xee, 0x28, 0xc9, 0x4e, 0xc3, 0x6c, 0xa8, 0xb6, 0xb7, 0x84, 0xe2, 0xea, 0x99, 0xf2, 0x31, 0x55, 0xc9, 0xff, 0xae, 0x07, 0xf3, 0x34, 0x57, 0xbc, 0x09, 0xf2, 0xf8, 0xe9,
0x0a, 0xf9, 0x83, 0x3f, 0xd1, 0xa6, 0xa0, 0x88, 0xdb, 0x4c, 0xb9, 0xa1, 0xaa, 0xe4, 0x7f, 0xc7, 0x7d, 0x8d, 0xa2, 0x75, 0x9e, 0xbc, 0x09, 0x25, 0xb8, 0xf4, 0xea, 0xd6, 0xa8, 0xbc, 0xba, 0x5d,
0x83, 0x79, 0x9a, 0x2b, 0x5e, 0x16, 0x79, 0xfc, 0xf4, 0x26, 0x47, 0x01, 0x3d, 0x4f, 0x5e, 0x96, 0x82, 0x96, 0x2c, 0x15, 0xcf, 0x54, 0x05, 0xc0, 0x2e, 0x43, 0xf3, 0x38, 0x49, 0xb5, 0xfe, 0x02,
0x12, 0x5c, 0x7a, 0xa9, 0x6b, 0x54, 0x5e, 0xea, 0x2e, 0x41, 0x4b, 0x96, 0x8a, 0xa7, 0xad, 0x02, 0x1d, 0x9e, 0x49, 0xd2, 0x80, 0x70, 0xff, 0x3a, 0xac, 0x3e, 0x4a, 0x86, 0xdc, 0xf2, 0xee, 0xcf,
0x60, 0x97, 0xa1, 0x79, 0x9c, 0xa4, 0x5a, 0xc5, 0x81, 0x8e, 0xe0, 0x24, 0x69, 0x40, 0xb8, 0x7f, 0x3c, 0x45, 0xff, 0xf7, 0x3c, 0x58, 0xd2, 0x8d, 0xd9, 0x35, 0x68, 0xa2, 0x1a, 0x2a, 0x19, 0x7e,
0x1d, 0x56, 0x1f, 0x25, 0x43, 0x6e, 0x05, 0x00, 0xce, 0x3c, 0x45, 0xff, 0x77, 0x3d, 0x58, 0xd2, 0x26, 0x94, 0x8a, 0xed, 0x02, 0x6a, 0x81, 0xe2, 0x83, 0xbc, 0xc2, 0xc2, 0x4c, 0xd0, 0x3e, 0x61,
0x8d, 0xd9, 0x35, 0x68, 0xa2, 0xa6, 0x2a, 0xd9, 0x86, 0x26, 0xda, 0x8a, 0xed, 0x02, 0x6a, 0x81, 0xa1, 0x80, 0xdf, 0x84, 0x15, 0x39, 0xe7, 0x92, 0xa2, 0x2a, 0xa1, 0xfe, 0x5f, 0x79, 0xb0, 0xec,
0x12, 0x86, 0x1c, 0xc7, 0xc2, 0x92, 0xd0, 0x6e, 0x63, 0xa1, 0xa3, 0xdf, 0x84, 0x15, 0x39, 0xe7, 0x8c, 0x81, 0x26, 0xfc, 0x38, 0xcc, 0x85, 0x0a, 0x4c, 0xa9, 0x4d, 0xb4, 0x21, 0x3b, 0xde, 0xd3,
0x92, 0x2e, 0x2b, 0xa1, 0xfe, 0x5f, 0x7b, 0xb0, 0xec, 0x8c, 0x81, 0x56, 0xfe, 0x38, 0xcc, 0x85, 0x70, 0xe3, 0x3d, 0x26, 0x12, 0x31, 0x67, 0x47, 0x22, 0x6e, 0x42, 0xab, 0x78, 0xc1, 0x6c, 0x3a,
0x8a, 0x5d, 0xa9, 0x4d, 0xb4, 0x21, 0x3b, 0x24, 0xd4, 0x70, 0x43, 0x42, 0x26, 0x58, 0x31, 0x67, 0x62, 0x01, 0x47, 0xd4, 0x41, 0xe2, 0xa2, 0x11, 0xd2, 0x19, 0x24, 0xe3, 0x24, 0x53, 0x0f, 0x7c,
0x07, 0x2b, 0x6e, 0x42, 0xab, 0x78, 0xf5, 0x6c, 0x3a, 0x92, 0x03, 0x47, 0xd4, 0x71, 0xe4, 0xa2, 0xb2, 0xe0, 0xdf, 0x82, 0xb6, 0xd5, 0x1e, 0xa7, 0x11, 0x73, 0x71, 0x9a, 0x64, 0x4f, 0x75, 0xd8,
0x11, 0xd2, 0x19, 0x24, 0xe3, 0x24, 0x53, 0x8f, 0x82, 0xb2, 0xe0, 0xdf, 0x82, 0xb6, 0xd5, 0x1e, 0x49, 0x15, 0xcd, 0x5b, 0x48, 0xa3, 0x78, 0x0b, 0xf1, 0xff, 0xda, 0x83, 0x65, 0xe4, 0x94, 0x28,
0xa7, 0x11, 0x73, 0x71, 0x9a, 0x64, 0x4f, 0x75, 0x64, 0x4a, 0x15, 0xcd, 0xfb, 0x49, 0xa3, 0x78, 0x1e, 0xed, 0x27, 0xe3, 0x68, 0x30, 0x23, 0x8e, 0xd1, 0x4c, 0xa1, 0x5e, 0xfe, 0x34, 0xc7, 0xb8,
0x3f, 0xf1, 0xff, 0xc6, 0x83, 0x65, 0xe4, 0x94, 0x28, 0x1e, 0xed, 0x27, 0xe3, 0x68, 0x30, 0x23, 0x30, 0xea, 0x7b, 0x6d, 0xc1, 0x2b, 0x7e, 0x31, 0x65, 0xe4, 0x7c, 0xd4, 0x5b, 0x87, 0x61, 0xce,
0x8e, 0xd1, 0x4c, 0xa1, 0x5e, 0x0b, 0x35, 0xc7, 0xb8, 0x30, 0x9a, 0x04, 0xda, 0xc8, 0x57, 0xfc, 0xa5, 0xc9, 0xaf, 0xe4, 0xb4, 0x03, 0xa2, 0x74, 0x41, 0x20, 0x0b, 0x05, 0xef, 0x4f, 0xa2, 0xf1,
0x62, 0xca, 0xc8, 0xf9, 0xa8, 0xda, 0x0e, 0xc3, 0x9c, 0x4b, 0xaf, 0x40, 0x89, 0x72, 0x07, 0x44, 0x38, 0x92, 0x6d, 0x25, 0x87, 0xd7, 0x55, 0xf9, 0x3f, 0x6a, 0x40, 0x5b, 0x49, 0x91, 0xfb, 0xc3,
0xe9, 0x82, 0x40, 0x16, 0x0a, 0xde, 0x9f, 0x44, 0xe3, 0x71, 0x24, 0xdb, 0x4a, 0x0e, 0xaf, 0xab, 0x91, 0x8c, 0xa0, 0x2a, 0x23, 0xc4, 0x5c, 0x3f, 0x0b, 0xd1, 0xf5, 0x8e, 0xd9, 0x62, 0x21, 0xe5,
0xf2, 0x7f, 0xd8, 0x80, 0xb6, 0x92, 0x22, 0xf7, 0x87, 0x23, 0x19, 0x64, 0x55, 0x76, 0x8a, 0xb9, 0x63, 0x9d, 0xab, 0x1e, 0xeb, 0x25, 0x68, 0x21, 0x7b, 0xbd, 0x4d, 0xf6, 0x91, 0x7c, 0xf0, 0x2e,
0x7e, 0x16, 0xa2, 0xeb, 0x1d, 0xcb, 0xc6, 0x42, 0xca, 0xc7, 0x3a, 0x57, 0x3d, 0xd6, 0x4b, 0xd0, 0x00, 0x5d, 0xbb, 0x43, 0xb5, 0xf3, 0x45, 0x2d, 0x01, 0x8e, 0x45, 0xb4, 0x50, 0xb2, 0x88, 0xde,
0x42, 0xf6, 0x7a, 0x9b, 0x4c, 0x28, 0xf9, 0x48, 0x5e, 0x00, 0xba, 0x76, 0x87, 0x6a, 0xe7, 0x8b, 0x85, 0x8e, 0x22, 0x43, 0xfb, 0x4e, 0x0e, 0x55, 0xc1, 0xe0, 0xce, 0x99, 0x04, 0x4e, 0x4b, 0xdd,
0x5a, 0x02, 0x1c, 0xa3, 0x69, 0xa1, 0x64, 0x34, 0xbd, 0x0b, 0x1d, 0x45, 0x86, 0xf6, 0x9d, 0x7c, 0x73, 0x47, 0xf7, 0x5c, 0x7a, 0x59, 0x4f, 0xdd, 0x92, 0x9e, 0x15, 0xe4, 0xde, 0x3c, 0xcc, 0xc2,
0xae, 0x82, 0xc1, 0x9d, 0x33, 0x09, 0x9c, 0x96, 0xba, 0xe7, 0x8e, 0xee, 0xb9, 0xf4, 0xb2, 0x9e, 0xf4, 0x58, 0x4b, 0xe6, 0xa1, 0x79, 0x2b, 0x25, 0x98, 0x5d, 0x87, 0x79, 0xec, 0xa6, 0xa5, 0x5f,
0xba, 0x25, 0xbd, 0x3c, 0xc8, 0xbd, 0x79, 0x98, 0x85, 0xe9, 0xb1, 0x96, 0xcc, 0x43, 0xf3, 0xbe, 0xfd, 0xa5, 0x93, 0x4d, 0xd8, 0x35, 0x98, 0xe7, 0xc3, 0x11, 0xd7, 0x56, 0x39, 0x73, 0xfd, 0x23,
0x4a, 0x30, 0xbb, 0x0e, 0xf3, 0xd8, 0x4d, 0x4b, 0xbf, 0xfa, 0x4b, 0x27, 0x9b, 0xb0, 0x6b, 0x30, 0x3c, 0xa3, 0x40, 0x36, 0x40, 0x11, 0x80, 0x68, 0x49, 0x04, 0xb8, 0x92, 0x73, 0x01, 0x8b, 0x1f,
0xcf, 0x87, 0x23, 0xae, 0x0d, 0x77, 0xe6, 0xba, 0x50, 0x78, 0x46, 0x81, 0x6c, 0x80, 0x22, 0x00, 0x0c, 0xfd, 0x4d, 0x60, 0x8f, 0x24, 0xd7, 0xda, 0xf1, 0xc0, 0x3f, 0x9c, 0x83, 0xb6, 0x05, 0xe3,
0xd1, 0x92, 0x08, 0x70, 0x25, 0xe7, 0x02, 0x16, 0xdf, 0x1f, 0xfa, 0x9b, 0xc0, 0x1e, 0x49, 0xae, 0x6d, 0x1e, 0xe1, 0x84, 0xfb, 0xc3, 0x28, 0x9c, 0x70, 0xc1, 0x33, 0xc5, 0xa9, 0x25, 0x94, 0x04,
0xb5, 0x43, 0x86, 0x7f, 0x30, 0x07, 0x6d, 0x0b, 0xc6, 0xdb, 0x3c, 0xc2, 0x09, 0xf7, 0x87, 0x51, 0xec, 0xc9, 0xa8, 0x9f, 0x4c, 0x45, 0x7f, 0xc8, 0x47, 0x19, 0x97, 0xfa, 0xce, 0x0b, 0x4a, 0x28,
0x38, 0xe1, 0x82, 0x67, 0x8a, 0x53, 0x4b, 0x28, 0x09, 0xd8, 0x93, 0x51, 0x3f, 0x99, 0x8a, 0xfe, 0xb6, 0x9b, 0x84, 0xcf, 0xec, 0x76, 0x92, 0x1f, 0x4a, 0xa8, 0x8e, 0xee, 0xc9, 0x3d, 0x6a, 0x16,
0x90, 0x8f, 0x32, 0x2e, 0xf5, 0x9d, 0x17, 0x94, 0x50, 0x6c, 0x37, 0x09, 0x9f, 0xd9, 0xed, 0x24, 0xd1, 0x3d, 0xb9, 0x23, 0x65, 0x39, 0x34, 0x5f, 0x23, 0x87, 0xde, 0x81, 0x2d, 0x29, 0x71, 0xd4,
0x3f, 0x94, 0x50, 0x1d, 0x00, 0x94, 0x7b, 0xd4, 0x2c, 0x02, 0x80, 0x72, 0x47, 0xca, 0x72, 0x68, 0xdd, 0xec, 0x97, 0xd8, 0xe4, 0x8c, 0x5a, 0x76, 0x1d, 0xd6, 0x70, 0xce, 0x9a, 0xc1, 0xf3, 0xe8,
0xbe, 0x46, 0x0e, 0xbd, 0x03, 0x5b, 0x52, 0xe2, 0xa8, 0xbb, 0xd9, 0x2f, 0xb1, 0xc9, 0x19, 0xb5, 0x53, 0xe9, 0x89, 0x7b, 0x41, 0x05, 0xc7, 0xb6, 0x78, 0x1d, 0x9d, 0xb6, 0xf2, 0x89, 0xa1, 0x82,
0xec, 0x3a, 0xac, 0xe1, 0x9c, 0x35, 0x83, 0xe7, 0xd1, 0x27, 0xd2, 0x59, 0xf7, 0x82, 0x0a, 0x8e, 0x53, 0xdb, 0xf0, 0x99, 0xdb, 0xb6, 0xa5, 0xda, 0x96, 0x70, 0x7f, 0x19, 0xda, 0x07, 0x22, 0x49,
0x6d, 0xf1, 0x3a, 0x3a, 0x6d, 0xe5, 0x2b, 0x44, 0x05, 0xa7, 0xb6, 0xe1, 0x33, 0xb7, 0x6d, 0x4b, 0xf5, 0xa1, 0xac, 0x40, 0x47, 0x16, 0xd5, 0xb3, 0xd2, 0x6b, 0x70, 0x91, 0xb8, 0xe8, 0x71, 0x92,
0xb5, 0x2d, 0xe1, 0xfe, 0x32, 0xb4, 0x0f, 0x44, 0x92, 0xea, 0x43, 0x59, 0x81, 0x8e, 0x2c, 0xaa, 0x26, 0xe3, 0x64, 0x34, 0x3b, 0x98, 0x1e, 0xe6, 0x83, 0x2c, 0x4a, 0xd1, 0x5a, 0xf6, 0xff, 0xd1,
0x97, 0xa7, 0xd7, 0xe0, 0x22, 0x71, 0xd1, 0xe3, 0x24, 0x4d, 0xc6, 0xc9, 0x68, 0x76, 0x30, 0x3d, 0x83, 0x0d, 0xa7, 0x56, 0xb9, 0xf9, 0x5f, 0x94, 0x2c, 0x6d, 0x5e, 0x02, 0x24, 0xe3, 0xad, 0x5b,
0xcc, 0x07, 0x59, 0x94, 0xa2, 0x41, 0xed, 0xff, 0x93, 0x07, 0x1b, 0x4e, 0xad, 0x8a, 0x04, 0x7c, 0xe2, 0x50, 0x36, 0x94, 0x41, 0x93, 0x27, 0xea, 0x71, 0xe0, 0x36, 0xac, 0xea, 0x99, 0xe9, 0x8e,
0x5e, 0xb2, 0xb4, 0x79, 0x2c, 0x90, 0x8c, 0xb7, 0x6e, 0x89, 0x43, 0xd9, 0x50, 0xc6, 0x55, 0x9e, 0x92, 0x0b, 0xbb, 0x55, 0x2e, 0x54, 0xfd, 0x57, 0x54, 0x07, 0x4d, 0xe2, 0xd7, 0xa5, 0xcd, 0xc9,
0xa8, 0xf7, 0x83, 0xdb, 0xb0, 0xaa, 0x67, 0xa6, 0x3b, 0x4a, 0x2e, 0xec, 0x56, 0xb9, 0x50, 0xf5, 0x87, 0xb4, 0x46, 0xed, 0xef, 0xf5, 0x74, 0x7f, 0xdb, 0xd0, 0xd5, 0x33, 0x18, 0x18, 0x30, 0xf7,
0x5f, 0x51, 0x1d, 0x34, 0x89, 0x5f, 0x93, 0x66, 0x29, 0x1f, 0xd2, 0x1a, 0xb5, 0x4b, 0xd8, 0xd3, 0xff, 0xd8, 0x03, 0x28, 0x66, 0x87, 0x8c, 0x51, 0x88, 0x74, 0x99, 0xe9, 0x64, 0x89, 0xef, 0xd7,
0xfd, 0x6d, 0x5b, 0x58, 0xcf, 0x60, 0x60, 0xc0, 0xdc, 0xff, 0x23, 0x0f, 0xa0, 0x98, 0x1d, 0x32, 0xa1, 0x63, 0x62, 0xd4, 0x85, 0x96, 0x68, 0x6b, 0x0c, 0x0d, 0x98, 0xab, 0xb0, 0x3a, 0x1a, 0x27,
0x46, 0x21, 0xd2, 0x65, 0x76, 0x94, 0x25, 0xbe, 0x5f, 0x87, 0x8e, 0x09, 0x63, 0x17, 0x5a, 0xa2, 0x87, 0xa4, 0x73, 0xe9, 0x9d, 0x32, 0x57, 0x8f, 0x6b, 0x2b, 0x12, 0x7e, 0xa0, 0xd0, 0x42, 0xa5,
0xad, 0x31, 0x34, 0x60, 0xae, 0xc2, 0xea, 0x68, 0x9c, 0x1c, 0x92, 0xce, 0xa5, 0xa7, 0xcc, 0x5c, 0x34, 0x2d, 0x95, 0xe2, 0x7f, 0xa7, 0x61, 0x62, 0x9e, 0xc5, 0x9a, 0xcf, 0xbc, 0x65, 0x6c, 0xa7,
0xbd, 0xbf, 0xad, 0x48, 0xf8, 0x81, 0x42, 0x0b, 0x95, 0xd2, 0xb4, 0x54, 0x8a, 0xff, 0xc7, 0x0d, 0x22, 0x1c, 0xcf, 0x08, 0x31, 0x52, 0x64, 0x63, 0xff, 0xa5, 0x4e, 0xde, 0x2d, 0x58, 0xc9, 0xa4,
0x13, 0x16, 0x2d, 0xd6, 0x7c, 0xe6, 0x2d, 0x63, 0x3b, 0x15, 0xe1, 0x78, 0x46, 0x14, 0x92, 0x82, 0xf4, 0xd1, 0xa2, 0xa9, 0xf9, 0x02, 0xd1, 0xb4, 0x9c, 0x39, 0x7a, 0xe7, 0xff, 0xc3, 0x5a, 0x38,
0x1f, 0xfb, 0x2f, 0xf5, 0x03, 0x6f, 0xc1, 0x4a, 0x26, 0xa5, 0x8f, 0x16, 0x4d, 0xcd, 0x17, 0x88, 0x3c, 0xe1, 0x99, 0x88, 0xc8, 0xda, 0x27, 0xa5, 0x2f, 0x05, 0xea, 0xaa, 0x85, 0x93, 0x2e, 0xbe,
0xa6, 0xe5, 0xcc, 0xd1, 0x3b, 0xbf, 0x08, 0x6b, 0xe1, 0xf0, 0x84, 0x67, 0x22, 0x22, 0x87, 0x80, 0x0a, 0xab, 0xea, 0x41, 0xd3, 0xb4, 0x54, 0x69, 0x2c, 0x05, 0x8c, 0x0d, 0xfd, 0x1f, 0xea, 0xf0,
0x94, 0xbe, 0x14, 0xa8, 0xab, 0x16, 0x4e, 0xba, 0xf8, 0x2a, 0xac, 0xaa, 0x37, 0x4f, 0xd3, 0x52, 0xaa, 0x7b, 0x86, 0x67, 0xef, 0x88, 0xbd, 0xba, 0x46, 0x69, 0x75, 0xff, 0x4f, 0x85, 0x3a, 0x87,
0xa5, 0xbe, 0x14, 0x30, 0x36, 0xf4, 0x7f, 0xa0, 0x23, 0xb0, 0xee, 0x19, 0x9e, 0xbd, 0x23, 0xf6, 0xda, 0xa5, 0x50, 0x41, 0x67, 0x09, 0xaa, 0xd0, 0xb4, 0xbb, 0xa5, 0xcd, 0x57, 0xd9, 0x52, 0xff,
0xea, 0x1a, 0xa5, 0xd5, 0xfd, 0x82, 0x8a, 0x86, 0x0e, 0xb5, 0xd7, 0xa1, 0xe2, 0xd2, 0x12, 0x54, 0xfb, 0x73, 0xb0, 0xf8, 0x41, 0x7c, 0x92, 0x44, 0x03, 0x0a, 0x3c, 0x4e, 0xf8, 0x24, 0xd1, 0xb9,
0xd1, 0x6b, 0x77, 0x4b, 0x9b, 0xaf, 0xb2, 0xa5, 0xfe, 0xf7, 0xe6, 0x60, 0xf1, 0xfd, 0xf8, 0x24, 0x02, 0xf8, 0x1b, 0x35, 0x3a, 0xbd, 0x98, 0xa5, 0x42, 0x45, 0x0e, 0x75, 0x11, 0xb5, 0x5b, 0x56,
0x89, 0x06, 0x14, 0x9b, 0x9c, 0xf0, 0x49, 0xa2, 0xf3, 0x0b, 0xf0, 0x37, 0x6a, 0x74, 0x7a, 0x54, 0xe4, 0xcf, 0x48, 0x4e, 0xb1, 0x10, 0xb4, 0x0f, 0x33, 0x3b, 0x79, 0x48, 0x95, 0x8a, 0x64, 0x8b,
0x4b, 0x85, 0x0a, 0x2e, 0xea, 0x22, 0x6a, 0xb7, 0xac, 0xc8, 0xb9, 0x91, 0x9c, 0x62, 0x21, 0x68, 0x79, 0x2b, 0xd9, 0x82, 0xc2, 0xd4, 0xf2, 0x31, 0x90, 0xb6, 0x73, 0x29, 0xd0, 0x45, 0xb2, 0x63,
0x1f, 0x66, 0x76, 0xc2, 0x91, 0x2a, 0x15, 0x09, 0x1a, 0xf3, 0x56, 0x82, 0x06, 0x45, 0xb2, 0xe5, 0x33, 0x2e, 0x1d, 0x5e, 0xd2, 0x93, 0x8b, 0xca, 0x8e, 0xb5, 0x41, 0xd4, 0xa5, 0xb2, 0x83, 0x6c,
0x7b, 0x21, 0x6d, 0xe7, 0x52, 0xa0, 0x8b, 0x64, 0xc7, 0x66, 0x5c, 0xfa, 0xc4, 0xa4, 0x27, 0x17, 0x23, 0x65, 0x8d, 0x0d, 0xa1, 0x6d, 0x51, 0xce, 0x3f, 0x6a, 0xc9, 0x23, 0x2e, 0xc1, 0x28, 0x90,
0x95, 0x1d, 0x6b, 0x83, 0xa8, 0x4b, 0x65, 0x07, 0xd9, 0x46, 0xca, 0x1a, 0x1b, 0x42, 0xdb, 0xa2, 0x86, 0xdc, 0xc8, 0x0d, 0xb9, 0x06, 0x90, 0xf9, 0x41, 0x65, 0xdc, 0xb2, 0x82, 0xe5, 0xa3, 0xa6,
0x9c, 0xb3, 0xd4, 0x92, 0x47, 0x5c, 0x82, 0x51, 0x20, 0x0d, 0xb9, 0x91, 0x1b, 0x72, 0x0d, 0x20, 0x2a, 0x91, 0x0d, 0x12, 0x8e, 0xc7, 0x87, 0xe1, 0xe0, 0x29, 0x65, 0x85, 0xd1, 0x1b, 0x66, 0x2b,
0x73, 0x8a, 0xca, 0xb8, 0x65, 0x05, 0xcb, 0x77, 0x4f, 0x55, 0x22, 0x1b, 0x24, 0x1c, 0x8f, 0x0f, 0x70, 0x41, 0x9c, 0x35, 0x25, 0x39, 0x29, 0x12, 0xcb, 0xf2, 0x0d, 0xd2, 0x82, 0xfc, 0x6f, 0x00,
0xc3, 0xc1, 0x53, 0xca, 0x24, 0xa3, 0x67, 0xce, 0x56, 0xe0, 0x82, 0x38, 0x6b, 0x4a, 0x8c, 0x52, 0xbb, 0x3d, 0x1c, 0xaa, 0x13, 0x32, 0x3e, 0x42, 0xb1, 0xb7, 0x9e, 0xb3, 0xb7, 0x35, 0x6b, 0x6c,
0x24, 0x96, 0xe5, 0x33, 0xa5, 0x05, 0xf9, 0xdf, 0x00, 0x76, 0x7b, 0x38, 0x54, 0x27, 0x64, 0x7c, 0xd4, 0xae, 0xd1, 0xbf, 0x0f, 0xed, 0x7d, 0x2b, 0x99, 0x8b, 0x0e, 0x53, 0xa7, 0x71, 0x29, 0x06,
0x84, 0x62, 0x6f, 0x3d, 0x67, 0x6f, 0x6b, 0xd6, 0xd8, 0xa8, 0x5d, 0xa3, 0x7f, 0x1f, 0xda, 0xfb, 0xb0, 0x10, 0x6b, 0xc0, 0x86, 0x3d, 0xa0, 0xff, 0x2b, 0xc0, 0xf6, 0xa2, 0x5c, 0x98, 0xf9, 0xc9,
0x56, 0x02, 0x18, 0x1d, 0xa6, 0x4e, 0xfd, 0x52, 0x0c, 0x60, 0x21, 0xd6, 0x80, 0x0d, 0x7b, 0x40, 0x0d, 0x7c, 0x1d, 0x3a, 0x26, 0xda, 0x15, 0x8f, 0x67, 0x2a, 0xd6, 0xd8, 0x56, 0xd8, 0xd7, 0xe2,
0xff, 0x57, 0x80, 0xed, 0x45, 0xb9, 0x30, 0xf3, 0x33, 0x9e, 0xa4, 0x09, 0x88, 0x59, 0x9e, 0xa4, 0xf1, 0xcc, 0xbf, 0x2d, 0x9f, 0x42, 0xcb, 0x0b, 0xbb, 0x0e, 0x4b, 0x91, 0x84, 0xb4, 0x1c, 0x5e,
0xc2, 0xc8, 0x93, 0xbc, 0x2d, 0x5f, 0x4b, 0xcb, 0x0b, 0xbb, 0x0e, 0x4b, 0x91, 0x84, 0xb4, 0x1c, 0x51, 0x0c, 0xac, 0x5b, 0x9a, 0x7a, 0x34, 0x28, 0x14, 0xe8, 0x88, 0xf9, 0x1f, 0x79, 0xb0, 0xa8,
0x5e, 0x51, 0x0c, 0xac, 0x5b, 0x9a, 0x7a, 0x34, 0x28, 0x14, 0xe8, 0x88, 0xf9, 0x1f, 0x7a, 0xb0, 0x96, 0x86, 0xea, 0xd0, 0x49, 0x63, 0x93, 0x0b, 0x73, 0xb0, 0xfa, 0xe4, 0x9f, 0x2a, 0xd7, 0xcd,
0xa8, 0x96, 0x86, 0xea, 0xd0, 0x49, 0x7d, 0x93, 0x0b, 0x73, 0xb0, 0xfa, 0x84, 0xa1, 0x2a, 0xd7, 0xd5, 0x71, 0x1d, 0x83, 0x66, 0x1a, 0x8a, 0x63, 0xb2, 0xa0, 0x5b, 0x01, 0xfd, 0xd6, 0x9e, 0xd2,
0xcd, 0xd5, 0x71, 0x1d, 0x83, 0x66, 0x1a, 0x8a, 0x63, 0xb2, 0xa0, 0x5b, 0x01, 0xfd, 0xd6, 0x9e, 0x7c, 0xe1, 0x29, 0xd5, 0xe5, 0x9b, 0x49, 0x99, 0x51, 0xc1, 0xf5, 0x13, 0xb1, 0x5a, 0x80, 0x89,
0xd2, 0x7c, 0xe1, 0x29, 0xd5, 0xe5, 0xa8, 0x49, 0x99, 0x51, 0xc1, 0xf5, 0x2b, 0xb2, 0x5a, 0x80, 0x6e, 0xde, 0x91, 0x4f, 0xc4, 0x05, 0x5c, 0xec, 0x97, 0x22, 0x51, 0xde, 0x2f, 0xd5, 0x34, 0x30,
0x09, 0x80, 0xde, 0x91, 0xaf, 0xc8, 0x05, 0x5c, 0xec, 0x97, 0x22, 0x51, 0xde, 0x2f, 0xd5, 0x34, 0xf5, 0x7e, 0x0f, 0xba, 0xf7, 0xf8, 0x98, 0x0b, 0x7e, 0x7b, 0x3c, 0x2e, 0xd3, 0x7f, 0x0d, 0x2e,
0x30, 0xf5, 0x7e, 0x0f, 0xba, 0xf7, 0xf8, 0x98, 0x0b, 0x7e, 0x7b, 0x3c, 0x2e, 0xd3, 0x7f, 0x0d, 0xd6, 0xd4, 0x29, 0xad, 0xfa, 0x00, 0xd6, 0xef, 0xf1, 0xc3, 0xe9, 0x68, 0x8f, 0x9f, 0x14, 0xcf,
0x2e, 0xd6, 0xd4, 0x29, 0xad, 0xfa, 0x00, 0xd6, 0xef, 0xf1, 0xc3, 0xe9, 0x68, 0x8f, 0x9f, 0x14, 0x0a, 0x0c, 0x9a, 0xf9, 0x71, 0x72, 0xaa, 0xce, 0x96, 0x7e, 0xa3, 0xc3, 0x3b, 0xc6, 0x36, 0xfd,
0x2f, 0x0f, 0x0c, 0x9a, 0xf9, 0x71, 0x72, 0xaa, 0xce, 0x96, 0x7e, 0xa3, 0xc3, 0x3b, 0xc6, 0x36, 0x3c, 0xe5, 0x03, 0x9d, 0xf6, 0x42, 0xc8, 0x41, 0xca, 0x07, 0xfe, 0x3b, 0xc0, 0x6c, 0x3a, 0x6a,
0xfd, 0x3c, 0xe5, 0x03, 0x9d, 0x19, 0x43, 0xc8, 0x41, 0xca, 0x07, 0xfe, 0x3b, 0xc0, 0x6c, 0x3a, 0x09, 0x78, 0x73, 0xa7, 0x87, 0xfd, 0x7c, 0x96, 0x0b, 0x3e, 0xd1, 0xf9, 0x3c, 0x36, 0xe4, 0x5f,
0x6a, 0x09, 0x78, 0x73, 0xa7, 0x87, 0xfd, 0x7c, 0x96, 0x0b, 0x3e, 0xd1, 0x29, 0x3f, 0x36, 0xe4, 0x85, 0xce, 0x7e, 0x38, 0x0b, 0xf8, 0x27, 0x2a, 0x93, 0x10, 0x9d, 0xb7, 0x70, 0x86, 0xac, 0x6c,
0x5f, 0x85, 0xce, 0x7e, 0x38, 0x0b, 0xf8, 0xc7, 0x2a, 0xfb, 0x10, 0x9d, 0xb7, 0x70, 0x86, 0xac, 0x9c, 0x37, 0xaa, 0xf6, 0xff, 0xbe, 0x01, 0x0b, 0xb2, 0x25, 0x52, 0x1d, 0xf2, 0x5c, 0x44, 0xb1,
0x6c, 0x9c, 0x37, 0xaa, 0xf6, 0xff, 0xa1, 0x01, 0x0b, 0xb2, 0x25, 0x52, 0x1d, 0xf2, 0x5c, 0x44, 0x0c, 0xbf, 0x2b, 0xaa, 0x16, 0x54, 0xe1, 0x8d, 0x46, 0x0d, 0x6f, 0x28, 0x73, 0x4a, 0x27, 0x0f,
0xb1, 0x8c, 0xd0, 0x2b, 0xaa, 0x16, 0x54, 0xe1, 0x8d, 0x46, 0x0d, 0x6f, 0x28, 0x73, 0x4a, 0xe7, 0x28, 0x26, 0x70, 0x30, 0xf2, 0x4d, 0xcd, 0x83, 0x64, 0x53, 0xf9, 0xa6, 0x1a, 0x28, 0x79, 0xc9,
0x17, 0x28, 0x26, 0x70, 0x30, 0xf2, 0x4d, 0xcd, 0x9b, 0x65, 0x53, 0xf9, 0xa6, 0x1a, 0x28, 0x79, 0x85, 0x7c, 0x90, 0xf3, 0xd3, 0x4c, 0xab, 0xd8, 0xc1, 0x86, 0x6a, 0xa5, 0xd0, 0xa2, 0xe4, 0x9a,
0xc9, 0x85, 0x7c, 0x90, 0xf3, 0xd3, 0x4c, 0xab, 0xd8, 0xc1, 0x86, 0x6a, 0xa5, 0xd0, 0xa2, 0xe4, 0x8a, 0x14, 0xaa, 0x48, 0x9b, 0xa5, 0x57, 0x90, 0x36, 0xd2, 0xc6, 0x72, 0xa4, 0x0d, 0x83, 0xb5,
0x9a, 0x8a, 0x14, 0xaa, 0x48, 0x9b, 0xa5, 0x57, 0x90, 0x36, 0xd2, 0xc6, 0x72, 0xa4, 0x0d, 0x83, 0x07, 0x9c, 0x07, 0x3c, 0x4d, 0x32, 0x9d, 0x8e, 0xe9, 0x7f, 0xcf, 0x83, 0x35, 0xa5, 0x3d, 0x4c,
0xb5, 0x07, 0x9c, 0x07, 0x3c, 0x4d, 0x32, 0x9d, 0xc2, 0xe9, 0x7f, 0xd7, 0x83, 0x35, 0xa5, 0x3d, 0x1d, 0x7b, 0xdd, 0x51, 0x35, 0x5e, 0x5d, 0x44, 0xf6, 0x0d, 0x58, 0x26, 0x67, 0x0b, 0x3d, 0x29,
0x4c, 0x1d, 0x7b, 0xdd, 0x51, 0x35, 0x5e, 0x5d, 0xd0, 0xf6, 0x0d, 0x58, 0x26, 0x67, 0x0b, 0x3d, 0xf2, 0xac, 0x54, 0xfc, 0xc1, 0x01, 0x71, 0x4e, 0x3a, 0xc6, 0x38, 0x89, 0xc6, 0x6a, 0x83, 0x6d,
0x29, 0xf2, 0xac, 0x54, 0xfc, 0xc1, 0x01, 0x71, 0x4e, 0x3a, 0x0c, 0x39, 0x89, 0xc6, 0x6a, 0x83, 0x08, 0xd5, 0xa2, 0x76, 0xc6, 0x68, 0x7b, 0xbd, 0xc0, 0x94, 0xfd, 0xbf, 0xf3, 0x60, 0xdd, 0x9a,
0x6d, 0x08, 0xd5, 0xa2, 0x76, 0xc6, 0x68, 0x7b, 0xbd, 0xc0, 0x94, 0xfd, 0xbf, 0xf7, 0x60, 0xdd, 0xb0, 0xe2, 0xa8, 0x5b, 0xa0, 0x9f, 0x25, 0x65, 0x3c, 0x41, 0x5e, 0x8c, 0x0b, 0xae, 0x26, 0x2c,
0x9a, 0xb0, 0xe2, 0xa8, 0x5b, 0xa0, 0x5f, 0x2e, 0x65, 0x3c, 0x41, 0x5e, 0x8c, 0x0b, 0xae, 0x26, 0xba, 0x39, 0x8d, 0xe9, 0x60, 0xc2, 0x19, 0x4d, 0x30, 0x9f, 0xca, 0xc4, 0xa8, 0x66, 0x60, 0x43,
0x2c, 0xba, 0x39, 0x8d, 0xe9, 0x60, 0xc2, 0x19, 0x4d, 0x30, 0x9f, 0xca, 0xdc, 0xa9, 0x66, 0x60, 0xc8, 0x14, 0xa7, 0x9c, 0x3f, 0x35, 0x4d, 0xe6, 0xa8, 0x89, 0x83, 0xd1, 0xab, 0x53, 0x12, 0x8b,
0x43, 0xc8, 0x14, 0xa7, 0x9c, 0x3f, 0x35, 0x4d, 0xe6, 0xa8, 0x89, 0x83, 0xd1, 0xc3, 0x54, 0x12, 0x63, 0xd3, 0x48, 0xa6, 0x53, 0xb8, 0xa0, 0xff, 0x2f, 0x1e, 0x6c, 0x48, 0x0b, 0x44, 0xd9, 0x77,
0x8b, 0x63, 0xd3, 0x48, 0x66, 0x5c, 0xb8, 0xa0, 0xff, 0xaf, 0x1e, 0x6c, 0x48, 0x0b, 0x44, 0xd9, 0x26, 0xa3, 0x6a, 0x41, 0x9a, 0x5c, 0xf2, 0x76, 0xed, 0x9e, 0x0b, 0x54, 0x99, 0x7d, 0xe9, 0x15,
0x77, 0x26, 0xe9, 0x6a, 0x41, 0x9a, 0x5c, 0xf2, 0x76, 0xed, 0x9e, 0x0b, 0x54, 0x99, 0x7d, 0xe1, 0xad, 0x26, 0xf3, 0xda, 0x78, 0xc6, 0x59, 0xcc, 0xd5, 0x9d, 0xc5, 0x0b, 0x76, 0xba, 0xce, 0x33,
0x15, 0xad, 0x26, 0xf3, 0x20, 0x79, 0xc6, 0x59, 0xcc, 0xd5, 0x9d, 0xc5, 0x0b, 0x76, 0xba, 0xce, 0x9f, 0xaf, 0xf5, 0xcc, 0xef, 0x2c, 0xc2, 0x7c, 0x3e, 0x48, 0x52, 0xee, 0x6f, 0xc1, 0xa6, 0xbb,
0x33, 0x9f, 0xaf, 0xf5, 0xcc, 0xef, 0x2c, 0xc2, 0x7c, 0x3e, 0x48, 0x52, 0xee, 0x6f, 0xc1, 0xa6, 0x38, 0x25, 0x4e, 0x7e, 0xe0, 0x41, 0xf7, 0x81, 0x0c, 0x2b, 0x45, 0xf1, 0x68, 0x37, 0xca, 0x45,
0xbb, 0x38, 0x25, 0x4e, 0xbe, 0xef, 0x41, 0xf7, 0x81, 0x0c, 0x2b, 0x45, 0xf1, 0x68, 0x37, 0xca, 0x92, 0x99, 0x14, 0xd2, 0xcb, 0x00, 0xb9, 0x08, 0x33, 0x21, 0x73, 0x3e, 0x94, 0x4f, 0x5d, 0x20,
0x45, 0x92, 0x99, 0xb4, 0xd3, 0xcb, 0x00, 0xb9, 0x08, 0x33, 0x21, 0xd3, 0x42, 0x94, 0x4f, 0x5d, 0x38, 0x47, 0x1e, 0x0f, 0x65, 0xad, 0x3c, 0x1b, 0x53, 0xc6, 0x83, 0xa1, 0x97, 0xd0, 0x7e, 0x72,
0x20, 0x38, 0x47, 0x1e, 0x0f, 0x65, 0xad, 0x3c, 0x1b, 0x53, 0xc6, 0x83, 0xa1, 0xc7, 0xd2, 0x7e, 0x74, 0x94, 0x73, 0x63, 0x23, 0xd9, 0x18, 0xba, 0x59, 0x78, 0x7b, 0xd1, 0xb1, 0xe0, 0x27, 0x24,
0x72, 0x74, 0x94, 0x73, 0x63, 0x23, 0xd9, 0x18, 0xba, 0x59, 0x78, 0x7b, 0xd1, 0xb1, 0xe0, 0x27, 0x36, 0xa5, 0x0f, 0x55, 0x42, 0xfd, 0xbf, 0xf1, 0x60, 0xb5, 0x98, 0xe4, 0x7d, 0x04, 0xdd, 0x9b,
0x24, 0x36, 0xa5, 0x0f, 0x55, 0x42, 0xfd, 0xbf, 0xf5, 0x60, 0xb5, 0x98, 0xe4, 0x7d, 0x04, 0xdd, 0x2e, 0xa7, 0x66, 0xdd, 0x74, 0xed, 0xed, 0x47, 0xc3, 0x7e, 0x14, 0xab, 0xb9, 0x59, 0x08, 0xdd,
0x9b, 0x2e, 0xa7, 0x66, 0xdd, 0x74, 0xed, 0xed, 0x47, 0xc3, 0x7e, 0x14, 0xab, 0xb9, 0x59, 0x08, 0x3e, 0x55, 0x4a, 0xa6, 0x3a, 0xbf, 0xc6, 0x86, 0xe4, 0x13, 0x9c, 0xc0, 0xde, 0x32, 0xb9, 0x46,
0xdd, 0x3e, 0x55, 0x4a, 0xa6, 0x3a, 0x05, 0xc7, 0x86, 0xe4, 0x2b, 0x9d, 0xc0, 0xde, 0x32, 0xff, 0x95, 0x28, 0x65, 0x67, 0x22, 0xa8, 0xd7, 0x82, 0x8c, 0xf1, 0xa9, 0xa2, 0xd6, 0x35, 0x8b, 0x84,
0x46, 0x95, 0x28, 0xab, 0x67, 0x22, 0xa8, 0xd7, 0x82, 0x8c, 0xf1, 0xa9, 0xa2, 0xd6, 0x35, 0x8b, 0xe2, 0x4f, 0xff, 0xbb, 0x1e, 0x5c, 0xac, 0xd9, 0x5c, 0x75, 0x33, 0xee, 0xc1, 0xfa, 0x91, 0xa9,
0x84, 0xe2, 0x4f, 0xff, 0x3b, 0x1e, 0x5c, 0xac, 0xd9, 0x5c, 0x75, 0x33, 0xee, 0xc1, 0xfa, 0x91, 0xd4, 0x1b, 0x20, 0xaf, 0xc7, 0x96, 0xe2, 0xa2, 0xd2, 0xa2, 0x83, 0x6a, 0x07, 0xf6, 0x16, 0xac,
0xa9, 0xd4, 0x1b, 0x20, 0xaf, 0xc7, 0x96, 0xe2, 0xa2, 0xd2, 0xa2, 0x83, 0x6a, 0x07, 0xf6, 0x16, 0x53, 0x90, 0x42, 0x6e, 0xa9, 0xf3, 0x22, 0x5d, 0xad, 0xd8, 0xf9, 0x61, 0x03, 0x56, 0x64, 0xcc,
0xac, 0x53, 0x90, 0x42, 0x6e, 0xa9, 0xf3, 0x68, 0x5d, 0xad, 0xd8, 0xf9, 0x41, 0x03, 0x56, 0x64, 0x58, 0x7e, 0x44, 0xc0, 0x33, 0xf6, 0x21, 0x2c, 0xaa, 0x4f, 0x36, 0xd8, 0x79, 0x35, 0xac, 0xfb,
0xcc, 0x58, 0x7e, 0x78, 0xc0, 0x33, 0xf6, 0x01, 0x2c, 0xaa, 0xcf, 0x3c, 0xd8, 0x79, 0x35, 0xac, 0x91, 0x48, 0x6f, 0xab, 0x0c, 0x2b, 0xde, 0xd9, 0xf8, 0x83, 0x9f, 0xfc, 0xfb, 0x9f, 0x34, 0x96,
0xfb, 0x61, 0x49, 0x6f, 0xab, 0x0c, 0x2b, 0xde, 0xd9, 0xf8, 0xfd, 0x1f, 0xff, 0xc7, 0x9f, 0x34, 0x59, 0x7b, 0xfb, 0xe4, 0xed, 0xed, 0x11, 0x8f, 0x73, 0xa4, 0xf1, 0x5b, 0x00, 0xc5, 0x57, 0x0f,
0x96, 0x59, 0x7b, 0xfb, 0xe4, 0xed, 0xed, 0x11, 0x8f, 0x73, 0xa4, 0xf1, 0x9b, 0x00, 0xc5, 0x97, 0xac, 0x6b, 0x0c, 0x86, 0xd2, 0x57, 0x1a, 0xbd, 0x8b, 0x35, 0x35, 0x8a, 0xee, 0x45, 0xa2, 0xbb,
0x12, 0xac, 0x6b, 0x0c, 0x86, 0xd2, 0x97, 0x1d, 0xbd, 0x8b, 0x35, 0x35, 0x8a, 0xee, 0x45, 0xa2, 0xe1, 0xaf, 0x20, 0xdd, 0x28, 0x8e, 0x84, 0xfc, 0x04, 0xe2, 0x3d, 0xef, 0x3a, 0x1b, 0x42, 0xc7,
0xbb, 0xe1, 0xaf, 0x20, 0xdd, 0x28, 0x8e, 0x84, 0xfc, 0x6c, 0xe2, 0x3d, 0xef, 0x3a, 0x1b, 0x42, 0xfe, 0xfa, 0x81, 0x69, 0xff, 0xac, 0xe6, 0x93, 0x8a, 0xde, 0x6b, 0xb5, 0x75, 0xda, 0x39, 0xa5,
0xc7, 0xfe, 0x62, 0x82, 0x69, 0xff, 0xac, 0xe6, 0x33, 0x8c, 0xde, 0x6b, 0xb5, 0x75, 0xda, 0x39, 0x31, 0xce, 0xfb, 0x6b, 0x38, 0xc6, 0x94, 0x5a, 0x98, 0x51, 0x76, 0xfe, 0xf5, 0x35, 0x68, 0x99,
0xa5, 0x31, 0xce, 0xfb, 0x6b, 0x38, 0xc6, 0x94, 0x5a, 0x98, 0x51, 0x76, 0xfe, 0xed, 0x35, 0x68, 0x18, 0x07, 0xfb, 0x36, 0x2c, 0x3b, 0x61, 0x76, 0xa6, 0x09, 0xd7, 0x45, 0xe5, 0x7b, 0x97, 0xea,
0x99, 0x18, 0x07, 0xfb, 0x36, 0x2c, 0x3b, 0x61, 0x76, 0xa6, 0x09, 0xd7, 0x05, 0xee, 0x7b, 0x97, 0x2b, 0xd5, 0xb0, 0x97, 0x69, 0xd8, 0x2e, 0xdb, 0xc2, 0x61, 0x55, 0x6c, 0x7b, 0x9b, 0x1e, 0x17,
0xea, 0x2b, 0xd5, 0xb0, 0x97, 0x69, 0xd8, 0x2e, 0xdb, 0xc2, 0x61, 0x55, 0x6c, 0x7b, 0x9b, 0xde, 0x64, 0x8a, 0xce, 0x53, 0x58, 0x71, 0x43, 0xe3, 0xec, 0x92, 0x2b, 0x50, 0x4a, 0xa3, 0x7d, 0xee,
0x1f, 0x64, 0x16, 0xcf, 0x53, 0x58, 0x71, 0x43, 0xe3, 0xec, 0x92, 0x2b, 0x50, 0x4a, 0xa3, 0x7d, 0x8c, 0x5a, 0x35, 0xdc, 0x25, 0x1a, 0x6e, 0x8b, 0x6d, 0xda, 0xc3, 0x99, 0xd8, 0x03, 0xa7, 0xa4,
0xe6, 0x8c, 0x5a, 0x35, 0xdc, 0x25, 0x1a, 0x6e, 0x8b, 0x6d, 0xda, 0xc3, 0x99, 0xd8, 0x03, 0xa7, 0x2a, 0xfb, 0xb3, 0x08, 0xf6, 0x39, 0x73, 0xd4, 0x75, 0x9f, 0x4b, 0x98, 0x43, 0xab, 0x7e, 0x33,
0xbc, 0x2b, 0xfb, 0x53, 0x0a, 0xf6, 0x19, 0x73, 0xd4, 0x75, 0x9f, 0x58, 0x98, 0x43, 0xab, 0x7e, 0xe1, 0x77, 0x69, 0x28, 0xc6, 0x68, 0x43, 0xed, 0xaf, 0x22, 0xd8, 0xc7, 0xd0, 0x32, 0xa9, 0xd0,
0x67, 0xe1, 0x77, 0x69, 0x28, 0xc6, 0x68, 0x43, 0xed, 0x2f, 0x29, 0xd8, 0x47, 0xd0, 0x32, 0xe9, 0xec, 0x82, 0x95, 0x7f, 0x6e, 0xe7, 0x67, 0xf7, 0xba, 0xd5, 0x8a, 0xba, 0xa3, 0xb2, 0x29, 0x23,
0xd3, 0xec, 0x82, 0x95, 0xb3, 0x6e, 0xe7, 0x74, 0xf7, 0xba, 0xd5, 0x8a, 0xba, 0xa3, 0xb2, 0x29, 0x43, 0xec, 0xc1, 0x79, 0x65, 0x70, 0x1e, 0xf2, 0x9f, 0x65, 0x25, 0x35, 0x1f, 0x73, 0xdc, 0xf4,
0x23, 0x43, 0xec, 0xc1, 0x79, 0x65, 0x70, 0x1e, 0xf2, 0x9f, 0x66, 0x25, 0x35, 0x1f, 0x80, 0xdc, 0xd8, 0x2d, 0x58, 0xd2, 0x19, 0xe6, 0x6c, 0xab, 0x3e, 0x53, 0xbe, 0x77, 0xa1, 0x82, 0xab, 0xfb,
0xf4, 0xd8, 0x2d, 0x58, 0xd2, 0x59, 0xe9, 0x6c, 0xab, 0x3e, 0xbb, 0xbe, 0x77, 0xa1, 0x82, 0xab, 0x7c, 0x1b, 0xa0, 0xc8, 0x8e, 0x36, 0x9c, 0x5f, 0xc9, 0xd9, 0x36, 0x9b, 0x58, 0x93, 0x4a, 0x3d,
0xfb, 0x7c, 0x1b, 0xa0, 0x48, 0xa0, 0x36, 0x9c, 0x5f, 0xc9, 0xf3, 0x36, 0x9b, 0x58, 0x93, 0x6d, 0xa2, 0x5c, 0x70, 0x37, 0xf9, 0x9a, 0x7d, 0xbe, 0x68, 0x5f, 0x9b, 0x96, 0xfd, 0x02, 0x82, 0xfe,
0x3d, 0xa2, 0xfc, 0x71, 0x37, 0x3f, 0x9b, 0x7d, 0xb6, 0x68, 0x5f, 0x9b, 0xb9, 0xfd, 0x02, 0x82, 0x16, 0xed, 0xdd, 0x1a, 0xa3, 0xab, 0x14, 0xf3, 0x53, 0x9d, 0x5e, 0x78, 0x0f, 0xda, 0x56, 0xc6,
0xfe, 0x16, 0xed, 0xdd, 0x1a, 0xa3, 0xab, 0x14, 0xf3, 0x53, 0x9d, 0x81, 0x78, 0x0f, 0xda, 0x56, 0x35, 0xd3, 0x14, 0xaa, 0xd9, 0xda, 0xbd, 0x5e, 0x5d, 0x95, 0x9a, 0xee, 0x57, 0x60, 0xd9, 0x49,
0x52, 0x36, 0xd3, 0x14, 0xaa, 0x09, 0xdd, 0xbd, 0x5e, 0x5d, 0x95, 0x9a, 0xee, 0x57, 0x60, 0xd9, 0x9d, 0x36, 0x37, 0xa3, 0x2e, 0x31, 0xdb, 0xdc, 0x8c, 0xfa, 0x6c, 0xeb, 0x6f, 0x42, 0xdb, 0x4a,
0xc9, 0xae, 0x36, 0x37, 0xa3, 0x2e, 0x77, 0xdb, 0xdc, 0x8c, 0xfa, 0x84, 0xec, 0x6f, 0x42, 0xdb, 0x74, 0x66, 0x56, 0x72, 0x46, 0x29, 0xc5, 0xd9, 0xcc, 0xa8, 0x2e, 0x2f, 0x7a, 0x93, 0xd6, 0xbb,
0xca, 0x85, 0x66, 0x56, 0xfe, 0x46, 0x29, 0x0b, 0xda, 0xcc, 0xa8, 0x2e, 0x75, 0x7a, 0x93, 0xd6, 0xe2, 0xb7, 0x70, 0xbd, 0x94, 0x63, 0x87, 0x4c, 0xf2, 0x6d, 0x58, 0x71, 0x53, 0x9f, 0xcd, 0xad,
0xbb, 0xe2, 0xb7, 0x70, 0xbd, 0x94, 0x86, 0x87, 0x4c, 0xf2, 0x6d, 0x58, 0x71, 0xb3, 0xa3, 0xcd, 0xaa, 0x4d, 0xa2, 0x36, 0xb7, 0xea, 0x8c, 0x7c, 0x69, 0xc5, 0x90, 0xd7, 0x37, 0xcc, 0x20, 0xdb,
0xad, 0xaa, 0xcd, 0xb3, 0x36, 0xb7, 0xea, 0x8c, 0x94, 0x6a, 0xc5, 0x90, 0xd7, 0x37, 0xcc, 0x20, 0x9f, 0xa9, 0x08, 0xff, 0x73, 0xf6, 0x75, 0x14, 0x1d, 0x2a, 0xe9, 0x91, 0x15, 0x09, 0xdf, 0x6e,
0xdb, 0x9f, 0xaa, 0x08, 0xff, 0x73, 0xf6, 0x75, 0x14, 0x1d, 0x2a, 0x2f, 0x92, 0x15, 0x39, 0xe1, 0x6a, 0xa4, 0xe1, 0xf6, 0x4a, 0x7e, 0xa4, 0xbf, 0x4e, 0xc4, 0xdb, 0xac, 0x58, 0x81, 0x94, 0xd0,
0x6e, 0xf6, 0xa4, 0xe1, 0xf6, 0x4a, 0x0a, 0xa5, 0xbf, 0x4e, 0xc4, 0xdb, 0xac, 0x58, 0x81, 0x94, 0x94, 0xfc, 0x68, 0x49, 0x68, 0x3b, 0x3f, 0xd2, 0x92, 0xd0, 0x4e, 0x8e, 0x64, 0x59, 0x42, 0x8b,
0xd0, 0x94, 0x1f, 0x69, 0x49, 0x68, 0x3b, 0x85, 0xd2, 0x92, 0xd0, 0x4e, 0x1a, 0x65, 0x59, 0x42, 0x08, 0x69, 0xc4, 0xb0, 0x5a, 0x7a, 0x90, 0x35, 0x97, 0xa5, 0x3e, 0x9d, 0xa3, 0x77, 0xf9, 0xc5,
0x8b, 0x08, 0x69, 0xc4, 0xb0, 0x5a, 0x7a, 0xb3, 0x35, 0x97, 0xa5, 0x3e, 0xe3, 0xa3, 0x77, 0xf9, 0xef, 0xb8, 0xae, 0x98, 0xd1, 0xe2, 0x65, 0x5b, 0x67, 0xdf, 0xfc, 0x36, 0x74, 0xec, 0x8c, 0x5a,
0xc5, 0x4f, 0xbd, 0xae, 0x98, 0xd1, 0xe2, 0x65, 0x5b, 0x27, 0xe8, 0xfc, 0x16, 0x74, 0xec, 0xa4, 0x23, 0xb3, 0x6b, 0xb2, 0x6f, 0x8d, 0xcc, 0xae, 0x4b, 0xc1, 0xd5, 0x87, 0xcb, 0x3a, 0xf6, 0x30,
0x5b, 0x23, 0xb3, 0x6b, 0x12, 0x74, 0x8d, 0xcc, 0xae, 0xcb, 0xd2, 0xd5, 0x87, 0xcb, 0x3a, 0xf6, 0xec, 0x9b, 0xb0, 0x6a, 0x3d, 0xfd, 0x1f, 0xcc, 0xe2, 0x81, 0x61, 0x9e, 0x6a, 0x02, 0x56, 0xaf,
0x30, 0xec, 0x9b, 0xb0, 0x6a, 0x65, 0x07, 0x1c, 0xcc, 0xe2, 0x81, 0x61, 0x9e, 0x6a, 0x8e, 0x56, 0xce, 0x3e, 0xf3, 0x2f, 0x10, 0xe1, 0x75, 0xdf, 0x21, 0x8c, 0x8c, 0x73, 0x17, 0xda, 0x76, 0x5a,
0xaf, 0xce, 0x3e, 0xf3, 0x2f, 0x10, 0xe1, 0x75, 0xdf, 0x21, 0x8c, 0x8c, 0x73, 0x17, 0xda, 0x76, 0xc1, 0x0b, 0xe8, 0x5e, 0xb0, 0xaa, 0xec, 0xbc, 0xa5, 0x9b, 0x1e, 0xfb, 0x73, 0x0f, 0x3a, 0x76,
0xe6, 0xc1, 0x0b, 0xe8, 0x5e, 0xb0, 0xaa, 0xec, 0xd4, 0xa6, 0x9b, 0x1e, 0xfb, 0x73, 0x0f, 0x3a, 0x6a, 0x1f, 0x73, 0x82, 0x8a, 0x25, 0x3a, 0x5d, 0xbb, 0xce, 0x26, 0xe4, 0x07, 0x34, 0xc9, 0xbd,
0x76, 0xf6, 0x1f, 0x73, 0x82, 0x8a, 0x25, 0x3a, 0x5d, 0xbb, 0xce, 0x26, 0xe4, 0x07, 0x34, 0xc9, 0xeb, 0x5f, 0x71, 0x36, 0xf9, 0x33, 0xc7, 0xce, 0xbf, 0x51, 0xfe, 0x1a, 0xe9, 0x79, 0xb9, 0x81,
0xbd, 0xeb, 0x5f, 0x71, 0x36, 0xf9, 0x53, 0xc7, 0xce, 0xbf, 0x51, 0xfe, 0x82, 0xe9, 0x79, 0xb9, 0x9d, 0xa4, 0xf6, 0xfc, 0xa6, 0xc7, 0xde, 0x93, 0x5f, 0xac, 0x69, 0x1f, 0x9d, 0x59, 0xc2, 0xad,
0x81, 0x9d, 0xc7, 0xf6, 0xfc, 0xa6, 0xc7, 0xde, 0x93, 0x5f, 0xb9, 0x69, 0x1f, 0x9d, 0x59, 0xc2, 0xbc, 0x65, 0xf6, 0xc7, 0x5d, 0xd7, 0xbc, 0x9b, 0x1e, 0xfb, 0x96, 0xfc, 0xe8, 0x48, 0xf5, 0xa5,
0xad, 0xbc, 0x65, 0xf6, 0x07, 0x61, 0xd7, 0xbc, 0x9b, 0x1e, 0xfb, 0x96, 0xfc, 0x50, 0x49, 0xf5, 0x9d, 0x7f, 0xd5, 0xfe, 0xfe, 0x1b, 0xb4, 0x9a, 0xcb, 0xfe, 0x45, 0x67, 0x35, 0x65, 0xe9, 0xbe,
0xa5, 0x9d, 0x7f, 0xd5, 0xfe, 0xfe, 0x1b, 0xb4, 0x9a, 0xcb, 0xfe, 0x45, 0x67, 0x35, 0x65, 0xe9, 0x0f, 0x50, 0x04, 0x5c, 0x58, 0x29, 0xfa, 0x60, 0xe4, 0x5e, 0x35, 0x26, 0xe3, 0x9e, 0xa8, 0x0e,
0xbe, 0x0f, 0x50, 0x04, 0x5c, 0x58, 0x29, 0xfa, 0x60, 0xe4, 0x5e, 0x35, 0x26, 0xe3, 0x9e, 0xa8, 0x52, 0x20, 0xc5, 0x8f, 0x25, 0x33, 0xaa, 0xf6, 0xb9, 0x39, 0xd2, 0x6a, 0xe0, 0xa4, 0xd7, 0xab,
0x0e, 0x52, 0x20, 0xc5, 0x8f, 0x24, 0x33, 0xaa, 0xf6, 0xb9, 0x39, 0xd2, 0x6a, 0xe0, 0xa4, 0xd7, 0xab, 0xaa, 0x63, 0x45, 0x4d, 0x9f, 0x3d, 0x81, 0xe5, 0xbd, 0x24, 0x79, 0x3a, 0x4d, 0x4d, 0x10,
0xab, 0xab, 0xaa, 0x63, 0x45, 0x4d, 0x9f, 0x3d, 0x81, 0xe5, 0xbd, 0x24, 0x79, 0x3a, 0x4d, 0x4d, 0xcf, 0xf5, 0xff, 0x77, 0xc3, 0xfc, 0xb8, 0x57, 0x5a, 0x85, 0x7f, 0x85, 0x48, 0xf5, 0x58, 0xd7,
0x10, 0xcf, 0xf5, 0xff, 0x77, 0xc3, 0xfc, 0xb8, 0x57, 0x5a, 0x85, 0x7f, 0x85, 0x48, 0xf5, 0x58, 0x22, 0xb5, 0xfd, 0x59, 0x11, 0xee, 0x79, 0xce, 0x42, 0x58, 0x37, 0x3a, 0xce, 0x4c, 0xbc, 0xe7,
0xd7, 0x22, 0xb5, 0xfd, 0x69, 0x11, 0xee, 0x79, 0xce, 0x42, 0x58, 0x37, 0x3a, 0xce, 0x4c, 0xbc, 0x92, 0xb1, 0xa3, 0x2e, 0x95, 0x21, 0x1c, 0xab, 0x43, 0xcf, 0x76, 0x3b, 0xd7, 0x34, 0x6f, 0x7a,
0xe7, 0x92, 0xb1, 0xa3, 0x2e, 0x95, 0x21, 0x1c, 0xab, 0x43, 0xcf, 0x76, 0x3b, 0xd7, 0x34, 0x6f, 0x6c, 0x1f, 0x3a, 0xf7, 0xf8, 0x20, 0x19, 0x72, 0xe5, 0xb1, 0x6f, 0x14, 0x13, 0x37, 0xae, 0x7e,
0x7a, 0x6c, 0x1f, 0x3a, 0xf7, 0xf8, 0x20, 0x19, 0x72, 0xe5, 0xb1, 0x6f, 0x14, 0x13, 0x37, 0xae, 0x6f, 0xd9, 0x01, 0xdd, 0x5b, 0x9f, 0x86, 0xb3, 0x8c, 0x7f, 0xb2, 0xfd, 0x99, 0x8a, 0x05, 0x3c,
0x7e, 0x6f, 0xd9, 0x01, 0xdd, 0x5b, 0x9f, 0x86, 0xb3, 0x8c, 0x7f, 0xbc, 0xfd, 0xa9, 0x8a, 0x05, 0xd7, 0xb7, 0x5e, 0xc7, 0x2f, 0x9c, 0x5b, 0x5f, 0x0a, 0x78, 0x38, 0xb7, 0xbe, 0x12, 0xf0, 0x70,
0x3c, 0xd7, 0xb7, 0x5e, 0xc7, 0x2f, 0x9c, 0x5b, 0x5f, 0x0a, 0x78, 0x38, 0xb7, 0xbe, 0x12, 0xf0, 0xb6, 0x5a, 0xc7, 0x4f, 0xd8, 0x18, 0xd6, 0x2b, 0x31, 0x12, 0xa3, 0x29, 0xcf, 0x8a, 0xac, 0xf4,
0x70, 0xb6, 0x5a, 0xc7, 0x4f, 0xd8, 0x18, 0xd6, 0x2b, 0x31, 0x12, 0xa3, 0x29, 0xcf, 0x8a, 0xac, 0xae, 0x9c, 0xdd, 0xc0, 0x1d, 0xed, 0xba, 0x3b, 0xda, 0x01, 0x2c, 0xdf, 0xe3, 0x72, 0xb3, 0xe4,
0xf4, 0xae, 0x9c, 0xdd, 0xc0, 0x1d, 0xed, 0xba, 0x3b, 0xda, 0x01, 0x2c, 0xdf, 0xe3, 0x72, 0xb3, 0xc3, 0x58, 0xcf, 0x15, 0x23, 0xf6, 0x23, 0x5a, 0x59, 0xc4, 0x50, 0x9d, 0x2b, 0xd6, 0xe9, 0x55,
0xe4, 0xc3, 0x58, 0xcf, 0x15, 0x23, 0xf6, 0x23, 0x5a, 0x59, 0xc4, 0x50, 0x9d, 0x2b, 0xd6, 0xe9, 0x8a, 0x7d, 0x0c, 0xed, 0x87, 0x5c, 0xe8, 0x97, 0x30, 0x63, 0x6f, 0x94, 0x9e, 0xc6, 0x7a, 0x35,
0x55, 0x8a, 0x7d, 0x04, 0xed, 0x87, 0x5c, 0xe8, 0x97, 0x30, 0x63, 0x6f, 0x94, 0x9e, 0xc6, 0x7a, 0x0f, 0x69, 0x2e, 0xcf, 0x10, 0xb5, 0x6d, 0x3e, 0x1c, 0x71, 0x79, 0xd9, 0xfb, 0xd1, 0xf0, 0x39,
0x35, 0x0f, 0x69, 0x2e, 0xcf, 0x10, 0xb5, 0x6d, 0x3e, 0x1c, 0x71, 0x79, 0xd9, 0xfb, 0xd1, 0xf0, 0xfb, 0x0d, 0x22, 0x6e, 0x1e, 0xcf, 0xb7, 0xac, 0x07, 0x14, 0x9b, 0xf8, 0x6a, 0x09, 0xaf, 0xa3,
0x39, 0xfb, 0x75, 0x22, 0x6e, 0x1e, 0xcf, 0xb7, 0xac, 0x07, 0x14, 0x9b, 0xf8, 0x6a, 0x09, 0xaf, 0x1c, 0x27, 0x43, 0x6e, 0x29, 0xb8, 0x18, 0xda, 0x56, 0xa6, 0x84, 0xb9, 0x40, 0xd5, 0xec, 0x0c,
0xa3, 0x1c, 0x27, 0x43, 0x6e, 0x29, 0xb8, 0x18, 0xda, 0x56, 0xa6, 0x84, 0xb9, 0x40, 0xd5, 0xec, 0x73, 0x81, 0x6a, 0x12, 0x2b, 0xfc, 0x6b, 0x34, 0x8e, 0xcf, 0xae, 0x14, 0xe3, 0xc8, 0x64, 0x8a,
0x0c, 0x73, 0x81, 0x6a, 0x12, 0x2b, 0xfc, 0x6b, 0x34, 0x8e, 0xcf, 0xae, 0x14, 0xe3, 0xc8, 0x64, 0x62, 0xa4, 0xed, 0xcf, 0xc2, 0x89, 0x78, 0xce, 0x3e, 0xa2, 0xaf, 0x01, 0xec, 0xd7, 0xbe, 0xc2,
0x8a, 0x62, 0xa4, 0xed, 0x4f, 0xc3, 0x89, 0x78, 0xce, 0x3e, 0xa4, 0x0f, 0x06, 0xec, 0xd7, 0xbe, 0xde, 0x29, 0x3f, 0x0c, 0x9a, 0xcd, 0xb2, 0xaa, 0x5c, 0x1b, 0x48, 0x0e, 0x45, 0x7a, 0xf0, 0x4b,
0xc2, 0xde, 0x29, 0x3f, 0x0c, 0x9a, 0xcd, 0xb2, 0xaa, 0x5c, 0x1b, 0x48, 0x0e, 0x45, 0x7a, 0xf0, 0x00, 0x07, 0x22, 0x49, 0xef, 0x85, 0x7c, 0x92, 0xc4, 0x85, 0xe4, 0x2a, 0x5e, 0xb4, 0x0a, 0xc9,
0x0b, 0x00, 0x07, 0x22, 0x49, 0xef, 0x85, 0x7c, 0x92, 0xc4, 0x85, 0xe4, 0x2a, 0x5e, 0xb4, 0x0a, 0x65, 0x3d, 0x6b, 0xb1, 0x8f, 0x2c, 0x8b, 0xd3, 0x79, 0x2c, 0xd5, 0xcc, 0x75, 0xe6, 0xa3, 0x97,
0xc9, 0x65, 0x3d, 0x6b, 0xb1, 0x0f, 0x2d, 0x8b, 0xd3, 0x79, 0x2c, 0xd5, 0xcc, 0x75, 0xe6, 0xa3, 0xd9, 0x90, 0x9a, 0x87, 0xaf, 0x9b, 0x1e, 0xda, 0x8f, 0x45, 0x44, 0xce, 0xd8, 0x8f, 0x95, 0x60,
0x97, 0xd9, 0x90, 0x9a, 0x87, 0xaf, 0x9b, 0x1e, 0xda, 0x8f, 0x45, 0x44, 0xce, 0xd8, 0x8f, 0x95, 0x9f, 0x11, 0x7b, 0x35, 0xe1, 0xbb, 0x7d, 0x68, 0x15, 0x61, 0x21, 0xad, 0x92, 0xca, 0x41, 0x24,
0x60, 0x9f, 0x11, 0x7b, 0x35, 0xe1, 0xbb, 0x7d, 0x68, 0x15, 0x61, 0x21, 0xad, 0x92, 0xca, 0x41, 0xa3, 0x63, 0x2a, 0xc1, 0x1a, 0x7f, 0x8d, 0xb6, 0x0a, 0xd8, 0x12, 0x6e, 0x15, 0x45, 0x60, 0x22,
0x24, 0xa3, 0x63, 0x2a, 0xc1, 0x1a, 0x7f, 0x8d, 0xb6, 0x0a, 0xd8, 0x12, 0x6e, 0x15, 0x45, 0x60, 0xd8, 0x90, 0x13, 0x34, 0x0a, 0x93, 0xde, 0x68, 0xf4, 0x4a, 0x6a, 0x02, 0x26, 0xe6, 0x36, 0xd7,
0x22, 0xd8, 0x90, 0x13, 0x34, 0x0a, 0x93, 0xde, 0x68, 0xf4, 0x4a, 0x6a, 0x02, 0x26, 0xe6, 0x36, 0xc6, 0x1b, 0x1c, 0xdf, 0x0e, 0xb9, 0x55, 0xbe, 0x0f, 0xa1, 0x68, 0x9e, 0xc0, 0x7a, 0xc5, 0x59,
0xd7, 0xc6, 0x1b, 0x1c, 0xdf, 0x0e, 0xb9, 0x55, 0xbe, 0x0f, 0xa1, 0x68, 0x9e, 0xc0, 0x7a, 0xc5, 0x36, 0x57, 0xfa, 0xac, 0x18, 0x85, 0xb9, 0xd2, 0x67, 0xfa, 0xd9, 0xfe, 0x79, 0x1a, 0x72, 0xd5,
0x59, 0x36, 0x57, 0xfa, 0xac, 0x18, 0x85, 0xb9, 0xd2, 0x67, 0xfa, 0xd9, 0xfe, 0x79, 0x1a, 0x72, 0x07, 0x1c, 0x32, 0x3f, 0x8d, 0xc4, 0xe0, 0xf8, 0x3d, 0xef, 0xfa, 0xe1, 0x02, 0xfd, 0x27, 0xc2,
0xd5, 0x07, 0x1c, 0x32, 0x3f, 0x8d, 0xc4, 0xe0, 0xf8, 0x3d, 0xef, 0xfa, 0xe1, 0x02, 0xfd, 0x8f, 0x17, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x34, 0xfb, 0x9b, 0x45, 0x41, 0x00, 0x00,
0xc2, 0xe7, 0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, 0x2a, 0xe2, 0xf7, 0xd2, 0x79, 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) { 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 protoReq WalletBalanceRequest
var metadata runtime.ServerMetadata 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)) msg, err := client.WalletBalance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err return msg, metadata, err

View File

@ -145,8 +145,7 @@ service Lightning {
/** lncli: `walletbalance` /** lncli: `walletbalance`
WalletBalance returns total unspent outputs(confirmed and unconfirmed), all WalletBalance returns total unspent outputs(confirmed and unconfirmed), all
confirmed unspent outputs and all unconfirmed unspent outputs under control confirmed unspent outputs and all unconfirmed unspent outputs under control
by the wallet. This method can be modified by having the request specify of the wallet.
only witness outputs should be factored into the final output sum.
*/ */
rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) { rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) {
option (google.api.http) = { option (google.api.http) = {
@ -681,7 +680,6 @@ message NewAddressRequest {
enum AddressType { enum AddressType {
WITNESS_PUBKEY_HASH = 0; WITNESS_PUBKEY_HASH = 0;
NESTED_PUBKEY_HASH = 1; NESTED_PUBKEY_HASH = 1;
PUBKEY_HASH = 2;
} }
/// The address type /// The address type
@ -1099,8 +1097,6 @@ message PendingChannelsResponse {
} }
message WalletBalanceRequest { message WalletBalanceRequest {
/// If only witness outputs should be considered when calculating the wallet's balance
bool witness_only = 1;
} }
message WalletBalanceResponse { message WalletBalanceResponse {
/// The balance of the wallet /// The balance of the wallet

View File

@ -17,7 +17,7 @@
"paths": { "paths": {
"/v1/balance/blockchain": { "/v1/balance/blockchain": {
"get": { "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", "operationId": "WalletBalance",
"responses": { "responses": {
"200": { "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": [ "tags": [
"Lightning" "Lightning"
] ]

View File

@ -9,6 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg"
@ -27,9 +28,25 @@ const (
) )
var ( var (
lnNamespace = []byte("ln") // waddrmgrNamespaceKey is the namespace key that the waddrmgr state is
rootKey = []byte("ln-root") // stored within the top-level waleltdb buckets of btcwallet.
waddrmgrNamespaceKey = []byte("waddrmgr") 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 // BtcWallet is an implementation of the lnwallet.WalletController interface
@ -83,49 +100,23 @@ func New(cfg Config) (*BtcWallet, error) {
wallet, err = loader.CreateNewWallet( wallet, err = loader.CreateNewWallet(
pubPass, cfg.PrivatePass, cfg.HdSeed, pubPass, cfg.PrivatePass, cfg.HdSeed,
) )
if err != nil {
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:
return nil, err return nil, err
} }
} else {
if err := loader.UnloadWallet(); err != nil { // 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 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{ return &BtcWallet{
cfg: &cfg, cfg: &cfg,
wallet: wallet, wallet: wallet,
db: db, db: wallet.Database(),
chain: cfg.ChainSource, chain: cfg.ChainSource,
netParams: cfg.NetParams, netParams: cfg.NetParams,
utxoCache: make(map[wire.OutPoint]*wire.TxOut), utxoCache: make(map[wire.OutPoint]*wire.TxOut),
@ -143,6 +134,12 @@ func (b *BtcWallet) BackEnd() string {
return "" 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 // Start initializes the underlying rpc connection, the wallet itself, and
// begins syncing to the current available blockchain state. // begins syncing to the current available blockchain state.
// //
@ -165,6 +162,27 @@ func (b *BtcWallet) Start() error {
return err 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 return nil
} }
@ -188,25 +206,16 @@ func (b *BtcWallet) Stop() error {
// final sum. // final sum.
// //
// This is a part of the WalletController interface. // 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 var balance btcutil.Amount
if witness { witnessOutputs, err := b.ListUnspentWitness(confs)
witnessOutputs, err := b.ListUnspentWitness(confs) if err != nil {
if err != nil { return 0, err
return 0, err }
}
for _, witnessOutput := range witnessOutputs { for _, witnessOutput := range witnessOutputs {
balance += witnessOutput.Value balance += witnessOutput.Value
}
} else {
outputSum, err := b.wallet.CalculateBalance(confs)
if err != nil {
return 0, err
}
balance = outputSum
} }
return balance, nil 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. // This is a part of the WalletController interface.
func (b *BtcWallet) NewAddress(t lnwallet.AddressType, change bool) (btcutil.Address, error) { func (b *BtcWallet) NewAddress(t lnwallet.AddressType, change bool) (btcutil.Address, error) {
var addrType waddrmgr.AddressType var keyScope waddrmgr.KeyScope
switch t { switch t {
case lnwallet.WitnessPubKey: case lnwallet.WitnessPubKey:
addrType = waddrmgr.WitnessPubKey keyScope = waddrmgr.KeyScopeBIP0084
case lnwallet.NestedWitnessPubKey: case lnwallet.NestedWitnessPubKey:
addrType = waddrmgr.NestedWitnessPubKey keyScope = waddrmgr.KeyScopeBIP0049Plus
case lnwallet.PubKeyHash:
addrType = waddrmgr.PubKeyHash
default: default:
return nil, fmt.Errorf("unknown address type") return nil, fmt.Errorf("unknown address type")
} }
if change { 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 // 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) 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 // SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to
// the specified outputs. In the case the wallet has insufficient funds, or the // 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. // 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. // This is a part of the WalletController interface.
func (b *BtcWallet) IsSynced() (bool, int64, error) { func (b *BtcWallet) IsSynced() (bool, int64, error) {
// Grab the best chain state the wallet is currently aware of. We'll // Grab the best chain state the wallet is currently aware of.
// also grab the timestamp of the best block to return for determining
// sync progress.
syncState := b.wallet.Manager.SyncedTo() syncState := b.wallet.Manager.SyncedTo()
walletBestHeader, err := b.chain.GetBlockHeader(&syncState.Hash)
if err != nil {
return false, 0, err
}
var ( // We'll also extract the current best wallet timestamp so the caller
bestHash *chainhash.Hash // can get an idea of where we are in the sync timeline.
bestHeight int32 bestTimestamp := syncState.Timestamp.Unix()
)
// Next, query the chain backend to grab the info about the tip of the // Next, query the chain backend to grab the info about the tip of the
// main chain. // main chain.
bestHash, bestHeight, err = b.cfg.ChainSource.GetBestBlock() bestHash, bestHeight, err := b.cfg.ChainSource.GetBestBlock()
if err != nil { if err != nil {
return false, 0, err 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, // If the wallet hasn't yet fully synced to the node's best chain tip,
// then we're not yet fully synced. // then we're not yet fully synced.
if syncState.Height < bestHeight { 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 // 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 // If the timestamp no the best header is more than 2 hours in the
// past, then we're not yet synced. // past, then we're not yet synced.
minus24Hours := time.Now().Add(-2 * time.Hour) 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 ( import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
@ -10,6 +11,7 @@ import (
"github.com/roasbeef/btcutil" "github.com/roasbeef/btcutil"
"github.com/roasbeef/btcwallet/waddrmgr" "github.com/roasbeef/btcwallet/waddrmgr"
base "github.com/roasbeef/btcwallet/wallet" base "github.com/roasbeef/btcwallet/wallet"
"github.com/roasbeef/btcwallet/walletdb"
) )
// FetchInputInfo queries for the WalletController's knowledge of the passed // 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 // fetchPrivKey attempts to retrieve the raw private key corresponding to the
// passed public key. // passed public key if populated, or the key descriptor path (if non-empty).
// TODO(roasbeef): alternatively can extract all the data pushes within the func (b *BtcWallet) fetchPrivKey(keyDesc *keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
// script, then attempt to match keys one by one // If the key locator within the descriptor *isn't* empty, then we can
func (b *BtcWallet) fetchPrivKey(pub *btcec.PublicKey) (*btcec.PrivateKey, error) { // directly derive the keys raw.
hash160 := btcutil.Hash160(pub.SerializeCompressed()) 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) addr, err := btcutil.NewAddressWitnessPubKeyHash(hash160, b.netParams)
if err != nil { if err != nil {
return nil, err return nil, err
@ -117,11 +152,12 @@ func maybeTweakPrivKey(signDesc *lnwallet.SignDescriptor,
// This is a part of the WalletController interface. // This is a part of the WalletController interface.
func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx, func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx,
signDesc *lnwallet.SignDescriptor) ([]byte, error) { signDesc *lnwallet.SignDescriptor) ([]byte, error) {
witnessScript := signDesc.WitnessScript witnessScript := signDesc.WitnessScript
// First attempt to fetch the private key which corresponds to the // First attempt to fetch the private key which corresponds to the
// specified public key. // specified public key.
privKey, err := b.fetchPrivKey(signDesc.PubKey) privKey, err := b.fetchPrivKey(&signDesc.KeyDesc)
if err != nil { if err != nil {
return nil, err 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 // If we're spending p2wkh output nested within a p2sh output, then
// we'll need to attach a sigScript in addition to witness data. // we'll need to attach a sigScript in addition to witness data.
case pka.IsNestedWitness(): case pka.AddrType() == waddrmgr.NestedWitnessPubKey:
pubKey := privKey.PubKey() pubKey := privKey.PubKey()
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) 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 // First attempt to fetch the private key which corresponds to the
// specified public key. // specified public key.
privKey, err := b.fetchPrivKey(pubKey) privKey, err := b.fetchPrivKey(&keychain.KeyDescriptor{
PubKey: pubKey,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -835,15 +835,18 @@ func deriveCommitmentKeys(commitPoint *btcec.PublicKey, isOurCommit bool,
keyRing := &CommitmentKeyRing{ keyRing := &CommitmentKeyRing{
CommitPoint: commitPoint, CommitPoint: commitPoint,
LocalCommitKeyTweak: SingleTweakBytes(commitPoint, LocalCommitKeyTweak: SingleTweakBytes(
localChanCfg.PaymentBasePoint), commitPoint, localChanCfg.PaymentBasePoint.PubKey,
LocalHtlcKeyTweak: SingleTweakBytes(commitPoint, ),
localChanCfg.HtlcBasePoint), LocalHtlcKeyTweak: SingleTweakBytes(
commitPoint, localChanCfg.HtlcBasePoint.PubKey,
LocalHtlcKey: TweakPubKey(localChanCfg.HtlcBasePoint, ),
commitPoint), LocalHtlcKey: TweakPubKey(
RemoteHtlcKey: TweakPubKey(remoteChanCfg.HtlcBasePoint, localChanCfg.HtlcBasePoint.PubKey, commitPoint,
commitPoint), ),
RemoteHtlcKey: TweakPubKey(
remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
),
} }
// We'll now compute the delay, no delay, and revocation key based on // 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 revocationBasePoint *btcec.PublicKey
) )
if isOurCommit { if isOurCommit {
delayBasePoint = localChanCfg.DelayBasePoint delayBasePoint = localChanCfg.DelayBasePoint.PubKey
noDelayBasePoint = remoteChanCfg.PaymentBasePoint noDelayBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
revocationBasePoint = remoteChanCfg.RevocationBasePoint revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
} else { } else {
delayBasePoint = remoteChanCfg.DelayBasePoint delayBasePoint = remoteChanCfg.DelayBasePoint.PubKey
noDelayBasePoint = localChanCfg.PaymentBasePoint noDelayBasePoint = localChanCfg.PaymentBasePoint.PubKey
revocationBasePoint = localChanCfg.RevocationBasePoint revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
} }
// With the base points assigned, we can now derive the actual keys // With the base points assigned, we can now derive the actual keys
@ -1241,8 +1244,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
remoteUpdateLog: remoteUpdateLog, remoteUpdateLog: remoteUpdateLog,
ChanPoint: &state.FundingOutpoint, ChanPoint: &state.FundingOutpoint,
Capacity: state.Capacity, Capacity: state.Capacity,
LocalFundingKey: state.LocalChanCfg.MultiSigKey, LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey,
RemoteFundingKey: state.RemoteChanCfg.MultiSigKey, RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey,
quit: make(chan struct{}), quit: make(chan struct{}),
} }
@ -1277,8 +1280,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
// createSignDesc derives the SignDescriptor for commitment transactions from // createSignDesc derives the SignDescriptor for commitment transactions from
// other fields on the LightningChannel. // other fields on the LightningChannel.
func (lc *LightningChannel) createSignDesc() error { func (lc *LightningChannel) createSignDesc() error {
localKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
remoteKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
multiSigScript, err := genMultiSigScript(localKey, remoteKey) multiSigScript, err := genMultiSigScript(localKey, remoteKey)
if err != nil { if err != nil {
@ -1290,7 +1293,7 @@ func (lc *LightningChannel) createSignDesc() error {
return err return err
} }
lc.signDesc = &SignDescriptor{ lc.signDesc = &SignDescriptor{
PubKey: lc.localChanCfg.MultiSigKey, KeyDesc: lc.localChanCfg.MultiSigKey,
WitnessScript: multiSigScript, WitnessScript: multiSigScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: fundingPkScript, PkScript: fundingPkScript,
@ -1310,13 +1313,13 @@ func (lc *LightningChannel) createStateHintObfuscator() {
state := lc.channelState state := lc.channelState
if state.IsInitiator { if state.IsInitiator {
lc.stateHintObfuscator = DeriveStateHintObfuscator( lc.stateHintObfuscator = DeriveStateHintObfuscator(
state.LocalChanCfg.PaymentBasePoint, state.LocalChanCfg.PaymentBasePoint.PubKey,
state.RemoteChanCfg.PaymentBasePoint, state.RemoteChanCfg.PaymentBasePoint.PubKey,
) )
} else { } else {
lc.stateHintObfuscator = DeriveStateHintObfuscator( lc.stateHintObfuscator = DeriveStateHintObfuscator(
state.RemoteChanCfg.PaymentBasePoint, state.RemoteChanCfg.PaymentBasePoint.PubKey,
state.LocalChanCfg.PaymentBasePoint, state.LocalChanCfg.PaymentBasePoint.PubKey,
) )
} }
} }
@ -1820,7 +1823,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
if localAmt >= chanState.RemoteChanCfg.DustLimit { if localAmt >= chanState.RemoteChanCfg.DustLimit {
localSignDesc = &SignDescriptor{ localSignDesc = &SignDescriptor{
SingleTweak: keyRing.LocalCommitKeyTweak, SingleTweak: keyRing.LocalCommitKeyTweak,
PubKey: chanState.LocalChanCfg.PaymentBasePoint, KeyDesc: chanState.LocalChanCfg.PaymentBasePoint,
WitnessScript: localPkScript, WitnessScript: localPkScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: localPkScript, PkScript: localPkScript,
@ -1834,7 +1837,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// limit, assemble the remote sign descriptor. // limit, assemble the remote sign descriptor.
if remoteAmt >= chanState.RemoteChanCfg.DustLimit { if remoteAmt >= chanState.RemoteChanCfg.DustLimit {
remoteSignDesc = &SignDescriptor{ remoteSignDesc = &SignDescriptor{
PubKey: chanState.LocalChanCfg.RevocationBasePoint, KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
DoubleTweak: commitmentSecret, DoubleTweak: commitmentSecret,
WitnessScript: remotePkScript, WitnessScript: remotePkScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -1894,7 +1897,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
htlcRetributions[i] = HtlcRetribution{ htlcRetributions[i] = HtlcRetribution{
SignDesc: SignDescriptor{ SignDesc: SignDescriptor{
PubKey: chanState.LocalChanCfg.RevocationBasePoint, KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
DoubleTweak: commitmentSecret, DoubleTweak: commitmentSecret,
WitnessScript: htlcScript, WitnessScript: htlcScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -2462,7 +2465,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// signature to give to the remote party for this commitment // signature to give to the remote party for this commitment
// transaction. Note we use the raw HTLC amount. // transaction. Note we use the raw HTLC amount.
sigJob.signDesc = SignDescriptor{ sigJob.signDesc = SignDescriptor{
PubKey: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlc.theirWitnessScript, WitnessScript: htlc.theirWitnessScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -2512,7 +2515,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// signature to give to the remote party for this commitment // signature to give to the remote party for this commitment
// transaction. Note we use the raw HTLC amount. // transaction. Note we use the raw HTLC amount.
sigJob.signDesc = SignDescriptor{ sigJob.signDesc = SignDescriptor{
PubKey: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlc.theirWitnessScript, WitnessScript: htlc.theirWitnessScript,
Output: &wire.TxOut{ 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 // we'll ensure that the newly constructed commitment state has a valid
// signature. // signature.
verifyKey := btcec.PublicKey{ verifyKey := btcec.PublicKey{
X: lc.remoteChanCfg.MultiSigKey.X, X: lc.remoteChanCfg.MultiSigKey.PubKey.X,
Y: lc.remoteChanCfg.MultiSigKey.Y, Y: lc.remoteChanCfg.MultiSigKey.PubKey.Y,
Curve: btcec.S256(), Curve: btcec.S256(),
} }
cSig, err := commitSig.ToSignature() cSig, err := commitSig.ToSignature()
@ -4145,8 +4148,8 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// With the final signature generated, create the witness stack // With the final signature generated, create the witness stack
// required to spend from the multi-sig output. // required to spend from the multi-sig output.
ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
commitTx.TxIn[0].Witness = SpendMultiSig( commitTx.TxIn[0].Witness = SpendMultiSig(
lc.signDesc.WitnessScript, ourKey, lc.signDesc.WitnessScript, ourKey,
@ -4267,7 +4270,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer,
commitResolution = &CommitOutputResolution{ commitResolution = &CommitOutputResolution{
SelfOutPoint: *selfPoint, SelfOutPoint: *selfPoint,
SelfOutputSignDesc: SignDescriptor{ SelfOutputSignDesc: SignDescriptor{
PubKey: localPayBase, KeyDesc: localPayBase,
SingleTweak: keyRing.LocalCommitKeyTweak, SingleTweak: keyRing.LocalCommitKeyTweak,
WitnessScript: selfP2WKH, WitnessScript: selfP2WKH,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4435,7 +4438,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
Expiry: htlc.RefundTimeout, Expiry: htlc.RefundTimeout,
ClaimOutpoint: op, ClaimOutpoint: op,
SweepSignDesc: SignDescriptor{ SweepSignDesc: SignDescriptor{
PubKey: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlcReceiverScript, WitnessScript: htlcReceiverScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4475,7 +4478,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
return nil, err return nil, err
} }
timeoutSignDesc := SignDescriptor{ timeoutSignDesc := SignDescriptor{
PubKey: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlcCreationScript, WitnessScript: htlcCreationScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4509,8 +4512,9 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
return nil, err return nil, err
} }
localDelayTweak := SingleTweakBytes(keyRing.CommitPoint, localDelayTweak := SingleTweakBytes(
localChanCfg.DelayBasePoint) keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
)
return &OutgoingHtlcResolution{ return &OutgoingHtlcResolution{
Expiry: htlc.RefundTimeout, Expiry: htlc.RefundTimeout,
SignedTimeoutTx: timeoutTx, SignedTimeoutTx: timeoutTx,
@ -4520,7 +4524,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
Index: 0, Index: 0,
}, },
SweepSignDesc: SignDescriptor{ SweepSignDesc: SignDescriptor{
PubKey: localChanCfg.DelayBasePoint, KeyDesc: localChanCfg.DelayBasePoint,
SingleTweak: localDelayTweak, SingleTweak: localDelayTweak,
WitnessScript: htlcSweepScript, WitnessScript: htlcSweepScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4573,7 +4577,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
ClaimOutpoint: op, ClaimOutpoint: op,
CsvDelay: csvDelay, CsvDelay: csvDelay,
SweepSignDesc: SignDescriptor{ SweepSignDesc: SignDescriptor{
PubKey: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlcSenderScript, WitnessScript: htlcSenderScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4609,7 +4613,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
return nil, err return nil, err
} }
successSignDesc := SignDescriptor{ successSignDesc := SignDescriptor{
PubKey: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlcCreationScript, WitnessScript: htlcCreationScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4645,7 +4649,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
} }
localDelayTweak := SingleTweakBytes( localDelayTweak := SingleTweakBytes(
keyRing.CommitPoint, localChanCfg.DelayBasePoint, keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
) )
return &IncomingHtlcResolution{ return &IncomingHtlcResolution{
Preimage: preimage, Preimage: preimage,
@ -4656,7 +4660,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
Index: 0, Index: 0,
}, },
SweepSignDesc: SignDescriptor{ SweepSignDesc: SignDescriptor{
PubKey: localChanCfg.DelayBasePoint, KeyDesc: localChanCfg.DelayBasePoint,
SingleTweak: localDelayTweak, SingleTweak: localDelayTweak,
WitnessScript: htlcSweepScript, WitnessScript: htlcSweepScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -4844,8 +4848,9 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
// nil. // nil.
var commitResolution *CommitOutputResolution var commitResolution *CommitOutputResolution
if len(delayScript) != 0 { if len(delayScript) != 0 {
singleTweak := SingleTweakBytes(commitPoint, singleTweak := SingleTweakBytes(
lc.localChanCfg.DelayBasePoint) commitPoint, lc.localChanCfg.DelayBasePoint.PubKey,
)
localBalance := localCommitment.LocalBalance localBalance := localCommitment.LocalBalance
commitResolution = &CommitOutputResolution{ commitResolution = &CommitOutputResolution{
SelfOutPoint: wire.OutPoint{ SelfOutPoint: wire.OutPoint{
@ -4853,7 +4858,7 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
Index: delayIndex, Index: delayIndex,
}, },
SelfOutputSignDesc: SignDescriptor{ SelfOutputSignDesc: SignDescriptor{
PubKey: lc.localChanCfg.DelayBasePoint, KeyDesc: lc.localChanCfg.DelayBasePoint,
SingleTweak: singleTweak, SingleTweak: singleTweak,
WitnessScript: selfScript, WitnessScript: selfScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
@ -5012,8 +5017,8 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte,
// Finally, construct the witness stack minding the order of the // Finally, construct the witness stack minding the order of the
// pubkeys+sigs on the stack. // pubkeys+sigs on the stack.
ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
witness := SpendMultiSig(lc.signDesc.WitnessScript, ourKey, witness := SpendMultiSig(lc.signDesc.WitnessScript, ourKey,
localSig, theirKey, remoteSig) localSig, theirKey, remoteSig)
closeTx.TxIn[0].Witness = witness closeTx.TxIn[0].Witness = witness

View File

@ -13,6 +13,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
"github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/blockchain"
@ -168,12 +169,22 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
MinHTLC: 0, MinHTLC: 0,
MaxAcceptedHtlcs: MaxHTLCNumber / 2, MaxAcceptedHtlcs: MaxHTLCNumber / 2,
}, },
CsvDelay: uint16(csvTimeoutAlice), CsvDelay: uint16(csvTimeoutAlice),
MultiSigKey: aliceKeys[0].PubKey(), MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: aliceKeys[1].PubKey(), PubKey: aliceKeys[0].PubKey(),
PaymentBasePoint: aliceKeys[2].PubKey(), },
DelayBasePoint: aliceKeys[3].PubKey(), RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: aliceKeys[4].PubKey(), 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{ bobCfg := channeldb.ChannelConfig{
ChannelConstraints: channeldb.ChannelConstraints{ ChannelConstraints: channeldb.ChannelConstraints{
@ -183,28 +194,40 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
MinHTLC: 0, MinHTLC: 0,
MaxAcceptedHtlcs: MaxHTLCNumber / 2, MaxAcceptedHtlcs: MaxHTLCNumber / 2,
}, },
CsvDelay: uint16(csvTimeoutBob), CsvDelay: uint16(csvTimeoutBob),
MultiSigKey: bobKeys[0].PubKey(), MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: bobKeys[1].PubKey(), PubKey: bobKeys[0].PubKey(),
PaymentBasePoint: bobKeys[2].PubKey(), },
DelayBasePoint: bobKeys[3].PubKey(), RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: bobKeys[4].PubKey(), 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( bobRoot, err := chainhash.NewHash(bobKeys[0].Serialize())
bobKeys[0], testHdSeed, aliceKeys[0].PubKey(), if err != nil {
) return nil, nil, nil, err
bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) }
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:]) bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:])
aliceRoot := DeriveRevocationRoot( aliceRoot, err := chainhash.NewHash(aliceKeys[0].Serialize())
aliceKeys[0], testHdSeed, bobKeys[0].PubKey(), if err != nil {
) return nil, nil, nil, err
alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) }
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, err 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. // The rest of the close summary should have been populated properly.
aliceDelayPoint := aliceChannel.channelState.LocalChanCfg.DelayBasePoint aliceDelayPoint := aliceChannel.channelState.LocalChanCfg.DelayBasePoint
if !aliceCommitResolution.SelfOutputSignDesc.PubKey.IsEqual( if !aliceCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(
aliceDelayPoint, aliceDelayPoint.PubKey,
) { ) {
t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc") 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") t.Fatalf("bob fails to include to-self output in ForceCloseSummary")
} }
bobDelayPoint := bobChannel.channelState.LocalChanCfg.DelayBasePoint 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") t.Fatalf("bob incorrect pubkey in SelfOutputSignDesc")
} }
if bobCommitResolution.SelfOutputSignDesc.Output.Value != if bobCommitResolution.SelfOutputSignDesc.Output.Value !=
@ -1156,8 +1179,8 @@ func TestForceCloseDustOutput(t *testing.T) {
t.Fatalf("alice fails to include to-self output in " + t.Fatalf("alice fails to include to-self output in " +
"ForceCloseSummary") "ForceCloseSummary")
} }
if !commitResolution.SelfOutputSignDesc.PubKey.IsEqual( if !commitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(
aliceChannel.channelState.LocalChanCfg.DelayBasePoint, aliceChannel.channelState.LocalChanCfg.DelayBasePoint.PubKey,
) { ) {
t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc") 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) { func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) {
pubkey := signDesc.PubKey pubkey := signDesc.KeyDesc.PubKey
switch { switch {
case signDesc.SingleTweak != nil: case signDesc.SingleTweak != nil:
pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak) pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak)

View File

@ -3,6 +3,7 @@ package lnwallet
import ( import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg"
) )
@ -22,6 +23,12 @@ type Config struct {
// counterparty's broadcasting revoked commitment states. // counterparty's broadcasting revoked commitment states.
Notifier chainntnfs.ChainNotifier 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 // WalletController is the core wallet, all non Lightning Network
// specific interaction is proxied to the internal wallet. // specific interaction is proxied to the internal wallet.
WalletController WalletController WalletController WalletController

View File

@ -20,19 +20,16 @@ var ErrNotMine = errors.New("the passed output doesn't belong to the wallet")
type AddressType uint8 type AddressType uint8
const ( const (
// UnknownAddressType represents an output with an unknown or non-standard
// script.
UnknownAddressType AddressType = iota
// WitnessPubKey represents a p2wkh address. // WitnessPubKey represents a p2wkh address.
WitnessPubKey WitnessPubKey AddressType = iota
// NestedWitnessPubKey represents a p2sh output which is itself a // NestedWitnessPubKey represents a p2sh output which is itself a
// nested p2wkh output. // nested p2wkh output.
NestedWitnessPubKey NestedWitnessPubKey
// PubKeyHash represents a regular p2pkh output. // UnknownAddressType represents an output with an unknown or non-standard
PubKeyHash // script.
UnknownAddressType
) )
// ErrDoubleSpend is returned from PublishTransaction in case the // 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, // that have at least confs confirmations. If confs is set to zero,
// then all unspent outputs, including those currently in the mempool // then all unspent outputs, including those currently in the mempool
// will be included in the final sum. // 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 // NewAddress returns the next external or internal address for the
// wallet dictated by the value of the `change` parameter. If change is // wallet dictated by the value of the `change` parameter. If change is
// true, then an internal address should be used, otherwise an external // true, then an internal address should be used, otherwise an external
// address should be returned. The type of address returned is dictated // address should be returned. The type of address returned is dictated
// by the wallet's capabilities, and may be of type: p2sh, p2pkh, // by the wallet's capabilities, and may be of type: p2sh, p2wkh,
// p2wkh, p2wsh, etc. // p2wsh, etc.
NewAddress(addrType AddressType, change bool) (btcutil.Address, error) NewAddress(addrType AddressType, change bool) (btcutil.Address, error)
// GetPrivKey retrieves the underlying private key associated with the // GetPrivKey retrieves the underlying private key associated with the
@ -143,20 +144,6 @@ type WalletController interface {
// error should be returned. // error should be returned.
GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error) 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 // SendOutputs funds, signs, and broadcasts a Bitcoin transaction
// paying out to the specified outputs. In the case the wallet has // paying out to the specified outputs. In the case the wallet has
// insufficient funds, or the outputs are non-standard, an error should // 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"
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@ -101,7 +102,7 @@ var (
// within the wallet are *exactly* amount. If unable to retrieve the current // 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. // 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) { 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 { if err != nil {
t.Fatalf("unable to query for balance: %v", err) 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 // Using the mining node, spend from a coinbase output numOutputs to
// give us btcPerOutput with each output. // give us btcPerOutput with each output.
satoshiPerOutput := int64(btcPerOutput * 1e8) satoshiPerOutput := int64(btcPerOutput * 1e8)
expectedBalance, err := w.ConfirmedBalance(1, false) expectedBalance, err := w.ConfirmedBalance(1)
if err != nil { if err != nil {
return err return err
} }
@ -202,7 +203,7 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
timeout := time.After(30 * time.Second) timeout := time.After(30 * time.Second)
for range ticker.C { for range ticker.C {
balance, err := w.ConfirmedBalance(1, false) balance, err := w.ConfirmedBalance(1)
if err != nil { if err != nil {
return err return err
} }
@ -230,8 +231,8 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
// available for funding channels. // available for funding channels.
func createTestWallet(tempTestDir string, miningNode *rpctest.Harness, func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier, netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier,
wc lnwallet.WalletController, signer lnwallet.Signer, wc lnwallet.WalletController, keyRing keychain.SecretKeyRing,
bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) { signer lnwallet.Signer, bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) {
dbDir := filepath.Join(tempTestDir, "cdb") dbDir := filepath.Join(tempTestDir, "cdb")
cdb, err := channeldb.Open(dbDir) cdb, err := channeldb.Open(dbDir)
@ -242,6 +243,7 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
cfg := lnwallet.Config{ cfg := lnwallet.Config{
Database: cdb, Database: cdb,
Notifier: notifier, Notifier: notifier,
SecretKeyRing: keyRing,
WalletController: wc, WalletController: wc,
Signer: signer, Signer: signer,
ChainIO: bio, ChainIO: bio,
@ -360,7 +362,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
t.Fatalf("bob's commit signatures not populated") 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. // then the other way around.
_, err = aliceChanReservation.CompleteReservation( _, err = aliceChanReservation.CompleteReservation(
bobFundingSigs, bobCommitSig, bobFundingSigs, bobCommitSig,
@ -597,16 +599,16 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi
t.Fatalf("line #%v: csv delay not set", line) 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) 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) 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) 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) 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) t.Fatalf("line #%v: max pending amt not set", line)
} }
if c.ChanReserve == 0 { 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) t.Fatalf("line #%v: chan reserve not set", line)
} }
if c.MinHTLC == 0 { if c.MinHTLC == 0 {
@ -1154,11 +1154,13 @@ func testPublishTransaction(r *rpctest.Harness,
} }
// Generate a pubkey, and pay-to-addr script. // Generate a pubkey, and pay-to-addr script.
pubKey, err := alice.NewRawKey() pubKey, err := alice.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
t.Fatalf("unable to obtain public key: %v", err) 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, keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash,
&chaincfg.RegressionNetParams) &chaincfg.RegressionNetParams)
if err != nil { if err != nil {
@ -1214,7 +1216,9 @@ func testPublishTransaction(r *rpctest.Harness,
// Now we can populate the sign descriptor which we'll use to // Now we can populate the sign descriptor which we'll use to
// generate the signature. // generate the signature.
signDesc := &lnwallet.SignDescriptor{ signDesc := &lnwallet.SignDescriptor{
PubKey: pubKey, KeyDesc: keychain.KeyDescriptor{
PubKey: pubKey.PubKey,
},
WitnessScript: keyScript, WitnessScript: keyScript,
Output: tx.TxOut[outputIndex], Output: tx.TxOut[outputIndex],
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
@ -1231,7 +1235,7 @@ func testPublishTransaction(r *rpctest.Harness,
} }
witness := make([][]byte, 2) witness := make([][]byte, 2)
witness[0] = append(spendSig, byte(txscript.SigHashAll)) witness[0] = append(spendSig, byte(txscript.SigHashAll))
witness[1] = pubKey.SerializeCompressed() witness[1] = pubKey.PubKey.SerializeCompressed()
tx1.TxIn[0].Witness = witness tx1.TxIn[0].Witness = witness
// Finally, attempt to validate the completed transaction. This // 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) t.Fatalf("unable to mine tx: %v", err)
} }
txFee := btcutil.Amount(0.1 * btcutil.SatoshiPerBitcoin) txFee := btcutil.Amount(0.1 * btcutil.SatoshiPerBitcoin)
tx1 := txFromOutput(tx.MsgTx(), pubKey, txFee) tx1 := txFromOutput(tx.MsgTx(), pubKey.PubKey, txFee)
return tx1 return tx1
} }
@ -1367,7 +1371,7 @@ func testPublishTransaction(r *rpctest.Harness,
// from the tx just mined. This should be accepted // from the tx just mined. This should be accepted
// into the mempool. // into the mempool.
txFee := btcutil.Amount(0.05 * btcutil.SatoshiPerBitcoin) 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 { if err := alice.PublishTransaction(tx4); err != nil {
t.Fatalf("unable to publish: %v", err) 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 // Create a new key we'll pay to, to ensure we create
// a unique transaction. // a unique transaction.
pubKey2, err := alice.NewRawKey() pubKey2, err := alice.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
t.Fatalf("unable to obtain public key: %v", err) 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 // Create a new transaction that spends the output from
// tx3, and that pays to a different address. We expect // tx3, and that pays to a different address. We expect
// this to be rejected because it is a double spend. // 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 { if err := alice.PublishTransaction(tx5); err != lnwallet.ErrDoubleSpend {
t.Fatalf("expected ErrDoubleSpend, got: %v", err) 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 // but has a higher fee. We expect also this tx to be
// rejected, since the sequence number of tx3 is set to Max, // rejected, since the sequence number of tx3 is set to Max,
// indicating it is not replacable. // indicating it is not replacable.
pubKey3, err := alice.NewRawKey() pubKey3, err := alice.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
t.Fatalf("unable to obtain public key: %v", err) t.Fatalf("unable to obtain public key: %v", err)
} }
tx6 := txFromOutput(tx3, pubKey3, 3*txFee) tx6 := txFromOutput(tx3, pubKey3.PubKey, 3*txFee)
// Expect rejection. // Expect rejection.
if err := alice.PublishTransaction(tx6); err != lnwallet.ErrDoubleSpend { if err := alice.PublishTransaction(tx6); err != lnwallet.ErrDoubleSpend {
@ -1422,11 +1430,13 @@ func testPublishTransaction(r *rpctest.Harness,
} }
// Create another tx spending tx3. // Create another tx spending tx3.
pubKey4, err := alice.NewRawKey() pubKey4, err := alice.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
t.Fatalf("unable to obtain public key: %v", err) t.Fatalf("unable to obtain public key: %v", err)
} }
tx7 := txFromOutput(tx3, pubKey4, txFee) tx7 := txFromOutput(tx3, pubKey4.PubKey, txFee)
// Expect rejection. // Expect rejection.
if err := alice.PublishTransaction(tx7); err != lnwallet.ErrDoubleSpend { 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, // First, generate a new public key under the control of the wallet,
// then generate a revocation key using it. // then generate a revocation key using it.
pubKey, err := alice.NewRawKey() pubKey, err := alice.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
t.Fatalf("unable to obtain public key: %v", err) t.Fatalf("unable to obtain public key: %v", err)
} }
@ -1461,10 +1473,10 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(), commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(),
commitPreimage) commitPreimage)
revocationKey := lnwallet.DeriveRevocationPubkey(pubKey, commitPoint) revocationKey := lnwallet.DeriveRevocationPubkey(pubKey.PubKey, commitPoint)
commitTweak := lnwallet.SingleTweakBytes(commitPoint, pubKey) 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 // 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, // 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 // based on this tweak value and the key we originally
// generated above. // generated above.
signDesc := &lnwallet.SignDescriptor{ signDesc := &lnwallet.SignDescriptor{
PubKey: baseKey, KeyDesc: keychain.KeyDescriptor{
PubKey: baseKey.PubKey,
},
WitnessScript: keyScript, WitnessScript: keyScript,
Output: newOutput, Output: newOutput,
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
@ -1584,6 +1598,7 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet, func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
_ *lnwallet.LightningWallet, t *testing.T) { _ *lnwallet.LightningWallet, t *testing.T) {
// We first mine a few blocks to ensure any transactions still in the // We first mine a few blocks to ensure any transactions still in the
// mempool confirm, and then get the original balance, before a // mempool confirm, and then get the original balance, before a
// reorganization that doesn't invalidate any existing transactions or // 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. // Get the original balance.
origBalance, err := w.ConfirmedBalance(1, false) origBalance, err := w.ConfirmedBalance(1)
if err != nil { if err != nil {
t.Fatalf("unable to query for balance: %v", err) 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) t.Fatalf("unable to set up mining node: %v", err)
} }
defer r2.TearDown() defer r2.TearDown()
newBalance, err := w.ConfirmedBalance(1, false) newBalance, err := w.ConfirmedBalance(1)
if err != nil { if err != nil {
t.Fatalf("unable to query for balance: %v", err) 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. // Now we check that the wallet balance stays the same.
newBalance, err = w.ConfirmedBalance(1, false) newBalance, err = w.ConfirmedBalance(1)
if err != nil { if err != nil {
t.Fatalf("unable to query for balance: %v", err) t.Fatalf("unable to query for balance: %v", err)
} }
@ -1966,6 +1981,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
aliceSigner lnwallet.Signer aliceSigner lnwallet.Signer
bobSigner lnwallet.Signer bobSigner lnwallet.Signer
aliceKeyRing keychain.SecretKeyRing
bobKeyRing keychain.SecretKeyRing
aliceWalletController lnwallet.WalletController aliceWalletController lnwallet.WalletController
bobWalletController lnwallet.WalletController bobWalletController lnwallet.WalletController
@ -2008,14 +2026,17 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
if err != nil { if err != nil {
t.Fatalf("unable to make chain rpc: %v", err) t.Fatalf("unable to make chain rpc: %v", err)
} }
case "neutrino": case "neutrino":
feeEstimator = lnwallet.StaticFeeEstimator{FeeRate: 250} feeEstimator = lnwallet.StaticFeeEstimator{FeeRate: 250}
// Set some package-level variable to speed up // Set some package-level variable to speed up
// operation for tests. // operation for tests.
neutrino.WaitForMoreCFHeaders = time.Millisecond * 100 neutrino.WaitForMoreCFHeaders = time.Millisecond * 100
neutrino.BanDuration = time.Millisecond * 100 neutrino.BanDuration = time.Millisecond * 100
neutrino.QueryTimeout = time.Millisecond * 500 neutrino.QueryTimeout = time.Millisecond * 500
neutrino.QueryNumRetries = 2 neutrino.QueryNumRetries = 2
// Start Alice - open a database, start a neutrino // Start Alice - open a database, start a neutrino
// instance, and initialize a btcwallet driver for it. // instance, and initialize a btcwallet driver for it.
aliceDB, err := walletdb.Create("bdb", aliceDB, err := walletdb.Create("bdb",
@ -2065,6 +2086,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
bobChain.Start() bobChain.Start()
defer bobChain.Stop() defer bobChain.Stop()
bobClient = chain.NewNeutrinoClient(bobChain) bobClient = chain.NewNeutrinoClient(bobChain)
case "bitcoind": case "bitcoind":
feeEstimator, err = lnwallet.NewBitcoindFeeEstimator( feeEstimator, err = lnwallet.NewBitcoindFeeEstimator(
rpcConfig, 250) rpcConfig, 250)
@ -2133,6 +2155,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
t.Fatalf("unable to create btcwallet: %v", err) t.Fatalf("unable to create btcwallet: %v", err)
} }
aliceSigner = aliceWalletController.(*btcwallet.BtcWallet) aliceSigner = aliceWalletController.(*btcwallet.BtcWallet)
aliceKeyRing = keychain.NewBtcWalletKeyRing(
aliceWalletController.(*btcwallet.BtcWallet).InternalWallet(),
)
bobWalletConfig := &btcwallet.Config{ bobWalletConfig := &btcwallet.Config{
PrivatePass: []byte("bob-pass"), PrivatePass: []byte("bob-pass"),
@ -2147,21 +2172,30 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
t.Fatalf("unable to create btcwallet: %v", err) t.Fatalf("unable to create btcwallet: %v", err)
} }
bobSigner = bobWalletController.(*btcwallet.BtcWallet) bobSigner = bobWalletController.(*btcwallet.BtcWallet)
bobKeyRing = keychain.NewBtcWalletKeyRing(
bobWalletController.(*btcwallet.BtcWallet).InternalWallet(),
)
bio = bobWalletController.(*btcwallet.BtcWallet) bio = bobWalletController.(*btcwallet.BtcWallet)
default: default:
t.Fatalf("unknown wallet driver: %v", walletType) t.Fatalf("unknown wallet driver: %v", walletType)
} }
// Funding via 20 outputs with 4BTC each. // Funding via 20 outputs with 4BTC each.
alice, err := createTestWallet(tempTestDirAlice, miningNode, netParams, alice, err := createTestWallet(
chainNotifier, aliceWalletController, aliceSigner, bio) tempTestDirAlice, miningNode, netParams,
chainNotifier, aliceWalletController, aliceKeyRing,
aliceSigner, bio,
)
if err != nil { if err != nil {
t.Fatalf("unable to create test ln wallet: %v", err) t.Fatalf("unable to create test ln wallet: %v", err)
} }
defer alice.Shutdown() defer alice.Shutdown()
bob, err := createTestWallet(tempTestDirBob, miningNode, netParams, bob, err := createTestWallet(
chainNotifier, bobWalletController, bobSigner, bio) tempTestDirBob, miningNode, netParams,
chainNotifier, bobWalletController, bobKeyRing,
bobSigner, bio,
)
if err != nil { if err != nil {
t.Fatalf("unable to create test ln wallet: %v", err) t.Fatalf("unable to create test ln wallet: %v", err)
} }

View File

@ -7,11 +7,9 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil" "github.com/roasbeef/btcutil"
@ -312,10 +310,17 @@ func senderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
func SenderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, func SenderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
sweepTx *wire.MsgTx) (wire.TxWitness, error) { 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 // Derive the revocation key using the local revocation base point and
// commitment point. // commitment point.
revokeKey := DeriveRevocationPubkey(signDesc.PubKey, revokeKey := DeriveRevocationPubkey(
signDesc.DoubleTweak.PubKey()) signDesc.KeyDesc.PubKey,
signDesc.DoubleTweak.PubKey(),
)
return senderHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx) return senderHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx)
} }
@ -562,10 +567,17 @@ func receiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
func ReceiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, func ReceiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
sweepTx *wire.MsgTx) (wire.TxWitness, error) { 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 // Derive the revocation key using the local revocation base point and
// commitment point. // commitment point.
revokeKey := DeriveRevocationPubkey(signDesc.PubKey, revokeKey := DeriveRevocationPubkey(
signDesc.DoubleTweak.PubKey()) signDesc.KeyDesc.PubKey,
signDesc.DoubleTweak.PubKey(),
)
return receiverHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx) return receiverHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx)
} }
@ -1023,6 +1035,11 @@ func CommitSpendRevoke(signer Signer, signDesc *SignDescriptor,
func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor, func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor,
sweepTx *wire.MsgTx) (wire.TxWitness, error) { 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: // This is just a regular p2wkh spend which looks something like:
// * witness: <sig> <pubkey> // * witness: <sig> <pubkey>
sweepSig, err := signer.SignOutputRaw(sweepTx, signDesc) sweepSig, err := signer.SignOutputRaw(sweepTx, signDesc)
@ -1037,7 +1054,7 @@ func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor,
witness := make([][]byte, 2) witness := make([][]byte, 2)
witness[0] = append(sweepSig, byte(signDesc.HashType)) witness[0] = append(sweepSig, byte(signDesc.HashType))
witness[1] = TweakPubKeyWithTweak( witness[1] = TweakPubKeyWithTweak(
signDesc.PubKey, signDesc.SingleTweak, signDesc.KeyDesc.PubKey, signDesc.SingleTweak,
).SerializeCompressed() ).SerializeCompressed()
return witness, nil return witness, nil
@ -1218,33 +1235,6 @@ func DeriveRevocationPrivKey(revokeBasePriv *btcec.PrivateKey,
return priv 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 // SetStateNumHint encodes the current state number within the passed
// commitment transaction by re-purposing the locktime and sequence fields in // commitment transaction by re-purposing the locktime and sequence fields in
// the commitment transaction to encode the obfuscated state number. The state // the commitment transaction to encode the obfuscated state number. The state

View File

@ -8,6 +8,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/lightningnetwork/lnd/keychain"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/txscript"
@ -112,9 +113,11 @@ func TestCommitmentSpendValidation(t *testing.T) {
sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, csvTimeout) sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, csvTimeout)
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
WitnessScript: delayScript, WitnessScript: delayScript,
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
SingleTweak: aliceCommitTweak, PubKey: aliceKeyPub,
SigHashes: txscript.NewTxSigHashes(sweepTx), },
SingleTweak: aliceCommitTweak,
SigHashes: txscript.NewTxSigHashes(sweepTx),
Output: &wire.TxOut{ Output: &wire.TxOut{
Value: int64(channelBalance), Value: int64(channelBalance),
}, },
@ -143,7 +146,9 @@ func TestCommitmentSpendValidation(t *testing.T) {
// simulate the scenario when Alice broadcasts this commitment // simulate the scenario when Alice broadcasts this commitment
// transaction after it's been revoked. // transaction after it's been revoked.
signDesc = &SignDescriptor{ signDesc = &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
DoubleTweak: commitSecret, DoubleTweak: commitSecret,
WitnessScript: delayScript, WitnessScript: delayScript,
SigHashes: txscript.NewTxSigHashes(sweepTx), SigHashes: txscript.NewTxSigHashes(sweepTx),
@ -186,7 +191,9 @@ func TestCommitmentSpendValidation(t *testing.T) {
t.Fatalf("unable to create bob p2wkh script: %v", err) t.Fatalf("unable to create bob p2wkh script: %v", err)
} }
signDesc = &SignDescriptor{ signDesc = &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak, SingleTweak: bobCommitTweak,
WitnessScript: bobScriptP2WKH, WitnessScript: bobScriptP2WKH,
SigHashes: txscript.NewTxSigHashes(sweepTx), 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 // that will act as Bob's signature to Alice for the second level HTLC
// transaction. // transaction.
bobSignDesc := SignDescriptor{ bobSignDesc := SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak, SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -416,7 +425,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
// TODO(roasbeef): test invalid revoke // TODO(roasbeef): test invalid revoke
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
DoubleTweak: commitSecret, DoubleTweak: commitSecret,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -434,7 +445,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
// HTLC with invalid preimage size // HTLC with invalid preimage size
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak, SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -455,7 +468,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
// TODO(roasbeef): invalid preimage // TODO(roasbeef): invalid preimage
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak, SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -475,7 +490,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
// transaction. // transaction.
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak, SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, 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 { for i, testCase := range testCases {
sweepTx.TxIn[0].Witness = testCase.witness() 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 // that will act as Alice's signature to Bob for the second level HTLC
// transaction. // transaction.
aliceSignDesc := SignDescriptor{ aliceSignDesc := SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak, SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -661,7 +683,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
// HTLC redemption w/ invalid preimage size // HTLC redemption w/ invalid preimage size
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak, SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -681,7 +705,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
// HTLC redemption w/ valid preimage size // HTLC redemption w/ valid preimage size
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak, SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -700,7 +726,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
// revoke w/ sig // revoke w/ sig
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DoubleTweak: commitSecret, DoubleTweak: commitSecret,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -718,7 +746,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
// refund w/ invalid lock time // refund w/ invalid lock time
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak, SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -736,7 +766,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
// refund w/ valid lock time // refund w/ valid lock time
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak, SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -881,7 +913,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
// use the double tweak in this case). // use the double tweak in this case).
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
@ -898,7 +932,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
// Sender of HTLC activates the revocation clause. // Sender of HTLC activates the revocation clause.
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: aliceKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DoubleTweak: commitSecret, DoubleTweak: commitSecret,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -918,7 +954,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
// blocks instead of 5 blocks). // blocks instead of 5 blocks).
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: commitTweak, SingleTweak: commitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
@ -938,7 +976,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
// tweak). // tweak).
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
@ -956,7 +996,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
// delay, and the correct key. // delay, and the correct key.
makeWitnessTestCase(t, func() (wire.TxWitness, error) { makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{ signDesc := &SignDescriptor{
PubKey: bobKeyPub, KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: commitTweak, SingleTweak: commitTweak,
WitnessScript: htlcWitnessScript, WitnessScript: htlcWitnessScript,
Output: htlcOutput, Output: htlcOutput,

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"io" "io"
"github.com/lightningnetwork/lnd/keychain"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire" "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 // 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. // access to critical data needed to generate a valid signature.
type SignDescriptor struct { type SignDescriptor struct {
// Pubkey is the public key to which the signature should be generated // KeyDesc is a descriptor that precisely describes *which* key to use
// over. The Signer should then generate a signature with the private // for signing. This may provide the raw public key directly, or
// key corresponding to this public key. // require the Signer to re-derive the key according to the populated
PubKey *btcec.PublicKey // derivation path.
KeyDesc keychain.KeyDescriptor
// SingleTweak is a scalar value that will be added to the private key // 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 // 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 // yet, since that is usually done just before broadcast by the witness
// generator. // generator.
func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error { func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error {
serializedPubKey := sd.PubKey.SerializeCompressed() err := binary.Write(w, binary.BigEndian, sd.KeyDesc.Family)
if err := wire.WriteVarBytes(w, 0, serializedPubKey); err != nil { if err != nil {
return err 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 { if err := wire.WriteVarBytes(w, 0, sd.SingleTweak); err != nil {
return err return err
@ -120,15 +138,34 @@ func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error {
// ReadSignDescriptor deserializes a SignDescriptor struct from the passed // ReadSignDescriptor deserializes a SignDescriptor struct from the passed
// io.Reader stream. // io.Reader stream.
func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error { 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 { if err != nil {
return err return err
} }
sd.PubKey, err = btcec.ParsePubKey(pubKeyBytes, btcec.S256()) err = binary.Read(r, binary.BigEndian, &sd.KeyDesc.Index)
if err != nil { if err != nil {
return err 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") singleTweak, err := wire.ReadVarBytes(r, 0, 32, "singleTweak")
if err != nil { if err != nil {
return err return err

View File

@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/lightningnetwork/lnd/keychain"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcd/wire"
@ -99,7 +100,13 @@ func TestSignDescriptorSerialization(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to parse pubkey: %v", err) 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. // Test that serialize -> deserialize yields same result as original.
var buf bytes.Buffer var buf bytes.Buffer

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
@ -325,7 +326,7 @@ func (tc *testContext) extractFundingInput() (*Utxo, *wire.TxOut, error) {
} }
block1Utxo := Utxo{ block1Utxo := Utxo{
AddressType: PubKeyHash, AddressType: WitnessPubKey,
Value: btcutil.Amount(txout.Value), Value: btcutil.Amount(txout.Value),
OutPoint: wire.OutPoint{ OutPoint: wire.OutPoint{
Hash: *tx.Hash(), Hash: *tx.Hash(),
@ -378,16 +379,30 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
MaxPendingAmount: lnwire.NewMSatFromSatoshis(tc.fundingAmount), MaxPendingAmount: lnwire.NewMSatFromSatoshis(tc.fundingAmount),
MaxAcceptedHtlcs: MaxHTLCNumber, MaxAcceptedHtlcs: MaxHTLCNumber,
}, },
CsvDelay: tc.localCsvDelay, CsvDelay: tc.localCsvDelay,
MultiSigKey: tc.localFundingPubKey, MultiSigKey: keychain.KeyDescriptor{
PaymentBasePoint: tc.localPaymentBasePoint, PubKey: tc.localFundingPubKey,
HtlcBasePoint: tc.localPaymentBasePoint, },
DelayBasePoint: localDelayBasePoint, PaymentBasePoint: keychain.KeyDescriptor{
PubKey: tc.localPaymentBasePoint,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: tc.localPaymentBasePoint,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: localDelayBasePoint,
},
}, },
RemoteChanCfg: channeldb.ChannelConfig{ RemoteChanCfg: channeldb.ChannelConfig{
MultiSigKey: tc.remoteFundingPubKey, MultiSigKey: keychain.KeyDescriptor{
PaymentBasePoint: tc.remotePaymentBasePoint, PubKey: tc.remoteFundingPubKey,
HtlcBasePoint: tc.remotePaymentBasePoint, },
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: tc.remotePaymentBasePoint,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: tc.remotePaymentBasePoint,
},
}, },
Capacity: tc.fundingAmount, Capacity: tc.fundingAmount,
RevocationProducer: shachain.NewRevocationProducer(zeroHash), RevocationProducer: shachain.NewRevocationProducer(zeroHash),

View File

@ -10,6 +10,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/blockchain"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
@ -27,24 +28,6 @@ const (
// The size of the buffered queue of requests to the wallet from the // The size of the buffered queue of requests to the wallet from the
// outside word. // outside word.
msgBufferSize = 100 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 // 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. // specific interaction is proxied to the internal wallet.
WalletController 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 // This mutex is to be held when generating external keys to be used as
// multi-sig, and commitment keys within the channel. // multi-sig, and commitment keys within the channel.
keyGenMtx sync.RWMutex keyGenMtx sync.RWMutex
@ -297,6 +285,7 @@ func NewLightningWallet(Cfg Config) (*LightningWallet, error) {
return &LightningWallet{ return &LightningWallet{
Cfg: Cfg, Cfg: Cfg,
SecretKeyRing: Cfg.SecretKeyRing,
WalletController: Cfg.WalletController, WalletController: Cfg.WalletController,
msgChan: make(chan interface{}, msgBufferSize), msgChan: make(chan interface{}, msgBufferSize),
nextFundingID: 0, nextFundingID: 0,
@ -319,20 +308,6 @@ func (l *LightningWallet) Startup() error {
return err 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) l.wg.Add(1)
// TODO(roasbeef): multiple request handlers? // TODO(roasbeef): multiple request handlers?
go l.requestHandler() go l.requestHandler()
@ -390,17 +365,6 @@ func (l *LightningWallet) ActiveReservations() []*ChannelReservation {
return reservations 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 // requestHandler is the primary goroutine(s) responsible for handling, and
// dispatching relies to all messages. // dispatching relies to all messages.
func (l *LightningWallet) requestHandler() { 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, the base revocation key, the base htlc key,the base payment
// key, and the delayed payment key. // key, and the delayed payment key.
// //
// TODO(roasbeef): special derivaiton? // TODO(roasbeef): "salt" each key as well?
reservation.ourContribution.MultiSigKey, err = l.NewRawKey() reservation.ourContribution.MultiSigKey, err = l.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.ourContribution.RevocationBasePoint, err = l.NewRawKey() reservation.ourContribution.RevocationBasePoint, err = l.DeriveNextKey(
keychain.KeyFamilyRevocationBase,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.ourContribution.HtlcBasePoint, err = l.NewRawKey() reservation.ourContribution.HtlcBasePoint, err = l.DeriveNextKey(
keychain.KeyFamilyHtlcBase,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
// TODO(roasbeef); allow for querying to extract key distinct from HD reservation.ourContribution.PaymentBasePoint, err = l.DeriveNextKey(
// chain keychain.KeyFamilyPaymentBase,
// * allows for offline commitment keys )
reservation.ourContribution.PaymentBasePoint, err = l.NewRawKey()
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.ourContribution.DelayBasePoint, err = l.NewRawKey() reservation.ourContribution.DelayBasePoint, err = l.DeriveNextKey(
keychain.KeyFamilyDelayBase,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil 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 // With the above keys created, we'll also need to initialization our
// initial revocation tree state. In order to do so in a deterministic // initial revocation tree state.
// manner (for recovery purposes), we'll use the current block hash nextRevocationKeyDesc, err := l.DeriveNextKey(
// along with the identity public key of the node we're creating the keychain.KeyFamilyRevocationRoot,
// 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()
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
bestHash, _, err := l.Cfg.ChainIO.GetBestBlock() revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
revocationRoot := DeriveRevocationRoot(masterElkremRoot, *bestHash,
req.nodeID)
// Once we have the root, we can then generate our shachain producer // Once we have the root, we can then generate our shachain producer
// and from that generate the per-commitment point. // 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) firstPreimage, err := producer.AtIndex(0)
if err != nil { if err != nil {
req.err <- err 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 // Finally, add the 2-of-2 multi-sig output which will set up the lightning
// channel. // channel.
channelCapacity := int64(pendingReservation.partialState.Capacity) channelCapacity := int64(pendingReservation.partialState.Capacity)
witnessScript, multiSigOut, err := GenFundingPkScript(ourKey.SerializeCompressed(), witnessScript, multiSigOut, err := GenFundingPkScript(
theirKey.SerializeCompressed(), channelCapacity) ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(), channelCapacity,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
return return
@ -850,22 +824,22 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
var stateObfuscator [StateHintSize]byte var stateObfuscator [StateHintSize]byte
if chanState.ChanType == channeldb.SingleFunder { if chanState.ChanType == channeldb.SingleFunder {
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
ourContribution.PaymentBasePoint, ourContribution.PaymentBasePoint.PubKey,
theirContribution.PaymentBasePoint, theirContribution.PaymentBasePoint.PubKey,
) )
} else { } else {
ourSer := ourContribution.PaymentBasePoint.SerializeCompressed() ourSer := ourContribution.PaymentBasePoint.PubKey.SerializeCompressed()
theirSer := theirContribution.PaymentBasePoint.SerializeCompressed() theirSer := theirContribution.PaymentBasePoint.PubKey.SerializeCompressed()
switch bytes.Compare(ourSer, theirSer) { switch bytes.Compare(ourSer, theirSer) {
case -1: case -1:
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
ourContribution.PaymentBasePoint, ourContribution.PaymentBasePoint.PubKey,
theirContribution.PaymentBasePoint, theirContribution.PaymentBasePoint.PubKey,
) )
default: default:
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
theirContribution.PaymentBasePoint, theirContribution.PaymentBasePoint.PubKey,
ourContribution.PaymentBasePoint, ourContribution.PaymentBasePoint.PubKey,
) )
} }
} }
@ -895,7 +869,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// transaction. // transaction.
signDesc = SignDescriptor{ signDesc = SignDescriptor{
WitnessScript: witnessScript, WitnessScript: witnessScript,
PubKey: ourKey, KeyDesc: ourKey,
Output: multiSigOut, Output: multiSigOut,
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(theirCommitTx), 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 // Re-generate both the witnessScript and p2sh output. We sign the
// witnessScript script, but include the p2sh output as the subscript // witnessScript script, but include the p2sh output as the subscript
// for verification. // for verification.
witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), witnessScript, _, err := GenFundingPkScript(
theirKey.SerializeCompressed(), int64(res.partialState.Capacity)) ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(),
int64(res.partialState.Capacity),
)
if err != nil { if err != nil {
msg.err <- err msg.err <- err
msg.completeChan <- nil msg.completeChan <- nil
@ -1066,7 +1043,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
msg.err <- err msg.err <- err
msg.completeChan <- nil msg.completeChan <- nil
return 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.err <- fmt.Errorf("counterparty's commitment signature is invalid")
msg.completeChan <- nil msg.completeChan <- nil
return return
@ -1168,8 +1145,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
// generator state obfuscator to encode the current state number within // generator state obfuscator to encode the current state number within
// both commitment transactions. // both commitment transactions.
stateObfuscator := DeriveStateHintObfuscator( stateObfuscator := DeriveStateHintObfuscator(
pendingReservation.theirContribution.PaymentBasePoint, pendingReservation.theirContribution.PaymentBasePoint.PubKey,
pendingReservation.ourContribution.PaymentBasePoint) pendingReservation.ourContribution.PaymentBasePoint.PubKey,
)
err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator) err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator)
if err != nil { if err != nil {
req.err <- err req.err <- err
@ -1194,8 +1172,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
hashCache := txscript.NewTxSigHashes(ourCommitTx) hashCache := txscript.NewTxSigHashes(ourCommitTx)
theirKey := pendingReservation.theirContribution.MultiSigKey theirKey := pendingReservation.theirContribution.MultiSigKey
ourKey := pendingReservation.ourContribution.MultiSigKey ourKey := pendingReservation.ourContribution.MultiSigKey
witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), witnessScript, _, err := GenFundingPkScript(
theirKey.SerializeCompressed(), channelValue) ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(), channelValue,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.completeChan <- nil req.completeChan <- nil
@ -1217,8 +1197,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
req.err <- err req.err <- err
req.completeChan <- nil req.completeChan <- nil
return return
} else if !sig.Verify(sigHash, theirKey) { } else if !sig.Verify(sigHash, theirKey.PubKey) {
req.err <- fmt.Errorf("counterparty's commitment signature is invalid") req.err <- fmt.Errorf("counterparty's commitment signature " +
"is invalid")
req.completeChan <- nil req.completeChan <- nil
return return
} }
@ -1235,7 +1216,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
} }
signDesc := SignDescriptor{ signDesc := SignDescriptor{
WitnessScript: witnessScript, WitnessScript: witnessScript,
PubKey: ourKey, KeyDesc: ourKey,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: p2wsh, PkScript: p2wsh,
Value: channelValue, Value: channelValue,
@ -1349,18 +1330,6 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte,
return nil 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 // 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 // state hints from the root to be used for a new channel. The obfuscator is
// generated via the following computation: // generated via the following computation:
@ -1437,8 +1406,6 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
weightEstimate.AddP2WKHInput() weightEstimate.AddP2WKHInput()
case NestedWitnessPubKey: case NestedWitnessPubKey:
weightEstimate.AddNestedP2WKHInput() weightEstimate.AddNestedP2WKHInput()
case PubKeyHash:
weightEstimate.AddP2PKHInput()
default: default:
return nil, 0, fmt.Errorf("Unsupported address type: %v", return nil, 0, fmt.Errorf("Unsupported address type: %v",
utxo.AddressType) utxo.AddressType)
@ -1449,7 +1416,9 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
weightEstimate.AddP2WSHOutput() weightEstimate.AddP2WSHOutput()
// Assume that change output is a P2WKH output. // 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() weightEstimate.AddP2WKHOutput()
// The difference between the selected amount and the amount // The difference between the selected amount and the amount

41
mock.go
View File

@ -6,6 +6,7 @@ import (
"sync" "sync"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg"
@ -28,7 +29,7 @@ func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx,
witnessScript := signDesc.WitnessScript witnessScript := signDesc.WitnessScript
privKey := m.key privKey := m.key
if !privKey.PubKey().IsEqual(signDesc.PubKey) { if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) {
return nil, fmt.Errorf("incorrect key passed") return nil, fmt.Errorf("incorrect key passed")
} }
@ -202,8 +203,7 @@ func (*mockWalletController) FetchInputInfo(
} }
return txOut, nil return txOut, nil
} }
func (*mockWalletController) ConfirmedBalance(confs int32, func (*mockWalletController) ConfirmedBalance(confs int32) (btcutil.Amount, error) {
witness bool) (btcutil.Amount, error) {
return 0, nil return 0, nil
} }
@ -218,16 +218,6 @@ func (*mockWalletController) GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, e
return nil, nil 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, func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
_ lnwallet.SatPerVByte) (*chainhash.Hash, error) { _ lnwallet.SatPerVByte) (*chainhash.Hash, error) {
@ -272,6 +262,31 @@ func (*mockWalletController) Stop() error {
return nil 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 { type mockPreimageCache struct {
sync.Mutex sync.Mutex
preimageMap map[[32]byte][]byte preimageMap map[[32]byte][]byte

View File

@ -146,7 +146,7 @@ func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error)
Heuristic: prefAttachment, Heuristic: prefAttachment,
ChanController: &chanController{svr}, ChanController: &chanController{svr},
WalletBalance: func() (btcutil.Amount, error) { WalletBalance: func() (btcutil.Amount, error) {
return svr.cc.wallet.ConfirmedBalance(1, true) return svr.cc.wallet.ConfirmedBalance(1)
}, },
Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()), Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()),
} }

View File

@ -463,8 +463,6 @@ func (r *rpcServer) NewAddress(ctx context.Context,
addrType = lnwallet.WitnessPubKey addrType = lnwallet.WitnessPubKey
case lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH: case lnrpc.NewAddressRequest_NESTED_PUBKEY_HASH:
addrType = lnwallet.NestedWitnessPubKey addrType = lnwallet.NestedWitnessPubKey
case lnrpc.NewAddressRequest_PUBKEY_HASH:
addrType = lnwallet.PubKeyHash
} }
addr, err := r.server.cc.wallet.NewAddress(addrType, false) 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) { in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) {
// Get total balance, from txs that have >= 0 confirmations. // 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 { if err != nil {
return nil, err return nil, err
} }
// Get confirmed balance, from txs that have >= 1 confirmations. // 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
@ -79,12 +80,22 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MinHTLC: lnwire.MilliSatoshi(rand.Int63()),
MaxAcceptedHtlcs: uint16(rand.Int31()), MaxAcceptedHtlcs: uint16(rand.Int31()),
}, },
CsvDelay: uint16(csvTimeoutAlice), CsvDelay: uint16(csvTimeoutAlice),
MultiSigKey: aliceKeyPub, MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: aliceKeyPub, PubKey: aliceKeyPub,
PaymentBasePoint: aliceKeyPub, },
DelayBasePoint: aliceKeyPub, RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: aliceKeyPub, PubKey: aliceKeyPub,
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
} }
bobCfg := channeldb.ChannelConfig{ bobCfg := channeldb.ChannelConfig{
ChannelConstraints: channeldb.ChannelConstraints{ ChannelConstraints: channeldb.ChannelConstraints{
@ -94,24 +105,40 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
MinHTLC: lnwire.MilliSatoshi(rand.Int63()), MinHTLC: lnwire.MilliSatoshi(rand.Int63()),
MaxAcceptedHtlcs: uint16(rand.Int31()), MaxAcceptedHtlcs: uint16(rand.Int31()),
}, },
CsvDelay: uint16(csvTimeoutBob), CsvDelay: uint16(csvTimeoutBob),
MultiSigKey: bobKeyPub, MultiSigKey: keychain.KeyDescriptor{
RevocationBasePoint: bobKeyPub, PubKey: bobKeyPub,
PaymentBasePoint: bobKeyPub, },
DelayBasePoint: bobKeyPub, RevocationBasePoint: keychain.KeyDescriptor{
HtlcBasePoint: bobKeyPub, PubKey: bobKeyPub,
},
PaymentBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
DelayBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
HtlcBasePoint: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
} }
bobRoot := lnwallet.DeriveRevocationRoot(bobKeyPriv, testHdSeed, aliceKeyPub) bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) if err != nil {
return nil, nil, nil, nil, err
}
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:]) bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:])
aliceRoot := lnwallet.DeriveRevocationRoot(aliceKeyPriv, testHdSeed, bobKeyPub) aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) if err != nil {
return nil, nil, nil, nil, err
}
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err

View File

@ -319,7 +319,7 @@ func init() {
if err != nil { if err != nil {
panic(fmt.Sprintf("unable to parse pub key during init: %v", err)) 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 { for i := range kidOutputs {