bitcoinjs / bitcoinjs-lib

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

[Rough] Things to work on (Roadmap?) #1437

Open junderw opened 4 years ago

junderw commented 4 years ago
  1. Dependency clean up - move bitcoinjs packages to mono-repo style, and try to reduce the number of non-bitcoinjs/cryptocoinjs dependencies.
  2. Wallet example project - Create a nice Wallet library that is simple and abstract enough to take the concepts and fit their usecase... thinking of a Wallet class, a PsbtCreator class, and UtxoGetter class. The PsbtCreator class will use a UtxoGetter object to help create etc... just ideas
  3. Async Hardware Wallet library - Now that PSBT is merged, implementing a versatile Async HD based signing class will be great... we send in a PSBT to get signed, and it will pass it along once HW wallets support it, or we will parse the info out and send it to the HW interface.

Any other ideas for future projects are welcome.

tarun1475 commented 4 years ago

Hi , I am willing to develop a wallet example project and have an experience of using bitcoinjs lib except psbt functions which are recently added.. Tell me the next steps.

junderw commented 4 years ago

@tarun1475

Sure.

  1. Create a new repo under your account.
  2. Create an issue here on bitcoinjs-lib and call it something like "Create Wallet example project" and in the issue body post a link to your repo.
  3. Create the repo. Any questions you need, ask in the issue you created in 2.

As for advice:

  1. I was thinking maybe there would be a Wallet class, the constructor would accept a bip32 object. Then a static method could do a fromBIP39 where we can generate the wallet from BIP39... The Wallet object imo shouldn't hold the phrase, but it should hold the root bip32 node.
  2. The Wallet class should differentiate if the bip32 node it was given has private key or not. (via isNeutered()) and if not, any send methods (ie. sendMany sendCoin etc) should throw an error.
  3. There should be a few other classes, UtxoGetter, which had methods of getting Utxos for a scriptPubkey (or address).. PsbtCreator which should contain a UtxoGetter allowing it to create the Psbt to sign. This can be done without private keys.
  4. In general, the base implementation should use Bitcoin Core where possible...

Once you get an issue up and running you can ask some more questions there.

Thanks.

tarun1475 commented 4 years ago

Sounds great.

I have few more questions i will ask in the issue then. Thanks.

dakk commented 4 years ago

Regarding "Async Hardware Wallet library", I'm interested in implementing it for the Ledger HW wallet since I'm already using ledger nano with psbt in my project. The main problem is that LedgerJS's sign function needs the whole transaction for every input being signed (not just for non witness inputs) so atm it's impossible to sign a segwit input only with data contained in pbst transaction.

(I asked them if they planned to support bip174 https://github.com/LedgerHQ/ledgerjs/issues/399)

junderw commented 4 years ago

I added support for nonWitnessUtxo with segwit. (it just ignores the extra info, since the witnessUtxo is contained within the nonWitnessUtxo)

So while not exactly the intended spec, you can use nonWitnessUtxo for all inputs in this library, and the segwit detection is done by matching scripts.

Most other PSBT implementations don't do this though. So they should eventually support it.

dakk commented 4 years ago

Great, even if it is just a patch waiting for ledgerjs implementing it. Another thing I need is a way to extract the unsigned transaction hex like I did with

txb.buildIncomplete().toHex()

since ledger.signP2SH needs serialized transaction outputs: atm i'm doing as follow:

const outshex = btc.serializeTransactionOutputs(btc.splitTransaction((txb as any).__CACHE.__TX.toHex(), true)).toString('hex');

which is not a clean way. Maybe could be useful an additional extractTransactionIncomplete which ignores non-finalized inputs?

junderw commented 4 years ago

hmmm.... tbh, I think the whole point of PSBT is that we have a format to encode Partially Signed Bitcoin Transactions...

So having an extract incomplete method seems antithetical to the idea of PSBT.

Your hack is OK for now. Let's wait until Ledger supports PSBT to fix it. :-D

dakk commented 4 years ago

yea, I agree with you, let's wait :) I post here the code I'm using to sign p2wsh and p2sh-p2wsh with the ledger, maybe could be useful for other users while waiting:

TransportU2F.create().then(transport => {
    const btc = new Btc(transport);

    /* Download utxo transaction raw */
    this.rawTransactions(options.utxos.map(utxo => utxo.tx)).then(transactions => {
        /* Create inputs and serialized outputs */
        const inputs = options.utxos.map(utxo => [
            btc.splitTransaction(transactions[utxo.tx], bitcoinjs.Transaction.fromHex(transactions[utxo.tx]).hasWitnesses()),
            utxo.n,
            walletScripts.p2wsh.redeem.output.toString('hex'),
        ]);
        const paths = inputs.map(i => this.signPath);
        const txb = bitcoinjs.Psbt.fromHex(txhex, { network: this.config.network });
        const outshex = btc.serializeTransactionOutputs(btc.splitTransaction((txb as any).__CACHE.__TX.toHex(), true)).toString('hex');

        btc.signP2SHTransaction(inputs, paths, outshex, 0/*tx.locktime*/, 1 /*SIGHASH_ALL*/, segwit, 2).then(signatures => {
            /* Inject signatures */
            for (let j = 0; j < txb.inputCount; j++) {
                txb.signInput(j, {
                    network: this.config.network,
                    publicKey: toByteArray(publickey),
                    sign: (hash) => bitcoinjs.script.signature.decode(toByteArray(signatures[j])).signature
                } as bitcoinjs.ECPairInterface);
            }

            /* Build the transaction */
            resolve(txb.toHex());
        });
    });
});

And this is the code for retrieving the pubkey:

    private getPublicKeyFromPath(path: string) {
        return new Promise((resolve, reject) => {
            TransportU2F.create().then(transport => {
                const btc = new Btc(transport);
                btc.getWalletPublicKey(path, { verify: false, format: 'legacy' }).then(result => {
                    const comppk = compressPublicKey(result.publicKey);
                    resolve(comppk);
                }).catch(err => {
                    reject(err);
                });
            }).catch(err => {
                reject(err);
            });
        });
    };
junderw commented 4 years ago

Very cool.

I would like to see the bigger project. Not sure where publickey is coming from etc.

dakk commented 4 years ago

@junderw I put the getpubkey function in the previous message; anyway for further details the frontend wallet part of our platform is opensource: https://github.com/helperbit/helperbit-wallet

jasonandjay commented 2 months ago

@junderw maybe we can merge this with #1326