BIP32 - defined HD wallet
BIP39 - mnemonic code(phrase), master seed
BIP44 - layer definition m / purpose' / coin_type' / account' / change / address_index
EIP55 - address with check bits
Algorithm see https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Specification_Wallet_structure
Generate with https://github.com/bitcoinjs/bip39
Master seed brings master node.
Write CKDpriv(CKDpriv(CKDpriv(m,3H),2),5) as m/3H/2/5 (for private keys)
Also in some implementations m/44'/0'/0'/0/0 where the apostrophe means hardened key
Write CKDpub(CKDpub(CKDpub(M,3),2),5) as M/3/2/5 (for public keys, only not hardened)
m/account/0 external 1 internal / serial : m/iH/0/k corresponds to the k'th keypair of the external chain of account number i of the HDW derived from master m
m / purpose' / coin_type' / account' / change / address_index
purpose: always 44 indicating this proposal
coin_type: 60 for ETH (more to see registry)
account: accounts
change: 0 for external, 1 for internal
index
var bip39 = require('bip39')
var hdkey = require('ethereumjs-wallet/hdkey')
var util = require('ethereumjs-util')
var mnemonic = bip39.generateMnemonic()
mnemonic
'tackle humble usage bargain side pact umbrella lizard right vapor canal dilemma'
typeof mnemonic
'string'
bip39.validateMnemonic(mnemonic)
true
Conclusion: Mnemonic - repeat and every time a random one, and just a string
var seed = bip39.mnemonicToSeed(mnemonic)
typeof(seed)
'object'
seed
<Buffer e3 b7 22 25 df c8 45 8e 65 34 d1 6a b0 a9 89 1b f3 ea 6e 46 ea 57 bc 01 87 42 9a 03 e0 79 dc 42 eb 0a 78 15 b1 d4 7b ad ae 5a 70 ec a9 39 95 51 a8 ec ... >
console.log(seed)
<Buffer e3 b7 22 25 df c8 45 8e 65 34 d1 6a b0 a9 89 1b f3 ea 6e 46 ea 57 bc 01 87 42 9a 03 e0 79 dc 42 eb 0a 78 15 b1 d4 7b ad ae 5a 70 ec a9 39 95 51 a8 ec ... >
check(seed)
============================== Start Reporting obj ==============================
obj: <Buffer e3 b7 22 25 df c8 45 8e 65 34 d1 6a b0 a9 89 1b f3 ea 6e 46 ea 57 bc 01 87 42 9a 03 e0 79 dc 42 eb 0a 78 15 b1 d4 7b ad ae 5a 70 ec a9 39 95 51 a8 ec ... >
typeof obj: object
obj's own properties: [ '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31',
'32',
'33',
'34',
'35',
'36',
'37',
'38',
'39',
'40',
'41',
'42',
'43',
'44',
'45',
'46',
'47',
'48',
'49',
'50',
'51',
'52',
'53',
'54',
'55',
'56',
'57',
'58',
'59',
'60',
'61',
'62',
'63' ]
obj's prototype's own properties: [ 'constructor',
'asciiSlice',
'base64Slice',
'latin1Slice',
'hexSlice',
'ucs2Slice',
'utf8Slice',
'asciiWrite',
'base64Write',
'latin1Write',
'hexWrite',
'ucs2Write',
'utf8Write',
'parent',
'offset',
'copy',
'toString',
'equals',
'inspect',
'compare',
'indexOf',
'lastIndexOf',
'includes',
'fill',
'write',
'toJSON',
'slice',
'readUIntLE',
'readUIntBE',
'readUInt8',
'readUInt16LE',
'readUInt16BE',
'readUInt32LE',
'readUInt32BE',
'readIntLE',
'readIntBE',
'readInt8',
'readInt16LE',
'readInt16BE',
'readInt32LE',
'readInt32BE',
'readFloatLE',
'readFloatBE',
'readDoubleLE',
'readDoubleBE',
'writeUIntLE',
'writeUIntBE',
'writeUInt8',
'writeUInt16LE',
'writeUInt16BE',
'writeUInt32LE',
'writeUInt32BE',
'writeIntLE',
'writeIntBE',
'writeInt8',
'writeInt16LE',
'writeInt16BE',
'writeInt32LE',
'writeInt32BE',
'writeFloatLE',
'writeFloatBE',
'writeDoubleLE',
'writeDoubleBE',
'swap16',
'swap32',
'swap64',
'toLocaleString' ]
obj's prototype's prototype's own properties: [ 'constructor', 'BYTES_PER_ELEMENT' ]
obj's prototype's prototype's prototype's own properties: [ 'constructor',
'buffer',
'byteLength',
'byteOffset',
'length',
'entries',
'keys',
'values',
'copyWithin',
'every',
'fill',
'forEach',
'includes',
'indexOf',
'lastIndexOf',
'map',
'reverse',
'reduce',
'reduceRight',
'slice',
'some',
'subarray',
'set',
'filter',
'find',
'findIndex',
'sort',
'toLocaleString',
'join',
'toString' ]
obj's prototype's prototype's prototype's prototype's own properties: [ 'constructor',
'__defineGetter__',
'__defineSetter__',
'hasOwnProperty',
'__lookupGetter__',
'__lookupSetter__',
'isPrototypeOf',
'propertyIsEnumerable',
'toString',
'valueOf',
'__proto__',
'toLocaleString' ]
=============================== End Reporting obj ===============================
seed.length
64
console.log(seed[0],seed[63])
227 176
console.log(seed[64])
undefined
seed.toString('hex')
'e3b72225dfc8458e6534d16ab0a9891bf3ea6e46ea57bc0187429a03e079dc42eb0a7815b1d47badae5a70eca9399551a8ecf0cb63db6ff3a28d58f8306cf2b0'
Conclusion: Seed is 64 byte long, that is 864=512 bits, maximum length *
var hdWallet = hdkey.fromMasterSeed(seed)
typeof hdWallet
'object'
check(hdWallet)
============================== Start Reporting obj ==============================
obj: EthereumHDKey {
_hdkey:
HDKey {
versions: { private: 76066276, public: 76067358 },
depth: 0,
index: 0,
_privateKey: <Buffer b0 40 70 5c 12 4d b4 da df 89 cd c5 78 22 d3 d4 45 47 49 30 cd 10 86 c0 df e4 1f 2d 21 d6 00 78>,
_publicKey: <Buffer 02 05 c7 c1 8e 78 f0 cd ca 8d ce b0 4d 3b 00 b6 ad 71 45 aa 47 6a e1 9c 72 39 f9 d4 1f d3 90 3c 9d>,
chainCode: <Buffer 21 c1 89 bd 57 6a e0 a5 9d 1e 54 e8 54 f6 47 73 32 e4 f2 35 dc e1 2c 8c 75 8e 1e a4 4e 73 a2 16>,
_fingerprint: 794267183,
parentFingerprint: 0,
_identifier: <Buffer 2f 57 8e 2f c5 65 ab e9 0f 7a a0 1e 05 31 21 74 6c a8 8f 25> } }
typeof obj: object
obj's own properties: [ '_hdkey' ]
obj's prototype's own properties: [ 'constructor',
'privateExtendedKey',
'publicExtendedKey',
'derivePath',
'deriveChild',
'getWallet' ]
obj's prototype's prototype's own properties: [ 'constructor',
'__defineGetter__',
'__defineSetter__',
'hasOwnProperty',
'__lookupGetter__',
'__lookupSetter__',
'isPrototypeOf',
'propertyIsEnumerable',
'toString',
'valueOf',
'__proto__',
'toLocaleString' ]
=============================== End Reporting obj ===============================
hdWallet.publicExtendedKey()
'xpub661MyMwAqRbcEsvhNKC4mcjMforcppGMUx6MqnV1qGJrjPcroEDJTZjtEMmnR6RfkkfUSdC18Mea3ypkCRkVmCqtJ6DPWsqPxvCdfQqJbwP'
hdWallet.privateExtendedKey()
'xprv9s21ZrQH143K2PrEGHf4QUnd7n28RMYW7jAm3Q5QGvmsrbHiFgu3umRQP836SLngFbGzrS5ib1syUp2w4HLrDjPU2UgbYZQhsuueXUeRMVk'
var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0")
key1
EthereumHDKey {
_hdkey:
HDKey {
versions: { private: 76066276, public: 76067358 },
depth: 5,
index: 0,
_privateKey: <Buffer 34 9e ec d7 fb 7f 56 29 95 8f 44 2d 13 25 72 a1 36 b1 51 64 b4 b9 1d ff 9e ee 21 d2 7c 32 4c 44>,
_publicKey: <Buffer 03 b8 28 a4 eb 65 97 49 af ca a5 f5 0d 2b 95 80 31 1f 5c c0 c7 84 c1 22 b5 18 b8 4f e0 74 f3 f4 ec>,
chainCode: <Buffer a8 d6 71 e3 ae 1e 0c a4 ca 7a 0b ac c2 13 77 4e 49 28 3b d8 31 4b 4c 2b 5c 91 a1 00 bf 5c bb 7c>,
_fingerprint: 3779756055,
parentFingerprint: 1768819565,
_identifier: <Buffer e1 4a 80 17 82 da c1 74 78 9a 91 70 ef 65 d8 f8 e8 74 87 61> } }
console.log(key1)
EthereumHDKey {
_hdkey:
HDKey {
versions: { private: 76066276, public: 76067358 },
depth: 5,
index: 0,
_privateKey: <Buffer 34 9e ec d7 fb 7f 56 29 95 8f 44 2d 13 25 72 a1 36 b1 51 64 b4 b9 1d ff 9e ee 21 d2 7c 32 4c 44>,
_publicKey: <Buffer 03 b8 28 a4 eb 65 97 49 af ca a5 f5 0d 2b 95 80 31 1f 5c c0 c7 84 c1 22 b5 18 b8 4f e0 74 f3 f4 ec>,
chainCode: <Buffer a8 d6 71 e3 ae 1e 0c a4 ca 7a 0b ac c2 13 77 4e 49 28 3b d8 31 4b 4c 2b 5c 91 a1 00 bf 5c bb 7c>,
_fingerprint: 3779756055,
parentFingerprint: 1768819565,
_identifier: <Buffer e1 4a 80 17 82 da c1 74 78 9a 91 70 ef 65 d8 f8 e8 74 87 61> } }
check(key1)
============================== Start Reporting obj ==============================
obj: EthereumHDKey {
_hdkey:
HDKey {
versions: { private: 76066276, public: 76067358 },
depth: 5,
index: 0,
_privateKey: <Buffer 34 9e ec d7 fb 7f 56 29 95 8f 44 2d 13 25 72 a1 36 b1 51 64 b4 b9 1d ff 9e ee 21 d2 7c 32 4c 44>,
_publicKey: <Buffer 03 b8 28 a4 eb 65 97 49 af ca a5 f5 0d 2b 95 80 31 1f 5c c0 c7 84 c1 22 b5 18 b8 4f e0 74 f3 f4 ec>,
chainCode: <Buffer a8 d6 71 e3 ae 1e 0c a4 ca 7a 0b ac c2 13 77 4e 49 28 3b d8 31 4b 4c 2b 5c 91 a1 00 bf 5c bb 7c>,
_fingerprint: 3779756055,
parentFingerprint: 1768819565,
_identifier: <Buffer e1 4a 80 17 82 da c1 74 78 9a 91 70 ef 65 d8 f8 e8 74 87 61> } }
typeof obj: object
obj's own properties: [ '_hdkey' ]
obj's prototype's own properties: [ 'constructor',
'privateExtendedKey',
'publicExtendedKey',
'derivePath',
'deriveChild',
'getWallet' ]
obj's prototype's prototype's own properties: [ 'constructor',
'__defineGetter__',
'__defineSetter__',
'hasOwnProperty',
'__lookupGetter__',
'__lookupSetter__',
'isPrototypeOf',
'propertyIsEnumerable',
'toString',
'valueOf',
'__proto__',
'toLocaleString' ]
=============================== End Reporting obj ===============================
"0x"+key1._hdkey._privateKey.toString('hex')
'0x349eecd7fb7f5629958f442d132572a136b15164b4b91dff9eee21d27c324c44'
"0x"+key1._hdkey.publicKey.toString('hex')
'0x03b828a4eb659749afcaa5f50d2b9580311f5cc0c784c122b518b84fe074f3f4ec'
key1.privateExtendedKey()
'xprvA3Cdie938XHhtuwdKr4JzDh3hvSZQSFb6Z4MsNxKiNzcR3mcH7wxGwTinDEK9rZSj9rbuU9ccVCaFLKzXufxYLgZwYhVXKcinmk2v4TBTZh'
key1.publicExtendedKey()
'xpub6GBz89fvxtr17Q26RsbKMMdnFxH3otySTmyxfmMwGiXbHr6kpfGCpjnCdXCkjT6ezWzkcnuJQbmyMNUFpkh1vp52H5XcEsFkdBCxZrKXRbz'
var address1 = util.pubToAddress(key1._hdkey._publicKey, true)
address1
<Buffer 79 e5 68 75 67 e6 78 c2 74 c6 88 cf a8 66 46 da 51 f0 52 b3>
check(address1)
============================== Start Reporting obj ==============================
obj: <Buffer 79 e5 68 75 67 e6 78 c2 74 c6 88 cf a8 66 46 da 51 f0 52 b3>
typeof obj: object
obj's own properties: [ '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19' ]
obj's prototype's own properties: [ 'constructor',
'asciiSlice',
'base64Slice',
'latin1Slice',
'hexSlice',
'ucs2Slice',
'utf8Slice',
'asciiWrite',
'base64Write',
'latin1Write',
'hexWrite',
'ucs2Write',
'utf8Write',
'parent',
'offset',
'copy',
'toString',
'equals',
'inspect',
'compare',
'indexOf',
'lastIndexOf',
'includes',
'fill',
'write',
'toJSON',
'slice',
'readUIntLE',
'readUIntBE',
'readUInt8',
'readUInt16LE',
'readUInt16BE',
'readUInt32LE',
'readUInt32BE',
'readIntLE',
'readIntBE',
'readInt8',
'readInt16LE',
'readInt16BE',
'readInt32LE',
'readInt32BE',
'readFloatLE',
'readFloatBE',
'readDoubleLE',
'readDoubleBE',
'writeUIntLE',
'writeUIntBE',
'writeUInt8',
'writeUInt16LE',
'writeUInt16BE',
'writeUInt32LE',
'writeUInt32BE',
'writeIntLE',
'writeIntBE',
'writeInt8',
'writeInt16LE',
'writeInt16BE',
'writeInt32LE',
'writeInt32BE',
'writeFloatLE',
'writeFloatBE',
'writeDoubleLE',
'writeDoubleBE',
'swap16',
'swap32',
'swap64',
'toLocaleString' ]
obj's prototype's prototype's own properties: [ 'constructor', 'BYTES_PER_ELEMENT' ]
obj's prototype's prototype's prototype's own properties: [ 'constructor',
'buffer',
'byteLength',
'byteOffset',
'length',
'entries',
'keys',
'values',
'copyWithin',
'every',
'fill',
'forEach',
'includes',
'indexOf',
'lastIndexOf',
'map',
'reverse',
'reduce',
'reduceRight',
'slice',
'some',
'subarray',
'set',
'filter',
'find',
'findIndex',
'sort',
'toLocaleString',
'join',
'toString' ]
obj's prototype's prototype's prototype's prototype's own properties: [ 'constructor',
'__defineGetter__',
'__defineSetter__',
'hasOwnProperty',
'__lookupGetter__',
'__lookupSetter__',
'isPrototypeOf',
'propertyIsEnumerable',
'toString',
'valueOf',
'__proto__',
'toLocaleString' ]
=============================== End Reporting obj ===============================
address1 instanceof Buffer
true
address1.length
20
console.log(address1.toString('hex'))
79e5687567e678c274c688cfa86646da51f052b3
Ethereum address: 40 (hex) characters / 160 bits / 20 bytes
Now go to https://iancoleman.io/bip39/, input mnemonic words and verify everything. Result: (1) seed identical (2) root extended private key identical (3) derived key, public and private key identical (4) address, not checked.
addChecked = util.toChecksumAddress(address1.toString('hex'))
'0x79e5687567E678c274c688cFA86646dA51F052B3'