This document describes how to setup a BTP network and interchain token transfer scenario.
- Start the server
CONFIG_DIR=/path/to/config
docker run -d --name goloop -p 9080:9080 \
-v ${CONFIG_DIR}:/goloop/config \
iconloop/goloop
for follows, execute under docker exec -ti --workdir /goloop/config goloop sh
.
- Create genesis
goloop gn gen --out src.genesis.json $GOLOOP_KEY_STORE
goloop gn gen --out dst.genesis.json $GOLOOP_KEY_STORE
- 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
zip each BTP-Smart-Contracts from project source, and copy files to /path/to/config
which is mounted with /goloop/config
of goloop container
make dist-py
mkdir -p ${CONFIG_DIR}/pyscore
cp build/pyscore/*.zip ${CONFIG_DIR}/pyscore/
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:-1}
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
}
Deploy BMC contract to 'src' chain
rpcch src
echo "$GOLOOP_RPC_NID.icon" > net.btp.$(rpcch)
goloop rpc sendtx deploy pyscore/bmc.zip \
--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 pyscore/bmc.zip \
--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 pyscore/bmv.zip \
--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 pyscore/bmv.zip \
--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 pyscore/token_bsh.zip \
--param _bmc=$(cat bmc.$(rpcch)) | jq -r . > tx.token_bsh.$(rpcch)
Extract Token-BSH contract address from deploy result
goloop rpc txresult $(cat tx.token_bsh.$(rpcch)) | jq -r .scoreAddress > token_bsh.$(rpcch)
For 'dst' chain, same flows with replace 'src' to 'dst'.
rpcch dst
goloop rpc sendtx deploy pyscore/token_bsh.zip \
--param _bmc=$(cat bmc.$(rpcch)) | jq -r . > tx.token_bsh.$(rpcch)
goloop rpc txresult $(cat tx.token_bsh.$(rpcch)) | jq -r .scoreAddress > token_bsh.$(rpcch)
Deploy IRC-2.0 Token contract to 'src' chain
rpcch src
goloop rpc sendtx deploy pyscore/irc2_token.zip \
--param _name=IRC2Token \
--param _symbol=I2T \
--param _initialSupply=1000 \
--param _decimals=18 \
| jq -r . > tx.irc2_token.$(rpcch)
Extract IRC-2.0 Token contract address from deploy result
goloop rpc txresult $(cat tx.irc2_token.$(rpcch)) | jq -r .scoreAddress > irc2_token.$(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 pyscore/irc2_token.zip \
--param _name=IRC2Token \
--param _symbol=I2T \
--param _initialSupply=0x3E8 \
--param _decimals=0x12 \
--param _owner=$(cat token_bsh.$(rpcch)) \
| jq -r . > tx.irc2_token.$(rpcch)
goloop rpc txresult $(cat tx.irc2_token.$(rpcch)) | jq -r .scoreAddress > irc2_token.$(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.setLink
Set properties of 'dst' link to 'src' chain
rpcch src
goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method setLink \
--param _link=$(cat btp.dst) \
--param _block_interval=0x3e8 \
--param _max_agg=0x10 \
--param _delay_limit=3 \
| jq -r . > tx.setlink.$(rpcch)
Set properties of 'src' link to 'dst' chain
rpcch dst
goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method setLink \
--param _link=$(cat btp.src) \
--param _block_interval=0x3e8 \
--param _max_agg=0x10 \
--param _delay_limit=3 \
| jq -r . > tx.setlink.$(rpcch)
To retrieve properties of link, use
getStatus(_link)
method of BMC.goloop rpc call --to $(cat bmc.src) --method getStatus --param _link=$(cat btp.dst)
goloop rpc call --to $(cat bmc.dst) --method getStatus --param _link=$(cat btp.src)
Register IRC 2.0 Token contract to Token-BSH
rpcch src
goloop rpc sendtx call --to $(cat token_bsh.src) \
--method register \
--param _name=IRC2Token \
--param _addr=$(cat irc2_token.src)
rpcch dst
goloop rpc sendtx call --to $(cat token_bsh.dst) \
--method register \
--param _name=IRC2Token \
--param _addr=$(cat irc2_token.dst)
To retrieve list of registered token, use
tokenNames
method of Token-BSH.
goloop rpc call --to $(cat token_bsh.$(rpcch)) --method tokenNames
Create key store for relay of 'src' chain
echo -n $(date|md5sum|head -c16) > src.secret
goloop ks gen -o src.ks.json -p $(cat src.secret)
Register relayer and relay to 'dst' chain
rpcch dst
rpcks src.ks.json src.secret
goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addRelayer \
--param _addr=$(jq -r .address src.ks.json) \
--param _desc="$(rpcch) relayer"
goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addRelay \
--param _link=$(cat btp.src) \
--param _addr=$(jq -r .address src.ks.json)
For relay of 'dst' chain, same flows with replace 'src' to 'dst'
echo -n $(date|md5sum|head -c16) > dst.secret
goloop ks gen -o dst.ks.json -p $(cat dst.secret)
rpcch src
rpcks dst.ks.json dst.secret
goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addRelayer \
--param _addr=$(jq -r .address dst.ks.json) \
--param _desc="$(rpcch) relayer"
goloop rpc sendtx call --to $(cat bmc.$(rpcch)) \
--method addRelay \
--param _link=$(cat btp.dst) \
--param _addr=$(jq -r .address dst.ks.json)
To retrieve list of registered relayers, use
getRelayers
method of BMC.
goloop rpc call --to $(cat bmc.$(rpcch)) --method getRelayers
To retrieve list of registered relay of link, use
getRelays(_link)
method of BMC.
goloop rpc call --to $(cat bmc.src) --method getRelays --param _link=$(cat btp.dst)
goloop rpc call --to $(cat bmc.dst) --method getRelays --param _link=$(cat btp.src)
Prepare 'iconbridge' docker image via make iconbridge-image
Start relay 'src' chain to 'dst' chain
docker run -d --name iconbridge_src --link goloop \
-v ${CONFIG_DIR}:/iconbridge/config \
-e ICONBRIDGE_CONFIG=/iconbridge/config/src.config.json \
-e ICONBRIDGE_SRC_ADDRESS=$(cat ${CONFIG_DIR}/btp.src) \
-e ICONBRIDGE_SRC_ENDPOINT=http://goloop:9080/api/v3/src \
-e ICONBRIDGE_DST_ADDRESS=$(cat ${CONFIG_DIR}/btp.dst) \
-e ICONBRIDGE_DST_ENDPOINT=http://goloop:9080/api/v3/dst \
-e ICONBRIDGE_OFFSET=$(cat ${CONFIG_DIR}/offset.src) \
-e ICONBRIDGE_KEY_STORE=/iconbridge/config/src.ks.json \
-e ICONBRIDGE_KEY_SECRET=/iconbridge/config/src.secret \
iconbridge
For relay of 'dst' chain, same flows with replace 'src' to 'dst' and 'dst' to 'src'.
docker run -d --name iconbridge_dst --link goloop \
-v ${CONFIG_DIR}:/iconbridge/config \
-e ICONBRIDGE_CONFIG=/iconbridge/config/dst.config.json \
-e ICONBRIDGE_SRC_ADDRESS=$(cat ${CONFIG_DIR}/btp.dst) \
-e ICONBRIDGE_SRC_ENDPOINT=http://goloop:9080/api/v3/dst \
-e ICONBRIDGE_DST_ADDRESS=$(cat ${CONFIG_DIR}/btp.src) \
-e ICONBRIDGE_DST_ENDPOINT=http://goloop:9080/api/v3/src \
-e ICONBRIDGE_OFFSET=$(cat ${CONFIG_DIR}/offset.dst) \
-e ICONBRIDGE_KEY_STORE=/iconbridge/config/dst.ks.json \
-e ICONBRIDGE_KEY_SECRET=/iconbridge/config/dst.secret \
iconbridge
To retrieve status of relay, use
getStatus(_link)
method of BMC.
goloop rpc call --to $(cat bmc.src) --method getStatus --param _link=$(cat btp.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_token.src) \
--method transfer \
--param _to=$(jq -r .address alice.ks.json) \
--param _value=10
To retrieve balance of Alice, use
balanceOf(_owner)
method of IRC-2.0 Token contract.
goloop rpc call --to $(cat irc2_token.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json)
Alice transfer token to Token-BSH
rpcch src
rpcks alice.ks.json alice.secret
goloop rpc sendtx call --to $(cat irc2_token.src) \
--method transfer \
--param _to=$(cat token_bsh.src) \
--param _value=10
To retrieve balance of Alice which is able to interchain-transfer, use
balanceOf(_owner)
method of Token-BSH.
goloop rpc call --to $(cat token_bsh.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json) | jq -r .IRC2Token.usable
Alice transfer token to Bob via Token-BSH
rpcch src
rpcks alice.ks.json alice.secret
goloop rpc sendtx call --to $(cat token_bsh.src) \
--method transfer \
--param _tokenName=IRC2Token \
--param _to=btp://$(cat net.btp.dst)/$(jq -r .address bob.ks.json) \
--param _value=10
To retrieve locked-balance of Alice, use
balanceOf(_owner)
method of Token-BSH.
goloop rpc call --to $(cat token_bsh.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json) | jq -r .IRC2Token.locked
Bob withdraw token from Token-BSH
rpcch dst
rpcks bob.ks.json bob.secret
goloop rpc sendtx call --to $(cat token_bsh.dst) \
--method reclaim \
--param _tokenName=IRC2Token \
--param _value=10
To retrieve transferred balance of Bob which is , use
balanceOf(_owner)
method of Token-BSH.
goloop rpc call --to $(cat token_bsh.dst) --method balanceOf --param _owner=$(jq -r .address bob.ks.json) | jq -r .IRC2Token.usable
Tutorial with Docker-compose
Prepare 'iconbridge' docker image via make iconbridge-image
and Copy files from project source to /path/to/tutorial
make iconbridge-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 goloop
, iconbridge_src
, iconbridge_dst
services
To use
goloop
as json-rpc client, execute shell viadocker-compose exec goloop sh
And applysource token.sh
for transfer and retrieve balance
Create key store for Alice and Bob
source keystore.sh
ensure_key_store alice.ks.json alice.secret
ensure_key_store bob.ks.json bob.secret
Mint token to Alice
rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET
irc2_transfer src 0x10 alice.ks.json
To retrieve balance of Alice, use
irc2_balance src alice.ks.json
Alice transfer token to Token-BSH
rpcks alice.ks.json alice.secret
irc2_transfer src 0x10
To retrieve balance of Alice which is able to interchain-transfer, use
bsh_balance src alice.ks.json | jq -r .IRC2Token.usable
Alice transfer token to Bob via Token-BSH
rpcks alice.ks.json alice.secret
bsh_transfer src dst 0x10 bob.ks.json
To retrieve locked-balance of Alice,
bsh_balance src alice.ks.json | jq -r .IRC2Token.locked
Bob reclaim token from Token-BSH
rpcks bob.ks.json bob.secret
bsh_reclaim dst 0x10
To retrieve transferred balance of Bob, use
bsh_balance dst bob.ks.json | jq -r .IRC2Token.usable