Elements code tutorial

An easy way to run the main tutorial’s code

Rather than have to copy and paste or type in each line of code in the tutorial, you can use the code below.

Save the code in a file named runtutorial.sh and place it in your home directory.

To run this code just open a terminal in your $HOME directory and run:

bash runtutorial.sh

Then press the return key to execute each line in turn.

More advanced examples, like manual ‘raw’ issuance of an asset, can be found here.


Note: If you want to run some of the steps automatically and then have execution stop and wait for you to press enter before continuing one line at a time: move the trap read debug statement down so that it is above the line you want to stop at. Execution will run each line automatically and stop when that line is reached. It will then switch to executing one line at a time, waiting for you to press return before executing the next command.

You will see that occasionally we will use the sleep command to pause execution. This allows the daemons time to do things like stop, start and sync mempools.

It is perhaps a good idea to have the relevant tutorial pages open as you run through this code for reference, as it is not itself annotated in any meaningful way.

There is a chance that the “ and ‘ characters may not paste into your runtutorial.sh file correctly, so type over them yourself if you come across any issues executing the code.
#!/bin/bash
set -x

# This code is based upon the Python example at https://github.com/ElementsProject/elements found within the contrib/assets_tutorial folder

################################
#
# Save this code in a file named runtutorial.sh and place it in your home directory.
#
# To run this code just open a terminal in your home directory and run:
# bash runtutorial.sh
#
# If you want to run some of the steps automatically and then have execution stop
# and wait for you to press enter before continuing one line at a time: uncomment 
# and move the 'trap read debug' statement down so that it is above the line you want
# to stop at. Execution will run each line automatically and stop when that line is 
# reached. It will then switch to executing one line at a time, waiting for you 
# to press return before executing the next command.
# 
# You will see that occasionally we will use the **sleep** command to pause execution.
# This allows the daemons time to do things like stop, start and sync mempools. You
# can probably decrease the sleeps without issue. The numbers used below are so a low
# powered machine like a Raspberry Pi can run without incident.
# 
# It is perhaps a good idea to have the relevant tutorial pages open as you run 
# through this code for reference, as it is not itself annotated in any meaningful way.
#
# There is a chance that the " and ' characters may not paste into your 
# **runtutorial.sh** file correctly, so type over them yourself if you come 
# across any issues executing the code.
#
# Press Ctrl + c to stop script execution
#
#################################

# Remove to run without stopping:
# trap read debug

shopt -s expand_aliases

# Ensure that bitcoind, bitcoin-cli, elementsd, and elements-cli are in the PATH

alias b-dae="bitcoind -datadir=$HOME/bitcoindir"
alias b-cli="bitcoin-cli -datadir=$HOME/bitcoindir"

alias e1-dae="elementsd -datadir=$HOME/elementsdir1"
alias e1-cli="elements-cli -datadir=$HOME/elementsdir1"

alias e2-dae="elementsd -datadir=$HOME/elementsdir2"
alias e2-cli="elements-cli -datadir=$HOME/elementsdir2"

echo "The following 3 lines may error - that is fine."

# Ignore error
set +o errexit

b-cli stop
e1-cli stop
e2-cli stop
sleep 15

echo "The following 3 'rm' commands may error - that is fine."

rm -r ~/bitcoindir ; rm -r ~/elementsdir1 ; rm -r ~/elementsdir2
mkdir ~/bitcoindir ; mkdir ~/elementsdir1 ; mkdir ~/elementsdir2

echo -n "regtest=1
txindex=1
daemon=1
rpcuser=user3
rpcpassword=password3
fallbackfee=0.0002
[regtest]
rpcport=18888
port=18889
" > ~/bitcoindir/bitcoin.conf

echo -n "chain=elementsregtest
rpcuser=user1
rpcpassword=password1
daemon=1
server=1
listen=1
txindex=1
validatepegin=1
mainchainrpcport=18888
mainchainrpcuser=user3
mainchainrpcpassword=password3
initialfreecoins=2100000000000000
fallbackfee=0.0002
[elementsregtest]
rpcport=18884
port=18886
anyonecanspendaremine=1
connect=localhost:18887
" > ~/elementsdir1/elements.conf

echo -n "chain=elementsregtest
rpcuser=user2
rpcpassword=password2
daemon=1
server=1
listen=1
txindex=1
mainchainrpcport=18888
mainchainrpcuser=user3
mainchainrpcpassword=password3
initialfreecoins=2100000000000000
fallbackfee=0.0002
[elementsregtest]
rpcport=18885
port=18887
anyonecanspendaremine=1
connect=localhost:18886
" > ~/elementsdir2/elements.conf

b-dae

sleep 10

# Create new wallet
b-cli createwallet ""

# Wait for bitcoin node to finish startup and respond to commands
until b-cli getwalletinfo
do
  echo "Waiting for bitcoin node to finish loading..."
  sleep 2
done

e1-dae
e2-dae

sleep 10

# Create new wallets
e1-cli createwallet ""
e2-cli createwallet ""
e1-cli rescanblockchain
e2-cli rescanblockchain

# Wait for e1 node to finish startup and respond to commands
until e1-cli getwalletinfo
do
  echo "Waiting for e1 to finish loading..."
  sleep 2
done

# Wait for e2 node to finish startup and respond to commands
until e2-cli getwalletinfo
do
  echo "Waiting for e2 to finish loading..."
  sleep 2
done

# Exit on error
set -o errexit

### Basic Operations ###

ADDRGENB=$(b-cli getnewaddress)
ADDRGEN1=$(e1-cli getnewaddress)
ADDRGEN2=$(e2-cli getnewaddress)

e1-cli sendtoaddress $(e1-cli getnewaddress) 21000000 "" "" true
e1-cli generatetoaddress 101 $ADDRGEN1
sleep 10
e1-cli sendtoaddress $(e2-cli getnewaddress) 10500000 "" "" false
e1-cli generatetoaddress 101 $ADDRGEN1
sleep 10

e1-cli getwalletinfo
e2-cli getwalletinfo

ADDR=$(e2-cli getnewaddress)

echo $ADDR

e2-cli getaddressinfo $ADDR

TXID=$(e2-cli sendtoaddress $ADDR 1)

sleep 10

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

e2-cli generatetoaddress 1 $ADDRGEN2
sleep 10

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

e2-cli gettransaction $TXID

# Ignore error
set +o errexit

echo "This may error - that is ok, not aware of tx"
e1-cli gettransaction $TXID

# Exit on error
set -o errexit

e1-cli getrawtransaction $TXID 1

e1-cli importaddress $ADDR

e1-cli gettransaction $TXID true

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

e1-cli gettransaction $TXID true

### Issued Assets ###

e1-cli getwalletinfo

e1-cli dumpassetlabels

ISSUE=$(e1-cli issueasset 100 1)

ASSET=$(echo $ISSUE | jq -r '.asset')
TOKEN=$(echo $ISSUE | jq -r '.token')
ITXID=$(echo $ISSUE | jq -r '.txid')
IVIN=$(echo $ISSUE | jq -r '.vin')

echo $ASSET

e1-cli listissuances

e1-cli stop
sleep 15
e1-dae -assetdir=$ASSET:demoasset
sleep 10

# Ignore error
set +o errexit

# Wait for e1 node to finish startup and respond to commands
until e1-cli getwalletinfo
do
  echo "Waiting for e1 to finish loading..."
  sleep 2
done

# Exit on error
set -o errexit

e1-cli listissuances

e1-cli generatetoaddress 1 $ADDRGEN1

sleep 10
e2-cli listissuances

IADDR=$(e1-cli gettransaction $ITXID | jq -r '.details[0].address')
e2-cli importaddress $IADDR

# Or if the address is not known to e2 but the TXID is (requires index=1 in config file to work):
#ISSUE_RAW_TX=$(e2-cli getrawtransaction $ITXID 1)
#ISSUE_VOUTS=$(echo $ISSUE_RAW_TX | jq -r '.vout')
#VOUT_ADDRESS_ISSUE=$(echo $ISSUE_VOUTS | jq -r '.[0].scriptPubKey.addresses[0]')
#e2-cli importaddress $VOUT_ADDRESS_ISSUE

e2-cli listissuances

ISSUEKEY=$(e1-cli dumpissuanceblindingkey $ITXID $IVIN)

e2-cli importissuanceblindingkey $ITXID $IVIN $ISSUEKEY

e2-cli listissuances

E2DEMOADD=$(e2-cli getnewaddress)
e1-cli sendtoaddress $E2DEMOADD 10 "" "" false false 1 UNSET false demoasset
sleep 10
e1-cli generatetoaddress 1 $ADDRGEN1
sleep 10

e2-cli getwalletinfo
e1-cli getwalletinfo

E1DEMOADD=$(e1-cli getnewaddress)
e2-cli sendtoaddress $E1DEMOADD 10 "" "" false false 1 UNSET false $ASSET
sleep 10
e2-cli generatetoaddress 1 $ADDRGEN2
sleep 10
e1-cli getwalletinfo
e2-cli getwalletinfo

### Reissuing Assets ###

RTRANS=$(e1-cli reissueasset $ASSET 99)
RTXID=$(echo $RTRANS | jq -r '.txid')

e1-cli listissuances $ASSET

e1-cli gettransaction $RTXID

e1-cli generatetoaddress 1 $ADDRGEN1
sleep 10
RAWRTRANS=$(e2-cli getrawtransaction $RTXID)
e2-cli decoderawtransaction $RAWRTRANS

e1-cli getwalletinfo
e2-cli getwalletinfo

# Ignore error
set +o errexit

echo "This will error and that is expected:"
e2-cli reissueasset $ASSET 10

# Exit on error
set -o errexit

RITRECADD=$(e2-cli getnewaddress)
e1-cli sendtoaddress $RITRECADD 1 "" "" false false 1 UNSET false $TOKEN
e1-cli generatetoaddress 1 $ADDRGEN1
sleep 10
e1-cli getwalletinfo
e2-cli getwalletinfo

RISSUE=$(e2-cli reissueasset $ASSET 10)
e2-cli getwalletinfo

e2-cli generatetoaddress 1 $ADDRGEN2
sleep 10

e1-cli listissuances

RITXID=$(echo $RISSUE | jq -r '.txid')
RIADDR=$(e2-cli gettransaction $RITXID | jq -r '.details[0].address')
e1-cli importaddress $RIADDR

# Or if the address is not known to e1 but the TXID is (requires index=1 in config file to work):
#REISSUE_RAW_TX=$(e1-cli getrawtransaction $RITXID 1)
#REISSUE_VOUTS=$(echo $REISSUE_RAW_TX | jq -r '.vout')
#VOUT_ADDRESS_REISSUE=$(echo $REISSUE_VOUTS | jq -r '.[0].scriptPubKey.addresses[0]')
#e1-cli importaddress $VOUT_ADDRESS_REISSUE

e1-cli listissuances

UBRISSUE=$(e2-cli issueasset 55 1 false)

UBASSET=$(echo $UBRISSUE | jq -r '.asset')

e2-cli getwalletinfo

e2-cli generatetoaddress 1 $ADDRGEN2
sleep 10

e1-cli listissuances

UBRITXID=$(echo $UBRISSUE | jq -r '.txid')

UBRIADDR=$(e2-cli gettransaction $UBRITXID | jq -r '.details[0].address')
e1-cli importaddress $UBRIADDR

# Or if the address is not known to e1 but the TXID is (requires index=1 in config file to work):
#UBREISSUE_RAW_TX=$(e1-cli getrawtransaction $UBRITXID 1)
#UBREISSUE_VOUTS=$(echo $UBREISSUE_RAW_TX | jq -r '.vout')
#UBVOUT_ADDRESS_REISSUE=$(echo $UBREISSUE_VOUTS | jq -r '.[0].scriptPubKey.addresses[0]')
#e1-cli importaddress $UBVOUT_ADDRESS_REISSUE

e1-cli listissuances

e2-cli destroyamount $UBASSET 5
e2-cli getwalletinfo

### Block Signing ###

e1-cli generatetoaddress 1 $ADDRGEN1
sleep 10

ADDR1=$(e1-cli getnewaddress)
ADDR2=$(e2-cli getnewaddress)

VALID1=$(e1-cli getaddressinfo $ADDR1)
PUBKEY1=$(echo $VALID1 | jq -r '.pubkey')

VALID2=$(e2-cli getaddressinfo $ADDR2)
PUBKEY2=$(echo $VALID2 | jq -r '.pubkey')

KEY1=$(e1-cli dumpprivkey $ADDR1)
KEY2=$(e2-cli dumpprivkey $ADDR2)

MULTISIG=$(e1-cli createmultisig 2 '''["'''$PUBKEY1'''", "'''$PUBKEY2'''"]''')
REDEEMSCRIPT=$(echo $MULTISIG | jq -r '.redeemScript')
echo $REDEEMSCRIPT

e1-cli stop
e2-cli stop
sleep 15

SIGNBLOCKARGS=("-signblockscript=$(echo $REDEEMSCRIPT)" "-con_max_block_sig_size=214" "-evbparams=dynafed:0:::")

rm -r ~/elementsdir1/elementsregtest
rm -r ~/elementsdir2/elementsregtest

e1-dae ${SIGNBLOCKARGS[@]}
e2-dae ${SIGNBLOCKARGS[@]}

sleep 10

# Create new wallets
e1-cli createwallet ""
e2-cli createwallet ""
e1-cli rescanblockchain
e2-cli rescanblockchain

# Ignore error
set +o errexit

# Wait for e1 node to finish startup and respond to commands
until e1-cli getwalletinfo
do
  echo "Waiting for e1 to finish loading..."
  sleep 2
done

# Wait for e2 node to finish startup and respond to commands
until e2-cli getwalletinfo
do
  echo "Waiting for e2 to finish loading..."
  sleep 2
done

# Exit on error
set -o errexit

e1-cli importprivkey $KEY1
e2-cli importprivkey $KEY2

ADDRGEN1=$(e1-cli getnewaddress)
ADDRGEN2=$(e2-cli getnewaddress)

# Ignore error
set +o errexit

echo "This will error - that is ok:"
e1-cli generatetoaddress 1 $ADDRGEN1
e2-cli generatetoaddress 1 $ADDRGEN2

# Exit on error
set -o errexit

HEX=$(e1-cli getnewblockhex)

e1-cli getblockcount
 
e1-cli submitblock $HEX

e1-cli getblockcount

SIGN1=$(e1-cli signblock $HEX "$REDEEMSCRIPT")
SIGN2=$(e2-cli signblock $HEX "$REDEEMSCRIPT")

SIGN1DATA=$(echo $SIGN1 | jq '.[0]')
SIGN2DATA=$(echo $SIGN2 | jq '.[0]')

COMBINED=$(e1-cli combineblocksigs $HEX "[$SIGN1DATA,$SIGN2DATA]" "$REDEEMSCRIPT")

SIGNEDBLOCK=$(echo $COMBINED | jq -r '.hex')

e2-cli submitblock $SIGNEDBLOCK

e1-cli getblockcount
e2-cli getblockcount

e1-cli stop
e2-cli stop
sleep 15

### Sidechain - Peg-In ###

rm -r ~/elementsdir1/elementsregtest
rm -r ~/elementsdir2/elementsregtest

# When testing, you can also use the OP_TRUE script -fedpegscript=51 
# so that you do not have to provide any pubkey values as we do below.
FEDPEGARG="-fedpegscript=5221$(echo $PUBKEY1)21$(echo $PUBKEY2)52ae"

e1-dae $FEDPEGARG
e2-dae $FEDPEGARG
sleep 10

# Create new wallets
e1-cli createwallet ""
e2-cli createwallet ""
e1-cli rescanblockchain
e2-cli rescanblockchain

# Ignore error
set +o errexit

# Wait for e1 node to finish startup and respond to commands
until e1-cli getwalletinfo
do
  echo "Waiting for e1 to finish loading..."
  sleep 2
done

# Wait for e2 node to finish startup and respond to commands
until e2-cli getwalletinfo
do
  echo "Waiting for e2 to finish loading..."
  sleep 2
done

# Exit on error
set -o errexit

ADDRGEN1=$(e1-cli getnewaddress)
ADDRGEN2=$(e2-cli getnewaddress)

e1-cli generatetoaddress 101 $ADDRGEN1
b-cli generatetoaddress 101 $ADDRGENB

# Generate a peg-in address
e1-cli getpeginaddress
# If you execute the command again you’ll notice that the returned data changes:
e1-cli getpeginaddress

# Warning: Peg-in addresses are not long-term durable and should not be reused.

ADDRS=$(e1-cli getpeginaddress)

MAINCHAIN=$(echo $ADDRS | jq -r '.mainchain_address')
CLAIMSCRIPT=$(echo $ADDRS | jq -r '.claim_script')

b-cli getwalletinfo

TXID=$(b-cli sendtoaddress $MAINCHAIN 1)

b-cli getwalletinfo

b-cli generatetoaddress 101 $ADDRGENB

b-cli getwalletinfo

PROOF=$(b-cli gettxoutproof '''["'''$TXID'''"]''')
RAW=$(b-cli getrawtransaction $TXID)

CLAIMTXID=$(e1-cli claimpegin $RAW $PROOF $CLAIMSCRIPT)

# Warning: The time between generating a peg-in address with "getpeginaddress" and claiming it with "claimpegin" should be kept as small as possible.

e2-cli generatetoaddress 1 $ADDRGEN2
sleep 10

e1-cli getrawtransaction $CLAIMTXID 1

e1-cli getwalletinfo

### Sidechain - Peg-Out ###

e1-cli sendtomainchain $(b-cli getnewaddress) 10

e1-cli generatetoaddress 1 $ADDRGEN1

e1-cli getwalletinfo

e1-cli stop
e2-cli stop
b-cli stop
sleep 15

### Standalone Blockchain ###

rm -r ~/elementsdir1/elementsregtest
rm -r ~/elementsdir2/elementsregtest

STANDALONEARGS=("-validatepegin=0" "-defaultpeggedassetname=newasset" "-initialfreecoins=100000000000000" "-initialreissuancetokens=200000000")

e1-dae ${STANDALONEARGS[@]}
e2-dae ${STANDALONEARGS[@]}
sleep 10

# Create new wallets
e1-cli createwallet ""
e2-cli createwallet ""
e1-cli rescanblockchain
e2-cli rescanblockchain

# Ignore error
set +o errexit

# Wait for e1 node to finish startup and respond to commands
until e1-cli getwalletinfo
do
  echo "Waiting for e1 to finish loading..."
  sleep 2
done

# Wait for e2 node to finish startup and respond to commands
until e2-cli getwalletinfo
do
  echo "Waiting for e2 to finish loading..."
  sleep 2
done

# Exit on error
set -o errexit

ADDRGEN1=$(e1-cli getnewaddress)
ADDRGEN2=$(e2-cli getnewaddress)

e1-cli getwalletinfo
e2-cli getwalletinfo

DEFAULTRIT=$(e1-cli getwalletinfo | jq '[.balance] | .[0]' | jq -r 'keys | .[0]')

if [ $DEFAULTRIT = "newasset" ]; then
  DEFAULTRIT=$(e1-cli getwalletinfo | jq '[.balance] | .[0]' | jq -r 'keys | .[1]')
fi

echo $DEFAULTRIT

e1-cli sendtoaddress $(e1-cli getnewaddress) 1000000 "" "" true

e1-cli sendtoaddress $(e1-cli getnewaddress) 2 "" "" false false 1 UNSET false $DEFAULTRIT
e1-cli generatetoaddress 101 $ADDRGEN1
sleep 10

e1-cli sendtoaddress $(e2-cli getnewaddress) 500 "" "" false 

e1-cli sendtoaddress $(e2-cli getnewaddress) 1 "" "" false false 1 UNSET false $DEFAULTRIT
e1-cli generatetoaddress 101 $ADDRGEN1
sleep 10

e1-cli getwalletinfo
e2-cli getwalletinfo

e1-cli reissueasset newasset 100
e1-cli generatetoaddress 101 $ADDRGEN1
sleep 10

e1-cli getwalletinfo

e2-cli reissueasset newasset 100
e2-cli generatetoaddress 101 $ADDRGEN2
sleep 10

e1-cli getwalletinfo
e2-cli getwalletinfo

e1-cli stop
e2-cli stop
sleep 10

echo "Completed without error"

More advanced examples, like manual ‘raw’ issuance of an asset, can be found here.