bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.72k stars 2.11k forks source link

Generating HD Litecoin wallet and deriving pubic addresses from xpub #2067

Closed N3IMYS closed 8 months ago

N3IMYS commented 8 months ago

Hello everyone, hope you are doing well. I just started to delve into the Bitcoin and Litecoin chains world to achieve some results regarding my work tasks:

  1. I need to create an HD Litecoin wallet, preferably in testnet, then test it and implement it on mainnet.
  2. I need to create many public addresses of the wallet.
  3. I need to ensure all my public addresses can be signed with the private key I get on wallet creation.

I found Litecoin testnet and mainnet network type objects and just pasted them into code:


export const litecoinNetwork: { mainnet: LitecoinNetwork; testnet: LitecoinNetwork } = {
    mainnet: {
        messagePrefix: '\x19Litecoin Signed Message:\n',
        bech32: 'ltc',
        bip32: {
            public: 0x019da462,
            private: 0x019d9cfe,
        },
        pubKeyHash: 0x30,
        scriptHash: 0x32,
        wif: 0xb0,
    },
    testnet: {
        messagePrefix: '\x19Litecoin Signed Message:\n',
        bech32: 'tltc',
        bip32: {
            public: 0x043587cf,
            private: 0x04358394,
        },
        pubKeyHash: 0x6f,
        scriptHash: 0xc4,
        wif: 0xef,
    },
};

const derivationPath = "m/44'/2'/0'/0/0";

I am trying to generate an HD Wallet with bip39 and bip32:

// Generate a mnemonic (12 words)
const mnemonic = generateMnemonic();

// Derive the seed from the mnemonic
const seed: Buffer = await mnemonicToSeed(mnemonic);

const bip32 = BIP32Factory(ecc);
const masterNode: BIP32Interface = tbip32.fromSeed(seed, litecoinNetwork.testnet);
const masterPrivateKey: string = masterNode.toBase58();
const masterPublicKey: string = masterNode.derivePath(derivationPath).neutered().toBase58();

I am not pretty sure that I am deriving the xpub from xpriv in a right way (don't know what derivation path should be for reaching the xpub) But at this point I have xpriv and xpub like:

"publicKey": "tpubDH3NjqJAx19kPfD5MfFwyn74WaQpbcvgT8yCu1ALvHoWeHD3L2Gt9QBwoVyRpNzR6vEdkWN2eDK7JBVzwwtybvHbWuym854FcDfWy7fTK7T",
"privateKey": "tprv8ZgxMBicQKsPduQqxLqsE8iJTGBQxhZa4mS3Cdhb29hokaBaeP6fBWQMVH7MNsdre2deje2qCGE6iQvSsr8JQyxpEQW397FimNMWH2aUPhF"

So next when I am generating the new public addresses with derivationPath I am changing the fifth digit of the derivation meaning: m/44'/2'/0'/0/0 will be the derivation path for the frist public address, m/44'/2'/0'/0/1 for the second e.t.c And I get them as :

tltc1qyp6x9dukk5tzxdhvpyugrydp9hn4t7mjz7zm3r
tltc1q3jgyx88s4ujc87unf0j8rfdeq60kud7a94assr
tltc1qr782cj0yu4yrttdq6tuxpx37udr2wejggs7yze

So the question how can I check if these pks really related to the xpub and how can I sign them using xpriv ?

junderw commented 8 months ago
const masterNode: BIP32Interface = bip32.fromSeed(seed, litecoinNetwork.testnet);
const masterPrivateKey: string = masterNode.toBase58();
const masterPublicKey: string = masterNode.derivePath(`m/44'/2'/0'`).neutered().toBase58();

const xpubNode = bip32.fromBase58(masterPublicKey, litecoinNetwork.testnet);
const firstAddressChild = xpubNode.derivePath(`0/0`);

const firstAddressPrivChild = masterNode.derivePath(`m/44'/2'/0'/0/0`);

const rawSignature = firstAddressPrivChild.sign(...);
N3IMYS commented 8 months ago

@junderw Hi thank you so much for your reply , but can you please also extend a bit logic of creating a partially signed bitcoin transaction in litecoin network ?

I want to send 10000 satoshis from this public address by creating a psbpt (don't know other ways to send crypto from HD wallet address) => then broadcast the transaction to the blockchain

I have this code :

//create a new Psbt
const psbt = new Psbt({ network: this.network });

my utxos is  just  a public addresses which is got one time faucet deposit on testnet with 1.5 LTC
so I am going to add the only input to my utxo
// don't really understand what I should put in the "script" property of an witnesUtxo object
psbt.addInput({
             hash: '940b2a13f13404be0e16e3499018a682626c34a1bb37be444a6600e9ac49c216',
         index: 0,
             witnessUtxo: {
                       script: Buffer.from('0014072ad96f57b57b9731c7999648ea0a486a3a507d', 'hex'),
                       value: 150000000,
                      } 
             })
 .addOutput({
     address: toAddress,
     value: 10000,
});

const xpriv: string = masterNode.toBase58();
const xprivChildNode = bip32.fromBase58(masterPrivateKey, this.network).derivePath('0/0');

So the question is: am I using right the psbt in this way:

const ecPair = ECPairFactory(ecc);
const keyPair = ecPair.fromWIF(xprivChildNode.toWIF(), this.network);
// sign inputs
psbt.signAllInputs(keyPair);

// finalaize the tx
psbt.finalizeAllInputs();

Its just eventually returns me an error of psbt: no inputs were signed ....

junderw commented 8 months ago

don't really understand what I should put in the "script" property of an witnesUtxo object

The input points to a UTXO which is an output of a previous transaction.

The hash points to the transaction.

The index tells it which output of the transaction with txid hash we need to look at.

The witnessUtxo tells it the script and the value contained in the output.

Given all the info (1. Where to find the output. 2. What is the content of the output.) we can sign the input.

Its just eventually returns me an error of psbt: no inputs were signed ....

That means you are mixing up keys.

  1. Wrong seed.
  2. Wrong xprv.
  3. Wrong path.
  4. Getting mixed up with which xpub/xprv is using which path.
  5. Any of the variables dealing with the above info are somehow mutated incorrectly.
  6. Many many other reasons.

Any number of mix up in your brain/code can cause you to generate the wrong key.

It could also be that the script in your witnessUtxo is incorrect, since that's what the signing code uses to compare against when searching for inputs to sign.

N3IMYS commented 8 months ago

Thanks a lot for your help !!