• TokenSoft

Signing Bitcoin Transactions on Ledger with bcoin's Bledger

by Randall Leung, Developer, TokenSoft




If you are already using bcoin as part of your bitcoin infrastructure and need to sign transactions on a Ledger, you have come to the right place.


We’ve found that Bledger, which is found in bcoin’s open source library, works well to connect to the ledger hardware wallet.


Bledger is an API layer that plays nicely with Bcoin’s transaction objects. In this post, I will walk through the implementation to help simplify the process of signing a bitcoin transaction.


Why is this important? By signing a transaction with your ledger, you can prove that you control a particular Bitcoin address and hence assert the ownership of funds.



Connecting to the Ledger


First, let's connect to the bledger app:

import bledger from 'bledger'
const { Device } = bledger.HID
const { LedgerBcoin } = bledger
// Get all available devices via bledger
const device = await Device.getDevices()
// Get the first device found
let device = new Device({
  device: devices[0],
  timeout: 60000 // 60 seconds
})
// Connect to the device's interface
await device.open()
// Connect to the ledger device via bledger
const bcoinApp = await new LedgerBcoin({ device })



Signing Implementation


To start, we need the bcoin transaction that needs to be signed (bcoinTransaction) and the inputs (ledgerTxInputsData) that we wish to include in the transaction. Read more about it here.


The bcoin transaction contains the following structure:

bcoinTransaction = {
  fee: String,
  hash: String,
  hex: String,
  inputs: Array,
  locktime: Int,
  mtime: Int,
  outputs: Array,
  rate: Int,
  version: Int,
  witnessHash: String
}

and the tx input’s structure:

ledgerTxInputsData = {
  hash: String,
  index: String,
  path: String,
  rawTx: String,
  redeemScript: String,
  witness: Boolean
}

Since the transactions in bcoin are immutable, we’ll need to create a mutable transaction:

const MTX = bcoin.MTX
const createdTx = MTX.fromJSON(bcoinTransaction)

The transaction inputs are the reference to an output from a previous transaction. A transaction often has multiple inputs. We’ll need to build the ledger input object for each input that needs to be signed.

// Bledger's wrapper on the ledger transaction input class
const { LedgerTXInput } = bledger
for (let i = 0; i < ledgerTxInputsData.length; i++) {
 let input = ledgerTxInputsData[i]
 // Set up the TX
  const tx = TX.fromRaw(Buffer.from(input.rawTx, 'hex'))
 // Get the public key from the ledger
  const hdPubKey = await bcoinApp.getPublicKey(input.path)
  const publicKey = hdPubKey.publicKey
 // If multisig, it will have a redeemscript
  let redeem = input.redeemScript && Buffer.from(input.redeemScript, 'hex')
 // Push the new object to the list
  ledgerInputTxs.push(
    new LedgerTXInput({
      witness: true, // segwit or not
      tx,
      index: input.index,
      redeem,
      path: input.path,
      publicKey
    })
  )
}

Now that we have set up our ledger transaction inputs, we can use bcoinApp to sign the transaction:

await bcoinApp.signTransaction(createdTx, ledgerInputTxs)

At this point, the ledger will ask the user to confirm the output details.


Once the user confirms, we can verify it is a valid transaction:

console.log(`Valid Transaction: ${createdTx.verify()}.`)

If everything is correct, the transaction is signed.

const signedTransaction = createdTx.toJSON()

With the signed transaction, it is now ready to be broadcast to the blockchain!



Deployment with Electron


One of the problems we ran into was compiling bledger in an electron app. One of bledger’s dependencies, bcrypto, statically links with OpenSSL libs by default.


If you are unable to link Electron’s headers with the native modules, you may get the following error:

dyld: lazy symbol binding failed: Symbol not found: _SHA256_Init

To get around this, update your webpack plugins and replace environment variables at build time:

plugins: [
  new webpack.EnvironmentPlugin({
    // This is to force bcrypto to use JS only implementation
    NODE_BACKEND: 'js'
  })
]

This will force bcrypto to use full JS implementation instead of linking against native OpenSSL libs. 👍



Shoutout


Feel free to reach out to me if you have any questions. Also, we’re hiring at TokenSoft if you want to check us out.


Otherwise, the bcoin’s slack channel has been very responsive and helpful. Check them out at:

Slack

Bcoin

Bledger

  • medium
  • White Twitter Icon
  • White LinkedIn Icon

Join our mailing list

THIS WEBSITE IS OWNED AND OPERATED BY TOKENSOFT, INC. (“TOKENSOFT”), A TECHNOLOGY COMPANY PROVIDING COMPLIANCE AND BLOCKCHAIN-BASED SERVICES FOR ISSUERS OF SECURITIES OR OTHER DIGITAL ASSETS.  TOKENSOFT IS NOT A BROKER-DEALER, INVESTMENT ADVISER, OR FINANCIAL ADVISOR.  TOKENSOFT IS NOT REGISTERED WITH THE U.S. SECURITIES & EXCHANGE COMMISSION (SEC) NOR ANY OTHER REGULATORY AGENCY OR BODY IN THE UNITED STATES OR INTERNATIONALLY.  TOKENSOFT DOES NOT GIVE INVESTMENT OR LEGAL ADVICE, ENDORSEMENTS, ANALYSIS, OR RECOMMENDATIONS WITH RESPECT TO ANY SECURITIES OR OTHER DIGITAL ASSETS. NOTHING ON THIS WEBSITE SHALL CONSTITUTE OR BE CONSTRUED AS AN OFFERING OF SECURITIES OR AS INVESTMENT ADVICE OR INVESTMENT RECOMMENDATIONS BY TOKENSOFT OR ANY OF ITS AFFILIATES OR A RECOMMENDATION AS TO AN INVESTMENT. ALL THIRD PARTY SECURITIES OFFERINGS AND DIGITAL ASSETS POWERED BY TOKENSOFT’S TECHNOLOGY ARE OFFERED BY, AND ALL INFORMATION RELATED THERETO IS THE RESPONSIBILITY OF, THE APPLICABLE ISSUER OF SUCH SECURITIES OR DIGITAL ASSETS. TOKENSOFT DOES NOT CUSTODY ANY DIGITAL SECURITIES OR DIGITAL ASSETS ON BEHALF OF ANY OF ITS CUSTOMERS OR USERS OF OUR WEBSITE SERVICES.


For information relating to Tokensoft Global Markets, our affiliate broker-dealer, please visit https://www.tokensoftmarkets.com.