Fast CashAccount lookups using BITBOX and Electrum

Aug 6, 19

TLDR? Just read the code.

In this article we will connect to an electrum server with nodejs. We will use it to find payment information for a cashaccount and parse it using BITBOX

The article walks you trough the important snippets of this final script.

The electrum server we use is ElectrsCash, which is written in Rust. At the time of writing (July, 2019) the cashaccount support only exists as a pull request to ElectrsCash, but is already deployed on bitcoincash.network:50002 and planned for next release.

Dependencies

In this example, we will make use of three node modules $ npm i electrum-client # For connecting to the electrum server $ npm i cashaccounts   # For parsing payment information $ npm i bitbox-sdk        # The duct tape for gluing all together

Connecting to an Electrum Server

We connect to bitcoincash.network on port 50002, with a secure ssl connection.

You can also run ElectrsCash on localhost, in that case you get to skip ssl setup and just pass 'tcp' instead of 'tls'.

const ElectrumCli = require('electrum-client')   const ecl = new ElectrumCli(50002, 'bitcoincash.network', 'tls') await ecl.connect()                                                       

Looking up a cash account

We'll look up the account dagur#216. The electrum server API is blockchain agnostic, so make sure to add the Bitcoin Cash block modifier, which is 563620.

const BCH_BLOCK_MODIFIER = 563620 const transactions = await ecl.request("cashaccount.query.name", ["dagur", 216 + BCH_BLOCK_MODIFIER]);

The response is all the transactions that match the account name. In most cases, this is one account, but there can be collisions. This is a known limitation if the cash accounts protocol, so make sure to handle this correctly. To test collisions, try looking up bitcoin#1000

In our case, for dagur#216, the response looks like this: [ {     blockhash: '000000000000000003c73e50b9de6317c4d2b2ac5f3c1253b01e61a6e329219a',     height: 563836,     tx: '0100000001bca903bbc429218234857628b382e8aa8e3bfa74c5b59628ad053284e50bf6ac010000006b4830450221009bbd0a96ef5ef33e09c4fce7fafd2add714ebe05d87a9cb6c826b863d0e99225022039d77b8bd9c8067636e64d6f1aeeeeb8b816bbc875afd04cef9eb299df83b7d64121037a291b1a7f21b03b2a5120434b7a06b61944e0edc1337c76d737d0b5fa1c871fffffffff020000000000000000226a040101010105646167757215018c092ec2cbd842e89432c7c53b54db3a958c83a575f00d00000000001976a914dfdd3e914d73fee85ad40cd71430327f0404c15488ac00000000'   } ]

Decoding the response

As the API is intended for SPV clients, the transactions are returned rather than the parsed payment information. This way the client doesn't need to trust the server, but can verify this themselves. Further, the client can validate that the transaction is in the block and that the bock has valid proof-of-work, but this is outside the scope of this article. Don't trust, (SPV) verify!

To decode the payment information, we'll use BITBOX. The payment information is in an nulldata (OP_RETURN) output.

const BITBOX = require("bitbox-sdk").BITBOX const bitbox = new BITBOX() const CashAccounts = require('cashaccounts') const tx_hex = transactions.pop()["tx"] const tx = await bitbox.RawTransactions.decodeRawTransaction(tx_hex) const opreturn = tx.vout.find(o => o.scriptPubKey.type === 'nulldata').scriptPubKey.asm const cashaccounts = new CashAccounts()  const payload = await cashaccounts.parsePaymentInfo(opreturn)  The payload variable now contains   [{     type: 'Key Hash',     address: 'bitcoincash:qzxqjtkze0vy96y5xtru2w65mvaftryr55yzmhy8sl' }]

And there we have it! The cashaccount dagur#216 refers to the P2PKH address bitcoincash:qzxqjtkze0vy96y5xtru2w65mvaftryr55yzmhy8sl

Bonus: Get the transaction history of the cashaccount

An electrum server provides lots of API functionality to work with the blockchain — and mostly fast as well. As we've already established a connection to an electrum server, we may as well do additional API calls.

Electrum uses script hashes for address lookups, this is a more generic way to look up balances and is address encoding agnostic. See the function toScriptHash in the full script for how to find the script hash for an address.

const scriptHash = toScriptHash(payload[0].address) console.log("Transaction history: ",     await ecl.request('blockchain.scripthash.get_history', [scriptHash])) console.log("Address balance: ",     await ecl.request('blockchain.scripthash.get_balance', [scriptHash]))

Result: Transaction history:  [ {     height: 580764,     tx_hash: '990e165f647c880eebe6e066b3fdc86f8ca15cfbc7d07fa8dc9837a648364328'  }, {     height: 580784,     tx_hash: '2d0531b82c529d6c26a2dbbed28fadce09a94b002beee5a3640e534d8d558a0c' } ] Address balance:  { confirmed: 0, unconfirmed: 0 }

Thanks for reading!

I hope some of you may find electrum useful as your project backend.

If you want to run your own server, consider ElectrsCash as it is fast, has low storage overhead and low CPU usage (after indexing).

I write about software development for the Bitcoin Cash network


Upvotes (5)

Comments (1)
sort by  /

6 Aug 19 09:09

How is started this code?

0   1  

8 Aug 19 12:34

Hey, this should work:

dagurval@p8:/tmp$ mkdir example-code

dagurval@p8:/tmp$ cd example-code/

dagurval@p8:/tmp/example-code$ npm init

dagurval@p8:/tmp/example-code$ npm i electrum-client cashaccounts bitbox-sdk

dagurval@p8:/tmp/example-code$ wget https://gist.githubusercontent.com/dagurval/dea9947e6eaead98e7d09cf72fc177e5/raw/b764856ddc67ab71b778ab0677a9b81e664ed900/index.js

dagurval@p8:/tmp/example-code$ node index.js

Results: [

{

blockhash: '000000000000000003c73e50b9de6317c4d2b2ac5f3c1253b01e61a6e329219a',

height: 563836,

tx: '0100000001bca903bbc429218234857628b382e8aa8e3bfa74c5b59628ad053284e50bf6ac010000006b4830450221009bbd0a96ef5ef33e09c4fce7fafd2add714ebe05d87a9cb6c826b863d0e99225022039d77b8bd9c8067636e64d6f1aeeeeb8b816bbc875afd04cef9eb299df83b7d64121037a291b1a7f21b03b2a5120434b7a06b61944e0edc1337c76d737d0b5fa1c871fffffffff020000000000000000226a040101010105646167757215018c092ec2cbd842e89432c7c53b54db3a958c83a575f00d00000000001976a914dfdd3e914d73fee85ad40cd71430327f0404c15488ac00000000'

}

]

Payload: [

{

type: 'Key Hash',

address: 'bitcoincash:qzxqjtkze0vy96y5xtru2w65mvaftryr55yzmhy8sl'

}

]

Transaction history: [

{

height: 580764,

tx_hash: '990e165f647c880eebe6e066b3fdc86f8ca15cfbc7d07fa8dc9837a648364328'

},

{

height: 580784,

tx_hash: '2d0531b82c529d6c26a2dbbed28fadce09a94b002beee5a3640e534d8d558a0c'

}

]

Address balance: { confirmed: 0, unconfirmed: 0 }

0   0