From d6447e2200d2f219d4a33fd5bc006c92bcf119b9 Mon Sep 17 00:00:00 2001 From: nhannamsiu Date: Tue, 17 Aug 2021 18:22:49 +0700 Subject: [PATCH 01/21] Init commit --- .github/workflows/release.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..a1e0f8e1 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,28 @@ +name: Release prod docker image +on: + push: + branches: [release-prod] + +jobs: + docker-release: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout peggo + uses: actions/checkout@master + with: + repository: InjectiveLabs/peggo + ref: release-prod + token: ${{ secrets.GH_TOKEN }} + path: ./peggo + - name: Build image + run: | + cd peggo + docker build -t public.ecr.aws/l9h3g6c6/peggo:prod . + - name: Push image + run: | + aws configure set aws_access_key_id ${{ secrets.AWS_KEY }} + aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET }} + aws configure set region us-east-1 + aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/l9h3g6c6 + docker push public.ecr.aws/l9h3g6c6/peggo:prod From b007736e1949e8c75ace849602d991311cae6c2c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 20 Dec 2023 17:18:39 +0100 Subject: [PATCH 02/21] add new files from test-patch --- orchestrator/coingecko/dummy_coingecko.go | 28 +++ test/deploy_token.sh | 11 ++ test/peggo/deploy_bridge.sh | 221 ++++++++++++++++++++++ test/peggo/example.env | 47 +++++ test/peggo/peggy_params.json | 38 ++++ test/run.sh | 35 ++++ test/send_to_eth.sh | 13 ++ test/send_to_inj.sh | 16 ++ 8 files changed, 409 insertions(+) create mode 100644 orchestrator/coingecko/dummy_coingecko.go create mode 100755 test/deploy_token.sh create mode 100755 test/peggo/deploy_bridge.sh create mode 100644 test/peggo/example.env create mode 100644 test/peggo/peggy_params.json create mode 100755 test/run.sh create mode 100755 test/send_to_eth.sh create mode 100755 test/send_to_inj.sh diff --git a/orchestrator/coingecko/dummy_coingecko.go b/orchestrator/coingecko/dummy_coingecko.go new file mode 100644 index 00000000..1732ac5d --- /dev/null +++ b/orchestrator/coingecko/dummy_coingecko.go @@ -0,0 +1,28 @@ +package coingecko + +import ( + "errors" + "github.com/ethereum/go-ethereum/common" +) + +type DummyCoingeckoFeed struct { + tokens map[string]string // token_addr -> denom +} + +func NewDummyCoingeckoFeed() DummyCoingeckoFeed { + return DummyCoingeckoFeed{ + tokens: map[string]string{ + "0x7E5C521F8515017487750c13C3bF3B15f3f5f654": "inj", + "0x1ccec198630F2024c64C0aFC5aE2427bc8e2dce8": "wut", + }, + } +} + +func (f DummyCoingeckoFeed) QueryUSDPrice(address common.Address) (float64, error) { + switch f.tokens[address.Hex()] { + case "inj", "wut": + return 10, nil + default: + return 0, errors.New("unknown token") + } +} diff --git a/test/deploy_token.sh b/test/deploy_token.sh new file mode 100755 index 00000000..c1c48502 --- /dev/null +++ b/test/deploy_token.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +deployer_pk=$(cat ./ethereum/geth/clique_signer.key) +peggy_contract="../solidity/contracts/Peggy.sol" +peggy_contract_address=0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe + +etherman --name Peggy --source "$peggy_contract" -P "$deployer_pk" tx "$peggy_contract_address" deployERC20 "wut" "wat" "wat" 18 diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh new file mode 100755 index 00000000..5e936df8 --- /dev/null +++ b/test/peggo/deploy_bridge.sh @@ -0,0 +1,221 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +# bytes32 encoding of "injective-peggyid". See peggy_params.json +PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d70656767796964000000000000000000000000000000}" +POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" +VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" +VALIDATOR_POWERS="${VALIDATOR_POWERS:-1431655765,1431655765,1431655765}" + +echo "** Deploying Peggy contract suite **" + +deployer_pk=$(cat ../ethereum/geth/clique_signer.key) + +peggy_contract_path="../../solidity/contracts/Peggy.sol" +peggy_admin_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" +upgradeable_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" +cosmos_coin_contract_path="../../solidity/contracts/CosmosToken.sol" + +echo "Using PEGGY_ID $PEGGY_ID" +echo "Using POWER_THRESHOLD $POWER_THRESHOLD" +echo "Using VALIDATOR_ADDRESSES $VALIDATOR_ADDRESSES" +echo "Using VALIDATOR_POWERS $VALIDATOR_POWERS" +echo -e "\n" + +peggy_impl_address=$(etherman --name Peggy \ + --source $peggy_contract_path \ + -P "$deployer_pk" \ + deploy) +echo "Deployed Peggy implementation contract: $peggy_impl_address" + +peggy_init_data=$(etherman --name Peggy \ + --source $peggy_contract_path \ + -P "$deployer_pk" \ + tx --bytecode "$peggy_impl_address" \ + initialize "$PEGGY_ID" "$POWER_THRESHOLD" "$VALIDATOR_ADDRESSES" "$VALIDATOR_POWERS") +echo "Initialized Peggy implementation contract. Init data:" +echo "$peggy_init_data" + +proxy_admin_address=$(etherman --name ProxyAdmin \ + -P "$deployer_pk" \ + --source "$peggy_admin_proxy_contract_path" \ + deploy) +echo "Deployed ProxyAdmin contract for Peggy: $proxy_admin_address" + +peggy_proxy_address=$(etherman --name TransparentUpgradeableProxy \ + --source "$upgradeable_proxy_contract_path" \ + -P "$deployer_pk" \ + deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") +echo "Deployed TransparentUpgradeableProxy for $peggy_impl_address (Peggy) with $proxy_admin_address (ProxyAdmin) as the admin" + +coin_contract_address=$(etherman --name CosmosERC20 \ + -P "$deployer_pk" \ + --source "$cosmos_coin_contract_path" \ + deploy "$peggy_proxy_address" "Injective" "inj" 18) +echo "Deployed Cosmos Coin contract: $coin_contract_address" + +peggy_block_number=$(curl http://localhost:8545 \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"id":1,"jsonrpc":"2.0", "method":"eth_getBlockByNumber","params":["latest", true]}' 2>/dev/null \ + | python3 -c "import sys, json; print(int(json.load(sys.stdin)['result']['number'], 0))") + +echo "Peggy deployment done!" +echo " * Contract address: $peggy_proxy_address" +echo " * Contract deployment height: $peggy_block_number" +echo -e "=======================\n" + +sleep 2 + +PASSPHRASE="12345678" +TX_OPTS="--chain-id injective-333 --keyring-backend test --broadcast-mode sync --yes" + +peggy_params_json="./peggy_params.json" +chain_dir="/Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333" +n0_home_dir=$chain_dir/n0 +n1_home_dir=$chain_dir/n1 +n2_home_dir=$chain_dir/n2 + +# Update peggy_params.json +jq --arg cosmos_coin_erc20 "$coin_contract_address" \ + --arg bridge_contract_height "$peggy_block_number" \ + --arg bridge_ethereum "$peggy_proxy_address" \ + '.messages[0].params.cosmos_coin_erc20_contract = $cosmos_coin_erc20 | + .messages[0].params.bridge_contract_start_height = $bridge_contract_height | + .messages[0].params.bridge_ethereum_address = $bridge_ethereum' \ + $peggy_params_json > tmpfile && mv tmpfile $peggy_params_json + +# usage: resp_check [resp] [err_msg] +resp_check() { + if [ "$(echo -e "$1" | awk -F"'" '/raw_log: /{print $2}')" != "[]" ]; then + echo "$2" + exit 1 + fi +} + +echo "Submitting gov proposal for Peggy module params update..." +cat $peggy_params_json + +resp="$(yes $PASSPHRASE | injectived tx gov submit-proposal $peggy_params_json --home $n0_home_dir --from user --gas 2000000 --gas-prices 500000000inj $TX_OPTS)" +resp_check "$resp" "Failed to submit gov proposal" + +sleep 2 + +current_proposal_id=$(curl 'http://localhost:10337/cosmos/gov/v1beta1/proposals?proposal_status=0&pagination.limit=1&pagination.reverse=true' 2>/dev/null | jq -r '.proposals[].proposal_id') + +resp="$(yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n0_home_dir --from val --gas-prices 500000000inj $TX_OPTS)" +resp_check "$resp" "val0 failed to vote on gov proposal" + +resp="$(yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n1_home_dir --from val --gas-prices 500000000inj $TX_OPTS)" +resp_check "$resp" "val1 failed to vote on gov proposal" + +resp="$(yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n2_home_dir --from val --gas-prices 500000000inj $TX_OPTS)" +resp_check "$resp" "val2 failed to vote on gov proposal" + +echo -n "Waiting for proposal to pass..." +sleep 8 +echo "DONE" + +n0_inj_addr="inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" +n1_inj_addr="inj1jcltmuhplrdcwp7stlr4hlhlhgd4htqhe4c0cs" +n2_inj_addr="inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz" + +n0_eth_addr="0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4" +n1_eth_addr="0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c" +n2_eth_addr="0x8B094eD440900CEB75B83A22eD8A2C7582B442C2" + +resp="$(injectived tx peggy set-orchestrator-address $n0_inj_addr $n0_inj_addr $n0_eth_addr --home $n0_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS)" +resp_check "$resp" "val0 failed to register orchestrator address" + +resp="$(injectived tx peggy set-orchestrator-address $n1_inj_addr $n1_inj_addr $n1_eth_addr --home $n1_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS)" +resp_check "$resp" "val1 failed to register orchestrator address" + +resp="$(injectived tx peggy set-orchestrator-address $n2_inj_addr $n2_inj_addr $n2_eth_addr --home $n2_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS)" +resp_check "$resp" "val2 failed to register orchestrator address" + +echo -n "Registering orchestrator ETH addresses..." +sleep 2 +echo "DONE" + +# Start peggo service +echo "Starting 3x Peggo orchestrators..." + +cwd=$(pwd) +example_env=$cwd/example.env +localhost_tcp="tcp://localhost" +localhost_http="http://localhost" + +n0_peggo_dir=$cwd/data/n0 +n1_peggo_dir=$cwd/data/n1 +n2_peggo_dir=$cwd/data/n2 + +n0_peggo_env=$n0_peggo_dir/.env +n1_peggo_env=$n1_peggo_dir/.env +n2_peggo_env=$n2_peggo_dir/.env + +mkdir -p "$n0_peggo_dir" && touch "$n0_peggo_env" +mkdir -p "$n1_peggo_dir" && touch "$n1_peggo_env" +mkdir -p "$n2_peggo_dir" && touch "$n2_peggo_env" + +n0_cosmos_grpc="$localhost_tcp:9090" +n1_cosmos_grpc="$localhost_tcp:9091" +n2_cosmos_grpc="$localhost_tcp:9092" + +n0_tendermint_rpc="$localhost_http:26657" +n1_tendermint_rpc="$localhost_http:26667" +n2_tendermint_rpc="$localhost_http:26677" + +n0_eth_pk="e85344fa1e00f06bd286b716e410ee0ad73541956c4cf59520f6db13599eb3f3" +n1_eth_pk="60f6ee19454b8ff45693cd54c55860785e4af9eeb06d6c5617568458e4ca5c54" +n2_eth_pk="21eeff959d9752704e3f1ad6562fd0458c003bce0947e5aecf07b602f4e457aa" + +sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n0_eth_addr\"|" \ + -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n0_eth_pk\"|" \ + -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n0_home_dir\"|" \ + -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n0_cosmos_grpc\"|" \ + -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n0_tendermint_rpc\"|" \ + "$example_env" > "$n0_peggo_env" + +sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n1_eth_addr\"|" \ + -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n1_eth_pk\"|" \ + -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n1_home_dir\"|" \ + -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n1_cosmos_grpc\"|" \ + -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n1_tendermint_rpc\"|" \ + "$example_env" > "$n1_peggo_env" + +sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n2_eth_addr\"|" \ + -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n2_eth_pk\"|" \ + -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n2_home_dir\"|" \ + -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n2_cosmos_grpc\"|" \ + -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n2_tendermint_rpc\"|" \ + "$example_env" > "$n2_peggo_env" + +# One relayer has lower min batch fee +CHEAP_RELAYER="${CHEAP_RELAYER:-false}" +if [[ "$CHEAP_RELAYER" == true ]]; then + echo "Setting n2 orchestrator with PEGGO_MIN_BATCH_FEE_USD to 10" + echo "$n2_peggo_env" + sed -i '' 's/^PEGGO_MIN_BATCH_FEE_USD=.*/PEGGO_MIN_BATCH_FEE_USD=10/' "$n2_peggo_env" +fi + +# Start a new tmux session +tmux new-session -d -s mysession + +# Split the terminal vertically into three equally spaced panes +tmux split-window -v +tmux split-window -v +tmux select-layout even-vertical + +# Select each pane and run a command from a different directory +peggo_cmd="peggo orchestrator" +tmux send-keys -t 0 "cd $n0_peggo_dir" C-m "$peggo_cmd" C-m +tmux send-keys -t 1 "cd $n1_peggo_dir" C-m "$peggo_cmd" C-m +tmux send-keys -t 2 "cd $n2_peggo_dir" C-m "$peggo_cmd" C-m + +# Attach to the tmux session to view the processes +tmux attach-session -t mysession + + diff --git a/test/peggo/example.env b/test/peggo/example.env new file mode 100644 index 00000000..f7beae17 --- /dev/null +++ b/test/peggo/example.env @@ -0,0 +1,47 @@ +PEGGO_ENV="local" +PEGGO_LOG_LEVEL="debug" +PEGGO_SERVICE_WAIT_TIMEOUT="1m" + +PEGGO_COSMOS_CHAIN_ID="injective-333" +PEGGO_COSMOS_GRPC="tcp://localhost:9900" +PEGGO_TENDERMINT_RPC="http://localhost:26657" + +PEGGO_COSMOS_FEE_DENOM="inj" +PEGGO_COSMOS_GAS_PRICES="500000000inj" + +PEGGO_COSMOS_KEYRING="test" +PEGGO_COSMOS_KEYRING_DIR=/Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 +PEGGO_COSMOS_KEYRING_APP="injectived" +PEGGO_COSMOS_FROM=val +PEGGO_COSMOS_FROM_PASSPHRASE=12345678 +PEGGO_COSMOS_PK= +PEGGO_COSMOS_USE_LEDGER=false + +PEGGO_ETH_CHAIN_ID=50 +PEGGO_ETH_RPC="http://localhost:8545" +PEGGO_ETH_ALCHEMY_WS="" +PEGGO_ETH_CONTRACT_ADDRESS= + +PEGGO_COINGECKO_API="https://api.coingecko.com/api/v3" + +PEGGO_ETH_KEYSTORE_DIR="/Users/dbrajovic/Desktop/dev/Injective/peggo/test/ethereum/data/50/keystore" +PEGGO_ETH_FROM=0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4 +PEGGO_ETH_PASSPHRASE=12345678 +PEGGO_ETH_PK=e85344fa1e00f06bd286b716e410ee0ad73541956c4cf59520f6db13599eb3f3 +PEGGO_ETH_USE_LEDGER=false +PEGGO_ETH_GAS_PRICE_ADJUSTMENT=1.3 +PEGGO_ETH_MAX_GAS_PRICE="500gwei" + +PEGGO_RELAY_VALSETS=true +PEGGO_RELAY_VALSET_OFFSET_DUR="3m" +PEGGO_RELAY_BATCHES=true +PEGGO_RELAY_BATCH_OFFSET_DUR="3m" +PEGGO_RELAY_PENDING_TX_WAIT_DURATION="20m" + +PEGGO_MIN_BATCH_FEE_USD=23.2 + +PEGGO_STATSD_PREFIX="peggo." +PEGGO_STATSD_ADDR="localhost:8125" +PEGGO_STATSD_STUCK_DUR="5m" +PEGGO_STATSD_MOCKING=false +PEGGO_STATSD_DISABLED=true \ No newline at end of file diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json new file mode 100644 index 00000000..f551c6f0 --- /dev/null +++ b/test/peggo/peggy_params.json @@ -0,0 +1,38 @@ +{ + "messages": [ + { + "@type": "/injective.peggy.v1.MsgUpdateParams", + "authority": "inj10d07y265gmmuvt4z0w9aw880jnsr700jstypyt", + "params": { + "peggy_id": "injective-peggyid", + "contract_source_hash": "", + "bridge_ethereum_address": "0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe", + "bridge_chain_id": "50", + "signed_valsets_window": "25000", + "signed_batches_window": "25000", + "signed_claims_window": "25000", + "target_batch_timeout": "43200000", + "average_block_time": "1000", + "average_ethereum_block_time": "15000", + "slash_fraction_valset": "0.001000000000000000", + "slash_fraction_batch": "0.001000000000000000", + "slash_fraction_claim": "0.001000000000000000", + "slash_fraction_conflicting_claim": "0.001000000000000000", + "unbond_slashing_valsets_window": "25000", + "slash_fraction_bad_eth_signature": "0.001000000000000000", + "cosmos_coin_denom": "inj", + "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", + "claim_slashing_enabled": false, + "bridge_contract_start_height": "23", + "valset_reward": { + "denom": "inj", + "amount": "0" + } + } + } + ], + "metadata": "ipfs://CID", + "deposit": "100000001inj", + "title": "Peggo local testing env", + "summary": "The time I needed a working environment to test the bridge" +} diff --git a/test/run.sh b/test/run.sh new file mode 100755 index 00000000..46f00297 --- /dev/null +++ b/test/run.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +# +#killall injectived 2> /dev/null +#killall geth 2> /dev/null + + +# +#killall "injectived" &> /dev/null +#killall "geth" &> /dev/null + + +cwd=$(pwd) +cosmos_dir="$cwd/cosmos" +eth_dir="$cwd/ethereum" +peggo_dir="$cwd/peggo" + +rm -rf "$cosmos_dir/data" +rm -rf "$eth_dir/data" +rm -rf "$peggo_dir/data" +rm -rf "$peggo_dir/build" + +# Start the Ethereum chain +"$eth_dir"/geth-init.sh +"$eth_dir"/geth.sh + +# Start the Cosmos chain +"$cosmos_dir"/multinode.sh injectived + +# Deploy Peggy contract suite and start peggo relayers +"$peggo_dir"/deploy_bridge.sh diff --git a/test/send_to_eth.sh b/test/send_to_eth.sh new file mode 100755 index 00000000..1b26da16 --- /dev/null +++ b/test/send_to_eth.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +passphrase=12345678 + +# Send INJ tokens +yes $passphrase | injectived tx peggy send-to-eth "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" 10inj 3000000000000000000inj --chain-id=injective-333 --gas-prices 500000000inj --keyring-backend test --broadcast-mode=sync --yes --home /Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 --from user + +# Send WAT tokens (premined) +#yes $passphrase | injectived tx peggy send-to-eth "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" 10wut 1500000000000000000wut --chain-id=injective-333 --gas-prices 500000000inj --keyring-backend test --broadcast-mode=sync --yes --home /Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 --from user diff --git a/test/send_to_inj.sh b/test/send_to_inj.sh new file mode 100755 index 00000000..dc86d1ec --- /dev/null +++ b/test/send_to_inj.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +deployer_pk=$(cat ./ethereum/geth/clique_signer.key) +peggy_contract="../solidity/contracts/Peggy.sol" +cosmos_token_contract="../solidity/contracts/CosmosToken.sol" + +peggy_contract_address=0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe +inj_coin_contract_address=$(cat ./peggo/peggy_coin_address.txt) + +etherman --name CosmosERC20 --source "$cosmos_token_contract" -P "$deployer_pk" tx "$inj_coin_contract_address" approve "$peggy_contract_address" 100 +etherman --name Peggy --source "$peggy_contract" -P "$deployer_pk" tx "$peggy_contract_address" sendToInjective "$inj_coin_contract_address" 727aee334987c52fa7b567b2662bdbb68614e48c 10 "" + From d1bcda5d509b4e82e429abb142f32925224c4445 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 20 Dec 2023 17:23:51 +0100 Subject: [PATCH 03/21] add new changes in existing files from test-patch --- solidity/contracts/CosmosToken.sol | 5 +- test/cosmos/multinode.sh | 104 ++++++++++++++++++------- test/ethereum/geth-init.sh | 28 +++---- test/ethereum/geth.sh | 45 ++++++----- test/ethereum/geth/clique_genesis.json | 14 +++- 5 files changed, 128 insertions(+), 68 deletions(-) diff --git a/solidity/contracts/CosmosToken.sol b/solidity/contracts/CosmosToken.sol index aa7f32a9..b1b52fa8 100644 --- a/solidity/contracts/CosmosToken.sol +++ b/solidity/contracts/CosmosToken.sol @@ -15,7 +15,10 @@ contract CosmosERC20 is ERC20 { uint8 decimals_ ) ERC20(name_, symbol_) { _decimals = decimals_; - _mint(peggyAddress_, MAX_UINT); + uint amountToMint = 100 * 10**decimals_; + + _mint(peggyAddress_, MAX_UINT-amountToMint); + _mint(0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e, amountToMint); } function decimals() public view virtual override returns (uint8) { diff --git a/test/cosmos/multinode.sh b/test/cosmos/multinode.sh index 9e3debda..cc17e45f 100755 --- a/test/cosmos/multinode.sh +++ b/test/cosmos/multinode.sh @@ -7,27 +7,32 @@ set -e +cd "${0%/*}" # cd to current script dir + CWD=$(pwd) # These options can be overridden by env -CHAIN_ID="${CHAIN_ID:-888}" +CHAIN_ID="${CHAIN_ID:-"injective-333"}" CHAIN_DIR="${CHAIN_DIR:-$CWD/data}" -DENOM="${DENOM:-uatom}" +DENOM="${DENOM:-inj}" STAKE_DENOM="${STAKE_DENOM:-$DENOM}" CLEANUP="${CLEANUP:-0}" LOG_LEVEL="${LOG_LEVEL:-main:info,state:info,statesync:info,*:error}" SCALE_FACTOR="${SCALE_FACTOR:-000000000000000000}" +NEWLINE=$'\n' # Default 3 account keys + 1 user key with no special grants VAL0_KEY="val" VAL0_MNEMONIC="copper push brief egg scan entry inform record adjust fossil boss egg comic alien upon aspect dry avoid interest fury window hint race symptom" + VAL1_KEY="val" VAL1_MNEMONIC="maximum display century economy unlock van census kite error heart snow filter midnight usage egg venture cash kick motor survey drastic edge muffin visual" + VAL2_KEY="val" VAL2_MNEMONIC="keep liar demand upon shed essence tip undo eagle run people strong sense another salute double peasant egg royal hair report winner student diamond" + USER_KEY="user" USER_MNEMONIC="pony glide frown crisp unfold lawn cup loan trial govern usual matrix theory wash fresh address pioneer between meadow visa buffalo keep gallery swear" -NEWLINE=$'\n' hdir="$CHAIN_DIR/$CHAIN_ID" @@ -44,22 +49,12 @@ then exit 1 fi -# Expect Chain ID to be provided -if [[ -z "$CHAIN_ID" ]]; then - echo "Please provide Cosmos CHAIN_ID env" - exit 1 -fi - -# Expect data prefix to be provided -if [[ -z "$CHAIN_DIR" ]]; then - echo "Please provide CHAIN_DIR data prefix" - exit 1 -fi - NODE_BIN="$1" echo "Using $CHAIN_ID as Chain ID and $CHAIN_DIR as data prefix." echo "Using $DENOM as Cosmos Coin Denom." +echo "Using $STAKE_DENOM as Cosmos Stake Denom." + if [[ "$CLEANUP" == 1 || "$CLEANUP" == "1" ]]; then echo "Will remove $CHAIN_DIR" fi @@ -135,10 +130,10 @@ if [[ ! -d "$hdir" ]]; then yes "$USER_MNEMONIC$NEWLINE" | $NODE_BIN $home2 keys add $USER_KEY $kbt --recover &>/dev/null # Add addresses to genesis - $NODE_BIN $home0 add-genesis-account $($NODE_BIN $home0 keys show $VAL0_KEY -a $kbt) $coins &>/dev/null - $NODE_BIN $home0 add-genesis-account $($NODE_BIN $home1 keys show $VAL1_KEY -a $kbt) $coins &>/dev/null - $NODE_BIN $home0 add-genesis-account $($NODE_BIN $home2 keys show $VAL2_KEY -a $kbt) $coins &>/dev/null - $NODE_BIN $home0 add-genesis-account $($NODE_BIN $home0 keys show $USER_KEY -a $kbt) $coins_user &>/dev/null + $NODE_BIN $home0 $cid add-genesis-account $($NODE_BIN $home0 keys show $VAL0_KEY -a $kbt) $coins + $NODE_BIN $home0 $cid add-genesis-account $($NODE_BIN $home1 keys show $VAL1_KEY -a $kbt) $coins &>/dev/null + $NODE_BIN $home0 $cid add-genesis-account $($NODE_BIN $home2 keys show $VAL2_KEY -a $kbt) $coins &>/dev/null + $NODE_BIN $home0 $cid add-genesis-account $($NODE_BIN $home0 keys show $USER_KEY -a $kbt) $coins_user &>/dev/null # Patch genesis.json to better configure stuff for testing purposes if [[ "$STAKE_DENOM" == "$DENOM" ]]; then @@ -146,19 +141,62 @@ if [[ ! -d "$hdir" ]]; then cat $n0cfgDir/genesis.json | jq '.app_state["crisis"]["constant_fee"]["denom"]="'$DENOM'"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="'$DENOM'"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json cat $n0cfgDir/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="'$DENOM'"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json + cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["params"]["min_deposit"][0]["denom"]="'$DENOM'"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json fi echo "NOTE: Setting Governance Voting Period to 10 seconds for rapid testing" cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["voting_params"]["voting_period"]="10s"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json + cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["params"]["voting_period"]="10s"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json + + + # Mint Watever tokens for each validator + jq '.app_state.bank.supply += [ + { + "denom": "wut", + "amount": "16000000000000000000000000" + } + ]' $n0cfgDir/genesis.json > tmp_file && mv tmp_file $n0cfgDir/genesis.json + + jq '.app_state.bank.balances |= map(.coins += [ + { + "denom": "wut", + "amount": "4000000000000000000000000" + } + ])' $n0cfgDir/genesis.json > tmp_file && mv tmp_file $n0cfgDir/genesis.json + + jq '.app_state.bank.denom_metadata += [ + { + "description": "Some token I made for testing the bridge", + "denom_units": [ + { + "denom": "wut", + "exponent": 0, + "aliases": [] + }, + { + "denom": "wat", + "exponent": 18, + "aliases": [] + } + ], + "base": "wut", + "display": "wat", + "name": "Watever", + "symbol": "wat", + "uri": "", + "uri_hash": "" + } + ]' $n0cfgDir/genesis.json > tmp_file && mv tmp_file $n0cfgDir/genesis.json + # Copy genesis around to sign cp $n0cfgDir/genesis.json $n1cfgDir/genesis.json cp $n0cfgDir/genesis.json $n2cfgDir/genesis.json # Create gentxs and collect them in n0 - $NODE_BIN $home0 gentx $VAL0_KEY --amount=1000$SCALE_FACTOR$STAKE_DENOM $kbt $cid &>/dev/null - $NODE_BIN $home1 gentx $VAL1_KEY --amount=1000$SCALE_FACTOR$STAKE_DENOM $kbt $cid &>/dev/null - $NODE_BIN $home2 gentx $VAL2_KEY --amount=1000$SCALE_FACTOR$STAKE_DENOM $kbt $cid &>/dev/null + $NODE_BIN $home0 gentx $VAL0_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid + $NODE_BIN $home1 gentx $VAL1_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid &>/dev/null + $NODE_BIN $home2 gentx $VAL2_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid &>/dev/null cp $n1cfgDir/gentx/*.json $n0cfgDir/gentx/ cp $n2cfgDir/gentx/*.json $n0cfgDir/gentx/ @@ -177,7 +215,7 @@ if [[ ! -d "$hdir" ]]; then # Example usage: $REGEX_REPLACE 's/^param = ".*?"/param = "100"/' config.toml REGEX_REPLACE="perl -i -pe" - echo "regex replacing config variables" + echo "Regex replacing config variables" $REGEX_REPLACE 's|addr_book_strict = true|addr_book_strict = false|g' $n0cfg $REGEX_REPLACE 's|external_address = ""|external_address = "tcp://127.0.0.1:26657"|g' $n0cfg @@ -217,20 +255,26 @@ if [[ ! -d "$hdir" ]]; then $REGEX_REPLACE 's|persistent_peers = ""|persistent_peers = "'$peer1','$peer2'"|g' $n0cfg $REGEX_REPLACE 's|persistent_peers = ""|persistent_peers = "'$peer0','$peer2'"|g' $n1cfg $REGEX_REPLACE 's|persistent_peers = ""|persistent_peers = "'$peer0','$peer1'"|g' $n2cfg + fi # data dir check # Start the instances -echo "Starting nodes..." +echo "Starting injectived nodes..." + +echo $NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" --grpc-web.address "0.0.0.0:9080" +$NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" --grpc-web.address "0.0.0.0:9080" > $hdir.n0.log 2>&1 & + +echo $NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" --grpc-web.address "0.0.0.0:9081" +$NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" --grpc-web.address "0.0.0.0:9081" > $hdir.n1.log 2>&1 & + +echo $NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" --grpc-web.address "0.0.0.0:9082" +$NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" --grpc-web.address "0.0.0.0:9082" > $hdir.n2.log 2>&1 & -echo $NODE_BIN $home0 start --grpc.address="0.0.0.0:9090" -$NODE_BIN $home0 start --grpc.address="0.0.0.0:9090" > $hdir.n0.log 2>&1 & -$NODE_BIN $home1 start --grpc.address="0.0.0.0:9091" > $hdir.n1.log 2>&1 & -$NODE_BIN $home2 start --grpc.address="0.0.0.0:9092" > $hdir.n2.log 2>&1 & # Wait for chains to start -echo "Waiting for chains to start..." -sleep 8 +echo "Waiting for chain to start..." +sleep 5 echo echo "Logs:" diff --git a/test/ethereum/geth-init.sh b/test/ethereum/geth-init.sh index 4cdab0dc..d4c2a1b9 100755 --- a/test/ethereum/geth-init.sh +++ b/test/ethereum/geth-init.sh @@ -2,24 +2,26 @@ set -e +cd "${0%/*}" # cd to current script dir + CWD=$(pwd) # These options can be overridden by env GETH_NETWORK_ID="${GETH_NETWORK_ID:-50}" -GETH_ALGO="${GETH_ALGO:-ethash}" +GETH_ALGO="${GETH_ALGO:-clique}" CHAIN_DIR="${CHAIN_DIR:-$CWD/data}" -hdir="$CHAIN_DIR/$GETH_NETWORK_ID" -ddir="--datadir $hdir" +if [[ $GETH_ALGO != "clique" ]]; then + echo "Unsupported geth algo: $GETH_ALGO. Must use clique" + exit 1 +fi + +DATA_DIR="$CHAIN_DIR/$GETH_NETWORK_ID" -cd "${0%/*}" # cd to current script dir +# Initialize geth dir and setup account +geth init --datadir "$DATA_DIR" ./geth/clique_genesis.json +geth account import --datadir "$DATA_DIR" --lightkdf --password ./geth/clique_password.txt ./geth/clique_signer.key -if [[ $GETH_ALGO == "ethash" ]]; then - geth init $ddir ./geth/genesis.json -elif [[ $GETH_ALGO == "clique" ]]; then - geth init $ddir ./geth/clique_genesis.json - geth account import $ddir --lightkdf --password ./geth/clique_password.txt ./geth/clique_signer.key -else - echo "Unsupported Geth algo: $GETH_ALGO, use ethash or clique" - exit 1 -fi +# Create PID and log file +touch "$CHAIN_DIR/$GETH_NETWORK_ID.geth.pid" +touch "$CHAIN_DIR/$GETH_NETWORK_ID.geth.log" \ No newline at end of file diff --git a/test/ethereum/geth.sh b/test/ethereum/geth.sh index ac7ad4a7..9d5c0409 100755 --- a/test/ethereum/geth.sh +++ b/test/ethereum/geth.sh @@ -2,45 +2,44 @@ set -e +cd "${0%/*}" # cd to current script dir + CWD=$(pwd) # These options can be overridden by env GETH_PORT="${GETH_PORT:-8545}" GETH_NETWORK_ID="${GETH_NETWORK_ID:-50}" -GETH_ALGO="${GETH_ALGO:-ethash}" +GETH_ALGO="${GETH_ALGO:-clique}" GETH_BLOCK_GAS_LIMIT="${GETH_BLOCK_GAS_LIMIT:-60000000}" CHAIN_DIR="${CHAIN_DIR:-$CWD/data}" +MINER_ADDR="0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" -hdir="$CHAIN_DIR/$GETH_NETWORK_ID" -mkdir -p $hdir +DATA_DIR="$CHAIN_DIR/$GETH_NETWORK_ID" -# killall geth -kill $(cat $hdir.geth.pid) &>/dev/null && rm $hdir.geth.pid || true +if [[ $GETH_ALGO != "clique" ]]; then + echo "Unsupported geth algo: $GETH_ALGO. Must use clique" + exit 1 +fi + +# Kill the node if it's already running +pid="$(cat "$DATA_DIR.geth.pid")" +if kill "$pid" &>/dev/null; then + rm "$DATA_DIR.geth.pid" +fi sleep 1 -cd "${0%/*}" # cd to current script dir +# Start the local geth node +geth --datadir "$DATA_DIR" --networkid "$GETH_NETWORK_ID" --nodiscover \ + --http --http.port "$GETH_PORT" --http.api personal,eth,net,web3 --allow-insecure-unlock \ + --miner.etherbase $MINER_ADDR --unlock $MINER_ADDR --password ./geth/clique_password.txt \ + --mine --miner.gaslimit "$GETH_BLOCK_GAS_LIMIT" > "$DATA_DIR".geth.log 2>&1 & -if [[ $GETH_ALGO == "ethash" ]]; then - geth --datadir $hdir --networkid $GETH_NETWORK_ID --nodiscover \ - --http --http.port $GETH_PORT --http.api personal,eth,net,web3 \ - --miner.threads=1 --etherbase=0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e \ - --mine --targetgaslimit $GETH_BLOCK_GAS_LIMIT > $hdir.geth.log 2>&1 & - echo $! > $hdir.geth.pid -elif [[ $GETH_ALGO == "clique" ]]; then - geth --datadir $hdir --networkid $GETH_NETWORK_ID --nodiscover \ - --http --http.port $GETH_PORT --http.api personal,eth,net,web3 --allow-insecure-unlock \ - --unlock 0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e --password ./geth/clique_password.txt \ - --mine --targetgaslimit $GETH_BLOCK_GAS_LIMIT > $hdir.geth.log 2>&1 & - echo $! > $hdir.geth.pid -else - echo "Unsupported Geth algo: $GETH_ALGO, use ethash or clique" - exit 1 -fi +echo $! > "$DATA_DIR".geth.pid # overwrite previous PID sleep 1 -echo +echo "** USAGE **" echo "Logs:" echo " tail -f ./data/$GETH_NETWORK_ID.geth.log" echo diff --git a/test/ethereum/geth/clique_genesis.json b/test/ethereum/geth/clique_genesis.json index 825399fd..5c9d9c84 100644 --- a/test/ethereum/geth/clique_genesis.json +++ b/test/ethereum/geth/clique_genesis.json @@ -21,7 +21,7 @@ "difficulty": "1", "alloc": { "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e": { - "balance": "0x1" + "balance": "0x6bc75e2d63100000" }, "0x5409ED021D9299bf6814279A6A1411A7e866A631": { "balance": "0x6bc75e2d63100000" @@ -52,6 +52,18 @@ }, "0x91c987bf62D25945dB517BDAa840A6c661374402": { "balance": "0x6bc75e2d63100000" + }, + + "0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4": { + "balance": "0x6bc75e2d63100000" + }, + + "0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c": { + "balance": "0x6bc75e2d63100000" + }, + + "0x8B094eD440900CEB75B83A22eD8A2C7582B442C2": { + "balance": "0x6bc75e2d63100000" } } } \ No newline at end of file From c29528d107986a94e82a899b286a34a46c006e4c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 20 Dec 2023 18:54:39 +0100 Subject: [PATCH 04/21] connect to local injective node during startup --- orchestrator/cosmos/network.go | 71 +++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 9ea64978..664970a8 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -2,7 +2,8 @@ package cosmos import ( "context" - "strconv" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tmclient" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" "time" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -23,7 +24,7 @@ import ( ) type Network struct { - //tmclient.TendermintClient + tmclient.TendermintClient PeggyQueryClient PeggyBroadcastClient explorerclient.ExplorerClient @@ -44,15 +45,6 @@ func NewNetwork( return nil, errors.Wrapf(err, "failed to create client context for Injective chain") } - //clientCtx = clientCtx.WithNodeURI(tendermintRPC) - - //tmRPC, err := rpchttp.New(tendermintRPC, "/websocket") - //if err != nil { - // return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", tendermintRPC) - //} - - //clientCtx = clientCtx.WithClient(tmRPC) - var networkName string switch chainID { @@ -63,16 +55,34 @@ func NewNetwork( case "injective-888": networkName = "testnet" default: - return nil, errors.Errorf("provided chain id %v does not belong to any known Injective network", chainID) - } + // local env testing + clientCtx = clientCtx.WithNodeURI(tendermintRPC) - netCfg := common.LoadNetwork(networkName, "lb") + tmRPC, err := rpchttp.New(tendermintRPC, "/websocket") + if err != nil { + return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", tendermintRPC) + } - explorer, err := explorerclient.NewExplorerClient(netCfg) - if err != nil { - return nil, err + clientCtx = clientCtx.WithClient(tmRPC) } + _ = networkName + + netCfg := common.LoadNetwork("devnet", "") + netCfg.Name = "local" + netCfg.ChainId = chainID + netCfg.Fee_denom = "inj" // todo: this is actually an env var but it's not parsed + netCfg.TmEndpoint = tendermintRPC + netCfg.ChainGrpcEndpoint = injectiveGRPC + netCfg.ExplorerGrpcEndpoint = "" + netCfg.LcdEndpoint = "" + netCfg.ExplorerGrpcEndpoint = "" + + //explorer, err := explorerclient.NewExplorerClient(netCfg) + //if err != nil { + // return nil, err + //} + daemonClient, err := chainclient.NewChainClient(clientCtx, netCfg, common.OptionGasPrices(injectiveGasPrices)) if err != nil { return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", injectiveGRPC) @@ -88,10 +98,9 @@ func NewNetwork( peggyQuerier := types.NewQueryClient(grpcConn) n := &Network{ - //TendermintClient: tmclient.NewRPCClient(tendermintRPC), + TendermintClient: tmclient.NewRPCClient(tendermintRPC), PeggyQueryClient: NewPeggyQueryClient(peggyQuerier), PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, signerFn, personalSignerFn), - ExplorerClient: explorer, } log.WithFields(log.Fields{ @@ -104,18 +113,26 @@ func NewNetwork( } func (n *Network) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) { - block, err := n.ExplorerClient.GetBlock(ctx, strconv.FormatInt(height, 10)) - if err != nil { - return time.Time{}, err - } + //block, err := n.ExplorerClient.GetBlock(ctx, strconv.FormatInt(height, 10)) + //if err != nil { + // return time.Time{}, err + //} + // + //blockTime, err := time.Parse("2006-01-02 15:04:05.999 -0700 MST", block.Data.Timestamp) + //if err != nil { + // return time.Time{}, errors.Wrap(err, "failed to parse timestamp from block") + //} + // + //return blockTime, nil - blockTime, err := time.Parse("2006-01-02 15:04:05.999 -0700 MST", block.Data.Timestamp) + // LOCAL TESTING // + + block, err := n.TendermintClient.GetBlock(ctx, height) if err != nil { - return time.Time{}, errors.Wrap(err, "failed to parse timestamp from block") + return time.Time{}, err } - return blockTime, nil - //return n.TendermintClient.GetBlock(ctx, height) + return block.Block.Time, nil } func (n *Network) PeggyParams(ctx context.Context) (*peggy.Params, error) { From df05057006744211b74d84dd54ddf50b696af870 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 21 Dec 2023 12:19:53 +0100 Subject: [PATCH 05/21] use dummy coingecko --- cmd/peggo/orchestrator.go | 4 +++- test/peggo/peggy_params.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 7aac5956..37fa462c 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -123,7 +123,9 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) - coingeckoFeed := coingecko.NewCoingeckoPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}) + //coingeckoFeed := coingecko.NewCoingeckoPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}) + // LOCAL ENV TESTING // + coingeckoFeed := coingecko.NewDummyCoingeckoFeed() // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index f551c6f0..899af315 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -23,7 +23,7 @@ "cosmos_coin_denom": "inj", "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", "claim_slashing_enabled": false, - "bridge_contract_start_height": "23", + "bridge_contract_start_height": "25", "valset_reward": { "denom": "inj", "amount": "0" From 6cfd22546b9bf82b5db1d64ecc6cab1994a8422f Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 21 Dec 2023 13:28:56 +0100 Subject: [PATCH 06/21] clean up oracle.go --- orchestrator/oracle.go | 413 +++++++++++++++++++++++------------ orchestrator/orchestrator.go | 56 +++++ 2 files changed, 326 insertions(+), 143 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 9ac8976d..bc83b5ba 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -8,7 +8,6 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/peggo/orchestrator/loops" wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) @@ -20,114 +19,54 @@ const ( defaultBlocksToSearch uint64 = 2000 ) -// EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain -// and ferried over to Cosmos where they will be used to issue tokens or process batches. -func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { - lastConfirmedEthHeight, err := s.getLastConfirmedEthHeightOnInjective(ctx) - if err != nil { - return err - } - - oracle := ðOracle{ - log: log.WithField("loop", "EthOracle"), - retries: s.maxAttempts, - lastResyncWithInjective: time.Now(), - lastCheckedEthHeight: lastConfirmedEthHeight, - } - - return loops.RunLoop( - ctx, - defaultLoopDur, - func() error { return oracle.run(ctx, s.injective, s.ethereum) }, - ) +type ethOracleLoop struct { + *PeggyOrchestrator + loopDuration time.Duration + lastResyncWithInjective time.Time + lastCheckedEthHeight uint64 } -func (s *PeggyOrchestrator) getLastConfirmedEthHeightOnInjective(ctx context.Context) (uint64, error) { - var lastConfirmedEthHeight uint64 - retryFn := func() error { - lastClaimEvent, err := s.injective.LastClaimEvent(ctx) - if err == nil && lastClaimEvent != nil && lastClaimEvent.EthereumEventHeight != 0 { - lastConfirmedEthHeight = lastClaimEvent.EthereumEventHeight - return nil - - } - - log.WithError(err).Warningf("failed to get last claim from Injective. Querying peggy params...") +func (l ethOracleLoop) Logger() log.Logger { + return l.logger.WithField("loop", "EthOracle") +} - peggyParams, err := s.injective.PeggyParams(ctx) +func (l ethOracleLoop) LoopFn(ctx context.Context, injective InjectiveNetwork, ethereum EthereumNetwork) func() error { + return func() error { + newHeight, err := l.relayEvents(ctx, injective, ethereum) if err != nil { - log.WithError(err).Fatalln("failed to query peggy module params, is injectived running?") return err } - lastConfirmedEthHeight = peggyParams.BridgeContractStartHeight - return nil - } - - if err := retry.Do(retryFn, - retry.Context(ctx), - retry.Attempts(s.maxAttempts), - retry.OnRetry(func(n uint, err error) { - log.WithError(err).Warningf("failed to get last confirmed Ethereum height on Injective, will retry (%d)", n) - }), - ); err != nil { - log.WithError(err).Errorln("got error, loop exits") - return 0, err - } - - return lastConfirmedEthHeight, nil -} - -type ethOracle struct { - log log.Logger - retries uint - lastResyncWithInjective time.Time - lastCheckedEthHeight uint64 -} - -func (o *ethOracle) run( - ctx context.Context, - injective InjectiveNetwork, - ethereum EthereumNetwork, -) error { - o.log.WithField("last_checked_eth_height", o.lastCheckedEthHeight).Infoln("scanning Ethereum for events") - - // Relays events from Ethereum -> Cosmos - newHeight, err := o.relayEvents(ctx, injective, ethereum) - if err != nil { - return err - } - - o.lastCheckedEthHeight = newHeight - - if time.Since(o.lastResyncWithInjective) >= 48*time.Hour { - /** - Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. - 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. - we need to re-scan this block to ensure events are not missed due to indexing delay. - 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. - 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. - **/ - if err := o.autoResync(ctx, injective); err != nil { - return err + l.Logger().WithFields(log.Fields{ + "block_start": l.lastCheckedEthHeight, + "block_end": newHeight, + }).Debugln("scanned Ethereum blocks for events") + l.lastCheckedEthHeight = newHeight + + if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { + /** + Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. + 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. + we need to re-scan this block to ensure events are not missed due to indexing delay. + 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. + 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. + **/ + if err := l.autoResync(ctx, injective); err != nil { + return err + } } - } - return nil + return nil + } } -func (o *ethOracle) relayEvents( - ctx context.Context, - injective InjectiveNetwork, - ethereum EthereumNetwork, -) (uint64, error) { - // Relays events from Ethereum -> Cosmos +func (l ethOracleLoop) relayEvents(ctx context.Context, injective InjectiveNetwork, ethereum EthereumNetwork) (uint64, error) { var ( latestHeight uint64 - currentHeight = o.lastCheckedEthHeight + currentHeight = l.lastCheckedEthHeight ) - retryFn := func() error { + scanEthBlocksAndRelayEventsFn := func() error { latestHeader, err := ethereum.HeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") @@ -136,7 +75,6 @@ func (o *ethOracle) relayEvents( // add delay to ensure minimum confirmations are received and block is finalised latestHeight = latestHeader.Number.Uint64() - ethBlockConfirmationDelay if latestHeight < currentHeight { - println(latestHeight) return nil } @@ -180,45 +118,18 @@ func (o *ethOracle) relayEvents( } legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) - o.log.WithFields(log.Fields{ - "block_start": currentHeight, - "block_end": latestHeight, - "events": legacyDeposits, - }).Debugln("scanned SendToCosmos events") - deposits = filterSendToInjectiveEventsByNonce(deposits, lastClaimEvent.EthereumEventNonce) - o.log.WithFields(log.Fields{ - "block_start": currentHeight, - "block_end": latestHeight, - "events": deposits, - }).Debugln("scanned SendToInjective events") - withdrawals = filterTransactionBatchExecutedEventsByNonce(withdrawals, lastClaimEvent.EthereumEventNonce) - o.log.WithFields(log.Fields{ - "block_start": currentHeight, - "block_end": latestHeight, - "events": withdrawals, - }).Debugln("scanned TransactionBatchExecuted events") - erc20Deployments = filterERC20DeployedEventsByNonce(erc20Deployments, lastClaimEvent.EthereumEventNonce) - o.log.WithFields(log.Fields{ - "block_start": currentHeight, - "block_end": latestHeight, - "events": erc20Deployments, - }).Debugln("scanned FilterERC20Deployed events") - valsetUpdates = filterValsetUpdateEventsByNonce(valsetUpdates, lastClaimEvent.EthereumEventNonce) - o.log.WithFields(log.Fields{ - "block_start": currentHeight, - "block_end": latestHeight, - "events": valsetUpdates, - }).Debugln("scanned ValsetUpdated events") if len(legacyDeposits) == 0 && len(deposits) == 0 && len(withdrawals) == 0 && len(erc20Deployments) == 0 && len(valsetUpdates) == 0 { + l.Logger().Debugln("no new events on Ethereum") + return nil } @@ -233,35 +144,35 @@ func (o *ethOracle) relayEvents( return errors.Wrap(err, "failed to send event claims to Injective") } - o.log.WithFields(log.Fields{ + l.Logger().WithFields(log.Fields{ "last_claim_event_nonce": lastClaimEvent.EthereumEventNonce, "legacy_deposits": len(legacyDeposits), "deposits": len(deposits), "withdrawals": len(withdrawals), - "erc20Deployments": len(erc20Deployments), - "valsetUpdates": len(valsetUpdates), + "erc2_deployments": len(erc20Deployments), + "valset_updates": len(valsetUpdates), }).Infoln("sent new claims to Injective") return nil } - if err := retry.Do(retryFn, + if err := retry.Do(scanEthBlocksAndRelayEventsFn, retry.Context(ctx), - retry.Attempts(o.retries), + retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - o.log.WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) }), ); err != nil { - o.log.WithError(err).Errorln("got error, loop exits") + l.Logger().WithError(err).Errorln("got error, loop exits") return 0, err } return latestHeight, nil } -func (o *ethOracle) autoResync(ctx context.Context, injective InjectiveNetwork) error { +func (l ethOracleLoop) autoResync(ctx context.Context, injective InjectiveNetwork) error { var latestHeight uint64 - retryFn := func() error { + getLastClaimEventFn := func() error { lastClaimEvent, err := injective.LastClaimEvent(ctx) if err != nil { return err @@ -271,28 +182,244 @@ func (o *ethOracle) autoResync(ctx context.Context, injective InjectiveNetwork) return nil } - if err := retry.Do(retryFn, + if err := retry.Do(getLastClaimEventFn, retry.Context(ctx), - retry.Attempts(o.retries), + retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - o.log.WithError(err).Warningf("failed to get last confirmed eth height, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get last confirmed eth height, will retry (%d)", n) }), ); err != nil { - o.log.WithError(err).Errorln("got error, loop exits") + l.Logger().WithError(err).Errorln("got error, loop exits") return err } - o.lastCheckedEthHeight = latestHeight - o.lastResyncWithInjective = time.Now() + l.lastCheckedEthHeight = latestHeight + l.lastResyncWithInjective = time.Now() - o.log.WithFields(log.Fields{ - "last_resync": o.lastResyncWithInjective.String(), - "last_confirmed_eth_height": o.lastCheckedEthHeight, - }).Infoln("auto resync") + l.Logger().WithFields(log.Fields{ + "last_resync_time": l.lastResyncWithInjective.String(), + "last_confirmed_eth_height": l.lastCheckedEthHeight, + }).Infoln("auto resync nonce with Injective") return nil } +// +//type ethOracle struct { +// log log.Logger +// retries uint +// lastResyncWithInjective time.Time +// lastCheckedEthHeight uint64 +//} +// +//func (o *ethOracle) run( +// ctx context.Context, +// injective InjectiveNetwork, +// ethereum EthereumNetwork, +//) error { +// o.log.WithField("last_checked_eth_height", o.lastCheckedEthHeight).Infoln("scanning Ethereum for events") +// +// // Relays events from Ethereum -> Cosmos +// newHeight, err := o.relayEvents(ctx, injective, ethereum) +// if err != nil { +// return err +// } +// +// o.lastCheckedEthHeight = newHeight +// +// if time.Since(o.lastResyncWithInjective) >= 48*time.Hour { +// /** +// Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. +// 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. +// we need to re-scan this block to ensure events are not missed due to indexing delay. +// 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. +// 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. +// **/ +// if err := o.autoResync(ctx, injective); err != nil { +// return err +// } +// } +// +// return nil +//} +// +//func (o *ethOracle) relayEvents( +// ctx context.Context, +// injective InjectiveNetwork, +// ethereum EthereumNetwork, +//) (uint64, error) { +// // Relays events from Ethereum -> Cosmos +// var ( +// latestHeight uint64 +// currentHeight = o.lastCheckedEthHeight +// ) +// +// retryFn := func() error { +// latestHeader, err := ethereum.HeaderByNumber(ctx, nil) +// if err != nil { +// return errors.Wrap(err, "failed to get latest ethereum header") +// } +// +// // add delay to ensure minimum confirmations are received and block is finalised +// latestHeight = latestHeader.Number.Uint64() - ethBlockConfirmationDelay +// if latestHeight < currentHeight { +// println(latestHeight) +// return nil +// } +// +// if latestHeight > currentHeight+defaultBlocksToSearch { +// latestHeight = currentHeight + defaultBlocksToSearch +// } +// +// legacyDeposits, err := ethereum.GetSendToCosmosEvents(currentHeight, latestHeight) +// if err != nil { +// return errors.Wrap(err, "failed to get SendToCosmos events") +// } +// +// deposits, err := ethereum.GetSendToInjectiveEvents(currentHeight, latestHeight) +// if err != nil { +// return errors.Wrap(err, "failed to get SendToInjective events") +// } +// +// withdrawals, err := ethereum.GetTransactionBatchExecutedEvents(currentHeight, latestHeight) +// if err != nil { +// return errors.Wrap(err, "failed to get TransactionBatchExecuted events") +// } +// +// erc20Deployments, err := ethereum.GetPeggyERC20DeployedEvents(currentHeight, latestHeight) +// if err != nil { +// return errors.Wrap(err, "failed to get ERC20Deployed events") +// } +// +// valsetUpdates, err := ethereum.GetValsetUpdatedEvents(currentHeight, latestHeight) +// if err != nil { +// return errors.Wrap(err, "failed to get ValsetUpdated events") +// } +// +// // note that starting block overlaps with our last checked block, because we have to deal with +// // the possibility that the relayer was killed after relaying only one of multiple events in a single +// // block, so we also need this routine so make sure we don't send in the first event in this hypothetical +// // multi event block again. In theory we only send all events for every block and that will pass of fail +// // atomically but lets not take that risk. +// lastClaimEvent, err := injective.LastClaimEvent(ctx) +// if err != nil { +// return errors.New("failed to query last claim event from Injective") +// } +// +// legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) +// o.log.WithFields(log.Fields{ +// "block_start": currentHeight, +// "block_end": latestHeight, +// "events": legacyDeposits, +// }).Debugln("scanned SendToCosmos events") +// +// deposits = filterSendToInjectiveEventsByNonce(deposits, lastClaimEvent.EthereumEventNonce) +// o.log.WithFields(log.Fields{ +// "block_start": currentHeight, +// "block_end": latestHeight, +// "events": deposits, +// }).Debugln("scanned SendToInjective events") +// +// withdrawals = filterTransactionBatchExecutedEventsByNonce(withdrawals, lastClaimEvent.EthereumEventNonce) +// o.log.WithFields(log.Fields{ +// "block_start": currentHeight, +// "block_end": latestHeight, +// "events": withdrawals, +// }).Debugln("scanned TransactionBatchExecuted events") +// +// erc20Deployments = filterERC20DeployedEventsByNonce(erc20Deployments, lastClaimEvent.EthereumEventNonce) +// o.log.WithFields(log.Fields{ +// "block_start": currentHeight, +// "block_end": latestHeight, +// "events": erc20Deployments, +// }).Debugln("scanned FilterERC20Deployed events") +// +// valsetUpdates = filterValsetUpdateEventsByNonce(valsetUpdates, lastClaimEvent.EthereumEventNonce) +// o.log.WithFields(log.Fields{ +// "block_start": currentHeight, +// "block_end": latestHeight, +// "events": valsetUpdates, +// }).Debugln("scanned ValsetUpdated events") +// +// if len(legacyDeposits) == 0 && +// len(deposits) == 0 && +// len(withdrawals) == 0 && +// len(erc20Deployments) == 0 && +// len(valsetUpdates) == 0 { +// return nil +// } +// +// if err := injective.SendEthereumClaims(ctx, +// lastClaimEvent.EthereumEventNonce, +// legacyDeposits, +// deposits, +// withdrawals, +// erc20Deployments, +// valsetUpdates, +// ); err != nil { +// return errors.Wrap(err, "failed to send event claims to Injective") +// } +// +// o.log.WithFields(log.Fields{ +// "last_claim_event_nonce": lastClaimEvent.EthereumEventNonce, +// "legacy_deposits": len(legacyDeposits), +// "deposits": len(deposits), +// "withdrawals": len(withdrawals), +// "erc20Deployments": len(erc20Deployments), +// "valsetUpdates": len(valsetUpdates), +// }).Infoln("sent new claims to Injective") +// +// return nil +// } +// +// if err := retry.Do(retryFn, +// retry.Context(ctx), +// retry.Attempts(o.retries), +// retry.OnRetry(func(n uint, err error) { +// o.log.WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) +// }), +// ); err != nil { +// o.log.WithError(err).Errorln("got error, loop exits") +// return 0, err +// } +// +// return latestHeight, nil +//} +// +//func (o *ethOracle) autoResync(ctx context.Context, injective InjectiveNetwork) error { +// var latestHeight uint64 +// retryFn := func() error { +// lastClaimEvent, err := injective.LastClaimEvent(ctx) +// if err != nil { +// return err +// } +// +// latestHeight = lastClaimEvent.EthereumEventHeight +// return nil +// } +// +// if err := retry.Do(retryFn, +// retry.Context(ctx), +// retry.Attempts(o.retries), +// retry.OnRetry(func(n uint, err error) { +// o.log.WithError(err).Warningf("failed to get last confirmed eth height, will retry (%d)", n) +// }), +// ); err != nil { +// o.log.WithError(err).Errorln("got error, loop exits") +// return err +// } +// +// o.lastCheckedEthHeight = latestHeight +// o.lastResyncWithInjective = time.Now() +// +// o.log.WithFields(log.Fields{ +// "last_resync": o.lastResyncWithInjective.String(), +// "last_confirmed_eth_height": o.lastCheckedEthHeight, +// }).Infoln("auto resync") +// +// return nil +//} + func filterSendToCosmosEventsByNonce( events []*wrappers.PeggySendToCosmosEvent, nonce uint64, diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 41e27784..3f6cc641 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/avast/retry-go" "math/big" "time" @@ -202,3 +203,58 @@ func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context) error { return pg.Wait() } + +// EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain +// and ferried over to Cosmos where they will be used to issue tokens or process batches. +func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { + lastConfirmedEthHeight, err := s.getLastConfirmedEthHeightOnInjective(ctx) + if err != nil { + return err + } + + s.logger.Infoln("scanning Ethereum events from block", lastConfirmedEthHeight) + + oracle := ethOracleLoop{ + PeggyOrchestrator: s, + loopDuration: defaultLoopDur, + lastCheckedEthHeight: lastConfirmedEthHeight, + } + + return loops.RunLoop(ctx, defaultLoopDur, oracle.LoopFn(ctx, s.injective, s.ethereum)) +} + +func (s *PeggyOrchestrator) getLastConfirmedEthHeightOnInjective(ctx context.Context) (uint64, error) { + var lastConfirmedEthHeight uint64 + getLastConfirmedEthHeightFn := func() error { + lastClaimEvent, err := s.injective.LastClaimEvent(ctx) + if err == nil && lastClaimEvent != nil && lastClaimEvent.EthereumEventHeight != 0 { + lastConfirmedEthHeight = lastClaimEvent.EthereumEventHeight + return nil + + } + + s.logger.WithError(err).Warningln("failed to get last claim from Injective. Querying peggy module params...") + + peggyParams, err := s.injective.PeggyParams(ctx) + if err != nil { + s.logger.WithError(err).Fatalln("failed to query peggy module params, is injectived running?") + return err + } + + lastConfirmedEthHeight = peggyParams.BridgeContractStartHeight + return nil + } + + if err := retry.Do(getLastConfirmedEthHeightFn, + retry.Context(ctx), + retry.Attempts(s.maxAttempts), + retry.OnRetry(func(n uint, err error) { + s.logger.WithError(err).Warningf("failed to get last confirmed Ethereum height on Injective, will retry (%d)", n) + }), + ); err != nil { + s.logger.WithError(err).Errorln("got error, loop exits") + return 0, err + } + + return lastConfirmedEthHeight, nil +} From 6b76083021a0906cae9d55f4b45c0dbcf2ddba18 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 21 Dec 2023 15:30:46 +0100 Subject: [PATCH 07/21] clean up signer.go --- orchestrator/signer.go | 306 ++++++++++++++++++++++++++++++----------- 1 file changed, 229 insertions(+), 77 deletions(-) diff --git a/orchestrator/signer.go b/orchestrator/signer.go index a2d376b2..82ea551f 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "time" "github.com/avast/retry-go" "github.com/ethereum/go-ethereum/common" @@ -21,28 +22,24 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context) error { return err } - signer := ðSigner{ - log: log.WithField("loop", "EthSigner"), - peggyID: peggyID, - ethFrom: s.ethereum.FromAddress(), - retries: s.maxAttempts, + loop := ethSignerLoop{ + PeggyOrchestrator: s, + loopDuration: defaultLoopDur, + peggyID: peggyID, + ethFrom: s.ethereum.FromAddress(), } - return loops.RunLoop( - ctx, - defaultLoopDur, - func() error { return signer.run(ctx, s.injective) }, - ) + return loop.Run(ctx, s.injective) } func (s *PeggyOrchestrator) getPeggyID(ctx context.Context) (common.Hash, error) { var peggyID common.Hash - retryFn := func() (err error) { + getPeggyIDFn := func() (err error) { peggyID, err = s.ethereum.GetPeggyID(ctx) return err } - if err := retry.Do(retryFn, + if err := retry.Do(getPeggyIDFn, retry.Context(ctx), retry.Attempts(s.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -58,46 +55,74 @@ func (s *PeggyOrchestrator) getPeggyID(ctx context.Context) (common.Hash, error) return peggyID, nil } -type ethSigner struct { - log log.Logger - peggyID common.Hash - ethFrom common.Address - retries uint +type ethSignerLoop struct { + *PeggyOrchestrator + loopDuration time.Duration + peggyID common.Hash + ethFrom common.Address +} + +func (l *ethSignerLoop) Logger() log.Logger { + return l.logger.WithField("loop", "EthSigner") } -func (s *ethSigner) run(ctx context.Context, injective InjectiveNetwork) error { - s.log.Infoln("scanning Injective for unconfirmed batches and valset updates") +func (l *ethSignerLoop) Run(ctx context.Context, injective InjectiveNetwork) error { + return loops.RunLoop(ctx, l.loopDuration, l.loopFn(ctx, injective)) +} - if err := s.signNewValsetUpdates(ctx, injective); err != nil { - return err +func (l *ethSignerLoop) loopFn(ctx context.Context, injective InjectiveNetwork) func() error { + return func() error { + if err := l.signNewValsetUpdates(ctx, injective); err != nil { + return err + } + + if err := l.signNewBatch(ctx, injective); err != nil { + return err + } + + return nil } +} - if err := s.signNewBatches(ctx, injective); err != nil { +func (l *ethSignerLoop) signNewValsetUpdates(ctx context.Context, injective InjectiveNetwork) error { + oldestUnsignedValsets, err := l.getUnsignedValsets(ctx, injective) + if err != nil { return err } + if len(oldestUnsignedValsets) == 0 { + l.Logger().Debugln("no valset updates to confirm") + return nil + } + + for _, vs := range oldestUnsignedValsets { + if err := l.signValset(ctx, injective, vs); err != nil { + return err + } + } + return nil } -func (s *ethSigner) signNewBatches(ctx context.Context, injective InjectiveNetwork) error { - oldestUnsignedTransactionBatch, err := s.getUnsignedBatch(ctx, injective) +func (l *ethSignerLoop) signNewBatch(ctx context.Context, injective InjectiveNetwork) error { + oldestUnsignedTransactionBatch, err := l.getUnsignedBatch(ctx, injective) if err != nil { return err } if oldestUnsignedTransactionBatch == nil { - s.log.Debugln("no batch to confirm") + l.Logger().Debugln("no batch to confirm") return nil } - if err := s.signBatch(ctx, injective, oldestUnsignedTransactionBatch); err != nil { + if err := l.signBatch(ctx, injective, oldestUnsignedTransactionBatch); err != nil { return err } return nil } -func (s *ethSigner) getUnsignedBatch(ctx context.Context, injective InjectiveNetwork) (*types.OutgoingTxBatch, error) { +func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context, injective InjectiveNetwork) (*types.OutgoingTxBatch, error) { var oldestUnsignedTransactionBatch *types.OutgoingTxBatch retryFn := func() (err error) { // sign the last unsigned batch, TODO check if we already have signed this @@ -111,64 +136,37 @@ func (s *ethSigner) getUnsignedBatch(ctx context.Context, injective InjectiveNet if err := retry.Do(retryFn, retry.Context(ctx), - retry.Attempts(s.retries), + retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - s.log.WithError(err).Warningf("failed to get unconfirmed batch, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get unconfirmed batch, will retry (%d)", n) }), ); err != nil { - s.log.WithError(err).Errorln("got error, loop exits") + l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } return oldestUnsignedTransactionBatch, nil } -func (s *ethSigner) signBatch( - ctx context.Context, - injective InjectiveNetwork, - batch *types.OutgoingTxBatch, -) error { +func (l *ethSignerLoop) signBatch(ctx context.Context, injective InjectiveNetwork, batch *types.OutgoingTxBatch) error { if err := retry.Do( - func() error { return injective.SendBatchConfirm(ctx, s.peggyID, batch, s.ethFrom) }, + func() error { return injective.SendBatchConfirm(ctx, l.peggyID, batch, l.ethFrom) }, retry.Context(ctx), - retry.Attempts(s.retries), + retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - s.log.WithError(err).Warningf("failed to confirm batch on Injective, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to confirm batch on Injective, will retry (%d)", n) }), ); err != nil { - s.log.WithError(err).Errorln("got error, loop exits") - return err - } - - s.log.WithField("batch_nonce", batch.BatchNonce).Infoln("confirmed batch on Injective") - - return nil -} - -func (s *ethSigner) signNewValsetUpdates( - ctx context.Context, - injective InjectiveNetwork, -) error { - oldestUnsignedValsets, err := s.getUnsignedValsets(ctx, injective) - if err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") return err } - if len(oldestUnsignedValsets) == 0 { - s.log.Debugln("no valset updates to confirm") - return nil - } - - for _, vs := range oldestUnsignedValsets { - if err := s.signValset(ctx, injective, vs); err != nil { - return err - } - } + l.Logger().WithField("batch_nonce", batch.BatchNonce).Infoln("confirmed batch on Injective") return nil } -func (s *ethSigner) getUnsignedValsets(ctx context.Context, injective InjectiveNetwork) ([]*types.Valset, error) { +func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context, injective InjectiveNetwork) ([]*types.Valset, error) { var oldestUnsignedValsets []*types.Valset retryFn := func() (err error) { oldestUnsignedValsets, err = injective.OldestUnsignedValsets(ctx) @@ -181,36 +179,190 @@ func (s *ethSigner) getUnsignedValsets(ctx context.Context, injective InjectiveN if err := retry.Do(retryFn, retry.Context(ctx), - retry.Attempts(s.retries), + retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - s.log.WithError(err).Warningf("failed to get unconfirmed valset updates, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get unconfirmed valset updates, will retry (%d)", n) }), ); err != nil { - s.log.WithError(err).Errorln("got error, loop exits") + l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } return oldestUnsignedValsets, nil } -func (s *ethSigner) signValset( - ctx context.Context, - injective InjectiveNetwork, - vs *types.Valset, -) error { +func (l *ethSignerLoop) signValset(ctx context.Context, injective InjectiveNetwork, vs *types.Valset) error { if err := retry.Do( - func() error { return injective.SendValsetConfirm(ctx, s.peggyID, vs, s.ethFrom) }, + func() error { return injective.SendValsetConfirm(ctx, l.peggyID, vs, l.ethFrom) }, retry.Context(ctx), - retry.Attempts(s.retries), + retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - s.log.WithError(err).Warningf("failed to confirm valset update on Injective, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to confirm valset update on Injective, will retry (%d)", n) }), ); err != nil { - s.log.WithError(err).Errorln("got error, loop exits") + l.Logger().WithError(err).Errorln("got error, loop exits") return err } - s.log.WithField("valset_nonce", vs.Nonce).Infoln("confirmed valset update on Injective") + l.Logger().WithField("valset_nonce", vs.Nonce).Infoln("confirmed valset update on Injective") return nil } + +// +//type ethSigner struct { +// log log.Logger +// peggyID common.Hash +// ethFrom common.Address +// retries uint +//} +// +//func (s *ethSigner) run(ctx context.Context, injective InjectiveNetwork) error { +// s.log.Infoln("scanning Injective for unconfirmed batches and valset updates") +// +// if err := s.signNewValsetUpdates(ctx, injective); err != nil { +// return err +// } +// +// if err := s.signNewBatches(ctx, injective); err != nil { +// return err +// } +// +// return nil +//} +// +//func (s *ethSigner) signNewBatches(ctx context.Context, injective InjectiveNetwork) error { +// oldestUnsignedTransactionBatch, err := s.getUnsignedBatch(ctx, injective) +// if err != nil { +// return err +// } +// +// if oldestUnsignedTransactionBatch == nil { +// s.log.Debugln("no batch to confirm") +// return nil +// } +// +// if err := s.signBatch(ctx, injective, oldestUnsignedTransactionBatch); err != nil { +// return err +// } +// +// return nil +//} +// +//func (s *ethSigner) getUnsignedBatch(ctx context.Context, injective InjectiveNetwork) (*types.OutgoingTxBatch, error) { +// var oldestUnsignedTransactionBatch *types.OutgoingTxBatch +// retryFn := func() (err error) { +// // sign the last unsigned batch, TODO check if we already have signed this +// oldestUnsignedTransactionBatch, err = injective.OldestUnsignedTransactionBatch(ctx) +// if err == cosmos.ErrNotFound || oldestUnsignedTransactionBatch == nil { +// return nil +// } +// +// return err +// } +// +// if err := retry.Do(retryFn, +// retry.Context(ctx), +// retry.Attempts(s.retries), +// retry.OnRetry(func(n uint, err error) { +// s.log.WithError(err).Warningf("failed to get unconfirmed batch, will retry (%d)", n) +// }), +// ); err != nil { +// s.log.WithError(err).Errorln("got error, loop exits") +// return nil, err +// } +// +// return oldestUnsignedTransactionBatch, nil +//} +// +//func (s *ethSigner) signBatch( +// ctx context.Context, +// injective InjectiveNetwork, +// batch *types.OutgoingTxBatch, +//) error { +// if err := retry.Do( +// func() error { return injective.SendBatchConfirm(ctx, s.peggyID, batch, s.ethFrom) }, +// retry.Context(ctx), +// retry.Attempts(s.retries), +// retry.OnRetry(func(n uint, err error) { +// s.log.WithError(err).Warningf("failed to confirm batch on Injective, will retry (%d)", n) +// }), +// ); err != nil { +// s.log.WithError(err).Errorln("got error, loop exits") +// return err +// } +// +// s.log.WithField("batch_nonce", batch.BatchNonce).Infoln("confirmed batch on Injective") +// +// return nil +//} +// +//func (s *ethSigner) signNewValsetUpdates( +// ctx context.Context, +// injective InjectiveNetwork, +//) error { +// oldestUnsignedValsets, err := s.getUnsignedValsets(ctx, injective) +// if err != nil { +// return err +// } +// +// if len(oldestUnsignedValsets) == 0 { +// s.log.Debugln("no valset updates to confirm") +// return nil +// } +// +// for _, vs := range oldestUnsignedValsets { +// if err := s.signValset(ctx, injective, vs); err != nil { +// return err +// } +// } +// +// return nil +//} +// +//func (s *ethSigner) getUnsignedValsets(ctx context.Context, injective InjectiveNetwork) ([]*types.Valset, error) { +// var oldestUnsignedValsets []*types.Valset +// retryFn := func() (err error) { +// oldestUnsignedValsets, err = injective.OldestUnsignedValsets(ctx) +// if err == cosmos.ErrNotFound || oldestUnsignedValsets == nil { +// return nil +// } +// +// return err +// } +// +// if err := retry.Do(retryFn, +// retry.Context(ctx), +// retry.Attempts(s.retries), +// retry.OnRetry(func(n uint, err error) { +// s.log.WithError(err).Warningf("failed to get unconfirmed valset updates, will retry (%d)", n) +// }), +// ); err != nil { +// s.log.WithError(err).Errorln("got error, loop exits") +// return nil, err +// } +// +// return oldestUnsignedValsets, nil +//} +// +//func (s *ethSigner) signValset( +// ctx context.Context, +// injective InjectiveNetwork, +// vs *types.Valset, +//) error { +// if err := retry.Do( +// func() error { return injective.SendValsetConfirm(ctx, s.peggyID, vs, s.ethFrom) }, +// retry.Context(ctx), +// retry.Attempts(s.retries), +// retry.OnRetry(func(n uint, err error) { +// s.log.WithError(err).Warningf("failed to confirm valset update on Injective, will retry (%d)", n) +// }), +// ); err != nil { +// s.log.WithError(err).Errorln("got error, loop exits") +// return err +// } +// +// s.log.WithField("valset_nonce", vs.Nonce).Infoln("confirmed valset update on Injective") +// +// return nil +//} From 0d626f2656c063e0f056c7b091daacdbefff4443 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 28 Dec 2023 14:22:08 +0100 Subject: [PATCH 08/21] remove redundant parameters --- cmd/peggo/orchestrator.go | 49 ++++++++-------------------- orchestrator/cosmos/broadcast.go | 2 -- orchestrator/cosmos/custom_rpc.go | 6 ++-- orchestrator/cosmos/load_balanced.go | 10 ++---- 4 files changed, 18 insertions(+), 49 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index e9903d5d..060f7069 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -71,64 +71,41 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - log.WithFields(log.Fields{ - "inj_addr": valAddress.String(), - "eth_addr": ethKeyFromAddress.String(), - }).Infoln("starting peggo service") + log.WithFields(log.Fields{"inj_addr": valAddress.String(), "eth_addr": ethKeyFromAddress.String()}).Infoln("starting peggo service") + + var ( + injectiveNet orchestrator.InjectiveNetwork + customEndpointRPCs = *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != "" + ) - var injective orchestrator.InjectiveNetwork - if customEndpointRPCs := *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != ""; customEndpointRPCs { - injective, err = cosmos.NewCustomRPCNetwork( + if customEndpointRPCs { + injectiveNet, err = cosmos.NewCustomRPCNetwork( *cfg.cosmosChainID, valAddress.String(), *cfg.cosmosGRPC, *cfg.cosmosGasPrices, *cfg.tendermintRPC, cosmosKeyring, - signerFn, personalSignFn, ) } else { // load balanced connection - injective, err = cosmos.NewLoadBalancedNetwork( + injectiveNet, err = cosmos.NewLoadBalancedNetwork( *cfg.cosmosChainID, valAddress.String(), - *cfg.cosmosGRPC, *cfg.cosmosGasPrices, - *cfg.tendermintRPC, cosmosKeyring, - signerFn, personalSignFn, ) } orShutdown(err) - // Connect to Injective network - //injNetwork, err := cosmos.NewLoadBalancedNetwork( - // *cfg.cosmosChainID, - // valAddress.String(), - // *cfg.cosmosGRPC, - // *cfg.cosmosGasPrices, - // *cfg.tendermintRPC, - // cosmosKeyring, - // signerFn, - // personalSignFn, - //) - //orShutdown(err) - - // todo - // See if the provided ETH address belongs to a validator and determine in which mode peggo should run - //isValidator, err := isValidatorAddress(injNetwork.PeggyQueryClient, ethKeyFromAddress) - //if err != nil { - // log.WithError(err).Fatalln("failed to query current validator set on Injective") - //} - ctx, cancelFn := context.WithCancel(context.Background()) closer.Bind(cancelFn) // Construct erc20 token mapping - peggyParams, err := injective.PeggyParams(ctx) + peggyParams, err := injectiveNet.PeggyParams(ctx) if err != nil { log.WithError(err).Fatalln("failed to query peggy params, is injectived running?") } @@ -140,7 +117,7 @@ func orchestratorCmd(cmd *cli.Cmd) { erc20ContractMapping[injTokenAddr] = chaintypes.InjectiveCoin // Connect to ethereum network - ethNetwork, err := ethereum.NewNetwork( + ethereumNet, err := ethereum.NewNetwork( *cfg.ethNodeRPC, peggyContractAddr, ethKeyFromAddress, @@ -156,8 +133,8 @@ func orchestratorCmd(cmd *cli.Cmd) { // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( - injective, - ethNetwork, + injectiveNet, + ethereumNet, coingeckoFeed, erc20ContractMapping, *cfg.minBatchFeeUSD, diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index 9d4ae931..533c670b 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -79,13 +79,11 @@ type PeggyBroadcastClient interface { func NewPeggyBroadcastClient( queryClient types.QueryClient, broadcastClient chainclient.ChainClient, - ethSignerFn keystore.SignerFn, ethPersonalSignFn keystore.PersonalSignFn, ) PeggyBroadcastClient { return &peggyBroadcastClient{ daemonQueryClient: queryClient, broadcastClient: broadcastClient, - ethSignerFn: ethSignerFn, ethPersonalSignFn: ethPersonalSignFn, svcTags: metrics.Tags{ diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 0429dd3e..5bf35412 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -11,7 +11,6 @@ import ( "github.com/InjectiveLabs/sdk-go/client/common" rpchttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/ethereum/go-ethereum/accounts/abi/bind" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" @@ -46,7 +45,6 @@ func NewCustomRPCNetwork( injectiveGasPrices, tendermintRPC string, keyring keyring.Keyring, - signerFn bind.SignerFn, personalSignerFn keystore.PersonalSignFn, ) (*CustomRPCNetwork, error) { clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) @@ -80,12 +78,12 @@ func NewCustomRPCNetwork( n := &CustomRPCNetwork{ TendermintClient: tmclient.NewRPCClient(tendermintRPC), PeggyQueryClient: NewPeggyQueryClient(peggyQuerier), - PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, signerFn, personalSignerFn), + PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, personalSignerFn), } log.WithFields(log.Fields{ "chain_id": chainID, - "connection": "custom_rpc", + "connection": "custom", "injective": injectiveGRPC, "tendermint": tendermintRPC, }).Infoln("connected to Injective network") diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index 3f17e3ea..c3c79680 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -6,7 +6,6 @@ import ( "time" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/ethereum/go-ethereum/accounts/abi/bind" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" @@ -36,11 +35,8 @@ type LoadBalancedNetwork struct { func NewLoadBalancedNetwork( chainID, validatorAddress, - injectiveGRPC, - injectiveGasPrices, - tendermintRPC string, + injectiveGasPrices string, keyring keyring.Keyring, - signerFn bind.SignerFn, personalSignerFn keystore.PersonalSignFn, ) (*LoadBalancedNetwork, error) { clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) @@ -68,7 +64,7 @@ func NewLoadBalancedNetwork( daemonClient, err := chainclient.NewChainClient(clientCtx, netCfg, common.OptionGasPrices(injectiveGasPrices)) if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", injectiveGRPC) + return nil, errors.Wrapf(err, "failed to connect to Injective network: %s", networkName) } time.Sleep(1 * time.Second) @@ -82,7 +78,7 @@ func NewLoadBalancedNetwork( n := &LoadBalancedNetwork{ PeggyQueryClient: NewPeggyQueryClient(peggyQuerier), - PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, signerFn, personalSignerFn), + PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, personalSignerFn), ExplorerClient: explorer, } From 1a1337691908e587d8171f06eebab13e7f038837 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 29 Dec 2023 12:26:18 +0100 Subject: [PATCH 09/21] remove redundant network methods --- Makefile | 2 +- cmd/peggo/orchestrator.go | 1 - orchestrator/batch_request.go | 2 +- orchestrator/cosmos/custom_rpc.go | 108 +++------------------------ orchestrator/cosmos/load_balanced.go | 77 +------------------ orchestrator/injective.go | 6 +- orchestrator/mocks_test.go | 12 ++- orchestrator/signer.go | 4 +- 8 files changed, 28 insertions(+), 184 deletions(-) diff --git a/Makefile b/Makefile index d1be7dca..40b5cd70 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ push: install: export GOPROXY=direct install: export VERSION_FLAGS="-X $(VERSION_PKG).GitCommit=$(GIT_COMMIT) -X $(VERSION_PKG).BuildDate=$(BUILD_DATE)" install: - $(DOCKER) && go install -tags muslc -ldflags $(VERSION_FLAGS) ./cmd/... || go install -ldflags $(VERSION_FLAGS) ./cmd/... + docker=$(DOCKER) && go install -tags muslc -ldflags $(VERSION_FLAGS) ./cmd/... || go install -ldflags $(VERSION_FLAGS) ./cmd/... .PHONY: install image push test gen diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 060f7069..bc6d729e 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -41,7 +41,6 @@ func orchestratorCmd(cmd *cli.Cmd) { "go_arch": version.GoArch, }).Infoln("peggo - peggy binary for Ethereum bridge") - // todo: remove if *cfg.cosmosUseLedger || *cfg.ethUseLedger { log.Fatalln("cannot use Ledger for peggo, since signatures must be realtime") } diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 5512b9d0..72f628d4 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -63,7 +63,7 @@ func (l *batchRequestLoop) requestBatches(ctx context.Context) error { func (l *batchRequestLoop) getUnbatchedTokenFees(ctx context.Context) ([]*types.BatchFees, error) { var unbatchedFees []*types.BatchFees getUnbatchedTokenFeesFn := func() (err error) { - unbatchedFees, err = l.inj.UnbatchedTokenFees(ctx) + unbatchedFees, err = l.inj.UnbatchedTokensWithFees(ctx) return err } diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 5bf35412..079a4d8e 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -2,19 +2,18 @@ package cosmos import ( "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tmclient" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + "time" + "github.com/InjectiveLabs/sdk-go/client/common" rpchttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/crypto/keyring" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" - "time" + + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tmclient" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" ) type CustomRPCNetwork struct { @@ -73,7 +72,7 @@ func NewCustomRPCNetwork( grpcConn := daemonClient.QueryClient() waitForService(daemonWaitCtx, grpcConn) - peggyQuerier := types.NewQueryClient(grpcConn) + peggyQuerier := peggytypes.NewQueryClient(grpcConn) n := &CustomRPCNetwork{ TendermintClient: tmclient.NewRPCClient(tendermintRPC), @@ -83,7 +82,7 @@ func NewCustomRPCNetwork( log.WithFields(log.Fields{ "chain_id": chainID, - "connection": "custom", + "conn": "custom", "injective": injectiveGRPC, "tendermint": tendermintRPC, }).Infoln("connected to Injective network") @@ -100,103 +99,14 @@ func (n *CustomRPCNetwork) GetBlockCreationTime(ctx context.Context, height int6 return block.Block.Time, nil } -//func (n *LoadBalancedNetwork) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { -// return n.PeggyQueryClient.PeggyParams(ctx) -//} - func (n *CustomRPCNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { return n.LastClaimEventByAddr(ctx, n.AccFromAddress()) } -func (n *CustomRPCNetwork) SendEthereumClaims( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -) error { - return n.PeggyBroadcastClient.SendEthereumClaims(ctx, - lastClaimEvent, - oldDeposits, - deposits, - withdraws, - erc20Deployed, - valsetUpdates, - ) -} - -func (n *CustomRPCNetwork) UnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { - return n.PeggyQueryClient.UnbatchedTokensWithFees(ctx) -} - -func (n *CustomRPCNetwork) SendRequestBatch(ctx context.Context, denom string) error { - return n.PeggyBroadcastClient.SendRequestBatch(ctx, denom) -} - func (n *CustomRPCNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.AccFromAddress()) } -func (n *CustomRPCNetwork) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.PeggyQueryClient.LatestValsets(ctx) -} - -func (n *CustomRPCNetwork) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return n.PeggyQueryClient.AllValsetConfirms(ctx, nonce) -} - -func (n *CustomRPCNetwork) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { - return n.PeggyQueryClient.ValsetAt(ctx, nonce) -} - -func (n *CustomRPCNetwork) SendValsetConfirm( - ctx context.Context, - peggyID gethcommon.Hash, - valset *peggytypes.Valset, - ethFrom gethcommon.Address, -) error { - return n.PeggyBroadcastClient.SendValsetConfirm(ctx, ethFrom, peggyID, valset) -} - func (n *CustomRPCNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.AccFromAddress()) } - -func (n *CustomRPCNetwork) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return n.PeggyQueryClient.LatestTransactionBatches(ctx) -} - -func (n *CustomRPCNetwork) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return n.PeggyQueryClient.TransactionBatchSignatures(ctx, nonce, tokenContract) -} - -func (n *CustomRPCNetwork) SendBatchConfirm( - ctx context.Context, - peggyID gethcommon.Hash, - batch *peggytypes.OutgoingTxBatch, - ethFrom gethcommon.Address, -) error { - return n.PeggyBroadcastClient.SendBatchConfirm(ctx, ethFrom, peggyID, batch) -} - -// waitForService awaits an active ClientConn to a GRPC service. -//func waitForService(ctx context.Context, clientconn *grpc.ClientConn) { -// for { -// select { -// case <-ctx.Done(): -// log.Fatalln("GRPC service wait timed out") -// default: -// state := clientconn.GetState() -// -// if state != connectivity.Ready { -// log.WithField("state", state.String()).Warningln("state of GRPC connection not ready") -// time.Sleep(5 * time.Second) -// continue -// } -// -// return -// } -// } -//} diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index c3c79680..620da606 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -6,14 +6,12 @@ import ( "time" "github.com/cosmos/cosmos-sdk/crypto/keyring" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" "github.com/InjectiveLabs/sdk-go/chain/peggy/types" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" chainclient "github.com/InjectiveLabs/sdk-go/client/chain" @@ -82,7 +80,7 @@ func NewLoadBalancedNetwork( ExplorerClient: explorer, } - log.WithFields(log.Fields{"chain_id": chainID, "connection": "load_balanced"}).Infoln("connected to Injective network") + log.WithFields(log.Fields{"chain_id": chainID, "conn": "load_balanced"}).Infoln("connected to Injective network") return n, nil } @@ -101,95 +99,26 @@ func (n *LoadBalancedNetwork) GetBlockCreationTime(ctx context.Context, height i return blockTime, nil } -func (n *LoadBalancedNetwork) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { - return n.PeggyQueryClient.PeggyParams(ctx) -} - func (n *LoadBalancedNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { return n.LastClaimEventByAddr(ctx, n.AccFromAddress()) } -func (n *LoadBalancedNetwork) SendEthereumClaims( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -) error { - return n.PeggyBroadcastClient.SendEthereumClaims(ctx, - lastClaimEvent, - oldDeposits, - deposits, - withdraws, - erc20Deployed, - valsetUpdates, - ) -} - -func (n *LoadBalancedNetwork) UnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { - return n.PeggyQueryClient.UnbatchedTokensWithFees(ctx) -} - -func (n *LoadBalancedNetwork) SendRequestBatch(ctx context.Context, denom string) error { - return n.PeggyBroadcastClient.SendRequestBatch(ctx, denom) -} - func (n *LoadBalancedNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.AccFromAddress()) } -func (n *LoadBalancedNetwork) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.PeggyQueryClient.LatestValsets(ctx) -} - -func (n *LoadBalancedNetwork) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return n.PeggyQueryClient.AllValsetConfirms(ctx, nonce) -} - -func (n *LoadBalancedNetwork) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { - return n.PeggyQueryClient.ValsetAt(ctx, nonce) -} - -func (n *LoadBalancedNetwork) SendValsetConfirm( - ctx context.Context, - peggyID gethcommon.Hash, - valset *peggytypes.Valset, - ethFrom gethcommon.Address, -) error { - return n.PeggyBroadcastClient.SendValsetConfirm(ctx, ethFrom, peggyID, valset) -} - func (n *LoadBalancedNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.AccFromAddress()) } -func (n *LoadBalancedNetwork) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return n.PeggyQueryClient.LatestTransactionBatches(ctx) -} - -func (n *LoadBalancedNetwork) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return n.PeggyQueryClient.TransactionBatchSignatures(ctx, nonce, tokenContract) -} - -func (n *LoadBalancedNetwork) SendBatchConfirm( - ctx context.Context, - peggyID gethcommon.Hash, - batch *peggytypes.OutgoingTxBatch, - ethFrom gethcommon.Address, -) error { - return n.PeggyBroadcastClient.SendBatchConfirm(ctx, ethFrom, peggyID, batch) -} - // waitForService awaits an active ClientConn to a GRPC service. -func waitForService(ctx context.Context, clientconn *grpc.ClientConn) { +func waitForService(ctx context.Context, clientConn *grpc.ClientConn) { for { select { case <-ctx.Done(): log.Fatalln("GRPC service wait timed out") default: - state := clientconn.GetState() + state := clientConn.GetState() if state != connectivity.Ready { log.WithField("state", state.String()).Warningln("state of GRPC connection not ready") diff --git a/orchestrator/injective.go b/orchestrator/injective.go index 66209d27..6e6fb25d 100644 --- a/orchestrator/injective.go +++ b/orchestrator/injective.go @@ -26,15 +26,15 @@ type InjectiveNetwork interface { valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, ) error - UnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) + UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) SendRequestBatch(ctx context.Context, denom string) error OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) - SendBatchConfirm(ctx context.Context, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch, ethFrom gethcommon.Address) error + SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) - SendValsetConfirm(ctx context.Context, peggyID gethcommon.Hash, valset *peggytypes.Valset, ethFrom gethcommon.Address) error + SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) diff --git a/orchestrator/mocks_test.go b/orchestrator/mocks_test.go index 636bf5b6..1011dcf4 100644 --- a/orchestrator/mocks_test.go +++ b/orchestrator/mocks_test.go @@ -53,9 +53,15 @@ type mockInjective struct { latestTransactionBatchesFn func(context.Context) ([]*peggytypes.OutgoingTxBatch, error) transactionBatchSignaturesFn func(context.Context, uint64, eth.Address) ([]*peggytypes.MsgConfirmBatch, error) + + hasRegisteredEthAddress func(ctx context.Context, address eth.Address) (bool, error) +} + +func (i *mockInjective) HasRegisteredEthAddress(ctx context.Context, addr eth.Address) (bool, error) { + return i.hasRegisteredEthAddress(ctx, addr) } -func (i *mockInjective) UnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { +func (i *mockInjective) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { i.unbatchedTokenFeesCallCount++ return i.unbatchedTokenFeesFn(ctx) } @@ -98,7 +104,7 @@ func (i *mockInjective) OldestUnsignedValsets(ctx context.Context) ([]*peggytype return i.oldestUnsignedValsetsFn(ctx) } -func (i *mockInjective) SendValsetConfirm(ctx context.Context, peggyID eth.Hash, valset *peggytypes.Valset, ethFrom eth.Address) error { +func (i *mockInjective) SendValsetConfirm(ctx context.Context, ethFrom eth.Address, peggyID eth.Hash, valset *peggytypes.Valset) error { return i.sendValsetConfirmFn(ctx, peggyID, valset, ethFrom) } @@ -114,7 +120,7 @@ func (i *mockInjective) AllValsetConfirms(ctx context.Context, nonce uint64) ([] return i.allValsetConfirmsFn(ctx, nonce) } -func (i *mockInjective) SendBatchConfirm(ctx context.Context, peggyID eth.Hash, batch *peggytypes.OutgoingTxBatch, ethFrom eth.Address) error { +func (i *mockInjective) SendBatchConfirm(ctx context.Context, ethFrom eth.Address, peggyID eth.Hash, batch *peggytypes.OutgoingTxBatch) error { return i.sendBatchConfirmFn(ctx, peggyID, batch, ethFrom) } diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 6aa3b8d9..98eb61d9 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -153,7 +153,7 @@ func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTx func (l *ethSignerLoop) signBatch(ctx context.Context, batch *types.OutgoingTxBatch) error { if err := retry.Do( - func() error { return l.inj.SendBatchConfirm(ctx, l.peggyID, batch, l.ethFrom) }, + func() error { return l.inj.SendBatchConfirm(ctx, l.ethFrom, l.peggyID, batch) }, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -196,7 +196,7 @@ func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*types.Valset func (l *ethSignerLoop) signValset(ctx context.Context, vs *types.Valset) error { if err := retry.Do(func() error { - return l.inj.SendValsetConfirm(ctx, l.peggyID, vs, l.ethFrom) + return l.inj.SendValsetConfirm(ctx, l.ethFrom, l.peggyID, vs) }, retry.Context(ctx), retry.Attempts(l.maxAttempts), From cbd7894aa51e09b66575b7a61ba8f429e6c5f3c7 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 4 Jan 2024 12:58:48 +0100 Subject: [PATCH 10/21] cleanup --- cmd/peggo/orchestrator.go | 3 +- cmd/peggo/tx.go | 1 - orchestrator/oracle.go | 6 +- orchestrator/relayer.go | 175 +++++++++++++++----------------------- orchestrator/signer.go | 51 +++++------ 5 files changed, 99 insertions(+), 137 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index bc6d729e..3eac2716 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -2,11 +2,12 @@ package main import ( "context" + "os" + gethcommon "github.com/ethereum/go-ethereum/common" cli "github.com/jawher/mow.cli" "github.com/xlab/closer" log "github.com/xlab/suplog" - "os" "github.com/InjectiveLabs/peggo/orchestrator" "github.com/InjectiveLabs/peggo/orchestrator/coingecko" diff --git a/cmd/peggo/tx.go b/cmd/peggo/tx.go index 136adb57..c69c41ff 100644 --- a/cmd/peggo/tx.go +++ b/cmd/peggo/tx.go @@ -126,7 +126,6 @@ func registerEthKeyCmd(cmd *cli.Cmd) { return } - // here var ( peggyBroadcastClient cosmos.PeggyBroadcastClient customCosmosRPC = *cosmosGRPC != "" && *tendermintRPC != "" diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index b6495afb..aa7d35c4 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -30,7 +30,7 @@ func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { return err } - s.logger.Debugln("last observed ethereum block", lastConfirmedEthHeight) + s.logger.Debugln("last observed Ethereum block", lastConfirmedEthHeight) loop := ethOracleLoop{ PeggyOrchestrator: s, @@ -189,8 +189,8 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { } l.logger.WithFields(log.Fields{ - "event_nonce": lastClaimEvent.EthereumEventNonce, - "event_block": lastClaimEvent.EthereumEventHeight, + "event_nonce": lastClaimEvent.EthereumEventNonce, + "event_height": lastClaimEvent.EthereumEventHeight, }).Debugln("last Ethereum claim event on Injective") legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index b167081f..2a0e9867 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -84,207 +84,168 @@ func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { return nil } -func (l *relayerLoop) loopFn(ctx context.Context) func() error { - return func() error { - var pg loops.ParanoidGroup - - if l.valsetRelayEnabled { - pg.Go(func() error { - return retry.Do(func() error { return l.relayValset(ctx) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay valsets, will retry (%d)", n) - }), - ) - }) - } - - if l.batchRelayEnabled { - pg.Go(func() error { - return retry.Do(func() error { return l.relayBatch(ctx) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay batches, will retry (%d)", n) - }), - ) - }) - } - - if pg.Initialized() { - if err := pg.Wait(); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - } - - return nil - } -} - -func (l *relayerLoop) relayBatch(ctx context.Context) error { +func (l *relayerLoop) relayValset(ctx context.Context) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() - latestBatches, err := l.inj.LatestTransactionBatches(ctx) + // we should determine if we need to relay one + // to Ethereum for that we will find the latest confirmed valset and compare it to the ethereum chain + latestValsets, err := l.inj.LatestValsets(ctx) if err != nil { - return err + return errors.Wrap(err, "failed to get latest valset updates from Injective") } var ( - oldestConfirmedBatch *types.OutgoingTxBatch - oldestConfirmedBatchSigs []*types.MsgConfirmBatch + oldestConfirmedValset *types.Valset + oldestConfirmedValsetSigs []*types.MsgValsetConfirm ) - for _, batch := range latestBatches { - sigs, err := l.inj.TransactionBatchSignatures(ctx, batch.BatchNonce, common.HexToAddress(batch.TokenContract)) + for _, set := range latestValsets { + sigs, err := l.inj.AllValsetConfirms(ctx, set.Nonce) if err != nil { - return err + return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) } else if len(sigs) == 0 { continue } - oldestConfirmedBatch = batch - oldestConfirmedBatchSigs = sigs + oldestConfirmedValsetSigs = sigs + oldestConfirmedValset = set + break } - if oldestConfirmedBatch == nil { - l.Logger().Debugln("no outgoing batch to relay") + if oldestConfirmedValset == nil { + l.Logger().Debugln("no valset update to relay") return nil } - latestEthereumBatch, err := l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedBatch.TokenContract)) - if err != nil { - return err - } - - currentValset, err := l.findLatestValsetOnEth(ctx, l.inj, l.eth) + currentEthValset, err := l.findLatestValsetOnEth(ctx, l.inj, l.eth) if err != nil { - return errors.Wrap(err, "failed to find latest valset") - } else if currentValset == nil { - return errors.Wrap(err, "latest valset not found") + return errors.Wrap(err, "failed to find latest confirmed valset update on Ethereum") } - if oldestConfirmedBatch.BatchNonce <= latestEthereumBatch.Uint64() { + if oldestConfirmedValset.Nonce <= currentEthValset.Nonce { return nil } - latestEthereumBatch, err = l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedBatch.TokenContract)) + latestEthereumValsetNonce, err := l.eth.GetValsetNonce(ctx) if err != nil { - return err + return errors.Wrap(err, "failed to get latest valset nonce from Ethereum") } - // Check if ethereum batch was updated by other validators - if oldestConfirmedBatch.BatchNonce <= latestEthereumBatch.Uint64() { + // Check if other validators already updated the valset + if oldestConfirmedValset.Nonce <= latestEthereumValsetNonce.Uint64() { return nil } - l.Logger().WithFields(log.Fields{"inj_batch_nonce": oldestConfirmedBatch.BatchNonce, "eth_batch_nonce": latestEthereumBatch.Uint64()}).Debugln("latest batch updates") + l.Logger().WithFields(log.Fields{"inj_valset_nonce": oldestConfirmedValset.Nonce, "eth_valset_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("latest valset updates") // Check custom time delay offset - blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedBatch.Block)) + blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedValset.Height)) if err != nil { return errors.Wrap(err, "failed to parse timestamp from block") } - if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayBatchOffsetDur { - timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) - l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset duration not expired") + if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayValsetOffsetDur { + timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) + l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset duration not expired") return nil } - // Send SendTransactionBatch to Ethereum - txHash, err := l.eth.SendTransactionBatch(ctx, currentValset, oldestConfirmedBatch, oldestConfirmedBatchSigs) + txHash, err := l.eth.SendEthValsetUpdate( + ctx, + currentEthValset, + oldestConfirmedValset, + oldestConfirmedValsetSigs, + ) if err != nil { return err } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent tx batch to Ethereum") + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent valset update on Ethereum") return nil } -func (l *relayerLoop) relayValset(ctx context.Context) error { +func (l *relayerLoop) relayBatch(ctx context.Context) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() - // we should determine if we need to relay one - // to Ethereum for that we will find the latest confirmed valset and compare it to the ethereum chain - latestValsets, err := l.inj.LatestValsets(ctx) + latestBatches, err := l.inj.LatestTransactionBatches(ctx) if err != nil { - return errors.Wrap(err, "failed to get latest valset updates from Injective") + return err } var ( - oldestConfirmedValset *types.Valset - oldestConfirmedValsetSigs []*types.MsgValsetConfirm + oldestConfirmedBatch *types.OutgoingTxBatch + oldestConfirmedBatchSigs []*types.MsgConfirmBatch ) - for _, set := range latestValsets { - sigs, err := l.inj.AllValsetConfirms(ctx, set.Nonce) + for _, batch := range latestBatches { + sigs, err := l.inj.TransactionBatchSignatures(ctx, batch.BatchNonce, common.HexToAddress(batch.TokenContract)) if err != nil { - return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) + return err } else if len(sigs) == 0 { continue } - oldestConfirmedValsetSigs = sigs - oldestConfirmedValset = set - break + oldestConfirmedBatch = batch + oldestConfirmedBatchSigs = sigs } - if oldestConfirmedValset == nil { - l.Logger().Debugln("no valset update to relay") + if oldestConfirmedBatch == nil { + l.Logger().Debugln("no outgoing batch to relay") return nil } - currentEthValset, err := l.findLatestValsetOnEth(ctx, l.inj, l.eth) + latestEthereumBatch, err := l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedBatch.TokenContract)) if err != nil { - return errors.Wrap(err, "failed to find latest confirmed valset update on Ethereum") + return err } - if oldestConfirmedValset.Nonce <= currentEthValset.Nonce { + currentValset, err := l.findLatestValsetOnEth(ctx, l.inj, l.eth) + if err != nil { + return errors.Wrap(err, "failed to find latest valset") + } else if currentValset == nil { + return errors.Wrap(err, "latest valset not found") + } + + if oldestConfirmedBatch.BatchNonce <= latestEthereumBatch.Uint64() { return nil } - latestEthereumValsetNonce, err := l.eth.GetValsetNonce(ctx) + latestEthereumBatch, err = l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedBatch.TokenContract)) if err != nil { - return errors.Wrap(err, "failed to get latest valset nonce from Ethereum") + return err } - // Check if other validators already updated the valset - if oldestConfirmedValset.Nonce <= latestEthereumValsetNonce.Uint64() { + // Check if ethereum batch was updated by other validators + if oldestConfirmedBatch.BatchNonce <= latestEthereumBatch.Uint64() { return nil } - l.Logger().WithFields(log.Fields{"inj_valset_nonce": oldestConfirmedValset.Nonce, "eth_valset_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("latest valset updates") + l.Logger().WithFields(log.Fields{"inj_batch_nonce": oldestConfirmedBatch.BatchNonce, "eth_batch_nonce": latestEthereumBatch.Uint64()}).Debugln("latest batch updates") // Check custom time delay offset - blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedValset.Height)) + blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedBatch.Block)) if err != nil { return errors.Wrap(err, "failed to parse timestamp from block") } - if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayValsetOffsetDur { - timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) - l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset duration not expired") + if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayBatchOffsetDur { + timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) + l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset duration not expired") return nil } - txHash, err := l.eth.SendEthValsetUpdate( - ctx, - currentEthValset, - oldestConfirmedValset, - oldestConfirmedValsetSigs, - ) + // Send SendTransactionBatch to Ethereum + txHash, err := l.eth.SendTransactionBatch(ctx, currentValset, oldestConfirmedBatch, oldestConfirmedBatchSigs) if err != nil { return err } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent valset update on Ethereum") + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent tx batch to Ethereum") return nil } diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 98eb61d9..cdb9b688 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -69,20 +69,16 @@ func (l *ethSignerLoop) Logger() log.Logger { func (l *ethSignerLoop) Run(ctx context.Context) error { return loops.RunLoop(ctx, l.loopDuration, func() error { - return l.signBatchesAndValsets(ctx) - }) -} - -func (l *ethSignerLoop) signBatchesAndValsets(ctx context.Context) error { - if err := l.signNewValsetUpdates(ctx); err != nil { - return err - } + if err := l.signNewValsetUpdates(ctx); err != nil { + return err + } - if err := l.signNewBatch(ctx); err != nil { - return err - } + if err := l.signNewBatch(ctx); err != nil { + return err + } - return nil + return nil + }) } func (l *ethSignerLoop) signNewValsetUpdates(ctx context.Context) error { @@ -101,7 +97,7 @@ func (l *ethSignerLoop) signNewValsetUpdates(ctx context.Context) error { return err } - // todo: in case of multiple updates, we should sleep in between confirms requests (non-continuous nonce) + // todo: in case of multiple updates, we should sleep in between tx (non-continuous nonce) } return nil @@ -126,18 +122,18 @@ func (l *ethSignerLoop) signNewBatch(ctx context.Context) error { } func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTxBatch, error) { - var oldestUnsignedTransactionBatch *types.OutgoingTxBatch - retryFn := func() (err error) { + var oldestUnsignedBatch *types.OutgoingTxBatch + getOldestUnsignedBatchFn := func() (err error) { // sign the last unsigned batch, TODO check if we already have signed this - oldestUnsignedTransactionBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx) - if errors.Is(err, cosmos.ErrNotFound) || oldestUnsignedTransactionBatch == nil { + oldestUnsignedBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx) + if errors.Is(err, cosmos.ErrNotFound) || oldestUnsignedBatch == nil { return nil } return err } - if err := retry.Do(retryFn, + if err := retry.Do(getOldestUnsignedBatchFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -148,12 +144,15 @@ func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTx return nil, err } - return oldestUnsignedTransactionBatch, nil + return oldestUnsignedBatch, nil } func (l *ethSignerLoop) signBatch(ctx context.Context, batch *types.OutgoingTxBatch) error { - if err := retry.Do( - func() error { return l.inj.SendBatchConfirm(ctx, l.ethFrom, l.peggyID, batch) }, + signFn := func() error { + return l.inj.SendBatchConfirm(ctx, l.ethFrom, l.peggyID, batch) + } + + if err := retry.Do(signFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -164,7 +163,7 @@ func (l *ethSignerLoop) signBatch(ctx context.Context, batch *types.OutgoingTxBa return err } - l.Logger().WithFields(log.Fields{"token_contract": batch.TokenContract, "nonce": batch.BatchNonce, "txs": len(batch.Transactions)}).Infoln("confirmed batch on Injective") + l.Logger().WithFields(log.Fields{"token_contract": batch.TokenContract, "batch_nonce": batch.BatchNonce, "txs": len(batch.Transactions)}).Infoln("confirmed batch on Injective") return nil } @@ -195,9 +194,11 @@ func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*types.Valset } func (l *ethSignerLoop) signValset(ctx context.Context, vs *types.Valset) error { - if err := retry.Do(func() error { + signFn := func() error { return l.inj.SendValsetConfirm(ctx, l.ethFrom, l.peggyID, vs) - }, + } + + if err := retry.Do(signFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -208,7 +209,7 @@ func (l *ethSignerLoop) signValset(ctx context.Context, vs *types.Valset) error return err } - l.Logger().WithFields(log.Fields{"nonce": vs.Nonce, "members": len(vs.Members)}).Infoln("confirmed valset update on Injective") + l.Logger().WithFields(log.Fields{"valset_nonce": vs.Nonce, "validators": len(vs.Members)}).Infoln("confirmed valset update on Injective") return nil } From f45bad17b993b27e877e24344222710400fe6c82 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 17 Jan 2024 10:32:29 +0100 Subject: [PATCH 11/21] ensure oracle catches first peggy event --- test/peggo/deploy_bridge.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh index 5e936df8..cfbf2e6b 100755 --- a/test/peggo/deploy_bridge.sh +++ b/test/peggo/deploy_bridge.sh @@ -51,18 +51,20 @@ peggy_proxy_address=$(etherman --name TransparentUpgradeableProxy \ deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") echo "Deployed TransparentUpgradeableProxy for $peggy_impl_address (Peggy) with $proxy_admin_address (ProxyAdmin) as the admin" -coin_contract_address=$(etherman --name CosmosERC20 \ - -P "$deployer_pk" \ - --source "$cosmos_coin_contract_path" \ - deploy "$peggy_proxy_address" "Injective" "inj" 18) -echo "Deployed Cosmos Coin contract: $coin_contract_address" +# get the block number early so Oracle can catch the first event by Peggy.sol peggy_block_number=$(curl http://localhost:8545 \ -X POST \ -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0", "method":"eth_getBlockByNumber","params":["latest", true]}' 2>/dev/null \ | python3 -c "import sys, json; print(int(json.load(sys.stdin)['result']['number'], 0))") +coin_contract_address=$(etherman --name CosmosERC20 \ + -P "$deployer_pk" \ + --source "$cosmos_coin_contract_path" \ + deploy "$peggy_proxy_address" "Injective" "inj" 18) +echo "Deployed Cosmos Coin contract: $coin_contract_address" + echo "Peggy deployment done!" echo " * Contract address: $peggy_proxy_address" echo " * Contract deployment height: $peggy_block_number" From 60455ba500d57fb0624dc656aea00b088caf7ac4 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 29 Jan 2024 14:33:27 +0100 Subject: [PATCH 12/21] fix test readme and script --- test/README.md | 140 +++++------------------------------ test/peggo/peggy_params.json | 2 +- test/run.sh | 12 +-- test/send_to_inj.sh | 2 +- 4 files changed, 20 insertions(+), 136 deletions(-) diff --git a/test/README.md b/test/README.md index efc26c57..52b42def 100644 --- a/test/README.md +++ b/test/README.md @@ -1,144 +1,38 @@ ## Peggo Testsuite +[IN PROGRESS] + Welcome to the PegGo testing framework. The goal of this suite is aligned with the overall project goal - to move stuff onto common ground and iterate faster. By using the same lang for module, orchestrator and test we can achieve the full test coverage of all logical branches. -This is a special place where we don't care about things like: -* Node version -* Myriads of JS packages complaining about versions and API inconsistencies -* Different locations of ERC20 contract artifacts -* Stuff being deployed slowly -* Debugging ganache bugs - -We care about: -* Speed of the full run -* Go code coverage reports -* Zero issues coming from tooling or dev env -* Cross-platformity (macOS youKnow) -* Supporting any target EVM that implements Ethereum JSON-RPC -* 100% compatibility with real network +To set up the testing env, just run `test/run.sh` The script initializes 3 Injective validator nodes and 1 geth instance to simulate Injective and Ethereum networks respectively. After the networks are started 3 Peggo orchestrators are run for each of the validator nodes. +For simplicity, the script runs with hardcoded values for most of the configurations. Tweaking parameters is not yet supported. ## Prerequisites -You can specify any remote EVM endpoint to run the test against, but the best and most stable way to test the stuff is to run a Ganache or Hardhat instance. Hardhat is used solely as a JSON-RPC node provider. - -Preferred Solc compiler toolkit: -* https://github.com/crytic/solc-select - -Run `solc-select use 0.8.2` before starting any tests. - -### Running with Hardhat - -Hardhat is a newer alternative to Ganache that has convenient initialization via the config file. - -Running the init script will install node_modules inside `./test/ethereum` dir. - -``` -$ ./test/ethereum/hardhat-init.sh -``` - -After init is done, the following command can be used to launch a Hardhat server instance: - -``` -$ ./test/ethereum/hardhat.sh -``` - -The only option that can be set via ENV variable: - -* `HARDHAT_PORT` - specify the port for server to listen on. Defaults to `8545`. - -The rest of the options can be tweaked via `./test/ethereum/hardhat.config.js` - -### Pre-prod testing with Geth - -In order to get maximum compatibility with the real blockchain environment and avoid any bugs in the EVM runtime of Hardhat/Ganache, -also check different blocktime conditions, one might want to run Geth itself. +- `injective-core`: run `make install` on the `fix/peggy-contract-redeployment` branch +- `geth`: version 1.13.10-stable +- `etherman`: from https://github.com/InjectiveLabs/etherman/ +- `jq` +- `tmux` +- `perl` +- `sed` -Running this script will init a persistent data storage for the private network. -``` -$ ./test/ethereum/geth-init.sh -``` +### Injective -> Ethereum flow -Init options can be set via ENV variables: +- To send some `inj` tokens to Ethereum, run `test/send_to_eth.sh`. +- To send a cosmos native token other than `inj`, the `test/deploy_token.sh` deploys a new "WAT" token on Ethereum. Afterward, tweak the `test/send_to_eth.sh` to send the new token (already premined during `test/run.sh`) -* `GETH_NETWORK_ID` - specify Ethereum Network ID, defaults to `50`. -* `GETH_ALGO` - specify the consensus algorith for block producing. Defaults to `clique` (PoA), but `ethash` (PoW) is supported too. Make sure you adjust difficulty by patching your Geth (see at the bottom of this page) -* `CHAIN_DIR` - specify the data dir, a prefix for all data dirs and logs. Defaults to `./data` +### Ethereum -> Injective flow -Chain options can be tweaked in `./test/ethereum/geth/genesis.json` - -After init is done, the following command can be used to launch a full Geth node instance: - -``` -$ ./test/ethereum/geth.sh -``` - -Running options can be set via ENV variables: - -* `GETH_NETWORK_ID` - specify Ethereum Network ID, defaults to `50`. -* `GETH_ALGO` - specify the consensus algorith for block producing. Defaults to `ethash` (PoW), but `clique` (PoA) supported. -* `GETH_PORT` - specify the port for server to listen on. Defaults to `8545`. -* `CHAIN_DIR` - specify the data dir, a prefix for all data dirs and logs. Defaults to `./data` - -### Cosmos Daemon - -This testsuite supports different Cosmos backends, basically any app that has `peggy` module built-in will do. We expect that the generic app is Cosmos-SDK compatible and has very similar CLI interface to `gaiad`. There is a script that would launch an isolated full 3-node network, running natively on the host machine. Just make sure to provide the target executable as an argument. - -``` -$ CHAIN_ID=888 DENOM=inj ./test/cosmos/multinode.sh injectived -$ CHAIN_ID=somm DENOM=samoleans STAKE_DENOM=stake SCALE_FACTOR=000000 ./test/cosmos/multinode.sh sommelier -``` - -Full list of the supported ENV variables: -* `CHAIN_ID` - specifies Cosmos Chain ID, like `peggy-1` -* `CHAIN_DIR` - is a prefix for all data dirs and logs, will be removed if `CLEANUP=1` -* `DENOM` - Cosmos coin denom, the default coin of the network. Examples: `uatom`, `aphoton`, `samoleans` etc -* `STAKE_DENOM` - Cosmos coin denom that is used for staking and governance. On the Cosmos Hub it's `stake`. Defaults to value of `DENOM` in the script. -* `SCALE_FACTOR` - Scale factor for the Cosmos coin. Defaults to 1e18 to reflect Ethereum token balances. Use `000000` to follow Cosmos uatom (1e6) style. -* `CLEANUP` - if this option set to `1`, then the `CHAIN_DIR` will be removed in the most unsafe manner. -* `LOG_LEVEL` - sets the log level of the Cosmos node configuration. Defaults to Cosmos' default (`main:info,state:info,statesync:info,*:error`). - -**Important**: it is safe to run the script multiple times, it will stop nodes upon running, and optionally cleanup the state. If the state is not empty, the script will start nodes without running initialization again. So it could be used for manually retriable tests. +- To send some `inj` tokens to Ethereum, run `test/send_to_inj.sh` +- Other tokens: TODO ### Cosmos Accounts The script imports 3 validator accounts and 1 user account, specified by mnemonics in the script itself. Each validator account accessible as `val` on the corresponding nodes, and user account is shared across all three nodes as `user`. -### Misc: Patching Geth - -Geth by default scales difficulty of the blocks to hit the target block pace. So even if your network starts with `difficulty=1` in genesis, the difficulty will be higher in the next blocks and waiting times would be very high. Especially that DAG regeneration phases. A solution to this in local setup would be to either use `clique` consensus for PoA-style block producing, or just patch the Geth code, so the difficulty won't grow. - -Just clone the `go-ethereum` repo, apply this patch: - -```diff -diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go -index bdc02098a..c17ea5b76 100644 ---- a/consensus/ethash/consensus.go -+++ b/consensus/ethash/consensus.go -@@ -315,19 +315,7 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uin - // the difficulty that a new block should have when created at time - // given the parent block's time and difficulty. - func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { -- next := new(big.Int).Add(parent.Number, big1) -- switch { -- case config.IsMuirGlacier(next): -- return calcDifficultyEip2384(time, parent) -- case config.IsConstantinople(next): -- return calcDifficultyConstantinople(time, parent) -- case config.IsByzantium(next): -- return calcDifficultyByzantium(time, parent) -- case config.IsHomestead(next): -- return calcDifficultyHomestead(time, parent) -- default: -- return calcDifficultyFrontier(time, parent) -- } -+ return big1 - } -``` - -And install it with `go install ./cmd/geth`. Welcome to the Geth forking! - ## Contributing Patches and suggestions are welcome. We're looking for better coverage and maybe some isolated benchmarks. diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index 899af315..3c952d37 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -23,7 +23,7 @@ "cosmos_coin_denom": "inj", "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", "claim_slashing_enabled": false, - "bridge_contract_start_height": "25", + "bridge_contract_start_height": "20", "valset_reward": { "denom": "inj", "amount": "0" diff --git a/test/run.sh b/test/run.sh index 46f00297..dbb88213 100755 --- a/test/run.sh +++ b/test/run.sh @@ -4,16 +4,6 @@ set -e cd "${0%/*}" # cd in the script dir -# -#killall injectived 2> /dev/null -#killall geth 2> /dev/null - - -# -#killall "injectived" &> /dev/null -#killall "geth" &> /dev/null - - cwd=$(pwd) cosmos_dir="$cwd/cosmos" eth_dir="$cwd/ethereum" @@ -31,5 +21,5 @@ rm -rf "$peggo_dir/build" # Start the Cosmos chain "$cosmos_dir"/multinode.sh injectived -# Deploy Peggy contract suite and start peggo relayers +# Deploy Peggy contract suite and start Peggo orchestrators "$peggo_dir"/deploy_bridge.sh diff --git a/test/send_to_inj.sh b/test/send_to_inj.sh index dc86d1ec..066a02d9 100755 --- a/test/send_to_inj.sh +++ b/test/send_to_inj.sh @@ -9,7 +9,7 @@ peggy_contract="../solidity/contracts/Peggy.sol" cosmos_token_contract="../solidity/contracts/CosmosToken.sol" peggy_contract_address=0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe -inj_coin_contract_address=$(cat ./peggo/peggy_coin_address.txt) +inj_coin_contract_address=0x7E5C521F8515017487750c13C3bF3B15f3f5f654 etherman --name CosmosERC20 --source "$cosmos_token_contract" -P "$deployer_pk" tx "$inj_coin_contract_address" approve "$peggy_contract_address" 100 etherman --name Peggy --source "$peggy_contract" -P "$deployer_pk" tx "$peggy_contract_address" sendToInjective "$inj_coin_contract_address" 727aee334987c52fa7b567b2662bdbb68614e48c 10 "" From 98f636bad48dd6ab4be3d8fec0405b21eb2dede6 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 29 Jan 2024 15:19:33 +0100 Subject: [PATCH 13/21] update testing readme --- test/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/README.md b/test/README.md index 52b42def..68fa27af 100644 --- a/test/README.md +++ b/test/README.md @@ -8,6 +8,9 @@ By using the same lang for module, orchestrator and test we can achieve the full To set up the testing env, just run `test/run.sh` The script initializes 3 Injective validator nodes and 1 geth instance to simulate Injective and Ethereum networks respectively. After the networks are started 3 Peggo orchestrators are run for each of the validator nodes. For simplicity, the script runs with hardcoded values for most of the configurations. Tweaking parameters is not yet supported. +The script `test/run.sh` can be run multiple times. On each run it removes all previously written files with new ones. +Before running the script again, make sure you've killed all the injectived/geth processes (e.g. `killall injectived`). + ## Prerequisites - `injective-core`: run `make install` on the `fix/peggy-contract-redeployment` branch From a0b5035b4dc3bbfd6c6dd02190810d8ef3d2c3a8 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 22 May 2024 13:41:33 +0100 Subject: [PATCH 14/21] cleanup --- cmd/peggo/options.go | 2 +- cmd/peggo/orchestrator.go | 120 +- orchestrator/batch_creator.go | 114 ++ orchestrator/batch_requester.go | 128 -- orchestrator/cosmos/network.go | 11 +- orchestrator/ethereum/network.go | 5 +- orchestrator/inj_signer.go | 113 -- orchestrator/mocks_test.go | 43 +- orchestrator/{eth_oracle.go => oracle.go} | 160 +- ...{peggy_orchestrator.go => orchestrator.go} | 97 +- orchestrator/orchestrator_test.go | 1292 +++++++++++++++++ orchestrator/peggy_orchestrator_test.go | 1230 ---------------- .../{coingecko => pricefeed}/coingecko.go | 6 +- .../coingecko_test.go | 6 +- orchestrator/{inj_relayer.go => relayer.go} | 134 +- orchestrator/signer.go | 106 ++ solidity/wrappers/CosmosToken.sol/wrapper.go | 26 +- solidity/wrappers/HashingTest.sol/wrapper.go | 2 +- solidity/wrappers/Peggy.sol/wrapper.go | 64 +- solidity/wrappers/TestERC20.sol/wrapper.go | 26 +- 20 files changed, 1837 insertions(+), 1848 deletions(-) create mode 100644 orchestrator/batch_creator.go delete mode 100644 orchestrator/batch_requester.go delete mode 100644 orchestrator/inj_signer.go rename orchestrator/{eth_oracle.go => oracle.go} (55%) rename orchestrator/{peggy_orchestrator.go => orchestrator.go} (57%) create mode 100644 orchestrator/orchestrator_test.go delete mode 100644 orchestrator/peggy_orchestrator_test.go rename orchestrator/{coingecko => pricefeed}/coingecko.go (95%) rename orchestrator/{coingecko => pricefeed}/coingecko_test.go (95%) rename orchestrator/{inj_relayer.go => relayer.go} (67%) create mode 100644 orchestrator/signer.go diff --git a/cmd/peggo/options.go b/cmd/peggo/options.go index d89556cf..e53e3236 100644 --- a/cmd/peggo/options.go +++ b/cmd/peggo/options.go @@ -468,7 +468,7 @@ func initConfig(cmd *cli.Cmd) Config { cfg.coingeckoApi = cmd.String(cli.StringOpt{ Name: "coingecko_api", - Desc: "Specify HTTP endpoint for coingecko api.", + Desc: "Specify HTTP endpoint for pricefeed api.", EnvVar: "PEGGO_COINGECKO_API", Value: "https://api.coingecko.com/api/v3", }) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 727b45b4..d4eed5d1 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -3,6 +3,7 @@ package main import ( "context" "os" + "time" gethcommon "github.com/ethereum/go-ethereum/common" cli "github.com/jawher/mow.cli" @@ -11,9 +12,9 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/peggo/orchestrator" - "github.com/InjectiveLabs/peggo/orchestrator/coingecko" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" + "github.com/InjectiveLabs/peggo/orchestrator/pricefeed" "github.com/InjectiveLabs/peggo/orchestrator/version" chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" ) @@ -31,7 +32,35 @@ func orchestratorCmd(cmd *cli.Cmd) { // ensure a clean exit defer closer.Close() - cfg := initConfig(cmd) + var ( + cfg = initConfig(cmd) + cosmosKeyringCfg = cosmos.KeyringConfig{ + KeyringDir: *cfg.cosmosKeyringDir, + KeyringAppName: *cfg.cosmosKeyringAppName, + KeyringBackend: *cfg.cosmosKeyringBackend, + KeyFrom: *cfg.cosmosKeyFrom, + KeyPassphrase: *cfg.cosmosKeyPassphrase, + PrivateKey: *cfg.cosmosPrivKey, + UseLedger: *cfg.cosmosUseLedger, + } + cosmosNetworkCfg = cosmos.NetworkConfig{ + ChainID: *cfg.cosmosChainID, + CosmosGRPC: *cfg.cosmosGRPC, + TendermintRPC: *cfg.tendermintRPC, + GasPrice: *cfg.cosmosGasPrices, + } + ethNetworkCfg = ethereum.NetworkConfig{ + EthNodeRPC: *cfg.ethNodeRPC, + GasPriceAdjustment: *cfg.ethGasPriceAdjustment, + MaxGasPrice: *cfg.ethMaxGasPrice, + PendingTxWaitDuration: *cfg.pendingTxWaitDuration, + EthNodeAlchemyWS: *cfg.ethNodeAlchemyWS, + } + ) + + if *cfg.cosmosUseLedger || *cfg.ethUseLedger { + log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") + } log.WithFields(log.Fields{ "version": version.AppVersion, @@ -39,23 +68,12 @@ func orchestratorCmd(cmd *cli.Cmd) { "build_date": version.BuildDate, "go_version": version.GoVersion, "go_arch": version.GoArch, - }).Infoln("Peggo - Peggy module companion binary used to bridge assets between Injective and Ethereum") + }).Infoln("Peggo - Peggy module companion binary for bridging assets between Injective and Ethereum") - if *cfg.cosmosUseLedger || *cfg.ethUseLedger { - log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") - } + // 1. Connect to Injective network - cosmosKeyring, err := cosmos.NewKeyring(cosmos.KeyringConfig{ - KeyringDir: *cfg.cosmosKeyringDir, - KeyringAppName: *cfg.cosmosKeyringAppName, - KeyringBackend: *cfg.cosmosKeyringBackend, - KeyFrom: *cfg.cosmosKeyFrom, - KeyPassphrase: *cfg.cosmosKeyPassphrase, - PrivateKey: *cfg.cosmosPrivKey, - UseLedger: *cfg.cosmosUseLedger, - }) + cosmosKeyring, err := cosmos.NewKeyring(cosmosKeyringCfg) orShutdown(errors.Wrap(err, "failed to initialize Injective keyring")) - log.Infoln("initialized Injective keyring", cosmosKeyring.Addr.String()) ethKeyFromAddress, signerFn, personalSignFn, err := initEthereumAccountsManager( @@ -67,24 +85,16 @@ func orchestratorCmd(cmd *cli.Cmd) { cfg.ethUseLedger, ) orShutdown(errors.Wrap(err, "failed to initialize Ethereum keyring")) - log.Infoln("initialized Ethereum keyring", ethKeyFromAddress.String()) - cosmosNetwork, err := cosmos.NewNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ - ChainID: *cfg.cosmosChainID, - ValidatorAddress: cosmosKeyring.Addr.String(), - CosmosGRPC: *cfg.cosmosGRPC, - TendermintRPC: *cfg.tendermintRPC, - GasPrice: *cfg.cosmosGasPrices, - }) + cosmosNetworkCfg.ValidatorAddress = cosmosKeyring.Addr.String() + cosmosNetwork, err := cosmos.NewNetwork(cosmosKeyring, personalSignFn, cosmosNetworkCfg) orShutdown(err) - log.WithFields(log.Fields{"chain_id": *cfg.cosmosChainID, "gas_price": *cfg.cosmosGasPrices}).Infoln("connected to Injective network") ctx, cancelFn := context.WithCancel(context.Background()) closer.Bind(cancelFn) - // Construct erc20 token mapping peggyParams, err := cosmosNetwork.PeggyParams(ctx) orShutdown(errors.Wrap(err, "failed to query peggy params, is injectived running?")) @@ -96,14 +106,9 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithFields(log.Fields{"peggy_contract": peggyContractAddr.String(), "inj_token_contract": injTokenAddr.String()}).Debugln("loaded Peggy module params") - // Connect to ethereum network - ethereumNetwork, err := ethereum.NewNetwork(peggyContractAddr, ethKeyFromAddress, signerFn, ethereum.NetworkConfig{ - EthNodeRPC: *cfg.ethNodeRPC, - GasPriceAdjustment: *cfg.ethGasPriceAdjustment, - MaxGasPrice: *cfg.ethMaxGasPrice, - PendingTxWaitDuration: *cfg.pendingTxWaitDuration, - EthNodeAlchemyWS: *cfg.ethNodeAlchemyWS, - }) + // 2. Connect to ethereum network + + ethNetwork, err := ethereum.NewNetwork(peggyContractAddr, ethKeyFromAddress, signerFn, ethNetworkCfg) orShutdown(err) log.WithFields(log.Fields{ @@ -118,25 +123,44 @@ func orchestratorCmd(cmd *cli.Cmd) { log.Debugln("provided ETH address is registered with an orchestrator", addr.String()) } + var ( + valsetDur time.Duration + batchDur time.Duration + ) + + if *cfg.relayValsets { + valsetDur, err = time.ParseDuration(*cfg.relayValsetOffsetDur) + orShutdown(err) + } + + if *cfg.relayBatches { + batchDur, err = time.ParseDuration(*cfg.relayBatchOffsetDur) + orShutdown(err) + } + + orchestratorCfg := orchestrator.Config{ + CosmosAddr: cosmosKeyring.Addr, + EthereumAddr: ethKeyFromAddress, + MinBatchFeeUSD: *cfg.minBatchFeeUSD, + ERC20ContractMapping: erc20ContractMapping, + RelayValsetOffsetDur: valsetDur, + RelayBatchOffsetDur: batchDur, + RelayValsets: *cfg.relayValsets, + RelayBatches: *cfg.relayBatches, + RelayerMode: !isValidator, + } + // Create peggo and run it - peggo, err := orchestrator.NewPeggyOrchestrator( - cosmosKeyring.Addr, - ethKeyFromAddress, - coingecko.NewPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}), - orchestrator.Config{ - MinBatchFeeUSD: *cfg.minBatchFeeUSD, - ERC20ContractMapping: erc20ContractMapping, - RelayValsetOffsetDur: *cfg.relayValsetOffsetDur, - RelayBatchOffsetDur: *cfg.relayBatchOffsetDur, - RelayValsets: *cfg.relayValsets, - RelayBatches: *cfg.relayBatches, - RelayerMode: !isValidator, - }, + peggo, err := orchestrator.NewOrchestrator( + cosmosNetwork, + ethNetwork, + pricefeed.NewCoingeckoPriceFeed(100, &pricefeed.Config{BaseURL: *cfg.coingeckoApi}), + orchestratorCfg, ) orShutdown(err) go func() { - if err := peggo.Run(ctx, cosmosNetwork, ethereumNetwork); err != nil { + if err := peggo.Run(ctx, cosmosNetwork, ethNetwork); err != nil { log.Errorln(err) os.Exit(1) } diff --git a/orchestrator/batch_creator.go b/orchestrator/batch_creator.go new file mode 100644 index 00000000..b4cf1242 --- /dev/null +++ b/orchestrator/batch_creator.go @@ -0,0 +1,114 @@ +package orchestrator + +import ( + "context" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/shopspring/decimal" + log "github.com/xlab/suplog" + + "github.com/InjectiveLabs/peggo/orchestrator/loops" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" +) + +func (s *Orchestrator) runBatchCreator(ctx context.Context) (err error) { + bc := batchCreator{Orchestrator: s} + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting BatchCreator...") + + return loops.RunLoop(ctx, defaultLoopDur, func() error { + return bc.requestTokenBatches(ctx) + }) +} + +type batchCreator struct { + *Orchestrator +} + +func (l *batchCreator) Log() log.Logger { + return l.logger.WithField("loop", "BatchRequest") +} + +func (l *batchCreator) requestTokenBatches(ctx context.Context) error { + fees, err := l.getUnbatchedTokenFees(ctx) + if err != nil { + l.Log().WithError(err).Warningln("failed to get un-batched token fees") + return nil + } + + if len(fees) == 0 { + l.Log().Infoln("no token fees to batch") + return nil + } + + for _, fee := range fees { + l.requestTokenBatch(ctx, fee) + } + + return nil +} + +func (l *batchCreator) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { + var fees []*peggytypes.BatchFees + fn := func() (err error) { + fees, err = l.injective.UnbatchedTokensWithFees(ctx) + return + } + + if err := l.retry(ctx, fn); err != nil { + return nil, err + } + + return fees, nil +} + +func (l *batchCreator) requestTokenBatch(ctx context.Context, fee *peggytypes.BatchFees) { + tokenAddress := gethcommon.HexToAddress(fee.Token) + tokenPriceUSD, err := l.priceFeed.QueryUSDPrice(tokenAddress) + if err != nil { + l.Log().WithError(err).Warningln("failed to query price feed") + return + } + + tokenDecimals, err := l.ethereum.TokenDecimals(ctx, tokenAddress) + if err != nil { + l.Log().WithError(err).Warningln("is token contract address valid?") + return + } + + if l.checkMinBatchFee(fee, tokenPriceUSD, tokenDecimals) { + return + } + + tokenDenom := l.getTokenDenom(tokenAddress) + l.Log().WithFields(log.Fields{"token_denom": tokenDenom, "token_address": tokenAddress.String()}).Infoln("requesting token batch on Injective") + + _ = l.injective.SendRequestBatch(ctx, tokenDenom) +} + +func (l *batchCreator) getTokenDenom(tokenAddr gethcommon.Address) string { + if cosmosDenom, ok := l.cfg.ERC20ContractMapping[tokenAddr]; ok { + return cosmosDenom + } + + return peggytypes.PeggyDenomString(tokenAddr) +} + +func (l *batchCreator) checkMinBatchFee(fee *peggytypes.BatchFees, tokenPriceInUSD float64, tokenDecimals uint8) bool { + minFee := l.cfg.MinBatchFeeUSD + if minFee == 0 { + return true + } + + var ( + minFeeUSD = decimal.NewFromFloat(minFee) + tokenPriceUSD = decimal.NewFromFloat(tokenPriceInUSD) + totalFeeUSD = decimal.NewFromBigInt(fee.TotalFees.BigInt(), -1*int32(tokenDecimals)).Mul(tokenPriceUSD) + ) + + if totalFeeUSD.LessThan(minFeeUSD) { + l.Log().WithFields(log.Fields{"token_address": fee.Token, "total_fee": totalFeeUSD.String() + "USD", "min_fee": minFeeUSD.String() + "USD"}).Debugln("insufficient fee for a batch request, skipping...") + return false + } + + return true +} diff --git a/orchestrator/batch_requester.go b/orchestrator/batch_requester.go deleted file mode 100644 index 1d098308..00000000 --- a/orchestrator/batch_requester.go +++ /dev/null @@ -1,128 +0,0 @@ -package orchestrator - -import ( - "context" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum" - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/shopspring/decimal" - log "github.com/xlab/suplog" - - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" - "github.com/InjectiveLabs/peggo/orchestrator/loops" -) - -func (s *Orchestrator) runBatchRequester(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { - requester := batchRequester{ - Orchestrator: s, - Injective: inj, - Ethereum: eth, - } - - s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting BatchRequester...") - - return loops.RunLoop(ctx, defaultLoopDur, func() error { - return requester.RequestBatches(ctx) - }) -} - -type batchRequester struct { - *Orchestrator - Injective cosmos.Network - Ethereum ethereum.Network -} - -func (l *batchRequester) Logger() log.Logger { - return l.logger.WithField("loop", "BatchRequest") -} - -func (l *batchRequester) RequestBatches(ctx context.Context) error { - fees, err := l.GetUnbatchedTokenFees(ctx) - if err != nil { - l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") - return nil - } - - if len(fees) == 0 { - l.Logger().Infoln("no withdrawals to batch") - return nil - } - - for _, fee := range fees { - l.RequestTokenBatch(ctx, fee) - } - - return nil -} - -func (l *batchRequester) GetUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { - var unbatchedFees []*peggytypes.BatchFees - fn := func() error { - fees, err := l.Injective.UnbatchedTokensWithFees(ctx) - if err != nil { - return err - } - - unbatchedFees = fees - - return nil - } - - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - return nil, err - } - - return unbatchedFees, nil -} - -func (l *batchRequester) RequestTokenBatch(ctx context.Context, fee *peggytypes.BatchFees) { - tokenContract := gethcommon.HexToAddress(fee.Token) - tokenPriceInUSD, err := l.priceFeed.QueryUSDPrice(tokenContract) - if err != nil { - l.Logger().WithError(err).Warningln("failed to query oracle for token price") - return - } - - tokenDecimals, err := l.Ethereum.TokenDecimals(ctx, tokenContract) - if err != nil { - l.Logger().WithError(err).Warningln("failed to query decimals from token contract") - return - } - - if l.CheckMinBatchFee(fee, tokenPriceInUSD, tokenDecimals) { - return - } - - tokenDenom := l.GetTokenDenom(tokenContract) - l.Logger().WithFields(log.Fields{"token_denom": tokenDenom, "token_contract": tokenContract.String()}).Infoln("requesting new token batch on Injective") - - _ = l.Injective.SendRequestBatch(ctx, tokenDenom) -} - -func (l *batchRequester) GetTokenDenom(tokenAddr gethcommon.Address) string { - if cosmosDenom, ok := l.erc20ContractMapping[tokenAddr]; ok { - return cosmosDenom - } - - return peggytypes.PeggyDenomString(tokenAddr) -} - -func (l *batchRequester) CheckMinBatchFee(fee *peggytypes.BatchFees, tokenPriceInUSD float64, tokenDecimals uint8) bool { - if l.minBatchFeeUSD == 0 { - return true - } - - var ( - minFeeInUSDDec = decimal.NewFromFloat(l.minBatchFeeUSD) - tokenPriceInUSDDec = decimal.NewFromFloat(tokenPriceInUSD) - totalFeeInUSDDec = decimal.NewFromBigInt(fee.TotalFees.BigInt(), -1*int32(tokenDecimals)).Mul(tokenPriceInUSDDec) - ) - - if totalFeeInUSDDec.LessThan(minFeeInUSDDec) { - l.Logger().WithFields(log.Fields{"token_contract": fee.Token, "total_fee": totalFeeInUSDDec.String(), "min_fee": minFeeInUSDDec.String()}).Debugln("insufficient fee for token batch request, skipping...") - return false - } - - return true -} diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 43d5f712..498ed6d8 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -3,23 +3,22 @@ package cosmos import ( "context" "fmt" - cosmostypes "github.com/cosmos/cosmos-sdk/types" - gethcommon "github.com/ethereum/go-ethereum/common" "time" comethttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/crypto/keyring" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" log "github.com/xlab/suplog" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/sdk-go/client/chain" - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" ) type NetworkConfig struct { diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 6aec8945..3a97ac86 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -2,11 +2,11 @@ package ethereum import ( "context" - "github.com/ethereum/go-ethereum" "math/big" "strings" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" gethcommon "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" @@ -113,9 +113,8 @@ func NewNetwork( func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) { msg := ethereum.CallMsg{ - //From: gethcommon.Address{}, To: &tokenContract, - Data: gethcommon.Hex2Bytes("313ce567"), // Function signature for decimals(), + Data: gethcommon.Hex2Bytes("313ce567"), // decimals() method signature } res, err := n.Provider().CallContract(ctx, msg, nil) diff --git a/orchestrator/inj_signer.go b/orchestrator/inj_signer.go deleted file mode 100644 index d656d5e0..00000000 --- a/orchestrator/inj_signer.go +++ /dev/null @@ -1,113 +0,0 @@ -package orchestrator - -import ( - "context" - gethcommon "github.com/ethereum/go-ethereum/common" - log "github.com/xlab/suplog" - - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" - "github.com/InjectiveLabs/peggo/orchestrator/loops" -) - -// runEthSigner simply signs off on any batches or validator sets provided by the validator -// since these are provided directly by a trusted Injective node they can simply be assumed to be -// valid and signed off on. -func (s *Orchestrator) runEthSigner(ctx context.Context, inj cosmos.Network, peggyID gethcommon.Hash) error { - signer := ethSigner{ - Orchestrator: s, - Injective: inj, - PeggyID: peggyID, - } - - s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting Signer...") - - return loops.RunLoop(ctx, defaultLoopDur, func() error { - return signer.SignValsetsAndBatches(ctx) - }) -} - -type ethSigner struct { - *Orchestrator - Injective cosmos.Network - PeggyID gethcommon.Hash -} - -func (l *ethSigner) Logger() log.Logger { - return l.logger.WithField("loop", "Signer") -} - -func (l *ethSigner) SignValsetsAndBatches(ctx context.Context) error { - if err := l.signNewValsetUpdates(ctx); err != nil { - return err - } - - if err := l.signNewBatch(ctx); err != nil { - return err - } - - return nil -} - -func (l *ethSigner) signNewValsetUpdates(ctx context.Context) error { - var oldestUnsignedValsets []*peggytypes.Valset - getUnsignedValsetsFn := func() error { - oldestUnsignedValsets, _ = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) - return nil - } - - if err := retryFnOnErr(ctx, l.Logger(), getUnsignedValsetsFn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - - if len(oldestUnsignedValsets) == 0 { - l.Logger().Infoln("no valset updates to confirm") - return nil - } - - for _, vs := range oldestUnsignedValsets { - if err := retryFnOnErr(ctx, l.Logger(), func() error { - return l.Injective.SendValsetConfirm(ctx, l.ethAddr, l.PeggyID, vs) - }); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - - l.Logger().WithFields(log.Fields{"valset_nonce": vs.Nonce, "validators": len(vs.Members)}).Infoln("confirmed valset update on Injective") - } - - return nil -} - -func (l *ethSigner) signNewBatch(ctx context.Context) error { - var oldestUnsignedBatch *peggytypes.OutgoingTxBatch - getBatchFn := func() error { - oldestUnsignedBatch, _ = l.Injective.OldestUnsignedTransactionBatch(ctx, l.injAddr) - return nil - } - - if err := retryFnOnErr(ctx, l.Logger(), getBatchFn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - - if oldestUnsignedBatch == nil { - l.Logger().Infoln("no batch to confirm") - return nil - } - - confirmBatchFn := func() error { - return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, oldestUnsignedBatch) - } - - if err := retryFnOnErr(ctx, l.Logger(), confirmBatchFn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - - l.Logger().WithFields(log.Fields{"token_contract": oldestUnsignedBatch.TokenContract, "batch_nonce": oldestUnsignedBatch.BatchNonce, "txs": len(oldestUnsignedBatch.Transactions)}).Infoln("confirmed batch on Injective") - - return nil -} diff --git a/orchestrator/mocks_test.go b/orchestrator/mocks_test.go index e549411c..532b5a2f 100644 --- a/orchestrator/mocks_test.go +++ b/orchestrator/mocks_test.go @@ -2,14 +2,14 @@ package orchestrator import ( "context" - log "github.com/xlab/suplog" "math/big" "time" - comettypes "github.com/cometbft/cometbft/rpc/core/types" - sdktypes "github.com/cosmos/cosmos-sdk/types" + cometrpc "github.com/cometbft/cometbft/rpc/core/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" + log "github.com/xlab/suplog" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" @@ -25,28 +25,28 @@ func (p MockPriceFeed) QueryUSDPrice(address gethcommon.Address) (float64, error type MockCosmosNetwork struct { PeggyParamsFn func(ctx context.Context) (*peggytypes.Params, error) - LastClaimEventByAddrFn func(ctx context.Context, address sdktypes.AccAddress) (*peggytypes.LastClaimEvent, error) - GetValidatorAddressFn func(ctx context.Context, address gethcommon.Address) (sdktypes.AccAddress, error) + LastClaimEventByAddrFn func(ctx context.Context, address cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) + GetValidatorAddressFn func(ctx context.Context, address gethcommon.Address) (cosmostypes.AccAddress, error) CurrentValsetFn func(ctx context.Context) (*peggytypes.Valset, error) ValsetAtFn func(ctx context.Context, uint642 uint64) (*peggytypes.Valset, error) - OldestUnsignedValsetsFn func(ctx context.Context, address sdktypes.AccAddress) ([]*peggytypes.Valset, error) + OldestUnsignedValsetsFn func(ctx context.Context, address cosmostypes.AccAddress) ([]*peggytypes.Valset, error) LatestValsetsFn func(ctx context.Context) ([]*peggytypes.Valset, error) AllValsetConfirmsFn func(ctx context.Context, uint642 uint64) ([]*peggytypes.MsgValsetConfirm, error) - OldestUnsignedTransactionBatchFn func(ctx context.Context, address sdktypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) + OldestUnsignedTransactionBatchFn func(ctx context.Context, address cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) LatestTransactionBatchesFn func(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) UnbatchedTokensWithFeesFn func(ctx context.Context) ([]*peggytypes.BatchFees, error) TransactionBatchSignaturesFn func(ctx context.Context, uint642 uint64, address gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) - UpdatePeggyOrchestratorAddressesFn func(ctx context.Context, address gethcommon.Address, address2 sdktypes.Address) error + UpdatePeggyOrchestratorAddressesFn func(ctx context.Context, address gethcommon.Address, address2 cosmostypes.Address) error SendValsetConfirmFn func(ctx context.Context, address gethcommon.Address, hash gethcommon.Hash, valset *peggytypes.Valset) error SendBatchConfirmFn func(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error SendRequestBatchFn func(ctx context.Context, denom string) error - SendToEthFn func(ctx context.Context, destination gethcommon.Address, amount, fee sdktypes.Coin) error + SendToEthFn func(ctx context.Context, destination gethcommon.Address, amount, fee cosmostypes.Coin) error SendOldDepositClaimFn func(ctx context.Context, deposit *peggyevents.PeggySendToCosmosEvent) error SendDepositClaimFn func(ctx context.Context, deposit *peggyevents.PeggySendToInjectiveEvent) error SendWithdrawalClaimFn func(ctx context.Context, withdrawal *peggyevents.PeggyTransactionBatchExecutedEvent) error SendValsetClaimFn func(ctx context.Context, vs *peggyevents.PeggyValsetUpdatedEvent) error SendERC20DeployedClaimFn func(ctx context.Context, erc20 *peggyevents.PeggyERC20DeployedEvent) error - GetBlockFn func(ctx context.Context, height int64) (*comettypes.ResultBlock, error) + GetBlockFn func(ctx context.Context, height int64) (*cometrpc.ResultBlock, error) GetLatestBlockHeightFn func(ctx context.Context) (int64, error) } @@ -54,11 +54,11 @@ func (n MockCosmosNetwork) PeggyParams(ctx context.Context) (*peggytypes.Params, return n.PeggyParamsFn(ctx) } -func (n MockCosmosNetwork) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdktypes.AccAddress) (*peggytypes.LastClaimEvent, error) { +func (n MockCosmosNetwork) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { return n.LastClaimEventByAddrFn(ctx, validatorAccountAddress) } -func (n MockCosmosNetwork) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdktypes.AccAddress, error) { +func (n MockCosmosNetwork) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) { return n.GetValidatorAddressFn(ctx, addr) } @@ -70,7 +70,7 @@ func (n MockCosmosNetwork) CurrentValset(ctx context.Context) (*peggytypes.Valse return n.CurrentValsetFn(ctx) } -func (n MockCosmosNetwork) OldestUnsignedValsets(ctx context.Context, valAccountAddress sdktypes.AccAddress) ([]*peggytypes.Valset, error) { +func (n MockCosmosNetwork) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { return n.OldestUnsignedValsetsFn(ctx, valAccountAddress) } @@ -82,7 +82,7 @@ func (n MockCosmosNetwork) AllValsetConfirms(ctx context.Context, nonce uint64) return n.AllValsetConfirmsFn(ctx, nonce) } -func (n MockCosmosNetwork) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress sdktypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { +func (n MockCosmosNetwork) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { return n.OldestUnsignedTransactionBatchFn(ctx, valAccountAddress) } @@ -98,7 +98,7 @@ func (n MockCosmosNetwork) TransactionBatchSignatures(ctx context.Context, nonce return n.TransactionBatchSignaturesFn(ctx, nonce, tokenContract) } -func (n MockCosmosNetwork) UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom gethcommon.Address, orchAddr sdktypes.AccAddress) error { +func (n MockCosmosNetwork) UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom gethcommon.Address, orchAddr cosmostypes.AccAddress) error { return n.UpdatePeggyOrchestratorAddressesFn(ctx, ethFrom, orchAddr) } @@ -114,7 +114,7 @@ func (n MockCosmosNetwork) SendRequestBatch(ctx context.Context, denom string) e return n.SendRequestBatchFn(ctx, denom) } -func (n MockCosmosNetwork) SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee sdktypes.Coin) error { +func (n MockCosmosNetwork) SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee cosmostypes.Coin) error { return n.SendToEthFn(ctx, destination, amount, fee) } @@ -138,7 +138,7 @@ func (n MockCosmosNetwork) SendERC20DeployedClaim(ctx context.Context, erc20 *pe return n.SendERC20DeployedClaimFn(ctx, erc20) } -func (n MockCosmosNetwork) GetBlock(ctx context.Context, height int64) (*comettypes.ResultBlock, error) { +func (n MockCosmosNetwork) GetBlock(ctx context.Context, height int64) (*cometrpc.ResultBlock, error) { return n.GetBlockFn(ctx, height) } @@ -147,12 +147,12 @@ func (n MockCosmosNetwork) GetLatestBlockHeight(ctx context.Context) (int64, err panic("implement me") } -func (n MockCosmosNetwork) GetTxs(ctx context.Context, block *comettypes.ResultBlock) ([]*comettypes.ResultTx, error) { +func (n MockCosmosNetwork) GetTxs(ctx context.Context, block *cometrpc.ResultBlock) ([]*cometrpc.ResultTx, error) { //TODO implement me panic("implement me") } -func (n MockCosmosNetwork) GetValidatorSet(ctx context.Context, height int64) (*comettypes.ResultValidators, error) { +func (n MockCosmosNetwork) GetValidatorSet(ctx context.Context, height int64) (*cometrpc.ResultValidators, error) { //TODO implement me panic("implement me") } @@ -169,12 +169,17 @@ type MockEthereumNetwork struct { SendEthValsetUpdateFn func(ctx context.Context, oldValset *peggytypes.Valset, newValset *peggytypes.Valset, confirms []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) GetTxBatchNonceFn func(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) SendTransactionBatchFn func(ctx context.Context, currentValset *peggytypes.Valset, batch *peggytypes.OutgoingTxBatch, confirms []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) + TokenDecimalsFn func(ctx context.Context, address gethcommon.Address) (uint8, error) } func (n MockEthereumNetwork) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { return n.GetHeaderByNumberFn(ctx, number) } +func (n MockEthereumNetwork) TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) { + return n.TokenDecimalsFn(ctx, tokenContract) +} + func (n MockEthereumNetwork) GetPeggyID(ctx context.Context) (gethcommon.Hash, error) { return n.GetPeggyIDFn(ctx) } diff --git a/orchestrator/eth_oracle.go b/orchestrator/oracle.go similarity index 55% rename from orchestrator/eth_oracle.go rename to orchestrator/oracle.go index 0e30e636..eb104e65 100644 --- a/orchestrator/eth_oracle.go +++ b/orchestrator/oracle.go @@ -8,8 +8,6 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" @@ -32,57 +30,48 @@ const ( resyncInterval = 24 * time.Hour ) -// runEthOracle is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain +// runOracle is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain // and ferried over to Cosmos where they will be used to issue tokens or process batches. -func (s *Orchestrator) runEthOracle( - ctx context.Context, - inj cosmos.Network, - eth ethereum.Network, - lastObservedBlock uint64, -) error { - oracle := ethOracle{ +func (s *Orchestrator) runOracle(ctx context.Context, lastObservedBlock uint64) error { + oracle := oracle{ Orchestrator: s, - Injective: inj, - Ethereum: eth, - LastObservedEthHeight: lastObservedBlock, - LastResyncWithInjective: time.Now(), + lastObservedEthHeight: lastObservedBlock, + lastResyncWithInjective: time.Now(), } - s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting EthOracle...") + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting Oracle...") return loops.RunLoop(ctx, defaultLoopDur, func() error { - return oracle.ObserveEthEvents(ctx) + return oracle.observeEthEvents(ctx) }) } -type ethOracle struct { +type oracle struct { *Orchestrator - Injective cosmos.Network - Ethereum ethereum.Network - LastResyncWithInjective time.Time - LastObservedEthHeight uint64 + lastResyncWithInjective time.Time + lastObservedEthHeight uint64 } -func (l *ethOracle) Logger() log.Logger { - return l.logger.WithField("loop", "EthOracle") +func (l *oracle) Log() log.Logger { + return l.logger.WithField("loop", "Oracle") } -func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { +func (l *oracle) observeEthEvents(ctx context.Context) error { // check if validator is in the active set since claims will fail otherwise - vs, err := l.Injective.CurrentValset(ctx) + vs, err := l.injective.CurrentValset(ctx) if err != nil { - return errors.Wrap(err, "failed to get current valset on Injective") + return errors.Wrap(err, "failed to get active validator set on Injective") } bonded := false for _, v := range vs.Members { - if l.ethAddr.Hex() == v.EthereumAddress { + if l.cfg.EthereumAddr.Hex() == v.EthereumAddress { bonded = true } } if !bonded { - l.Logger().WithFields(log.Fields{"latest_inj_block": vs.Height}).Infoln("validator not in active set, cannot make claims...") + l.Log().WithFields(log.Fields{"latest_inj_block": vs.Height}).Warningln("validator not in active set, cannot make claims...") return nil } @@ -93,23 +82,23 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { // not enough blocks on ethereum yet if latestHeight <= ethBlockConfirmationDelay { - l.Logger().Debugln("not enough blocks on Ethereum") + l.Log().Debugln("not enough blocks on Ethereum") return nil } // ensure that latest block has minimum confirmations latestHeight = latestHeight - ethBlockConfirmationDelay - if latestHeight <= l.LastObservedEthHeight { - l.Logger().WithFields(log.Fields{"latest": latestHeight, "observed": l.LastObservedEthHeight}).Debugln("latest Ethereum height already observed") + if latestHeight <= l.lastObservedEthHeight { + l.Log().WithFields(log.Fields{"latest": latestHeight, "observed": l.lastObservedEthHeight}).Debugln("latest Ethereum height already observed") return nil } // ensure the block range is within defaultBlocksToSearch - if latestHeight > l.LastObservedEthHeight+defaultBlocksToSearch { - latestHeight = l.LastObservedEthHeight + defaultBlocksToSearch + if latestHeight > l.lastObservedEthHeight+defaultBlocksToSearch { + latestHeight = l.lastObservedEthHeight + defaultBlocksToSearch } - events, err := l.getEthEvents(ctx, l.LastObservedEthHeight, latestHeight) + events, err := l.getEthEvents(ctx, l.lastObservedEthHeight, latestHeight) if err != nil { return err } @@ -125,16 +114,14 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { }) if len(newEvents) == 0 { - l.Logger().WithFields(log.Fields{"last_claimed_event_nonce": lastClaim.EthereumEventNonce, "eth_block_start": l.LastObservedEthHeight, "eth_block_end": latestHeight}).Infoln("no new events on Ethereum") - l.LastObservedEthHeight = latestHeight - + l.Log().WithFields(log.Fields{"last_claimed_event_nonce": lastClaim.EthereumEventNonce, "eth_block_start": l.lastObservedEthHeight, "eth_block_end": latestHeight}).Infoln("no new events on Ethereum") + l.lastObservedEthHeight = latestHeight return nil } if expected, actual := lastClaim.EthereumEventNonce+1, newEvents[0].Nonce(); expected != actual { - l.Logger().WithFields(log.Fields{"expected_nonce": expected, "actual_nonce": actual, "last_claimed_event_nonce": lastClaim.EthereumEventNonce}).Debugln("orchestrator missed an Ethereum event. Resyncing event nonce with last claimed event...") - l.LastObservedEthHeight = lastClaim.EthereumEventHeight - + l.Log().WithFields(log.Fields{"expected": expected, "actual": actual, "last_claimed_event_nonce": lastClaim.EthereumEventNonce}).Debugln("orchestrator missed an Ethereum event. Restarting block search from last attested claim...") + l.lastObservedEthHeight = lastClaim.EthereumEventHeight return nil } @@ -142,10 +129,10 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { return err } - l.Logger().WithFields(log.Fields{"claims": len(newEvents), "eth_block_start": l.LastObservedEthHeight, "eth_block_end": latestHeight}).Infoln("sent new event claims to Injective") - l.LastObservedEthHeight = latestHeight + l.Log().WithFields(log.Fields{"claims": len(newEvents), "eth_block_start": l.lastObservedEthHeight, "eth_block_end": latestHeight}).Infoln("sent new event claims to Injective") + l.lastObservedEthHeight = latestHeight - if time.Since(l.LastResyncWithInjective) >= resyncInterval { + if time.Since(l.lastResyncWithInjective) >= resyncInterval { if err := l.autoResync(ctx); err != nil { return err } @@ -154,32 +141,32 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { return nil } -func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) ([]event, error) { +func (l *oracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) ([]event, error) { var events []event scanEthEventsFn := func() error { - events = nil // clear previous result in case a retry happens + events = nil // clear previous result in case a retry occurred - oldDepositEvents, err := l.Ethereum.GetSendToCosmosEvents(startBlock, endBlock) + oldDepositEvents, err := l.ethereum.GetSendToCosmosEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToCosmos events") } - depositEvents, err := l.Ethereum.GetSendToInjectiveEvents(startBlock, endBlock) + depositEvents, err := l.ethereum.GetSendToInjectiveEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToInjective events") } - withdrawalEvents, err := l.Ethereum.GetTransactionBatchExecutedEvents(startBlock, endBlock) + withdrawalEvents, err := l.ethereum.GetTransactionBatchExecutedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get TransactionBatchExecuted events") } - erc20DeploymentEvents, err := l.Ethereum.GetPeggyERC20DeployedEvents(startBlock, endBlock) + erc20DeploymentEvents, err := l.ethereum.GetPeggyERC20DeployedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get ERC20Deployed events") } - valsetUpdateEvents, err := l.Ethereum.GetValsetUpdatedEvents(startBlock, endBlock) + valsetUpdateEvents, err := l.ethereum.GetValsetUpdatedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get ValsetUpdated events") } @@ -212,18 +199,17 @@ func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint6 return nil } - if err := retryFnOnErr(ctx, l.Logger(), scanEthEventsFn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + if err := l.retry(ctx, scanEthEventsFn); err != nil { return nil, err } return events, nil } -func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { +func (l *oracle) getLatestEthHeight(ctx context.Context) (uint64, error) { latestHeight := uint64(0) fn := func() error { - h, err := l.Ethereum.GetHeaderByNumber(ctx, nil) + h, err := l.ethereum.GetHeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") } @@ -232,38 +218,31 @@ func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { return nil } - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + if err := l.retry(ctx, fn); err != nil { return 0, err } return latestHeight, nil } -func (l *ethOracle) getLastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { +func (l *oracle) getLastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { var claim *peggytypes.LastClaimEvent - fn := func() error { - c, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) - if err != nil { - return err - } - - claim = c - return nil + fn := func() (err error) { + claim, err = l.injective.LastClaimEventByAddr(ctx, l.cfg.CosmosAddr) + return } - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + if err := l.retry(ctx, fn); err != nil { return nil, err } return claim, nil } -func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) error { +func (l *oracle) sendNewEventClaims(ctx context.Context, events []event) error { sendEventsFn := func() error { // in case sending one of more claims fails, we reload the latest claimed nonce to filter processed events - lastClaim, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) + lastClaim, err := l.injective.LastClaimEventByAddr(ctx, l.cfg.CosmosAddr) if err != nil { return err } @@ -278,64 +257,57 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) erro return err } - // Considering blockTime=1s on Injective chain, adding Sleep to make sure new event is sent + // Considering block time ~1s on Injective chain, adding Sleep to make sure new event is sent // only after previous event is executed successfully. Otherwise it will through `non contiguous event nonce` failing CheckTx. - time.Sleep(1200 * time.Millisecond) + time.Sleep(1100 * time.Millisecond) } return nil } - if err := retryFnOnErr(ctx, l.Logger(), sendEventsFn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + if err := l.retry(ctx, sendEventsFn); err != nil { return err } return nil } -func (l *ethOracle) autoResync(ctx context.Context) error { - latestHeight := uint64(0) - fn := func() error { - h, err := l.getLastClaimBlockHeight(ctx, l.Injective) - if err != nil { - return err - } - - latestHeight = h - return nil +func (l *oracle) autoResync(ctx context.Context) error { + var height uint64 + fn := func() (err error) { + height, err = l.getLastClaimBlockHeight(ctx, l.injective) + return } - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + if err := l.retry(ctx, fn); err != nil { return err } - l.Logger().WithFields(log.Fields{"last_resync": l.LastResyncWithInjective.String(), "last_claimed_eth_height": latestHeight}).Infoln("auto resyncing with last claimed event on Injective") + l.Log().WithFields(log.Fields{"last_resync": l.lastResyncWithInjective.String(), "last_claimed_eth_height": height}).Infoln("auto resyncing with last claimed event on Injective") - l.LastObservedEthHeight = latestHeight - l.LastResyncWithInjective = time.Now() + l.lastObservedEthHeight = height + l.lastResyncWithInjective = time.Now() return nil } -func (l *ethOracle) sendEthEventClaim(ctx context.Context, ev event) error { +func (l *oracle) sendEthEventClaim(ctx context.Context, ev event) error { switch e := ev.(type) { case *oldDeposit: ev := peggyevents.PeggySendToCosmosEvent(*e) - return l.Injective.SendOldDepositClaim(ctx, &ev) + return l.injective.SendOldDepositClaim(ctx, &ev) case *deposit: ev := peggyevents.PeggySendToInjectiveEvent(*e) - return l.Injective.SendDepositClaim(ctx, &ev) + return l.injective.SendDepositClaim(ctx, &ev) case *valsetUpdate: ev := peggyevents.PeggyValsetUpdatedEvent(*e) - return l.Injective.SendValsetClaim(ctx, &ev) + return l.injective.SendValsetClaim(ctx, &ev) case *withdrawal: ev := peggyevents.PeggyTransactionBatchExecutedEvent(*e) - return l.Injective.SendWithdrawalClaim(ctx, &ev) + return l.injective.SendWithdrawalClaim(ctx, &ev) case *erc20Deployment: ev := peggyevents.PeggyERC20DeployedEvent(*e) - return l.Injective.SendERC20DeployedClaim(ctx, &ev) + return l.injective.SendERC20DeployedClaim(ctx, &ev) default: panic(errors.Errorf("unknown ev type %T", e)) } diff --git a/orchestrator/peggy_orchestrator.go b/orchestrator/orchestrator.go similarity index 57% rename from orchestrator/peggy_orchestrator.go rename to orchestrator/orchestrator.go index 2562fa24..c01b0737 100644 --- a/orchestrator/peggy_orchestrator.go +++ b/orchestrator/orchestrator.go @@ -7,11 +7,9 @@ import ( "github.com/avast/retry-go" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/loops" @@ -21,73 +19,48 @@ const ( defaultLoopDur = 60 * time.Second ) -var ( - maxRetryAttempts uint = 10 -) - // PriceFeed provides token price for a given contract address type PriceFeed interface { QueryUSDPrice(address gethcommon.Address) (float64, error) } type Config struct { + CosmosAddr cosmostypes.AccAddress + EthereumAddr gethcommon.Address MinBatchFeeUSD float64 ERC20ContractMapping map[gethcommon.Address]string - RelayValsetOffsetDur string - RelayBatchOffsetDur string + RelayValsetOffsetDur time.Duration + RelayBatchOffsetDur time.Duration RelayValsets bool RelayBatches bool RelayerMode bool } type Orchestrator struct { - logger log.Logger - svcTags metrics.Tags - - injAddr cosmostypes.AccAddress - ethAddr gethcommon.Address - - priceFeed PriceFeed - erc20ContractMapping map[gethcommon.Address]string - relayValsetOffsetDur time.Duration - relayBatchOffsetDur time.Duration - minBatchFeeUSD float64 - isRelayer bool + logger log.Logger + svcTags metrics.Tags + cfg Config + maxAttempts uint + + injective cosmos.Network + ethereum ethereum.Network + priceFeed PriceFeed } -func NewPeggyOrchestrator( - orchestratorAddr cosmostypes.AccAddress, - ethAddr gethcommon.Address, +func NewOrchestrator( + inj cosmos.Network, + eth ethereum.Network, priceFeed PriceFeed, cfg Config, ) (*Orchestrator, error) { o := &Orchestrator{ - logger: log.DefaultLogger, - svcTags: metrics.Tags{"svc": "peggy_orchestrator"}, - injAddr: orchestratorAddr, - ethAddr: ethAddr, - priceFeed: priceFeed, - erc20ContractMapping: cfg.ERC20ContractMapping, - minBatchFeeUSD: cfg.MinBatchFeeUSD, - isRelayer: cfg.RelayerMode, - } - - if cfg.RelayValsets { - dur, err := time.ParseDuration(cfg.RelayValsetOffsetDur) - if err != nil { - return nil, errors.Wrapf(err, "valset relaying enabled but offset duration is not properly set") - } - - o.relayValsetOffsetDur = dur - } - - if cfg.RelayBatches { - dur, err := time.ParseDuration(cfg.RelayBatchOffsetDur) - if err != nil { - return nil, errors.Wrapf(err, "batch relaying enabled but offset duration is not properly set") - } - - o.relayBatchOffsetDur = dur + logger: log.DefaultLogger, + svcTags: metrics.Tags{"svc": "peggy_orchestrator"}, + injective: inj, + ethereum: eth, + priceFeed: priceFeed, + cfg: cfg, + maxAttempts: 10, } return o, nil @@ -96,7 +69,7 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. func (s *Orchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { - if s.isRelayer { + if s.cfg.RelayerMode { return s.startRelayerMode(ctx, inj, eth) } @@ -108,7 +81,6 @@ func (s *Orchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum func (s *Orchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in validator mode") - // get gethcommon block observed by this validator lastObservedEthBlock, _ := s.getLastClaimBlockHeight(ctx, inj) if lastObservedEthBlock == 0 { peggyParams, err := inj.PeggyParams(ctx) @@ -127,10 +99,10 @@ func (s *Orchestrator) startValidatorMode(ctx context.Context, inj cosmos.Networ var pg loops.ParanoidGroup - pg.Go(func() error { return s.runEthOracle(ctx, inj, eth, lastObservedEthBlock) }) - pg.Go(func() error { return s.runEthSigner(ctx, inj, peggyContractID) }) - pg.Go(func() error { return s.runBatchRequester(ctx, inj, eth) }) - pg.Go(func() error { return s.runRelayer(ctx, inj, eth) }) + pg.Go(func() error { return s.runOracle(ctx, lastObservedEthBlock) }) + pg.Go(func() error { return s.runSigner(ctx, peggyContractID) }) + pg.Go(func() error { return s.runBatchCreator(ctx) }) + pg.Go(func() error { return s.runRelayer(ctx) }) return pg.Wait() } @@ -143,8 +115,8 @@ func (s *Orchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, var pg loops.ParanoidGroup - pg.Go(func() error { return s.runBatchRequester(ctx, inj, eth) }) - pg.Go(func() error { return s.runRelayer(ctx, inj, eth) }) + pg.Go(func() error { return s.runBatchCreator(ctx) }) + pg.Go(func() error { return s.runRelayer(ctx) }) return pg.Wait() } @@ -154,7 +126,7 @@ func (s *Orchestrator) getLastClaimBlockHeight(ctx context.Context, inj cosmos.N doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() - claim, err := inj.LastClaimEventByAddr(ctx, s.injAddr) + claim, err := inj.LastClaimEventByAddr(ctx, s.cfg.CosmosAddr) if err != nil { return 0, err } @@ -162,12 +134,11 @@ func (s *Orchestrator) getLastClaimBlockHeight(ctx context.Context, inj cosmos.N return claim.EthereumEventHeight, nil } -func retryFnOnErr(ctx context.Context, log log.Logger, fn func() error) error { +func (s *Orchestrator) retry(ctx context.Context, fn func() error) error { return retry.Do(fn, retry.Context(ctx), - retry.Attempts(maxRetryAttempts), + retry.Attempts(s.maxAttempts), retry.OnRetry(func(n uint, err error) { - log.WithError(err).Warningf("encountered error, retrying (%d)", n) - }), - ) + s.logger.WithError(err).Warningf("loop error, retrying... (#%d)", n+1) + })) } diff --git a/orchestrator/orchestrator_test.go b/orchestrator/orchestrator_test.go new file mode 100644 index 00000000..10992528 --- /dev/null +++ b/orchestrator/orchestrator_test.go @@ -0,0 +1,1292 @@ +package orchestrator + +import ( + "context" + "errors" + "math/big" + "testing" + "time" + + cometrpc "github.com/cometbft/cometbft/rpc/core/types" + comettypes "github.com/cometbft/cometbft/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" + + "github.com/InjectiveLabs/metrics" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" +) + +const maxLoopRetries = 1 + +func Test_BatchCreator(t *testing.T) { + t.Parallel() + + injTokenAddress := gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30") + + testTable := []struct { + name string + expected error + orch *Orchestrator + }{ + { + name: "failed to get token fees", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + UnbatchedTokensWithFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "no unbatched token fees", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + UnbatchedTokensWithFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "token fee less than threshold", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + cfg: Config{ + MinBatchFeeUSD: 51.0, + ERC20ContractMapping: map[gethcommon.Address]string{injTokenAddress: "injective"}, + }, + priceFeed: MockPriceFeed{QueryUSDPriceFn: func(_ gethcommon.Address) (float64, error) { return 1, nil }}, + injective: MockCosmosNetwork{ + SendRequestBatchFn: func(context.Context, string) error { return nil }, + UnbatchedTokensWithFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { + fees, _ := cosmostypes.NewIntFromString("50000000000000000000") + return []*peggytypes.BatchFees{ + { + Token: injTokenAddress.String(), + TotalFees: fees, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + TokenDecimalsFn: func(_ context.Context, _ gethcommon.Address) (uint8, error) { + return 18, nil + }, + }, + }, + }, + + { + name: "token fees exceed threshold", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + priceFeed: MockPriceFeed{QueryUSDPriceFn: func(_ gethcommon.Address) (float64, error) { return 1, nil }}, + cfg: Config{ + MinBatchFeeUSD: 49.0, + ERC20ContractMapping: map[gethcommon.Address]string{injTokenAddress: "injective"}, + }, + injective: MockCosmosNetwork{ + SendRequestBatchFn: func(context.Context, string) error { return nil }, + UnbatchedTokensWithFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { + fees, _ := cosmostypes.NewIntFromString("50000000000000000000") + return []*peggytypes.BatchFees{{ + Token: injTokenAddress.String(), + TotalFees: fees, + }}, nil + }, + }, + ethereum: MockEthereumNetwork{ + TokenDecimalsFn: func(_ context.Context, _ gethcommon.Address) (uint8, error) { + return 18, nil + }, + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + bc := batchCreator{Orchestrator: tt.orch} + assert.ErrorIs(t, bc.requestTokenBatches(context.Background()), tt.expected) + }) + } +} + +func Test_Oracle(t *testing.T) { + t.Parallel() + + ethAddr1 := gethcommon.HexToAddress("0x76D2dDbb89C36FA39FAa5c5e7C61ee95AC4D76C4") + ethAddr2 := gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88") + + testTable := []struct { + name string + expected error + orch *Orchestrator + lastResyncWithInjective time.Time + lastObservedEthHeight uint64 + }{ + { + name: "failed to get current validator set", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "orchestrator not bonded", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr1}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + }, + }, + lastResyncWithInjective: time.Time{}, + lastObservedEthHeight: 0, + }, + + { + name: "failed to get latest ethereum height", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return nil, errors.New("fail") + }, + }, + }, + }, + + { + name: "not enough block on ethereum", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil // minimum is 12 + }, + }, + }, + }, + + { + name: "failed to get ethereum events", + expected: errors.New("oops"), + lastObservedEthHeight: 100, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "failed to get last claim event", + expected: errors.New("oops"), + lastObservedEthHeight: 100, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return nil, errors.New("oops") + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(100), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "no new events", + expected: nil, + lastObservedEthHeight: 100, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 101, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(100), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "missed events triggers resync", + expected: nil, + lastObservedEthHeight: 100, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 102, + EthereumEventHeight: 1000, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(104), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "sent new event claim", + expected: nil, + lastObservedEthHeight: 100, + lastResyncWithInjective: time.Now(), // skip auto resync + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 102, + EthereumEventHeight: 1000, + }, nil + }, + + SendOldDepositClaimFn: func(_ context.Context, _ *peggyevents.PeggySendToCosmosEvent) error { + return nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(103), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "auto resync", + expected: nil, + lastObservedEthHeight: 100, + orch: &Orchestrator{ + logger: DummyLog, + cfg: Config{EthereumAddr: ethAddr2}, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: ethAddr2.String(), + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 102, + EthereumEventHeight: 1000, + }, nil + }, + + SendOldDepositClaimFn: func(_ context.Context, _ *peggyevents.PeggySendToCosmosEvent) error { + return nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(103), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + o := oracle{ + Orchestrator: tt.orch, + lastResyncWithInjective: tt.lastResyncWithInjective, + lastObservedEthHeight: tt.lastObservedEthHeight, + } + + err := o.observeEthEvents(context.Background()) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} + +func Test_Relayer_Valsets(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + }{ + { + name: "failed to get latest valset updates", + expected: errors.New("oops"), + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "failed to get valset confirmations", + expected: errors.New("oops"), + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "no new valset to relay", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return nil, nil + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "no new valset to relay", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + ethereum: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return nil, errors.New("oops") + }, + }, + injective: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + }, + + { + name: "valset already updated", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + ethereum: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(101), nil + }, + }, + injective: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + }, + + { + name: "failed to get injective block", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + ethereum: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + }, + injective: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return nil, errors.New("oops") + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + }, + + { + name: "relay valset offser duration not expired", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + cfg: Config{RelayValsetOffsetDur: 10 * time.Second}, + ethereum: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + }, + injective: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return &cometrpc.ResultBlock{ + Block: &comettypes.Block{ + Header: comettypes.Header{Time: time.Now()}, + }, + }, nil + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + }, + + { + name: "failed to send valset update", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + cfg: Config{RelayValsetOffsetDur: 0}, + ethereum: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + + SendEthValsetUpdateFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.Valset, _ []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { + return nil, errors.New("oops") + }, + }, + injective: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return &cometrpc.ResultBlock{ + Block: &comettypes.Block{ + Header: comettypes.Header{Time: time.Now()}, + }, + }, nil + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + }, + + { + name: "sent valset update", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + svcTags: metrics.Tags{"svc": "relayer"}, + cfg: Config{RelayValsetOffsetDur: 0}, + ethereum: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + + SendEthValsetUpdateFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.Valset, _ []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { + return &gethcommon.Hash{}, nil + }, + }, + injective: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return &cometrpc.ResultBlock{ + Block: &comettypes.Block{ + Header: comettypes.Header{Time: time.Now()}, + }, + }, nil + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := relayer{tt.orch} + + err := r.relayValset(context.Background(), &peggytypes.Valset{Nonce: 101}) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + +} + +func Test_Relayer_Batches(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + }{ + { + name: "failed to get token batches", + expected: errors.New("oops"), + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "failed to get token batch confirmations", + expected: errors.New("oops"), + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchTimeout: 100, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return nil, errors.New("oops") + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + }, + }, + }, + + { + name: "no batch to relay", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return nil, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + }, + }, + }, + + { + name: "failed to get latest batch nonce", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{}}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return nil, errors.New("oops") + }, + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + }, + }, + }, + + { + name: "batch already updated", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 100, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + }, + }, + }, + + { + name: "failed to get injective block", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return nil, errors.New("oops") + }, + }, + ethereum: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + }, + }, + }, + + { + name: "batch relay offset not expired", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + cfg: Config{RelayBatchOffsetDur: 10 * time.Second}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return &cometrpc.ResultBlock{ + Block: &comettypes.Block{ + Header: comettypes.Header{Time: time.Now()}, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + }, + }, + }, + + { + name: "failed to send batch update", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + cfg: Config{RelayBatchOffsetDur: 0}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return &cometrpc.ResultBlock{ + Block: &comettypes.Block{ + Header: comettypes.Header{Time: time.Now()}, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + + SendTransactionBatchFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.OutgoingTxBatch, _ []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "sent batch update", + expected: nil, + orch: &Orchestrator{ + maxAttempts: maxLoopRetries, + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + cfg: Config{RelayBatchOffsetDur: 0}, + injective: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*cometrpc.ResultBlock, error) { + return &cometrpc.ResultBlock{ + Block: &comettypes.Block{ + Header: comettypes.Header{Time: time.Now()}, + }, + }, nil + }, + }, + ethereum: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + + GetHeaderByNumberFn: func(_ context.Context, _ *big.Int) (*gethtypes.Header, error) { + return &gethtypes.Header{Number: big.NewInt(10)}, nil + }, + + SendTransactionBatchFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.OutgoingTxBatch, _ []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { + return &gethcommon.Hash{}, nil + }, + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := relayer{Orchestrator: tt.orch} + err := r.relayTokenBatch(context.Background(), &peggytypes.Valset{Nonce: 101}) + + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} + +func Test_Signer_Valsets(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + }{ + { + name: "failed to get unsigned valsets", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { + return nil, errors.New("oops") + }, + }, + }, + }, + + { + name: "no valset updates to sign", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { + return nil, nil + }, + }, + }, + }, + + { + name: "failed to send valset confirm", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil + }, + + SendValsetConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.Valset) error { + return errors.New("oops") + }, + }, + }, + }, + + { + name: "sent valset confirm", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil + }, + + SendValsetConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.Valset) error { + return nil + }, + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + s := signer{Orchestrator: tt.orch} + err := s.signValidatorSets(context.Background()) + + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} + +func Test_Signer_Batches(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + }{ + { + name: "failed to get unsigned batches/no batch to confirm", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return nil, errors.New("ooops") + }, + }, + }, + }, + + { + name: "failed to send batch confirm", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return &peggytypes.OutgoingTxBatch{}, nil + }, + + SendBatchConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.OutgoingTxBatch) error { + return errors.New("oops") + }, + }, + }, + }, + + { + name: "sent batch confirm", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + maxAttempts: maxLoopRetries, + injective: MockCosmosNetwork{ + OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return &peggytypes.OutgoingTxBatch{}, nil + }, + + SendBatchConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.OutgoingTxBatch) error { + return nil + }, + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + s := signer{Orchestrator: tt.orch} + err := s.signNewBatch(context.Background()) + + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} diff --git a/orchestrator/peggy_orchestrator_test.go b/orchestrator/peggy_orchestrator_test.go deleted file mode 100644 index 5320af6e..00000000 --- a/orchestrator/peggy_orchestrator_test.go +++ /dev/null @@ -1,1230 +0,0 @@ -package orchestrator - -import ( - "context" - "errors" - "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - comettypes "github.com/cometbft/cometbft/rpc/core/types" - comet "github.com/cometbft/cometbft/types" - cosmtypes "github.com/cosmos/cosmos-sdk/types" - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/stretchr/testify/assert" - "math/big" - "testing" - "time" -) - -func Test_Orchestrator_Loops(t *testing.T) { - t.Parallel() - - // faster test runs - maxRetryAttempts = 1 - - t.Run("batch requester", func(t *testing.T) { - t.Parallel() - - testTable := []struct { - name string - expected error - orch *Orchestrator - inj cosmos.Network - eth ethereum.Network - }{ - { - name: "failed to get token fees", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - UnbatchedTokensWithFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "no unbatched tokens", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - UnbatchedTokensWithFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { - return nil, nil - }, - }, - }, - - { - name: "batch does not meet fee threshold", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - priceFeed: MockPriceFeed{QueryUSDPriceFn: func(_ gethcommon.Address) (float64, error) { return 1, nil }}, - minBatchFeeUSD: 51.0, - erc20ContractMapping: map[gethcommon.Address]string{ - gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30"): "inj", - }, - }, - inj: MockCosmosNetwork{ - SendRequestBatchFn: func(context.Context, string) error { return nil }, - UnbatchedTokensWithFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { - fees, _ := cosmtypes.NewIntFromString("50000000000000000000") - return []*peggytypes.BatchFees{ - { - Token: gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30").String(), - TotalFees: fees, - }, - }, nil - }, - }, - }, - - { - name: "batch meets threshold and a request is sent", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - priceFeed: MockPriceFeed{QueryUSDPriceFn: func(_ gethcommon.Address) (float64, error) { return 1, nil }}, - minBatchFeeUSD: 49.0, - erc20ContractMapping: map[gethcommon.Address]string{ - gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30"): "inj", - }, - }, - inj: MockCosmosNetwork{ - SendRequestBatchFn: func(context.Context, string) error { return nil }, - UnbatchedTokensWithFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { - fees, _ := cosmtypes.NewIntFromString("50000000000000000000") - return []*peggytypes.BatchFees{{ - Token: gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30").String(), - TotalFees: fees, - }}, nil - }, - }, - }, - } - - for _, tt := range testTable { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - r := batchRequester{ - Orchestrator: tt.orch, - Injective: tt.inj, - } - - assert.ErrorIs(t, r.RequestBatches(context.Background()), tt.expected) - }) - } - }) - - t.Run("oracle", func(t *testing.T) { - t.Parallel() - - testTable := []struct { - name string - expected error - orch *Orchestrator - inj cosmos.Network - eth ethereum.Network - lastResyncWithInjective time.Time - lastObservedEthHeight uint64 - }{ - { - name: "failed to get current valset", - expected: errors.New("oops"), - orch: &Orchestrator{ - logger: DummyLog, - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "orchestrator not bonded", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x76D2dDbb89C36FA39FAa5c5e7C61ee95AC4D76C4"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - }, - }, - - { - name: "failed to get latest eth height", - expected: errors.New("oops"), - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return nil, errors.New("fail") - }, - }, - }, - - { - name: "not enough block on ethereum", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(10)}, nil // minimum is 12 - }, - }, - }, - - { - name: "failed to get ethereum events", - expected: errors.New("oops"), - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(2100)}, nil - }, - GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return nil, errors.New("oops") - }, - }, - lastObservedEthHeight: 100, - }, - - { - name: "failed to get last claim event", - expected: errors.New("oops"), - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - - LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { - return nil, errors.New("oops") - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(2100)}, nil - }, - GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{ - { - EventNonce: big.NewInt(100), - }, - }, nil - }, - - GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - }, - lastObservedEthHeight: 100, - }, - - { - name: "no new events", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - - LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{ - EthereumEventNonce: 101, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(2100)}, nil - }, - GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{ - { - EventNonce: big.NewInt(100), - }, - }, nil - }, - - GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - }, - lastObservedEthHeight: 100, - }, - - { - name: "missed events triggers resync", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - - LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{ - EthereumEventNonce: 102, - EthereumEventHeight: 1000, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(2100)}, nil - }, - GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{ - { - EventNonce: big.NewInt(104), - }, - }, nil - }, - - GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - }, - lastObservedEthHeight: 100, - }, - - { - name: "sent new event claim", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - - LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{ - EthereumEventNonce: 102, - EthereumEventHeight: 1000, - }, nil - }, - - SendOldDepositClaimFn: func(_ context.Context, _ *peggyevents.PeggySendToCosmosEvent) error { - return nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(2100)}, nil - }, - GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{ - { - EventNonce: big.NewInt(103), - }, - }, nil - }, - - GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - }, - lastObservedEthHeight: 100, - lastResyncWithInjective: time.Now(), // skip auto resync - }, - - { - name: "auto resync", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), - }, - inj: MockCosmosNetwork{ - CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { - return &peggytypes.Valset{ - Members: []*peggytypes.BridgeValidator{ - { - EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", - }, - }, - }, nil - }, - - LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{ - EthereumEventNonce: 102, - EthereumEventHeight: 1000, - }, nil - }, - - SendOldDepositClaimFn: func(_ context.Context, _ *peggyevents.PeggySendToCosmosEvent) error { - return nil - }, - }, - eth: MockEthereumNetwork{ - GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(2100)}, nil - }, - GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{ - { - EventNonce: big.NewInt(103), - }, - }, nil - }, - - GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - }, - lastObservedEthHeight: 100, - }, - } - - for _, tt := range testTable { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - o := ethOracle{ - Orchestrator: tt.orch, - Injective: tt.inj, - Ethereum: tt.eth, - LastResyncWithInjective: tt.lastResyncWithInjective, - LastObservedEthHeight: tt.lastObservedEthHeight, - } - - err := o.ObserveEthEvents(context.Background()) - if tt.expected == nil { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } - }) - - t.Run("relayer valset", func(t *testing.T) { - t.Parallel() - - testTable := []struct { - name string - expected error - orch *Orchestrator - inj cosmos.Network - eth ethereum.Network - }{ - { - name: "failed to get latest valset updates", - expected: errors.New("oops"), - orch: &Orchestrator{svcTags: metrics.Tags{"svc": "relayer"}}, - inj: MockCosmosNetwork{ - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "failed to get valset confirmations", - expected: errors.New("oops"), - orch: &Orchestrator{svcTags: metrics.Tags{"svc": "relayer"}}, - inj: MockCosmosNetwork{ - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "no new valset to relay", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return nil, nil - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return nil, nil - }, - }, - }, - - { - name: "no new valset to relay", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - eth: MockEthereumNetwork{ - GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return nil, errors.New("oops") - }, - }, - - inj: MockCosmosNetwork{ - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do - }, - }, - }, - - { - name: "valset already updated", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - eth: MockEthereumNetwork{ - GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(101), nil - }, - }, - - inj: MockCosmosNetwork{ - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do - }, - }, - }, - - { - name: "failed to get injective block", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - eth: MockEthereumNetwork{ - GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(99), nil - }, - }, - - inj: MockCosmosNetwork{ - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return nil, errors.New("oops") - }, - - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do - }, - }, - }, - - { - name: "relay valset offser duration not expired", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - relayValsetOffsetDur: 10 * time.Second, - }, - eth: MockEthereumNetwork{ - GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(99), nil - }, - }, - - inj: MockCosmosNetwork{ - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return &comettypes.ResultBlock{ - Block: &comet.Block{ - Header: comet.Header{Time: time.Now()}, - }, - }, nil - }, - - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do - }, - }, - }, - - { - name: "failed to send valset update", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - relayValsetOffsetDur: 0, - }, - eth: MockEthereumNetwork{ - GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(99), nil - }, - - SendEthValsetUpdateFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.Valset, _ []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { - return nil, errors.New("oops") - }, - }, - - inj: MockCosmosNetwork{ - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return &comettypes.ResultBlock{ - Block: &comet.Block{ - Header: comet.Header{Time: time.Now()}, - }, - }, nil - }, - - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do - }, - }, - }, - - { - name: "sent valset update", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - relayValsetOffsetDur: 0, - }, - eth: MockEthereumNetwork{ - GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(99), nil - }, - - SendEthValsetUpdateFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.Valset, _ []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { - return &gethcommon.Hash{}, nil - }, - }, - - inj: MockCosmosNetwork{ - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return &comettypes.ResultBlock{ - Block: &comet.Block{ - Header: comet.Header{Time: time.Now()}, - }, - }, nil - }, - - LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil // non-empty will do - }, - - AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do - }, - }, - }, - } - - for _, tt := range testTable { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - r := relayer{ - Orchestrator: tt.orch, - Injective: tt.inj, - Ethereum: tt.eth, - } - - latestEthValset := &peggytypes.Valset{ - Nonce: 101, - } - - err := r.relayValset(context.Background(), latestEthValset) - if tt.expected == nil { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } - }) - - t.Run("relayer batches", func(t *testing.T) { - t.Parallel() - - testTable := []struct { - name string - expected error - orch *Orchestrator - inj cosmos.Network - eth ethereum.Network - }{ - { - name: "failed to get latest batches", - expected: errors.New("oops"), - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "failed to get batch confirmations", - expected: errors.New("oops"), - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{}}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "no batch to relay", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return nil, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - }, - }, - - { - name: "failed to get latest batch nonce", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{}}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - }, - eth: MockEthereumNetwork{ - GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "batch already updated", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{ - BatchNonce: 100, - }}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - }, - eth: MockEthereumNetwork{ - GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { - return big.NewInt(100), nil - }, - }, - }, - - { - name: "failed to get injective block", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{ - BatchNonce: 101, - }}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return nil, errors.New("oops") - }, - }, - eth: MockEthereumNetwork{ - GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { - return big.NewInt(100), nil - }, - }, - }, - - { - name: "batch relay offset not expired", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - relayBatchOffsetDur: 10 * time.Second, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{ - BatchNonce: 101, - }}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return &comettypes.ResultBlock{ - Block: &comet.Block{ - Header: comet.Header{Time: time.Now()}, - }, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { - return big.NewInt(100), nil - }, - }, - }, - - { - name: "failed to send batch update", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - relayBatchOffsetDur: 0, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{ - BatchNonce: 101, - }}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return &comettypes.ResultBlock{ - Block: &comet.Block{ - Header: comet.Header{Time: time.Now()}, - }, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { - return big.NewInt(100), nil - }, - - SendTransactionBatchFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.OutgoingTxBatch, _ []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "sent batch update", - expected: nil, - orch: &Orchestrator{ - logger: DummyLog, - svcTags: metrics.Tags{"svc": "relayer"}, - relayBatchOffsetDur: 0, - }, - inj: MockCosmosNetwork{ - LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return []*peggytypes.OutgoingTxBatch{{ - BatchNonce: 101, - }}, nil - }, - - TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return []*peggytypes.MsgConfirmBatch{{}}, nil - }, - - GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { - return &comettypes.ResultBlock{ - Block: &comet.Block{ - Header: comet.Header{Time: time.Now()}, - }, - }, nil - }, - }, - eth: MockEthereumNetwork{ - GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { - return big.NewInt(100), nil - }, - - SendTransactionBatchFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.OutgoingTxBatch, _ []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { - return &gethcommon.Hash{}, nil - }, - }, - }, - } - - for _, tt := range testTable { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - r := relayer{ - Orchestrator: tt.orch, - Injective: tt.inj, - Ethereum: tt.eth, - } - - latestEthValset := &peggytypes.Valset{ - Nonce: 101, - } - - err := r.relayBatch(context.Background(), latestEthValset) - if tt.expected == nil { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } - }) - - t.Run("signer valsets", func(t *testing.T) { - t.Parallel() - - testTable := []struct { - name string - expected error - orch *Orchestrator - inj cosmos.Network - }{ - { - name: "failed to get unsigned valsets", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { - return nil, errors.New("oops") - }, - }, - }, - - { - name: "no valset updates to sign", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { - return nil, nil - }, - }, - }, - - { - name: "failed to send valset confirm", - expected: errors.New("oops"), - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil - }, - - SendValsetConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.Valset) error { - return errors.New("oops") - }, - }, - }, - - { - name: "sent valset confirm", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { - return []*peggytypes.Valset{{}}, nil - }, - - SendValsetConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.Valset) error { - return nil - }, - }, - }, - } - - for _, tt := range testTable { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - s := ethSigner{ - Orchestrator: tt.orch, - Injective: tt.inj, - } - - err := s.signNewValsetUpdates(context.Background()) - if tt.expected == nil { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } - }) - - t.Run("signer batches", func(t *testing.T) { - t.Parallel() - - testTable := []struct { - name string - expected error - orch *Orchestrator - inj cosmos.Network - }{ - { - name: "failed to get unsigned batches/no batch to confirm", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { - return nil, errors.New("ooops") - }, - }, - }, - - { - name: "failed to send batch confirm", - expected: errors.New("oops"), - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { - return &peggytypes.OutgoingTxBatch{}, nil - }, - - SendBatchConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.OutgoingTxBatch) error { - return errors.New("oops") - }, - }, - }, - - { - name: "sent batch confirm", - expected: nil, - orch: &Orchestrator{logger: DummyLog}, - inj: MockCosmosNetwork{ - OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { - return &peggytypes.OutgoingTxBatch{}, nil - }, - - SendBatchConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.OutgoingTxBatch) error { - return nil - }, - }, - }, - } - - for _, tt := range testTable { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - s := ethSigner{ - Orchestrator: tt.orch, - Injective: tt.inj, - } - - err := s.signNewBatch(context.Background()) - if tt.expected == nil { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } - }) -} diff --git a/orchestrator/coingecko/coingecko.go b/orchestrator/pricefeed/coingecko.go similarity index 95% rename from orchestrator/coingecko/coingecko.go rename to orchestrator/pricefeed/coingecko.go index 9f2e7289..71653d00 100644 --- a/orchestrator/coingecko/coingecko.go +++ b/orchestrator/pricefeed/coingecko.go @@ -1,4 +1,4 @@ -package coingecko +package pricefeed import ( "encoding/json" @@ -122,9 +122,9 @@ func (cp *PriceFeed) QueryUSDPrice(erc20Contract common.Address) (float64, error return tokenPriceInUSD, nil } -// NewPriceFeed returns price puller for given symbol. The price will be pulled +// NewCoingeckoPriceFeed returns price puller for given symbol. The price will be pulled // from endpoint and divided by scaleFactor. Symbol name (if reported by endpoint) must match. -func NewPriceFeed(interval time.Duration, endpointConfig *Config) *PriceFeed { +func NewCoingeckoPriceFeed(interval time.Duration, endpointConfig *Config) *PriceFeed { return &PriceFeed{ client: &http.Client{ Transport: &http.Transport{ diff --git a/orchestrator/coingecko/coingecko_test.go b/orchestrator/pricefeed/coingecko_test.go similarity index 95% rename from orchestrator/coingecko/coingecko_test.go rename to orchestrator/pricefeed/coingecko_test.go index 1ae706b4..dccdf4a2 100644 --- a/orchestrator/coingecko/coingecko_test.go +++ b/orchestrator/pricefeed/coingecko_test.go @@ -1,4 +1,4 @@ -package coingecko +package pricefeed import ( "math/big" @@ -13,7 +13,7 @@ func TestFeeThresholdTwoDecimals(t *testing.T) { // https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0xe28b3b32b6c345a34ff64674606124dd5aceca30&vs_currencies=usd injTokenContract := common.HexToAddress("0xe28b3b32b6c345a34ff64674606124dd5aceca30") - coingeckoFeed := NewPriceFeed(100, &Config{}) + coingeckoFeed := NewCoingeckoPriceFeed(100, &Config{}) currentTokenPrice, _ := coingeckoFeed.QueryUSDPrice(injTokenContract) // "usd":9.35 minFeeInUSD := float64(23.5) // 23.5 USD to submit batch tx @@ -34,7 +34,7 @@ func TestFeeThresholdTwoDecimals(t *testing.T) { func TestFeeThresholdNineDecimals(t *testing.T) { // https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce&vs_currencies=usd shibTokenContract := common.HexToAddress("0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce") - coingeckoFeed := NewPriceFeed(100, &Config{}) + coingeckoFeed := NewCoingeckoPriceFeed(100, &Config{}) currentTokenPrice, _ := coingeckoFeed.QueryUSDPrice(shibTokenContract) // "usd":0.000008853 minFeeInUSD := float64(23.5) // 23.5 USD to submit batch tx diff --git a/orchestrator/inj_relayer.go b/orchestrator/relayer.go similarity index 67% rename from orchestrator/inj_relayer.go rename to orchestrator/relayer.go index 0c94b199..db53fb47 100644 --- a/orchestrator/inj_relayer.go +++ b/orchestrator/relayer.go @@ -11,13 +11,10 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) const ( @@ -25,71 +22,53 @@ const ( findValsetBlocksToSearch = 2000 ) -func (s *Orchestrator) runRelayer(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { - rel := relayer{ - Orchestrator: s, - Injective: inj, - Ethereum: eth, - } - - relayingBatches := rel.IsRelayingBatches() - relayingValsets := rel.IsRelayingValsets() - if noRelay := !relayingBatches && !relayingValsets; noRelay { +func (s *Orchestrator) runRelayer(ctx context.Context) error { + if noRelay := !s.cfg.RelayValsets && !s.cfg.RelayBatches; noRelay { return nil } - s.logger.WithFields(log.Fields{"loop_duration": defaultRelayerLoopDur.String(), "relay_batches": relayingBatches, "relay_valsets": relayingValsets}).Debugln("starting Relayer...") + r := relayer{Orchestrator: s} + s.logger.WithFields(log.Fields{"loop_duration": defaultRelayerLoopDur.String(), "relay_token_batches": r.cfg.RelayBatches, "relay_validator_sets": s.cfg.RelayValsets}).Debugln("starting Relayer...") return loops.RunLoop(ctx, defaultRelayerLoopDur, func() error { - return rel.RelayValsetsAndBatches(ctx) + return r.relay(ctx) }) } type relayer struct { *Orchestrator - Injective cosmos.Network - Ethereum ethereum.Network } -func (l *relayer) Logger() log.Logger { +func (l *relayer) Log() log.Logger { return l.logger.WithField("loop", "Relayer") } -func (l *relayer) IsRelayingBatches() bool { - return l.relayBatchOffsetDur != 0 -} - -func (l *relayer) IsRelayingValsets() bool { - return l.relayValsetOffsetDur != 0 -} - -func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { - ethValset, err := l.GetLatestEthValset(ctx) +func (l *relayer) relay(ctx context.Context) error { + ethValset, err := l.getLatestEthValset(ctx) if err != nil { return err } var pg loops.ParanoidGroup - if l.relayValsetOffsetDur != 0 { + if l.cfg.RelayValsets { pg.Go(func() error { - return retryFnOnErr(ctx, l.Logger(), func() error { + return l.retry(ctx, func() error { return l.relayValset(ctx, ethValset) }) }) } - if l.relayBatchOffsetDur != 0 { + if l.cfg.RelayBatches { pg.Go(func() error { - return retryFnOnErr(ctx, l.Logger(), func() error { - return l.relayBatch(ctx, ethValset) + return l.retry(ctx, func() error { + return l.relayTokenBatch(ctx, ethValset) }) }) } if pg.Initialized() { if err := pg.Wait(); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") return err } } @@ -98,7 +77,7 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { } -func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { +func (l *relayer) getLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { var latestEthValset *peggytypes.Valset fn := func() error { vs, err := l.findLatestValsetOnEth(ctx) @@ -110,8 +89,7 @@ func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, e return nil } - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + if err := l.retry(ctx, fn); err != nil { return nil, err } @@ -123,9 +101,9 @@ func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.V doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() - latestInjectiveValsets, err := l.Injective.LatestValsets(ctx) + latestInjectiveValsets, err := l.injective.LatestValsets(ctx) if err != nil { - return errors.Wrap(err, "failed to get latest valset updates from Injective") + return errors.Wrap(err, "failed to get latest validator set from Injective") } var ( @@ -134,9 +112,9 @@ func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.V ) for _, set := range latestInjectiveValsets { - sigs, err := l.Injective.AllValsetConfirms(ctx, set.Nonce) + sigs, err := l.injective.AllValsetConfirms(ctx, set.Nonce) if err != nil { - return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) + return errors.Wrapf(err, "failed to get validator set confirmations for nonce %d", set.Nonce) } if len(sigs) == 0 { @@ -149,7 +127,7 @@ func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.V } if latestConfirmedValset == nil { - l.Logger().Infoln("no valset to relay") + l.Log().Infoln("no validator set to relay") return nil } @@ -157,58 +135,58 @@ func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.V return nil } - txHash, err := l.Ethereum.SendEthValsetUpdate(ctx, latestEthValset, latestConfirmedValset, confirmations) + txHash, err := l.ethereum.SendEthValsetUpdate(ctx, latestEthValset, latestConfirmedValset, confirmations) if err != nil { return err } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent validator set update to Ethereum") + l.Log().WithField("tx_hash", txHash.Hex()).Infoln("sent validator set update to Ethereum") return nil } func (l *relayer) shouldRelayValset(ctx context.Context, vs *peggytypes.Valset) bool { - latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) + latestEthereumValsetNonce, err := l.ethereum.GetValsetNonce(ctx) if err != nil { - l.Logger().WithError(err).Warningln("failed to get latest valset nonce from Ethereum") + l.Log().WithError(err).Warningln("failed to get latest valset nonce from Ethereum") return false } // Check if other validators already updated the valset if vs.Nonce <= latestEthereumValsetNonce.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": vs.Nonce}).Debugln("valset already updated on Ethereum") + l.Log().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": vs.Nonce}).Debugln("valset already updated on Ethereum") return false } // Check custom time delay offset - block, err := l.Injective.GetBlock(ctx, int64(vs.Height)) + block, err := l.injective.GetBlock(ctx, int64(vs.Height)) if err != nil { - l.Logger().WithError(err).Warningln("unable to get latest block from Injective") + l.Log().WithError(err).Warningln("unable to get latest block from Injective") return false } - if timeElapsed := time.Since(block.Block.Time); timeElapsed <= l.relayValsetOffsetDur { - timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) - l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset not reached yet") + if timeElapsed := time.Since(block.Block.Time); timeElapsed <= l.cfg.RelayValsetOffsetDur { + timeRemaining := time.Duration(int64(l.cfg.RelayValsetOffsetDur) - int64(timeElapsed)) + l.Log().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset not reached yet") return false } - l.Logger().WithFields(log.Fields{"inj_nonce": vs.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") + l.Log().WithFields(log.Fields{"inj_nonce": vs.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") return true } -func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { +func (l *relayer) relayTokenBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() - latestBatches, err := l.Injective.LatestTransactionBatches(ctx) + batches, err := l.injective.LatestTransactionBatches(ctx) if err != nil { return err } - latestEthHeight, err := l.Ethereum.GetHeaderByNumber(ctx, nil) + latestEthHeight, err := l.ethereum.GetHeaderByNumber(ctx, nil) if err != nil { return err } @@ -218,13 +196,13 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va confirmations []*peggytypes.MsgConfirmBatch ) - for _, batch := range latestBatches { + for _, batch := range batches { if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { - l.Logger().WithFields(log.Fields{"batch_nonce": batch.BatchNonce, "batch_timeout_height": batch.BatchTimeout, "latest_eth_height": latestEthHeight.Number.Uint64()}).Debugln("skipping timed out batch") + l.Log().WithFields(log.Fields{"batch_nonce": batch.BatchNonce, "batch_timeout_height": batch.BatchTimeout, "latest_eth_height": latestEthHeight.Number.Uint64()}).Debugln("skipping timed out batch") continue } - sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) + sigs, err := l.injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { return err } @@ -238,7 +216,7 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va } if oldestConfirmedBatch == nil { - l.Logger().Infoln("no batch to relay") + l.Log().Infoln("no batch to relay") return nil } @@ -246,7 +224,7 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va return nil } - txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedBatch, confirmations) + txHash, err := l.ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedBatch, confirmations) if err != nil { // Returning an error here triggers retries which don't help much except risk a binary crash // Better to warn the user and try again in the next loop interval @@ -254,38 +232,38 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va return nil } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent outgoing tx batch to Ethereum") + l.Log().WithField("tx_hash", txHash.Hex()).Infoln("sent outgoing tx batch to Ethereum") return nil } func (l *relayer) shouldRelayBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) bool { - latestEthBatch, err := l.Ethereum.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) + latestEthBatch, err := l.ethereum.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { - l.Logger().WithError(err).Warningf("unable to get latest batch nonce from Ethereum: token_contract=%s", gethcommon.HexToAddress(batch.TokenContract)) + l.Log().WithError(err).Warningf("unable to get latest batch nonce from Ethereum: token_contract=%s", gethcommon.HexToAddress(batch.TokenContract)) return false } // Check if ethereum batch was updated by other validators if batch.BatchNonce <= latestEthBatch.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": batch.BatchNonce}).Debugln("batch already updated on Ethereum") + l.Log().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": batch.BatchNonce}).Debugln("batch already updated on Ethereum") return false } // Check custom time delay offset - blockTime, err := l.Injective.GetBlock(ctx, int64(batch.Block)) + blockTime, err := l.injective.GetBlock(ctx, int64(batch.Block)) if err != nil { - l.Logger().WithError(err).Warningln("unable to get latest block from Injective") + l.Log().WithError(err).Warningln("unable to get latest block from Injective") return false } - if timeElapsed := time.Since(blockTime.Block.Time); timeElapsed <= l.relayBatchOffsetDur { - timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) - l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset not reached yet") + if timeElapsed := time.Since(blockTime.Block.Time); timeElapsed <= l.cfg.RelayBatchOffsetDur { + timeRemaining := time.Duration(int64(l.cfg.RelayBatchOffsetDur) - int64(timeElapsed)) + l.Log().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset not reached yet") return false } - l.Logger().WithFields(log.Fields{"inj_nonce": batch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") + l.Log().WithFields(log.Fields{"inj_nonce": batch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") return true } @@ -296,17 +274,17 @@ func (l *relayer) shouldRelayBatch(ctx context.Context, batch *peggytypes.Outgoi // backwards in time. In the case that the validator set has not been updated for a very long time // this will take longer. func (l *relayer) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { - latestHeader, err := l.Ethereum.GetHeaderByNumber(ctx, nil) + latestHeader, err := l.ethereum.GetHeaderByNumber(ctx, nil) if err != nil { - return nil, errors.Wrap(err, "failed to get latest eth header") + return nil, errors.Wrap(err, "failed to get latest ethereum header") } - latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) + latestEthereumValsetNonce, err := l.ethereum.GetValsetNonce(ctx) if err != nil { return nil, errors.Wrap(err, "failed to get latest valset nonce on Ethereum") } - cosmosValset, err := l.Injective.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) + cosmosValset, err := l.injective.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) if err != nil { return nil, errors.Wrap(err, "failed to get Injective valset") } @@ -321,7 +299,7 @@ func (l *relayer) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset startSearchBlock = currentBlock - findValsetBlocksToSearch } - valsetUpdatedEvents, err := l.Ethereum.GetValsetUpdatedEvents(startSearchBlock, currentBlock) + valsetUpdatedEvents, err := l.ethereum.GetValsetUpdatedEvents(startSearchBlock, currentBlock) if err != nil { return nil, errors.Wrap(err, "failed to filter past ValsetUpdated events from Ethereum") } @@ -428,7 +406,7 @@ type BridgeValidators []*peggytypes.BridgeValidator func (b BridgeValidators) Sort() { sort.Slice(b, func(i, j int) bool { if b[i].Power == b[j].Power { - // Secondary sort on eth address in case powers are equal + // Secondary sort on ethereum address in case powers are equal return util.EthAddrLessThan(b[i].EthereumAddress, b[j].EthereumAddress) } return b[i].Power > b[j].Power diff --git a/orchestrator/signer.go b/orchestrator/signer.go new file mode 100644 index 00000000..4c89658b --- /dev/null +++ b/orchestrator/signer.go @@ -0,0 +1,106 @@ +package orchestrator + +import ( + "context" + + gethcommon "github.com/ethereum/go-ethereum/common" + log "github.com/xlab/suplog" + + "github.com/InjectiveLabs/peggo/orchestrator/loops" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" +) + +// runSigner simply signs off on any batches or validator sets provided by the validator +// since these are provided directly by a trusted Injective node they can simply be assumed to be +// valid and signed off on. +func (s *Orchestrator) runSigner(ctx context.Context, peggyID gethcommon.Hash) error { + signer := signer{ + Orchestrator: s, + peggyID: peggyID, + } + + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting Signer...") + + return loops.RunLoop(ctx, defaultLoopDur, func() error { + return signer.sign(ctx) + }) +} + +type signer struct { + *Orchestrator + peggyID gethcommon.Hash +} + +func (l *signer) Log() log.Logger { + return l.logger.WithField("loop", "Signer") +} + +func (l *signer) sign(ctx context.Context) error { + if err := l.signValidatorSets(ctx); err != nil { + return err + } + + if err := l.signNewBatch(ctx); err != nil { + return err + } + + return nil +} + +func (l *signer) signValidatorSets(ctx context.Context) error { + var valsets []*peggytypes.Valset + fn := func() error { + valsets, _ = l.injective.OldestUnsignedValsets(ctx, l.cfg.CosmosAddr) + return nil + } + + if err := l.retry(ctx, fn); err != nil { + return err + } + + if len(valsets) == 0 { + l.Log().Infoln("no validator set to confirm") + return nil + } + + for _, vs := range valsets { + if err := l.retry(ctx, func() error { + return l.injective.SendValsetConfirm(ctx, l.cfg.EthereumAddr, l.peggyID, vs) + }); err != nil { + return err + } + + l.Log().WithFields(log.Fields{"valset_nonce": vs.Nonce, "validators": len(vs.Members)}).Infoln("confirmed valset update on Injective") + } + + return nil +} + +func (l *signer) signNewBatch(ctx context.Context) error { + var oldestUnsignedBatch *peggytypes.OutgoingTxBatch + getBatchFn := func() error { + oldestUnsignedBatch, _ = l.injective.OldestUnsignedTransactionBatch(ctx, l.cfg.CosmosAddr) + return nil + } + + if err := l.retry(ctx, getBatchFn); err != nil { + return err + } + + if oldestUnsignedBatch == nil { + l.Log().Infoln("no batch to confirm") + return nil + } + + confirmBatchFn := func() error { + return l.injective.SendBatchConfirm(ctx, l.cfg.EthereumAddr, l.peggyID, oldestUnsignedBatch) + } + + if err := l.retry(ctx, confirmBatchFn); err != nil { + return err + } + + l.Log().WithFields(log.Fields{"token_contract": oldestUnsignedBatch.TokenContract, "batch_nonce": oldestUnsignedBatch.BatchNonce, "txs": len(oldestUnsignedBatch.Transactions)}).Infoln("confirmed batch on Injective") + + return nil +} diff --git a/solidity/wrappers/CosmosToken.sol/wrapper.go b/solidity/wrappers/CosmosToken.sol/wrapper.go index c6e29871..c194085b 100644 --- a/solidity/wrappers/CosmosToken.sol/wrapper.go +++ b/solidity/wrappers/CosmosToken.sol/wrapper.go @@ -33,7 +33,7 @@ const ContextABI = "[]" type Context struct { ContextCaller // Read-only binding to the contract ContextTransactor // Write-only binding to the contract - ContextFilterer // Log filterer for contract events + ContextFilterer // log filterer for contract events } // ContextCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -210,7 +210,7 @@ func DeployCosmosERC20(auth *bind.TransactOpts, backend bind.ContractBackend, pe type CosmosERC20 struct { CosmosERC20Caller // Read-only binding to the contract CosmosERC20Transactor // Write-only binding to the contract - CosmosERC20Filterer // Log filterer for contract events + CosmosERC20Filterer // log filterer for contract events } // CosmosERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -646,7 +646,7 @@ type CosmosERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -800,7 +800,7 @@ type CosmosERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -986,7 +986,7 @@ func DeployERC20(auth *bind.TransactOpts, backend bind.ContractBackend, name_ st type ERC20 struct { ERC20Caller // Read-only binding to the contract ERC20Transactor // Write-only binding to the contract - ERC20Filterer // Log filterer for contract events + ERC20Filterer // log filterer for contract events } // ERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -1422,7 +1422,7 @@ type ERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1576,7 +1576,7 @@ type ERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1740,7 +1740,7 @@ var IERC20FuncSigs = map[string]string{ type IERC20 struct { IERC20Caller // Read-only binding to the contract IERC20Transactor // Write-only binding to the contract - IERC20Filterer // Log filterer for contract events + IERC20Filterer // log filterer for contract events } // IERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -2041,7 +2041,7 @@ type IERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2195,7 +2195,7 @@ type IERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2362,7 +2362,7 @@ var IERC20MetadataFuncSigs = map[string]string{ type IERC20Metadata struct { IERC20MetadataCaller // Read-only binding to the contract IERC20MetadataTransactor // Write-only binding to the contract - IERC20MetadataFilterer // Log filterer for contract events + IERC20MetadataFilterer // log filterer for contract events } // IERC20MetadataCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -2756,7 +2756,7 @@ type IERC20MetadataApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2910,7 +2910,7 @@ type IERC20MetadataTransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration diff --git a/solidity/wrappers/HashingTest.sol/wrapper.go b/solidity/wrappers/HashingTest.sol/wrapper.go index 7432287e..94d3f710 100644 --- a/solidity/wrappers/HashingTest.sol/wrapper.go +++ b/solidity/wrappers/HashingTest.sol/wrapper.go @@ -63,7 +63,7 @@ func DeployHashingTest(auth *bind.TransactOpts, backend bind.ContractBackend) (c type HashingTest struct { HashingTestCaller // Read-only binding to the contract HashingTestTransactor // Write-only binding to the contract - HashingTestFilterer // Log filterer for contract events + HashingTestFilterer // log filterer for contract events } // HashingTestCaller is an auto generated read-only Go binding around an Ethereum contract. diff --git a/solidity/wrappers/Peggy.sol/wrapper.go b/solidity/wrappers/Peggy.sol/wrapper.go index 39adbf4b..159251b8 100644 --- a/solidity/wrappers/Peggy.sol/wrapper.go +++ b/solidity/wrappers/Peggy.sol/wrapper.go @@ -59,7 +59,7 @@ func DeployAddress(auth *bind.TransactOpts, backend bind.ContractBackend) (commo type Address struct { AddressCaller // Read-only binding to the contract AddressTransactor // Write-only binding to the contract - AddressFilterer // Log filterer for contract events + AddressFilterer // log filterer for contract events } // AddressCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -204,7 +204,7 @@ const ContextABI = "[]" type Context struct { ContextCaller // Read-only binding to the contract ContextTransactor // Write-only binding to the contract - ContextFilterer // Log filterer for contract events + ContextFilterer // log filterer for contract events } // ContextCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -349,7 +349,7 @@ const ContextUpgradeableABI = "[]" type ContextUpgradeable struct { ContextUpgradeableCaller // Read-only binding to the contract ContextUpgradeableTransactor // Write-only binding to the contract - ContextUpgradeableFilterer // Log filterer for contract events + ContextUpgradeableFilterer // log filterer for contract events } // ContextUpgradeableCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -526,7 +526,7 @@ func DeployCosmosERC20(auth *bind.TransactOpts, backend bind.ContractBackend, pe type CosmosERC20 struct { CosmosERC20Caller // Read-only binding to the contract CosmosERC20Transactor // Write-only binding to the contract - CosmosERC20Filterer // Log filterer for contract events + CosmosERC20Filterer // log filterer for contract events } // CosmosERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -962,7 +962,7 @@ type CosmosERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1116,7 +1116,7 @@ type CosmosERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1302,7 +1302,7 @@ func DeployERC20(auth *bind.TransactOpts, backend bind.ContractBackend, name_ st type ERC20 struct { ERC20Caller // Read-only binding to the contract ERC20Transactor // Write-only binding to the contract - ERC20Filterer // Log filterer for contract events + ERC20Filterer // log filterer for contract events } // ERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -1738,7 +1738,7 @@ type ERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1892,7 +1892,7 @@ type ERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2056,7 +2056,7 @@ var IERC20FuncSigs = map[string]string{ type IERC20 struct { IERC20Caller // Read-only binding to the contract IERC20Transactor // Write-only binding to the contract - IERC20Filterer // Log filterer for contract events + IERC20Filterer // log filterer for contract events } // IERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -2357,7 +2357,7 @@ type IERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2511,7 +2511,7 @@ type IERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2678,7 +2678,7 @@ var IERC20MetadataFuncSigs = map[string]string{ type IERC20Metadata struct { IERC20MetadataCaller // Read-only binding to the contract IERC20MetadataTransactor // Write-only binding to the contract - IERC20MetadataFilterer // Log filterer for contract events + IERC20MetadataFilterer // log filterer for contract events } // IERC20MetadataCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -3072,7 +3072,7 @@ type IERC20MetadataApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -3226,7 +3226,7 @@ type IERC20MetadataTransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -3380,7 +3380,7 @@ const InitializableABI = "[]" type Initializable struct { InitializableCaller // Read-only binding to the contract InitializableTransactor // Write-only binding to the contract - InitializableFilterer // Log filterer for contract events + InitializableFilterer // log filterer for contract events } // InitializableCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -3535,7 +3535,7 @@ var OwnableUpgradeableWithExpiryFuncSigs = map[string]string{ type OwnableUpgradeableWithExpiry struct { OwnableUpgradeableWithExpiryCaller // Read-only binding to the contract OwnableUpgradeableWithExpiryTransactor // Write-only binding to the contract - OwnableUpgradeableWithExpiryFilterer // Log filterer for contract events + OwnableUpgradeableWithExpiryFilterer // log filterer for contract events } // OwnableUpgradeableWithExpiryCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -3836,7 +3836,7 @@ type OwnableUpgradeableWithExpiryOwnershipTransferredIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -3994,7 +3994,7 @@ var PausableFuncSigs = map[string]string{ type Pausable struct { PausableCaller // Read-only binding to the contract PausableTransactor // Write-only binding to the contract - PausableFilterer // Log filterer for contract events + PausableFilterer // log filterer for contract events } // PausableCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -4170,7 +4170,7 @@ type PausablePausedIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -4304,7 +4304,7 @@ type PausableUnpausedIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -4481,7 +4481,7 @@ func DeployPeggy(auth *bind.TransactOpts, backend bind.ContractBackend) (common. type Peggy struct { PeggyCaller // Read-only binding to the contract PeggyTransactor // Write-only binding to the contract - PeggyFilterer // Log filterer for contract events + PeggyFilterer // log filterer for contract events } // PeggyCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -5208,7 +5208,7 @@ type PeggyERC20DeployedEventIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -5357,7 +5357,7 @@ type PeggyOwnershipTransferredIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -5510,7 +5510,7 @@ type PeggyPausedIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -5644,7 +5644,7 @@ type PeggySendToCosmosEventIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -5808,7 +5808,7 @@ type PeggySendToInjectiveEventIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -5973,7 +5973,7 @@ type PeggyTransactionBatchExecutedEventIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -6127,7 +6127,7 @@ type PeggyUnpausedIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -6261,7 +6261,7 @@ type PeggyValsetUpdatedEventIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -6410,7 +6410,7 @@ const ReentrancyGuardABI = "[]" type ReentrancyGuard struct { ReentrancyGuardCaller // Read-only binding to the contract ReentrancyGuardTransactor // Write-only binding to the contract - ReentrancyGuardFilterer // Log filterer for contract events + ReentrancyGuardFilterer // log filterer for contract events } // ReentrancyGuardCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -6572,7 +6572,7 @@ func DeploySafeERC20(auth *bind.TransactOpts, backend bind.ContractBackend) (com type SafeERC20 struct { SafeERC20Caller // Read-only binding to the contract SafeERC20Transactor // Write-only binding to the contract - SafeERC20Filterer // Log filterer for contract events + SafeERC20Filterer // log filterer for contract events } // SafeERC20Caller is an auto generated read-only Go binding around an Ethereum contract. diff --git a/solidity/wrappers/TestERC20.sol/wrapper.go b/solidity/wrappers/TestERC20.sol/wrapper.go index c12dafe5..003b0b54 100644 --- a/solidity/wrappers/TestERC20.sol/wrapper.go +++ b/solidity/wrappers/TestERC20.sol/wrapper.go @@ -33,7 +33,7 @@ const ContextABI = "[]" type Context struct { ContextCaller // Read-only binding to the contract ContextTransactor // Write-only binding to the contract - ContextFilterer // Log filterer for contract events + ContextFilterer // log filterer for contract events } // ContextCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -210,7 +210,7 @@ func DeployERC20(auth *bind.TransactOpts, backend bind.ContractBackend, name_ st type ERC20 struct { ERC20Caller // Read-only binding to the contract ERC20Transactor // Write-only binding to the contract - ERC20Filterer // Log filterer for contract events + ERC20Filterer // log filterer for contract events } // ERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -646,7 +646,7 @@ type ERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -800,7 +800,7 @@ type ERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -964,7 +964,7 @@ var IERC20FuncSigs = map[string]string{ type IERC20 struct { IERC20Caller // Read-only binding to the contract IERC20Transactor // Write-only binding to the contract - IERC20Filterer // Log filterer for contract events + IERC20Filterer // log filterer for contract events } // IERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -1265,7 +1265,7 @@ type IERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1419,7 +1419,7 @@ type IERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -1586,7 +1586,7 @@ var IERC20MetadataFuncSigs = map[string]string{ type IERC20Metadata struct { IERC20MetadataCaller // Read-only binding to the contract IERC20MetadataTransactor // Write-only binding to the contract - IERC20MetadataFilterer // Log filterer for contract events + IERC20MetadataFilterer // log filterer for contract events } // IERC20MetadataCaller is an auto generated read-only Go binding around an Ethereum contract. @@ -1980,7 +1980,7 @@ type IERC20MetadataApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2134,7 +2134,7 @@ type IERC20MetadataTransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2320,7 +2320,7 @@ func DeployTestERC20(auth *bind.TransactOpts, backend bind.ContractBackend) (com type TestERC20 struct { TestERC20Caller // Read-only binding to the contract TestERC20Transactor // Write-only binding to the contract - TestERC20Filterer // Log filterer for contract events + TestERC20Filterer // log filterer for contract events } // TestERC20Caller is an auto generated read-only Go binding around an Ethereum contract. @@ -2756,7 +2756,7 @@ type TestERC20ApprovalIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration @@ -2910,7 +2910,7 @@ type TestERC20TransferIterator struct { contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data - logs chan types.Log // Log channel receiving the found contract events + logs chan types.Log // log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration From b1b39ad272795c25ed050004a523328f923c17fd Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 3 Jun 2024 15:52:13 +0200 Subject: [PATCH 15/21] update scripts --- test/cosmos/multinode.sh | 28 ++++++----- test/peggo/deploy_bridge.sh | 97 ++++++++++++------------------------ test/peggo/peggy_params.json | 2 +- 3 files changed, 49 insertions(+), 78 deletions(-) diff --git a/test/cosmos/multinode.sh b/test/cosmos/multinode.sh index cc17e45f..5073ca36 100755 --- a/test/cosmos/multinode.sh +++ b/test/cosmos/multinode.sh @@ -147,6 +147,8 @@ if [[ ! -d "$hdir" ]]; then echo "NOTE: Setting Governance Voting Period to 10 seconds for rapid testing" cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["voting_params"]["voting_period"]="10s"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["params"]["voting_period"]="10s"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json + cat $n0cfgDir/genesis.json | jq '.app_state["gov"]["params"]["expedited_voting_period"]="9s"' > $n0cfgDir/tmp_genesis.json && mv $n0cfgDir/tmp_genesis.json $n0cfgDir/genesis.json + # Mint Watever tokens for each validator @@ -194,22 +196,22 @@ if [[ ! -d "$hdir" ]]; then cp $n0cfgDir/genesis.json $n2cfgDir/genesis.json # Create gentxs and collect them in n0 - $NODE_BIN $home0 gentx $VAL0_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid - $NODE_BIN $home1 gentx $VAL1_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid &>/dev/null - $NODE_BIN $home2 gentx $VAL2_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid &>/dev/null + $NODE_BIN $home0 genesis gentx $VAL0_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid + $NODE_BIN $home1 genesis gentx $VAL1_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid &>/dev/null + $NODE_BIN $home2 genesis gentx $VAL2_KEY "1000$SCALE_FACTOR$STAKE_DENOM" $kbt $cid &>/dev/null cp $n1cfgDir/gentx/*.json $n0cfgDir/gentx/ cp $n2cfgDir/gentx/*.json $n0cfgDir/gentx/ - $NODE_BIN $home0 collect-gentxs &>/dev/null + $NODE_BIN $home0 genesis collect-gentxs &>/dev/null # Copy genesis file into n1 and n2s cp $n0cfgDir/genesis.json $n1cfgDir/genesis.json cp $n0cfgDir/genesis.json $n2cfgDir/genesis.json # Run this to ensure everything worked and that the genesis file is setup correctly - $NODE_BIN $home0 validate-genesis - $NODE_BIN $home1 validate-genesis - $NODE_BIN $home2 validate-genesis + $NODE_BIN $home0 genesis validate + $NODE_BIN $home1 genesis validate + $NODE_BIN $home2 genesis validate # Actually a cross-platform solution, sed is rubbish # Example usage: $REGEX_REPLACE 's/^param = ".*?"/param = "100"/' config.toml @@ -261,14 +263,14 @@ fi # data dir check # Start the instances echo "Starting injectived nodes..." -echo $NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" --grpc-web.address "0.0.0.0:9080" -$NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" --grpc-web.address "0.0.0.0:9080" > $hdir.n0.log 2>&1 & +echo $NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" +$NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" > $hdir.n0.log 2>&1 & -echo $NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" --grpc-web.address "0.0.0.0:9081" -$NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" --grpc-web.address "0.0.0.0:9081" > $hdir.n1.log 2>&1 & +echo $NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" +$NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" > $hdir.n1.log 2>&1 & -echo $NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" --grpc-web.address "0.0.0.0:9082" -$NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" --grpc-web.address "0.0.0.0:9082" > $hdir.n2.log 2>&1 & +echo $NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" +$NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" > $hdir.n2.log 2>&1 & diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh index cfbf2e6b..18926352 100755 --- a/test/peggo/deploy_bridge.sh +++ b/test/peggo/deploy_bridge.sh @@ -10,65 +10,41 @@ POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" VALIDATOR_POWERS="${VALIDATOR_POWERS:-1431655765,1431655765,1431655765}" -echo "** Deploying Peggy contract suite **" +echo "*** Deploying Peggy contract suite ***" +echo " PEGGY_ID=$PEGGY_ID" +echo " POWER_THRESHOLD=$POWER_THRESHOLD" +echo " VALIDATOR_ADDRESSES=$VALIDATOR_ADDRESSES" +echo " VALIDATOR_POWERS=$VALIDATOR_POWERS" +echo -e "\n" + deployer_pk=$(cat ../ethereum/geth/clique_signer.key) peggy_contract_path="../../solidity/contracts/Peggy.sol" -peggy_admin_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" +admin_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" upgradeable_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" cosmos_coin_contract_path="../../solidity/contracts/CosmosToken.sol" -echo "Using PEGGY_ID $PEGGY_ID" -echo "Using POWER_THRESHOLD $POWER_THRESHOLD" -echo "Using VALIDATOR_ADDRESSES $VALIDATOR_ADDRESSES" -echo "Using VALIDATOR_POWERS $VALIDATOR_POWERS" -echo -e "\n" -peggy_impl_address=$(etherman --name Peggy \ - --source $peggy_contract_path \ - -P "$deployer_pk" \ - deploy) -echo "Deployed Peggy implementation contract: $peggy_impl_address" - -peggy_init_data=$(etherman --name Peggy \ - --source $peggy_contract_path \ - -P "$deployer_pk" \ - tx --bytecode "$peggy_impl_address" \ - initialize "$PEGGY_ID" "$POWER_THRESHOLD" "$VALIDATOR_ADDRESSES" "$VALIDATOR_POWERS") -echo "Initialized Peggy implementation contract. Init data:" -echo "$peggy_init_data" - -proxy_admin_address=$(etherman --name ProxyAdmin \ - -P "$deployer_pk" \ - --source "$peggy_admin_proxy_contract_path" \ - deploy) -echo "Deployed ProxyAdmin contract for Peggy: $proxy_admin_address" - -peggy_proxy_address=$(etherman --name TransparentUpgradeableProxy \ - --source "$upgradeable_proxy_contract_path" \ - -P "$deployer_pk" \ - deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") -echo "Deployed TransparentUpgradeableProxy for $peggy_impl_address (Peggy) with $proxy_admin_address (ProxyAdmin) as the admin" - - -# get the block number early so Oracle can catch the first event by Peggy.sol +# get the bridge_contract_start_height early so orchestrators can catch the first Valset Updated event from Peggy.sol peggy_block_number=$(curl http://localhost:8545 \ -X POST \ -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0", "method":"eth_getBlockByNumber","params":["latest", true]}' 2>/dev/null \ | python3 -c "import sys, json; print(int(json.load(sys.stdin)['result']['number'], 0))") -coin_contract_address=$(etherman --name CosmosERC20 \ - -P "$deployer_pk" \ - --source "$cosmos_coin_contract_path" \ - deploy "$peggy_proxy_address" "Injective" "inj" 18) -echo "Deployed Cosmos Coin contract: $coin_contract_address" - -echo "Peggy deployment done!" -echo " * Contract address: $peggy_proxy_address" -echo " * Contract deployment height: $peggy_block_number" -echo -e "=======================\n" +peggy_impl_address=$(etherman --name Peggy --source $peggy_contract_path -P "$deployer_pk" deploy) +peggy_init_data=$(etherman --name Peggy --source $peggy_contract_path -P "$deployer_pk" tx --bytecode "$peggy_impl_address" initialize "$PEGGY_ID" "$POWER_THRESHOLD" "$VALIDATOR_ADDRESSES" "$VALIDATOR_POWERS") +proxy_admin_address=$(etherman --name ProxyAdmin -P "$deployer_pk" --source "$admin_proxy_contract_path" deploy) +peggy_proxy_address=$(etherman --name TransparentUpgradeableProxy --source "$upgradeable_proxy_contract_path" -P "$deployer_pk" deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") +coin_contract_address=$(etherman --name CosmosERC20 -P "$deployer_pk" --source "$cosmos_coin_contract_path" deploy "$peggy_proxy_address" "Injective" "inj" 18) + +echo "Peggy.sol: $peggy_impl_address" +echo "ProxyAdmin.sol: $proxy_admin_address" +echo "TransparentUpgradeableProxy.sol: $peggy_proxy_address" +echo "Injective token: $coin_contract_address" +echo "Contract suite deployment done!" +echo -e "\n" sleep 2 @@ -101,23 +77,21 @@ resp_check() { echo "Submitting gov proposal for Peggy module params update..." cat $peggy_params_json +#resp="$(yes $PASSPHRASE | injectived tx gov submit-proposal $peggy_params_json --home $n0_home_dir --from user --gas 2000000 --gas-prices 500000000inj $TX_OPTS)" +#resp_check "$resp" "Failed to submit gov proposal" + resp="$(yes $PASSPHRASE | injectived tx gov submit-proposal $peggy_params_json --home $n0_home_dir --from user --gas 2000000 --gas-prices 500000000inj $TX_OPTS)" -resp_check "$resp" "Failed to submit gov proposal" +echo "$resp" sleep 2 current_proposal_id=$(curl 'http://localhost:10337/cosmos/gov/v1beta1/proposals?proposal_status=0&pagination.limit=1&pagination.reverse=true' 2>/dev/null | jq -r '.proposals[].proposal_id') -resp="$(yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n0_home_dir --from val --gas-prices 500000000inj $TX_OPTS)" -resp_check "$resp" "val0 failed to vote on gov proposal" - -resp="$(yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n1_home_dir --from val --gas-prices 500000000inj $TX_OPTS)" -resp_check "$resp" "val1 failed to vote on gov proposal" - -resp="$(yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n2_home_dir --from val --gas-prices 500000000inj $TX_OPTS)" -resp_check "$resp" "val2 failed to vote on gov proposal" +yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n0_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null +yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n1_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null +yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n2_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null -echo -n "Waiting for proposal to pass..." +echo -n "Waiting for proposal to pass... " sleep 8 echo "DONE" @@ -129,16 +103,11 @@ n0_eth_addr="0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4" n1_eth_addr="0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c" n2_eth_addr="0x8B094eD440900CEB75B83A22eD8A2C7582B442C2" -resp="$(injectived tx peggy set-orchestrator-address $n0_inj_addr $n0_inj_addr $n0_eth_addr --home $n0_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS)" -resp_check "$resp" "val0 failed to register orchestrator address" - -resp="$(injectived tx peggy set-orchestrator-address $n1_inj_addr $n1_inj_addr $n1_eth_addr --home $n1_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS)" -resp_check "$resp" "val1 failed to register orchestrator address" - -resp="$(injectived tx peggy set-orchestrator-address $n2_inj_addr $n2_inj_addr $n2_eth_addr --home $n2_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS)" -resp_check "$resp" "val2 failed to register orchestrator address" +injectived tx peggy set-orchestrator-address $n0_inj_addr $n0_inj_addr $n0_eth_addr --home $n0_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null +injectived tx peggy set-orchestrator-address $n1_inj_addr $n1_inj_addr $n1_eth_addr --home $n1_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null +injectived tx peggy set-orchestrator-address $n2_inj_addr $n2_inj_addr $n2_eth_addr --home $n2_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null -echo -n "Registering orchestrator ETH addresses..." +echo -n "Registering orchestrator ETH addresses... " sleep 2 echo "DONE" diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index 3c952d37..09cabba6 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -23,7 +23,7 @@ "cosmos_coin_denom": "inj", "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", "claim_slashing_enabled": false, - "bridge_contract_start_height": "20", + "bridge_contract_start_height": "12", "valset_reward": { "denom": "inj", "amount": "0" From 2e2d7d8eb3d25e316e70188f06416b832f48dcd3 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 16 Oct 2024 13:12:51 +0200 Subject: [PATCH 16/21] remove cosmos token mint hack --- solidity/contracts/CosmosToken.sol | 2 -- test/peggo/deploy_bridge.sh | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/solidity/contracts/CosmosToken.sol b/solidity/contracts/CosmosToken.sol index d469ab6d..914b37cd 100644 --- a/solidity/contracts/CosmosToken.sol +++ b/solidity/contracts/CosmosToken.sol @@ -14,8 +14,6 @@ contract CosmosERC20 is ERC20, Ownable { uint8 decimals_ ) ERC20(name_, symbol_) { _decimals = decimals_; - // mint all the tokens to the peggy proxy address (normally this happens on Etherscan) - _mint(0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe, 100_000_000 * 10 ** 18); } function decimals() public view virtual override returns (uint8) { diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh index 6d84eb1d..a7248f9a 100755 --- a/test/peggo/deploy_bridge.sh +++ b/test/peggo/deploy_bridge.sh @@ -9,6 +9,7 @@ PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d706567677969640000000000000000000000 POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" VALIDATOR_POWERS="${VALIDATOR_POWERS:-1431655765,1431655765,1431655765}" +DEPLOYER_ADDR=0xbbdf3283d1cf510c17b4ffa1b900f444be4a4a4e echo "*** Deploying Peggy contract suite ***" echo " PEGGY_ID=$PEGGY_ID" @@ -48,6 +49,10 @@ peggy_proxy_address=$(etherman --name TransparentUpgradeableProxy --source "$upg echo "Deploying Injective (CosmosERC20) token ..." coin_contract_address=$(etherman --name CosmosERC20 -P "$deployer_pk" --source "$cosmos_coin_contract_path" deploy "Injective" "inj" 18) +echo "Minting 100_000_000 Injective tokens to Peggy proxy ..." +etherman -P "$deployer_pk" --name CosmosERC20 --source "$cosmos_coin_contract_path" tx "$coin_contract_address" mint "$peggy_proxy_address" 100000000000000000000000000 + + echo "Done!" echo "Peggy.sol: $peggy_impl_address" From a711f2676e2ffa8d45540625ea6aa8973b550935 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 16 Oct 2024 16:40:22 +0200 Subject: [PATCH 17/21] chore: update test scripts --- test/peggo/deploy_bridge.sh | 90 +++++++++++++---------- test/peggo/deploy_peggy_contract_suite.sh | 69 +++++++++++++++++ test/run.sh | 2 + 3 files changed, 123 insertions(+), 38 deletions(-) create mode 100755 test/peggo/deploy_peggy_contract_suite.sh diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh index a7248f9a..8fb6828b 100755 --- a/test/peggo/deploy_bridge.sh +++ b/test/peggo/deploy_bridge.sh @@ -4,28 +4,42 @@ set -e cd "${0%/*}" # cd in the script dir -# bytes32 encoding of "injective-peggyid". See peggy_params.json -PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d70656767796964000000000000000000000000000000}" -POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" -VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" -VALIDATOR_POWERS="${VALIDATOR_POWERS:-1431655765,1431655765,1431655765}" -DEPLOYER_ADDR=0xbbdf3283d1cf510c17b4ffa1b900f444be4a4a4e - -echo "*** Deploying Peggy contract suite ***" -echo " PEGGY_ID=$PEGGY_ID" -echo " POWER_THRESHOLD=$POWER_THRESHOLD" -echo " VALIDATOR_ADDRESSES=$VALIDATOR_ADDRESSES" -echo " VALIDATOR_POWERS=$VALIDATOR_POWERS" echo -e "\n" - - -deployer_pk=$(cat ../ethereum/geth/clique_signer.key) - -peggy_contract_path="../../solidity/contracts/Peggy.sol" -admin_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" -upgradeable_proxy_contract_path="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" -cosmos_coin_contract_path="../../solidity/contracts/CosmosToken.sol" - +echo "*** Peggy Contract Suite deployment ***" + +# Initial Validator Set on Injective +PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d70656767796964000000000000000000000000000000}" # bytes32 encoding of "injective-peggyid" +POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" # how to get: 2/3 of total validator power on Injective + +VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-\ +0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,\ +0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,\ +0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" + +VALIDATOR_POWERS="${VALIDATOR_POWERS:-\ +1431655765,\ +1431655765,\ +1431655765}" + +PEGGY_INIT_ARGS="$PEGGY_ID $POWER_THRESHOLD $VALIDATOR_ADDRESSES $VALIDATOR_POWERS" + +# Peggy contracts +PEGGY_CONTRACT_PATH="../../solidity/contracts/Peggy.sol" +PROXY_ADMIN_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" +PROXY_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" +COSMOS_TOKEN_CONTRACT_PATH="../../solidity/contracts/CosmosToken.sol" +COSMOS_TOKEN_DEPLOY_ARGS="Injective inj 18" +COSMOS_TOKEN_MAX_AMOUNT=100000000000000000000000000 # 100 million tokens that will be minted straight to Peggy proxy + +# Ethereum +DEPLOYER_PK=$(cat ../ethereum/geth/clique_signer.key) +ETH_ENDPOINT="http://localhost:8545" + +TX_OPTS="-P $DEPLOYER_PK --endpoint $ETH_ENDPOINT" +COSMOS_TOKEN_OPTS="$TX_OPTS --name CosmosERC20 --source $COSMOS_TOKEN_CONTRACT_PATH" +PEGGY_OPTS="$TX_OPTS --name Peggy --source $PEGGY_CONTRACT_PATH" +PROXY_ADMIN_OPTS="$TX_OPTS --name ProxyAdmin --source $PROXY_ADMIN_CONTRACT_PATH" +PEGGY_PROXY_OPTS="$TX_OPTS --name TransparentUpgradeableProxy --source $PROXY_CONTRACT_PATH" # get the bridge_contract_start_height early so orchestrators can catch the first Valset Updated event from Peggy.sol peggy_block_number=$(curl http://localhost:8545 \ @@ -34,32 +48,32 @@ peggy_block_number=$(curl http://localhost:8545 \ -d '{"id":1,"jsonrpc":"2.0", "method":"eth_getBlockByNumber","params":["latest", true]}' 2>/dev/null \ | python3 -c "import sys, json; print(int(json.load(sys.stdin)['result']['number'], 0))") -echo "Deploying Peggy contract ..." -peggy_impl_address=$(etherman --name Peggy --source $peggy_contract_path -P "$deployer_pk" deploy) -echo "Initializing Peggy contract ..." -peggy_init_data=$(etherman --name Peggy --source $peggy_contract_path -P "$deployer_pk" tx --bytecode "$peggy_impl_address" initialize "$PEGGY_ID" "$POWER_THRESHOLD" "$VALIDATOR_ADDRESSES" "$VALIDATOR_POWERS") +echo "Deploying Peggy.sol ..." +peggy_impl_address=$(etherman $PEGGY_OPTS deploy) -echo "Deploying ProxyAdmin contract ..." -proxy_admin_address=$(etherman --name ProxyAdmin -P "$deployer_pk" --source "$admin_proxy_contract_path" deploy) +echo "Initializing Peggy.sol ..." +peggy_init_data=$(etherman $PEGGY_OPTS tx --bytecode "$peggy_impl_address" initialize $PEGGY_INIT_ARGS) -echo "Deploying TransparentUpgradeableProxy contract ..." -peggy_proxy_address=$(etherman --name TransparentUpgradeableProxy --source "$upgradeable_proxy_contract_path" -P "$deployer_pk" deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") +echo "Deploying ProxyAdmin.sol ..." +proxy_admin_address=$(etherman $PROXY_ADMIN_OPTS deploy) -echo "Deploying Injective (CosmosERC20) token ..." -coin_contract_address=$(etherman --name CosmosERC20 -P "$deployer_pk" --source "$cosmos_coin_contract_path" deploy "Injective" "inj" 18) +echo "Deploying TransparentUpgradeableProxy.sol ..." +peggy_proxy_address=$(etherman $PEGGY_PROXY_OPTS deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") -echo "Minting 100_000_000 Injective tokens to Peggy proxy ..." -etherman -P "$deployer_pk" --name CosmosERC20 --source "$cosmos_coin_contract_path" tx "$coin_contract_address" mint "$peggy_proxy_address" 100000000000000000000000000 +echo "Deploying Injective (CosmosERC20.sol) token ..." +coin_contract_address=$(etherman $COSMOS_TOKEN_OPTS deploy $COSMOS_TOKEN_DEPLOY_ARGS) +echo "Minting 100_000_000 Injective tokens to Peggy.sol proxy ..." +etherman $COSMOS_TOKEN_OPTS tx "$coin_contract_address" mint "$peggy_proxy_address" $COSMOS_TOKEN_MAX_AMOUNT echo "Done!" -echo "Peggy.sol: $peggy_impl_address" -echo "ProxyAdmin.sol: $proxy_admin_address" -echo "TransparentUpgradeableProxy.sol: $peggy_proxy_address" -echo "Injective token: $coin_contract_address" -echo "Contract suite deployment done!" +echo "Contract addresses:" +echo " * $peggy_impl_address Peggy.sol" +echo " * $proxy_admin_address ProxyAdmin.sol:" +echo " * $peggy_proxy_address TransparentUpgradeableProxy.sol" +echo " * $coin_contract_address Injective token" echo -e "\n" sleep 2 diff --git a/test/peggo/deploy_peggy_contract_suite.sh b/test/peggo/deploy_peggy_contract_suite.sh new file mode 100755 index 00000000..48d72d4c --- /dev/null +++ b/test/peggo/deploy_peggy_contract_suite.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +echo "*** Peggy Contract Suite deployment ***" + +### USER PROVIDED + +# Initial Validator Set on Injective +PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d70656767796964000000000000000000000000000000}" # bytes32 encoding of "injective-peggyid" +POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" # how to get: 2/3 of total validator power on Injective +VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" +VALIDATOR_POWERS="${VALIDATOR_POWERS:-1431655765,1431655765,1431655765}" + +# Peggy contracts +PEGGY_CONTRACT_PATH="../../solidity/contracts/Peggy.sol" +PROXY_ADMIN_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" +PROXY_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" +COSMOS_TOKEN_CONTRACT_PATH="../../solidity/contracts/CosmosToken.sol" +COSMOS_TOKEN_DEPLOY_ARGS="Nasud nas 18" +COSMOS_TOKEN_MAX_AMOUNT=100000000000000000000000000 # 100 million tokens that will be minted straight to Peggy proxy + +# Ethereum +DEPLOYER_PK="b8fe92b390dc8a830a9544be585bf87a4b1aa318beb629a8d60e0d83fa68eb72" +ETH_ENDPOINT="https://eth-sepolia.g.alchemy.com/v2/VranSAqL6UIW7YSsj_5mkHg7UMMHP6jR" + +### DEPLOYMENT + +PEGGY_INIT_ARGS="$PEGGY_ID $POWER_THRESHOLD $VALIDATOR_ADDRESSES $VALIDATOR_POWERS" +TX_OPTS="-P $DEPLOYER_PK --endpoint $ETH_ENDPOINT --tx-timeout 60s" +COSMOS_TOKEN_OPTS="$TX_OPTS --name CosmosERC20 --source $COSMOS_TOKEN_CONTRACT_PATH" +PEGGY_OPTS="$TX_OPTS --name Peggy --source $PEGGY_CONTRACT_PATH" +PROXY_ADMIN_OPTS="$TX_OPTS --name ProxyAdmin --source $PROXY_ADMIN_CONTRACT_PATH" +PEGGY_PROXY_OPTS="$TX_OPTS --name TransparentUpgradeableProxy --source $PROXY_CONTRACT_PATH" + +echo "Deploying Peggy.sol ..." +peggy_address=$(etherman $PEGGY_OPTS deploy) +echo "$peggy_address" + +echo "Initializing Peggy.sol ..." +peggy_init_data=$(etherman $PEGGY_OPTS tx --bytecode "$peggy_address" initialize $PEGGY_INIT_ARGS) +echo "$peggy_init_data" + +echo "Deploying ProxyAdmin.sol ..." +proxy_admin_address=$(etherman $PROXY_ADMIN_OPTS deploy) +echo "$proxy_admin_address" + +echo "Deploying TransparentUpgradeableProxy.sol ..." +peggy_proxy_address=$(etherman $PEGGY_PROXY_OPTS deploy "$peggy_address" "$proxy_admin_address" "$peggy_init_data") +echo "$peggy_proxy_address" + + +echo "Deploying Injective (CosmosERC20.sol) token ..." +cosmos_coin_address=$(etherman $COSMOS_TOKEN_OPTS deploy $COSMOS_TOKEN_DEPLOY_ARGS) +echo "$cosmos_coin_address" + +echo "Minting 100_000_000 Injective tokens to Peggy.sol proxy ..." +etherman $COSMOS_TOKEN_OPTS tx "$cosmos_coin_address" mint "$peggy_proxy_address" $COSMOS_TOKEN_MAX_AMOUNT + +echo "Done!" + +echo "Contract addresses:" +echo " * $peggy_address Peggy.sol" +echo " * $proxy_admin_address ProxyAdmin.sol:" +echo " * $peggy_proxy_address TransparentUpgradeableProxy.sol" +echo " * $cosmos_coin_address Injective token" +echo -e "\n" diff --git a/test/run.sh b/test/run.sh index dbb88213..9dc1f7af 100755 --- a/test/run.sh +++ b/test/run.sh @@ -4,6 +4,8 @@ set -e cd "${0%/*}" # cd in the script dir +killall injectived geth &>/dev/null || true + cwd=$(pwd) cosmos_dir="$cwd/cosmos" eth_dir="$cwd/ethereum" From 8d2d350ee9440f9db7cd0f209a3dbec0ff6645c0 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Nov 2024 17:53:52 +0100 Subject: [PATCH 18/21] chore: update scripts --- test/blacklist_eth.sh | 12 ++++++++++++ test/peggo/deploy_bridge.sh | 12 ++++++++++++ test/peggo/peggy_params.json | 6 +++++- test/send_to_eth.sh | 2 +- test/send_to_inj.sh | 4 ++-- 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100755 test/blacklist_eth.sh diff --git a/test/blacklist_eth.sh b/test/blacklist_eth.sh new file mode 100755 index 00000000..26aa2b1b --- /dev/null +++ b/test/blacklist_eth.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +passphrase=12345678 +tx_opts="--chain-id=injective-333 --gas-prices 500000000inj --keyring-backend test --broadcast-mode=sync --yes --home /Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 --from user" + +# Send INJ tokens +yes $passphrase | injectived tx peggy blacklist-ethereum-addresses "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" $tx_opts + diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh index 8fb6828b..b01c597b 100755 --- a/test/peggo/deploy_bridge.sh +++ b/test/peggo/deploy_bridge.sh @@ -52,21 +52,33 @@ peggy_block_number=$(curl http://localhost:8545 \ echo "Deploying Peggy.sol ..." peggy_impl_address=$(etherman $PEGGY_OPTS deploy) +sleep 1 + echo "Initializing Peggy.sol ..." peggy_init_data=$(etherman $PEGGY_OPTS tx --bytecode "$peggy_impl_address" initialize $PEGGY_INIT_ARGS) +sleep 1 + echo "Deploying ProxyAdmin.sol ..." proxy_admin_address=$(etherman $PROXY_ADMIN_OPTS deploy) +sleep 1 + echo "Deploying TransparentUpgradeableProxy.sol ..." peggy_proxy_address=$(etherman $PEGGY_PROXY_OPTS deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") +sleep 1 + echo "Deploying Injective (CosmosERC20.sol) token ..." coin_contract_address=$(etherman $COSMOS_TOKEN_OPTS deploy $COSMOS_TOKEN_DEPLOY_ARGS) +sleep 1 + echo "Minting 100_000_000 Injective tokens to Peggy.sol proxy ..." etherman $COSMOS_TOKEN_OPTS tx "$coin_contract_address" mint "$peggy_proxy_address" $COSMOS_TOKEN_MAX_AMOUNT +sleep 1 + echo "Done!" echo "Contract addresses:" diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index 09cabba6..42fb506f 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -27,7 +27,11 @@ "valset_reward": { "denom": "inj", "amount": "0" - } + }, + "admins": [ + "inj1wfawuv6fslzjlfa4v7exv27mk6rpfeyvhvxchc" + ], + "segregated_wallet_address": "inj1x2ck0ql2ngyxqtw8jteyc0tchwnwxv7npaungt" } } ], diff --git a/test/send_to_eth.sh b/test/send_to_eth.sh index 1b26da16..8424d3e7 100755 --- a/test/send_to_eth.sh +++ b/test/send_to_eth.sh @@ -7,7 +7,7 @@ cd "${0%/*}" # cd in the script dir passphrase=12345678 # Send INJ tokens -yes $passphrase | injectived tx peggy send-to-eth "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" 10inj 3000000000000000000inj --chain-id=injective-333 --gas-prices 500000000inj --keyring-backend test --broadcast-mode=sync --yes --home /Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 --from user +yes $passphrase | injectived tx peggy send-to-eth "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" 30inj 3000000000000000000inj --chain-id=injective-333 --gas-prices 500000000inj --keyring-backend test --broadcast-mode=sync --yes --home /Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 --from user # Send WAT tokens (premined) #yes $passphrase | injectived tx peggy send-to-eth "0xBbDf3283d1Cf510c17B4FfA1b900F444bE4A4A4e" 10wut 1500000000000000000wut --chain-id=injective-333 --gas-prices 500000000inj --keyring-backend test --broadcast-mode=sync --yes --home /Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333/n0 --from user diff --git a/test/send_to_inj.sh b/test/send_to_inj.sh index 066a02d9..db67b85a 100755 --- a/test/send_to_inj.sh +++ b/test/send_to_inj.sh @@ -11,6 +11,6 @@ cosmos_token_contract="../solidity/contracts/CosmosToken.sol" peggy_contract_address=0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe inj_coin_contract_address=0x7E5C521F8515017487750c13C3bF3B15f3f5f654 -etherman --name CosmosERC20 --source "$cosmos_token_contract" -P "$deployer_pk" tx "$inj_coin_contract_address" approve "$peggy_contract_address" 100 -etherman --name Peggy --source "$peggy_contract" -P "$deployer_pk" tx "$peggy_contract_address" sendToInjective "$inj_coin_contract_address" 727aee334987c52fa7b567b2662bdbb68614e48c 10 "" +#etherman --name CosmosERC20 --source "$cosmos_token_contract" -P "$deployer_pk" tx "$inj_coin_contract_address" approve "$peggy_contract_address" 100 +etherman --name Peggy --source "$peggy_contract" -P "$deployer_pk" tx "$peggy_contract_address" sendToInjective "$inj_coin_contract_address" 000000000000000000000000727aee334987c52fa7b567b2662bdbb68614e48c 10 "" From 119009523251f96a54030a5839b79b02724bd5f2 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Nov 2024 17:54:51 +0100 Subject: [PATCH 19/21] fix: remove field from peggy_params.json --- test/peggo/peggy_params.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index 42fb506f..37aade01 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -30,8 +30,7 @@ }, "admins": [ "inj1wfawuv6fslzjlfa4v7exv27mk6rpfeyvhvxchc" - ], - "segregated_wallet_address": "inj1x2ck0ql2ngyxqtw8jteyc0tchwnwxv7npaungt" + ] } } ], From 1d461b3b4064d3122247563810a0e27084a1010b Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 10 Dec 2024 15:41:17 +0100 Subject: [PATCH 20/21] chore: update params.json --- test/peggo/peggy_params.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index 37aade01..53b5016e 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -23,14 +23,15 @@ "cosmos_coin_denom": "inj", "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", "claim_slashing_enabled": false, - "bridge_contract_start_height": "12", + "bridge_contract_start_height": "15", "valset_reward": { "denom": "inj", "amount": "0" }, "admins": [ "inj1wfawuv6fslzjlfa4v7exv27mk6rpfeyvhvxchc" - ] + ], + "segregated_wallet_address": "inj1dqryh824u0w7p6ajk2gsr29tgj6d0nkfwsgs46" } } ], From 5518bb8cf837bc802fade84d175a4c61a7bf5e96 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 11 Dec 2024 14:52:59 +0100 Subject: [PATCH 21/21] chore: clean up scripts --- test/cosmos/multinode.sh | 32 +-- test/ethereum/geth.sh | 14 +- test/peggo/deploy_bridge.sh | 234 ---------------------- test/peggo/deploy_peggy_contract_suite.sh | 86 +++++--- test/peggo/peggy_params.json | 2 +- test/peggo/start_orchestrators.sh | 94 +++++++++ test/peggo/update_peggy_module.sh | 60 ++++++ test/run.sh | 10 +- 8 files changed, 235 insertions(+), 297 deletions(-) delete mode 100755 test/peggo/deploy_bridge.sh create mode 100755 test/peggo/start_orchestrators.sh create mode 100755 test/peggo/update_peggy_module.sh diff --git a/test/cosmos/multinode.sh b/test/cosmos/multinode.sh index 5073ca36..3d142356 100755 --- a/test/cosmos/multinode.sh +++ b/test/cosmos/multinode.sh @@ -51,14 +51,9 @@ fi NODE_BIN="$1" -echo "Using $CHAIN_ID as Chain ID and $CHAIN_DIR as data prefix." -echo "Using $DENOM as Cosmos Coin Denom." -echo "Using $STAKE_DENOM as Cosmos Stake Denom." - if [[ "$CLEANUP" == 1 || "$CLEANUP" == "1" ]]; then echo "Will remove $CHAIN_DIR" fi -echo "Press ^C if you don't agree.." killall "$NODE_BIN" &>/dev/null || true @@ -99,7 +94,7 @@ cid="--chain-id $CHAIN_ID" # Check if the data dir has been initialized already if [[ ! -d "$hdir" ]]; then - echo "Creating 3x $NODE_BIN validators with chain-id=$CHAIN_ID" + echo "Creating 3x $NODE_BIN validators and 1 user account..." # Build genesis file and create accounts if [[ "$STAKE_DENOM" != "$DENOM" ]]; then @@ -109,8 +104,6 @@ if [[ ! -d "$hdir" ]]; then fi coins_user="1000000$SCALE_FACTOR$DENOM" - echo "initializing node homes..." - # Initialize the home directories of each node $NODE_BIN $home0 $cid init n0 &>/dev/null $NODE_BIN $home1 $cid init n1 &>/dev/null @@ -261,7 +254,7 @@ if [[ ! -d "$hdir" ]]; then fi # data dir check # Start the instances -echo "Starting injectived nodes..." +echo "Starting validator nodes..." echo $NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" $NODE_BIN $home0 start --grpc.address "0.0.0.0:9090" > $hdir.n0.log 2>&1 & @@ -272,24 +265,15 @@ $NODE_BIN $home1 start --grpc.address "0.0.0.0:9091" > $hdir.n1.log 2>&1 & echo $NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" $NODE_BIN $home2 start --grpc.address "0.0.0.0:9092" > $hdir.n2.log 2>&1 & - - # Wait for chains to start -echo "Waiting for chain to start..." sleep 5 echo echo "Logs:" -echo " * tail -f ./data/$CHAIN_ID.n0.log" -echo " * tail -f ./data/$CHAIN_ID.n1.log" -echo " * tail -f ./data/$CHAIN_ID.n2.log" +echo " tail -f ./data/$CHAIN_ID.n0.log" +echo " tail -f ./data/$CHAIN_ID.n1.log" +echo " tail -f ./data/$CHAIN_ID.n2.log" echo -echo "Env for easy access:" -echo "export H1='--home ./data/$CHAIN_ID/n0/'" -echo "export H2='--home ./data/$CHAIN_ID/n1/'" -echo "export H3='--home ./data/$CHAIN_ID/n2/'" -echo -echo "Command Line Access:" -echo " * $NODE_BIN --home ./data/$CHAIN_ID/n0 status" -echo " * $NODE_BIN --home ./data/$CHAIN_ID/n1 status" -echo " * $NODE_BIN --home ./data/$CHAIN_ID/n2 status" \ No newline at end of file +echo "Injective network setup complete!" +echo + diff --git a/test/ethereum/geth.sh b/test/ethereum/geth.sh index 9d5c0409..83f07ee2 100755 --- a/test/ethereum/geth.sh +++ b/test/ethereum/geth.sh @@ -36,16 +36,16 @@ geth --datadir "$DATA_DIR" --networkid "$GETH_NETWORK_ID" --nodiscover \ --mine --miner.gaslimit "$GETH_BLOCK_GAS_LIMIT" > "$DATA_DIR".geth.log 2>&1 & echo $! > "$DATA_DIR".geth.pid # overwrite previous PID +PID=$(cat ./data/$GETH_NETWORK_ID.geth.pid) sleep 1 -echo "** USAGE **" +echo "Geth:" +echo " http://localhost:$GETH_PORT" echo "Logs:" echo " tail -f ./data/$GETH_NETWORK_ID.geth.log" -echo -echo "Command Line Access:" -echo " geth attach http://localhost:8545" -echo " geth attach ./data/$GETH_NETWORK_ID/geth.ipc" -echo echo "Shutdown:" -echo " kill \$(cat ./data/$GETH_NETWORK_ID.geth.pid)" +echo " kill $PID" +echo +echo "Ethereum network setup complete!" +echo \ No newline at end of file diff --git a/test/peggo/deploy_bridge.sh b/test/peggo/deploy_bridge.sh deleted file mode 100755 index b01c597b..00000000 --- a/test/peggo/deploy_bridge.sh +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/bash - -set -e - -cd "${0%/*}" # cd in the script dir - -echo -e "\n" -echo "*** Peggy Contract Suite deployment ***" - -# Initial Validator Set on Injective -PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d70656767796964000000000000000000000000000000}" # bytes32 encoding of "injective-peggyid" -POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" # how to get: 2/3 of total validator power on Injective - -VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-\ -0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,\ -0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,\ -0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" - -VALIDATOR_POWERS="${VALIDATOR_POWERS:-\ -1431655765,\ -1431655765,\ -1431655765}" - -PEGGY_INIT_ARGS="$PEGGY_ID $POWER_THRESHOLD $VALIDATOR_ADDRESSES $VALIDATOR_POWERS" - -# Peggy contracts -PEGGY_CONTRACT_PATH="../../solidity/contracts/Peggy.sol" -PROXY_ADMIN_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" -PROXY_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" -COSMOS_TOKEN_CONTRACT_PATH="../../solidity/contracts/CosmosToken.sol" -COSMOS_TOKEN_DEPLOY_ARGS="Injective inj 18" -COSMOS_TOKEN_MAX_AMOUNT=100000000000000000000000000 # 100 million tokens that will be minted straight to Peggy proxy - -# Ethereum -DEPLOYER_PK=$(cat ../ethereum/geth/clique_signer.key) -ETH_ENDPOINT="http://localhost:8545" - -TX_OPTS="-P $DEPLOYER_PK --endpoint $ETH_ENDPOINT" -COSMOS_TOKEN_OPTS="$TX_OPTS --name CosmosERC20 --source $COSMOS_TOKEN_CONTRACT_PATH" -PEGGY_OPTS="$TX_OPTS --name Peggy --source $PEGGY_CONTRACT_PATH" -PROXY_ADMIN_OPTS="$TX_OPTS --name ProxyAdmin --source $PROXY_ADMIN_CONTRACT_PATH" -PEGGY_PROXY_OPTS="$TX_OPTS --name TransparentUpgradeableProxy --source $PROXY_CONTRACT_PATH" - -# get the bridge_contract_start_height early so orchestrators can catch the first Valset Updated event from Peggy.sol -peggy_block_number=$(curl http://localhost:8545 \ - -X POST \ - -H "Content-Type: application/json" \ - -d '{"id":1,"jsonrpc":"2.0", "method":"eth_getBlockByNumber","params":["latest", true]}' 2>/dev/null \ - | python3 -c "import sys, json; print(int(json.load(sys.stdin)['result']['number'], 0))") - - -echo "Deploying Peggy.sol ..." -peggy_impl_address=$(etherman $PEGGY_OPTS deploy) - -sleep 1 - -echo "Initializing Peggy.sol ..." -peggy_init_data=$(etherman $PEGGY_OPTS tx --bytecode "$peggy_impl_address" initialize $PEGGY_INIT_ARGS) - -sleep 1 - -echo "Deploying ProxyAdmin.sol ..." -proxy_admin_address=$(etherman $PROXY_ADMIN_OPTS deploy) - -sleep 1 - -echo "Deploying TransparentUpgradeableProxy.sol ..." -peggy_proxy_address=$(etherman $PEGGY_PROXY_OPTS deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") - -sleep 1 - -echo "Deploying Injective (CosmosERC20.sol) token ..." -coin_contract_address=$(etherman $COSMOS_TOKEN_OPTS deploy $COSMOS_TOKEN_DEPLOY_ARGS) - -sleep 1 - -echo "Minting 100_000_000 Injective tokens to Peggy.sol proxy ..." -etherman $COSMOS_TOKEN_OPTS tx "$coin_contract_address" mint "$peggy_proxy_address" $COSMOS_TOKEN_MAX_AMOUNT - -sleep 1 - -echo "Done!" - -echo "Contract addresses:" -echo " * $peggy_impl_address Peggy.sol" -echo " * $proxy_admin_address ProxyAdmin.sol:" -echo " * $peggy_proxy_address TransparentUpgradeableProxy.sol" -echo " * $coin_contract_address Injective token" -echo -e "\n" - -sleep 2 - -PASSPHRASE="12345678" -TX_OPTS="--chain-id injective-333 --keyring-backend test --broadcast-mode sync --yes" - -peggy_params_json="./peggy_params.json" -chain_dir="/Users/dbrajovic/Desktop/dev/Injective/peggo/test/cosmos/data/injective-333" -n0_home_dir=$chain_dir/n0 -n1_home_dir=$chain_dir/n1 -n2_home_dir=$chain_dir/n2 - -# Update peggy_params.json -jq --arg cosmos_coin_erc20 "$coin_contract_address" \ - --arg bridge_contract_height "$peggy_block_number" \ - --arg bridge_ethereum "$peggy_proxy_address" \ - '.messages[0].params.cosmos_coin_erc20_contract = $cosmos_coin_erc20 | - .messages[0].params.bridge_contract_start_height = $bridge_contract_height | - .messages[0].params.bridge_ethereum_address = $bridge_ethereum' \ - $peggy_params_json > tmpfile && mv tmpfile $peggy_params_json - -# usage: resp_check [resp] [err_msg] -resp_check() { - if [ "$(echo -e "$1" | awk -F"'" '/raw_log: /{print $2}')" != "[]" ]; then - echo "$2" - exit 1 - fi -} - -echo "Submitting gov proposal for Peggy module params update..." -cat $peggy_params_json - -#resp="$(yes $PASSPHRASE | injectived tx gov submit-proposal $peggy_params_json --home $n0_home_dir --from user --gas 2000000 --gas-prices 500000000inj $TX_OPTS)" -#resp_check "$resp" "Failed to submit gov proposal" - -resp="$(yes $PASSPHRASE | injectived tx gov submit-proposal $peggy_params_json --home $n0_home_dir --from user --gas 2000000 --gas-prices 500000000inj $TX_OPTS)" -echo "$resp" - -sleep 2 - -current_proposal_id=$(curl 'http://localhost:10337/cosmos/gov/v1beta1/proposals?proposal_status=0&pagination.limit=1&pagination.reverse=true' 2>/dev/null | jq -r '.proposals[].proposal_id') - -yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n0_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null -yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n1_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null -yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n2_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null - -echo -n "Waiting for proposal to pass... " -sleep 8 -echo "DONE" - -n0_inj_addr="inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" -n1_inj_addr="inj1jcltmuhplrdcwp7stlr4hlhlhgd4htqhe4c0cs" -n2_inj_addr="inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz" - -n0_eth_addr="0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4" -n1_eth_addr="0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c" -n2_eth_addr="0x8B094eD440900CEB75B83A22eD8A2C7582B442C2" - -injectived tx peggy set-orchestrator-address $n0_inj_addr $n0_inj_addr $n0_eth_addr --home $n0_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null -injectived tx peggy set-orchestrator-address $n1_inj_addr $n1_inj_addr $n1_eth_addr --home $n1_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null -injectived tx peggy set-orchestrator-address $n2_inj_addr $n2_inj_addr $n2_eth_addr --home $n2_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null - -echo -n "Registering orchestrator ETH addresses... " -sleep 2 -echo "DONE" - -# Start peggo service -echo "Starting 3x Peggo orchestrators..." - -cwd=$(pwd) -example_env=$cwd/example.env -localhost_tcp="tcp://localhost" -localhost_http="http://localhost" - -n0_peggo_dir=$cwd/data/n0 -n1_peggo_dir=$cwd/data/n1 -n2_peggo_dir=$cwd/data/n2 - -n0_peggo_env=$n0_peggo_dir/.env -n1_peggo_env=$n1_peggo_dir/.env -n2_peggo_env=$n2_peggo_dir/.env - -mkdir -p "$n0_peggo_dir" && touch "$n0_peggo_env" -mkdir -p "$n1_peggo_dir" && touch "$n1_peggo_env" -mkdir -p "$n2_peggo_dir" && touch "$n2_peggo_env" - -n0_cosmos_grpc="$localhost_tcp:9090" -n1_cosmos_grpc="$localhost_tcp:9091" -n2_cosmos_grpc="$localhost_tcp:9092" - -n0_tendermint_rpc="$localhost_http:26657" -n1_tendermint_rpc="$localhost_http:26667" -n2_tendermint_rpc="$localhost_http:26677" - -n0_eth_pk="e85344fa1e00f06bd286b716e410ee0ad73541956c4cf59520f6db13599eb3f3" -n1_eth_pk="60f6ee19454b8ff45693cd54c55860785e4af9eeb06d6c5617568458e4ca5c54" -n2_eth_pk="21eeff959d9752704e3f1ad6562fd0458c003bce0947e5aecf07b602f4e457aa" - -sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n0_eth_addr\"|" \ - -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n0_eth_pk\"|" \ - -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n0_home_dir\"|" \ - -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n0_cosmos_grpc\"|" \ - -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n0_tendermint_rpc\"|" \ - "$example_env" > "$n0_peggo_env" - -sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n1_eth_addr\"|" \ - -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n1_eth_pk\"|" \ - -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n1_home_dir\"|" \ - -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n1_cosmos_grpc\"|" \ - -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n1_tendermint_rpc\"|" \ - "$example_env" > "$n1_peggo_env" - -sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n2_eth_addr\"|" \ - -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n2_eth_pk\"|" \ - -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n2_home_dir\"|" \ - -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n2_cosmos_grpc\"|" \ - -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n2_tendermint_rpc\"|" \ - "$example_env" > "$n2_peggo_env" - -# One relayer has lower min batch fee -CHEAP_RELAYER="${CHEAP_RELAYER:-false}" -if [[ "$CHEAP_RELAYER" == true ]]; then - echo "Setting n2 orchestrator with PEGGO_MIN_BATCH_FEE_USD to 10" - echo "$n2_peggo_env" - sed -i '' 's/^PEGGO_MIN_BATCH_FEE_USD=.*/PEGGO_MIN_BATCH_FEE_USD=10/' "$n2_peggo_env" -fi - -# Start a new tmux session -tmux new-session -d -s mysession - -# Split the terminal vertically into three equally spaced panes -tmux split-window -v -tmux split-window -v -tmux select-layout even-vertical - -# Select each pane and run a command from a different directory -peggo_cmd="peggo orchestrator" -tmux send-keys -t 0 "cd $n0_peggo_dir" C-m "$peggo_cmd" C-m -tmux send-keys -t 1 "cd $n1_peggo_dir" C-m "$peggo_cmd" C-m -tmux send-keys -t 2 "cd $n2_peggo_dir" C-m "$peggo_cmd" C-m - -# Attach to the tmux session to view the processes -tmux attach-session -t mysession - - diff --git a/test/peggo/deploy_peggy_contract_suite.sh b/test/peggo/deploy_peggy_contract_suite.sh index 48d72d4c..2c291c99 100755 --- a/test/peggo/deploy_peggy_contract_suite.sh +++ b/test/peggo/deploy_peggy_contract_suite.sh @@ -4,66 +4,94 @@ set -e cd "${0%/*}" # cd in the script dir -echo "*** Peggy Contract Suite deployment ***" +# get the bridge_contract_start_height early so orchestrators can catch the first Valset Updated event from Peggy.sol +peggy_block_number=$(curl http://localhost:8545 \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"id":1,"jsonrpc":"2.0", "method":"eth_getBlockByNumber","params":["latest", true]}' 2>/dev/null \ + | python3 -c "import sys, json; print(int(json.load(sys.stdin)['result']['number'], 0))") -### USER PROVIDED # Initial Validator Set on Injective PEGGY_ID="${PEGGY_ID:-0x696e6a6563746976652d70656767796964000000000000000000000000000000}" # bytes32 encoding of "injective-peggyid" POWER_THRESHOLD="${POWER_THRESHOLD:-1431655765}" # how to get: 2/3 of total validator power on Injective -VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" -VALIDATOR_POWERS="${VALIDATOR_POWERS:-1431655765,1431655765,1431655765}" + +VALIDATOR_ADDRESSES="${VALIDATOR_ADDRESSES:-\ +0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4,\ +0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c,\ +0x8B094eD440900CEB75B83A22eD8A2C7582B442C2}" + +VALIDATOR_POWERS="${VALIDATOR_POWERS:-\ +1431655765,\ +1431655765,\ +1431655765}" + +PEGGY_INIT_ARGS="$PEGGY_ID $POWER_THRESHOLD $VALIDATOR_ADDRESSES $VALIDATOR_POWERS" # Peggy contracts PEGGY_CONTRACT_PATH="../../solidity/contracts/Peggy.sol" PROXY_ADMIN_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/ProxyAdmin.sol" PROXY_CONTRACT_PATH="../../solidity/contracts/@openzeppelin/contracts/TransparentUpgradeableProxy.sol" COSMOS_TOKEN_CONTRACT_PATH="../../solidity/contracts/CosmosToken.sol" -COSMOS_TOKEN_DEPLOY_ARGS="Nasud nas 18" +COSMOS_TOKEN_DEPLOY_ARGS="Injective INJ 18" COSMOS_TOKEN_MAX_AMOUNT=100000000000000000000000000 # 100 million tokens that will be minted straight to Peggy proxy -# Ethereum -DEPLOYER_PK="b8fe92b390dc8a830a9544be585bf87a4b1aa318beb629a8d60e0d83fa68eb72" -ETH_ENDPOINT="https://eth-sepolia.g.alchemy.com/v2/VranSAqL6UIW7YSsj_5mkHg7UMMHP6jR" - -### DEPLOYMENT - -PEGGY_INIT_ARGS="$PEGGY_ID $POWER_THRESHOLD $VALIDATOR_ADDRESSES $VALIDATOR_POWERS" -TX_OPTS="-P $DEPLOYER_PK --endpoint $ETH_ENDPOINT --tx-timeout 60s" +# Ethereum opts +DEPLOYER_PK=$(cat ../ethereum/geth/clique_signer.key) +ETH_ENDPOINT="http://localhost:8545" +TX_OPTS="-P $DEPLOYER_PK --endpoint $ETH_ENDPOINT" COSMOS_TOKEN_OPTS="$TX_OPTS --name CosmosERC20 --source $COSMOS_TOKEN_CONTRACT_PATH" PEGGY_OPTS="$TX_OPTS --name Peggy --source $PEGGY_CONTRACT_PATH" PROXY_ADMIN_OPTS="$TX_OPTS --name ProxyAdmin --source $PROXY_ADMIN_CONTRACT_PATH" PEGGY_PROXY_OPTS="$TX_OPTS --name TransparentUpgradeableProxy --source $PROXY_CONTRACT_PATH" + echo "Deploying Peggy.sol ..." -peggy_address=$(etherman $PEGGY_OPTS deploy) -echo "$peggy_address" +peggy_impl_address=$(etherman $PEGGY_OPTS deploy) + +sleep 1 echo "Initializing Peggy.sol ..." -peggy_init_data=$(etherman $PEGGY_OPTS tx --bytecode "$peggy_address" initialize $PEGGY_INIT_ARGS) -echo "$peggy_init_data" +peggy_init_data=$(etherman $PEGGY_OPTS tx --bytecode "$peggy_impl_address" initialize $PEGGY_INIT_ARGS) + +sleep 1 echo "Deploying ProxyAdmin.sol ..." proxy_admin_address=$(etherman $PROXY_ADMIN_OPTS deploy) -echo "$proxy_admin_address" + +sleep 1 echo "Deploying TransparentUpgradeableProxy.sol ..." -peggy_proxy_address=$(etherman $PEGGY_PROXY_OPTS deploy "$peggy_address" "$proxy_admin_address" "$peggy_init_data") -echo "$peggy_proxy_address" +peggy_proxy_address=$(etherman $PEGGY_PROXY_OPTS deploy "$peggy_impl_address" "$proxy_admin_address" "$peggy_init_data") +sleep 1 echo "Deploying Injective (CosmosERC20.sol) token ..." -cosmos_coin_address=$(etherman $COSMOS_TOKEN_OPTS deploy $COSMOS_TOKEN_DEPLOY_ARGS) -echo "$cosmos_coin_address" +coin_contract_address=$(etherman $COSMOS_TOKEN_OPTS deploy $COSMOS_TOKEN_DEPLOY_ARGS) + +sleep 1 echo "Minting 100_000_000 Injective tokens to Peggy.sol proxy ..." -etherman $COSMOS_TOKEN_OPTS tx "$cosmos_coin_address" mint "$peggy_proxy_address" $COSMOS_TOKEN_MAX_AMOUNT +etherman $COSMOS_TOKEN_OPTS tx "$coin_contract_address" mint "$peggy_proxy_address" $COSMOS_TOKEN_MAX_AMOUNT -echo "Done!" +sleep 1 echo "Contract addresses:" -echo " * $peggy_address Peggy.sol" -echo " * $proxy_admin_address ProxyAdmin.sol:" -echo " * $peggy_proxy_address TransparentUpgradeableProxy.sol" -echo " * $cosmos_coin_address Injective token" -echo -e "\n" +echo " $peggy_impl_address Peggy.sol" +echo " $proxy_admin_address ProxyAdmin.sol:" +echo " $peggy_proxy_address TransparentUpgradeableProxy.sol" +echo " $coin_contract_address Injective token" +echo + +# Update peggy_params.json +peggy_params_json="./peggy_params.json" +jq --arg cosmos_coin_erc20 "$coin_contract_address" \ + --arg bridge_contract_height "$peggy_block_number" \ + --arg bridge_ethereum "$peggy_proxy_address" \ + '.messages[0].params.cosmos_coin_erc20_contract = $cosmos_coin_erc20 | + .messages[0].params.bridge_contract_start_height = $bridge_contract_height | + .messages[0].params.bridge_ethereum_address = $bridge_ethereum' \ + $peggy_params_json > tmpfile && mv tmpfile $peggy_params_json + +echo "Peggy contracts deployed!" +echo diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json index 53b5016e..6438cdcc 100644 --- a/test/peggo/peggy_params.json +++ b/test/peggo/peggy_params.json @@ -23,7 +23,7 @@ "cosmos_coin_denom": "inj", "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", "claim_slashing_enabled": false, - "bridge_contract_start_height": "15", + "bridge_contract_start_height": "13", "valset_reward": { "denom": "inj", "amount": "0" diff --git a/test/peggo/start_orchestrators.sh b/test/peggo/start_orchestrators.sh new file mode 100755 index 00000000..359c8056 --- /dev/null +++ b/test/peggo/start_orchestrators.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +# Start peggo service +echo "Starting 3x Peggo orchestrators..." + +cwd=$(pwd) +example_env=$cwd/example.env +localhost_tcp="tcp://localhost" +localhost_http="http://localhost" + +n0_peggo_dir=$cwd/data/n0 +n1_peggo_dir=$cwd/data/n1 +n2_peggo_dir=$cwd/data/n2 + +n0_peggo_env=$n0_peggo_dir/.env +n1_peggo_env=$n1_peggo_dir/.env +n2_peggo_env=$n2_peggo_dir/.env + +mkdir -p "$n0_peggo_dir" && touch "$n0_peggo_env" +mkdir -p "$n1_peggo_dir" && touch "$n1_peggo_env" +mkdir -p "$n2_peggo_dir" && touch "$n2_peggo_env" + +n0_cosmos_grpc="$localhost_tcp:9090" +n1_cosmos_grpc="$localhost_tcp:9091" +n2_cosmos_grpc="$localhost_tcp:9092" + +n0_tendermint_rpc="$localhost_http:26657" +n1_tendermint_rpc="$localhost_http:26667" +n2_tendermint_rpc="$localhost_http:26677" + +chain_dir=$(realpath "../cosmos/data/injective-333") +n0_home_dir=$chain_dir/n0 +n1_home_dir=$chain_dir/n1 +n2_home_dir=$chain_dir/n2 + +n0_eth_pk="e85344fa1e00f06bd286b716e410ee0ad73541956c4cf59520f6db13599eb3f3" +n1_eth_pk="60f6ee19454b8ff45693cd54c55860785e4af9eeb06d6c5617568458e4ca5c54" +n2_eth_pk="21eeff959d9752704e3f1ad6562fd0458c003bce0947e5aecf07b602f4e457aa" + +n0_eth_addr="0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4" +n1_eth_addr="0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c" +n2_eth_addr="0x8B094eD440900CEB75B83A22eD8A2C7582B442C2" + +sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n0_eth_addr\"|" \ + -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n0_eth_pk\"|" \ + -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n0_home_dir\"|" \ + -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n0_cosmos_grpc\"|" \ + -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n0_tendermint_rpc\"|" \ + "$example_env" > "$n0_peggo_env" + +sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n1_eth_addr\"|" \ + -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n1_eth_pk\"|" \ + -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n1_home_dir\"|" \ + -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n1_cosmos_grpc\"|" \ + -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n1_tendermint_rpc\"|" \ + "$example_env" > "$n1_peggo_env" + +sed -e "s|^PEGGO_ETH_FROM=.*|PEGGO_ETH_FROM=\"$n2_eth_addr\"|" \ + -e "s|^PEGGO_ETH_PK=.*|PEGGO_ETH_PK=\"$n2_eth_pk\"|" \ + -e "s|^PEGGO_COSMOS_KEYRING_DIR=.*|PEGGO_COSMOS_KEYRING_DIR=\"$n2_home_dir\"|" \ + -e "s|^PEGGO_COSMOS_GRPC=.*|PEGGO_COSMOS_GRPC=\"$n2_cosmos_grpc\"|" \ + -e "s|^PEGGO_TENDERMINT_RPC=.*|PEGGO_TENDERMINT_RPC=\"$n2_tendermint_rpc\"|" \ + "$example_env" > "$n2_peggo_env" + +# One relayer has lower min batch fee +CHEAP_RELAYER="${CHEAP_RELAYER:-false}" +if [[ "$CHEAP_RELAYER" == true ]]; then + echo "Setting n2 orchestrator with PEGGO_MIN_BATCH_FEE_USD to 10" + echo "$n2_peggo_env" + sed -i '' 's/^PEGGO_MIN_BATCH_FEE_USD=.*/PEGGO_MIN_BATCH_FEE_USD=10/' "$n2_peggo_env" +fi + +# Start a new tmux session +tmux new-session -d -s mysession + +# Split the terminal vertically into three equally spaced panes +tmux split-window -v +tmux split-window -v +tmux select-layout even-vertical + +# Select each pane and run a command from a different directory +peggo_cmd="peggo orchestrator" +tmux send-keys -t 0 "cd $n0_peggo_dir" C-m "$peggo_cmd" C-m +tmux send-keys -t 1 "cd $n1_peggo_dir" C-m "$peggo_cmd" C-m +tmux send-keys -t 2 "cd $n2_peggo_dir" C-m "$peggo_cmd" C-m + +# Attach to the tmux session to view the processes +tmux attach-session -t mysession + + diff --git a/test/peggo/update_peggy_module.sh b/test/peggo/update_peggy_module.sh new file mode 100755 index 00000000..ce753a58 --- /dev/null +++ b/test/peggo/update_peggy_module.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -e + +cd "${0%/*}" # cd in the script dir + +PASSPHRASE="12345678" +TX_OPTS="--chain-id injective-333 --keyring-backend test --broadcast-mode sync --yes" + +peggy_params_json="./peggy_params.json" +chain_dir="../cosmos/data/injective-333" +n0_home_dir=$chain_dir/n0 +n1_home_dir=$chain_dir/n1 +n2_home_dir=$chain_dir/n2 + +# usage: resp_check [resp] [err_msg] +resp_check() { + if [ "$(echo -e "$1" | awk -F"'" '/raw_log: /{print $2}')" != "[]" ]; then + echo "$2" + exit 1 + fi +} + +echo "Peggy params update:" +cat $peggy_params_json +echo +echo "Submitting gov proposal to update Peggy module params..." + +resp="$(yes $PASSPHRASE | injectived tx gov submit-proposal $peggy_params_json --home $n0_home_dir --from user --gas 2000000 --gas-prices 500000000inj $TX_OPTS)" +echo "$resp" + +sleep 2 + +current_proposal_id=$(curl 'http://localhost:10337/cosmos/gov/v1beta1/proposals?proposal_status=0&pagination.limit=1&pagination.reverse=true' 2>/dev/null | jq -r '.proposals[].proposal_id') + +yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n0_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null +yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n1_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null +yes $PASSPHRASE | injectived tx gov vote "$current_proposal_id" yes --home $n2_home_dir --from val --gas-prices 500000000inj $TX_OPTS &>/dev/null + +sleep 8 +echo "Gov proposal passed" + +n0_inj_addr="inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" +n1_inj_addr="inj1jcltmuhplrdcwp7stlr4hlhlhgd4htqhe4c0cs" +n2_inj_addr="inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz" + +n0_eth_addr="0x4e9feE2BCdf6F21b17b77BD0ac9faDD6fF16B4d4" +n1_eth_addr="0xec43B0eA83844Cbe5A20F5371604BD452Cb1012c" +n2_eth_addr="0x8B094eD440900CEB75B83A22eD8A2C7582B442C2" + +echo "Registering orchestrator ETH addresses..." +injectived tx peggy set-orchestrator-address $n0_inj_addr $n0_inj_addr $n0_eth_addr --home $n0_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null +injectived tx peggy set-orchestrator-address $n1_inj_addr $n1_inj_addr $n1_eth_addr --home $n1_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null +injectived tx peggy set-orchestrator-address $n2_inj_addr $n2_inj_addr $n2_eth_addr --home $n2_home_dir --from=val --gas-prices 100000000000000inj $TX_OPTS &>/dev/null + +sleep 2 +echo "Orchestrator addresses registered" +echo " * val1=$n0_inj_addr orch1=$n0_inj_addr eth1=$n0_eth_addr" +echo " * val2=$n0_inj_addr orch2=$n0_inj_addr eth2=$n0_eth_addr" +echo " * val3=$n0_inj_addr orch3=$n0_inj_addr eth3=$n0_eth_addr" diff --git a/test/run.sh b/test/run.sh index 9dc1f7af..ff452db1 100755 --- a/test/run.sh +++ b/test/run.sh @@ -23,5 +23,11 @@ rm -rf "$peggo_dir/build" # Start the Cosmos chain "$cosmos_dir"/multinode.sh injectived -# Deploy Peggy contract suite and start Peggo orchestrators -"$peggo_dir"/deploy_bridge.sh +# Deploy Peggy contracts suite +"$peggo_dir"/deploy_peggy_contract_suite.sh + +# Update Peggy module and register orchestrators +"$peggo_dir"/update_peggy_module.sh + +# Start the orchestrators +"$peggo_dir"/start_orchestrators.sh