deriveChild method
Derives a direct child wallet with the specified index.
Use index >= 2^31 for hardened derivation.
Implementation
HDWallet deriveChild(int index) {
if (index < 0) {
throw ArgumentError('Child index must be non-negative');
}
final isHardened = index >= (1 << 31);
final data = <int>[];
if (isHardened) {
// Hardened derivation: use private key
data.add(0x00);
data.addAll(privateKey);
} else {
// Non-hardened derivation: use public key
data.addAll(publicKey);
}
// Add index as big-endian 32-bit integer
data.addAll(_intToBytes(index, 4));
// BIP-32: Generate child key material using HMAC-SHA512
final hmac = HmacSha512.compute(chainCode, Uint8List.fromList(data));
final childPrivateKeyBytes = Uint8List.sublistView(hmac, 0, 32);
final childChainCode = Uint8List.sublistView(hmac, 32, 64);
// Validate child private key
final childPrivateKeyInt = _bytesToBigInt(childPrivateKeyBytes);
if (childPrivateKeyInt >= _secp256k1Order) {
throw StateError('Invalid child private key generated');
}
// Calculate final child private key
final parentPrivateKeyInt = _bytesToBigInt(privateKey);
final finalChildPrivateKeyInt =
(childPrivateKeyInt + parentPrivateKeyInt) % _secp256k1Order;
if (finalChildPrivateKeyInt == BigInt.zero) {
throw StateError('Invalid child private key (zero)');
}
final finalChildPrivateKey = _bigIntToBytes(finalChildPrivateKeyInt, 32);
final childPublicKey =
Secp256k1.getPublicKey(finalChildPrivateKey, compressed: true);
// BIP-32: Parent fingerprint = first 4 bytes of HASH160(parent public key)
// HASH160 = RIPEMD160(SHA256(data))
final parentHash160 = Ripemd160.hash160(publicKey);
final parentFingerprint = Uint8List.sublistView(parentHash160, 0, 4);
// Build child path
final childPath = path == 'm'
? 'm/${isHardened ? "${index - (1 << 31)}'" : index.toString()}'
: '$path/${isHardened ? "${index - (1 << 31)}'" : index.toString()}';
return HDWallet._(
privateKey: finalChildPrivateKey,
publicKey: childPublicKey,
chainCode: childChainCode,
depth: depth + 1,
path: childPath,
index: index,
parentFingerprint: parentFingerprint,
);
}