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:
41 files changed
+1936
-995
No files matched your search
+73
-145
@@ -9,6 +9,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg"
|
||||
@@ -27,9 +28,25 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
lnNamespace = []byte("ln")
|
||||
rootKey = []byte("ln-root")
|
||||
// waddrmgrNamespaceKey is the namespace key that the waddrmgr state is
|
||||
// stored within the top-level waleltdb buckets of btcwallet.
|
||||
waddrmgrNamespaceKey = []byte("waddrmgr")
|
||||
|
||||
// lightningKeyScope is the key scope that will be used within the
|
||||
// waddrmgr to create an HD chain for deriving all of our required
|
||||
// keys. We'll ensure this this scope is created upon start.
|
||||
lightningKeyScope = waddrmgr.KeyScope{
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
Coin: 0,
|
||||
}
|
||||
|
||||
// lightningAddrSchema is the scope addr schema for all keys that we
|
||||
// derive. We'll treat them all as p2wkh addresses, as atm we must
|
||||
// specify a particular type.
|
||||
lightningAddrSchema = waddrmgr.ScopeAddrSchema{
|
||||
ExternalAddrType: waddrmgr.WitnessPubKey,
|
||||
InternalAddrType: waddrmgr.WitnessPubKey,
|
||||
}
|
||||
)
|
||||
|
||||
// BtcWallet is an implementation of the lnwallet.WalletController interface
|
||||
@@ -83,49 +100,23 @@ func New(cfg Config) (*BtcWallet, error) {
|
||||
wallet, err = loader.CreateNewWallet(
|
||||
pubPass, cfg.PrivatePass, cfg.HdSeed,
|
||||
)
|
||||
|
||||
switch {
|
||||
// If the wallet already exists, then we'll ignore this error
|
||||
// and proceed directly to opening the wallet.
|
||||
case err == base.ErrExists:
|
||||
|
||||
// Otherwise, there's a greater error here, and we'll return
|
||||
// early.
|
||||
case err != nil:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := loader.UnloadWallet(); err != nil {
|
||||
} else {
|
||||
// Wallet has been created and been initialized at this point,
|
||||
// open it along with all the required DB namespaces, and the
|
||||
// DB itself.
|
||||
wallet, err = loader.OpenExistingWallet(pubPass, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Wallet has been created and been initialized at this point, open it
|
||||
// along with all the required DB namepsaces, and the DB itself.
|
||||
wallet, err = loader.OpenExistingWallet(pubPass, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a bucket within the wallet's database dedicated to storing
|
||||
// our LN specific data.
|
||||
db := wallet.Database()
|
||||
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
||||
_, err := tx.CreateTopLevelBucket(lnNamespace)
|
||||
if err != nil && err != walletdb.ErrBucketExists {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BtcWallet{
|
||||
cfg: &cfg,
|
||||
wallet: wallet,
|
||||
db: db,
|
||||
db: wallet.Database(),
|
||||
chain: cfg.ChainSource,
|
||||
netParams: cfg.NetParams,
|
||||
utxoCache: make(map[wire.OutPoint]*wire.TxOut),
|
||||
@@ -143,6 +134,12 @@ func (b *BtcWallet) BackEnd() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// InternalWallet returns a pointer to the internal base wallet which is the
|
||||
// core of btcwallet.
|
||||
func (b *BtcWallet) InternalWallet() *base.Wallet {
|
||||
return b.wallet
|
||||
}
|
||||
|
||||
// Start initializes the underlying rpc connection, the wallet itself, and
|
||||
// begins syncing to the current available blockchain state.
|
||||
//
|
||||
@@ -165,6 +162,27 @@ func (b *BtcWallet) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// We'll now ensure that the KeyScope: (1017, 1) exists within the
|
||||
// internal waddrmgr. We'll need this in order to properly generate the
|
||||
// keys required for signing various contracts.
|
||||
_, err := b.wallet.Manager.FetchScopedKeyManager(lightningKeyScope)
|
||||
if err != nil {
|
||||
// If the scope hasn't yet been created (it wouldn't been
|
||||
// loaded by default if it was), then we'll manually create the
|
||||
// scope for the first time ourselves.
|
||||
err := walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
|
||||
_, err := b.wallet.Manager.NewScopedKeyManager(
|
||||
addrmgrNs, lightningKeyScope, lightningAddrSchema,
|
||||
)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -188,25 +206,16 @@ func (b *BtcWallet) Stop() error {
|
||||
// final sum.
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, error) {
|
||||
func (b *BtcWallet) ConfirmedBalance(confs int32) (btcutil.Amount, error) {
|
||||
var balance btcutil.Amount
|
||||
|
||||
if witness {
|
||||
witnessOutputs, err := b.ListUnspentWitness(confs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
witnessOutputs, err := b.ListUnspentWitness(confs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, witnessOutput := range witnessOutputs {
|
||||
balance += witnessOutput.Value
|
||||
}
|
||||
} else {
|
||||
outputSum, err := b.wallet.CalculateBalance(confs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
balance = outputSum
|
||||
for _, witnessOutput := range witnessOutputs {
|
||||
balance += witnessOutput.Value
|
||||
}
|
||||
|
||||
return balance, nil
|
||||
@@ -219,24 +228,22 @@ func (b *BtcWallet) ConfirmedBalance(confs int32, witness bool) (btcutil.Amount,
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) NewAddress(t lnwallet.AddressType, change bool) (btcutil.Address, error) {
|
||||
var addrType waddrmgr.AddressType
|
||||
var keyScope waddrmgr.KeyScope
|
||||
|
||||
switch t {
|
||||
case lnwallet.WitnessPubKey:
|
||||
addrType = waddrmgr.WitnessPubKey
|
||||
keyScope = waddrmgr.KeyScopeBIP0084
|
||||
case lnwallet.NestedWitnessPubKey:
|
||||
addrType = waddrmgr.NestedWitnessPubKey
|
||||
case lnwallet.PubKeyHash:
|
||||
addrType = waddrmgr.PubKeyHash
|
||||
keyScope = waddrmgr.KeyScopeBIP0049Plus
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown address type")
|
||||
}
|
||||
|
||||
if change {
|
||||
return b.wallet.NewChangeAddress(defaultAccount, addrType)
|
||||
return b.wallet.NewChangeAddress(defaultAccount, keyScope)
|
||||
}
|
||||
|
||||
return b.wallet.NewAddress(defaultAccount, addrType)
|
||||
return b.wallet.NewAddress(defaultAccount, keyScope)
|
||||
}
|
||||
|
||||
// GetPrivKey retrieves the underlying private key associated with the passed
|
||||
@@ -250,78 +257,6 @@ func (b *BtcWallet) GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error) {
|
||||
return b.wallet.PrivKeyForAddress(a)
|
||||
}
|
||||
|
||||
// NewRawKey retrieves the next key within our HD key-chain for use within as a
|
||||
// multi-sig key within the funding transaction, or within the commitment
|
||||
// transaction's outputs.
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) NewRawKey() (*btcec.PublicKey, error) {
|
||||
addr, err := b.wallet.NewAddress(defaultAccount,
|
||||
waddrmgr.WitnessPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.wallet.PubKeyForAddress(addr)
|
||||
}
|
||||
|
||||
// FetchRootKey returns a root key which is intended to be used as an initial
|
||||
// seed/salt to generate any Lightning specific secrets.
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) FetchRootKey() (*btcec.PrivateKey, error) {
|
||||
// Fetch the root address hash from the database, this is persisted
|
||||
// locally within the database, then used to obtain the key from the
|
||||
// wallet based on the address hash.
|
||||
var rootAddrHash []byte
|
||||
if err := walletdb.View(b.db, func(tx walletdb.ReadTx) error {
|
||||
lnBucket := tx.ReadBucket(lnNamespace)
|
||||
|
||||
rootAddrHash = lnBucket.Get(rootKey)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rootAddrHash == nil {
|
||||
// Otherwise, we need to generate a fresh address from the
|
||||
// wallet, then stores its hash160 within the database so we
|
||||
// can look up the exact key later.
|
||||
if err := walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
addrs, err := b.wallet.Manager.NextExternalAddresses(addrmgrNs,
|
||||
defaultAccount, 1, waddrmgr.WitnessPubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootAddr := addrs[0].Address()
|
||||
|
||||
lnBucket := tx.ReadWriteBucket(lnNamespace)
|
||||
|
||||
rootAddrHash = rootAddr.ScriptAddress()
|
||||
return lnBucket.Put(rootKey, rootAddrHash)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// With the root address hash obtained, generate the corresponding
|
||||
// address, then retrieve the managed address from the wallet which
|
||||
// will allow us to obtain the private key.
|
||||
rootAddr, err := btcutil.NewAddressWitnessPubKeyHash(rootAddrHash,
|
||||
b.netParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv, err := b.wallet.PrivKeyForAddress(rootAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
// SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to
|
||||
// the specified outputs. In the case the wallet has insufficient funds, or the
|
||||
// outputs are non-standard, a non-nil error will be be returned.
|
||||
@@ -765,23 +700,16 @@ func (b *BtcWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, e
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) IsSynced() (bool, int64, error) {
|
||||
// Grab the best chain state the wallet is currently aware of. We'll
|
||||
// also grab the timestamp of the best block to return for determining
|
||||
// sync progress.
|
||||
// Grab the best chain state the wallet is currently aware of.
|
||||
syncState := b.wallet.Manager.SyncedTo()
|
||||
walletBestHeader, err := b.chain.GetBlockHeader(&syncState.Hash)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
var (
|
||||
bestHash *chainhash.Hash
|
||||
bestHeight int32
|
||||
)
|
||||
// We'll also extract the current best wallet timestamp so the caller
|
||||
// can get an idea of where we are in the sync timeline.
|
||||
bestTimestamp := syncState.Timestamp.Unix()
|
||||
|
||||
// Next, query the chain backend to grab the info about the tip of the
|
||||
// main chain.
|
||||
bestHash, bestHeight, err = b.cfg.ChainSource.GetBestBlock()
|
||||
bestHash, bestHeight, err := b.cfg.ChainSource.GetBestBlock()
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
@@ -789,7 +717,7 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) {
|
||||
// If the wallet hasn't yet fully synced to the node's best chain tip,
|
||||
// then we're not yet fully synced.
|
||||
if syncState.Height < bestHeight {
|
||||
return false, walletBestHeader.Timestamp.Unix(), nil
|
||||
return false, bestTimestamp, nil
|
||||
}
|
||||
|
||||
// If the wallet is on par with the current best chain tip, then we
|
||||
@@ -804,5 +732,5 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) {
|
||||
// If the timestamp no the best header is more than 2 hours in the
|
||||
// past, then we're not yet synced.
|
||||
minus24Hours := time.Now().Add(-2 * time.Hour)
|
||||
return !blockHeader.Timestamp.Before(minus24Hours), walletBestHeader.Timestamp.Unix(), nil
|
||||
return !blockHeader.Timestamp.Before(minus24Hours), bestTimestamp, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package btcwallet
|
||||
|
||||
import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/roasbeef/btcutil"
|
||||
"github.com/roasbeef/btcwallet/waddrmgr"
|
||||
base "github.com/roasbeef/btcwallet/wallet"
|
||||
"github.com/roasbeef/btcwallet/walletdb"
|
||||
)
|
||||
|
||||
// FetchInputInfo queries for the WalletController's knowledge of the passed
|
||||
@@ -74,11 +76,44 @@ func (b *BtcWallet) fetchOutputAddr(script []byte) (waddrmgr.ManagedAddress, err
|
||||
}
|
||||
|
||||
// fetchPrivKey attempts to retrieve the raw private key corresponding to the
|
||||
// passed public key.
|
||||
// TODO(roasbeef): alternatively can extract all the data pushes within the
|
||||
// script, then attempt to match keys one by one
|
||||
func (b *BtcWallet) fetchPrivKey(pub *btcec.PublicKey) (*btcec.PrivateKey, error) {
|
||||
hash160 := btcutil.Hash160(pub.SerializeCompressed())
|
||||
// passed public key if populated, or the key descriptor path (if non-empty).
|
||||
func (b *BtcWallet) fetchPrivKey(keyDesc *keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
|
||||
// If the key locator within the descriptor *isn't* empty, then we can
|
||||
// directly derive the keys raw.
|
||||
if !keyDesc.KeyLocator.IsEmpty() {
|
||||
// We'll assume the special lightning key scope in this case.
|
||||
scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager(
|
||||
lightningKeyScope,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var key *btcec.PrivateKey
|
||||
err = walletdb.View(b.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
|
||||
path := waddrmgr.DerivationPath{
|
||||
Account: uint32(keyDesc.Family),
|
||||
Branch: 0,
|
||||
Index: uint32(keyDesc.Index),
|
||||
}
|
||||
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
hash160 := btcutil.Hash160(keyDesc.PubKey.SerializeCompressed())
|
||||
addr, err := btcutil.NewAddressWitnessPubKeyHash(hash160, b.netParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -117,11 +152,12 @@ func maybeTweakPrivKey(signDesc *lnwallet.SignDescriptor,
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx,
|
||||
signDesc *lnwallet.SignDescriptor) ([]byte, error) {
|
||||
|
||||
witnessScript := signDesc.WitnessScript
|
||||
|
||||
// First attempt to fetch the private key which corresponds to the
|
||||
// specified public key.
|
||||
privKey, err := b.fetchPrivKey(signDesc.PubKey)
|
||||
privKey, err := b.fetchPrivKey(&signDesc.KeyDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -176,7 +212,7 @@ func (b *BtcWallet) ComputeInputScript(tx *wire.MsgTx,
|
||||
|
||||
// If we're spending p2wkh output nested within a p2sh output, then
|
||||
// we'll need to attach a sigScript in addition to witness data.
|
||||
case pka.IsNestedWitness():
|
||||
case pka.AddrType() == waddrmgr.NestedWitnessPubKey:
|
||||
pubKey := privKey.PubKey()
|
||||
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
|
||||
|
||||
@@ -249,7 +285,9 @@ func (b *BtcWallet) SignMessage(pubKey *btcec.PublicKey,
|
||||
|
||||
// First attempt to fetch the private key which corresponds to the
|
||||
// specified public key.
|
||||
privKey, err := b.fetchPrivKey(pubKey)
|
||||
privKey, err := b.fetchPrivKey(&keychain.KeyDescriptor{
|
||||
PubKey: pubKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+53
-48
@@ -835,15 +835,18 @@ func deriveCommitmentKeys(commitPoint *btcec.PublicKey, isOurCommit bool,
|
||||
keyRing := &CommitmentKeyRing{
|
||||
CommitPoint: commitPoint,
|
||||
|
||||
LocalCommitKeyTweak: SingleTweakBytes(commitPoint,
|
||||
localChanCfg.PaymentBasePoint),
|
||||
LocalHtlcKeyTweak: SingleTweakBytes(commitPoint,
|
||||
localChanCfg.HtlcBasePoint),
|
||||
|
||||
LocalHtlcKey: TweakPubKey(localChanCfg.HtlcBasePoint,
|
||||
commitPoint),
|
||||
RemoteHtlcKey: TweakPubKey(remoteChanCfg.HtlcBasePoint,
|
||||
commitPoint),
|
||||
LocalCommitKeyTweak: SingleTweakBytes(
|
||||
commitPoint, localChanCfg.PaymentBasePoint.PubKey,
|
||||
),
|
||||
LocalHtlcKeyTweak: SingleTweakBytes(
|
||||
commitPoint, localChanCfg.HtlcBasePoint.PubKey,
|
||||
),
|
||||
LocalHtlcKey: TweakPubKey(
|
||||
localChanCfg.HtlcBasePoint.PubKey, commitPoint,
|
||||
),
|
||||
RemoteHtlcKey: TweakPubKey(
|
||||
remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
|
||||
),
|
||||
}
|
||||
|
||||
// We'll now compute the delay, no delay, and revocation key based on
|
||||
@@ -857,13 +860,13 @@ func deriveCommitmentKeys(commitPoint *btcec.PublicKey, isOurCommit bool,
|
||||
revocationBasePoint *btcec.PublicKey
|
||||
)
|
||||
if isOurCommit {
|
||||
delayBasePoint = localChanCfg.DelayBasePoint
|
||||
noDelayBasePoint = remoteChanCfg.PaymentBasePoint
|
||||
revocationBasePoint = remoteChanCfg.RevocationBasePoint
|
||||
delayBasePoint = localChanCfg.DelayBasePoint.PubKey
|
||||
noDelayBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
|
||||
revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
|
||||
} else {
|
||||
delayBasePoint = remoteChanCfg.DelayBasePoint
|
||||
noDelayBasePoint = localChanCfg.PaymentBasePoint
|
||||
revocationBasePoint = localChanCfg.RevocationBasePoint
|
||||
delayBasePoint = remoteChanCfg.DelayBasePoint.PubKey
|
||||
noDelayBasePoint = localChanCfg.PaymentBasePoint.PubKey
|
||||
revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
|
||||
}
|
||||
|
||||
// With the base points assigned, we can now derive the actual keys
|
||||
@@ -1241,8 +1244,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
|
||||
remoteUpdateLog: remoteUpdateLog,
|
||||
ChanPoint: &state.FundingOutpoint,
|
||||
Capacity: state.Capacity,
|
||||
LocalFundingKey: state.LocalChanCfg.MultiSigKey,
|
||||
RemoteFundingKey: state.RemoteChanCfg.MultiSigKey,
|
||||
LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey,
|
||||
RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey,
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
|
||||
@@ -1277,8 +1280,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
|
||||
// createSignDesc derives the SignDescriptor for commitment transactions from
|
||||
// other fields on the LightningChannel.
|
||||
func (lc *LightningChannel) createSignDesc() error {
|
||||
localKey := lc.localChanCfg.MultiSigKey.SerializeCompressed()
|
||||
remoteKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed()
|
||||
localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
|
||||
remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
|
||||
|
||||
multiSigScript, err := genMultiSigScript(localKey, remoteKey)
|
||||
if err != nil {
|
||||
@@ -1290,7 +1293,7 @@ func (lc *LightningChannel) createSignDesc() error {
|
||||
return err
|
||||
}
|
||||
lc.signDesc = &SignDescriptor{
|
||||
PubKey: lc.localChanCfg.MultiSigKey,
|
||||
KeyDesc: lc.localChanCfg.MultiSigKey,
|
||||
WitnessScript: multiSigScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: fundingPkScript,
|
||||
@@ -1310,13 +1313,13 @@ func (lc *LightningChannel) createStateHintObfuscator() {
|
||||
state := lc.channelState
|
||||
if state.IsInitiator {
|
||||
lc.stateHintObfuscator = DeriveStateHintObfuscator(
|
||||
state.LocalChanCfg.PaymentBasePoint,
|
||||
state.RemoteChanCfg.PaymentBasePoint,
|
||||
state.LocalChanCfg.PaymentBasePoint.PubKey,
|
||||
state.RemoteChanCfg.PaymentBasePoint.PubKey,
|
||||
)
|
||||
} else {
|
||||
lc.stateHintObfuscator = DeriveStateHintObfuscator(
|
||||
state.RemoteChanCfg.PaymentBasePoint,
|
||||
state.LocalChanCfg.PaymentBasePoint,
|
||||
state.RemoteChanCfg.PaymentBasePoint.PubKey,
|
||||
state.LocalChanCfg.PaymentBasePoint.PubKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1820,7 +1823,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
||||
if localAmt >= chanState.RemoteChanCfg.DustLimit {
|
||||
localSignDesc = &SignDescriptor{
|
||||
SingleTweak: keyRing.LocalCommitKeyTweak,
|
||||
PubKey: chanState.LocalChanCfg.PaymentBasePoint,
|
||||
KeyDesc: chanState.LocalChanCfg.PaymentBasePoint,
|
||||
WitnessScript: localPkScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: localPkScript,
|
||||
@@ -1834,7 +1837,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
||||
// limit, assemble the remote sign descriptor.
|
||||
if remoteAmt >= chanState.RemoteChanCfg.DustLimit {
|
||||
remoteSignDesc = &SignDescriptor{
|
||||
PubKey: chanState.LocalChanCfg.RevocationBasePoint,
|
||||
KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
|
||||
DoubleTweak: commitmentSecret,
|
||||
WitnessScript: remotePkScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -1894,7 +1897,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
||||
|
||||
htlcRetributions[i] = HtlcRetribution{
|
||||
SignDesc: SignDescriptor{
|
||||
PubKey: chanState.LocalChanCfg.RevocationBasePoint,
|
||||
KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
|
||||
DoubleTweak: commitmentSecret,
|
||||
WitnessScript: htlcScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -2462,7 +2465,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
|
||||
// signature to give to the remote party for this commitment
|
||||
// transaction. Note we use the raw HTLC amount.
|
||||
sigJob.signDesc = SignDescriptor{
|
||||
PubKey: localChanCfg.HtlcBasePoint,
|
||||
KeyDesc: localChanCfg.HtlcBasePoint,
|
||||
SingleTweak: keyRing.LocalHtlcKeyTweak,
|
||||
WitnessScript: htlc.theirWitnessScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -2512,7 +2515,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
|
||||
// signature to give to the remote party for this commitment
|
||||
// transaction. Note we use the raw HTLC amount.
|
||||
sigJob.signDesc = SignDescriptor{
|
||||
PubKey: localChanCfg.HtlcBasePoint,
|
||||
KeyDesc: localChanCfg.HtlcBasePoint,
|
||||
SingleTweak: keyRing.LocalHtlcKeyTweak,
|
||||
WitnessScript: htlc.theirWitnessScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -3532,8 +3535,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
|
||||
// we'll ensure that the newly constructed commitment state has a valid
|
||||
// signature.
|
||||
verifyKey := btcec.PublicKey{
|
||||
X: lc.remoteChanCfg.MultiSigKey.X,
|
||||
Y: lc.remoteChanCfg.MultiSigKey.Y,
|
||||
X: lc.remoteChanCfg.MultiSigKey.PubKey.X,
|
||||
Y: lc.remoteChanCfg.MultiSigKey.PubKey.Y,
|
||||
Curve: btcec.S256(),
|
||||
}
|
||||
cSig, err := commitSig.ToSignature()
|
||||
@@ -4145,8 +4148,8 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
||||
|
||||
// With the final signature generated, create the witness stack
|
||||
// required to spend from the multi-sig output.
|
||||
ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed()
|
||||
theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed()
|
||||
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
|
||||
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
|
||||
|
||||
commitTx.TxIn[0].Witness = SpendMultiSig(
|
||||
lc.signDesc.WitnessScript, ourKey,
|
||||
@@ -4267,7 +4270,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer,
|
||||
commitResolution = &CommitOutputResolution{
|
||||
SelfOutPoint: *selfPoint,
|
||||
SelfOutputSignDesc: SignDescriptor{
|
||||
PubKey: localPayBase,
|
||||
KeyDesc: localPayBase,
|
||||
SingleTweak: keyRing.LocalCommitKeyTweak,
|
||||
WitnessScript: selfP2WKH,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4435,7 +4438,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
Expiry: htlc.RefundTimeout,
|
||||
ClaimOutpoint: op,
|
||||
SweepSignDesc: SignDescriptor{
|
||||
PubKey: localChanCfg.HtlcBasePoint,
|
||||
KeyDesc: localChanCfg.HtlcBasePoint,
|
||||
SingleTweak: keyRing.LocalHtlcKeyTweak,
|
||||
WitnessScript: htlcReceiverScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4475,7 +4478,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
return nil, err
|
||||
}
|
||||
timeoutSignDesc := SignDescriptor{
|
||||
PubKey: localChanCfg.HtlcBasePoint,
|
||||
KeyDesc: localChanCfg.HtlcBasePoint,
|
||||
SingleTweak: keyRing.LocalHtlcKeyTweak,
|
||||
WitnessScript: htlcCreationScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4509,8 +4512,9 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localDelayTweak := SingleTweakBytes(keyRing.CommitPoint,
|
||||
localChanCfg.DelayBasePoint)
|
||||
localDelayTweak := SingleTweakBytes(
|
||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
||||
)
|
||||
return &OutgoingHtlcResolution{
|
||||
Expiry: htlc.RefundTimeout,
|
||||
SignedTimeoutTx: timeoutTx,
|
||||
@@ -4520,7 +4524,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
Index: 0,
|
||||
},
|
||||
SweepSignDesc: SignDescriptor{
|
||||
PubKey: localChanCfg.DelayBasePoint,
|
||||
KeyDesc: localChanCfg.DelayBasePoint,
|
||||
SingleTweak: localDelayTweak,
|
||||
WitnessScript: htlcSweepScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4573,7 +4577,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
ClaimOutpoint: op,
|
||||
CsvDelay: csvDelay,
|
||||
SweepSignDesc: SignDescriptor{
|
||||
PubKey: localChanCfg.HtlcBasePoint,
|
||||
KeyDesc: localChanCfg.HtlcBasePoint,
|
||||
SingleTweak: keyRing.LocalHtlcKeyTweak,
|
||||
WitnessScript: htlcSenderScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4609,7 +4613,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
return nil, err
|
||||
}
|
||||
successSignDesc := SignDescriptor{
|
||||
PubKey: localChanCfg.HtlcBasePoint,
|
||||
KeyDesc: localChanCfg.HtlcBasePoint,
|
||||
SingleTweak: keyRing.LocalHtlcKeyTweak,
|
||||
WitnessScript: htlcCreationScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4645,7 +4649,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
}
|
||||
|
||||
localDelayTweak := SingleTweakBytes(
|
||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint,
|
||||
keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey,
|
||||
)
|
||||
return &IncomingHtlcResolution{
|
||||
Preimage: preimage,
|
||||
@@ -4656,7 +4660,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
|
||||
Index: 0,
|
||||
},
|
||||
SweepSignDesc: SignDescriptor{
|
||||
PubKey: localChanCfg.DelayBasePoint,
|
||||
KeyDesc: localChanCfg.DelayBasePoint,
|
||||
SingleTweak: localDelayTweak,
|
||||
WitnessScript: htlcSweepScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -4844,8 +4848,9 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
|
||||
// nil.
|
||||
var commitResolution *CommitOutputResolution
|
||||
if len(delayScript) != 0 {
|
||||
singleTweak := SingleTweakBytes(commitPoint,
|
||||
lc.localChanCfg.DelayBasePoint)
|
||||
singleTweak := SingleTweakBytes(
|
||||
commitPoint, lc.localChanCfg.DelayBasePoint.PubKey,
|
||||
)
|
||||
localBalance := localCommitment.LocalBalance
|
||||
commitResolution = &CommitOutputResolution{
|
||||
SelfOutPoint: wire.OutPoint{
|
||||
@@ -4853,7 +4858,7 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
|
||||
Index: delayIndex,
|
||||
},
|
||||
SelfOutputSignDesc: SignDescriptor{
|
||||
PubKey: lc.localChanCfg.DelayBasePoint,
|
||||
KeyDesc: lc.localChanCfg.DelayBasePoint,
|
||||
SingleTweak: singleTweak,
|
||||
WitnessScript: selfScript,
|
||||
Output: &wire.TxOut{
|
||||
@@ -5012,8 +5017,8 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte,
|
||||
|
||||
// Finally, construct the witness stack minding the order of the
|
||||
// pubkeys+sigs on the stack.
|
||||
ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed()
|
||||
theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed()
|
||||
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
|
||||
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
|
||||
witness := SpendMultiSig(lc.signDesc.WitnessScript, ourKey,
|
||||
localSig, theirKey, remoteSig)
|
||||
closeTx.TxIn[0].Witness = witness
|
||||
|
||||
+48
-25
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/shachain"
|
||||
"github.com/roasbeef/btcd/blockchain"
|
||||
@@ -168,12 +169,22 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
|
||||
MinHTLC: 0,
|
||||
MaxAcceptedHtlcs: MaxHTLCNumber / 2,
|
||||
},
|
||||
CsvDelay: uint16(csvTimeoutAlice),
|
||||
MultiSigKey: aliceKeys[0].PubKey(),
|
||||
RevocationBasePoint: aliceKeys[1].PubKey(),
|
||||
PaymentBasePoint: aliceKeys[2].PubKey(),
|
||||
DelayBasePoint: aliceKeys[3].PubKey(),
|
||||
HtlcBasePoint: aliceKeys[4].PubKey(),
|
||||
CsvDelay: uint16(csvTimeoutAlice),
|
||||
MultiSigKey: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeys[0].PubKey(),
|
||||
},
|
||||
RevocationBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeys[1].PubKey(),
|
||||
},
|
||||
PaymentBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeys[2].PubKey(),
|
||||
},
|
||||
DelayBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeys[3].PubKey(),
|
||||
},
|
||||
HtlcBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeys[4].PubKey(),
|
||||
},
|
||||
}
|
||||
bobCfg := channeldb.ChannelConfig{
|
||||
ChannelConstraints: channeldb.ChannelConstraints{
|
||||
@@ -183,28 +194,40 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
|
||||
MinHTLC: 0,
|
||||
MaxAcceptedHtlcs: MaxHTLCNumber / 2,
|
||||
},
|
||||
CsvDelay: uint16(csvTimeoutBob),
|
||||
MultiSigKey: bobKeys[0].PubKey(),
|
||||
RevocationBasePoint: bobKeys[1].PubKey(),
|
||||
PaymentBasePoint: bobKeys[2].PubKey(),
|
||||
DelayBasePoint: bobKeys[3].PubKey(),
|
||||
HtlcBasePoint: bobKeys[4].PubKey(),
|
||||
CsvDelay: uint16(csvTimeoutBob),
|
||||
MultiSigKey: keychain.KeyDescriptor{
|
||||
PubKey: bobKeys[0].PubKey(),
|
||||
},
|
||||
RevocationBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: bobKeys[1].PubKey(),
|
||||
},
|
||||
PaymentBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: bobKeys[2].PubKey(),
|
||||
},
|
||||
DelayBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: bobKeys[3].PubKey(),
|
||||
},
|
||||
HtlcBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: bobKeys[4].PubKey(),
|
||||
},
|
||||
}
|
||||
|
||||
bobRoot := DeriveRevocationRoot(
|
||||
bobKeys[0], testHdSeed, aliceKeys[0].PubKey(),
|
||||
)
|
||||
bobPreimageProducer := shachain.NewRevocationProducer(bobRoot)
|
||||
bobRoot, err := chainhash.NewHash(bobKeys[0].Serialize())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
|
||||
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:])
|
||||
|
||||
aliceRoot := DeriveRevocationRoot(
|
||||
aliceKeys[0], testHdSeed, bobKeys[0].PubKey(),
|
||||
)
|
||||
alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot)
|
||||
aliceRoot, err := chainhash.NewHash(aliceKeys[0].Serialize())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
|
||||
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -880,8 +903,8 @@ func TestForceClose(t *testing.T) {
|
||||
|
||||
// The rest of the close summary should have been populated properly.
|
||||
aliceDelayPoint := aliceChannel.channelState.LocalChanCfg.DelayBasePoint
|
||||
if !aliceCommitResolution.SelfOutputSignDesc.PubKey.IsEqual(
|
||||
aliceDelayPoint,
|
||||
if !aliceCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(
|
||||
aliceDelayPoint.PubKey,
|
||||
) {
|
||||
t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc")
|
||||
}
|
||||
@@ -1036,7 +1059,7 @@ func TestForceClose(t *testing.T) {
|
||||
t.Fatalf("bob fails to include to-self output in ForceCloseSummary")
|
||||
}
|
||||
bobDelayPoint := bobChannel.channelState.LocalChanCfg.DelayBasePoint
|
||||
if !bobCommitResolution.SelfOutputSignDesc.PubKey.IsEqual(bobDelayPoint) {
|
||||
if !bobCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(bobDelayPoint.PubKey) {
|
||||
t.Fatalf("bob incorrect pubkey in SelfOutputSignDesc")
|
||||
}
|
||||
if bobCommitResolution.SelfOutputSignDesc.Output.Value !=
|
||||
@@ -1156,8 +1179,8 @@ func TestForceCloseDustOutput(t *testing.T) {
|
||||
t.Fatalf("alice fails to include to-self output in " +
|
||||
"ForceCloseSummary")
|
||||
}
|
||||
if !commitResolution.SelfOutputSignDesc.PubKey.IsEqual(
|
||||
aliceChannel.channelState.LocalChanCfg.DelayBasePoint,
|
||||
if !commitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(
|
||||
aliceChannel.channelState.LocalChanCfg.DelayBasePoint.PubKey,
|
||||
) {
|
||||
t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc")
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ type mockSigner struct {
|
||||
}
|
||||
|
||||
func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) {
|
||||
pubkey := signDesc.PubKey
|
||||
pubkey := signDesc.KeyDesc.PubKey
|
||||
switch {
|
||||
case signDesc.SingleTweak != nil:
|
||||
pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak)
|
||||
|
||||
@@ -3,6 +3,7 @@ package lnwallet
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/roasbeef/btcd/chaincfg"
|
||||
)
|
||||
|
||||
@@ -22,6 +23,12 @@ type Config struct {
|
||||
// counterparty's broadcasting revoked commitment states.
|
||||
Notifier chainntnfs.ChainNotifier
|
||||
|
||||
// SecretKeyRing is used by the wallet during the funding workflow
|
||||
// process to obtain keys to be used directly within contracts. Usage
|
||||
// of this interface ensures that all key derivation is itself fully
|
||||
// deterministic.
|
||||
SecretKeyRing keychain.SecretKeyRing
|
||||
|
||||
// WalletController is the core wallet, all non Lightning Network
|
||||
// specific interaction is proxied to the internal wallet.
|
||||
WalletController WalletController
|
||||
|
||||
+11
-24
@@ -20,19 +20,16 @@ var ErrNotMine = errors.New("the passed output doesn't belong to the wallet")
|
||||
type AddressType uint8
|
||||
|
||||
const (
|
||||
// UnknownAddressType represents an output with an unknown or non-standard
|
||||
// script.
|
||||
UnknownAddressType AddressType = iota
|
||||
|
||||
// WitnessPubKey represents a p2wkh address.
|
||||
WitnessPubKey
|
||||
WitnessPubKey AddressType = iota
|
||||
|
||||
// NestedWitnessPubKey represents a p2sh output which is itself a
|
||||
// nested p2wkh output.
|
||||
NestedWitnessPubKey
|
||||
|
||||
// PubKeyHash represents a regular p2pkh output.
|
||||
PubKeyHash
|
||||
// UnknownAddressType represents an output with an unknown or non-standard
|
||||
// script.
|
||||
UnknownAddressType
|
||||
)
|
||||
|
||||
// ErrDoubleSpend is returned from PublishTransaction in case the
|
||||
@@ -127,14 +124,18 @@ type WalletController interface {
|
||||
// that have at least confs confirmations. If confs is set to zero,
|
||||
// then all unspent outputs, including those currently in the mempool
|
||||
// will be included in the final sum.
|
||||
ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, error)
|
||||
//
|
||||
// NOTE: Only witness outputs should be included in the computation of
|
||||
// the total spendable balance of the wallet. We require this as only
|
||||
// witness inputs can be used for funding channels.
|
||||
ConfirmedBalance(confs int32) (btcutil.Amount, error)
|
||||
|
||||
// NewAddress returns the next external or internal address for the
|
||||
// wallet dictated by the value of the `change` parameter. If change is
|
||||
// true, then an internal address should be used, otherwise an external
|
||||
// address should be returned. The type of address returned is dictated
|
||||
// by the wallet's capabilities, and may be of type: p2sh, p2pkh,
|
||||
// p2wkh, p2wsh, etc.
|
||||
// by the wallet's capabilities, and may be of type: p2sh, p2wkh,
|
||||
// p2wsh, etc.
|
||||
NewAddress(addrType AddressType, change bool) (btcutil.Address, error)
|
||||
|
||||
// GetPrivKey retrieves the underlying private key associated with the
|
||||
@@ -143,20 +144,6 @@ type WalletController interface {
|
||||
// error should be returned.
|
||||
GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error)
|
||||
|
||||
// NewRawKey returns a raw private key controlled by the wallet. These
|
||||
// keys are used for the 2-of-2 multi-sig outputs for funding
|
||||
// transactions, as well as the pub key used for commitment transactions.
|
||||
//
|
||||
// NOTE: The wallet MUST watch for on-chain outputs created to a p2wpkh
|
||||
// script using keys returned by this function.
|
||||
NewRawKey() (*btcec.PublicKey, error)
|
||||
|
||||
// FetchRootKey returns a root key which will be used by the
|
||||
// LightningWallet to deterministically generate secrets. The private
|
||||
// key returned by this method should remain constant in-between
|
||||
// WalletController restarts.
|
||||
FetchRootKey() (*btcec.PrivateKey, error)
|
||||
|
||||
// SendOutputs funds, signs, and broadcasts a Bitcoin transaction
|
||||
// paying out to the specified outputs. In the case the wallet has
|
||||
// insufficient funds, or the outputs are non-standard, an error should
|
||||
|
||||
+70
-36
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
@@ -101,7 +102,7 @@ var (
|
||||
// within the wallet are *exactly* amount. If unable to retrieve the current
|
||||
// balance, or the assertion fails, the test will halt with a fatal error.
|
||||
func assertProperBalance(t *testing.T, lw *lnwallet.LightningWallet, numConfirms int32, amount int64) {
|
||||
balance, err := lw.ConfirmedBalance(numConfirms, false)
|
||||
balance, err := lw.ConfirmedBalance(numConfirms)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for balance: %v", err)
|
||||
}
|
||||
@@ -159,7 +160,7 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
// Using the mining node, spend from a coinbase output numOutputs to
|
||||
// give us btcPerOutput with each output.
|
||||
satoshiPerOutput := int64(btcPerOutput * 1e8)
|
||||
expectedBalance, err := w.ConfirmedBalance(1, false)
|
||||
expectedBalance, err := w.ConfirmedBalance(1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,7 +203,7 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
timeout := time.After(30 * time.Second)
|
||||
|
||||
for range ticker.C {
|
||||
balance, err := w.ConfirmedBalance(1, false)
|
||||
balance, err := w.ConfirmedBalance(1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -230,8 +231,8 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
// available for funding channels.
|
||||
func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
|
||||
netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier,
|
||||
wc lnwallet.WalletController, signer lnwallet.Signer,
|
||||
bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) {
|
||||
wc lnwallet.WalletController, keyRing keychain.SecretKeyRing,
|
||||
signer lnwallet.Signer, bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) {
|
||||
|
||||
dbDir := filepath.Join(tempTestDir, "cdb")
|
||||
cdb, err := channeldb.Open(dbDir)
|
||||
@@ -242,6 +243,7 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
|
||||
cfg := lnwallet.Config{
|
||||
Database: cdb,
|
||||
Notifier: notifier,
|
||||
SecretKeyRing: keyRing,
|
||||
WalletController: wc,
|
||||
Signer: signer,
|
||||
ChainIO: bio,
|
||||
@@ -360,7 +362,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
|
||||
t.Fatalf("bob's commit signatures not populated")
|
||||
}
|
||||
|
||||
// To concludes, we'll consume first Alice's signatures with Bob, and
|
||||
// To conclude, we'll consume first Alice's signatures with Bob, and
|
||||
// then the other way around.
|
||||
_, err = aliceChanReservation.CompleteReservation(
|
||||
bobFundingSigs, bobCommitSig,
|
||||
@@ -597,16 +599,16 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi
|
||||
t.Fatalf("line #%v: csv delay not set", line)
|
||||
}
|
||||
|
||||
if c.MultiSigKey == nil {
|
||||
if c.MultiSigKey.PubKey == nil {
|
||||
t.Fatalf("line #%v: multi-sig key not set", line)
|
||||
}
|
||||
if c.RevocationBasePoint == nil {
|
||||
if c.RevocationBasePoint.PubKey == nil {
|
||||
t.Fatalf("line #%v: revocation key not set", line)
|
||||
}
|
||||
if c.PaymentBasePoint == nil {
|
||||
if c.PaymentBasePoint.PubKey == nil {
|
||||
t.Fatalf("line #%v: payment key not set", line)
|
||||
}
|
||||
if c.DelayBasePoint == nil {
|
||||
if c.DelayBasePoint.PubKey == nil {
|
||||
t.Fatalf("line #%v: delay key not set", line)
|
||||
}
|
||||
|
||||
@@ -617,8 +619,6 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi
|
||||
t.Fatalf("line #%v: max pending amt not set", line)
|
||||
}
|
||||
if c.ChanReserve == 0 {
|
||||
// TODO(roasbeef): need to follow up and ensure reserve set to
|
||||
// fraction
|
||||
t.Fatalf("line #%v: chan reserve not set", line)
|
||||
}
|
||||
if c.MinHTLC == 0 {
|
||||
@@ -1154,11 +1154,13 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
}
|
||||
|
||||
// Generate a pubkey, and pay-to-addr script.
|
||||
pubKey, err := alice.NewRawKey()
|
||||
pubKey, err := alice.DeriveNextKey(
|
||||
keychain.KeyFamilyMultiSig,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to obtain public key: %v", err)
|
||||
}
|
||||
pubkeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
|
||||
pubkeyHash := btcutil.Hash160(pubKey.PubKey.SerializeCompressed())
|
||||
keyAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash,
|
||||
&chaincfg.RegressionNetParams)
|
||||
if err != nil {
|
||||
@@ -1214,7 +1216,9 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
// Now we can populate the sign descriptor which we'll use to
|
||||
// generate the signature.
|
||||
signDesc := &lnwallet.SignDescriptor{
|
||||
PubKey: pubKey,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: pubKey.PubKey,
|
||||
},
|
||||
WitnessScript: keyScript,
|
||||
Output: tx.TxOut[outputIndex],
|
||||
HashType: txscript.SigHashAll,
|
||||
@@ -1231,7 +1235,7 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
}
|
||||
witness := make([][]byte, 2)
|
||||
witness[0] = append(spendSig, byte(txscript.SigHashAll))
|
||||
witness[1] = pubKey.SerializeCompressed()
|
||||
witness[1] = pubKey.PubKey.SerializeCompressed()
|
||||
tx1.TxIn[0].Witness = witness
|
||||
|
||||
// Finally, attempt to validate the completed transaction. This
|
||||
@@ -1280,7 +1284,7 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
t.Fatalf("unable to mine tx: %v", err)
|
||||
}
|
||||
txFee := btcutil.Amount(0.1 * btcutil.SatoshiPerBitcoin)
|
||||
tx1 := txFromOutput(tx.MsgTx(), pubKey, txFee)
|
||||
tx1 := txFromOutput(tx.MsgTx(), pubKey.PubKey, txFee)
|
||||
|
||||
return tx1
|
||||
}
|
||||
@@ -1367,7 +1371,7 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
// from the tx just mined. This should be accepted
|
||||
// into the mempool.
|
||||
txFee := btcutil.Amount(0.05 * btcutil.SatoshiPerBitcoin)
|
||||
tx4 := txFromOutput(tx3, pubKey, txFee)
|
||||
tx4 := txFromOutput(tx3, pubKey.PubKey, txFee)
|
||||
if err := alice.PublishTransaction(tx4); err != nil {
|
||||
t.Fatalf("unable to publish: %v", err)
|
||||
}
|
||||
@@ -1380,7 +1384,9 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
|
||||
// Create a new key we'll pay to, to ensure we create
|
||||
// a unique transaction.
|
||||
pubKey2, err := alice.NewRawKey()
|
||||
pubKey2, err := alice.DeriveNextKey(
|
||||
keychain.KeyFamilyMultiSig,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to obtain public key: %v", err)
|
||||
}
|
||||
@@ -1388,7 +1394,7 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
// Create a new transaction that spends the output from
|
||||
// tx3, and that pays to a different address. We expect
|
||||
// this to be rejected because it is a double spend.
|
||||
tx5 := txFromOutput(tx3, pubKey2, txFee)
|
||||
tx5 := txFromOutput(tx3, pubKey2.PubKey, txFee)
|
||||
if err := alice.PublishTransaction(tx5); err != lnwallet.ErrDoubleSpend {
|
||||
t.Fatalf("expected ErrDoubleSpend, got: %v", err)
|
||||
}
|
||||
@@ -1397,11 +1403,13 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
// but has a higher fee. We expect also this tx to be
|
||||
// rejected, since the sequence number of tx3 is set to Max,
|
||||
// indicating it is not replacable.
|
||||
pubKey3, err := alice.NewRawKey()
|
||||
pubKey3, err := alice.DeriveNextKey(
|
||||
keychain.KeyFamilyMultiSig,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to obtain public key: %v", err)
|
||||
}
|
||||
tx6 := txFromOutput(tx3, pubKey3, 3*txFee)
|
||||
tx6 := txFromOutput(tx3, pubKey3.PubKey, 3*txFee)
|
||||
|
||||
// Expect rejection.
|
||||
if err := alice.PublishTransaction(tx6); err != lnwallet.ErrDoubleSpend {
|
||||
@@ -1422,11 +1430,13 @@ func testPublishTransaction(r *rpctest.Harness,
|
||||
}
|
||||
|
||||
// Create another tx spending tx3.
|
||||
pubKey4, err := alice.NewRawKey()
|
||||
pubKey4, err := alice.DeriveNextKey(
|
||||
keychain.KeyFamilyMultiSig,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to obtain public key: %v", err)
|
||||
}
|
||||
tx7 := txFromOutput(tx3, pubKey4, txFee)
|
||||
tx7 := txFromOutput(tx3, pubKey4.PubKey, txFee)
|
||||
|
||||
// Expect rejection.
|
||||
if err := alice.PublishTransaction(tx7); err != lnwallet.ErrDoubleSpend {
|
||||
@@ -1449,7 +1459,9 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
|
||||
|
||||
// First, generate a new public key under the control of the wallet,
|
||||
// then generate a revocation key using it.
|
||||
pubKey, err := alice.NewRawKey()
|
||||
pubKey, err := alice.DeriveNextKey(
|
||||
keychain.KeyFamilyMultiSig,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to obtain public key: %v", err)
|
||||
}
|
||||
@@ -1461,10 +1473,10 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
|
||||
commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||
commitPreimage)
|
||||
|
||||
revocationKey := lnwallet.DeriveRevocationPubkey(pubKey, commitPoint)
|
||||
commitTweak := lnwallet.SingleTweakBytes(commitPoint, pubKey)
|
||||
revocationKey := lnwallet.DeriveRevocationPubkey(pubKey.PubKey, commitPoint)
|
||||
commitTweak := lnwallet.SingleTweakBytes(commitPoint, pubKey.PubKey)
|
||||
|
||||
tweakedPub := lnwallet.TweakPubKey(pubKey, commitPoint)
|
||||
tweakedPub := lnwallet.TweakPubKey(pubKey.PubKey, commitPoint)
|
||||
|
||||
// As we'd like to test both single and double tweaks, we'll repeat
|
||||
// the same set up twice. The first will use a regular single tweak,
|
||||
@@ -1539,7 +1551,9 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
|
||||
// based on this tweak value and the key we originally
|
||||
// generated above.
|
||||
signDesc := &lnwallet.SignDescriptor{
|
||||
PubKey: baseKey,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: baseKey.PubKey,
|
||||
},
|
||||
WitnessScript: keyScript,
|
||||
Output: newOutput,
|
||||
HashType: txscript.SigHashAll,
|
||||
@@ -1584,6 +1598,7 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
|
||||
|
||||
func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
_ *lnwallet.LightningWallet, t *testing.T) {
|
||||
|
||||
// We first mine a few blocks to ensure any transactions still in the
|
||||
// mempool confirm, and then get the original balance, before a
|
||||
// reorganization that doesn't invalidate any existing transactions or
|
||||
@@ -1640,7 +1655,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
}
|
||||
|
||||
// Get the original balance.
|
||||
origBalance, err := w.ConfirmedBalance(1, false)
|
||||
origBalance, err := w.ConfirmedBalance(1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for balance: %v", err)
|
||||
}
|
||||
@@ -1656,7 +1671,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
t.Fatalf("unable to set up mining node: %v", err)
|
||||
}
|
||||
defer r2.TearDown()
|
||||
newBalance, err := w.ConfirmedBalance(1, false)
|
||||
newBalance, err := w.ConfirmedBalance(1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for balance: %v", err)
|
||||
}
|
||||
@@ -1748,7 +1763,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
|
||||
}
|
||||
|
||||
// Now we check that the wallet balance stays the same.
|
||||
newBalance, err = w.ConfirmedBalance(1, false)
|
||||
newBalance, err = w.ConfirmedBalance(1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for balance: %v", err)
|
||||
}
|
||||
@@ -1966,6 +1981,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
aliceSigner lnwallet.Signer
|
||||
bobSigner lnwallet.Signer
|
||||
|
||||
aliceKeyRing keychain.SecretKeyRing
|
||||
bobKeyRing keychain.SecretKeyRing
|
||||
|
||||
aliceWalletController lnwallet.WalletController
|
||||
bobWalletController lnwallet.WalletController
|
||||
|
||||
@@ -2008,14 +2026,17 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
if err != nil {
|
||||
t.Fatalf("unable to make chain rpc: %v", err)
|
||||
}
|
||||
|
||||
case "neutrino":
|
||||
feeEstimator = lnwallet.StaticFeeEstimator{FeeRate: 250}
|
||||
|
||||
// Set some package-level variable to speed up
|
||||
// operation for tests.
|
||||
neutrino.WaitForMoreCFHeaders = time.Millisecond * 100
|
||||
neutrino.BanDuration = time.Millisecond * 100
|
||||
neutrino.QueryTimeout = time.Millisecond * 500
|
||||
neutrino.QueryNumRetries = 2
|
||||
|
||||
// Start Alice - open a database, start a neutrino
|
||||
// instance, and initialize a btcwallet driver for it.
|
||||
aliceDB, err := walletdb.Create("bdb",
|
||||
@@ -2065,6 +2086,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
bobChain.Start()
|
||||
defer bobChain.Stop()
|
||||
bobClient = chain.NewNeutrinoClient(bobChain)
|
||||
|
||||
case "bitcoind":
|
||||
feeEstimator, err = lnwallet.NewBitcoindFeeEstimator(
|
||||
rpcConfig, 250)
|
||||
@@ -2133,6 +2155,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
t.Fatalf("unable to create btcwallet: %v", err)
|
||||
}
|
||||
aliceSigner = aliceWalletController.(*btcwallet.BtcWallet)
|
||||
aliceKeyRing = keychain.NewBtcWalletKeyRing(
|
||||
aliceWalletController.(*btcwallet.BtcWallet).InternalWallet(),
|
||||
)
|
||||
|
||||
bobWalletConfig := &btcwallet.Config{
|
||||
PrivatePass: []byte("bob-pass"),
|
||||
@@ -2147,21 +2172,30 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
t.Fatalf("unable to create btcwallet: %v", err)
|
||||
}
|
||||
bobSigner = bobWalletController.(*btcwallet.BtcWallet)
|
||||
bobKeyRing = keychain.NewBtcWalletKeyRing(
|
||||
bobWalletController.(*btcwallet.BtcWallet).InternalWallet(),
|
||||
)
|
||||
bio = bobWalletController.(*btcwallet.BtcWallet)
|
||||
default:
|
||||
t.Fatalf("unknown wallet driver: %v", walletType)
|
||||
}
|
||||
|
||||
// Funding via 20 outputs with 4BTC each.
|
||||
alice, err := createTestWallet(tempTestDirAlice, miningNode, netParams,
|
||||
chainNotifier, aliceWalletController, aliceSigner, bio)
|
||||
alice, err := createTestWallet(
|
||||
tempTestDirAlice, miningNode, netParams,
|
||||
chainNotifier, aliceWalletController, aliceKeyRing,
|
||||
aliceSigner, bio,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test ln wallet: %v", err)
|
||||
}
|
||||
defer alice.Shutdown()
|
||||
|
||||
bob, err := createTestWallet(tempTestDirBob, miningNode, netParams,
|
||||
chainNotifier, bobWalletController, bobSigner, bio)
|
||||
bob, err := createTestWallet(
|
||||
tempTestDirBob, miningNode, netParams,
|
||||
chainNotifier, bobWalletController, bobKeyRing,
|
||||
bobSigner, bio,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test ln wallet: %v", err)
|
||||
}
|
||||
|
||||
+24
-34
@@ -7,11 +7,9 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/txscript"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
@@ -312,10 +310,17 @@ func senderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
|
||||
func SenderHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
|
||||
sweepTx *wire.MsgTx) (wire.TxWitness, error) {
|
||||
|
||||
if signDesc.KeyDesc.PubKey == nil {
|
||||
return nil, fmt.Errorf("cannot generate witness with nil " +
|
||||
"KeyDesc pubkey")
|
||||
}
|
||||
|
||||
// Derive the revocation key using the local revocation base point and
|
||||
// commitment point.
|
||||
revokeKey := DeriveRevocationPubkey(signDesc.PubKey,
|
||||
signDesc.DoubleTweak.PubKey())
|
||||
revokeKey := DeriveRevocationPubkey(
|
||||
signDesc.KeyDesc.PubKey,
|
||||
signDesc.DoubleTweak.PubKey(),
|
||||
)
|
||||
|
||||
return senderHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx)
|
||||
}
|
||||
@@ -562,10 +567,17 @@ func receiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
|
||||
func ReceiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor,
|
||||
sweepTx *wire.MsgTx) (wire.TxWitness, error) {
|
||||
|
||||
if signDesc.KeyDesc.PubKey == nil {
|
||||
return nil, fmt.Errorf("cannot generate witness with nil " +
|
||||
"KeyDesc pubkey")
|
||||
}
|
||||
|
||||
// Derive the revocation key using the local revocation base point and
|
||||
// commitment point.
|
||||
revokeKey := DeriveRevocationPubkey(signDesc.PubKey,
|
||||
signDesc.DoubleTweak.PubKey())
|
||||
revokeKey := DeriveRevocationPubkey(
|
||||
signDesc.KeyDesc.PubKey,
|
||||
signDesc.DoubleTweak.PubKey(),
|
||||
)
|
||||
|
||||
return receiverHtlcSpendRevoke(signer, signDesc, revokeKey, sweepTx)
|
||||
}
|
||||
@@ -1023,6 +1035,11 @@ func CommitSpendRevoke(signer Signer, signDesc *SignDescriptor,
|
||||
func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor,
|
||||
sweepTx *wire.MsgTx) (wire.TxWitness, error) {
|
||||
|
||||
if signDesc.KeyDesc.PubKey == nil {
|
||||
return nil, fmt.Errorf("cannot generate witness with nil " +
|
||||
"KeyDesc pubkey")
|
||||
}
|
||||
|
||||
// This is just a regular p2wkh spend which looks something like:
|
||||
// * witness: <sig> <pubkey>
|
||||
sweepSig, err := signer.SignOutputRaw(sweepTx, signDesc)
|
||||
@@ -1037,7 +1054,7 @@ func CommitSpendNoDelay(signer Signer, signDesc *SignDescriptor,
|
||||
witness := make([][]byte, 2)
|
||||
witness[0] = append(sweepSig, byte(signDesc.HashType))
|
||||
witness[1] = TweakPubKeyWithTweak(
|
||||
signDesc.PubKey, signDesc.SingleTweak,
|
||||
signDesc.KeyDesc.PubKey, signDesc.SingleTweak,
|
||||
).SerializeCompressed()
|
||||
|
||||
return witness, nil
|
||||
@@ -1218,33 +1235,6 @@ func DeriveRevocationPrivKey(revokeBasePriv *btcec.PrivateKey,
|
||||
return priv
|
||||
}
|
||||
|
||||
// DeriveRevocationRoot derives an root unique to a channel given the
|
||||
// derivation root, and the blockhash that the funding process began at and the
|
||||
// remote node's identity public key. The seed is derived using the HKDF[1][2]
|
||||
// instantiated with sha-256. With this schema, once we know the block hash of
|
||||
// the funding transaction, and who we funded the channel with, we can
|
||||
// reconstruct all of our revocation state.
|
||||
//
|
||||
// [1]: https://eprint.iacr.org/2010/264.pdf
|
||||
// [2]: https://tools.ietf.org/html/rfc5869
|
||||
func DeriveRevocationRoot(derivationRoot *btcec.PrivateKey,
|
||||
blockSalt chainhash.Hash, nodePubKey *btcec.PublicKey) chainhash.Hash {
|
||||
|
||||
secret := derivationRoot.Serialize()
|
||||
salt := blockSalt[:]
|
||||
info := nodePubKey.SerializeCompressed()
|
||||
|
||||
seedReader := hkdf.New(sha256.New, secret, salt, info)
|
||||
|
||||
// It's safe to ignore the error her as we know for sure that we won't
|
||||
// be draining the HKDF past its available entropy horizon.
|
||||
// TODO(roasbeef): revisit...
|
||||
var root chainhash.Hash
|
||||
seedReader.Read(root[:])
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
// SetStateNumHint encodes the current state number within the passed
|
||||
// commitment transaction by re-purposing the locktime and sequence fields in
|
||||
// the commitment transaction to encode the obfuscated state number. The state
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/txscript"
|
||||
@@ -112,9 +113,11 @@ func TestCommitmentSpendValidation(t *testing.T) {
|
||||
sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, csvTimeout)
|
||||
signDesc := &SignDescriptor{
|
||||
WitnessScript: delayScript,
|
||||
PubKey: aliceKeyPub,
|
||||
SingleTweak: aliceCommitTweak,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
SingleTweak: aliceCommitTweak,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
Output: &wire.TxOut{
|
||||
Value: int64(channelBalance),
|
||||
},
|
||||
@@ -143,7 +146,9 @@ func TestCommitmentSpendValidation(t *testing.T) {
|
||||
// simulate the scenario when Alice broadcasts this commitment
|
||||
// transaction after it's been revoked.
|
||||
signDesc = &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
DoubleTweak: commitSecret,
|
||||
WitnessScript: delayScript,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
@@ -186,7 +191,9 @@ func TestCommitmentSpendValidation(t *testing.T) {
|
||||
t.Fatalf("unable to create bob p2wkh script: %v", err)
|
||||
}
|
||||
signDesc = &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: bobScriptP2WKH,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
@@ -394,7 +401,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||
// that will act as Bob's signature to Alice for the second level HTLC
|
||||
// transaction.
|
||||
bobSignDesc := SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -416,7 +425,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||
// TODO(roasbeef): test invalid revoke
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
DoubleTweak: commitSecret,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -434,7 +445,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||
// HTLC with invalid preimage size
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -455,7 +468,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||
// TODO(roasbeef): invalid preimage
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -475,7 +490,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||
// transaction.
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
SingleTweak: aliceCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -491,6 +508,9 @@ func TestHTLCSenderSpendValidation(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// TODO(roasbeef): set of cases to ensure able to sign w/ keypath and
|
||||
// not
|
||||
|
||||
for i, testCase := range testCases {
|
||||
sweepTx.TxIn[0].Witness = testCase.witness()
|
||||
|
||||
@@ -639,7 +659,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
||||
// that will act as Alice's signature to Bob for the second level HTLC
|
||||
// transaction.
|
||||
aliceSignDesc := SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
SingleTweak: aliceCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -661,7 +683,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
||||
// HTLC redemption w/ invalid preimage size
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -681,7 +705,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
||||
// HTLC redemption w/ valid preimage size
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -700,7 +726,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
||||
// revoke w/ sig
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
DoubleTweak: commitSecret,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -718,7 +746,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
||||
// refund w/ invalid lock time
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
SingleTweak: aliceCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -736,7 +766,9 @@ func TestHTLCReceiverSpendValidation(t *testing.T) {
|
||||
// refund w/ valid lock time
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
SingleTweak: aliceCommitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -881,7 +913,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
|
||||
// use the double tweak in this case).
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
HashType: txscript.SigHashAll,
|
||||
@@ -898,7 +932,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
|
||||
// Sender of HTLC activates the revocation clause.
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
DoubleTweak: commitSecret,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -918,7 +954,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
|
||||
// blocks instead of 5 blocks).
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: commitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
@@ -938,7 +976,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
|
||||
// tweak).
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
HashType: txscript.SigHashAll,
|
||||
@@ -956,7 +996,9 @@ func TestSecondLevelHtlcSpends(t *testing.T) {
|
||||
// delay, and the correct key.
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
signDesc := &SignDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: commitTweak,
|
||||
WitnessScript: htlcWitnessScript,
|
||||
Output: htlcOutput,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/txscript"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
@@ -20,10 +21,11 @@ var (
|
||||
// a given output. This struct is used by the Signer interface in order to gain
|
||||
// access to critical data needed to generate a valid signature.
|
||||
type SignDescriptor struct {
|
||||
// Pubkey is the public key to which the signature should be generated
|
||||
// over. The Signer should then generate a signature with the private
|
||||
// key corresponding to this public key.
|
||||
PubKey *btcec.PublicKey
|
||||
// KeyDesc is a descriptor that precisely describes *which* key to use
|
||||
// for signing. This may provide the raw public key directly, or
|
||||
// require the Signer to re-derive the key according to the populated
|
||||
// derivation path.
|
||||
KeyDesc keychain.KeyDescriptor
|
||||
|
||||
// SingleTweak is a scalar value that will be added to the private key
|
||||
// corresponding to the above public key to obtain the private key to
|
||||
@@ -83,10 +85,26 @@ type SignDescriptor struct {
|
||||
// yet, since that is usually done just before broadcast by the witness
|
||||
// generator.
|
||||
func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error {
|
||||
serializedPubKey := sd.PubKey.SerializeCompressed()
|
||||
if err := wire.WriteVarBytes(w, 0, serializedPubKey); err != nil {
|
||||
err := binary.Write(w, binary.BigEndian, sd.KeyDesc.Family)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(w, binary.BigEndian, sd.KeyDesc.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = binary.Write(w, binary.BigEndian, sd.KeyDesc.PubKey != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sd.KeyDesc.PubKey != nil {
|
||||
serializedPubKey := sd.KeyDesc.PubKey.SerializeCompressed()
|
||||
if err := wire.WriteVarBytes(w, 0, serializedPubKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := wire.WriteVarBytes(w, 0, sd.SingleTweak); err != nil {
|
||||
return err
|
||||
@@ -120,15 +138,34 @@ func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error {
|
||||
// ReadSignDescriptor deserializes a SignDescriptor struct from the passed
|
||||
// io.Reader stream.
|
||||
func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error {
|
||||
pubKeyBytes, err := wire.ReadVarBytes(r, 0, 34, "pubkey")
|
||||
err := binary.Read(r, binary.BigEndian, &sd.KeyDesc.Family)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sd.PubKey, err = btcec.ParsePubKey(pubKeyBytes, btcec.S256())
|
||||
err = binary.Read(r, binary.BigEndian, &sd.KeyDesc.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hasKey bool
|
||||
err = binary.Read(r, binary.BigEndian, &hasKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasKey {
|
||||
pubKeyBytes, err := wire.ReadVarBytes(r, 0, 34, "pubkey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sd.KeyDesc.PubKey, err = btcec.ParsePubKey(
|
||||
pubKeyBytes, btcec.S256(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
singleTweak, err := wire.ReadVarBytes(r, 0, 32, "singleTweak")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/txscript"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
@@ -99,7 +100,13 @@ func TestSignDescriptorSerialization(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse pubkey: %v", err)
|
||||
}
|
||||
sd.PubKey = pubkey
|
||||
sd.KeyDesc = keychain.KeyDescriptor{
|
||||
KeyLocator: keychain.KeyLocator{
|
||||
Family: 50,
|
||||
Index: 99,
|
||||
},
|
||||
PubKey: pubkey,
|
||||
}
|
||||
|
||||
// Test that serialize -> deserialize yields same result as original.
|
||||
var buf bytes.Buffer
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/shachain"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
@@ -325,7 +326,7 @@ func (tc *testContext) extractFundingInput() (*Utxo, *wire.TxOut, error) {
|
||||
}
|
||||
|
||||
block1Utxo := Utxo{
|
||||
AddressType: PubKeyHash,
|
||||
AddressType: WitnessPubKey,
|
||||
Value: btcutil.Amount(txout.Value),
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: *tx.Hash(),
|
||||
@@ -378,16 +379,30 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
|
||||
MaxPendingAmount: lnwire.NewMSatFromSatoshis(tc.fundingAmount),
|
||||
MaxAcceptedHtlcs: MaxHTLCNumber,
|
||||
},
|
||||
CsvDelay: tc.localCsvDelay,
|
||||
MultiSigKey: tc.localFundingPubKey,
|
||||
PaymentBasePoint: tc.localPaymentBasePoint,
|
||||
HtlcBasePoint: tc.localPaymentBasePoint,
|
||||
DelayBasePoint: localDelayBasePoint,
|
||||
CsvDelay: tc.localCsvDelay,
|
||||
MultiSigKey: keychain.KeyDescriptor{
|
||||
PubKey: tc.localFundingPubKey,
|
||||
},
|
||||
PaymentBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: tc.localPaymentBasePoint,
|
||||
},
|
||||
HtlcBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: tc.localPaymentBasePoint,
|
||||
},
|
||||
DelayBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: localDelayBasePoint,
|
||||
},
|
||||
},
|
||||
RemoteChanCfg: channeldb.ChannelConfig{
|
||||
MultiSigKey: tc.remoteFundingPubKey,
|
||||
PaymentBasePoint: tc.remotePaymentBasePoint,
|
||||
HtlcBasePoint: tc.remotePaymentBasePoint,
|
||||
MultiSigKey: keychain.KeyDescriptor{
|
||||
PubKey: tc.remoteFundingPubKey,
|
||||
},
|
||||
PaymentBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: tc.remotePaymentBasePoint,
|
||||
},
|
||||
HtlcBasePoint: keychain.KeyDescriptor{
|
||||
PubKey: tc.remotePaymentBasePoint,
|
||||
},
|
||||
},
|
||||
Capacity: tc.fundingAmount,
|
||||
RevocationProducer: shachain.NewRevocationProducer(zeroHash),
|
||||
|
||||
+68
-99
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/blockchain"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
@@ -27,24 +28,6 @@ const (
|
||||
// The size of the buffered queue of requests to the wallet from the
|
||||
// outside word.
|
||||
msgBufferSize = 100
|
||||
|
||||
// revocationRootIndex is the top level HD key index from which secrets
|
||||
// used to generate producer roots should be derived from.
|
||||
revocationRootIndex = hdkeychain.HardenedKeyStart + 1
|
||||
|
||||
// identityKeyIndex is the top level HD key index which is used to
|
||||
// generate/rotate identity keys.
|
||||
//
|
||||
// TODO(roasbeef): should instead be child to make room for future
|
||||
// rotations, etc.
|
||||
identityKeyIndex = hdkeychain.HardenedKeyStart + 2
|
||||
)
|
||||
|
||||
var (
|
||||
// Namespace bucket keys.
|
||||
lightningNamespaceKey = []byte("ln-wallet")
|
||||
waddrmgrNamespaceKey = []byte("waddrmgr")
|
||||
wtxmgrNamespaceKey = []byte("wtxmgr")
|
||||
)
|
||||
|
||||
// ErrInsufficientFunds is a type matching the error interface which is
|
||||
@@ -248,6 +231,11 @@ type LightningWallet struct {
|
||||
// specific interaction is proxied to the internal wallet.
|
||||
WalletController
|
||||
|
||||
// SecretKeyRing is the interface we'll use to derive any keys related
|
||||
// to our purpose within the network including: multi-sig keys, node
|
||||
// keys, revocation keys, etc.
|
||||
keychain.SecretKeyRing
|
||||
|
||||
// This mutex is to be held when generating external keys to be used as
|
||||
// multi-sig, and commitment keys within the channel.
|
||||
keyGenMtx sync.RWMutex
|
||||
@@ -297,6 +285,7 @@ func NewLightningWallet(Cfg Config) (*LightningWallet, error) {
|
||||
|
||||
return &LightningWallet{
|
||||
Cfg: Cfg,
|
||||
SecretKeyRing: Cfg.SecretKeyRing,
|
||||
WalletController: Cfg.WalletController,
|
||||
msgChan: make(chan interface{}, msgBufferSize),
|
||||
nextFundingID: 0,
|
||||
@@ -319,20 +308,6 @@ func (l *LightningWallet) Startup() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch the root derivation key from the wallet's HD chain. We'll use
|
||||
// this to generate specific Lightning related secrets on the fly.
|
||||
rootKey, err := l.FetchRootKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(roasbeef): always re-derive on the fly?
|
||||
rootKeyRaw := rootKey.Serialize()
|
||||
l.rootKey, err = hdkeychain.NewMaster(rootKeyRaw, &l.Cfg.NetParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.wg.Add(1)
|
||||
// TODO(roasbeef): multiple request handlers?
|
||||
go l.requestHandler()
|
||||
@@ -390,17 +365,6 @@ func (l *LightningWallet) ActiveReservations() []*ChannelReservation {
|
||||
return reservations
|
||||
}
|
||||
|
||||
// GetIdentitykey returns the identity private key of the wallet.
|
||||
// TODO(roasbeef): should be moved elsewhere
|
||||
func (l *LightningWallet) GetIdentitykey() (*btcec.PrivateKey, error) {
|
||||
identityKey, err := l.rootKey.Child(identityKeyIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return identityKey.ECPrivKey()
|
||||
}
|
||||
|
||||
// requestHandler is the primary goroutine(s) responsible for handling, and
|
||||
// dispatching relies to all messages.
|
||||
func (l *LightningWallet) requestHandler() {
|
||||
@@ -534,35 +498,42 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
||||
// key, the base revocation key, the base htlc key,the base payment
|
||||
// key, and the delayed payment key.
|
||||
//
|
||||
// TODO(roasbeef): special derivaiton?
|
||||
reservation.ourContribution.MultiSigKey, err = l.NewRawKey()
|
||||
// TODO(roasbeef): "salt" each key as well?
|
||||
reservation.ourContribution.MultiSigKey, err = l.DeriveNextKey(
|
||||
keychain.KeyFamilyMultiSig,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
reservation.ourContribution.RevocationBasePoint, err = l.NewRawKey()
|
||||
reservation.ourContribution.RevocationBasePoint, err = l.DeriveNextKey(
|
||||
keychain.KeyFamilyRevocationBase,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
reservation.ourContribution.HtlcBasePoint, err = l.NewRawKey()
|
||||
reservation.ourContribution.HtlcBasePoint, err = l.DeriveNextKey(
|
||||
keychain.KeyFamilyHtlcBase,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
// TODO(roasbeef); allow for querying to extract key distinct from HD
|
||||
// chain
|
||||
// * allows for offline commitment keys
|
||||
reservation.ourContribution.PaymentBasePoint, err = l.NewRawKey()
|
||||
reservation.ourContribution.PaymentBasePoint, err = l.DeriveNextKey(
|
||||
keychain.KeyFamilyPaymentBase,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
reservation.ourContribution.DelayBasePoint, err = l.NewRawKey()
|
||||
reservation.ourContribution.DelayBasePoint, err = l.DeriveNextKey(
|
||||
keychain.KeyFamilyDelayBase,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
@@ -570,30 +541,31 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
||||
}
|
||||
|
||||
// With the above keys created, we'll also need to initialization our
|
||||
// initial revocation tree state. In order to do so in a deterministic
|
||||
// manner (for recovery purposes), we'll use the current block hash
|
||||
// along with the identity public key of the node we're creating the
|
||||
// channel with. In the event of a recovery, given these two items and
|
||||
// the initialize wallet HD seed, we can derive all of our revocation
|
||||
// secrets.
|
||||
masterElkremRoot, err := l.deriveMasterRevocationRoot()
|
||||
// initial revocation tree state.
|
||||
nextRevocationKeyDesc, err := l.DeriveNextKey(
|
||||
keychain.KeyFamilyRevocationRoot,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
bestHash, _, err := l.Cfg.ChainIO.GetBestBlock()
|
||||
revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
revocationRoot := DeriveRevocationRoot(masterElkremRoot, *bestHash,
|
||||
req.nodeID)
|
||||
|
||||
// Once we have the root, we can then generate our shachain producer
|
||||
// and from that generate the per-commitment point.
|
||||
producer := shachain.NewRevocationProducer(revocationRoot)
|
||||
revRoot, err := chainhash.NewHash(revocationRoot.Serialize())
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
producer := shachain.NewRevocationProducer(*revRoot)
|
||||
firstPreimage, err := producer.AtIndex(0)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
@@ -750,8 +722,10 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
||||
// Finally, add the 2-of-2 multi-sig output which will set up the lightning
|
||||
// channel.
|
||||
channelCapacity := int64(pendingReservation.partialState.Capacity)
|
||||
witnessScript, multiSigOut, err := GenFundingPkScript(ourKey.SerializeCompressed(),
|
||||
theirKey.SerializeCompressed(), channelCapacity)
|
||||
witnessScript, multiSigOut, err := GenFundingPkScript(
|
||||
ourKey.PubKey.SerializeCompressed(),
|
||||
theirKey.PubKey.SerializeCompressed(), channelCapacity,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
return
|
||||
@@ -850,22 +824,22 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
||||
var stateObfuscator [StateHintSize]byte
|
||||
if chanState.ChanType == channeldb.SingleFunder {
|
||||
stateObfuscator = DeriveStateHintObfuscator(
|
||||
ourContribution.PaymentBasePoint,
|
||||
theirContribution.PaymentBasePoint,
|
||||
ourContribution.PaymentBasePoint.PubKey,
|
||||
theirContribution.PaymentBasePoint.PubKey,
|
||||
)
|
||||
} else {
|
||||
ourSer := ourContribution.PaymentBasePoint.SerializeCompressed()
|
||||
theirSer := theirContribution.PaymentBasePoint.SerializeCompressed()
|
||||
ourSer := ourContribution.PaymentBasePoint.PubKey.SerializeCompressed()
|
||||
theirSer := theirContribution.PaymentBasePoint.PubKey.SerializeCompressed()
|
||||
switch bytes.Compare(ourSer, theirSer) {
|
||||
case -1:
|
||||
stateObfuscator = DeriveStateHintObfuscator(
|
||||
ourContribution.PaymentBasePoint,
|
||||
theirContribution.PaymentBasePoint,
|
||||
ourContribution.PaymentBasePoint.PubKey,
|
||||
theirContribution.PaymentBasePoint.PubKey,
|
||||
)
|
||||
default:
|
||||
stateObfuscator = DeriveStateHintObfuscator(
|
||||
theirContribution.PaymentBasePoint,
|
||||
ourContribution.PaymentBasePoint,
|
||||
theirContribution.PaymentBasePoint.PubKey,
|
||||
ourContribution.PaymentBasePoint.PubKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -895,7 +869,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
||||
// transaction.
|
||||
signDesc = SignDescriptor{
|
||||
WitnessScript: witnessScript,
|
||||
PubKey: ourKey,
|
||||
KeyDesc: ourKey,
|
||||
Output: multiSigOut,
|
||||
HashType: txscript.SigHashAll,
|
||||
SigHashes: txscript.NewTxSigHashes(theirCommitTx),
|
||||
@@ -1038,8 +1012,11 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
|
||||
// Re-generate both the witnessScript and p2sh output. We sign the
|
||||
// witnessScript script, but include the p2sh output as the subscript
|
||||
// for verification.
|
||||
witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(),
|
||||
theirKey.SerializeCompressed(), int64(res.partialState.Capacity))
|
||||
witnessScript, _, err := GenFundingPkScript(
|
||||
ourKey.PubKey.SerializeCompressed(),
|
||||
theirKey.PubKey.SerializeCompressed(),
|
||||
int64(res.partialState.Capacity),
|
||||
)
|
||||
if err != nil {
|
||||
msg.err <- err
|
||||
msg.completeChan <- nil
|
||||
@@ -1066,7 +1043,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
|
||||
msg.err <- err
|
||||
msg.completeChan <- nil
|
||||
return
|
||||
} else if !sig.Verify(sigHash, theirKey) {
|
||||
} else if !sig.Verify(sigHash, theirKey.PubKey) {
|
||||
msg.err <- fmt.Errorf("counterparty's commitment signature is invalid")
|
||||
msg.completeChan <- nil
|
||||
return
|
||||
@@ -1168,8 +1145,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
// generator state obfuscator to encode the current state number within
|
||||
// both commitment transactions.
|
||||
stateObfuscator := DeriveStateHintObfuscator(
|
||||
pendingReservation.theirContribution.PaymentBasePoint,
|
||||
pendingReservation.ourContribution.PaymentBasePoint)
|
||||
pendingReservation.theirContribution.PaymentBasePoint.PubKey,
|
||||
pendingReservation.ourContribution.PaymentBasePoint.PubKey,
|
||||
)
|
||||
err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
@@ -1194,8 +1172,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
hashCache := txscript.NewTxSigHashes(ourCommitTx)
|
||||
theirKey := pendingReservation.theirContribution.MultiSigKey
|
||||
ourKey := pendingReservation.ourContribution.MultiSigKey
|
||||
witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(),
|
||||
theirKey.SerializeCompressed(), channelValue)
|
||||
witnessScript, _, err := GenFundingPkScript(
|
||||
ourKey.PubKey.SerializeCompressed(),
|
||||
theirKey.PubKey.SerializeCompressed(), channelValue,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.completeChan <- nil
|
||||
@@ -1217,8 +1197,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
req.err <- err
|
||||
req.completeChan <- nil
|
||||
return
|
||||
} else if !sig.Verify(sigHash, theirKey) {
|
||||
req.err <- fmt.Errorf("counterparty's commitment signature is invalid")
|
||||
} else if !sig.Verify(sigHash, theirKey.PubKey) {
|
||||
req.err <- fmt.Errorf("counterparty's commitment signature " +
|
||||
"is invalid")
|
||||
req.completeChan <- nil
|
||||
return
|
||||
}
|
||||
@@ -1235,7 +1216,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
}
|
||||
signDesc := SignDescriptor{
|
||||
WitnessScript: witnessScript,
|
||||
PubKey: ourKey,
|
||||
KeyDesc: ourKey,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: p2wsh,
|
||||
Value: channelValue,
|
||||
@@ -1349,18 +1330,6 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte,
|
||||
return nil
|
||||
}
|
||||
|
||||
// deriveMasterRevocationRoot derives the private key which serves as the master
|
||||
// producer root. This master secret is used as the secret input to a HKDF to
|
||||
// generate revocation secrets based on random, but public data.
|
||||
func (l *LightningWallet) deriveMasterRevocationRoot() (*btcec.PrivateKey, error) {
|
||||
masterElkremRoot, err := l.rootKey.Child(revocationRootIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return masterElkremRoot.ECPrivKey()
|
||||
}
|
||||
|
||||
// DeriveStateHintObfuscator derives the bytes to be used for obfuscating the
|
||||
// state hints from the root to be used for a new channel. The obfuscator is
|
||||
// generated via the following computation:
|
||||
@@ -1437,8 +1406,6 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
|
||||
weightEstimate.AddP2WKHInput()
|
||||
case NestedWitnessPubKey:
|
||||
weightEstimate.AddNestedP2WKHInput()
|
||||
case PubKeyHash:
|
||||
weightEstimate.AddP2PKHInput()
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("Unsupported address type: %v",
|
||||
utxo.AddressType)
|
||||
@@ -1449,7 +1416,9 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
|
||||
weightEstimate.AddP2WSHOutput()
|
||||
|
||||
// Assume that change output is a P2WKH output.
|
||||
// TODO: Handle wallets that generate non-witness change addresses.
|
||||
//
|
||||
// TODO: Handle wallets that generate non-witness change
|
||||
// addresses.
|
||||
weightEstimate.AddP2WKHOutput()
|
||||
|
||||
// The difference between the selected amount and the amount
|
||||
|
||||
Reference in new issue
Block a user