From 59413973d76609de1023ab0b8aef880a73094d82 Mon Sep 17 00:00:00 2001 From: Maximilian Breithecker <72022235+mbreithecker@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:43:04 +0100 Subject: [PATCH] chore: add verify-script for checking validator migration proofs (#218) --- .github/workflows/all.yml | 3 + .github/workflows/migration.yml | 17 +++++ app/upgrades/v2_0/validator-proofs/Readme.md | 8 ++- .../kaon/example-validator.json | 7 -- .../mainnet/example-validator.json | 7 -- app/upgrades/v2_0/validator-proofs/verify.py | 69 +++++++++++++++++++ 6 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/migration.yml delete mode 100644 app/upgrades/v2_0/validator-proofs/kaon/example-validator.json delete mode 100644 app/upgrades/v2_0/validator-proofs/mainnet/example-validator.json create mode 100644 app/upgrades/v2_0/validator-proofs/verify.py diff --git a/.github/workflows/all.yml b/.github/workflows/all.yml index c126f513..090e9fd3 100644 --- a/.github/workflows/all.yml +++ b/.github/workflows/all.yml @@ -2,6 +2,9 @@ name: run all jobs on: push jobs: + verify-migration-proofs: + uses: ./.github/workflows/migration.yml + lint: uses: ./.github/workflows/lint.yml diff --git a/.github/workflows/migration.yml b/.github/workflows/migration.yml new file mode 100644 index 00000000..25c3f089 --- /dev/null +++ b/.github/workflows/migration.yml @@ -0,0 +1,17 @@ +name: migration +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + steps: + # Checkout the repository + - name: Check out repository code + uses: actions/checkout@v4 + # Setup Python + - name: Install dependencies + run: pip3 install bech32 requests + - name: Verify migration proofs + working-directory: ./app/upgrades/v2_0/validator-proofs + run: python3 verify.py \ No newline at end of file diff --git a/app/upgrades/v2_0/validator-proofs/Readme.md b/app/upgrades/v2_0/validator-proofs/Readme.md index 24418ed9..40ebc33e 100644 --- a/app/upgrades/v2_0/validator-proofs/Readme.md +++ b/app/upgrades/v2_0/validator-proofs/Readme.md @@ -11,19 +11,21 @@ all stake is returned to the original delegators during the migration. ### Steps -1. Enter the `mainnet`-directory and copy the `example-validator.json` config file and name it after your validator. +1. Enter the `mainnet`-directory and copy an existing config file and name it after your validator. 2. Fill out the `name`, `protocol_address` and `consensus_address` 3. Send 1 $KYVE from the protocol-address to the consensus-validator-operator address using the memo "Shared-Staking" and put the tx-hash in proof_1. -4. Send 1 $KYVE from the consensus-validator-operator address to the protocol address using the memo "Shared-Staking" +4. Send 1 $KYVE from the consensus-validator-operator address to the protocol-address using the memo "Shared-Staking" and put the tx-hash in proof_2. 5. Submit a Pull-Request to https://github.com/KYVENetwork/chain -6. (Optional) Perform the same steps for the `kaon` directory with your Kaon validators. +6. (Optional) Perform the same steps for the `kaon` directory with your Kaon validators. (Transactions must be submitted on the Kaon network.) + +7. Check if the GitHub action (verify-migration-proofs) is passing. You can also run `python3 verify.py` locally to check if everything is correct. ## General Upgrade Procedure diff --git a/app/upgrades/v2_0/validator-proofs/kaon/example-validator.json b/app/upgrades/v2_0/validator-proofs/kaon/example-validator.json deleted file mode 100644 index 7a533ce9..00000000 --- a/app/upgrades/v2_0/validator-proofs/kaon/example-validator.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "My example Kaon validator name - this is just for human-readability", - "consensus_address": "kyvevaloper...", - "protocol_address": "kyve...", - "proof_1": "89644D8598D007F7B744E91BA4490C11513860046C257F29CFAF41EAE37FAE9C", - "proof_2": "36A1110532EC28C4BEDA610720CA31DD1B14D4A49772F8890EB5C62B417D253B" -} \ No newline at end of file diff --git a/app/upgrades/v2_0/validator-proofs/mainnet/example-validator.json b/app/upgrades/v2_0/validator-proofs/mainnet/example-validator.json deleted file mode 100644 index 6d2d84ce..00000000 --- a/app/upgrades/v2_0/validator-proofs/mainnet/example-validator.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "My example validator name - this is just for human-readability", - "consensus_address": "kyvevaloper...", - "protocol_address": "kyve...", - "proof_1": "EEA08DDDA94434BBE1FA25AB941B58D85FC5EE48E866B602549CA194CAB41B11", - "proof_2": "21771F8B4B2F72D0F292F3517051FD6D8CAA5BB9EC83B906F2BF38684F1E624E" -} \ No newline at end of file diff --git a/app/upgrades/v2_0/validator-proofs/verify.py b/app/upgrades/v2_0/validator-proofs/verify.py new file mode 100644 index 00000000..41aacaa1 --- /dev/null +++ b/app/upgrades/v2_0/validator-proofs/verify.py @@ -0,0 +1,69 @@ +import os +import json +import sys + +import requests +import bech32 + + +def verify_tx(api_endpoint, tx_hash, expect_from_address, expect_to_address): + x = requests.get(api_endpoint + "/cosmos/tx/v1beta1/txs/" + tx_hash) + if x.status_code != 200: + raise Exception("transaction does not exist: ", tx_hash) + + tx = x.json() + if tx["tx"]["body"]["memo"] != "Shared-Staking": + raise Exception("incorrect memo for transaction: ", tx_hash) + + if tx["tx"]["body"]["messages"][0]["from_address"] != expect_from_address: + raise Exception("Incorrect from_address. Expected: {}, got: {}" + .format(expect_from_address, tx["tx"]["body"]["messages"][0]["from_address"])) + + if tx["tx"]["body"]["messages"][0]["to_address"] != expect_to_address: + raise Exception("Incorrect to_address. Expected: {}, got: {}" + .format(expect_to_address, tx["tx"]["body"]["messages"][0]["to_address"])) + + +def verify_proof(api_endpoint, entry): + x = requests.get(api_endpoint + "/cosmos/staking/v1beta1/validators/" + entry["consensus_address"]) + if x.status_code != 200: + raise Exception("Consensus validator does not exist: ", entry["consensus_address"]) + + x = requests.get(api_endpoint + "/kyve/query/v1beta1/staker/" + entry["protocol_address"]) + if x.status_code != 200: + raise Exception("Protocol validator does not exist: ", entry["protocol_address"]) + + prefix, address_bytes = bech32.bech32_decode(entry["consensus_address"]) + validator_acc_address = bech32.bech32_encode("kyve", address_bytes) + + verify_tx(api_endpoint, entry["proof_1"], entry["protocol_address"], validator_acc_address) + verify_tx(api_endpoint, entry["proof_2"], validator_acc_address, entry["protocol_address"]) + + +def verify_network(name, api_endpoint): + status = {"correct": 0, "error": 0} + for file in os.listdir("./" + name): + try: + proof = json.load(open("./{}/{}".format(name, file))) + verify_proof(api_endpoint, proof) + print("[{}]".format(name.title()), file, "✅") + status["correct"] += 1 + + except Exception as e: + print("[{}]".format(name.title()), file, "❌") + print(e) + status["error"] += 1 + + return status + + +status_kaon = verify_network("kaon", "https://api.kaon.kyve.network") +print("\n[Kaon] Correct: {}, Error: {}".format(status_kaon["correct"], status_kaon["error"])) + +print("\n") + +status_mainnet = verify_network("mainnet", "https://api.kyve.network") +print("\n[Mainnet] Correct: {}, Error: {}".format(status_mainnet["correct"], status_mainnet["error"])) + +if status_kaon["error"] != 0 or status_mainnet["error"] != 0: + sys.exit(1)