Elements code tutorial

Using Confidential Transactions

All addresses in Elements are, by default, blinded using Confidential Transactions. Blinding is the process by which the amount and type of asset being transferred are cryptographically hidden from everyone except the sending and receiving parties. This is done using a blinding key, which we will look at later.

Imagine that our cryptographic friends Alice and Bob are the owners of the Elements nodes we are running and the contents of the wallets they control. Alice owns the e1 wallet and Bob owns e2. We’ll have Bob send some assets to himself using a blinded Elements address as the destination.

NOTE: The Advanced Examples section shows you how to create and use multi-sig addresses.

First generate a new address and store it in a variable named “ADDR” so we can recall it for use later:

ADDR=$(e2-cli getnewaddress)

To see the new address we generated for Bob we can print out the value that was stored in the “ADDR” variable.

echo $ADDR
NOTE: We will use the “store the value returned from an RPC command in a variable for future use” technique throughout the rest of the tutorial. We’ll print out the contents of a variable every now and again when highlighting something that has been returned. You can always just echo out the contents of any others as we go along should you want to.

After running the echo command above you should see something similar to this:

NOTE: As of Elements v0.18, getnewaddress defaults to creating blinded bech32 addresses. You can create ‘CTE’ prefixed addresses (the Elements pre-0.17 default) by calling getnewaddress like this: e1-cli getnewaddress "" legacy. You can also set the addresstype=legacy argument on node startup, or set it in your config file to always get legacy addresses from ‘getnewaddress’. Some commands require a ‘legacy’ style address in order to work, such as message signing, as shown in the Advanced examples section.

Let’s look at the address in more detail to check that it is indeed a confidential one. To do this we can use the “getaddressinfo” command, passing in the address that we stored in the ADDR variable as a parameter:

e2-cli getaddressinfo $ADDR

You should see a long value for the “confidential_key” property. It will look something like this:

"confidential_key": "030788da8d9ca229cbe57e346daaf8d94cba3ed548b41922a8abefaec91ff1abb1"

The confidential_key is the public blinding key, which has been added to the address and is the reason why a confidential address is so long. You will also see that the ‘getaddressinfo’ command shows an associated ‘unconfidential’ address, which can be used to receive assets if you don’t want to make use of the Confidential Transaction feature for some reason.

We’ll now send an amount of 1 “bitcoin” from Bob’s wallet to the new address we generated for him:

TXID=$(e2-cli sendtoaddress $ADDR 1)
NOTE: To manually create a transaction using the createrawtransaction command, please see the advanced code examples.

In order to have the transaction confirm we need to generate a block that will include it. As an aside we can now query the mempool of each of our Elements nodes to see the transaction waiting to be added to a block as well as the current block count of each node’s blockchain:

e1-cli getrawmempool
e2-cli getrawmempool
e1-cli getblockcount
e2-cli getblockcount

Both should display just one transaction with the same ID as that stored in the “TXID” variable and a block value of 202 (the blocks that we generated by twice running “generate 101” further up). If it does not, wait a few seconds and try the calls again as it may take a moment for the nodes to synchronize. Now let’s generate a block and get the transaction confirmed for Bob:

e2-cli generatetoaddress 1 $ADDRGEN2

Checking the mempool again for each client will show that it is now empty:

e1-cli getrawmempool
e2-cli getrawmempool

Checking the block count for each client should show 203.

e1-cli getblockcount
e2-cli getblockcount

Note that although Bob sent an amount of 1 “bitcoin” to himself the net effect is that he now has slightly less than he did before; this is because some of the transaction amount was spent on fees. Although Bob mined the block, and will later collect the fees, he will need to wait until the block has matured before he sees it as spendable within his wallet.

NOTE: Sidechain fees are defaulted to being paid in “bitcoin”. This can be changed using the feeasset startup parameter.

The above shows that the client’s blockchains and mempools are in sync. If they are not, wait a few seconds and try the calls above again as it may take a moment for the nodes to synchronize. They display the same results not because they share a common data file (indeed, they do not), but because they are connected nodes on the same Elements network and broadcast transactions and blocks between each other in very much the same way as Bitcoin nodes do. You can check this for yourself by looking at the separate data files in the following locations and noting that they are separate stores of the same blockchain data:

/$HOME/elementsdir1/elementsregtest /$HOME/elementsdir2/elementsregtest

Now let’s examine the transaction as it is seen by Bob’s wallet and also how it is seen from the point of view of Alice’s wallet. First the view from Bob’s wallet:

e2-cli gettransaction $TXID

The output from that initially looks like just a huge random assortment of letters and numbers (the hex value of the transaction), but if you scroll up you will see some more readable content above that.

Looking in the “details” section near the top, you will see that there are two amount values:

"details": [ { ... "category": "send", "amount": -1.00000000, ... }, { ... "category": "receive", "amount": 1.00000000, ... } ]

And so we can confirm that Bob’s wallet can view the actual amounts being sent and received in this transaction. This is because the blinded transaction was sent from Bob’s own wallet and so it has access to the required data to unblind the amount values. You will also see two other properties and their values within the two details sections: “amountblinder” and “assetblinder”. These indicate that both the asset amount and the type of asset were blinded. This ensures that wallets without knowledge of the blinding key are prevented from viewing them.

Looking at the transaction from Alice’s wallet we would expect both amount and type to be unknown. Checking this using Alice’s wallet we initially get an error:

e1-cli gettransaction $TXID

The reason that we get an error using this command is that Alice’s wallet will not contain details of the transaction. We can get the raw transaction data from Alice’s node’s copy of the blockchain using the getrawtransaction command like this:

e1-cli getrawtransaction $TXID 1

That returns raw transaction details. If you look within the “vout” section you can see that there are three instances. The first two instances are the receiving and change amounts and the third is the transaction fee. Of these three amounts, the fee is the only one in which you can see a value, as the fee itself is unblinded. For the first two instances you will see (amongst others) properties with values similar to this:

"value-minimum": 0.00000001, "value-maximum": 45035996.27370496, ... "valuecommitment": "089dac0ee066b8c126ea69e2a9274cff2bc35d0bcd048e1e36baf4ffa74a80188d", "assetcommitment": "0a80020aeca516f22abb6c952bd4e4d1eaa013fdde6f98289a2fb9f0b6dcd03142",

What this shows are the “blinded ranges” of the value amounts and the commitment data that acts as proof of the actual amount and type of asset transacted.

Even if we were to import Bob’s private key into Alice’s wallet it would still not be able to see the amounts and type of asset because it still has no knowledge of the blinding key used.

If we want to let Alice’s wallet view the actual amount details we’ll need to import the address so gettransaction will work, and then import the blinding key so we can see the unblinded amounts. First, import and then view the transaction. Note the use of the second argument, set to true. This tells gettransaction to include watch only addresses, such as the one we imported.

e1-cli importaddress $ADDR
e1-cli gettransaction $TXID true

But because Alice still does not know the blinding key, the amount (towards the top of the output) will show as:

"amount": { "bitcoin": 0.00000000

Without knowledge of the Blinding Key, the amount and type of asset being transacted are hidden.

In order for anyone else (such as an auditor) to view the amount and type of assets being transacted, they need to know the blinding key that was used to generate the blinded address. To show this, we can export the blinding key Bob’s wallet used, import it into Alice’s wallet and try to view the transaction again. Let’s export the key for that particular address from Bob’s wallet and import it into Alice’s in one step:

e1-cli importblindingkey $ADDR $(e2-cli dumpblindingkey $ADDR)

Now that Alice’s wallet has knowledge of the blinding key used on that address, we can run the checks we did above from Alice’s wallet, this time expecting to see the actual amount value:

e1-cli gettransaction $TXID true
"amount": { "bitcoin": 1.00000000

Magic! Alice’s wallet now shows the actual value sent in the transaction.

We’ve seen that the use of a blinding key hides the amount and type of assets in an address and that by importing the right blinding key, we can reveal those values. In practical use, a blinding key may be given to an auditor, should there be a need to verify the amounts and types of assets held by a party. The Confidential Transactions feature of Elements also allows for “range proofs” to be performed without the need to expose actual amounts. This allows statements such as “address abc holds at least an amount x of asset y” to be cryptographically proven as true or false.

In the next section we will look at how to issue, label, and re-issue your own assets with Elements.

Next: Issuing your own assets