tilt: devnet deployment for ibc generic messaging (#2593)

* Tilt devnet deployment for ibc generic messaging

* Address review comments from kcsongor and hendrikhofstadt

* Add IBC channel whitelist updates to wormchain and terra devnet deploy scripts

* VAAs had guardian set index three instead of zero

* ci: update addresses

* Remove message.block_height and message.tx_index from attributes

* Remove unnecessary contracts from terra2 devnet deployment

* Update wormhole-ibc address on terra2

* Update wormhole-ibc guardian set on terra2 devnet deployment

* IBC relayer testnet deployment fixes

* Wormchain update whitelist fix

---------

Co-authored-by: Bruce Riley <briley@jumptrading.com>
Co-authored-by: Evan Gray <battledingo@gmail.com>
This commit is contained in:
Nikhil Suri 2023-05-18 17:56:18 -05:00 committed by GitHub
parent 892274ffa4
commit f6f93bf35e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 463 additions and 261 deletions

View File

@ -85,7 +85,7 @@ ci_tests = cfg.get("ci_tests", ci)
guardiand_debug = cfg.get("guardiand_debug", False) guardiand_debug = cfg.get("guardiand_debug", False)
node_metrics = cfg.get("node_metrics", False) node_metrics = cfg.get("node_metrics", False)
guardiand_governor = cfg.get("guardiand_governor", False) guardiand_governor = cfg.get("guardiand_governor", False)
ibc_relayer = cfg.get("ibc_relayer", False) ibc_relayer = cfg.get("ibc_relayer", ci)
btc = cfg.get("btc", False) btc = cfg.get("btc", False)
if cfg.get("manual", False): if cfg.get("manual", False):
@ -287,7 +287,13 @@ def build_node_yaml():
"--accountantContract", "--accountantContract",
"wormhole14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9srrg465", "wormhole14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9srrg465",
"--accountantCheckEnabled", "--accountantCheckEnabled",
"true" "true",
"--ibcWS",
"ws://wormchain:26657/websocket",
"--ibcLCD",
"http://wormchain:1317",
"--ibcContract",
"wormhole1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq0kdhcj"
] ]
return encode_yaml_stream(node_yaml_with_replicas) return encode_yaml_stream(node_yaml_with_replicas)
@ -854,7 +860,7 @@ if ibc_relayer:
port_forwards = [ port_forwards = [
port_forward(7597, name = "HTTPDEBUG [:7597]", host = webHost), port_forward(7597, name = "HTTPDEBUG [:7597]", host = webHost),
], ],
resource_deps = ["wormchain", "terra2-terrad"], resource_deps = ["wormchain-deploy", "terra2-terrad"],
labels = ["ibc-relayer"], labels = ["ibc-relayer"],
trigger_mode = trigger_mode, trigger_mode = trigger_mode,
) )

View File

@ -101,15 +101,13 @@ fn handle_packet_receive(msg: IbcPacketReceiveMsg) -> Result<IbcReceiveResponse,
} }
} }
const EXPECTED_WORMHOLE_IBC_EVENT_ATTRS: [&str; 8] = [ const EXPECTED_WORMHOLE_IBC_EVENT_ATTRS: [&str; 6] = [
"message.message", "message.message",
"message.sender", "message.sender",
"message.chain_id", "message.chain_id",
"message.nonce", "message.nonce",
"message.sequence", "message.sequence",
"message.block_time", "message.block_time",
"message.tx_index",
"message.block_height",
]; ];
fn receive_publish( fn receive_publish(

View File

@ -148,22 +148,8 @@ fn post_message_ibc(
// compute the packet timeout // compute the packet timeout
let packet_timeout = env.block.time.plus_seconds(PACKET_LIFETIME).into(); let packet_timeout = env.block.time.plus_seconds(PACKET_LIFETIME).into();
// compute the block height
let block_height = env.block.height.to_string();
// compute the transaction index
// (this is an optional since not all messages are executed as part of txns)
// (they may be executed part of the pre/post block handlers)
let tx_index = env.transaction.as_ref().map(|tx_info| tx_info.index);
// actually execute the postMessage call on the core contract // actually execute the postMessage call on the core contract
let mut res = core_execute(deps, env, info, msg).context("wormhole core execution failed")?; let res = core_execute(deps, env, info, msg).context("wormhole core execution failed")?;
res = match tx_index {
Some(index) => res.add_attribute("message.tx_index", index.to_string()),
None => res,
};
res = res.add_attribute("message.block_height", block_height);
// Send the result attributes over IBC on this channel // Send the result attributes over IBC on this channel
let packet = WormholeIbcPacketMsg::Publish { let packet = WormholeIbcPacketMsg::Publish {

View File

@ -9,6 +9,12 @@ import { readFileSync, readdirSync } from "fs";
import { Bech32, toHex } from "@cosmjs/encoding"; import { Bech32, toHex } from "@cosmjs/encoding";
import { zeroPad } from "ethers/lib/utils.js"; import { zeroPad } from "ethers/lib/utils.js";
// Generated using
// `guardiand template ibc-receiver-update-channel-chain --channel-id channel-0 --chain-id 3104 --target-chain-id 32 > terra2.prototxt`
// `guardiand admin governance-vaa-verify terra2.prototxt`
const WORMHOLE_IBC_WHITELIST_VAA =
"0100000000010025e55ab23c8d0a7fddd4686f41801792cdce1ff7335a2b9436192bd552fa0f9b5c18016057b0d4b3f24c759eafe3e5fedd7fce76fe6f21cec815ffbaf4ec3ad801000000009b9a6b2d0001000000000000000000000000000000000000000000000000000000000000000460efd4405060ac0c200000000000000000000000000000000000000000004962635265636569766572010020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006368616e6e656c2d300c20";
/* /*
NOTE: Only append to this array: keeping the ordering is crucial, as the NOTE: Only append to this array: keeping the ordering is crucial, as the
contracts must be imported in a deterministic order so their addresses remain contracts must be imported in a deterministic order so their addresses remain
@ -19,11 +25,6 @@ const artifacts = [
"cw_token_bridge.wasm", "cw_token_bridge.wasm",
"cw20_wrapped_2.wasm", "cw20_wrapped_2.wasm",
"cw20_base.wasm", "cw20_base.wasm",
"mock_bridge_integration_2.wasm",
"shutdown_core_bridge_cosmwasm.wasm",
"shutdown_token_bridge_cosmwasm.wasm",
"global_accountant.wasm",
"wormchain_ibc_receiver.wasm",
"wormhole_ibc.wasm", "wormhole_ibc.wasm",
]; ];
@ -50,18 +51,6 @@ if (missing_artifacts.length) {
process.exit(1); process.exit(1);
} }
const unexpected_artifacts = actual_artifacts.filter(
(a) => !artifacts.includes(a)
);
if (unexpected_artifacts.length) {
console.log(
"Error during terra deployment. The following files are not expected to be in the artifacts folder:"
);
unexpected_artifacts.forEach((file) => console.log(` - ${file}`));
console.log("Hint: you might need to modify tools/deploy.js");
process.exit(1);
}
/* Set up terra client & wallet */ /* Set up terra client & wallet */
const terra = new LCDClient({ const terra = new LCDClient({
@ -208,6 +197,28 @@ addresses["mock.wasm"] = await instantiate(
"mock" "mock"
); );
addresses["wormhole_ibc.wasm"] = await instantiate(
"wormhole_ibc.wasm",
{
gov_chain: govChain,
gov_address: Buffer.from(govAddress, "hex").toString("base64"),
guardian_set_expirity: 86400,
initial_guardian_set: {
// This is using one guardian so the above registration can be hard-coded
// TODO: instantiate with the correct guardian set and dynamically generate the registration
addresses: [
{
bytes: Buffer.from(init_guardians[0], "hex").toString("base64"),
},
],
expiration_time: 0,
},
chain_id: 32,
fee_denom: "uluna",
},
"wormholeIbc"
);
/* Registrations: tell the bridge contracts to know about each other */ /* Registrations: tell the bridge contracts to know about each other */
const contract_registrations = { const contract_registrations = {
@ -253,10 +264,49 @@ for (const [contract, registrations] of Object.entries(
memo: "", memo: "",
}) })
.then((tx) => terra.tx.broadcast(tx)) .then((tx) => terra.tx.broadcast(tx))
.then((rs) => console.log(rs)); .then((rs) => console.log(rs))
.catch((error) => {
if (error.response) {
// Request made and server responded
console.error(
error.response.data,
error.response.status,
error.response.headers
);
} else if (error.request) {
// The request was made but no response was received
console.error(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.error("Error", error.message);
}
throw new Error(`Registering chain failed: ${registration}`);
});
} }
} }
// submit wormchain channel ID whitelist to the wormhole_ibc contract
const ibc_whitelist_tx = await wallet.createAndSignTx({
msgs: [
new MsgExecuteContract(
wallet.key.accAddress,
addresses["wormhole_ibc.wasm"],
{
submit_update_channel_chain: {
vaa: Buffer.from(WORMHOLE_IBC_WHITELIST_VAA, "hex").toString(
"base64"
),
},
},
{ uluna: 1000 }
),
],
memo: "",
});
const ibc_whitelist_res = await terra.tx.broadcast(ibc_whitelist_tx);
console.log("updated wormhole_ibc channel whitelist", ibc_whitelist_res.txhash);
// Terra addresses are "human-readable", but for cross-chain registrations, we // Terra addresses are "human-readable", but for cross-chain registrations, we
// want the "canonical" version // want the "canonical" version
function convert_terra_address_to_hex(human_addr) { function convert_terra_address_to_hex(human_addr) {

View File

@ -37,7 +37,14 @@ spec:
- link-then-start - link-then-start
- terra-wormchain - terra-wormchain
- --debug-addr - --debug-addr
- localhost:7597 - 0.0.0.0:7597
- --src-port
- wasm.terra1436kxs0w2es6xlqpp9rd35e3d0cjnw4sv8j3a7483sgks29jqwgsnyey7t
- --dst-port
- wasm.wormhole1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq0kdhcj
- --version
- ibc-wormhole-v1
- --override
ports: ports:
- containerPort: 7597 - containerPort: 7597
name: rest name: rest
@ -48,4 +55,4 @@ spec:
path: / path: /
periodSeconds: 1 periodSeconds: 1
restartPolicy: Always restartPolicy: Always
serviceName: ibc-relayer serviceName: ibc-relayer

View File

@ -0,0 +1,84 @@
import { describe, test } from "@jest/globals";
import {
LCDClient,
MnemonicKey,
Msg,
MsgExecuteContract,
Wallet,
isTxError,
} from "@terra-money/terra.js";
import { getEmitterAddressTerra, parseSequenceFromLogTerra } from "../..";
import {
TERRA2_NODE_URL,
TERRA_CHAIN_ID,
} from "../../token_bridge/__tests__/utils/consts";
import {
getSignedVAABySequence,
waitForTerraExecution,
} from "../../token_bridge/__tests__/utils/helpers";
import { CHAIN_ID_SEI, CHAIN_ID_TERRA2 } from "../../utils/consts";
const TERRA2_PRIVATE_KEY_4 =
"bounce success option birth apple portion aunt rural episode solution hockey pencil lend session cause hedgehog slender journey system canvas decorate razor catch empty";
const lcd = new LCDClient({
URL: TERRA2_NODE_URL,
chainID: TERRA_CHAIN_ID,
});
const terraWallet = lcd.wallet(
new MnemonicKey({ mnemonic: TERRA2_PRIVATE_KEY_4 })
);
const terraWalletAddress = terraWallet.key.accAddress;
const terraBroadcastAndWaitForExecution = async (
msgs: Msg[],
wallet: Wallet
) => {
const tx = await wallet.createAndSignTx({
msgs,
});
const txResult = await lcd.tx.broadcast(tx);
if (isTxError(txResult)) {
throw new Error("tx error");
}
const txInfo = await waitForTerraExecution(txResult.txhash, lcd);
if (!txInfo) {
throw new Error("tx info not found");
}
return txInfo;
};
const terraBroadcastTxAndGetSignedVaa = async (
msgs: Msg[],
wallet: Wallet,
emitter: string
) => {
const txInfo = await terraBroadcastAndWaitForExecution(msgs, wallet);
const txSequence = parseSequenceFromLogTerra(txInfo);
if (!txSequence) {
throw new Error("tx sequence not found");
}
console.log(`${CHAIN_ID_SEI}/${emitter}/${txSequence}`);
return await getSignedVAABySequence(CHAIN_ID_SEI, txSequence, emitter);
};
describe("IBC Watcher Integration Tests", () => {
test('Send a message from "Sei" (Terra2) via IBC', async () => {
const postMsg = new MsgExecuteContract(
terraWalletAddress,
"terra1436kxs0w2es6xlqpp9rd35e3d0cjnw4sv8j3a7483sgks29jqwgsnyey7t",
{
post_message: {
message: Buffer.from("Hello World").toString("base64"),
nonce: 1,
},
}
);
const postedVaa = await terraBroadcastTxAndGetSignedVaa(
[postMsg],
terraWallet,
await getEmitterAddressTerra(terraWalletAddress)
);
console.log(postedVaa);
});
});

View File

@ -2,4 +2,5 @@
set -e set -e
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' guardian:6060/readyz)" != "200" ]]; do sleep 5; done while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' guardian:6060/readyz)" != "200" ]]; do sleep 5; done
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' spy:6060/metrics)" != "200" ]]; do sleep 5; done while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' spy:6060/metrics)" != "200" ]]; do sleep 5; done
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ibc-relayer:7597/debug/pprof/)" != "200" ]]; do sleep 5; done
CI=true npm --prefix ../sdk/js run test-ci CI=true npm --prefix ../sdk/js run test-ci

View File

@ -1,17 +1,17 @@
import { import {
CHAIN_ID_WORMCHAIN, CHAIN_ID_WORMCHAIN,
hexToUint8Array, hexToUint8Array,
Other, Other,
Payload, Payload,
serialiseVAA, serialiseVAA,
sign, sign,
VAA, VAA,
} from "@certusone/wormhole-sdk"; } from "@certusone/wormhole-sdk";
import { toBinary } from "@cosmjs/cosmwasm-stargate"; import { toBinary } from "@cosmjs/cosmwasm-stargate";
import { fromBase64, toUtf8 } from "@cosmjs/encoding"; import { fromBase64, toUtf8, fromBech32 } from "@cosmjs/encoding";
import { import {
getWallet, getWallet,
getWormchainSigningClient, getWormchainSigningClient,
} from "@wormhole-foundation/wormchain-sdk"; } from "@wormhole-foundation/wormchain-sdk";
import { ZERO_FEE } from "@wormhole-foundation/wormchain-sdk/lib/core/consts"; import { ZERO_FEE } from "@wormhole-foundation/wormchain-sdk/lib/core/consts";
import "dotenv/config"; import "dotenv/config";
@ -23,9 +23,9 @@ import * as util from "util";
import * as devnetConsts from "./devnet-consts.json"; import * as devnetConsts from "./devnet-consts.json";
if (process.env.INIT_SIGNERS_KEYS_CSV === "undefined") { if (process.env.INIT_SIGNERS_KEYS_CSV === "undefined") {
let msg = `.env is missing. run "make contracts-tools-deps" to fetch.`; let msg = `.env is missing. run "make contracts-tools-deps" to fetch.`;
console.error(msg); console.error(msg);
throw msg; throw msg;
} }
const VAA_SIGNERS = process.env.INIT_SIGNERS_KEYS_CSV.split(","); const VAA_SIGNERS = process.env.INIT_SIGNERS_KEYS_CSV.split(",");
@ -40,236 +40,306 @@ const readFileAsync = util.promisify(fs.readFile);
deterministic. deterministic.
*/ */
type ContractName = string; type ContractName = string;
const artifacts: ContractName[] = ["global_accountant.wasm"]; const artifacts: ContractName[] = [
"global_accountant.wasm",
"wormchain_ibc_receiver.wasm",
];
const ARTIFACTS_PATH = "../artifacts/"; const ARTIFACTS_PATH = "../artifacts/";
/* Check that the artifact folder contains all the wasm files we expect and nothing else */ /* Check that the artifact folder contains all the wasm files we expect and nothing else */
try { try {
const actual_artifacts = readdirSync(ARTIFACTS_PATH).filter((a) => const actual_artifacts = readdirSync(ARTIFACTS_PATH).filter((a) =>
a.endsWith(".wasm") a.endsWith(".wasm")
); );
const missing_artifacts = artifacts.filter( const missing_artifacts = artifacts.filter(
(a) => !actual_artifacts.includes(a) (a) => !actual_artifacts.includes(a)
);
if (missing_artifacts.length) {
console.log(
"Error during wormchain deployment. The following files are expected to be in the artifacts folder:"
); );
if (missing_artifacts.length) { missing_artifacts.forEach((file) => console.log(` - ${file}`));
console.log( console.log(
"Error during wormchain deployment. The following files are expected to be in the artifacts folder:" "Hint: the deploy script needs to run after the contracts have been built."
); );
missing_artifacts.forEach((file) => console.log(` - ${file}`)); console.log(
console.log( "External binary blobs need to be manually added in tools/Dockerfile."
"Hint: the deploy script needs to run after the contracts have been built."
);
console.log(
"External binary blobs need to be manually added in tools/Dockerfile."
);
process.exit(1);
}
} catch (err) {
console.error(
`${ARTIFACTS_PATH} cannot be read. Do you need to run "make contracts-deploy-setup"?`
); );
process.exit(1); process.exit(1);
}
} catch (err) {
console.error(
`${ARTIFACTS_PATH} cannot be read. Do you need to run "make contracts-deploy-setup"?`
);
process.exit(1);
} }
async function main() { async function main() {
/* Set up cosmos client & wallet */ /* Set up cosmos client & wallet */
let host = devnetConsts.chains[3104].tendermintUrlLocal; let host = devnetConsts.chains[3104].tendermintUrlLocal;
if (os.hostname().includes("wormchain-deploy")) { if (os.hostname().includes("wormchain-deploy")) {
// running in tilt devnet // running in tilt devnet
host = devnetConsts.chains[3104].tendermintUrlTilt; host = devnetConsts.chains[3104].tendermintUrlTilt;
} }
const mnemonic = const mnemonic =
devnetConsts.chains[3104].accounts.wormchainNodeOfGuardian0.mnemonic; devnetConsts.chains[3104].accounts.wormchainNodeOfGuardian0.mnemonic;
const wallet = await getWallet(mnemonic); const wallet = await getWallet(mnemonic);
const client = await getWormchainSigningClient(host, wallet); const client = await getWormchainSigningClient(host, wallet);
// there are several Cosmos chains in devnet, so check the config is as expected // there are several Cosmos chains in devnet, so check the config is as expected
let id = await client.getChainId(); let id = await client.getChainId();
if (id !== "wormchain") { if (id !== "wormchain") {
throw new Error( throw new Error(
`Wormchain CosmWasmClient connection produced an unexpected chainID: ${id}` `Wormchain CosmWasmClient connection produced an unexpected chainID: ${id}`
); );
} }
const signers = await wallet.getAccounts(); const signers = await wallet.getAccounts();
const signer = signers[0].address; const signer = signers[0].address;
console.log("wormchain contract deployer is: ", signer); console.log("wormchain contract deployer is: ", signer);
/* Deploy artifacts */ /* Deploy artifacts */
const codeIds: { [name: ContractName]: number } = await artifacts.reduce( const codeIds: { [name: ContractName]: number } = await artifacts.reduce(
async (prev, file) => { async (prev, file) => {
// wait for the previous to finish, to avoid the race condition of wallet sequence mismatch. // wait for the previous to finish, to avoid the race condition of wallet sequence mismatch.
const accum = await prev; const accum = await prev;
const contract_bytes = await readFileAsync(`${ARTIFACTS_PATH}${file}`); const contract_bytes = await readFileAsync(`${ARTIFACTS_PATH}${file}`);
const payload = keccak256(contract_bytes); const payload = keccak256(contract_bytes);
let vaa: VAA<Other> = { let vaa: VAA<Other> = {
version: 1, version: 1,
guardianSetIndex: 0, guardianSetIndex: 0,
signatures: [], signatures: [],
timestamp: 0, timestamp: 0,
nonce: 0, nonce: 0,
emitterChain: GOVERNANCE_CHAIN, emitterChain: GOVERNANCE_CHAIN,
emitterAddress: GOVERNANCE_EMITTER, emitterAddress: GOVERNANCE_EMITTER,
sequence: BigInt(Math.floor(Math.random() * 100000000)), sequence: BigInt(Math.floor(Math.random() * 100000000)),
consistencyLevel: 0, consistencyLevel: 0,
payload: { payload: {
type: "Other", type: "Other",
hex: `0000000000000000000000000000000000000000005761736D644D6F64756C65010${CHAIN_ID_WORMCHAIN.toString( hex: `0000000000000000000000000000000000000000005761736D644D6F64756C65010${CHAIN_ID_WORMCHAIN.toString(
16 16
)}${payload}`, )}${payload}`,
},
};
vaa.signatures = sign(VAA_SIGNERS, vaa as unknown as VAA<Payload>);
console.log("uploading", file);
const msg = client.core.msgStoreCode({
signer,
wasm_byte_code: new Uint8Array(contract_bytes),
vaa: hexToUint8Array(serialiseVAA(vaa as unknown as VAA<Payload>)),
});
const result = await client.signAndBroadcast(signer, [msg], {
...ZERO_FEE,
gas: "10000000",
});
const codeId = Number(
JSON.parse(result.rawLog)[0]
.events.find(({ type }) => type === "store_code")
.attributes.find(({ key }) => key === "code_id").value
);
console.log(
`uploaded ${file}, codeID: ${codeId}, tx: ${result.transactionHash}`
);
accum[file] = codeId;
return accum;
}, },
Object() };
); vaa.signatures = sign(VAA_SIGNERS, vaa as unknown as VAA<Payload>);
console.log("uploading", file);
// Instantiate contracts. const msg = client.core.msgStoreCode({
signer,
async function instantiate(code_id: number, inst_msg: any, label: string) { wasm_byte_code: new Uint8Array(contract_bytes),
const instMsgBinary = toBinary(inst_msg); vaa: hexToUint8Array(serialiseVAA(vaa as unknown as VAA<Payload>)),
const instMsgBytes = fromBase64(instMsgBinary); });
const result = await client.signAndBroadcast(signer, [msg], {
// see /sdk/vaa/governance.go
const codeIdBuf = Buffer.alloc(8);
codeIdBuf.writeBigInt64BE(BigInt(code_id));
const codeIdHash = keccak256(codeIdBuf);
const codeIdLabelHash = keccak256(
Buffer.concat([
Buffer.from(codeIdHash, "hex"),
Buffer.from(label, "utf8"),
])
);
const fullHash = keccak256(
Buffer.concat([Buffer.from(codeIdLabelHash, "hex"), instMsgBytes])
);
console.log(fullHash);
let vaa: VAA<Other> = {
version: 1,
guardianSetIndex: 0,
signatures: [],
timestamp: 0,
nonce: 0,
emitterChain: GOVERNANCE_CHAIN,
emitterAddress: GOVERNANCE_EMITTER,
sequence: BigInt(Math.floor(Math.random() * 100000000)),
consistencyLevel: 0,
payload: {
type: "Other",
hex: `0000000000000000000000000000000000000000005761736D644D6F64756C65020${CHAIN_ID_WORMCHAIN.toString(
16
)}${fullHash}`,
},
};
// TODO: check for number of guardians in set and use the corresponding keys
vaa.signatures = sign(VAA_SIGNERS, vaa as unknown as VAA<Payload>);
const msg = client.core.msgInstantiateContract({
signer,
code_id,
label,
msg: instMsgBytes,
vaa: hexToUint8Array(serialiseVAA(vaa as unknown as VAA<Payload>)),
});
const result = await client.signAndBroadcast(signer, [msg], {
...ZERO_FEE,
gas: "10000000",
});
const addr = JSON.parse(result.rawLog)[0]
.events.find(({ type }) => type === "instantiate")
.attributes.find(({ key }) => key === "_contract_address").value;
console.log(
`deployed contract ${label}, codeID: ${code_id}, address: ${addr}, txHash: ${result.transactionHash}`
);
return addr;
}
// Instantiate contracts.
// NOTE: Only append at the end, the ordering must be deterministic.
const addresses: {
[contractName: string]: string;
} = {};
const registrations: { [chainName: string]: string } = {
// keys are only used for logging success/failure
solana: String(process.env.REGISTER_SOL_TOKEN_BRIDGE_VAA),
ethereum: String(process.env.REGISTER_ETH_TOKEN_BRIDGE_VAA),
bsc: String(process.env.REGISTER_BSC_TOKEN_BRIDGE_VAA),
algo: String(process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA),
terra: String(process.env.REGISTER_TERRA_TOKEN_BRIDGE_VAA),
near: String(process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA),
terra2: String(process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA),
aptos: String(process.env.REGISTER_APTOS_TOKEN_BRIDGE_VAA),
sui: String(process.env.REGISTER_SUI_TOKEN_BRIDGE_VAA),
};
const instantiateMsg = {};
addresses["global_accountant.wasm"] = await instantiate(
codeIds["global_accountant.wasm"],
instantiateMsg,
"wormchainAccounting"
);
console.log("instantiated accounting: ", addresses["global_accountant.wasm"]);
const accountingRegistrations = Object.values(registrations).map((r) =>
Buffer.from(r, "hex").toString("base64")
);
const msg = client.wasm.msgExecuteContract({
sender: signer,
contract: addresses["global_accountant.wasm"],
msg: toUtf8(
JSON.stringify({
submit_vaas: {
vaas: accountingRegistrations,
},
})
),
funds: [],
});
const res = await client.signAndBroadcast(signer, [msg], {
...ZERO_FEE, ...ZERO_FEE,
gas: "10000000", gas: "10000000",
});
const codeId = Number(
JSON.parse(result.rawLog)[0]
.events.find(({ type }) => type === "store_code")
.attributes.find(({ key }) => key === "code_id").value
);
console.log(
`uploaded ${file}, codeID: ${codeId}, tx: ${result.transactionHash}`
);
accum[file] = codeId;
return accum;
},
Object()
);
// Instantiate contracts.
async function instantiate(code_id: number, inst_msg: any, label: string) {
const instMsgBinary = toBinary(inst_msg);
const instMsgBytes = fromBase64(instMsgBinary);
// see /sdk/vaa/governance.go
const codeIdBuf = Buffer.alloc(8);
codeIdBuf.writeBigInt64BE(BigInt(code_id));
const codeIdHash = keccak256(codeIdBuf);
const codeIdLabelHash = keccak256(
Buffer.concat([
Buffer.from(codeIdHash, "hex"),
Buffer.from(label, "utf8"),
])
);
const fullHash = keccak256(
Buffer.concat([Buffer.from(codeIdLabelHash, "hex"), instMsgBytes])
);
console.log(fullHash);
let vaa: VAA<Other> = {
version: 1,
guardianSetIndex: 0,
signatures: [],
timestamp: 0,
nonce: 0,
emitterChain: GOVERNANCE_CHAIN,
emitterAddress: GOVERNANCE_EMITTER,
sequence: BigInt(Math.floor(Math.random() * 100000000)),
consistencyLevel: 0,
payload: {
type: "Other",
hex: `0000000000000000000000000000000000000000005761736D644D6F64756C65020${CHAIN_ID_WORMCHAIN.toString(
16
)}${fullHash}`,
},
};
// TODO: check for number of guardians in set and use the corresponding keys
vaa.signatures = sign(VAA_SIGNERS, vaa as unknown as VAA<Payload>);
const msg = client.core.msgInstantiateContract({
signer,
code_id,
label,
msg: instMsgBytes,
vaa: hexToUint8Array(serialiseVAA(vaa as unknown as VAA<Payload>)),
}); });
console.log(`sent accounting chain registrations, tx: `, res.transactionHash); const result = await client.signAndBroadcast(signer, [msg], {
...ZERO_FEE,
gas: "10000000",
});
console.log("contract instantiation msg: ", msg);
console.log("contract instantiation result: ", result);
const addr = JSON.parse(result.rawLog)[0]
.events.find(({ type }) => type === "instantiate")
.attributes.find(({ key }) => key === "_contract_address").value;
console.log(
`deployed contract ${label}, codeID: ${code_id}, address: ${addr}, txHash: ${result.transactionHash}`
);
return addr;
}
// Instantiate contracts.
// NOTE: Only append at the end, the ordering must be deterministic.
const addresses: {
[contractName: string]: string;
} = {};
const registrations: { [chainName: string]: string } = {
// keys are only used for logging success/failure
solana: String(process.env.REGISTER_SOL_TOKEN_BRIDGE_VAA),
ethereum: String(process.env.REGISTER_ETH_TOKEN_BRIDGE_VAA),
bsc: String(process.env.REGISTER_BSC_TOKEN_BRIDGE_VAA),
algo: String(process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA),
terra: String(process.env.REGISTER_TERRA_TOKEN_BRIDGE_VAA),
near: String(process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA),
terra2: String(process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA),
aptos: String(process.env.REGISTER_APTOS_TOKEN_BRIDGE_VAA),
sui: String(process.env.REGISTER_SUI_TOKEN_BRIDGE_VAA),
};
const instantiateMsg = {};
addresses["global_accountant.wasm"] = await instantiate(
codeIds["global_accountant.wasm"],
instantiateMsg,
"wormchainAccounting"
);
console.log("instantiated accounting: ", addresses["global_accountant.wasm"]);
const accountingRegistrations = Object.values(registrations).map((r) =>
Buffer.from(r, "hex").toString("base64")
);
const msg = client.wasm.msgExecuteContract({
sender: signer,
contract: addresses["global_accountant.wasm"],
msg: toUtf8(
JSON.stringify({
submit_vaas: {
vaas: accountingRegistrations,
},
})
),
funds: [],
});
const res = await client.signAndBroadcast(signer, [msg], {
...ZERO_FEE,
gas: "10000000",
});
console.log(`sent accounting chain registrations, tx: `, res.transactionHash);
const wormchainIbcReceiverInstantiateMsg = {};
addresses["wormchain_ibc_receiver.wasm"] = await instantiate(
codeIds["wormchain_ibc_receiver.wasm"],
wormchainIbcReceiverInstantiateMsg,
"wormchainIbcReceiver"
);
console.log(
"instantiated wormchain ibc receiver contract: ",
addresses["wormchain_ibc_receiver.wasm"]
);
// Generated VAA using
// `guardiand template ibc-receiver-update-channel-chain --channel-id channel-0 --chain-id 32 --target-chain-id 3104 > wormchain.prototxt`
// `guardiand admin governance-vaa-verify wormchain.prototxt`
let wormchainIbcReceiverWhitelistVaa: VAA<Other> = {
version: 1,
guardianSetIndex: 0,
signatures: [],
timestamp: 0,
nonce: 0,
emitterChain: GOVERNANCE_CHAIN,
emitterAddress: GOVERNANCE_EMITTER,
sequence: BigInt(Math.floor(Math.random() * 100000000)),
consistencyLevel: 0,
payload: {
type: "Other",
hex: `0000000000000000000000000000000000000000004962635265636569766572010c20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006368616e6e656c2d300020`,
},
};
wormchainIbcReceiverWhitelistVaa.signatures = sign(
VAA_SIGNERS,
wormchainIbcReceiverWhitelistVaa as unknown as VAA<Payload>
);
const wormchainIbcReceiverUpdateWhitelistMsg = {
submit_update_channel_chain: {
vaas: [
Buffer.from(
serialiseVAA(
wormchainIbcReceiverWhitelistVaa as unknown as VAA<Payload>
),
"hex"
).toString("base64"),
],
},
};
const executeMsg = client.wasm.msgExecuteContract({
sender: signer,
contract: addresses["wormchain_ibc_receiver.wasm"],
msg: toUtf8(JSON.stringify(wormchainIbcReceiverUpdateWhitelistMsg)),
funds: [],
});
const updateIbcWhitelistRes = await client.signAndBroadcast(
signer,
[executeMsg],
{
...ZERO_FEE,
gas: "10000000",
}
);
console.log(
"updated wormchain_ibc_receiver whitelist: ",
updateIbcWhitelistRes.transactionHash,
updateIbcWhitelistRes.code
);
} }
try { try {
main(); main();
} catch (e: any) { } catch (e: any) {
if (e?.message) { if (e?.message) {
console.error(e.message); console.error(e.message);
} }
throw e; throw e;
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "wormhole-chain-sdk", "name": "@wormhole-foundation/wormchain-sdk",
"version": "0.0.0", "version": "0.0.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wormhole-chain-sdk", "name": "@wormhole-foundation/wormchain-sdk",
"version": "0.0.0", "version": "0.0.1",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@certusone/wormhole-sdk": "^0.2.0", "@certusone/wormhole-sdk": "^0.2.0",