test: accountant e2e
This commit is contained in:
parent
cca154baf6
commit
7f9a03254a
24
Tiltfile
24
Tiltfile
|
@ -280,7 +280,19 @@ def build_node_yaml():
|
||||||
"--wormchainWS",
|
"--wormchainWS",
|
||||||
"ws://wormchain:26657/websocket",
|
"ws://wormchain:26657/websocket",
|
||||||
"--wormchainLCD",
|
"--wormchainLCD",
|
||||||
"http://wormchain:1317"
|
"http://wormchain:1317",
|
||||||
|
"--wormchainURL",
|
||||||
|
"wormchain:9090",
|
||||||
|
"--wormchainKeyPath",
|
||||||
|
"/tmp/mounted-keys/wormchain/wormchainKey",
|
||||||
|
"--wormchainKeyPassPhrase",
|
||||||
|
"test0000",
|
||||||
|
"--accountantWS",
|
||||||
|
"http://wormchain:26657",
|
||||||
|
"--accountantContract",
|
||||||
|
"wormhole14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9srrg465",
|
||||||
|
"--accountantCheckEnabled",
|
||||||
|
"true"
|
||||||
]
|
]
|
||||||
|
|
||||||
return encode_yaml_stream(node_yaml_with_replicas)
|
return encode_yaml_stream(node_yaml_with_replicas)
|
||||||
|
@ -303,7 +315,7 @@ if algorand:
|
||||||
if aptos:
|
if aptos:
|
||||||
guardian_resource_deps = guardian_resource_deps + ["aptos"]
|
guardian_resource_deps = guardian_resource_deps + ["aptos"]
|
||||||
if wormchain:
|
if wormchain:
|
||||||
guardian_resource_deps = guardian_resource_deps + ["wormchain"]
|
guardian_resource_deps = guardian_resource_deps + ["wormchain", "wormchain-deploy"]
|
||||||
if sui:
|
if sui:
|
||||||
guardian_resource_deps = guardian_resource_deps + ["sui"]
|
guardian_resource_deps = guardian_resource_deps + ["sui"]
|
||||||
|
|
||||||
|
@ -571,6 +583,12 @@ if ci_tests:
|
||||||
trigger_mode = trigger_mode,
|
trigger_mode = trigger_mode,
|
||||||
resource_deps = [], # testing/spydk.sh handles waiting for spy, not having deps gets the build earlier
|
resource_deps = [], # testing/spydk.sh handles waiting for spy, not having deps gets the build earlier
|
||||||
)
|
)
|
||||||
|
k8s_resource(
|
||||||
|
"accountant-ci-tests",
|
||||||
|
labels = ["ci"],
|
||||||
|
trigger_mode = trigger_mode,
|
||||||
|
resource_deps = ["const-gen"], # uses devnet-consts.json, but wormchain/contracts/tools/test_accountant.sh handles waiting for guardian, not having deps gets the build earlier
|
||||||
|
)
|
||||||
|
|
||||||
if terra_classic:
|
if terra_classic:
|
||||||
docker_build(
|
docker_build(
|
||||||
|
@ -822,7 +840,7 @@ if wormchain:
|
||||||
|
|
||||||
k8s_resource(
|
k8s_resource(
|
||||||
"wormchain-deploy",
|
"wormchain-deploy",
|
||||||
resource_deps = ["wormchain"],
|
resource_deps = ["const-gen", "wormchain"],
|
||||||
labels = ["wormchain"],
|
labels = ["wormchain"],
|
||||||
trigger_mode = trigger_mode,
|
trigger_mode = trigger_mode,
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,3 +47,28 @@ spec:
|
||||||
- "/app/testing/success"
|
- "/app/testing/success"
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
|
---
|
||||||
|
kind: Job
|
||||||
|
apiVersion: batch/v1
|
||||||
|
metadata:
|
||||||
|
name: accountant-ci-tests
|
||||||
|
spec:
|
||||||
|
backoffLimit: 0
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: accountant-ci-tests
|
||||||
|
image: wormchain-deploy
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- "bash /app/tools/test_accountant.sh && touch /app/tools/success"
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- test
|
||||||
|
- -e
|
||||||
|
- "/app/accountant/success"
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
| Test ERC20 | ETH | 0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A | Tokens minted to Test Wallet |
|
| Test ERC20 | ETH | 0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A | Tokens minted to Test Wallet |
|
||||||
| Test NFT | ETH | 0x5b9b42d6e4B2e4Bf8d42Eba32D46918e10899B66 | One minted to Test Wallet |
|
| Test NFT | ETH | 0x5b9b42d6e4B2e4Bf8d42Eba32D46918e10899B66 | One minted to Test Wallet |
|
||||||
| Test WETH | ETH | 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E | Tokens minted to Test Wallet |
|
| Test WETH | ETH | 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E | Tokens minted to Test Wallet |
|
||||||
|
| Test ERC20 GA | ETH | 0xf19A2A01B70519f67ADb309a994Ec8c69A967E8b | Tokens minted to Test Wallet 9 |
|
||||||
| Bridge Core | ETH | 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550 | |
|
| Bridge Core | ETH | 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550 | |
|
||||||
| Token Bridge | ETH | 0x0290FB167208Af455bB137780163b7B7a9a10C16 | |
|
| Token Bridge | ETH | 0x0290FB167208Af455bB137780163b7B7a9a10C16 | |
|
||||||
| NFT Bridge | ETH | 0x26b4afb60d6c903165150c6f0aa14f8016be4aec | |
|
| NFT Bridge | ETH | 0x26b4afb60d6c903165150c6f0aa14f8016be4aec | |
|
||||||
|
@ -35,7 +36,7 @@ The terra testnet can be used just like a normal localterra network (can be sele
|
||||||
|
|
||||||
### Algorand
|
### Algorand
|
||||||
|
|
||||||
The `admin.py` deployment tool can be used to generate a set of five prefunded accounts using the `--fundDevAccounts` option, with the following addresses and mnemonics:
|
The `admin.py` deployment tool can be used to generate a set of five prefunded accounts using the `--fundDevAccounts` option, with the following addresses and mnemonics:
|
||||||
|
|
||||||
DEV7AREMQSPWWDDFFJ3A5OIMMDCZN4YT5U2MQBN76Y4J5ERQQ3MWPIHUYA 800M ALGO
|
DEV7AREMQSPWWDDFFJ3A5OIMMDCZN4YT5U2MQBN76Y4J5ERQQ3MWPIHUYA 800M ALGO
|
||||||
provide warfare better filter glory civil help jacket alpha penalty van fiber code upgrade web more curve sauce merit bike satoshi blame orphan absorb modify
|
provide warfare better filter glory civil help jacket alpha penalty van fiber code upgrade web more curve sauce merit bike satoshi blame orphan absorb modify
|
||||||
|
|
|
@ -21,7 +21,7 @@ const interateToStandardTransactionCount = async () => {
|
||||||
to: accounts[0],
|
to: accounts[0],
|
||||||
from: accounts[0],
|
from: accounts[0],
|
||||||
value: 530,
|
value: 530,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const burnCount = await web3.eth.getTransactionCount(accounts[0], "latest");
|
const burnCount = await web3.eth.getTransactionCount(accounts[0], "latest");
|
||||||
|
@ -31,7 +31,7 @@ const interateToStandardTransactionCount = async () => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = async function (callback) {
|
module.exports = async function(callback) {
|
||||||
try {
|
try {
|
||||||
const accounts = await web3.eth.getAccounts();
|
const accounts = await web3.eth.getAccounts();
|
||||||
|
|
||||||
|
@ -96,6 +96,25 @@ module.exports = async function (callback) {
|
||||||
throw new Error("unexpected WETH token address");
|
throw new Error("unexpected WETH token address");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deploy token contract
|
||||||
|
const accountantTokenAddress = (
|
||||||
|
await ERC20.new("Accountant Test Token", "GA")
|
||||||
|
).address;
|
||||||
|
const accountantToken = new web3.eth.Contract(
|
||||||
|
ERC20.abi,
|
||||||
|
accountantTokenAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Accountant test token deployed at: " + accountantTokenAddress);
|
||||||
|
|
||||||
|
// mint 1000 units
|
||||||
|
await accountantToken.methods
|
||||||
|
.mint(accounts[9], "1000000000000000000000")
|
||||||
|
.send({
|
||||||
|
from: accounts[0],
|
||||||
|
gas: 1000000,
|
||||||
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callback(e);
|
callback(e);
|
||||||
|
|
|
@ -148,6 +148,12 @@
|
||||||
"name": "Wrapped Ether",
|
"name": "Wrapped Ether",
|
||||||
"symbol": "WETH",
|
"symbol": "WETH",
|
||||||
"decimals": 18
|
"decimals": 18
|
||||||
|
},
|
||||||
|
"testGA": {
|
||||||
|
"address": "0xf19A2A01B70519f67ADb309a994Ec8c69A967E8b",
|
||||||
|
"name": "Accountant Test Token",
|
||||||
|
"symbol": "GA",
|
||||||
|
"decimals": 18
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const ETH_PRIVATE_KEY7 =
|
||||||
export const ETH_PRIVATE_KEY8 =
|
export const ETH_PRIVATE_KEY8 =
|
||||||
"0x829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4"; // account 8 - unused
|
"0x829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4"; // account 8 - unused
|
||||||
export const ETH_PRIVATE_KEY9 =
|
export const ETH_PRIVATE_KEY9 =
|
||||||
"0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773"; // account 9 - unused
|
"0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773"; // account 9 - accountant tests
|
||||||
export const SOLANA_HOST = ci
|
export const SOLANA_HOST = ci
|
||||||
? "http://solana-devnet:8899"
|
? "http://solana-devnet:8899"
|
||||||
: "http://localhost:8899";
|
: "http://localhost:8899";
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,30 +1,32 @@
|
||||||
{
|
{
|
||||||
"name": "@wormhole-foundation/wormchain-contract-tools",
|
"name": "@wormhole-foundation/wormchain-contract-tools",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "scripts for working with wormchain contracts",
|
"description": "scripts for working with wormchain contracts",
|
||||||
"main": "deploy_wormchain.ts",
|
"main": "deploy_wormchain.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy-wormchain": "ts-node deploy_wormchain.ts",
|
"deploy-wormchain": "ts-node deploy_wormchain.ts",
|
||||||
"test-wormchain": "ts-node test_wormchain.ts",
|
"test-accountant": "ts-node test_accountant.ts",
|
||||||
"deploy-and-test": "npm run deploy-wormchain && npm run test-wormchain"
|
"test-wormchain": "ts-node test_wormchain.ts",
|
||||||
},
|
"deploy-and-test": "npm run deploy-wormchain && npm run test-wormchain"
|
||||||
"keywords": [],
|
},
|
||||||
"author": "",
|
"keywords": [],
|
||||||
"dependencies": {
|
"author": "",
|
||||||
"@certusone/wormhole-sdk": "0.9.9",
|
"dependencies": {
|
||||||
"@cosmjs/cosmwasm-stargate": "0.29.5",
|
"@certusone/wormhole-sdk": "0.9.9",
|
||||||
"@wormhole-foundation/wormchain-sdk": "file:../../ts-sdk",
|
"@cosmjs/cosmwasm-stargate": "0.29.5",
|
||||||
"cosmwasm": "1.1.1",
|
"@improbable-eng/grpc-web-node-http-transport": "0.15.0",
|
||||||
"dotenv": "16.0.3",
|
"@wormhole-foundation/wormchain-sdk": "file:../../ts-sdk",
|
||||||
"elliptic": "6.5.4",
|
"cosmwasm": "1.1.1",
|
||||||
"ethers": "5.7.2",
|
"dotenv": "16.0.3",
|
||||||
"js-sha3": "0.8.0",
|
"elliptic": "6.5.4",
|
||||||
"web3-eth-abi": "1.8.1",
|
"ethers": "5.7.2",
|
||||||
"yargs": "17.6.2"
|
"js-sha3": "0.8.0",
|
||||||
},
|
"web3-eth-abi": "1.8.1",
|
||||||
"devDependencies": {
|
"yargs": "17.6.2"
|
||||||
"@types/elliptic": "6.4.14",
|
},
|
||||||
"ts-node": "10.9.1",
|
"devDependencies": {
|
||||||
"typescript": "4.9.4"
|
"@types/elliptic": "6.4.14",
|
||||||
}
|
"ts-node": "10.9.1",
|
||||||
|
"typescript": "4.9.4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' guardian:6060/readyz)" != "200" ]]; do sleep 5; done
|
||||||
|
CI=true npm run test-accountant
|
|
@ -0,0 +1,371 @@
|
||||||
|
import "dotenv/config";
|
||||||
|
import {
|
||||||
|
approveEth,
|
||||||
|
attestFromEth,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CONTRACTS,
|
||||||
|
createWrappedOnEth,
|
||||||
|
getEmitterAddressEth,
|
||||||
|
getForeignAssetEth,
|
||||||
|
getSignedVAAWithRetry,
|
||||||
|
hexToUint8Array,
|
||||||
|
parseSequenceFromLogEth,
|
||||||
|
redeemOnEth,
|
||||||
|
serialiseVAA,
|
||||||
|
sign,
|
||||||
|
TokenBridgeTransfer,
|
||||||
|
transferFromEth,
|
||||||
|
tryNativeToUint8Array,
|
||||||
|
uint8ArrayToHex,
|
||||||
|
VAA,
|
||||||
|
} from "@certusone/wormhole-sdk";
|
||||||
|
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import * as devnetConsts from "./devnet-consts.json";
|
||||||
|
import { parseUnits } from "ethers/lib/utils";
|
||||||
|
|
||||||
|
if (process.env.INIT_SIGNERS_KEYS_CSV === "undefined") {
|
||||||
|
let msg = `.env is missing. run "make contracts-tools-deps" to fetch.`;
|
||||||
|
console.error(msg);
|
||||||
|
throw msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consider using jest
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Goals:
|
||||||
|
* 1. Ensure a token can be sent from its origin chain
|
||||||
|
* 2. Ensure a token can be sent back from a foreign chain
|
||||||
|
* 3. Ensure spoofed tokens for more than the outstanding amount rejects successfully
|
||||||
|
* 4. Validate the guardian metrics for each of these cases
|
||||||
|
* 5. Bonus: Validate the on chain contract state via queries
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ci = !!process.env.CI;
|
||||||
|
|
||||||
|
const GUARDIAN_HOST = ci ? "guardian" : "localhost";
|
||||||
|
const GUARDIAN_RPCS = [`http://${GUARDIAN_HOST}:7071`];
|
||||||
|
const GUARDIAN_METRICS = `http://${GUARDIAN_HOST}:6060/metrics`;
|
||||||
|
const ETH_NODE_URL = ci ? "ws://eth-devnet:8545" : "ws://localhost:8545";
|
||||||
|
const BSC_NODE_URL = ci ? "ws://eth-devnet2:8545" : "ws://localhost:8546";
|
||||||
|
const ETH_PRIVATE_KEY9 =
|
||||||
|
"0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773";
|
||||||
|
const ETH_GA_TEST_TOKEN =
|
||||||
|
devnetConsts.chains[CHAIN_ID_ETH].addresses.testGA.address;
|
||||||
|
const DECIMALS = devnetConsts.chains[CHAIN_ID_ETH].addresses.testGA.decimals;
|
||||||
|
const VAA_SIGNERS = process.env.INIT_SIGNERS_KEYS_CSV.split(",");
|
||||||
|
const GOVERNANCE_CHAIN = Number(devnetConsts.global.governanceChainId);
|
||||||
|
const GOVERNANCE_EMITTER = devnetConsts.global.governanceEmitterAddress;
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guardian metrics are prometheus data
|
||||||
|
const fetchGlobalAccountantMetrics = async (): Promise<{
|
||||||
|
global_accountant_connection_errors_total: number;
|
||||||
|
global_accountant_error_events_received: number;
|
||||||
|
global_accountant_events_received: number;
|
||||||
|
global_accountant_submit_failures: number;
|
||||||
|
global_accountant_total_balance_errors: number;
|
||||||
|
global_accountant_total_digest_mismatches: number;
|
||||||
|
global_accountant_transfer_vaas_outstanding: number;
|
||||||
|
global_accountant_transfer_vaas_submitted: number;
|
||||||
|
global_accountant_transfer_vaas_submitted_and_approved: number;
|
||||||
|
}> =>
|
||||||
|
(await (await fetch(GUARDIAN_METRICS)).text())
|
||||||
|
.split("\n")
|
||||||
|
.filter((m) => m.startsWith("global_accountant"))
|
||||||
|
.reduce((p, m) => {
|
||||||
|
const [k, v] = m.split(" ");
|
||||||
|
p[k] = Number(v);
|
||||||
|
return p;
|
||||||
|
}, {} as any);
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
//
|
||||||
|
// PREAMBLE
|
||||||
|
//
|
||||||
|
|
||||||
|
// create a signer for Eth
|
||||||
|
const ethProvider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
|
||||||
|
const ethSigner = new ethers.Wallet(ETH_PRIVATE_KEY9, ethProvider);
|
||||||
|
// create a signer for BSC
|
||||||
|
const bscProvider = new ethers.providers.WebSocketProvider(BSC_NODE_URL);
|
||||||
|
const bscSigner = new ethers.Wallet(ETH_PRIVATE_KEY9, bscProvider);
|
||||||
|
|
||||||
|
let attestedAddress = "";
|
||||||
|
|
||||||
|
//
|
||||||
|
// STEP 0 - attest the token
|
||||||
|
//
|
||||||
|
{
|
||||||
|
attestedAddress = await getForeignAssetEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
bscProvider,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
tryNativeToUint8Array(ETH_GA_TEST_TOKEN, CHAIN_ID_ETH)
|
||||||
|
);
|
||||||
|
if (attestedAddress && attestedAddress !== ethers.constants.AddressZero) {
|
||||||
|
console.log("already attested");
|
||||||
|
} else {
|
||||||
|
console.log("attesting...");
|
||||||
|
// attest the test token
|
||||||
|
const receipt = await attestFromEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge,
|
||||||
|
ethSigner,
|
||||||
|
ETH_GA_TEST_TOKEN
|
||||||
|
);
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogEth(
|
||||||
|
receipt,
|
||||||
|
CONTRACTS.DEVNET.ethereum.core
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge
|
||||||
|
);
|
||||||
|
console.log(`fetching vaa ${sequence}...`);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
GUARDIAN_RPCS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("creating...");
|
||||||
|
await createWrappedOnEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
bscSigner,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
|
attestedAddress = await getForeignAssetEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
bscProvider,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
tryNativeToUint8Array(ETH_GA_TEST_TOKEN, CHAIN_ID_ETH)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STEP 1 - send the token out
|
||||||
|
//
|
||||||
|
{
|
||||||
|
const beforeMetrics = await fetchGlobalAccountantMetrics();
|
||||||
|
const amount = parseUnits("1", DECIMALS);
|
||||||
|
// approve the bridge to spend tokens
|
||||||
|
console.log("approving...");
|
||||||
|
await approveEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge,
|
||||||
|
ETH_GA_TEST_TOKEN,
|
||||||
|
ethSigner,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
// transfer tokens out
|
||||||
|
console.log("transferring...");
|
||||||
|
const receipt = await transferFromEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge,
|
||||||
|
ethSigner,
|
||||||
|
ETH_GA_TEST_TOKEN,
|
||||||
|
amount,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
|
tryNativeToUint8Array(await bscSigner.getAddress(), CHAIN_ID_BSC)
|
||||||
|
);
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogEth(
|
||||||
|
receipt,
|
||||||
|
CONTRACTS.DEVNET.ethereum.core
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge
|
||||||
|
);
|
||||||
|
console.log(`fetching vaa ${sequence}...`);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
GUARDIAN_RPCS,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("redeeming...");
|
||||||
|
await redeemOnEth(CONTRACTS.DEVNET.bsc.token_bridge, bscSigner, signedVAA);
|
||||||
|
const afterMetrics = await fetchGlobalAccountantMetrics();
|
||||||
|
console.log(
|
||||||
|
"approved b/a:",
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted_and_approved,
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted_and_approved
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
afterMetrics.global_accountant_events_received <=
|
||||||
|
beforeMetrics.global_accountant_events_received ||
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted <=
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted ||
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted_and_approved <=
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted_and_approved
|
||||||
|
) {
|
||||||
|
throw new Error("Expected metrics change did not occur");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STEP 2 - send the token back
|
||||||
|
//
|
||||||
|
{
|
||||||
|
const beforeMetrics = await fetchGlobalAccountantMetrics();
|
||||||
|
const amount = parseUnits("1", DECIMALS);
|
||||||
|
// approve the bridge to spend tokens
|
||||||
|
console.log("approving...");
|
||||||
|
await approveEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
attestedAddress,
|
||||||
|
bscSigner,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
// transfer tokens out
|
||||||
|
console.log("transferring...");
|
||||||
|
const receipt = await transferFromEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
bscSigner,
|
||||||
|
attestedAddress,
|
||||||
|
amount,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
tryNativeToUint8Array(await ethSigner.getAddress(), CHAIN_ID_ETH)
|
||||||
|
);
|
||||||
|
// get the sequence from the logs (needed to fetch the vaa)
|
||||||
|
const sequence = parseSequenceFromLogEth(
|
||||||
|
receipt,
|
||||||
|
CONTRACTS.DEVNET.bsc.core
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge
|
||||||
|
);
|
||||||
|
console.log(`fetching vaa ${sequence}...`);
|
||||||
|
// poll until the guardian(s) witness and sign the vaa
|
||||||
|
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
|
||||||
|
GUARDIAN_RPCS,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
|
emitterAddress,
|
||||||
|
sequence,
|
||||||
|
{
|
||||||
|
transport: NodeHttpTransport(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("redeeming...");
|
||||||
|
await redeemOnEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge,
|
||||||
|
ethSigner,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
|
const afterMetrics = await fetchGlobalAccountantMetrics();
|
||||||
|
console.log(
|
||||||
|
"approved b/a:",
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted_and_approved,
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted_and_approved
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
afterMetrics.global_accountant_events_received <=
|
||||||
|
beforeMetrics.global_accountant_events_received ||
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted <=
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted ||
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted_and_approved <=
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted_and_approved
|
||||||
|
) {
|
||||||
|
throw new Error("Expected metrics change did not occur");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STEP 3a - redeem spoofed tokens
|
||||||
|
//
|
||||||
|
{
|
||||||
|
console.log("redeeming spoofed tokens");
|
||||||
|
let vaa: VAA<TokenBridgeTransfer> = {
|
||||||
|
version: 1,
|
||||||
|
guardianSetIndex: 0,
|
||||||
|
signatures: [],
|
||||||
|
timestamp: 0,
|
||||||
|
nonce: 0,
|
||||||
|
emitterChain: CHAIN_ID_ETH,
|
||||||
|
emitterAddress: getEmitterAddressEth(
|
||||||
|
CONTRACTS.DEVNET.ethereum.token_bridge
|
||||||
|
),
|
||||||
|
sequence: BigInt(979999116 + Math.floor(Math.random() * 100000000)),
|
||||||
|
consistencyLevel: 0,
|
||||||
|
payload: {
|
||||||
|
module: "TokenBridge",
|
||||||
|
type: "Transfer",
|
||||||
|
tokenChain: CHAIN_ID_ETH,
|
||||||
|
tokenAddress: uint8ArrayToHex(
|
||||||
|
tryNativeToUint8Array(ETH_GA_TEST_TOKEN, CHAIN_ID_ETH)
|
||||||
|
),
|
||||||
|
amount: parseUnits("9000", DECIMALS).toBigInt(),
|
||||||
|
toAddress: uint8ArrayToHex(
|
||||||
|
tryNativeToUint8Array(await bscSigner.getAddress(), CHAIN_ID_BSC)
|
||||||
|
),
|
||||||
|
chain: CHAIN_ID_BSC,
|
||||||
|
fee: BigInt(0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
vaa.signatures = sign(VAA_SIGNERS, vaa);
|
||||||
|
await redeemOnEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
bscSigner,
|
||||||
|
hexToUint8Array(serialiseVAA(vaa))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STEP 3b - send the spoofed tokens back
|
||||||
|
//
|
||||||
|
{
|
||||||
|
const beforeMetrics = await fetchGlobalAccountantMetrics();
|
||||||
|
const amount = parseUnits("9000", DECIMALS);
|
||||||
|
// approve the bridge to spend tokens
|
||||||
|
console.log("approving...");
|
||||||
|
await approveEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
attestedAddress,
|
||||||
|
bscSigner,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
// transfer tokens out
|
||||||
|
console.log("transferring...");
|
||||||
|
const receipt = await transferFromEth(
|
||||||
|
CONTRACTS.DEVNET.bsc.token_bridge,
|
||||||
|
bscSigner,
|
||||||
|
attestedAddress,
|
||||||
|
amount,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
tryNativeToUint8Array(await ethSigner.getAddress(), CHAIN_ID_ETH)
|
||||||
|
);
|
||||||
|
console.log("waiting 30s to fetch metrics...");
|
||||||
|
await sleep(30 * 1000); // give the guardian a few seconds to pick up the transfers and attempt to submit them
|
||||||
|
const afterMetrics = await fetchGlobalAccountantMetrics();
|
||||||
|
console.log(
|
||||||
|
"balance errors b/a:",
|
||||||
|
beforeMetrics.global_accountant_total_balance_errors,
|
||||||
|
afterMetrics.global_accountant_total_balance_errors
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
afterMetrics.global_accountant_error_events_received <=
|
||||||
|
beforeMetrics.global_accountant_error_events_received ||
|
||||||
|
afterMetrics.global_accountant_transfer_vaas_submitted <=
|
||||||
|
beforeMetrics.global_accountant_transfer_vaas_submitted ||
|
||||||
|
afterMetrics.global_accountant_total_balance_errors <=
|
||||||
|
beforeMetrics.global_accountant_total_balance_errors
|
||||||
|
) {
|
||||||
|
throw new Error("Expected metrics change did not occur");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ethProvider.destroy();
|
||||||
|
bscProvider.destroy();
|
||||||
|
console.log("success!");
|
||||||
|
})();
|
Loading…
Reference in New Issue