mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 14:45:23 +01:00
lnwallet: modify the way we derive revocation roots to be deterministic
In this commit, we modify the way we generate the secrets for revocation roots to be fully deterministic. Rather than use a special key and derive all sub-roots from that (mixing in some “salts”), we’ll use the proper keychain.KeyFamily instead. This ensures that given a static description of the channel, we’re able to re-derive our revocation root properly.
This commit is contained in:
parent
a41f00e2d6
commit
5b063a0691
@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
@ -1236,33 +1235,6 @@ func DeriveRevocationPrivKey(revokeBasePriv *btcec.PrivateKey,
|
||||
return priv
|
||||
}
|
||||
|
||||
// DeriveRevocationRoot derives an root unique to a channel given the
|
||||
// derivation root, and the blockhash that the funding process began at and the
|
||||
// remote node's identity public key. The seed is derived using the HKDF[1][2]
|
||||
// instantiated with sha-256. With this schema, once we know the block hash of
|
||||
// the funding transaction, and who we funded the channel with, we can
|
||||
// reconstruct all of our revocation state.
|
||||
//
|
||||
// [1]: https://eprint.iacr.org/2010/264.pdf
|
||||
// [2]: https://tools.ietf.org/html/rfc5869
|
||||
func DeriveRevocationRoot(derivationRoot *btcec.PrivateKey,
|
||||
blockSalt chainhash.Hash, nodePubKey *btcec.PublicKey) chainhash.Hash {
|
||||
|
||||
secret := derivationRoot.Serialize()
|
||||
salt := blockSalt[:]
|
||||
info := nodePubKey.SerializeCompressed()
|
||||
|
||||
seedReader := hkdf.New(sha256.New, secret, salt, info)
|
||||
|
||||
// It's safe to ignore the error her as we know for sure that we won't
|
||||
// be draining the HKDF past its available entropy horizon.
|
||||
// TODO(roasbeef): revisit...
|
||||
var root chainhash.Hash
|
||||
seedReader.Read(root[:])
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
// SetStateNumHint encodes the current state number within the passed
|
||||
// commitment transaction by re-purposing the locktime and sequence fields in
|
||||
// the commitment transaction to encode the obfuscated state number. The state
|
||||
|
@ -541,30 +541,31 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
||||
}
|
||||
|
||||
// With the above keys created, we'll also need to initialization our
|
||||
// initial revocation tree state. In order to do so in a deterministic
|
||||
// manner (for recovery purposes), we'll use the current block hash
|
||||
// along with the identity public key of the node we're creating the
|
||||
// channel with. In the event of a recovery, given these two items and
|
||||
// the initialize wallet HD seed, we can derive all of our revocation
|
||||
// secrets.
|
||||
masterElkremRoot, err := l.deriveMasterRevocationRoot()
|
||||
// initial revocation tree state.
|
||||
nextRevocationKeyDesc, err := l.DeriveNextKey(
|
||||
keychain.KeyFamilyRevocationRoot,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
bestHash, _, err := l.Cfg.ChainIO.GetBestBlock()
|
||||
revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
revocationRoot := DeriveRevocationRoot(masterElkremRoot, *bestHash,
|
||||
req.nodeID)
|
||||
|
||||
// Once we have the root, we can then generate our shachain producer
|
||||
// and from that generate the per-commitment point.
|
||||
producer := shachain.NewRevocationProducer(revocationRoot)
|
||||
revRoot, err := chainhash.NewHash(revocationRoot.Serialize())
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.resp <- nil
|
||||
return
|
||||
}
|
||||
producer := shachain.NewRevocationProducer(*revRoot)
|
||||
firstPreimage, err := producer.AtIndex(0)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
@ -1329,18 +1330,6 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte,
|
||||
return nil
|
||||
}
|
||||
|
||||
// deriveMasterRevocationRoot derives the private key which serves as the master
|
||||
// producer root. This master secret is used as the secret input to a HKDF to
|
||||
// generate revocation secrets based on random, but public data.
|
||||
func (l *LightningWallet) deriveMasterRevocationRoot() (*btcec.PrivateKey, error) {
|
||||
masterElkremRoot, err := l.rootKey.Child(revocationRootIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return masterElkremRoot.ECPrivKey()
|
||||
}
|
||||
|
||||
// DeriveStateHintObfuscator derives the bytes to be used for obfuscating the
|
||||
// state hints from the root to be used for a new channel. The obfuscator is
|
||||
// generated via the following computation:
|
||||
@ -1417,8 +1406,6 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
|
||||
weightEstimate.AddP2WKHInput()
|
||||
case NestedWitnessPubKey:
|
||||
weightEstimate.AddNestedP2WKHInput()
|
||||
case PubKeyHash:
|
||||
weightEstimate.AddP2PKHInput()
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("Unsupported address type: %v",
|
||||
utxo.AddressType)
|
||||
@ -1429,7 +1416,9 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
|
||||
weightEstimate.AddP2WSHOutput()
|
||||
|
||||
// Assume that change output is a P2WKH output.
|
||||
// TODO: Handle wallets that generate non-witness change addresses.
|
||||
//
|
||||
// TODO: Handle wallets that generate non-witness change
|
||||
// addresses.
|
||||
weightEstimate.AddP2WKHOutput()
|
||||
|
||||
// The difference between the selected amount and the amount
|
||||
|
Loading…
Reference in New Issue
Block a user