This document describes how to setup a BTP network and interchain token transfer scenario.
- Docker
- goloop v0.9.6 or later
- Start the server
$ export CONFIG_DIR=/path/to/config
$ docker run -d --name goloop -p 9080:9080 \
-e GOLOOP_LOGFILE=/goloop/data/log/goloop.log \
-v ${CONFIG_DIR}:/goloop/config \
iconloop/goloop-icon
for follows, execute under docker exec -ti --workdir /goloop/config goloop sh
.
install jq
# apk add jq
- Create genesis
# goloop gn gen --out src.genesis.json $GOLOOP_KEY_STORE
# goloop gn gen --out dst.genesis.json $GOLOOP_KEY_STORE
modify genesis for javaee
# echo $(cat src.genesis.json | jq -r '.*{"chain":{"fee":{"stepLimit":{"invoke":"0x10000000","query":"0x1000000"}}}}') > src.genesis.json
# echo $(cat dst.genesis.json | jq -r '.*{"chain":{"fee":{"stepLimit":{"invoke":"0x10000000","query":"0x1000000"}}}}') > dst.genesis.json
- Join the chain
# goloop chain join --genesis_template src.genesis.json --channel src
# goloop chain join --genesis_template dst.genesis.json --channel dst
- Start the chain
# goloop chain start src
# goloop chain start dst
- Configure for debugging
set log level to trace
in ${CONFIG_DIR}/server.json
{
...skip...
"log_level": "trace",
"console_level": "trace"
}
restart goloop container to apply changes
set debug config to get smart-contract log with goloop debug trace txhash
# goloop system config rpcIncludeDebug true
log file located in
/goloop/data/log
zip each BTP-Smart-Contracts from project source, and copy files to /path/to/config
which is mounted with /goloop/config
of goloop container
package for javascore/bmc
$ make dist-java
$ mkdir -p ${CONFIG_DIR}/javascore
$ cp build/contracts/javascore/*.jar ${CONFIG_DIR}/javascore/
To use goloop
as json-rpc client, execute shell via docker exec -ti --workdir /goloop/config goloop sh
on goloop container.
For parse json-rpc response, install jq via apk add jq
to goloop container.
Prepare 'rpc.sh' file as below, and apply by source rpc.sh
.
Set keystore for json-rpc by rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
rpchelp() {
echo "rpcch CHANNEL"
echo "rpcks KEYSTORE_PATH"
}
rpcch() {
if [ ! "$1" == "" ]; then
export GOLOOP_RPC_CHANNEL=$1
URI_PREFIX=http://$(goloop system info -f '{{.Setting.RPCAddr}}')/api
export GOLOOP_RPC_URI=$URI_PREFIX/v3/$GOLOOP_RPC_CHANNEL
export GOLOOP_RPC_NID=$(goloop chain inspect $GOLOOP_RPC_CHANNEL --format {{.NID}})
export GOLOOP_DEBUG_URI=$URI_PREFIX/v3d/$GOLOOP_RPC_CHANNEL
export GOLOOP_RPC_STEP_LIMIT=${GOLOOP_RPC_STEP_LIMIT:-0x10000000}
fi
echo $GOLOOP_RPC_CHANNEL
}
rpcks() {
if [ ! "$1" == "" ]; then
export GOLOOP_RPC_KEY_STORE=$1
if [ ! "$2" == "" ]; then
if [ -f "$2" ]; then
export GOLOOP_RPC_KEY_SECRET=$2
else
export GOLOOP_RPC_KEY_PASSWORD=$2
fi
fi
fi
echo $GOLOOP_RPC_KEY_STORE
}
export GOLOOP_CHAINSCORE=cx0000000000000000000000000000000000000000
Deploy BMC contract to 'src' chain
# rpcch src
# echo "$GOLOOP_RPC_NID.icon" > net.btp.$(rpcch)
# goloop rpc sendtx deploy javascore/bmc.jar --content_type application/java \
--param _net=$(cat net.btp.$(rpcch)) | jq -r . > tx.bmc.$(rpcch)
Extract BMC contract address from deploy result
# goloop rpc txresult $(cat tx.bmc.$(rpcch)) | jq -r .scoreAddress > bmc.$(rpcch)
Create BTP-Address
# echo "btp://$(cat net.btp.$(rpcch))/$(cat bmc.$(rpcch))" > btp.$(rpcch)
For 'dst' chain, same flows with replace 'src' to 'dst'.
# rpcch dst
# echo "$GOLOOP_RPC_NID.icon" > net.btp.$(rpcch)
# goloop rpc sendtx deploy javascore/bmc.jar --content_type application/java \
--param _net=$(cat net.btp.$(rpcch)) | jq -r . > tx.bmc.$(rpcch)
# sleep 2
# goloop rpc txresult $(cat tx.bmc.$(rpcch)) | jq -r .scoreAddress > bmc.$(rpcch)
# echo "btp://$(cat net.btp.$(rpcch))/$(cat bmc.$(rpcch))" > btp.$(rpcch)
To create parameters for deploy BMV contract
# rpcch dst
# goloop rpc call --to $GOLOOP_CHAINSCORE --method getValidators| jq -r 'map(.)|join(",")' > validators.$(rpcch)
# echo "0x$(printf %x $(goloop chain inspect $(rpcch) --format {{.Height}}))" > offset.$(rpcch)
Deploy BMV contract to 'src' chain
# rpcch src
# goloop rpc sendtx deploy javascore/bmv-icon.jar --content_type application/java \
--param _bmc=$(cat bmc.$(rpcch)) \
--param _net=$(cat net.btp.dst) \
--param _validators=$(cat validators.dst) \
--param _offset=$(cat offset.dst) \
| jq -r . > tx.bmv.$(rpcch)
Extract BMV contract address from deploy result
# goloop rpc txresult $(cat tx.bmv.$(rpcch)) | jq -r .scoreAddress > bmv.$(rpcch)
For 'dst' chain, same flows with replace 'src' to 'dst' and 'dst' to 'src'.
# rpcch src
# goloop rpc call --to $GOLOOP_CHAINSCORE --method getValidators| jq -r 'map(.)|join(",")' > validators.$(rpcch)
# echo "0x$(printf %x $(goloop chain inspect $(rpcch) --format {{.Height}}))" > offset.$(rpcch)
# rpcch dst
# goloop rpc sendtx deploy javascore/bmv-icon.jar --content_type application/java \
--param _bmc=$(cat bmc.$(rpcch)) \
--param _net=$(cat net.btp.src) \
--param _validators=$(cat validators.src) \
--param _offset=$(cat offset.src) \
| jq -r . > tx.bmv.$(rpcch)
sleep 2
# goloop rpc txresult $(cat tx.bmv.$(rpcch)) | jq -r .scoreAddress > bmv.$(rpcch)
Deploy Token-BSH contract to 'src' chain
# rpcch src
# goloop rpc sendtx deploy javascore/bsh.jar --content_type application/java \
--param _bmc=$(cat bmc.$(rpcch)) | jq -r . > tx.token.$(rpcch)
Extract Token-BSH contract address from deploy result
# goloop rpc txresult $(cat tx.token.$(rpcch)) | jq -r .scoreAddress > token.$(rpcch)
For 'dst' chain, same flows with replace 'src' to 'dst'.
# rpcch dst
# goloop rpc sendtx deploy javascore/bsh.jar --content_type application/java \
--param _bmc=$(cat bmc.$(rpcch)) | jq -r . > tx.token.$(rpcch)
# goloop rpc txresult $(cat tx.token.$(rpcch)) | jq -r .scoreAddress > token.$(rpcch)
Deploy IRC-2.0 Token contract to 'src' chain
# rpcch src
# goloop rpc sendtx deploy javascore/irc2.jar --content_type application/java \
--param _name=IRC2Token \
--param _symbol=I2T \
--param _initialSupply=1000 \
--param _decimals=18 \
| jq -r . > tx.irc2.$(rpcch)
Extract IRC-2.0 Token contract address from deploy result
# goloop rpc txresult $(cat tx.irc2.$(rpcch)) | jq -r .scoreAddress > irc2.$(rpcch)
For 'dst' chain, same flows with replace 'src' to 'dst' and add _owner
parameter. because IRC-2.0 Token contract of 'dst' chain is proxy.
# rpcch dst
# goloop rpc sendtx deploy javascore/irc2.jar --content_type application/java \
--param _name=IRC2Token \
--param _symbol=I2T \
--param _initialSupply=0x3E8 \
--param _decimals=0x12 \
| jq -r . > tx.irc2.$(rpcch)
# goloop rpc txresult $(cat tx.irc2.$(rpcch)) | jq -r .scoreAddress > irc2.$(rpcch)
Deploy Fee aggregator
# rpcch src
# goloop rpc sendtx deploy javascore/fee-aggregation.jar --content_type application/java \
--param _cps_address=$(cat keystore.json | jq -r .address) \
--param _band_protocol_address=$(cat keystore.json | jq -r .address) \
| jq -r . > tx.feeaggr.$(rpcch)
Extract Fee aggregator contract address from deploy result
# goloop rpc txresult $(cat tx.feeaggr.$(rpcch)) | jq -r .scoreAddress > feeaggr.$(rpcch)
Register verifier of 'dst' chain to 'src' chain
# rpcch src
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addVerifier \
--param _net=$(cat net.btp.dst) \
--param _addr=$(cat bmv.$(rpcch)) \
| jq -r . > tx.verifier.$(rpcch)
Register verifier of 'src' chain to 'dst' chain
# rpcch dst
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addVerifier \
--param _net=$(cat net.btp.src) \
--param _addr=$(cat bmv.$(rpcch)) \
| jq -r . > tx.verifier.$(rpcch)
Register BTP-Address of 'dst' chain to 'src' chain
# rpcch src
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addLink \
--param _link=$(cat btp.dst) \
| jq -r . > tx.link.$(rpcch)
Register BTP-Address of 'src' chain to 'dst' chain
# rpcch dst
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addLink \
--param _link=$(cat btp.src) \
| jq -r . > tx.link.$(rpcch)
To retrieve list of registered links, use
getLinks
method of BMC.# goloop rpc call --to $(cat bmc.$(rpcch)) --method getLinks
To use multiple-BMR for relay, should set properties of link via BMC.setLinkRotateTerm
Set properties of 'dst' link to 'src' chain
# rpcch src
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method setLinkRotateTerm \
--param _link=$(cat btp.dst) \
--param _block_interval=0x3e8 \
--param _max_agg=0x10 \
| jq -r . > tx.setlink.$(rpcch)
Set properties of 'src' link to 'dst' chain
# rpcch dst
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method setLinkRotateTerm \
--param _link=$(cat btp.src) \
--param _block_interval=0x3e8 \
--param _max_agg=0x10 \
| jq -r . > tx.setlink.$(rpcch)
To retrieve properties of link, use
getStatus(_link)
method of BMC.# rpcch src # goloop rpc call --to $(cat bmc.src) --method getStatus --param _link=$(cat btp.dst) # rpcch dst # goloop rpc call --to $(cat bmc.dst) --method getStatus --param _link=$(cat btp.src)
Register Token service to BMC
# rpcch src
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addService \
--param _svc=TokenBSH \
--param _addr=$(cat token.$(rpcch)) | jq -r . > tx.service.token.$(rpcch)
# rpcch dst
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addService \
--param _svc=TokenBSH \
--param _addr=$(cat token.$(rpcch)) | jq -r . > tx.service.token.$(rpcch)
Register IRC 2.0 Token contract to Token-BSH
# rpcch src
# goloop rpc sendtx call --to $(cat token.$(rpcch)) \
--method register \
--param name=IRC2Token \
--param symbol=I2T \
--param feeNumerator=0x64 \
--param decimals=0x12 \
--param address=$(cat irc2.$(rpcch))
# rpcch dst
# goloop rpc sendtx call --to $(cat token.$(rpcch)) \
--method register \
--param name=IRC2Token \
--param symbol=I2T \
--param feeNumerator=0x64 \
--param decimals=0x12 \
--param address=$(cat irc2.$(rpcch))
To retrieve list of registered token, use
tokenNames
method of Token-BSH.# goloop rpc call --to $(cat token.$(rpcch)) --method tokenNames
Create key store for BMC-Owner, BMR of both chain
# echo -n $(date|md5sum|head -c16) > src.secret
# goloop ks gen -o src.ks.json -p $(cat src.secret)
# echo -n $(date|md5sum|head -c16) > dst.secret
# goloop ks gen -o dst.ks.json -p $(cat dst.secret)
Register BMC-Owner to 'src' chain
# rpcch src
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addOwner \
--param _addr=$(jq -r .address $(rpcch).ks.json)
BMC-Owner register BMR to 'src' chain (Address of BMR could be any keystore)
# rpcks src.ks.json src.secret
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addRelay \
--param _link=$(cat btp.dst) \
--param _addr=$(jq -r .address dst.ks.json)
For 'dst' chain, same flows with replace 'src' to 'dst'
# rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
# rpcch dst
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addOwner \
--param _addr=$(jq -r .address $(rpcch).ks.json)
# rpcks dst.ks.json dst.secret
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addRelay \
--param _link=$(cat btp.src) \
--param _addr=$(jq -r .address src.ks.json)
To retrieve list of registered BMC-Owners, use
getOwners
method of BMC.# goloop rpc call --to $(cat bmc.$(rpcch)) --method getOwners
To retrieve list of registered relay of link, use
getRelays(_link)
method of BMC.# rpcch src # goloop rpc call --to $(cat bmc.src) --method getRelays --param _link=$(cat btp.dst) # rpcch dst # goloop rpc call --to $(cat bmc.dst) --method getRelays --param _link=$(cat btp.src)
Register Fee aggreator to BMC
# rpcch src
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method setFeeAggregator \
--param _addr=$(cat feeaggr.$(rpcch)) \
| jq -r . > tx.setFeeAggr.$(rpcch)
Get registered contract address of Fee aggregator
# goloop rpc call --to $(cat bmc.$(rpcch)) \ --method getFeeAggregator
Create key store for relayer
# echo -n $(date|md5sum|head -c16) > relayer.secret
# goloop ks gen -o relayer.ks.json -p $(cat relayer.secret)
To register relayer candidate, bonding is required. The account of 'god' transfer some icx to account of relayer candidate. Assume 'src' chain as ICON
# rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
# rpcch src
# goloop rpc sendtx transfer --to $(jq -r .address relayer.ks.json) --value 0x10
Register to 'src' chain
# rpcch src
# rpcks relayer.ks.json relayer.secret
# goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method registerRelayer \
--param _desc="first candidate for relayer" \
--value 0x10
To retrieve list of registered relayers, use
getRelayers
method of BMC.# goloop rpc call --to $(cat bmc.src) --method getRelayers
To make it easier to deploy and configuration
$ cp scprits/provision.sh config/provision.sh
in goloop docker
# source provision.sh
# deploy_all
deploy all
...
# register_all
register all
...
Now you can check deploy result in
src_result.json
anddst_result.json
Prepare 'btpsimple' docker image via make btpsimple-image
Start relay 'src' chain to 'dst' chain
$ docker run -d --name btpsimple_src --link goloop \
-v ${CONFIG_DIR}:/btpsimple/config \
-e BTPSIMPLE_CONFIG=/btpsimple/config/src.config.json \
-e BTPSIMPLE_SRC_ADDRESS=$(cat ${CONFIG_DIR}/btp.src) \
-e BTPSIMPLE_SRC_ENDPOINT=http://goloop:9080/api/v3/src \
-e BTPSIMPLE_DST_ADDRESS=$(cat ${CONFIG_DIR}/btp.dst) \
-e BTPSIMPLE_DST_ENDPOINT=http://goloop:9080/api/v3/dst \
-e BTPSIMPLE_OFFSET=$(cat ${CONFIG_DIR}/offset.src) \
-e BTPSIMPLE_KEY_STORE=/btpsimple/config/src.ks.json \
-e BTPSIMPLE_KEY_SECRET=/btpsimple/config/src.secret \
btpsimple
For relay of 'dst' chain, same flows with replace 'src' to 'dst' and 'dst' to 'src'.
$ docker run -d --name btpsimple_dst --link goloop \
-v ${CONFIG_DIR}:/btpsimple/config \
-e BTPSIMPLE_CONFIG=/btpsimple/config/dst.config.json \
-e BTPSIMPLE_SRC_ADDRESS=$(cat ${CONFIG_DIR}/btp.dst) \
-e BTPSIMPLE_SRC_ENDPOINT=http://goloop:9080/api/v3/dst \
-e BTPSIMPLE_DST_ADDRESS=$(cat ${CONFIG_DIR}/btp.src) \
-e BTPSIMPLE_DST_ENDPOINT=http://goloop:9080/api/v3/src \
-e BTPSIMPLE_OFFSET=$(cat ${CONFIG_DIR}/offset.dst) \
-e BTPSIMPLE_KEY_STORE=/btpsimple/config/dst.ks.json \
-e BTPSIMPLE_KEY_SECRET=/btpsimple/config/dst.secret \
btpsimple
To retrieve status of relay, use
getStatus(_link)
method of BMC.# rpcch src # goloop rpc call --to $(cat bmc.src) --method getStatus --param _link=$(cat btp.dst) # rpcch dst # goloop rpc call --to $(cat bmc.dst) --method getStatus --param _link=$(cat btp.src)
To use
goloop
as json-rpc client, execute shell viadocker exec -ti --workdir /goloop/config goloop sh
on goloop container.
Create key store for Alice and Bob
# echo -n $(date|md5sum|head -c16) > alice.secret
# goloop ks gen -o alice.ks.json -p $(cat alice.secret)
# echo -n $(date|md5sum|head -c16) > bob.secret
# goloop ks gen -o bob.ks.json -p $(cat bob.secret)
Mint token to Alice
# rpcch src
# rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
# goloop rpc sendtx call --to $(cat irc2.src) \
--method transfer \
--param _to=$(jq -r .address alice.ks.json) \
--param _value=1000
To retrieve balance of Alice, use
balanceOf(_owner)
method of IRC-2.0 Token contract.# goloop rpc call --to $(cat irc2.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json)
Mint token to Token-BSH of dst
# rpcch dst
# rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
# goloop rpc sendtx call --to $(cat irc2.dst) \
--method transfer \
--param _to=$(cat token.dst) \
--param _value=1000
To retrieve balance of dst Token-BSH, use
balanceOf(_owner)
method of IRC-2.0 Token contract.# goloop rpc call --to $(cat irc2.dst) --method balanceOf --param _owner=$(cat token.dst)
Alice transfer token to Token-BSH
# rpcch src
# rpcks alice.ks.json alice.secret
# goloop rpc sendtx call --to $(cat irc2.src) \
--method transfer \
--param _to=$(cat token.src) \
--param _value=100
To retrieve balance of Alice which is able to interchain-transfer, use
balanceOf(_owner)
method of Token-BSH.# goloop rpc call --to $(cat token.src) --method getBalance \ --param user=$(jq -r .address alice.ks.json) \ --param tokenName=IRC2Token
Alice transfer token to Bob via Token-BSH
# rpcch src
# rpcks alice.ks.json alice.secret
# goloop rpc sendtx call --to $(cat token.src) \
--method transfer \
--param tokenName=IRC2Token \
--param to=btp://$(cat net.btp.dst)/$(jq -r .address bob.ks.json) \
--param value=10
To retrieve balance of Alice, use
balanceOf(_owner)
method of Token-BSH.# goloop rpc call --to $(cat token.src) --method getBalance \ --param user=$(jq -r .address alice.ks.json) \ --param tokenName=IRC2TokenTo retrieve transferred balance of Token-BSH which is, use
balanceOf(_owner)
method of IRC-2.0 token contract.# rpcch dst # goloop rpc call --to $(cat irc2.dst) --method balanceOf --param _owner=$(cat token.dst)alice usable balance is 0x3de, dst Token-BSH balance is 0x3de
Balance of Bob from Token-BSH
To retrieve balance of Bob which is, use
balanceOf(_owner)
method of IRC-2.0 Token contract.# goloop rpc call --to $(cat irc2.dst) --method balanceOf --param _owner=$(jq -r .address bob.ks.json) "0xa"
Tutorial with Docker-compose Using javascore/bmc instead of pyscore/bmc
Prepare 'btpsimple' docker image via make btpsimple-image
and Copy files from project source to /path/to/tutorial
$ make btpsimple-image
$ mkdir -p /path/to/tutorial
$ cp docker-compose/* /path/to/tutorial/
docker-compose up
will build tutorial_goloop
docker image that provisioned of belows
- scripts files in
/goloop/bin
- source chain and destination chain (channel name :
src
,dst
) - transaction related files in
/goloop/config
- Transaction hash :
tx.<method>.<chain>
- SCORE Address :
<score>.<chain>
- BTP Address :
btp.<chain>
,net.btp.<chain>
- Transaction hash :
And It creates containers for tutorial_goloop
, tutorial_btpsimple_src
, tutorial_btpsimple_dst
services
To use
goloop
as json-rpc client, execute shell viadocker-compose exec tutorial_goloop sh
Create key store for Alice and Bob
# source /goloop/bin/keystore.sh
# ensure_key_store alice.ks.json alice.secret
# ensure_key_store bob.ks.json bob.secret
apply
source /goloop/bin/nativecoin.sh
for transfer and retrieve balance
Alice for source chain
# rpcks alice.ks.json alice.secret
# irc31_approve src
Bob for destination chain
# rpcks bob.ks.json bob.secret
# irc31_approve dst
Mint native-coin of source chain to Alice
# rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
# rpcch src
# goloop rpc sendtx transfer --to $(rpceoa alice.ks.json) --value 0x64
To retrieve balance of Alice, use
goloop rpc balance $(rpceoa alice.ks.json)
Alice transfer native-coin of source chain to Bob of destination chain via NativeCoin-BSH
# rpcks alice.ks.json alice.secret
# nc_transfer src dst bob.ks.json src 0x64
To retrieve locked-balance of Alice,
nc_balance src src alice.ks.json
To retrieve transferred balance of Bob, useirc31_balance dst src bob.ks.json
Bob transfer irc31-token of destination chain to Alice of source chain via NativeCoin-BSH
# rpcks bob.ks.json bob.secret
# nc_transfer dst src alice.ks.json src 0x10
To retrieve locked-balance of Bob,
# nc_balance dst src bob.ks.json
To retrieve transferred balance of Alice, use
# rpcch src # goloop rpc balance $(rpceoa alice.ks.json)To retrieve remained balance of Bob, use
# irc31_balance dst src bob.ks.json