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: