Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evm safe upgrades #1272

Merged
merged 6 commits into from Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/build.yml
Expand Up @@ -67,6 +67,18 @@ jobs:
node-version: "16"
- run: cd ethereum && make test

ethereum-upgrade:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "16"
- run: cd clients/js && make install
- run: curl -L https://foundry.paradigm.xyz | bash
- run: $HOME/.foundry/bin/foundryup
- run: cd ethereum && PATH=$PATH:$HOME/.foundry/bin/ make test-upgrade

terra:
runs-on: ubuntu-20.04
steps:
Expand Down
319 changes: 304 additions & 15 deletions clients/js/evm.ts

Large diffs are not rendered by default.

238 changes: 229 additions & 9 deletions clients/js/main.ts
Expand Up @@ -3,9 +3,9 @@ import yargs from "yargs";

import { hideBin } from "yargs/helpers";

import { isTerraChain, setDefaultWasm } from "@certusone/wormhole-sdk";
import { isTerraChain, assertEVMChain, CONTRACTS, setDefaultWasm } from "@certusone/wormhole-sdk";
import { execute_governance_solana } from "./solana";
import { execute_governance_evm } from "./evm";
import { execute_governance_evm, hijack_evm, query_contract_evm, setStorageAt } from "./evm";
import { execute_governance_terra } from "./terra";
import * as vaa from "./vaa";
import { impossible, Payload, serialiseVAA, VAA } from "./vaa";
Expand All @@ -17,6 +17,8 @@ import {
isEVMChain,
toChainId,
} from "@certusone/wormhole-sdk";
import { ethers } from "ethers";
import { NETWORKS } from "./networks";

setDefaultWasm("node");

Expand Down Expand Up @@ -58,7 +60,7 @@ yargs(hideBin(process.argv))
.option("guardian-secret", {
alias: "g",
required: true,
describe: "Guardians' secret keys",
describe: "Guardians' secret keys (CSV)",
type: "string",
})
// Registration
Expand Down Expand Up @@ -148,7 +150,7 @@ yargs(hideBin(process.argv))
type: "ContractUpgrade",
chain: toChainId(argv["chain"]),
address: Buffer.from(
argv["contract-address"].padStart(64, "0"),
evm_address(argv["contract-address"]),
"hex"
),
};
Expand All @@ -168,7 +170,7 @@ yargs(hideBin(process.argv))
}
)
////////////////////////////////////////////////////////////////////////////////
// Parse
// Misc
.command(
"parse <vaa>",
"Parse a VAA",
Expand All @@ -182,6 +184,206 @@ yargs(hideBin(process.argv))
const buf = Buffer.from(String(argv.vaa), "hex");
const parsed_vaa = vaa.parse(buf);
console.log(parsed_vaa);
console.log("Digest:", vaa.vaaDigest(parsed_vaa))
})
.command("recover <digest> <signature>", "Recover an address from a signature", (yargs) => {
return yargs
.positional("digest", {
describe: "digest",
type: "string"
})
.positional("signature", {
describe: "signature",
type: "string"
});
}, async (argv) => {
console.log(ethers.utils.recoverAddress(hex(argv["digest"]), hex(argv["signature"])))
})
.command("contract <network> <chain> <module>", "Print contract address", (yargs) => {
return yargs
.positional("network", {
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
})
.positional("chain", {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
})
.positional("module", {
describe: "Module to query",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
})
}, async (argv) => {
assertChain(argv["chain"])
assertEVMChain(argv["chain"])
const network = argv.network.toUpperCase();
if (
network !== "MAINNET" &&
network !== "TESTNET" &&
network !== "DEVNET"
) {
throw Error(`Unknown network: ${network}`);
}
let module = argv["module"] as
| "Core"
| "NFTBridge"
| "TokenBridge";
switch (module) {
case "Core":
console.log(CONTRACTS[network][argv["chain"]]["core"])
break;
case "NFTBridge":
console.log(CONTRACTS[network][argv["chain"]]["nft_bridge"])
break;
case "TokenBridge":
console.log(CONTRACTS[network][argv["chain"]]["token_bridge"])
break;
default:
impossible(module)
}
})
.command("rpc <network> <chain>", "Print RPC address", (yargs) => {
return yargs
.positional("network", {
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
})
.positional("chain", {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
})
}, async (argv) => {
assertChain(argv["chain"])
assertEVMChain(argv["chain"])
const network = argv.network.toUpperCase();
if (
network !== "MAINNET" &&
network !== "TESTNET" &&
network !== "DEVNET"
) {
throw Error(`Unknown network: ${network}`);
}
console.log(NETWORKS[network][argv["chain"]].rpc)
})
////////////////////////////////////////////////////////////////////////////////
// Evm utilities
.command("evm", "EVM utilites", (yargs) => {
return yargs
.option("rpc", {
describe: "RPC endpoint",
type: "string",
required: false
})
.command("address-from-secret <secret>", "Compute a 20 byte eth address from a 32 byte private key", (yargs) => {
return yargs
.positional("secret", { type: "string", describe: "Secret key (32 bytes)" })
}, (argv) => {
console.log(ethers.utils.computeAddress(argv["secret"]))
})
.command("storage-update", "Update a storage slot on an EVM fork during testing (anvil or hardhat)", (yargs) => {
return yargs
.option("contract-address", {
alias: "a",
describe: "Contract address",
type: "string",
required: true,
})
.option("storage-slot", {
alias: "k",
describe: "Storage slot to modify",
type: "string",
required: true,
})
.option("value", {
alias: "v",
describe: "Value to write into the slot (32 bytes)",
type: "string",
required: true,
});
}, async (argv) => {
const result = await setStorageAt(argv["rpc"], evm_address(argv["contract-address"]), argv["storage-slot"], ["uint256"], [argv["value"]]);
console.log(result);
})
.command("info", "Query info about the on-chain state of the contract", (yargs) => {
return yargs
.option("chain", {
alias: "c",
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
required: true,
})
.option("module", {
alias: "m",
describe: "Module to query",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
required: true,
})
.option("network", {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
})
.option("contract-address", {
alias: "a",
describe: "Contract to query (override config)",
type: "string",
required: false,
});
}, async (argv) => {
assertChain(argv["chain"])
assertEVMChain(argv["chain"])
const network = argv.network.toUpperCase();
if (
network !== "MAINNET" &&
network !== "TESTNET" &&
network !== "DEVNET"
) {
throw Error(`Unknown network: ${network}`);
}
let module = argv["module"] as
| "Core"
| "NFTBridge"
| "TokenBridge";
let rpc = argv["rpc"] ?? NETWORKS[network][argv["chain"]].rpc
console.log(JSON.stringify(await query_contract_evm(network, argv["chain"], module, argv["contract-address"], rpc), null, 2))
})
.command("hijack", "Override the guardian set of the core bridge contract during testing (anvil or hardhat)", (yargs) => {
return yargs
.option("core-contract-address", {
alias: "a",
describe: "Core contract address",
type: "string",
default: CONTRACTS.MAINNET.ethereum.core,
})
.option("guardian-address", {
alias: "g",
required: true,
describe: "Guardians' public addresses (CSV)",
type: "string",
})
.option("guardian-set-index", {
alias: "i",
required: false,
describe: "New guardian set index (if unspecified, default to overriding the current index)",
type: "number"
});
}, async (argv) => {
const guardian_addresses = argv["guardian-address"].split(",")
let rpc = argv["rpc"] ?? NETWORKS.DEVNET.ethereum.rpc
await hijack_evm(rpc, argv["core-contract-address"], guardian_addresses, argv["guardian-set-index"])
})
},
(_) => {
yargs.showHelp();
}
)
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -209,9 +411,19 @@ yargs(hideBin(process.argv))
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
});
},
async (argv) => {
})
.option("contract-address", {
alias: "a",
describe: "Contract to submit VAA to (override config)",
type: "string",
required: false,
})
.option("rpc", {
describe: "RPC endpoint",
type: "string",
required: false
})
}, async (argv) => {
const vaa_hex = String(argv.vaa);
const buf = Buffer.from(vaa_hex, "hex");
const parsed_vaa = vaa.parse(buf);
Expand Down Expand Up @@ -270,7 +482,7 @@ yargs(hideBin(process.argv))
"This VAA does not specify the target chain, please provide it by hand using the '--chain' flag."
);
} else if (isEVMChain(chain)) {
await execute_governance_evm(parsed_vaa.payload, buf, network, chain);
await execute_governance_evm(parsed_vaa.payload, buf, network, chain, argv["contract-address"], argv["rpc"]);
} else if (isTerraChain(chain)) {
await execute_governance_terra(parsed_vaa.payload, buf, network, chain);
} else if (chain === "solana") {
Expand All @@ -286,3 +498,11 @@ yargs(hideBin(process.argv))
}
}
).argv;

function hex(x: string): string {
return ethers.utils.hexlify(x, { allowMissingPrefix: true })
}

function evm_address(x: string): string {
return hex(x).substring(2).padStart(64, "0")
}