From a2d674e39ec8ab6b5d48fc89e640607898f46275 Mon Sep 17 00:00:00 2001 From: Reece Williams <31943163+Reecepbcups@users.noreply.github.com> Date: Tue, 20 Aug 2024 08:18:35 -0700 Subject: [PATCH] feat(local-ic): bash driver (#1207) * feat: bash client base * just data return * faucet * GET_PEER * working foundation * `ICT_` prefix * bash e2e * fix: e2e * rm unused bash-e2e rust logic * `bash ./test.bash` * ICT_POLL_FOR_START * upload files + cw, * test: store file * local-ize bash variables to not leak context * `;` --- .github/workflows/local-interchain.yaml | 34 ++++ local-interchain/bash/source.bash | 152 ++++++++++++++++++ local-interchain/bash/test.bash | 88 ++++++++++ .../interchain/handlers/actions.go | 20 ++- 4 files changed, 290 insertions(+), 4 deletions(-) create mode 100755 local-interchain/bash/source.bash create mode 100755 local-interchain/bash/test.bash diff --git a/.github/workflows/local-interchain.yaml b/.github/workflows/local-interchain.yaml index 52e498a22..f674b3368 100644 --- a/.github/workflows/local-interchain.yaml +++ b/.github/workflows/local-interchain.yaml @@ -37,6 +37,40 @@ jobs: name: local-ic path: ~/go/bin/local-ic + bash-e2e: + name: bash + needs: build + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./local-interchain + strategy: + fail-fast: false + + steps: + - name: checkout chain + uses: actions/checkout@v4 + + - name: Download Tarball Artifact + uses: actions/download-artifact@v3 + with: + name: local-ic + path: /tmp + + - name: Make local-ic executable + run: chmod +x /tmp/local-ic + + - name: Start background ibc local-interchain + run: /tmp/local-ic start juno_ibc --api-port 8080 & + + - name: Run Bash Script + run: | + cd bash + bash ./test.bash + + - name: Cleanup + run: killall local-ic && exit 0 + rust-e2e: name: rust needs: build diff --git a/local-interchain/bash/source.bash b/local-interchain/bash/source.bash new file mode 100755 index 000000000..e0ad4dca6 --- /dev/null +++ b/local-interchain/bash/source.bash @@ -0,0 +1,152 @@ +# IMPORT ME WITH: source <(curl -s https://raw.githubusercontent.com/strangelove-ventures/interchaintest/main/local-interchain/bash/source.bash) + +# exitIfEmpty "$someKey" someKey +function ICT_exitIfEmpty() { + if [ -z "$1" ]; then + echo "Exiting because ${2} is empty" + exit 1 + fi +} + +# === BASE === + +# ICT_MAKE_REQUEST http://127.0.0.1:8080 localjuno-1 "q" "bank total" +ICT_MAKE_REQUEST() { + local API=$1 CHAIN_ID=$2 ACTION=$3 + shift 3 # get the 4th argument and up as the command + local COMMAND="$*" + + DATA=`printf '{"chain_id":"%s","action":"%s","cmd":"MYCOMMAND"}' $CHAIN_ID $ACTION` + DATA=`echo $DATA | sed "s/MYCOMMAND/$COMMAND/g"` + + curl "$API" -ss --no-progress-meter --header "Content-Type: application/json" -X POST -d "$DATA" +} + +# ICT_QUERY "http://localhost:8080" "localjuno-1" "bank balances juno10r39fueph9fq7a6lgswu4zdsg8t3gxlq670lt0" +ICT_QUERY() { + local API=$1 CHAIN_ID=$2 CMD=$3 # can be multiple words + ICT_MAKE_REQUEST "$API" $CHAIN_ID "q" "$CMD" +} + +# ICT_BIN "http://localhost:8080" "localjuno-1" "decode" +ICT_BIN() { + local API=$1 CHAIN_ID=$2 CMD=$3 # can be multiple words + ICT_MAKE_REQUEST "$API" $CHAIN_ID "bin" "$CMD" +} + +# ICT_SH_EXEC "http://localhost:8080" "localjuno-1" "ls -l" +# NOTE: if using a /, make sure to escape it with \ +ICT_SH_EXEC() { + local API=$1 CHAIN_ID=$2 CMD=$3 # can be multiple words + ICT_MAKE_REQUEST "$API" $CHAIN_ID "exec" "$CMD" +} + +# === RELAYER === + +# ICT_RELAYER_STOP http://127.0.0.1 "localjuno-1" +ICT_RELAYER_STOP() { + local API=$1 CHAIN_ID=$2 + + # TODO: how does this function? + ICT_MAKE_REQUEST $API $CHAIN_ID "stop-relayer" "" +} + +# ICT_RELAYER_START http://127.0.0.1 "localjuno-1" "demo-path2 --max-tx-size 10" +ICT_RELAYER_START() { + local API=$1 CHAIN_ID=$2 CMD=$3 + + ICT_MAKE_REQUEST $API $CHAIN_ID "start-relayer" "$CMD" +} + +# RELAYER_EXEC http://127.0.0.1:8080 "localjuno-1" "rly paths list" +ICT_RELAYER_EXEC() { + local API=$1 CHAIN_ID=$2 + shift 2 # get the 3rd argument and up as the command + local CMD="$*" + + ICT_MAKE_REQUEST $API $CHAIN_ID "relayer-exec" "$CMD" +} + +# RELAYER_CHANNELS http://127.0.0.1:8080 "localjuno-1" +ICT_RELAYER_CHANNELS() { + local API=$1 CHAIN_ID=$2 + + ICT_MAKE_REQUEST $API $CHAIN_ID "get_channels" "" +} + +# === COSMWASM === + +# ICT_WASM_DUMP_CONTRACT_STATE "http://localhost:8080" "localjuno-1" "cosmos1contractaddress" "100" +ICT_WASM_DUMP_CONTRACT_STATE() { + local API=$1 CHAIN_ID=$2 CONTRACT=$3 HEIGHT=$4 + + ICT_MAKE_REQUEST $API $CHAIN_ID "recover-key" "contract=$CONTRACT;height=$HEIGHT" +} + +# ICT_WASM_STORE_FILE "http://localhost:8080" "localjuno-1" "/host/absolute/path.wasm" "keyName" +# returns the code_id of the uploaded contract +ICT_WASM_STORE_FILE() { + local API=$1 CHAIN_ID=$2 FILE=$3 KEYNAME=$4 + + DATA=`printf '{"chain_id":"%s","file_path":"%s","key_name":"%s"}' $CHAIN_ID $FILE $KEYNAME` + curl "$API/upload" --header "Content-Type: application/json" --header "Upload-Type: cosmwasm" -X POST -d "$DATA" +} + +# === OTHER === + +# ICT_POLL_FOR_START "http://localhost:8080" 50 +ICT_POLL_FOR_START() { + local API=$1 ATTEMPTS_MAX=$2 + + curl --head -X GET --retry $ATTEMPTS_MAX --retry-connrefused --retry-delay 3 $API +} + +# ICT_KILL_ALL "http://localhost:8080" "localjuno-1" +# (Kills all running, keeps local-ic process. `killall local-ic` to kill that as well) +ICT_KILL_ALL() { + local API=$1 CHAIN_ID=$2 + ICT_MAKE_REQUEST $API $CHAIN_ID "kill-all" "" +} + +# ICT_GET_PEER "http://localhost:8080" "localjuno-1" +ICT_GET_PEER() { + local API=$1 CHAIN_ID=$2 + + if [[ $API != */info ]]; then + API="$API/info" + fi + + curl -G -d "chain_id=$CHAIN_ID" -d "request=peer" $API +} + +# ICT_FAUCET_REQUEST "http://localhost:8080" "localjuno-1" "1000000000ujuno" "juno1qk7zqy3k2v3jx2zq2z2zq2zq2zq2zq2zq2zq" +ICT_FAUCET_REQUEST() { + local API=$1 CHAIN_ID=$2 AMOUNT=$3 ADDRESS=$4 + ICT_MAKE_REQUEST $API $CHAIN_ID "faucet" "amount=$AMOUNT;address=$ADDRESS" +} + +# ICT_ADD_FULL_NODE http://127.0.0.1:8080 "localjuno-1" "1" +ICT_ADD_FULL_NODE() { + local API=$1 CHAIN_ID=$2 AMOUNT=$3 + + ICT_MAKE_REQUEST $API $CHAIN_ID "add-full-nodes" "amount=$AMOUNT" +} + +# ICT_RECOVER_KEY "http://localhost:8080" "localjuno-1" "mykey" "my mnemonic string here" +ICT_RECOVER_KEY() { + local API=$1 CHAIN_ID=$2 KEYNAME=$3 + shift 3 # get the 4th argument and up as the command + local MNEMONIC="$*" + + ICT_MAKE_REQUEST $API $CHAIN_ID "recover-key" "keyname=$KEYNAME;mnemonic=$MNEMONIC" +} + +# ICT_STORE_FILE "http://localhost:8080" "localjuno-1" "/host/absolute/path" +# Uploads any arbitrary host file to the chain node. +ICT_STORE_FILE() { + local API=$1 CHAIN_ID=$2 FILE=$3 + + DATA=`printf '{"chain_id":"%s","file_path":"%s"}' $CHAIN_ID $FILE` + curl "$API/upload" --header "Content-Type: application/json" -X POST -d "$DATA" +} + diff --git a/local-interchain/bash/test.bash b/local-interchain/bash/test.bash new file mode 100755 index 000000000..9fc37ed3a --- /dev/null +++ b/local-interchain/bash/test.bash @@ -0,0 +1,88 @@ +#!/bin/bash +# local-ic start juno_ibc +# +# bash local-interchain/bash/test.bash + +# exits if any command is non 0 status +set -e + +thisDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +# EXTERNAL: source <(curl -s https://raw.githubusercontent.com/strangelove-ventures/interchaintest/main/local-interchain/bash/source.bash) +source "$thisDir/source.bash" +API_ADDR="http://localhost:8080" + +# === BEGIN TESTS === + +ICT_POLL_FOR_START $API_ADDR 50 + +# Set standard interaction defaults +ICT_BIN "$API_ADDR" "localjuno-1" "config keyring-backend test" +ICT_BIN "$API_ADDR" "localjuno-1" "config output json" + +# Get total bank supply +BANK_TOTAL=`ICT_QUERY $API_ADDR "localjuno-1" "bank total"` && echo "BANK_TOTAL: $BANK_TOTAL" +ICT_exitIfEmpty "$BANK_TOTAL" "BANK_TOTAL" +echo $BANK_TOTAL | jq -r '.supply' + +# Get total bank supply another way (directly) +BANK_TOTAL=`ICT_MAKE_REQUEST $API_ADDR "localjuno-1" "q" "bank total"` && echo "BANK_TOTAL: $BANK_TOTAL" +ICT_exitIfEmpty "$BANK_TOTAL" "BANK_TOTAL" +echo $BANK_TOTAL | jq -r '.supply' + +# faucet to user +FAUCET_RES=`ICT_FAUCET_REQUEST "$API_ADDR" "localjuno-1" "7" "juno10r39fueph9fq7a6lgswu4zdsg8t3gxlq670lt0"` && echo "FAUCET_RES: $FAUCET_RES" +FAUCET_CONFIRM=`ICT_QUERY $API_ADDR "localjuno-1" "bank balances juno10r39fueph9fq7a6lgswu4zdsg8t3gxlq670lt0"` && echo "FAUCET_CONFIRM: $FAUCET_CONFIRM" +ICT_exitIfEmpty "$FAUCET_CONFIRM" "FAUCET_CONFIRM" + +if [ $(echo $FAUCET_CONFIRM | jq -r '.balances[0].amount') -lt 7 ]; then + echo "FAUCET_CONFIRM is less than 7" + exit 1 +fi + +# CosmWasm - Upload source file to chain & store +parent_dir=$(dirname $thisDir) # local-interchain folder +contract_source="$parent_dir/contracts/cw_ibc_example.wasm" +CODE_ID_JSON=`ICT_WASM_STORE_FILE $API_ADDR "localjuno-1" "$contract_source" "acc0"` && echo "CODE_ID_JSON: $CODE_ID_JSON" +CODE_ID=`echo $CODE_ID_JSON | jq -r '.code_id'` && echo "CODE_ID: $CODE_ID" +ICT_exitIfEmpty "$CODE_ID" "CODE_ID" + +# Upload random file +FILE_RESP=`ICT_STORE_FILE $API_ADDR "localjuno-1" "$thisDir/test.bash"` && echo "FILE_RESP: $FILE_RESP" +FILE_LOCATION=`echo $FILE_RESP | jq -r '.location'` && echo "FILE_LOCATION: $FILE_LOCATION" +ICT_exitIfEmpty "$FILE_LOCATION" "FILE_LOCATION" + +# Verify file contents are there +FILE_LOCATION_ESC=$(echo $FILE_LOCATION | sed 's/\//\\\//g') +MISC_BASH_CMD=`ICT_SH_EXEC "$API_ADDR" "localjuno-1" "cat $FILE_LOCATION_ESC"` && echo "MISC_BASH_CMD: $MISC_BASH_CMD" +ICT_exitIfEmpty "$MISC_BASH_CMD" "MISC_BASH_CMD" + +PEER=`ICT_GET_PEER $API_ADDR "localjuno-1"` && echo "PEER: $PEER" +ICT_exitIfEmpty "$PEER" "PEER" + +# RELAYER +CHANNELS=`ICT_RELAYER_CHANNELS $API_ADDR "localjuno-1"` && echo "CHANNELS: $CHANNELS" +ICT_exitIfEmpty "$CHANNELS" "CHANNELS" + +ICT_RELAYER_EXEC $API_ADDR "localjuno-1" "rly paths list" +ICT_RELAYER_EXEC $API_ADDR "localjuno-1" "rly chains list" +RLY_BALANCE=`ICT_RELAYER_EXEC $API_ADDR "localjuno-1" "rly q balance localjuno-1 --output=json"` && echo "RLY_BALANCE: $RLY_BALANCE" +ICT_exitIfEmpty "$RLY_BALANCE" "RLY_BALANCE" +echo $RLY_BALANCE | jq -r '.balance' + + +# Recover a key and validate +COSMOS_KEY_STATUS=`ICT_RECOVER_KEY $API_ADDR "localjuno-1" "mynewkey" "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"` && echo "COSMOS_KEY_STATUS: $COSMOS_KEY_STATUS" + +COSMOS_KEY_ADDRESS=`ICT_BIN "$API_ADDR" "localjuno-1" "keys show mynewkey -a"` && echo "COSMOS_KEY_ADDRESS: $COSMOS_KEY_ADDRESS" +ICT_exitIfEmpty "$COSMOS_KEY_ADDRESS" "COSMOS_KEY_ADDRESS" + +FULL_NODE_ADDED=`ICT_ADD_FULL_NODE $API_ADDR "localjuno-1" "1"` +ICT_exitIfEmpty "$FULL_NODE_ADDED" "FULL_NODE_ADDED" + +# Stop the relayer +ICT_RELAYER_STOP $API_ADDR "localjuno-1" + +# Kills all containers, not the local-ic process. Use `killall local-ic` to kill that as well +ICT_KILL_ALL $API_ADDR "localjuno-1" + +exit 0 \ No newline at end of file diff --git a/local-interchain/interchain/handlers/actions.go b/local-interchain/interchain/handlers/actions.go index 85998837f..f21b1f8a9 100644 --- a/local-interchain/interchain/handlers/actions.go +++ b/local-interchain/interchain/handlers/actions.go @@ -151,21 +151,29 @@ func (a *actions) PostActions(w http.ResponseWriter, r *http.Request) { // Relayer Actions if the above is not used. if len(stdout) == 0 && len(stderr) == 0 && err == nil { - if err := a.relayerCheck(w, r); err != nil { - return - } - switch action { case "stop-relayer", "stop_relayer", "stopRelayer": + if err := a.relayerCheck(w, r); err != nil { + return + } + err = a.relayer.StopRelayer(a.ctx, a.eRep) case "start-relayer", "start_relayer", "startRelayer": + if err := a.relayerCheck(w, r); err != nil { + return + } + paths := strings.FieldsFunc(ah.Cmd, func(c rune) bool { return c == ',' || c == ' ' }) err = a.relayer.StartRelayer(a.ctx, a.eRep, paths...) case "relayer", "relayer-exec", "relayer_exec", "relayerExec": + if err := a.relayerCheck(w, r); err != nil { + return + } + if !strings.Contains(ah.Cmd, "--home") { // does this ever change for any other relayer? cmd = append(cmd, "--home", "/home/relayer") @@ -177,6 +185,10 @@ func (a *actions) PostActions(w http.ResponseWriter, r *http.Request) { err = res.Err case "get_channels", "get-channels", "getChannels": + if err := a.relayerCheck(w, r); err != nil { + return + } + res, err := a.relayer.GetChannels(a.ctx, a.eRep, chainId) if err != nil { util.WriteError(w, err)