Unblinding Transactions

Sharing Unblinded Transaction Data With Third Parties

As Blockstream AMP transactions are carried out on the Liquid network they are, by default, covered by confidential transactions. Confidential transactions on Liquid mean that normally only the sender and receiver know how much of an asset, and which asset, has been sent. Blockstream AMP issued assets allow the asset issuer to also view the transacted asset and amount, via the Blockstream AMP API.

In some cases the issuer, or the user, might want to share the unblinded details of the transaction with a third party, such as an auditor.

For normal Liquid assets the unblinding data can only be shared by the user. Blockstream AMP unblinding data can additionally be shared by the issuer. We will look at each of these cases in turn from the perspective of the issuer and then the user.

For our example we will be unblinding transactions that involve Blockstream’s ‘Issuer Tracked Demo Asset’. The asset details can be found on the Blockstream explorer. You will see that the issuance itself was not confidential, meaning that the issuance data is viewable via the details section of the page. As with all AMP assets, any subsequent transactions involving the asset (apart from issuer-invoked reissuances and burns) will be covered by Liquid’s confidential transactions, where the amount and asset being transacted are cryptographically hidden from everyone looking at Liquid blockchain data.

By providing the ‘blinding keys’ to someone it is possible for either the issuer, or the user, to share the actual amount and asset id being transacted with a third party.

First, we will see how to use the Blockstream Liquid explorer to unblind a transaction’s output, from the perspective of the issuer and then the user, and then we will use the verify-elements-commitments Python script to verify the data ourselves.

Issuer link sharing

We’ll use an existing transaction involving the Blockstream AMP demo site’s Issuer Tracked Demo Asset for this example. A transaction sending 100 of the demo asset was sent to a user’s receiving address. The address came from a Blockstream Green Managed Assets Account with a Green Account ID (GAID) of GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C.

📘

Note

If you look at the demo site you are able to see the data as if you were the asset’s issuer. This is for demonstration purposes only and the issuer would not normally share such data with their users. Not all data accessible to the issuer is shown via the demo site.

If you visit the summary page for the asset, go to the UTXOS tab and filter the GAID by GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C You will see that in transaction e6d8feb9... the user was sent 100 of the asset.

Clicking on the transaction link takes you to the Blockstream explorer page for the transaction where you can see everyone else’s view of the transaction. The amount and asset are covered by confidential transactions and are blinded. Inspecting the transaction on the Liquid blockchain will show no further information about the asset and amount sent. In order for a third party to view the actual amount and asset sent they require the amount and asset blinders.

The issuer of the asset can use the Blockstream AMP API to see the full activities view for the asset, which includes the asset and amount blinders used in the transaction. With these details they can provide a link to the Blockstream Liquid explorer with the information needed to unblind the transaction contained within the URL itself. The explorer will use this data to present an unblinded view of the output in question.

In order to provide a third party with the required data, the issuer first calls the Blockstream AMP API /assets/{assetUuid}/activities endpoint for the relevant asset, in this case the ‘Issuer-Tracked Demo Asset’, which has an asset ID of f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2.

The returned activities data contains everything that is needed to form the URL. Using the example of the 100 ‘Issuer-Tracked Demo Asset’ sent to the GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C wallet of the user in transaction e6d8feb9... the issuer singles out the relevant activity using the txid, GAID, and amount as shown below.

{
    "type": "transfer",
    "datetime": "2021-02-11T09:10:58Z",
    "description": "Transfer to wallet GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C",
    "txid": "e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea",
    "vout": 0,
    "blockheight": 1188465,
    "investor": null,
    "GAID": "GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C",
    "amount": 100,
    "asset_blinder": "7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3",
    "amount_blinder": "3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838",
    "registered_user": null
},

📘

Note

Be sure to select the right activity output and not the change output for the transaction.

The Issuer now has all the data needed to form the unblinding URL.

The format of the unblinding URL is:

https://blockstream.info/liquid/tx/{txid}#blinded={amount},{asset_id},{amount_blinder},{asset_blinder}

📘

Note

You can provide multiple amount/asset/blinder values at once. The explorer will loop through each set and match to an output. For example: #blinded=,<asset_id>,<amount_blinder>,<asset_blinder>,,<asset_id2>,<amount_blinder2>,<asset_blinder2>

Selecting from the above activity, and the already known asset ID, we will use the values:

txid = e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea
amount = 100
asset_id = f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2
amount_blinder = 3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838
asset_blinder = 7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3

Forming the URL we get:

https://blockstream.info/liquid/tx/e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea#blinded=100,f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2,3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838,7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3

If you visit the above URL you will see the data as the third party would, with amount and asset ID unblinded:

User link sharing

A user who receives a Blockstream AMP asset to their Green mobile wallet can also choose to share the unblinded transaction data with a third party. To do this, they click the share icon from within the transaction details screen, which presents them with two options:

The option to send a link to the confidential transaction will share a link to the blinded transaction.

Additional data must be sent to the third party using the ‘Share unblinding data’ option if they want to create a link that enables someone to view the unblinded data.

For the example transaction e6d8feb9..., the unblinding data that is shared is shown below:

{
    "inputs":[],
    "outputs":[{
        "amountblinder":"3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838",
        "asset_id":"f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2",
        "assetblinder":"7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3",
        "satoshi":100,
        "vout":0
    }],
    "txid":"e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea",
    "type":"incoming",
    "version":0
}

The user can then form the URL to send to the third party using the URL format:

https://blockstream.info/liquid/tx/{txid}#blinded={satoshi},{asset_id},{amountblinder},{assetblinder}

Selecting from the unblinding data shared above we will use the values:

txid = e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea
satoshi = 100
asset_id = f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2
amountblinder = 3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838
assetblinder = 7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3

Forming the URL we get:

https://blockstream.info/liquid/tx/e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea#blinded=100,f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2,3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838,7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3

The user would then send this link to the third party in order to share the unblinded transaction details with them.

If you visit the above URL you will see the data as the third party would, with amount and asset ID unblinded.

Verifying commitments yourself

Rather than trust the commitment proof provided by the Blockstream Liquid explorer above you may want to run your own proof of commitments. You can do this using the verify-elements-commitments Python script, which itself uses Libwally, a cross-platform, cross-language collection of useful primitives for cryptocurrency wallets.

We’ll use the same transaction and commitment data used above, involving the Blockstream AMP demo site’s Issuer Tracked Demo Asset for this example.

To run the verify-elements-commitments.py Python script the issuer first needs to install the prerequisites. From the terminal, create a folder to run the code from and move into it:

mkdir verify_elements_commitments
cd verify_elements_commitments

Then (optionally) initialize a Python virtual environment to install the requirements within:

virtualenv -p python3 venv
source venv/bin/activate

Into this environment you need to install libwally-core. The source code for libwally can be found in the Libwally repository.

pip install wallycore

Download the verify-elements-commitments.py file from:

https://github.com/Blockstream/verify-elements-commitments

Place the downloaded file in the verify_elements_commitments folder.

Get the relevant transaction hex from your Elements node. For your own transaction this would be done using the following command from the terminal:

elements-cli getrawtransaction <transaction_id>

So for our example we would run:

elements-cli getrawtransaction e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea

Alternatively you can get the transaction hex from the Blockstream liquid explorer:

https://blockstream.info/liquid/api/tx/e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea/hex

Save the results of either of the above to a new file named transaction.txt within the verify_elements_commitments folder.

Next, the issuer would call the Blockstream AMP API /assets/{assetUuid}/activities endpoint for whichever asset they want to prove commitments for. The returned activities data contains everything that is needed to form the required blinders data. Using the example of the 100 ‘Issuer-Tracked Demo Asset’ sent to the GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C wallet of the user in transaction e6d8feb9... the issuer singles out the relevant activity using the txid, GAID, and amount.

The data for our example asset looks like this when the example issuer calls the activities API and filters the returned data to only show the investor receive activity:

{
    "type": "transfer",
    "datetime": "2021-02-11T09:10:58Z",
    "description": "Transfer to wallet GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C",
    "txid": "e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea",
    "vout": 0,
    "blockheight": 1188465,
    "investor": null,
    "GAID": "GA2eP3RJnt4CYHNmc8GwbwGfpzCz5C",
    "amount": 100,
    "asset_blinder": "7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3",
    "amount_blinder": "3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838",
    "registered_user": null
}

The issuer then selects and saves the output blinders they are interested in, using the following format, and saves it to a file named blinders.json:

{
    "inputs":[],
    "outputs":[
        {
            "asset_id_hex": "f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2",
            "asset_blinder_hex": "7dadf08a4b5750f4795a48f7ede24732ea6efca3dc46c51806d332fdd6464cc3",
            "amount_satoshi": 100,
            "amount_blinder_hex": "3d48822703130651d51583438f5d4692b337b9483a1b4d81d158672a4f288838"
        }
    ]
}

📘

Note

You can include as many outputs as you like for the transaction in the format shown above, i.e. if you wanted to include the change output as well, you can. We will just use the output where the investor received the asset in our example.

Now the issuer can execute the proof like this:

python verify-elements-commitments.py --tx transaction.txt --blinded blinders.json

Which, in our example case, will output:

{
  "txid": "e6d8feb9a539628fe9386570fc6c546315a1ce37d64ce104fc742545c45102ea",
  "inputs": [],
  "outputs": [
    {
      "vout": 0,
      "asset": "f266a3f15e78b71481adfedff9aefe47c69501a181ffc68527bb5fb26da6a4b2",
      "satoshi": 100
    }
  ]
}

Using the verify-elements-commitments.py Python script you can verify the blinders yourself, without having to trust the Blockstream Liquid explorer’s implementation of the same process.