web/docs/liquid/liquid-part2.md
2024-01-26 10:07:39 -06:00

10 KiB

DeepDive into Liquid: Part II - Asset Creation and Configuration

This is a multipart DeepDive that will focus on the Liquid Sidechain. It will be released in 3 Parts:

  • Part I: Overview, Installation, and first Peg-In
  • Part II (this): Asset Creation and Configuration
  • Part III: Advanced Topics and Peg-out

Assets

In Part I, we succesfully Pegged-In our BTC and received L-BTC, now lets use that L-BTC to do some interesting things with Asset Issuance:

  • Let's begin by checking out these 2 commands1:

    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS getwalletinfo

    "balance": {"bitcoin": 0.00149155}

    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS dumpassetlabels

    {"bitcoin": "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d"}

This is telling us that bitcoin is a label for an asset that has the UUID of 6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d. Everything in Liquid is an 'Asset' (including L-BTC) and each of these Assets can have different features.

Looking up this value on https://blockstream.info/liquid/assets we can see some info about it:

This shows us the total amount of L-BTC in circulation, additionally we can see how much was Pegged-In, Out, and Burned. Most notably, this confirms that everything is in balance. The Circulating Supply = (PEG_IN - PEG_OUT - BURNED). No inflation happening.

Issuing our own Asset

Issuing assets is pretty straight forward, it can be roughly broken down into 3 main steps2:

  1. Generate a Legacy Address3 and PUBKEY for that address
  2. Generate a Contract Hash
  3. Issue the Asset
  • STEP 1a: Let's generate our legacy address and set the output to a variable $ LEG_ADDRESS=$(sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS getnewaddress "" legacy)

    lq1qq2s92z0uq78kd4gfepua6qvfwx40g4lqqer9e3tzrr0j04ugyzsed39mnh9wvuhw0hmaqy5mxs9egqrr7j3rx8j2sdyun9m90

  • STEP 1b: Set this address to a variable for use later and generate the public key.

    • $ export LEG_ADDRESS="lq1qq2s92....."
    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS getaddressinfo $LEG_ADDRESS | jq '.pubkey'

    025aa49d444a150c99c904c8e779e5317aff4aee15ee9171f450e14af9dd8b8780

    • $ export PUBKEY="025aa49d444a150c99c904c8e779e5317aff4aee15ee9171f450e14af9dd8b8780"
  • STEP 2: In order to generate the contract hash, it requires a few steps - so I've simplified it by making a bash script. Copy this into a file named gen_asset_contract.sh and mark as executable

#!/bin/bash
#set -x

shopt -s expand_aliases

### FILENAME: gen_asset_contract.sh
### USAGE: ./gen_asset_contract.sh PUBKEY

if [ -z "$1" ]
  then
    echo "Please supply PUBKEY before running"
    exit 1
fi

PUBKEY=$1

###EDIT THESE VALUES###

DOMAIN="nulldata.org"
NAME="StackerNews-Demo1"
PRECISION=8
TICKER="SND-1"
CONTRACT='{"entity":{"domain":"'$DOMAIN'"},"issuer_pubkey":"'$PUBKEY'","name":"'$NAME'","precision":'$PRECISION',"ticker":"'$TICKER'","version":'0'}'
CONTRACT_HASH=$(echo -n "${CONTRACT}" | sha256sum | sha256sum | sed 's/ .*//g')

#Generate a byte-aligned reveresed CONTRACT_HASH for use in asset Creation
TEMP=$CONTRACT_HASH
LEN=${#TEMP}
until [ $LEN -eq "0" ]; do
  END=${TEMP:(-2)}
  CONTRACT_HASH_REV="$CONTRACT_HASH_REV$END"
  TEMP=${TEMP::$((${#TEMP} - 2))}
  LEN=$((LEN-2))
done

### END EDITING HERE ###
echo CONTRACT=$CONTRACT
echo CONTRACT_HASH=$CONTRACT_HASH
echo CONTRACT_HASH_REV=$CONTRACT_HASH_REVT_HASH_REV=$CONTRACT_HASH_REV
  • STEP 2: Now we can use the script we saved before to generate the contract:

    • $ ./gen_asset_contract.sh $PUBKEY

    CONTRACT={"entity":{"domain":"nulldata.org"},"issuer_pubkey":"025aa49d444a150c99c904c8e779e5317aff4aee15ee9171f450e14af9dd8b8780","name":"StackerNews-Demo1","precision":8,"ticker":"SND-1","version":0}

    CONTRACT_HASH=df4bd5aec71f808f78b752cbaac783019fa20268265f66baa406e4202b77e4a1

    CONTRACT_HASH_REV=a1e4772b20e406a4ba665f266802a29f0183c7aacb52b7788f801fc7aed54bdf

  • STEP 2: Finally export those variables via bash to use later:

export CONTRACT={"entity":{"domain":"nulldata.org"},"issuer_pubkey":"025aa49d444a150c99c904c8e779e5317aff4aee15ee9171f450e14af9dd8b8780","name":"StackerNews-Demo1","precision":8,"ticker":"SND-1","version":1}

export CONTRACT_HASH=7d6bf3e3228fd00e9591c4c6bd1163d088a387e95d29fdfcf2e4e5035a137a94

export CONTRACT_HASH_REV=947a135a03e5e4f2fcfd295de987a388d06311bdc6c491950ed08f22e3f36b7d
  • STEP 3: Now Issuing our own asset is straight-forward, as its a single line command in the client. Let's issue 10 new tokens and set the supply to be fixed (no re-issuance)

    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS issueasset 10 0 true $CONTRACT_HASH_REV

    { "txid": dd3983619f67e7a743ccfd32e48bbdb591c1d44b86a71d442be95a2453c0479a, "vin": 0, "entropy": cd3d7319525b28b6500d0a0b4688d91392ba8192be9bc264ec9637a001907322, "asset": 1e69f87952c26b8dfc80dda0427041a0279a0723ea12086b88b75752545e0d22, "token": 2dc5fb795fd42e08adb9dd29b5ff14f4326ecd8443e1cdc31dc6666d7576ff89 }

  • Checking out our wallet we see:

    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS getwalletinfo

    "balance": {"1e69f87952c26b8dfc80dda0427041a0279a0723ea12086b88b75752545e0d22": 10.00000000,"bitcoin": 0.00148701}

  • We can see our wallet has 10 of our assets4, lets checkout what the Liquid network knows about it:

  • So publiclly we know the following about our new asset:
    • It cannot be re-issued
    • It has only been issued once
    • However both the issued amount and the circulating supply has been hidden from us.

Issuing a Non-Confidential Asset

  • Let's create another asset and this time choose a different option (specifically, lets create this asset unblinded):

    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS issueasset 10 0 false

    {"txid": "1dfdf2f3a393a7a7be9f1f58c8651565a29486851268027794e1b69f5227864f", "vin": 0, "entropy": "5a32df406eb3f8c4f9b59c669d411e3a5ed0fd75058c2483e5732ccc6b482722", "asset": "03d63d6c373979954a4dda2afdd941a0de96a0739da455af1cdb1a2e2a63a9a5", "token": "ac860f370aabb829b70283c05e13a327cccec8ad0eea5493980cca53dd6631f9"}

...and checking again what the Liquid network knows about it

  • Now we can see the total issued amount / supply4

  • Let's try sending this new unblinded asset to another wallet (my Blockstream Green mobile wallet) and see what is visible on the network.

    • $sudo ./scripts/app compose elements exec node elements-cli -named -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS sendtoaddress address="VJL6Thq......" amount=1.0 fee_rate=1 assetlabel="03d63d6c...."

    edf087368703bbb99054b4c5d7d1a85a9fbc4ad1ba9eac97c71463eb822dea3b

  • Looking up this transaction on the block explorer, we see:

This is interesting: So although the transaction is visible, we can't see what was actually transferred (even though the details of the asset are visible, it is still hidden inside the transaction). Further, where is my Green Wallet address (ie. VJL6Th...)? We can see 3 output addresses, presumably these are: Change, Fee, and Receipiant...but which is my Green Wallet?

So now this shows us that the Confidential Address: VJL6Thq.... corresponds to the Unconfidential Address: GprWqLK.... Through the magic of one-way hashes, if we know the "Confidential" address we can then dervive its public address....however just by looking at the public blockchain, there is no way to reverse that hash.

Now, lets look at how things look on the Green mobile wallet:

We can see that we've received 100,000,000 'sats' of our custom asset. However, notice how there is no labels. The elements for L-BTC, USDT, etc show with ticker symbols...whereas ours is showing just the contract address....not very user friendly. Let's see if we can remedy.

Registering our Asset

The publication of asset metadata info on Liquid works on a .well-known system where specific files are published on a webserver that you control. There are a few different steps to accomplish this, so lets begin

  • The first step is we need to generate a legacy address in case we ever want to delete the asset from the registry later2

    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS getnewaddress "" legacy

    lq1qq2s92.....

  • Set this address to a variable for use later and generate the public key.

    • $ export DELETE_ADDRESS="lq1qq2s92....."
    • $ sudo ./scripts/app compose elements exec node elements-cli -rpcuser=$E_RPCUSER -rpcpassword=$E_RPCPASS getaddressinfo $DELETE_ADDRESS | jq '.pubkey'

    025aa49d444a150c99c904c8e779e5317aff4aee15ee9171f450e14af9dd8b8780

    • $ export PUBKEY="025aa49d444a150c99c904c8e779e5317aff4aee15ee9171f450e14af9dd8b8780"
  • We need to set some variable and then generate a json 'contract' file


  1. The values E_RPCUSER and E_RPCPASS, where environmental variables we set in Part I ↩︎

  2. Technically just running elements-cli issueasset 10 0 is enough to issue an asset, however there will be limitations later on when trying to register the asset, burn, remove, etc. ↩︎

  3. Using a legacy address for this task imposes no real implications to your security since its only used to remove assets from the registry. Its possible to do this with a non-legacy address, but more steps are involved and we will need to use other tools rather than what's already built-in to Elements node, so we will just use a legacy address for this. ↩︎

  4. Like in bitcoin, 1 integer unit is 100,000,000 base units (not sats, but equivalent concept) ↩︎