Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore the possibility of removing the bcoin dependency altogether #694

Closed
lukasz-zimnoch opened this issue Sep 13, 2023 · 5 comments
Closed
Assignees
Labels
🔌 typescript TypeScript library

Comments

@lukasz-zimnoch
Copy link
Member

Our SDK uses bcoin library for Bitcoin-related operations. The bcoin library depends on bcrypto which turned out to be a big pain that dramatically decreases the developer experience of our typescript SDK. Although we are getting rid of the direct bcrypto dependency in our SDK (see #693), bcoin still pulls bcrypto transitively.

One possible solution we want to explore is removing bcoin completely and replacing it with an alternative or our own code.

The goal of this task is:

  • Explore the current usage of bcoin in our SDK codebase and list all touching points
  • Think about possible replacement for each bcoin usage
  • Asses the time and complexity of the final solution
@tomaszslabon
Copy link
Contributor

tomaszslabon commented Sep 15, 2023

Here is the list of use cases for the bcoin library in the tbtc-ts. All of them are related to creating Bitcoin transactions:

  1. Creating a new transaction: A new mutable transaction (MTX) is created using new bcoin.MTX().
const transaction = new bcoin.MTX();
  1. Adding output: An output is added to the transaction with bcoin.Script.fromAddress(refunderAddress) used for generating the script from the given refunderAddress.
transaction.addOutput({
    script: bcoin.Script.fromAddress(refunderAddress),
    value: utxo.value.toNumber(),
});
  1. Creating a coin: A coin object is created from a raw transaction, which is used later for funding the transaction.
const inputCoin = bcoin.Coin.fromTX(
    bcoin.MTX.fromRaw(utxo.transactionHex, "hex"),
    utxo.outputIndex,
    -1
);
  1. Funding the transaction: The transaction is funded using the transaction.fund method.
await transaction.fund([inputCoin], {
    changeAddress: refunderAddress,
    hardFee: fee.toNumber(),
    subtractFee: true,
});
  1. Creating a script: A new script object is created from a raw buffer or address.
const depositScript = bcoin.Script.fromRaw(
    Buffer.from(await assembleDepositScript(depositScriptParameters), "hex")
);
bcoin.Script.fromAddress(walletAddress)
  1. Signing the transaction: The transaction.signature() method is used to generate a signature for specific inputs:
const signature: Buffer = transaction.signature(
  inputIndex,
  depositScript,
  previousOutputValue,
  refunderKeyRing.privateKey,
  bcoin.Script.hashType.ALL,
  0 // legacy sighash version for P2SH
);
// and similarly for P2WSH with segwit sighash version 1

Sometimes built-in functions for scripting and signing are used if the input is standard:

transaction.scriptInput(inputIndex, previousOutput, walletKeyRing)
transaction.signInput(inputIndex, previousOutput, walletKeyRing)
  1. Converting the transaction object to the raw format:
transaction.toRaw().toString("hex")
  1. Access to opcodes: The opcodes from the bcoin.script.common package are accessed. They are used to create a custom script:
const { opcodes } = bcoin.script.common
  1. Keyring creation from the provate key:
new bcoin.KeyRing({
    witness: witness,
    privateKey: decodedPrivateKey.privateKey,
    compressed: decodedPrivateKey.compressed,
  })
  1. Creating addresses based public keys:
bcoin.Address.fromWitnessPubkeyhash(buffer).toString(bcoinNetwork)
    : bcoin.Address.fromPubkeyhash(buffer).toString(bcoinNetwork)
  1. Unit test-only. Converting transaction to JSON format:
 const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet")

@tomaszslabon
Copy link
Contributor

tomaszslabon commented Sep 15, 2023

SOLUTION 1: Rewriting (parts of) the bcoin library to typescript:
Writing own code for parts of the bcoin library that are used in our code would require creating classes such as:

Therefore this solution would require recreating most of transaction-related bcoin-code and would be very time-consuming.
Parts related to block or chain could be skipped.

@tomaszslabon
Copy link
Contributor

tomaszslabon commented Sep 15, 2023

SOLUTION 2: Forking bcoin and removing bcrypto dependency.
In this solution, we could fork the bcoin library (possibly leaving only transaction-related code and removing the rest), replace the bcrypto with some other cryptographic library.
Those are some examples of what is imported from bcrypto into bcoin:

const ripemd160 = require('bcrypto/lib/ripemd160');
const sha1 = require('bcrypto/lib/sha1');
const sha256 = require('bcrypto/lib/sha256');
const hash160 = require('bcrypto/lib/hash160');
const hash256 = require('bcrypto/lib/hash256');
const secp256k1 = require('bcrypto/lib/secp256k1');
const base58 = require('bcrypto/lib/encoding/base58');

This solution could be faster than the first one, but it requires finding a replacement for the bcrypto library and possibly touching low-level error-prone cryptographic code.

@tomaszslabon
Copy link
Contributor

tomaszslabon commented Sep 15, 2023

SOLUTION 3: Replacing bcoin with another library.
Here are some Bitcoin-related javascript libraries:

This solution would probably be the fastest. However, the chosen library must be elastic enough to allow for creating non-standard Bitcoin transactions (i.e. singing inputs with non-standard scripts). Obviously, it must not have bcrypto in its dependencies.

The two libraries mentioned above do not seem to depend on bcrypto and are MIT-licensed.

@lukasz-zimnoch
Copy link
Member Author

We discussed the possible solutions with @tomaszslabon:

  • We decided to go with SOLUTION 3: Replacing bcoin with another library. Specifically, we will replace everything we can with https://github.com/bitcoinjs/bitcoinjs-lib which doesn't have painful dependencies and is actively maintained. It may happen that not everything will be replaceable 1:1 so we may need to write our own code for some parts. Nevertheless, this solution is the most promising and provides a good tradeoff between quality and time consumption.
  • We rejected SOLUTION 1: Rewriting (parts of) the bcoin library to typescript. Although it seems to be the best solution at a glance, it is the most time-consuming (rough guesstimate is even several weeks). There is a ton of code that would have to be written and covered with unit tests. Also, the burden of maintaining all of this will be on our shoulders
  • We rejected SOLUTION 2: Forking bcoin and removing bcrypto dependency. This would require us to make direct changes in bcoin library. This library is large, quite complex, and doesn't have active maintainers able to provide help. This solution has the biggest potential for generating serious blockers and extending the work beyond a reasonable duration. Additionally, we would have to maintain the bcoin fork on our own

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔌 typescript TypeScript library
Projects
None yet
Development

No branches or pull requests

2 participants