How to encode and decode a Counterparty transaction

This post explains the process of encoding and decoding Counterparty transactions

#Encode

You can use one of these methods:

  • Use Counterparty API (e.g. create_send) to create a raw Bitcoin transaction with embedded Counterparty payload. This approach is easier, but requires access to a Counterparty server or API endpoint.
  • Use any programming language to compose such a transaction manually. This is harder, but does not require a Counterparty server or Counterparty API access.

The API (create_send) returns a raw bitcoin transaction, i.e. there’s no need to encode anything manually.

###Counterparty Transaction Encoding

The best source of such information are Counterparty repositories on Github (for example, counterparty-lib). You can also reference this page (and other content in this repository): https://github.com/tokenly/counterparty-spec/blob/master/spec/03-encoding.md.

###Example

Go to a Counterparty block explorer, get a transaction ID and then use a Bitcoin explorer to see how such transaction looks from that side.

It’s probably easiest to create a transaction yourself using Counterwallet and then - having the address and private key - try to get the same transaction output using whatever coding approach you choose.

A working example of a Counterparty encoder written in JavaScript can be found here.

A Counterparty payload example from testnet used in this post: 0000000015056c5dd87f00000000000000000001

#Decode

Use the reverse process to decode a Counterparty transaction.

You can use a bitcoin transaction decoder such as (testnet example): https://live.blockcypher.com/btc-testnet/decodetx/ and then proceed to decode the Counterparty payload within the transaction.

###Counterparty Transaction Decoding

Implementations and code examples can be found in Counterparty repositories such as counterparty-lib and open source Counterparty projects that don’t use the Counterparty API.

There’s also a helpful page on decoding Counterparty transactions created by Tokenly:

###Example

Raw testnet transaction created by Counterparty (returned by create_send, for example):

0100000001a551b593f03e948d3a91770d1fe6dc78d92c960387cdf0c79bd3970fc2c7f845020000001976a914f8eb547223b286af4c069845bab650951de0ba8988acffffffff0335150000000000001976a914dd1262b79cba636a0366790369d8442f2b7fba0688ac00000000000000001e6a1c8e180488b758e3c22003bcd9cea28c633dc9a5d110a791c889910840a2fb3200000000001976a914f8eb547223b286af4c069845bab650951de0ba8988ac00000000

Signed testnet transaction (that was broadcast on the bitcoin network):

0100000001a551b593f03e948d3a91770d1fe6dc78d92c960387cdf0c79bd3970fc2c7f845020000006a473044022064355db66650c3a9b51ed4b4bae383f914486f61750db83164cc9f75dbf9bb0502204812af330d83655a4724b92caaf0634e9cc34b04cdbf16766c27d3a19f53403901210265f9763ea8f857571a003a9c7abddc9875a3dd921dbdb15c1659944869f945cdffffffff0335150000000000001976a914dd1262b79cba636a0366790369d8442f2b7fba0688ac00000000000000001e6a1c8e180488b758e3c22003bcd9cea28c633dc9a5d110a791c889910840a2fb3200000000001976a914f8eb547223b286af4c069845bab650951de0ba8988ac00000000

The same transaction can be decoded with a decoder such as https://live.blockcypher.com/btc-testnet/decodetx/ (for testnet):

{
    "addresses": [
        "n4D7ndiC4yLZxhAf726uMHXC914sEuUAcg", 
        "n1fseuMLWaKDnDkpXiz8PpwV8RqYna9Mbf"
    ], 
    "block_height": -1, 
    "block_index": -1, 
    "confirmations": 0, 
    "data_protocol": "unknown", 
    "double_spend": false, 
    "fees": 7875, 
    "hash": "afd90be0ffea165e7d4fe479920cdcea4c0efd11a1d14cc43bb0176f1e0bc4da", 
    "inputs": [
        {
            "addresses": [
                "n4D7ndiC4yLZxhAf726uMHXC914sEuUAcg"
            ], 
            "output_index": 2, 
            "output_value": 3354522, 
            "prev_hash": "45f8c7c20f97d39bc7f0cd8703962cd978dce61f0d77913a8d943ef093b551a5", 
            "script": "473044022064355db66650c3a9b51ed4b4bae383f914486f61750db83164cc9f75dbf9bb0502204812af330d83655a4724b92caaf0634e9cc34b04cdbf16766c27d3a19f53403901210265f9763ea8f857571a003a9c7abddc9875a3dd921dbdb15c1659944869f945cd", 
            "script_type": "pay-to-pubkey-hash", 
            "sequence": 4294967295
        }
    ], 
    "lock_time": 0, 
    "outputs": [
        {
            "addresses": [
                "n1fseuMLWaKDnDkpXiz8PpwV8RqYna9Mbf"
            ], 
            "script": "76a914dd1262b79cba636a0366790369d8442f2b7fba0688ac", 
            "script_type": "pay-to-pubkey-hash", 
            "value": 5429
        }, 
        {
            "addresses": null, 
            "data_hex": "8e180488b758e3c22003bcd9cea28c633dc9a5d110a791c889910840", 
            "script": "6a1c8e180488b758e3c22003bcd9cea28c633dc9a5d110a791c889910840", 
            "script_type": "null-data", 
            "value": 0
        }, 
        {
            "addresses": [
                "n4D7ndiC4yLZxhAf726uMHXC914sEuUAcg"
            ], 
            "script": "76a914f8eb547223b286af4c069845bab650951de0ba8988ac", 
            "script_type": "pay-to-pubkey-hash", 
            "value": 3341218
        }
    ], 
    "preference": "medium", 
    "received": "2016-10-27T07:00:31.93228414Z", 
    "relayed_by": "54.158.61.129", 
    "size": 264, 
    "total": 3346647, 
    "ver": 1, 
    "vin_sz": 1, 
    "vout_sz": 3
}

From here you’d have to decode the transaction using the same approach that counterparty-lib uses (or leverage the API).

With the Counterparty API you can use get_tx_info (see http://counterparty.io/docs/api/#get_tx_info). For input we use the raw unsigned transaction (hexadecimal serialization of the transaction, not its hash). Put another way, tx_hex is what the API or client returns before you sign and broadcast (or send, in Bitcoin Core language) the transaction. The Counterparty API (server) sometimes confusingly returns a weird message (‘Asked to read 32 bytes, but only got 27’) if you pass it the hash (which is actually 64 bytes!) of a signed & sent transaction.

{
  "method": "get_tx_info",
  "params": {"tx_hex": "0100000001a551b593f03e948d3a91770d1fe6dc78d92c960387cdf0c79bd3970fc2c7f845020000001976a914f8eb547223b286af4c069845bab650951de0ba8988acffffffff0335150000000000001976a914dd1262b79cba636a0366790369d8442f2b7fba0688ac00000000000000001e6a1c8e180488b758e3c22003bcd9cea28c633dc9a5d110a791c889910840a2fb3200000000001976a914f8eb547223b286af4c069845bab650951de0ba8988ac00000000"},
  "jsonrpc": "2.0",
  "id": 1
}

Response:

{
  "result": [
    "n4D7ndiC4yLZxhAf726uMHXC914sEuUAcg",
    "n1fseuMLWaKDnDkpXiz8PpwV8RqYna9Mbf",
    5429,
    7875,
    "0000000015056c5dd87f00000000000000000001"
  ],
  "id": 1,
  "jsonrpc": "2.0"
}

As you can probably guess, here’s what the list means:

  • source
  • destination
  • btc_amount (in satoshi)
  • tx_fee (to bitcoin miners)
  • data (Counterparty payload)

Now we need to decode the payload (0000000015056c5dd87f00000000000000000001) . Refer to Tokenly’s decoder or counterparty-lib or examples such as this JavaScript decoder.

Here we’ll use another Counterparty API, unpack, and pass the Counetrparty payload from the previous call:

{
  "method": "unpack",
  "params": {"data_hex": "0000000015056c5dd87f00000000000000000001"},
  "jsonrpc": "2.0",
  "id": 1
}

Finally, we get the result:

{
  "result": [
    0,
    {
      "quantity": 1,
      "asset": "A1514736000000000000"
    }
  ],
  "id": 1,
  "jsonrpc": "2.0"
}

#Send Transaction Used in This Post

This was the original transaction (again, on testnet):

send --source n4D7ndiC4yLZxhAf726uMHXC914sEuUAcg --destination n1fseuMLWaKDnDkpXiz8PpwV8RqYna9Mbf --asset A1514736000000000000 --quantity 1

1 Like