Merge pull request #824 from cfromknecht/multi-chain-coin-type
Configurable CoinType for HD Derivation
This commit is contained in:
commit
e0dc10b1a5
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
litecoinCfg "github.com/ltcsuite/ltcd/chaincfg"
|
||||
"github.com/roasbeef/btcd/chaincfg"
|
||||
bitcoinCfg "github.com/roasbeef/btcd/chaincfg"
|
||||
@ -16,41 +17,47 @@ var activeNetParams = bitcoinTestNetParams
|
||||
// corresponding RPC port of a daemon running on the particular network.
|
||||
type bitcoinNetParams struct {
|
||||
*bitcoinCfg.Params
|
||||
rpcPort string
|
||||
rpcPort string
|
||||
CoinType uint32
|
||||
}
|
||||
|
||||
// litecoinNetParams couples the p2p parameters of a network with the
|
||||
// corresponding RPC port of a daemon running on the particular network.
|
||||
type litecoinNetParams struct {
|
||||
*litecoinCfg.Params
|
||||
rpcPort string
|
||||
rpcPort string
|
||||
CoinType uint32
|
||||
}
|
||||
|
||||
// bitcoinTestNetParams contains parameters specific to the 3rd version of the
|
||||
// test network.
|
||||
var bitcoinTestNetParams = bitcoinNetParams{
|
||||
Params: &bitcoinCfg.TestNet3Params,
|
||||
rpcPort: "18334",
|
||||
Params: &bitcoinCfg.TestNet3Params,
|
||||
rpcPort: "18334",
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
}
|
||||
|
||||
// bitcoinSimNetParams contains parameters specific to the simulation test
|
||||
// network.
|
||||
var bitcoinSimNetParams = bitcoinNetParams{
|
||||
Params: &bitcoinCfg.SimNetParams,
|
||||
rpcPort: "18556",
|
||||
Params: &bitcoinCfg.SimNetParams,
|
||||
rpcPort: "18556",
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
}
|
||||
|
||||
// liteTestNetParams contains parameters specific to the 4th version of the
|
||||
// test network.
|
||||
var liteTestNetParams = litecoinNetParams{
|
||||
Params: &litecoinCfg.TestNet4Params,
|
||||
rpcPort: "19334",
|
||||
Params: &litecoinCfg.TestNet4Params,
|
||||
rpcPort: "19334",
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
}
|
||||
|
||||
// regTestNetParams contains parameters specific to a local regtest network.
|
||||
var regTestNetParams = bitcoinNetParams{
|
||||
Params: &bitcoinCfg.RegressionNetParams,
|
||||
rpcPort: "18334",
|
||||
Params: &bitcoinCfg.RegressionNetParams,
|
||||
rpcPort: "18334",
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
}
|
||||
|
||||
// applyLitecoinParams applies the relevant chain configuration parameters that
|
||||
@ -91,4 +98,5 @@ func applyLitecoinParams(params *bitcoinNetParams) {
|
||||
params.Checkpoints = checkPoints
|
||||
|
||||
params.rpcPort = liteTestNetParams.rpcPort
|
||||
params.CoinType = liteTestNetParams.CoinType
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
|
||||
DataDir: homeChainConfig.ChainDir,
|
||||
NetParams: activeNetParams.Params,
|
||||
FeeEstimator: cc.feeEstimator,
|
||||
CoinType: activeNetParams.CoinType,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -436,6 +437,10 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
|
||||
cc.signer = wc
|
||||
cc.chainIO = wc
|
||||
|
||||
keyRing := keychain.NewBtcWalletKeyRing(
|
||||
wc.InternalWallet(), activeNetParams.CoinType,
|
||||
)
|
||||
|
||||
// Create, and start the lnwallet, which handles the core payment
|
||||
// channel logic, and exposes control via proxy state machines.
|
||||
walletCfg := lnwallet.Config{
|
||||
@ -444,7 +449,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
|
||||
WalletController: wc,
|
||||
Signer: cc.signer,
|
||||
FeeEstimator: cc.feeEstimator,
|
||||
SecretKeyRing: keychain.NewBtcWalletKeyRing(wc.InternalWallet()),
|
||||
SecretKeyRing: keyRing,
|
||||
ChainIO: cc.chainIO,
|
||||
DefaultConstraints: defaultChannelConstraints,
|
||||
NetParams: *activeNetParams.Params,
|
||||
|
@ -10,15 +10,21 @@ import (
|
||||
"github.com/roasbeef/btcwallet/walletdb"
|
||||
)
|
||||
|
||||
var (
|
||||
// lightningKeyScope is the key scope that will be used within the
|
||||
// waddrmgr to create an HD chain for deriving all of our required
|
||||
// keys.
|
||||
lightningKeyScope = waddrmgr.KeyScope{
|
||||
Purpose: BIP0043Purpose,
|
||||
Coin: 0,
|
||||
}
|
||||
const (
|
||||
// CoinTypeBitcoin specifies the BIP44 coin type for Bitcoin key
|
||||
// derivation.
|
||||
CoinTypeBitcoin uint32 = 0
|
||||
|
||||
// CoinTypeTestnet specifies the BIP44 coin type for all testnet key
|
||||
// derivation.
|
||||
CoinTypeTestnet = 1
|
||||
|
||||
// CoinTypeLitecoin specifies the BIP44 coin type for Litecoin key
|
||||
// derivation.
|
||||
CoinTypeLitecoin = 2
|
||||
)
|
||||
|
||||
var (
|
||||
// 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.
|
||||
@ -44,6 +50,10 @@ type BtcWalletKeyRing struct {
|
||||
// transactions in order to derive addresses and lookup relevant keys
|
||||
wallet *wallet.Wallet
|
||||
|
||||
// chainKeyScope defines the purpose and coin type to be used when generating
|
||||
// keys for this keyring.
|
||||
chainKeyScope waddrmgr.KeyScope
|
||||
|
||||
// lightningScope is a pointer to the scope that we'll be using as a
|
||||
// sub key manager to derive all the keys that we require.
|
||||
lightningScope *waddrmgr.ScopedKeyManager
|
||||
@ -54,9 +64,18 @@ type BtcWalletKeyRing struct {
|
||||
//
|
||||
// NOTE: The passed waddrmgr.Manager MUST be unlocked in order for the keychain
|
||||
// to function.
|
||||
func NewBtcWalletKeyRing(w *wallet.Wallet) SecretKeyRing {
|
||||
func NewBtcWalletKeyRing(w *wallet.Wallet, coinType uint32) SecretKeyRing {
|
||||
// Construct the key scope that will be used within the waddrmgr to
|
||||
// create an HD chain for deriving all of our required keys. A different
|
||||
// scope is used for each specific coin type.
|
||||
chainKeyScope := waddrmgr.KeyScope{
|
||||
Purpose: BIP0043Purpose,
|
||||
Coin: coinType,
|
||||
}
|
||||
|
||||
return &BtcWalletKeyRing{
|
||||
wallet: w,
|
||||
wallet: w,
|
||||
chainKeyScope: chainKeyScope,
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,9 +99,7 @@ func (b *BtcWalletKeyRing) keyScope() (*waddrmgr.ScopedKeyManager, error) {
|
||||
|
||||
// If the manager is indeed unlocked, then we'll fetch the scope, cache
|
||||
// it, and return to the caller.
|
||||
lnScope, err := b.wallet.Manager.FetchScopedKeyManager(
|
||||
lightningKeyScope,
|
||||
)
|
||||
lnScope, err := b.wallet.Manager.FetchScopedKeyManager(b.chainKeyScope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/roasbeef/btcd/chaincfg"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcwallet/waddrmgr"
|
||||
"github.com/roasbeef/btcwallet/wallet"
|
||||
"github.com/roasbeef/btcwallet/walletdb"
|
||||
|
||||
@ -36,7 +37,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func createTestBtcWallet() (func(), *wallet.Wallet, error) {
|
||||
func createTestBtcWallet(coinType uint32) (func(), *wallet.Wallet, error) {
|
||||
tempDir, err := ioutil.TempDir("", "keyring-lnwallet")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -54,16 +55,23 @@ func createTestBtcWallet() (func(), *wallet.Wallet, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// We'll now ensure that the KeyScope: (201, 1) exists within the
|
||||
// internal waddrmgr. We'll need this in order to properly generate the
|
||||
// keys required for signing various contracts.
|
||||
_, err = baseWallet.Manager.FetchScopedKeyManager(lightningKeyScope)
|
||||
// Construct the key scope required to derive keys for the chose
|
||||
// coinType.
|
||||
chainKeyScope := waddrmgr.KeyScope{
|
||||
Purpose: BIP0043Purpose,
|
||||
Coin: coinType,
|
||||
}
|
||||
|
||||
// We'll now ensure that the KeyScope: (1017, coinType) exists within
|
||||
// the internal waddrmgr. We'll need this in order to properly generate
|
||||
// the keys required for signing various contracts.
|
||||
_, err = baseWallet.Manager.FetchScopedKeyManager(chainKeyScope)
|
||||
if err != nil {
|
||||
err := walletdb.Update(baseWallet.Database(), func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
|
||||
_, err := baseWallet.Manager.NewScopedKeyManager(
|
||||
addrmgrNs, lightningKeyScope, lightningAddrSchema,
|
||||
addrmgrNs, chainKeyScope, lightningAddrSchema,
|
||||
)
|
||||
return err
|
||||
})
|
||||
@ -93,15 +101,41 @@ func TestKeyRingDerivation(t *testing.T) {
|
||||
|
||||
keyRingImplementations := []keyRingConstructor{
|
||||
func() (string, func(), KeyRing, error) {
|
||||
cleanUp, wallet, err := createTestBtcWallet()
|
||||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeBitcoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet)
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeBitcoin)
|
||||
|
||||
return "btcwallet", cleanUp, keyRing, nil
|
||||
},
|
||||
func() (string, func(), KeyRing, error) {
|
||||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeLitecoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeLitecoin)
|
||||
|
||||
return "ltcwallet", cleanUp, keyRing, nil
|
||||
},
|
||||
func() (string, func(), KeyRing, error) {
|
||||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeTestnet,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeTestnet)
|
||||
|
||||
return "testwallet", cleanUp, keyRing, nil
|
||||
},
|
||||
}
|
||||
|
||||
// For each implementation constructor registered above, we'll execute
|
||||
@ -182,15 +216,41 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
||||
|
||||
secretKeyRingImplementations := []secretKeyRingConstructor{
|
||||
func() (string, func(), SecretKeyRing, error) {
|
||||
cleanUp, wallet, err := createTestBtcWallet()
|
||||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeBitcoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet)
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeBitcoin)
|
||||
|
||||
return "btcwallet", cleanUp, keyRing, nil
|
||||
},
|
||||
func() (string, func(), SecretKeyRing, error) {
|
||||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeLitecoin,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeLitecoin)
|
||||
|
||||
return "ltcwallet", cleanUp, keyRing, nil
|
||||
},
|
||||
func() (string, func(), SecretKeyRing, error) {
|
||||
cleanUp, wallet, err := createTestBtcWallet(
|
||||
CoinTypeTestnet,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
|
||||
keyRing := NewBtcWalletKeyRing(wallet, CoinTypeTestnet)
|
||||
|
||||
return "testwallet", cleanUp, keyRing, nil
|
||||
},
|
||||
}
|
||||
|
||||
// For each implementation constructor registered above, we'll execute
|
||||
|
@ -32,14 +32,6 @@ var (
|
||||
// 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.
|
||||
@ -65,6 +57,8 @@ type BtcWallet struct {
|
||||
|
||||
netParams *chaincfg.Params
|
||||
|
||||
chainKeyScope waddrmgr.KeyScope
|
||||
|
||||
// utxoCache is a cache used to speed up repeated calls to
|
||||
// FetchInputInfo.
|
||||
utxoCache map[wire.OutPoint]*wire.TxOut
|
||||
@ -81,6 +75,12 @@ func New(cfg Config) (*BtcWallet, error) {
|
||||
// Ensure the wallet exists or create it when the create flag is set.
|
||||
netDir := NetworkDir(cfg.DataDir, cfg.NetParams)
|
||||
|
||||
// Create the key scope for the coin type being managed by this wallet.
|
||||
chainKeyScope := waddrmgr.KeyScope{
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
Coin: cfg.CoinType,
|
||||
}
|
||||
|
||||
var pubPass []byte
|
||||
if cfg.PublicPass == nil {
|
||||
pubPass = defaultPubPassphrase
|
||||
@ -114,12 +114,13 @@ func New(cfg Config) (*BtcWallet, error) {
|
||||
}
|
||||
|
||||
return &BtcWallet{
|
||||
cfg: &cfg,
|
||||
wallet: wallet,
|
||||
db: wallet.Database(),
|
||||
chain: cfg.ChainSource,
|
||||
netParams: cfg.NetParams,
|
||||
utxoCache: make(map[wire.OutPoint]*wire.TxOut),
|
||||
cfg: &cfg,
|
||||
wallet: wallet,
|
||||
db: wallet.Database(),
|
||||
chain: cfg.ChainSource,
|
||||
netParams: cfg.NetParams,
|
||||
chainKeyScope: chainKeyScope,
|
||||
utxoCache: make(map[wire.OutPoint]*wire.TxOut),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -165,7 +166,7 @@ func (b *BtcWallet) Start() error {
|
||||
// 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)
|
||||
_, err := b.wallet.Manager.FetchScopedKeyManager(b.chainKeyScope)
|
||||
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
|
||||
@ -174,7 +175,7 @@ func (b *BtcWallet) Start() error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
|
||||
_, err := b.wallet.Manager.NewScopedKeyManager(
|
||||
addrmgrNs, lightningKeyScope, lightningAddrSchema,
|
||||
addrmgrNs, b.chainKeyScope, lightningAddrSchema,
|
||||
)
|
||||
return err
|
||||
})
|
||||
|
@ -75,6 +75,9 @@ type Config struct {
|
||||
|
||||
// NetParams is the net parameters for the target chain.
|
||||
NetParams *chaincfg.Params
|
||||
|
||||
// CoinType specifies the BIP 44 coin type to be used for derivation.
|
||||
CoinType uint32
|
||||
}
|
||||
|
||||
// NetworkDir returns the directory name of a network directory to hold wallet
|
||||
|
@ -83,7 +83,7 @@ func (b *BtcWallet) fetchPrivKey(keyDesc *keychain.KeyDescriptor) (*btcec.Privat
|
||||
if !keyDesc.KeyLocator.IsEmpty() {
|
||||
// We'll assume the special lightning key scope in this case.
|
||||
scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager(
|
||||
lightningKeyScope,
|
||||
b.chainKeyScope,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2149,6 +2149,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
NetParams: netParams,
|
||||
ChainSource: aliceClient,
|
||||
FeeEstimator: feeEstimator,
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
}
|
||||
aliceWalletController, err = walletDriver.New(aliceWalletConfig)
|
||||
if err != nil {
|
||||
@ -2157,6 +2158,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
aliceSigner = aliceWalletController.(*btcwallet.BtcWallet)
|
||||
aliceKeyRing = keychain.NewBtcWalletKeyRing(
|
||||
aliceWalletController.(*btcwallet.BtcWallet).InternalWallet(),
|
||||
keychain.CoinTypeTestnet,
|
||||
)
|
||||
|
||||
bobWalletConfig := &btcwallet.Config{
|
||||
@ -2166,6 +2168,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
NetParams: netParams,
|
||||
ChainSource: bobClient,
|
||||
FeeEstimator: feeEstimator,
|
||||
CoinType: keychain.CoinTypeTestnet,
|
||||
}
|
||||
bobWalletController, err = walletDriver.New(bobWalletConfig)
|
||||
if err != nil {
|
||||
@ -2174,6 +2177,7 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
||||
bobSigner = bobWalletController.(*btcwallet.BtcWallet)
|
||||
bobKeyRing = keychain.NewBtcWalletKeyRing(
|
||||
bobWalletController.(*btcwallet.BtcWallet).InternalWallet(),
|
||||
keychain.CoinTypeTestnet,
|
||||
)
|
||||
bio = bobWalletController.(*btcwallet.BtcWallet)
|
||||
default:
|
||||
|
Loading…
Reference in New Issue
Block a user