fix: use standard BIP-44 coin type 60 for key derivation paths

This commit is contained in:
Zach Kelling
2026-02-13 15:12:31 -08:00
parent 5505743592
commit d986eda263
+18 -18
View File
@@ -400,7 +400,7 @@ func (vk *ValidatorKey) CChainAddrHex() string {
}
// DeriveValidatorsFromMnemonic derives N validator keys from a BIP39 mnemonic.
// Each validator uses BIP44 path m/44'/9000'/0'/0/{index} for the EC key.
// Each validator uses BIP44 path m/44'/60'/0'/0/{index} for the EC key.
// TLS staking certs and BLS keys are generated fresh (not deterministic from mnemonic).
// This is designed for runtime use - no files are written to disk.
func DeriveValidatorsFromMnemonic(mnemonic string, count int) ([]*ValidatorKey, error) {
@@ -426,7 +426,7 @@ func DeriveValidatorsFromMnemonic(mnemonic string, count int) ([]*ValidatorKey,
func DeriveValidatorFromMnemonic(mnemonic string, accountIndex uint32) (*ValidatorKey, error) {
vk := &ValidatorKey{}
// 1. Derive EC key from mnemonic using BIP44 path m/44'/9000'/0'/0/{index}
// 1. Derive EC key from mnemonic using BIP44 path m/44'/60'/0'/0/{index}
ecKeyBytes, err := deriveMnemonicKey(mnemonic, accountIndex)
if err != nil {
return nil, fmt.Errorf("failed to derive EC key: %w", err)
@@ -443,7 +443,7 @@ func DeriveValidatorFromMnemonic(mnemonic string, accountIndex uint32) (*Validat
vk.CChainAddr = pubkeyToAddress(pubKey.ToECDSA())
// 2. Derive TLS staking cert deterministically from mnemonic
// Use a separate derivation path: m/44'/9000'/1'/0/{index} for TLS keys
// Use a separate derivation path: m/44'/60'/1'/0/{index} for TLS keys
tlsKeyBytes, err := deriveMnemonicKeyForPath(mnemonic, 1, accountIndex) // account=1 for TLS
if err != nil {
return nil, fmt.Errorf("failed to derive TLS key seed: %w", err)
@@ -474,7 +474,7 @@ func DeriveValidatorFromMnemonic(mnemonic string, accountIndex uint32) (*Validat
vk.NodeID = ids.NodeIDFromCert(stakingCert)
// 3. Derive BLS signer key deterministically from mnemonic
// Use a separate derivation path: m/44'/9000'/2'/0/{index} for BLS keys
// Use a separate derivation path: m/44'/60'/2'/0/{index} for BLS keys
blsSeed, err := deriveMnemonicKeyForPath(mnemonic, 2, accountIndex) // account=2 for BLS
if err != nil {
return nil, fmt.Errorf("failed to derive BLS key seed: %w", err)
@@ -525,7 +525,7 @@ func deriveP256Key(seed []byte) (*ecdsa.PrivateKey, error) {
return priv, nil
}
// deriveMnemonicKeyForPath derives a key using BIP44 path m/44'/9000'/{account}'/0/{index}
// deriveMnemonicKeyForPath derives a key using BIP44 path m/44'/60'/{account}'/0/{index}
func deriveMnemonicKeyForPath(mnemonic string, account, index uint32) ([]byte, error) {
if !bip39.IsMnemonicValid(mnemonic) {
return nil, fmt.Errorf("invalid mnemonic phrase")
@@ -538,32 +538,32 @@ func deriveMnemonicKeyForPath(mnemonic string, account, index uint32) ([]byte, e
return nil, fmt.Errorf("failed to create master key: %w", err)
}
// BIP-44 path: m/44'/9000'/{account}'/0/{index}
// BIP-44 path: m/44'/60'/{account}'/0/{index}
// m/44' (purpose)
key, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 44)
if err != nil {
return nil, fmt.Errorf("failed to derive purpose: %w", err)
}
// m/44'/9000' (coin type for LUX)
// m/44'/60' (coin type for LUX)
key, err = key.NewChildKey(bip32.FirstHardenedChild + LUXCoinType)
if err != nil {
return nil, fmt.Errorf("failed to derive coin type: %w", err)
}
// m/44'/9000'/{account}' (account - 0=EC, 1=TLS, 2=BLS)
// m/44'/60'/{account}' (account - 0=EC, 1=TLS, 2=BLS)
key, err = key.NewChildKey(bip32.FirstHardenedChild + account)
if err != nil {
return nil, fmt.Errorf("failed to derive account: %w", err)
}
// m/44'/9000'/{account}'/0 (change)
// m/44'/60'/{account}'/0 (change)
key, err = key.NewChildKey(0)
if err != nil {
return nil, fmt.Errorf("failed to derive change: %w", err)
}
// m/44'/9000'/{account}'/0/{index} (address index)
// m/44'/60'/{account}'/0/{index} (address index)
key, err = key.NewChildKey(index)
if err != nil {
return nil, fmt.Errorf("failed to derive address index: %w", err)
@@ -572,10 +572,10 @@ func deriveMnemonicKeyForPath(mnemonic string, account, index uint32) ([]byte, e
return key.Key, nil
}
// LUXCoinType is the BIP-44 coin type for LUX (9000')
const LUXCoinType = 9000
// LUXCoinType is the BIP-44 coin type for LUX (60' = standard Ethereum)
const LUXCoinType = 60
// deriveMnemonicKey derives an EC private key from mnemonic using BIP44 path m/44'/9000'/0'/0/{index}
// deriveMnemonicKey derives an EC private key from mnemonic using BIP44 path m/44'/60'/0'/0/{index}
func deriveMnemonicKey(mnemonic string, accountIndex uint32) ([]byte, error) {
if !bip39.IsMnemonicValid(mnemonic) {
return nil, fmt.Errorf("invalid mnemonic phrase")
@@ -588,32 +588,32 @@ func deriveMnemonicKey(mnemonic string, accountIndex uint32) ([]byte, error) {
return nil, fmt.Errorf("failed to create master key: %w", err)
}
// BIP-44 path: m/44'/9000'/0'/0/{accountIndex}
// BIP-44 path: m/44'/60'/0'/0/{accountIndex}
// m/44' (purpose)
key, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 44)
if err != nil {
return nil, fmt.Errorf("failed to derive purpose: %w", err)
}
// m/44'/9000' (coin type for LUX)
// m/44'/60' (coin type for LUX)
key, err = key.NewChildKey(bip32.FirstHardenedChild + LUXCoinType)
if err != nil {
return nil, fmt.Errorf("failed to derive coin type: %w", err)
}
// m/44'/9000'/0' (account)
// m/44'/60'/0' (account)
key, err = key.NewChildKey(bip32.FirstHardenedChild + 0)
if err != nil {
return nil, fmt.Errorf("failed to derive account: %w", err)
}
// m/44'/9000'/0'/0 (change)
// m/44'/60'/0'/0 (change)
key, err = key.NewChildKey(0)
if err != nil {
return nil, fmt.Errorf("failed to derive change: %w", err)
}
// m/44'/9000'/0'/0/{accountIndex} (address index)
// m/44'/60'/0'/0/{accountIndex} (address index)
key, err = key.NewChildKey(accountIndex)
if err != nil {
return nil, fmt.Errorf("failed to derive address index: %w", err)