Proof of Issuance - Blockstream’s Liquid Asset Registry

This example shows how to use Blockstream’s Liquid Asset Registry. The asset registry allows you to register an asset and prove ownership against a domain name.

The code below creates an asset, an associated token, and outputs two files:

  • A liquid-asset-proof-<asset-id> file that must be placed on the server of the registered domain over the entire lifecycle of the asset.

  • A register_asset.sh file that, when run, will post the asset registration data to Blockstream’s Liquid Asset Registry.

The use of the output files will be explained later.

There are six fields that need setting within the script:

  • NAME The name of the asset as it will apear in the registry and applications that use asset registry data, such as the Blockstream Explorer. Length must be 5 to 255 ASCII characters.

  • TICKER The ticker you would like to assign to the asset. Length must be 3 to 5 characters. Valid characters are: a-z A-Z . and -. If provided, ticker has to be unique when combined with the domain field.

  • DOMAIN The domain that will be used to verify the asset. Must be a valid domain name format and only supports (sub)domain names, for example: example.com or sub.example.com. Do not include the http/s or www prefixes. To verify you control the entity domain name, you’ll need to place a file on your webserver. Note that serving the file with https is required, except for .onion hidden services. The file should be made available at https://<domain>/.well-known/liquid-asset-proof-<asset-id>, with the following contents (See line 133 of the code example below for an example of file content generation): Authorize linking the domain name <domain> to the Liquid asset <asset-id>

  • ASSET_AMOUNT The amount to be issued. It is preferable to consider one satoshi unit as representing one asset. So to issue an amount of 100 of an asset, a value of 0.00000100 should be used. Please note that how this value is displayed within applications that use asset registry data is affected by the PRECISION field.

  • TOKEN_AMOUNT The amount of reissuance tokens to be created. It is preferable to consider one satoshi unit as representing one reissuance token, so a value of 0.00000001 will create 0.00000001 reissuance tokens and when viewed from Liquid Core it will also show as 0.00000001. When viewed from an app that uses satoshi sized units, such as the Blockstream Explorer it will show as 1. This field is not affected by the PRECISION field.

  • PRECISION The precision used to display the asset amount within applications that use Liquid Asset Registry data, such as the Blockstream Explorer. Represents the number of digits after the decimal point, i.e. 0 for non-divisible assets or 8 for BTC-like divisible assets.

Examples: a PRECISION value of 0 for an ASSET_AMOUNT of 0.00000100 will create a value of 0.00000100 when viewed from Liquid Core and 100 when viewed in an app that uses the asset registry data. A PRECISION value of 2 for an ASSET_AMOUNT of 0.00000100 will create a value of 0.00000100 when viewed from Liquid Core and 1.00 when viewed in the same app. The default is 0 and the maximum value is 8.

Note that the script also sets the PUBKEY variable’s value by calling getaddressinfo with a new address generated by the node’s wallet (line 52). The value of PUBKEY is then used to set the issuer_pubkey registration data field (line 64). The issuer_pubkey data field is used by the Asset Registry to verify ownership when removing or updating registry data in the future, and so the following should be noted:

  • PUBKEY holds the public key value that goes into the issuer_pubkey registration data field. The code example derives this from a new address generated by a private key in the node’s wallet.dat file. For safety, this file should be backed up after running the script. Any ECDSA secp256k1 public key could be used in its place, which may be of use when the issuing entity is not the same as the entity running the script. You should ensure that, whatever PUBKEY value you use to set the registration data’s issuer_pubkey field, it is recorded somewhere and that the corresponding private key is backed up for potential future use in authorizing amendments to the registry data.

The code below uses Liquid in live mode. The code can easily be adapted for test use.

Save the code below in a file named issue_and_prepare_register.sh and take a back up of the script before you run it as you may wish to refer to it in the future to track how you created the registry data.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/bin/bash
set -x
shopt -s expand_aliases

# ASSUMES elementsd IS ALREADY RUNNING

######################################################
#                                                    #
#    SCRIPT CONFIG - PLEASE REVIEW BEFORE RUNNING    #
#                                                    #
######################################################

# Amend the following:
NAME="Wintercoin"
TICKER="WTX"
# Do not use a domain prefix in the following:
DOMAIN="wintercooled.github.io"
# Issue 100 assets using the satoshi unit, dependant on PRECISION when viewed from
# applications using Asset Registry data.
ASSET_AMOUNT=0.00000100
# Issue 1 reissuance token using the satoshi unit, unaffected by PRECISION.
TOKEN_AMOUNT=0.00000001

# Amend the following if needed:
PRECISION=0

# Don't change the following:
VERSION=0

# If your Liquid node has not seen enough transactions to calculate
# its own feerate you will need to specify one to prevent a 'feeRate'
# error. We'll assume this is the case:
FEERATE=0.00003000

# Change the following to point to your elements-cli binary and liquid data directory.
alias e1-cli="$HOME/elements-0.17.0.1/bin/elements-cli -datadir=$HOME/.elements"

##############################
#                            #
#    END OF SCRIPT CONFIG    #
#                            #
##############################

# Exit on error
set -o errexit

# We need to get a 'legacy' type (prefix 'CTE') address for this:
NEWADDR=$(e1-cli getnewaddress "" legacy)

VALIDATEADDR=$(e1-cli getaddressinfo $NEWADDR)

PUBKEY=$(echo $VALIDATEADDR | jq '.pubkey' | tr -d '"')

ASSET_ADDR=$NEWADDR

NEWADDR=$(e1-cli getnewaddress "" legacy)

TOKEN_ADDR=$NEWADDR

# Create the contract and calculate the contract hash.
# The contract is formatted for use in the Blockstream Asset Registry
# Do not amend the following!

CONTRACT='{"entity":{"domain":"'$DOMAIN'"},"issuer_pubkey":"'$PUBKEY'","name":"'$NAME'","precision":'$PRECISION',"ticker":"'$TICKER'","version":'$VERSION'}'

# We will hash using openssl, other options are available
CONTRACT_HASH=$(echo -n $CONTRACT | openssl dgst -sha256)
CONTRACT_HASH=$(echo ${CONTRACT_HASH#"(stdin)= "})

# Reverse the hash. This will be calculated from the contract by the asset registry service to
# check validity of the issuance against the registry entry.
TEMP=$CONTRACT_HASH

LEN=${#TEMP}

until [ $LEN -eq "0" ]; do
    END=${TEMP:(-2)}
    CONTRACT_HASH_REV="$CONTRACT_HASH_REV$END"
    TEMP=${TEMP::-2}
    LEN=$((LEN-2))
done

RAWTX=$(e1-cli createrawtransaction '''[]''' '''{"''data''":"''00''"}''')

# If your Liquid node has seen enough transactions to calculate its
# own feeRate then you can switch the two lines below. We'll default
# to specifying a fee rate:
#FRT=$(e1-cli fundrawtransaction $RAWTX)
FRT=$(e1-cli fundrawtransaction $RAWTX '''{"''feeRate''":'$FEERATE'}''')

HEXFRT=$(echo $FRT | jq '.hex' | tr -d '"')

RIA=$(e1-cli rawissueasset $HEXFRT '''[{"''asset_amount''":'$ASSET_AMOUNT', "''asset_address''":"'''$ASSET_ADDR'''", "''token_amount''":'$TOKEN_AMOUNT', "''token_address''":"'''$TOKEN_ADDR'''", "''blind''":false, "''contract_hash''":"'''$CONTRACT_HASH_REV'''"}]''')

# Details of the issuance...
HEXRIA=$(echo $RIA | jq '.[0].hex' | tr -d '"')
ASSET=$(echo $RIA | jq '.[0].asset' | tr -d '"')
ENTROPY=$(echo $RIA | jq '.[0].entropy' | tr -d '"')
TOKEN=$(echo $RIA | jq '.[0].token' | tr -d '"')

# Blind, sign and send the issuance transaction...
BRT=$(e1-cli blindrawtransaction $HEXRIA true '''[]''' false)

SRT=$(e1-cli signrawtransactionwithwallet $BRT)

HEXSRT=$(echo $SRT | jq '.hex' | tr -d '"')

# Test the transaction's acceptance into the mempool
TEST=$(e1-cli testmempoolaccept '''["'$HEXSRT'"]''')
ALLOWED=$(echo $TEST | jq '.[0].allowed' | tr -d '"')

# If the transaction is valid
if [ "true" = $ALLOWED ] ; then
    # Broadcast the transaction
    ISSUETX=$(e1-cli sendrawtransaction $HEXSRT)
else
    echo "ERROR SENDING TRANSACTION!"
fi

#####################################
#                                   #
#    ASSET REGISTRY FILE OUTPUTS    #
#                                   #
#####################################

# Blockstream's Liquid Asset Registry (https://assets.blockstream.info/) can be used to register an asset to an issuer.
# We already have the required data and have formatted the contract plain text into a format that we can use for this.

# Write the domain and asset ownership proof to a file. The file should then be placed over the entire lifecycle of
# the asset in a directory within the root of your domain named ".well-known"
# The file should have no extension and just copied as it is created.

echo "Authorize linking the domain name $DOMAIN to the Liquid asset $ASSET" > liquid-asset-proof-$ASSET

# After you have placed the above file without your domain you can run the register_asset.sh script created below to post the asset data to the registry.

echo "curl https://assets.blockstream.info/ --data-raw '{\"asset_id\":\"$ASSET\",\"contract\":$CONTRACT}'" > register_asset.sh

# For reference, write some asset details. These are not needed by the asset registry.

echo "ISSUETX:$ISSUETX ASSET:$ASSET ENTROPY:$ENTROPY TOKEN:$TOKEN ASSET_AMOUNT:$ASSET_AMOUNT TOKEN_AMOUNT:$TOKEN_AMOUNT ASSET_ADDR:$ASSET_ADDR TOKEN_ADDR:$TOKEN_ADDR CONTRACT_HASH_REV:$CONTRACT_HASH_REV" > liquid-asset-ref-$ASSET

##################################################################

echo "Completed without error"

When you have saved the above to the file, edit the variables at the top and of the file and start elements-qt or elementsd using an argument of -server=1 to allow the Liquid client to communicate with it. Execute the script from the directory you created it in by opening a Terminal session and running:

bash issue_and_prepare_register.sh

In order to register the asset just created:

  • Wait a couple of minutes for the issuance transaction to confirm.

  • Place the liquid-asset-proof-<asset-id> file in a folder named .well-known in the root of your domain. This proof should be stored there over the entire lifecycle of the asset.

  • Run the register_asset.sh script.

For example, if your domain was www.your-example-domain-here.com and the asset id generated was 123abc (it will of course be much longer) then the file generated would be named:

liquid-asset-proof-123abc

The domain variable in the code above would be set to:

your-example-domain-here.com

So the path used to check asset to domain registry would end up being:

www.your-example-domain-here.com/.well-known/liquid-asset-proof-123abc

Once that file is accessible you can then run the register_asset.sh script and, when the required checks against the domain and issuance transaction have been made, the registration will be found on Blockstream’s Liquid Asset Registry.

Deleting Asset Registration Data

Deleting an asset from the Blockstream Asset Registry only removes its metadata from the registry. It does not, and cannot, delete the asset itself from the Liquid Blockchain.

To delete an asset from the Blockstream Asset Registry, sign the following message using the issuer_pubkey used when registering the asset:

remove <asset-id> from registry

Then submit the deletion request with the base64-encoded signature:

$  curl -X DELETE https://assets.blockstream.info/<asset-id> -H 'Content-Type: application/json' \
    -d '{"signature":"<base64-encoded-signature>"}'