Bitcoin Forum
May 03, 2024, 04:01:11 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Javascript HD Bitcoin wallet project start-up  (Read 164 times)
Bitpocket (OP)
Newbie
*
Offline Offline

Activity: 3
Merit: 0


View Profile
September 20, 2019, 10:16:13 PM
Last edit: September 21, 2019, 02:08:14 PM by Bitpocket
 #1

Greetings.

I'am developing a crypto wallet in Javascript and the first coin that I want to have in my wallet is bitcoin.

My code is working, I do generate an address that is 25 byte long.

How I proceed;

In NodeJS

Create a mnemonic with BIP39
Use it has a seed.

Then I use a npm package https://www.npmjs.com/package/hdkey. Its a lib to produce a BIP32 key pair that I have to look more into.

To produce the master keys.

I then create public keys from the master key

Hash it once and passing RIPMD160

I add the prefix.
 Hash twice to get the checksum and add the checksum at the end of prefixed address

Then I pass it in base58 encoder.

I end up with an adress that looks good but when I try to validate my adress I get a bad digest error msg.

It's been 3 days now and I can't figure what is not working.

Some validation tool are reporting bad digest or bad checksum but how can I validate?


Please help.

Here is my code;


Code:
  const bip39 = require('bip39');
        const hdkey = require('hdkey');
        const createHash = require('create-hash/browser');
        const Buffer = require('buffer').Buffer;
        const bs58check = require('bs58');

        /// BIP39 SEED
        /// https://www.npmjs.com/package/bip39

        //const mnemonic = bip39.generateMnemonic();
        const mnemonic ="tag observe slush wheel verb uncover evolve old refuse shoot swarm camera";
        const seed =  bip39.mnemonicToSeedSync(mnemonic);

        //console.log(bip39.mnemonicToSeed(mnemonic));
       // const seed = bip39.mnemonicToSeed('basket actual').then(bytes => bytes.toString('hex'))

        console.log('Mnemonic : ',mnemonic);

        console.log('Seed : ',seed.toString('hex'));
  
        /// HDKEY
        /// https://www.npmjs.com/package/hdkey

        const root = hdkey.fromMasterSeed(new Buffer(seed, 'hex'));
        const masterPrivateKey = root.privateKey.toString('hex');

        console.log('Master Private : ' , masterPrivateKey);

        /// COIN REGISTRATION
        /// https://github.com/satoshilabs/docs/blob/bdd9fd92386827bfa031a88c10e3e6fd834d56e3/slips/slip-0044.rst

        /// BITCOIN ADRESS TYPE
        const addrnode = root.derive("m/44'/0'/0'/0/0");  

       // console.log('hdkey pbx : ' ,root.publicExtendedKey);

        /// Private and Public Key
        const rawPbK = Buffer.from(addrnode._publicKey);
        const rawPvK = addrnode._privateKey;

        /// Hashing the raw publiv key

        const hash1pbk = createHash('sha256').update(rawPbK).digest();
        const hash1rmd = createHash('rmd160').update(hash1pbk).digest();

        console.log('Raw Public Key : ' ,rawPbK.toString('hex'));

        // Space allocation
        var pubAddr = Buffer.allocUnsafe(21);

        // Adding testnet prefix
        pubAddr.writeUInt8(0x00, 0);

        // Copying the public adress tafter the prefix?
        hash1rmd.copy(pubAddr, 1);

        console.log('Address Buffer : ' ,pubAddr.toString('hex'));

        const hash2 = createHash('sha256').update(hash1rmd).digest();
        const hash3 = createHash('sha256').update(hash2).digest();

        console.log('Hash 3 : ' ,hash3.toString('hex'));

         // 4 first byte of the last hash for checksum
        const chksum = hash3.slice(0,4);

        console.log('4 first bytes Checksum : ' ,chksum);
      
        /// Adding checksum to prefix Adress
        const address = Buffer.concat([pubAddr,chksum]);

        console.log('Public Adress : ' ,address);

        /// BASE 58
        /// https://www.npmjs.com/package/base-58

        const pbAddr = bs58check.encode(address);

        console.log('BASE58 ADDRESS : ' ,pbAddr);



This is my output in the console;

Mnemonic :  tag observe slush wheel verb uncover evolve old refuse shoot swarm camera
Seed :  f550bc2bc355998e74cd2116a104fdd8a1fe0d0b7e64826b5d14a470e6d1e2be2b991fd2607cd8d fc3ca435f351b346586faac8b20e945326f823c3ef5c60859
Master Private :  14ab6a6abed95d30420a1b9eb9ab7f6f7447b5255ca3d46c099d7b325fa7f1f3
Raw Public Key :  03b30574120027d4f51edb1cfc323d197aba3fe5e86c85cbbfc8946a4dbf55fa2b
Address Buffer :  003683f0d40e5cfb112e9055f792204e58614815f4
Hash 3 :  1b80d052329097dfb65beb34b2cf9fb5f00bd558ad3379e3428536ded1940cc3
4 first bytes Checksum :  1b80d052
Public Adress :  003683f0d40e5cfb112e9055f792204e58614815f41b80d052
BASE58 ADDRESS :  15yFYmBx7cWYhsjNh7EMDM5tw2JnzBNYtZ

Thank you for your time!
Even in the event that an attacker gains more than 50% of the network's computational power, only transactions sent by the attacker could be reversed or double-spent. The network would not be destroyed.
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
CounterEntropy
Full Member
***
Offline Offline

Activity: 214
Merit: 277


View Profile
September 21, 2019, 12:21:43 AM
 #2

JS based HD wallet generation is supported by Coinb.in. You may look into their code base for reference - https://github.com/OutCast3k/coinbin/.
pooya87
Legendary
*
Offline Offline

Activity: 3444
Merit: 10535



View Profile
September 21, 2019, 05:05:22 AM
Merited by hugeblack (1)
 #3

your code is pretty bad because you are doing a million different things inside one function. it is near impossible and extremely time consuming for anyone to find out where the problem is!
there is a very single solution for that though. you must simply split your function apart into multiple functions each doing a different job, and then write tests for each function individually and see if they are working as they should.

for example:
- 1 function should be calculating checksum of a given byte array and return 4 bytes. your test would be passing an arbitrary byte to this and seeing if it returns correct 4 bytes after doing double_sha256 on it.
- 1 function should encode a given byte array to base58
- another function should encode a given byte array to base58 with checksum using the previous 2 functions
- 1 function should convert a public key to address
- and so on...

.
.BLACKJACK ♠ FUN.
█████████
██████████████
████████████
█████████████████
████████████████▄▄
░█████████████▀░▀▀
██████████████████
░██████████████
████████████████
░██████████████
████████████
███████████████░██
██████████
CRYPTO CASINO &
SPORTS BETTING
▄▄███████▄▄
▄███████████████▄
███████████████████
█████████████████████
███████████████████████
█████████████████████████
█████████████████████████
█████████████████████████
███████████████████████
█████████████████████
███████████████████
▀███████████████▀
█████████
.
Bitpocket (OP)
Newbie
*
Offline Offline

Activity: 3
Merit: 0


View Profile
September 21, 2019, 01:51:46 PM
 #4

your code is pretty bad because you are doing a million different things inside one function. it is near impossible and extremely time consuming for anyone to find out where the problem is!
there is a very single solution for that though. you must simply split your function apart into multiple functions each doing a different job, and then write tests for each function individually and see if they are working as they should.

for example:
- 1 function should be calculating checksum of a given byte array and return 4 bytes. your test would be passing an arbitrary byte to this and seeing if it returns correct 4 bytes after doing double_sha256 on it.
- 1 function should encode a given byte array to base58
- another function should encode a given byte array to base58 with checksum using the previous 2 functions
- 1 function should convert a public key to address
- and so on...


I did not create a function.
I do call multiple functions to perform the task.

I dont get your point. I'am calling a function for each step. There is even a link to the module that provide the function.

Maybe it's all the comment you find confusing cuse codewise there is not a lot of code in there.

Step 1 Bip39 mnemoneic -> seed

Step 2 seed->Hash1->rmd160

Step 3 add prefix

Step 4 Checksum from 4 byte after 2 hash

step 5 add checksum to address

step 6 Base58 encode the address

Just like every exemple on the web.





pooya87
Legendary
*
Offline Offline

Activity: 3444
Merit: 10535



View Profile
September 22, 2019, 04:07:16 AM
Merited by hugeblack (1)
 #5

I did not create a function.
I do call multiple functions to perform the task.

I dont get your point. I'am calling a function for each step. There is even a link to the module that provide the function.

well i am not familiar with JavaScript, so let me rephrase it like this. you have to split your code into different components that you can call and test separately. for example if you had a public key and wanted its corresponding address, is there anything you could call in your code to do exactly that?
also do you have any tests written for your code?

you have to be able to write a test like this (pubkey -> bytes for encoding):
input:
Code:
pubkey: 03b30574120027d4f51edb1cfc323d197aba3fe5e86c85cbbfc8946a4dbf55fa2b
output:
Code:
expected: 003683f0d40e5cfb112e9055f792204e58614815f4c7615408

by splitting your code and "testing" each "component" you can easily find the problem. now when you test this you get a result from your code called "actual" then compare it with "expected" that you hard coded in your test. then you realize that your returned value has a wrong last 4 bytes. so now you know the problem is with your checksum and you can go back and focus on that part and fix that part.

in this case (again i am not familiar with JavaScript but) this part should change from:
Code:
const hash2 = createHash('sha256').update(hash1rmd).digest();
const hash3 = createHash('sha256').update(hash2).digest();
to
Code:
const hash2 = createHash('sha256').update(pubAddr).digest();
const hash3 = createHash('sha256').update(hash2).digest();
since your checksum is 2x SHA256 of 3683f0d40e5cfb112e9055f792204e58614815f4 instead of 003683f0d40e5cfb112e9055f792204e58614815f4

.
.BLACKJACK ♠ FUN.
█████████
██████████████
████████████
█████████████████
████████████████▄▄
░█████████████▀░▀▀
██████████████████
░██████████████
████████████████
░██████████████
████████████
███████████████░██
██████████
CRYPTO CASINO &
SPORTS BETTING
▄▄███████▄▄
▄███████████████▄
███████████████████
█████████████████████
███████████████████████
█████████████████████████
█████████████████████████
█████████████████████████
███████████████████████
█████████████████████
███████████████████
▀███████████████▀
█████████
.
eckmar
Legendary
*
Offline Offline

Activity: 1878
Merit: 1038


Telegram: https://t.me/eckmar


View Profile
September 22, 2019, 11:11:38 AM
 #6

Is there any particular reason why would you want to proceed with address generation like that ? There is library https://github.com/bitcoinjs/bitcoinjs-lib which is available and honestly you should use it. I encourage new developers not to use too many packages until they understand what are they doing exactly, but in this case I see no reason why wouldn't you use already made library for your purposes especially if you said you wanted to create a wallet ? Wallet should be secure bug free app, isn't it logical to use latest industry standard to build it ? 
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!