Merge pull request #824 from cfromknecht/multi-chain-coin-type

Configurable CoinType for HD Derivation
This commit is contained in:
Olaoluwa Osuntokun 2018-03-13 18:14:30 -07:00 committed by GitHub
commit e0dc10b1a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 149 additions and 51 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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