From 55da4cc547b96352e8f08997bac65e46c42d6988 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 16:31:01 -0700 Subject: [PATCH 1/8] chainparams: add CoinType to parameters for HD derivation --- chainparams.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/chainparams.go b/chainparams.go index 0c7fd348..6eb127b3 100644 --- a/chainparams.go +++ b/chainparams.go @@ -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 } From 2447f3097f9958455af73fd40aecde1a81f687b0 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 16:31:31 -0700 Subject: [PATCH 2/8] chainregistry: init secret key ring wth CoinType from params --- chainregistry.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chainregistry.go b/chainregistry.go index ae35a901..38969ec0 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -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, From cb7f34895c164cde330db7577cca629934524298 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 16:31:56 -0700 Subject: [PATCH 3/8] keychain/btwallet: support coin type configuration This commit allows for secret keyrings to be initialized with a specific coin type, which allows us to use different derivation paths for bitcion and litecoin. It also provide default constants for Bitcion and Litecoin BIP 44 coin types. --- keychain/btcwallet.go | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/keychain/btcwallet.go b/keychain/btcwallet.go index 2d9f2d8b..8826e495 100644 --- a/keychain/btcwallet.go +++ b/keychain/btcwallet.go @@ -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 } From 99a2ce00d65ee52b4c38d22b2b03911d74620560 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 16:38:23 -0700 Subject: [PATCH 4/8] keychain/interface_test: test btc and ltc key derivation --- keychain/interface_test.go | 80 +++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/keychain/interface_test.go b/keychain/interface_test.go index 8d0cf247..57f56a1b 100644 --- a/keychain/interface_test.go +++ b/keychain/interface_test.go @@ -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 From e99243d86bddc4e369dc0b131a3c356aae0bcc70 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 17:32:37 -0700 Subject: [PATCH 5/8] lnwallet/interface_test: inti btcwallet with test coin type --- lnwallet/interface_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index ec4a9f7a..275b5e65 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -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: From e760963eaddc30a1701eb18e87b85caeb775719d Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 17:33:11 -0700 Subject: [PATCH 6/8] lnwallet/btcwallet/btcwallet: use coin type in key scope --- lnwallet/btcwallet/btcwallet.go | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index ee2674f0..c8601dfc 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -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 }) From fbef81553090d04abead60d8006eae53e6af6192 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 17:33:55 -0700 Subject: [PATCH 7/8] lnwallet/btcwallet/config: add CoinType to configuration --- lnwallet/btcwallet/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lnwallet/btcwallet/config.go b/lnwallet/btcwallet/config.go index 9d6bd5a5..6096c774 100644 --- a/lnwallet/btcwallet/config.go +++ b/lnwallet/btcwallet/config.go @@ -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 From ca7992e8dde1407dae020b09635eb8dd502353b9 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 12 Mar 2018 19:20:07 -0700 Subject: [PATCH 8/8] lnwallet/btcwallet/singer: use chainKeyScope to fetch scoped mgr --- lnwallet/btcwallet/signer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnwallet/btcwallet/signer.go b/lnwallet/btcwallet/signer.go index b3c92d2a..a85f2472 100644 --- a/lnwallet/btcwallet/signer.go +++ b/lnwallet/btcwallet/signer.go @@ -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