Compare commits

...

13 Commits

Author SHA1 Message Date
Nikhil Suri a1ff81002b
Merge 16e5b65175 into d63f2ca532 2024-04-26 10:37:11 -04:00
Dirk Brink d63f2ca532 node: Manual initial token list for Blast and Scroll 2024-04-26 09:48:21 -04:00
Evan Gray e6dfb9115e node: shorten hostname for load testing 2024-04-25 16:11:57 -04:00
bruce-riley 034c570b33
Node/Acct: Add parameter checks (#3907) 2024-04-25 15:10:01 -05:00
Dirk Brink a566c3d3bf docs: Update security.md with Cantina NTT audit report 2024-04-24 18:50:49 -04:00
Csongor Kiss 9620fca895
node: generalised governance (#3895)
* node/admin: add generalised EVM call governance handler

Handles governance requests of the form:

```
current_set_index: 4
messages: {
  sequence: 4513077582118919631
  nonce: 2809988562
  evm_call: {
    chain_id: 3
    governance_contract: "0xD8E4C2DbDd2e2bd8F1336EA691dBFF6952B1a6eB"
    target_contract: "0xF890982f9310df57d00f659cf4fd87e65adEd8d7"
    abi_encoded_call: "6497f75a000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000f890982f9310df57d00f659cf4fd87e65aded8d70000000000000000000000000000000000000000000000000000000000000140bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe000000000000000000000000000000000000000000000000000000000000000268690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004beefface00000000000000000000000000000000000000000000000000000000"
  }
}
```

* node/admin: add admin template for evm governance call

* node/admin: add generalised Solana call governance handler

handles governance requests of the form

```
current_set_index: 4
messages: {
  sequence: 4513077582118919631
  nonce: 2809988562
  solana_call: {
    chain_id: 3
    governance_contract: "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"
    encoded_instruction: "BEEFFACE"
  }
}
```

* node/admin: check address lengths and fix typo in governance handler

* node/admin: better error handling and fix comments

* sdk/vaa: add constants for general purpose governance actions
2024-04-23 11:28:02 -04:00
Bruce Riley 35f0b343ed Client/js: Add new chains 2024-04-22 17:14:23 -04:00
Jeff Schroeder bda43b2ac7 wormchain: update cosmos ledger bindings
Ran automatically via:

    go get github.com/cosmos/cosmos-sdk/crypto/ledger@v0.45.11

Fixes:

	 => ERROR [15/16] RUN make client                                                                                                                                                                                                                                           0.7s
	------
	 > [15/16] RUN make client:
	0.202 building wormchaind-v0.0.1
	0.202 go build -v -ldflags '-X github.com/cosmos/cosmos-sdk/version.Name=wormchain -X github.com/cosmos/cosmos-sdk/version.ServerName=wormchaind -X github.com/cosmos/cosmos-sdk/version.Version=v0.0.1 -X github.com/cosmos/cosmos-sdk/version.Commit=abc123 -X "github.com/cosmos/cosmos-sdk/version.BuildTags=ledger"' -tags ledger -o build/wormchaind cmd/wormchaind/main.go
	0.649 /go/pkg/mod/github.com/wormhole-foundation/cosmos-sdk@v0.45.9-wormhole-2/crypto/ledger/ledger_real.go:6:8: missing go.sum entry for module providing package github.com/cosmos/ledger-cosmos-go (imported by github.com/cosmos/cosmos-sdk/crypto/ledger); to add:
	0.649 	go get github.com/cosmos/cosmos-sdk/crypto/ledger@v0.45.11
	0.650 make: *** [Makefile:26: build/wormchaind] Error 1
	------
	Dockerfile:37
	--------------------
	  35 |     RUN /bin/bash /app/devnet/create-genesis.sh
	  36 |
	  37 | >>> RUN make client
	  38 |     RUN chmod +x /app/build/wormchaind
	  39 |
	--------------------
	ERROR: failed to solve: process "/bin/sh -c make client" did not complete successfully: exit code: 2
2024-04-22 16:31:18 -04:00
Jeff Schroeder 14d73dafe0 node: run go mod tidy
Necessary when updating the go ledger dependencies.
2024-04-22 16:31:18 -04:00
Michael Nguyen ddf5ba159c Fixes transactions when using a Ledger 2024-04-22 16:31:18 -04:00
bruce-riley 9af1fac9e1
Node/EVM: Linea poller (#3872)
* Node/EVM: Linea poller

* Explicitly check finality type in watcher
2024-04-22 10:11:45 -05:00
Nikhil Suri 16e5b65175 Consume extra gas to be equal to 59774 2023-06-21 15:37:48 +00:00
Nikhil Suri 85fdc5c83b wormchain: add ibc error antehandler to retroactively fix consensus issues on block 3_151_174 2023-06-06 18:06:40 +00:00
30 changed files with 6774 additions and 503 deletions

View File

@ -44,6 +44,7 @@ As these 3rd party audits are completed and issues are sufficiently addressed, w
- **[Jan 2024 - OtterSec](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-01-ottersec-terra.pdf)**: _Terra Classic Contract Upgrades_
- **[Feb 2024 - Cyfrin](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-09-cyfrin-wormhole-evm-cctp-v2-1.pdf)**: _CCTP EVM Contracts_
- **[Mar 2024 - Cyfrin](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-11-cyfrin-wormhole-evm-ntt.pdf)**: _NTT EVM Contracts_
- **[Mar 2024 - Cantina](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-cantina-wormhole-evm-ntt.pdf)**: _NTT EVM Contracts_
- **[Mar 2024 - OtterSec](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-03-28-ottersec-solana-ntt.pdf)**: _NTT Solana Contracts_
- **[Mar 2024 - Neodyme](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-12-neodyme-solana-ntt.pdf)**: _NTT Solana Contracts_

View File

@ -253,9 +253,10 @@ Options:
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
-n, --network Network
[required] [choices: "mainnet", "testnet", "devnet"]
@ -311,18 +312,20 @@ Options:
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
--dst-chain destination chain
[required] [choices: "solana", "ethereum", "terra", "bsc", "polygon",
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
--dst-addr destination address [string] [required]
--token-addr token address [string] [default: native token]
@ -355,9 +358,10 @@ Positionals:
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
tx Source transaction hash [string]

View File

@ -10,7 +10,7 @@
"license": "Apache-2.0",
"dependencies": {
"@celo-tools/celo-ethers-wrapper": "^0.1.0",
"@certusone/wormhole-sdk": "^0.10.13",
"@certusone/wormhole-sdk": "^0.10.14",
"@cosmjs/encoding": "^0.26.2",
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@injectivelabs/networks": "^1.10.7",
@ -683,9 +683,9 @@
}
},
"node_modules/@certusone/wormhole-sdk": {
"version": "0.10.13",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.13.tgz",
"integrity": "sha512-04OCGoJUylTFNu4+g96Ax7jvn3M7FwVbTTcPNIjgta8QcC046kZBYAEd/yrK5hkDGqejQyJwO0ieaJfxL/kv1w==",
"version": "0.10.14",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.14.tgz",
"integrity": "sha512-36pEcLZbG+DLFKL2N7khLLnSYBMPoMpJrUs7IFqps3i+PhCZ6e8xb2ohNcIKX58G9Ibj+xRmc5ilo+D37WodVg==",
"dependencies": {
"@certusone/wormhole-sdk-proto-web": "0.0.7",
"@certusone/wormhole-sdk-wasm": "^0.0.1",
@ -11917,9 +11917,9 @@
"requires": {}
},
"@certusone/wormhole-sdk": {
"version": "0.10.13",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.13.tgz",
"integrity": "sha512-04OCGoJUylTFNu4+g96Ax7jvn3M7FwVbTTcPNIjgta8QcC046kZBYAEd/yrK5hkDGqejQyJwO0ieaJfxL/kv1w==",
"version": "0.10.14",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.14.tgz",
"integrity": "sha512-36pEcLZbG+DLFKL2N7khLLnSYBMPoMpJrUs7IFqps3i+PhCZ6e8xb2ohNcIKX58G9Ibj+xRmc5ilo+D37WodVg==",
"requires": {
"@certusone/wormhole-sdk-proto-web": "0.0.7",
"@certusone/wormhole-sdk-wasm": "^0.0.1",

View File

@ -30,7 +30,7 @@
],
"dependencies": {
"@celo-tools/celo-ethers-wrapper": "^0.1.0",
"@certusone/wormhole-sdk": "^0.10.13",
"@certusone/wormhole-sdk": "^0.10.14",
"@cosmjs/encoding": "^0.26.2",
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@injectivelabs/networks": "^1.10.7",

View File

@ -62,6 +62,11 @@ export const getOriginalAsset = async (
// case "rootstock":
case "scroll":
case "mantle":
case "blast":
case "xlayer":
case "linea":
case "berachain":
case "seievm":
case "sepolia":
case "arbitrum_sepolia":
case "base_sepolia":
@ -125,6 +130,7 @@ export const getOriginalAsset = async (
case "stargaze":
case "seda":
case "dymension":
case "provenance":
case "rootstock":
throw new Error(`${chainName} not supported`);
default:

View File

@ -72,6 +72,11 @@ export const getWrappedAssetAddress = async (
// case "rootstock":
case "scroll":
case "mantle":
case "blast":
case "xlayer":
case "linea":
case "berachain":
case "seievm":
case "sepolia":
case "arbitrum_sepolia":
case "base_sepolia":
@ -172,6 +177,7 @@ export const getWrappedAssetAddress = async (
case "stargaze":
case "seda":
case "dymension":
case "provenance":
throw new Error(`${chainName} not supported`);
default:
impossible(chainName);

View File

@ -99,6 +99,11 @@ export const getProviderForChain = <T extends ChainId | ChainName>(
// case "rootstock":
case "scroll":
case "mantle":
case "blast":
case "xlayer":
case "linea":
case "berachain":
case "seievm":
case "sepolia":
case "arbitrum_sepolia":
case "base_sepolia":
@ -169,6 +174,7 @@ export const getProviderForChain = <T extends ChainId | ChainName>(
case "stargaze":
case "seda":
case "dymension":
case "provenance":
case "rootstock":
throw new Error(`${chainName} not supported`);
default:

View File

@ -200,6 +200,8 @@ async function executeSubmit(
throw Error("seda is not supported yet");
} else if (chain === "dymension") {
throw Error("dymension is not supported yet");
} else if (chain === "provenance") {
throw Error("provenance is not supported yet");
} else if (chain === "rootstock") {
throw Error("rootstock is not supported yet");
} else {

View File

@ -154,6 +154,8 @@ export const handler = async (
throw Error("seda is not supported yet");
} else if (srcChain === "dymension") {
throw Error("dymension is not supported yet");
} else if (srcChain === "provenance") {
throw Error("provenance is not supported yet");
} else if (srcChain === "rootstock") {
throw Error("rootstock is not supported yet");
} else {

View File

@ -171,6 +171,41 @@ const MAINNET = {
key: getEnvVar("ETH_KEY"),
chain_id: 30,
},
scroll: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
mantle: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
blast: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
xlayer: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
linea: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
berachain: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seievm: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
sepolia: {
rpc: undefined,
key: undefined,
@ -206,16 +241,6 @@ const MAINNET = {
key: undefined,
chain_id: undefined,
},
scroll: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
mantle: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
arbitrum_sepolia: {
rpc: undefined,
key: undefined,
@ -251,6 +276,11 @@ const MAINNET = {
key: undefined,
chain_id: undefined,
},
provenance: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
};
const TESTNET = {
@ -362,6 +392,41 @@ const TESTNET = {
rpc: "https://rpc.atlantic-2.seinetwork.io",
key: getEnvVar("SEI_KEY_TESTNET"),
},
scroll: {
rpc: "https://rpc.ankr.com/scroll_sepolia_testnet",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 534353,
},
mantle: {
rpc: "https://mantle-sepolia.drpc.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 5003,
},
blast: {
rpc: "https://blast-sepolia.drpc.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 168587773,
},
xlayer: {
rpc: "https://testrpc.xlayer.tech/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 195,
},
linea: {
rpc: "https://rpc.sepolia.linea.build",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 59141,
},
berachain: {
rpc: "https://artio.rpc.berachain.com/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 80085,
},
seievm: {
rpc: "https://evm-rpc-arctic-1.sei-apis.com/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 713715,
},
sepolia: {
rpc: "https://rpc.ankr.com/eth_sepolia",
key: getEnvVar("ETH_KEY_TESTNET"),
@ -445,16 +510,6 @@ const TESTNET = {
key: undefined,
chain_id: undefined,
},
scroll: {
rpc: "https://rpc.ankr.com/scroll_sepolia_testnet",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 534353,
},
mantle: {
rpc: "https://mantle-sepolia.drpc.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 5003,
},
arbitrum_sepolia: {
rpc: "https://arbitrum-sepolia.publicnode.com",
key: getEnvVar("ETH_KEY_TESTNET"),
@ -490,6 +545,11 @@ const TESTNET = {
key: undefined,
chain_id: undefined,
},
provenance: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
};
const DEVNET = {
@ -584,6 +644,41 @@ const DEVNET = {
rpc: undefined,
key: undefined,
},
scroll: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
mantle: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
blast: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
xlayer: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
linea: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
berachain: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seievm: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
sepolia: {
rpc: undefined,
key: undefined,
@ -660,16 +755,6 @@ const DEVNET = {
key: undefined,
chain_id: undefined,
},
scroll: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
mantle: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
arbitrum_sepolia: {
rpc: undefined,
key: undefined,
@ -705,6 +790,11 @@ const DEVNET = {
key: undefined,
chain_id: undefined,
},
provenance: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
};
/**

View File

@ -3,8 +3,8 @@ import {
BridgeImplementation__factory,
Implementation__factory,
NFTBridgeImplementation__factory,
WormholeRelayer__factory,
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
import { WormholeRelayer__factory } from "@certusone/wormhole-sdk/lib/esm/ethers-relayer-contracts";
import { getWormholeRelayerAddress } from "@certusone/wormhole-sdk/lib/esm/relayer";
import {
CHAINS,

View File

@ -7,6 +7,7 @@ Algorand
algosdk
alist
Aptos
arbitrum
authorisation
authorise
authorised
@ -25,6 +26,7 @@ callstack
CCTP
celestia
Celestia
celo
certusone
Chainlink
Coinspect
@ -143,6 +145,7 @@ rustup
satoshi
secp
seda
seievm
Sepolia
serde
setcap
@ -192,6 +195,7 @@ wormchaind
Wormholescan
wormscan
wormscanurl
xlayer
xpla
XPLA
Zellic

View File

@ -60,6 +60,11 @@ var ibcUpdateChannelChainChainId *string
var recoverChainIdEvmChainId *string
var recoverChainIdNewChainId *string
var governanceContractAddress *string
var governanceTargetAddress *string
var governanceTargetChain *string
var governanceCallData *string
func init() {
governanceFlagSet := pflag.NewFlagSet("governance", pflag.ExitOnError)
chainID = governanceFlagSet.String("chain-id", "", "Chain ID")
@ -171,6 +176,19 @@ func init() {
AdminClientRecoverChainIdCmd.Flags().AddFlagSet(recoverChainIdFlagSet)
AdminClientRecoverChainIdCmd.Flags().AddFlagSet(moduleFlagSet)
TemplateCmd.AddCommand(AdminClientRecoverChainIdCmd)
// flags for general-purpose governance call command
generalPurposeGovernanceFlagSet := pflag.NewFlagSet("general-purpose-governance", pflag.ExitOnError)
governanceContractAddress = generalPurposeGovernanceFlagSet.String("governance-contract", "", "Governance contract address")
governanceTargetAddress = generalPurposeGovernanceFlagSet.String("target-address", "", "Address of the governed contract")
governanceCallData = generalPurposeGovernanceFlagSet.String("call-data", "", "calldata")
governanceTargetChain = generalPurposeGovernanceFlagSet.String("chain-id", "", "Chain ID")
// evm call command
AdminClientGeneralPurposeGovernanceEvmCallCmd.Flags().AddFlagSet(generalPurposeGovernanceFlagSet)
TemplateCmd.AddCommand(AdminClientGeneralPurposeGovernanceEvmCallCmd)
// solana call command
AdminClientGeneralPurposeGovernanceSolanaCallCmd.Flags().AddFlagSet(generalPurposeGovernanceFlagSet)
TemplateCmd.AddCommand(AdminClientGeneralPurposeGovernanceSolanaCallCmd)
}
var TemplateCmd = &cobra.Command{
@ -292,6 +310,18 @@ var AdminClientWormholeRelayerSetDefaultDeliveryProviderCmd = &cobra.Command{
Run: runWormholeRelayerSetDefaultDeliveryProviderTemplate,
}
var AdminClientGeneralPurposeGovernanceEvmCallCmd = &cobra.Command{
Use: "governance-evm-call",
Short: "Generate a 'general purpose evm governance call' template for specified chain and address",
Run: runGeneralPurposeGovernanceEvmCallTemplate,
}
var AdminClientGeneralPurposeGovernanceSolanaCallCmd = &cobra.Command{
Use: "governance-solana-call",
Short: "Generate a 'general purpose solana governance call' template for specified chain and address",
Run: runGeneralPurposeGovernanceSolanaCallTemplate,
}
func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
// Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture.
guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians)
@ -932,6 +962,100 @@ func runWormholeRelayerSetDefaultDeliveryProviderTemplate(cmd *cobra.Command, ar
fmt.Print(string(b))
}
func runGeneralPurposeGovernanceEvmCallTemplate(cmd *cobra.Command, args []string) {
if *governanceTargetAddress == "" {
log.Fatal("--target-address must be specified")
}
if !common.IsHexAddress(*governanceTargetAddress) {
log.Fatal("invalid target address")
}
governanceTargetAddress := common.HexToAddress(*governanceTargetAddress).Hex()
if *governanceCallData == "" {
log.Fatal("--call-data must be specified")
}
if *governanceContractAddress == "" {
log.Fatal("--governance-contract must be specified")
}
if !common.IsHexAddress(*governanceContractAddress) {
log.Fatal("invalid governance contract address")
}
governanceContractAddress := common.HexToAddress(*governanceContractAddress).Hex()
if *governanceTargetChain == "" {
log.Fatal("--chain-id must be specified")
}
chainID, err := parseChainID(*governanceTargetChain)
if err != nil {
log.Fatal("failed to parse chain id: ", err)
}
m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex),
Messages: []*nodev1.GovernanceMessage{
{
Sequence: rand.Uint64(),
Nonce: rand.Uint32(),
Payload: &nodev1.GovernanceMessage_EvmCall{
EvmCall: &nodev1.EvmCall{
ChainId: uint32(chainID),
GovernanceContract: governanceContractAddress,
TargetContract: governanceTargetAddress,
AbiEncodedCall: *governanceCallData,
},
},
},
},
}
b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m)
if err != nil {
panic(err)
}
fmt.Print(string(b))
}
func runGeneralPurposeGovernanceSolanaCallTemplate(cmd *cobra.Command, args []string) {
if *governanceCallData == "" {
log.Fatal("--call-data must be specified")
}
if *governanceContractAddress == "" {
log.Fatal("--governance-contract must be specified")
}
_, err := base58.Decode(*governanceContractAddress)
if err != nil {
log.Fatal("invalid base58 governance contract address")
}
if *governanceTargetChain == "" {
log.Fatal("--chain-id must be specified")
}
chainID, err := parseChainID(*governanceTargetChain)
if err != nil {
log.Fatal("failed to parse chain id: ", err)
}
m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex),
Messages: []*nodev1.GovernanceMessage{
{
Sequence: rand.Uint64(),
Nonce: rand.Uint32(),
Payload: &nodev1.GovernanceMessage_SolanaCall{
SolanaCall: &nodev1.SolanaCall{
ChainId: uint32(chainID),
GovernanceContract: *governanceContractAddress,
EncodedInstruction: *governanceCallData,
},
},
},
},
}
b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m)
if err != nil {
panic(err)
}
fmt.Print(string(b))
}
// parseAddress parses either a hex-encoded address and returns
// a left-padded 32 byte hex string.
func parseAddress(s string) (string, error) {

View File

@ -182,8 +182,10 @@ var (
xlayerRPC *string
xlayerContract *string
lineaRPC *string
lineaContract *string
lineaRPC *string
lineaContract *string
lineaRollUpUrl *string
lineaRollUpContract *string
berachainRPC *string
berachainContract *string
@ -386,6 +388,8 @@ func init() {
lineaRPC = node.RegisterFlagWithValidationOrFail(NodeCmd, "lineaRPC", "Linea RPC URL", "ws://eth-devnet:8545", []string{"ws", "wss"})
lineaContract = NodeCmd.Flags().String("lineaContract", "", "Linea contract address")
lineaRollUpUrl = NodeCmd.Flags().String("lineaRollUpUrl", "", "Linea roll up URL")
lineaRollUpContract = NodeCmd.Flags().String("lineaRollUpContract", "", "Linea roll up contract address")
berachainRPC = node.RegisterFlagWithValidationOrFail(NodeCmd, "berachainRPC", "Berachain RPC URL", "ws://eth-devnet:8545", []string{"ws", "wss"})
berachainContract = NodeCmd.Flags().String("berachainContract", "", "Berachain contract address")
@ -802,6 +806,9 @@ func runNode(cmd *cobra.Command, args []string) {
if (*lineaRPC == "") != (*lineaContract == "") {
logger.Fatal("Both --lineaContract and --lineaRPC must be set together or both unset")
}
if (*lineaRPC != "") && (*lineaRollUpUrl == "" || *lineaRollUpContract == "") && !*unsafeDevMode {
logger.Fatal("If --lineaRPC is specified, --lineaRollUpUrl and --lineaRollUpContract must also be specified")
}
if *berachainRPC != "" && !*testnetMode && !*unsafeDevMode {
logger.Fatal("berachain is currently only supported in devnet and testnet")
@ -996,10 +1003,16 @@ func runNode(cmd *cobra.Command, args []string) {
if idx != 0 {
// try to connect to guardian-0
for {
// tilt uses this hostname format
_, err := net.LookupIP("guardian-0.guardian")
if err == nil {
break
}
// load tests use this hostname format
_, err = net.LookupIP("guardian-0")
if err == nil {
break
}
logger.Info("Error resolving guardian-0.guardian. Trying again...")
time.Sleep(time.Second)
}
@ -1144,6 +1157,10 @@ func runNode(cmd *cobra.Command, args []string) {
var accountantWormchainConn, accountantNttWormchainConn *wormconn.ClientConn
if *accountantContract != "" {
if *wormchainURL == "" {
logger.Fatal("if accountantContract is specified, wormchainURL is required", zap.String("component", "gacct"))
}
if *accountantKeyPath == "" {
logger.Fatal("if accountantContract is specified, accountantKeyPath is required", zap.String("component", "gacct"))
}
@ -1176,6 +1193,10 @@ func runNode(cmd *cobra.Command, args []string) {
// If the NTT accountant is enabled, create a wormchain connection for it.
if *accountantNttContract != "" {
if *wormchainURL == "" {
logger.Fatal("if accountantNttContract is specified, wormchainURL is required", zap.String("component", "gacct"))
}
if *accountantNttKeyPath == "" {
logger.Fatal("if accountantNttContract is specified, accountantNttKeyPath is required", zap.String("component", "gacct"))
}
@ -1506,11 +1527,13 @@ func runNode(cmd *cobra.Command, args []string) {
if shouldStart(lineaRPC) {
wc := &evm.WatcherConfig{
NetworkID: "linea",
ChainID: vaa.ChainIDLinea,
Rpc: *lineaRPC,
Contract: *lineaContract,
CcqBackfillCache: *ccqBackfillCache,
NetworkID: "linea",
ChainID: vaa.ChainIDLinea,
Rpc: *lineaRPC,
Contract: *lineaContract,
CcqBackfillCache: *ccqBackfillCache,
LineaRollUpUrl: *lineaRollUpUrl,
LineaRollUpContract: *lineaRollUpContract,
}
watcherConfigs = append(watcherConfigs, wc)

View File

@ -106,7 +106,7 @@ require (
github.com/cosmos/gorocksdb v1.2.0 // indirect
github.com/cosmos/iavl v0.19.4 // indirect
github.com/cosmos/ibc-go/v4 v4.2.2 // indirect
github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect
github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect
github.com/creachadair/taskgroup v0.3.2 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
@ -314,8 +314,8 @@ require (
github.com/weaveworks/common v0.0.0-20230531151736-e2613bee6b73 // indirect
github.com/weaveworks/promrus v1.2.0 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
github.com/zondax/hid v0.9.1 // indirect
github.com/zondax/ledger-go v0.14.0 // indirect
github.com/zondax/hid v0.9.2 // indirect
github.com/zondax/ledger-go v0.14.3 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/api/v3 v3.5.5 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect

View File

@ -1036,8 +1036,8 @@ github.com/cosmos/ibc-go/v4 v4.2.2/go.mod h1:EFFqkrJHQPHWUlw155QUxGuis4Ett4lvewA
github.com/cosmos/interchain-accounts v0.2.4 h1:7UrroFQsCRSp17980mk6anx4YteveIJVkU+a0wlsHQI=
github.com/cosmos/interchain-accounts v0.2.4/go.mod h1:jeiJEb0zg609G0oCrCG0r6Guhb7YbA1uFiwww/1YgZE=
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w=
github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g=
github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw=
github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M=
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
@ -3153,10 +3153,10 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo=
github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/ledger-go v0.14.0 h1:dlMC7aO8Wss1CxBq2I96kZ69Nh1ligzbs8UWOtq/AsA=
github.com/zondax/ledger-go v0.14.0/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320=
github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI=
gitlab.com/bosi/decorder v0.2.1/go.mod h1:6C/nhLSbF6qZbYD8bRmISBwc6vcWdNsiIBkRvjJFrH0=
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=

View File

@ -577,6 +577,58 @@ func wormholeRelayerSetDefaultDeliveryProvider(req *nodev1.WormholeRelayerSetDef
return v, nil
}
func evmCallToVaa(evmCall *nodev1.EvmCall, timestamp time.Time, guardianSetIndex, nonce uint32, sequence uint64) (*vaa.VAA, error) {
governanceContract := ethcommon.HexToAddress(evmCall.GovernanceContract)
targetContract := ethcommon.HexToAddress(evmCall.TargetContract)
payload, err := hex.DecodeString(evmCall.AbiEncodedCall)
if err != nil {
return nil, fmt.Errorf("failed to decode ABI encoded call: %w", err)
}
body, err := vaa.BodyGeneralPurposeGovernanceEvm{
ChainID: vaa.ChainID(evmCall.ChainId),
GovernanceContract: governanceContract,
TargetContract: targetContract,
Payload: payload,
}.Serialize()
if err != nil {
return nil, fmt.Errorf("failed to serialize governance body: %w", err)
}
v := vaa.CreateGovernanceVAA(timestamp, nonce, sequence, guardianSetIndex, body)
return v, nil
}
func solanaCallToVaa(solanaCall *nodev1.SolanaCall, timestamp time.Time, guardianSetIndex, nonce uint32, sequence uint64) (*vaa.VAA, error) {
address, err := base58.Decode(solanaCall.GovernanceContract)
if err != nil {
return nil, fmt.Errorf("failed to decode base58 governance contract address: %w", err)
}
if len(address) != 32 {
return nil, errors.New("invalid governance contract address length (expected 32 bytes)")
}
var governanceContract [32]byte
copy(governanceContract[:], address)
instruction, err := hex.DecodeString(solanaCall.EncodedInstruction)
if err != nil {
return nil, fmt.Errorf("failed to decode instruction: %w", err)
}
v := vaa.CreateGovernanceVAA(timestamp, nonce, sequence, guardianSetIndex,
vaa.BodyGeneralPurposeGovernanceSolana{
ChainID: vaa.ChainID(solanaCall.ChainId),
GovernanceContract: governanceContract,
Instruction: instruction,
}.Serialize())
return v, nil
}
func GovMsgToVaa(message *nodev1.GovernanceMessage, currentSetIndex uint32, timestamp time.Time) (*vaa.VAA, error) {
var (
v *vaa.VAA
@ -620,6 +672,10 @@ func GovMsgToVaa(message *nodev1.GovernanceMessage, currentSetIndex uint32, time
v, err = ibcUpdateChannelChain(payload.IbcUpdateChannelChain, timestamp, currentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_WormholeRelayerSetDefaultDeliveryProvider:
v, err = wormholeRelayerSetDefaultDeliveryProvider(payload.WormholeRelayerSetDefaultDeliveryProvider, timestamp, currentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_EvmCall:
v, err = evmCallToVaa(payload.EvmCall, timestamp, currentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_SolanaCall:
v, err = solanaCallToVaa(payload.SolanaCall, timestamp, currentSetIndex, message.Nonce, message.Sequence)
default:
panic(fmt.Sprintf("unsupported VAA type: %T", payload))
}

View File

@ -14,5 +14,37 @@ func manualTokenList() []tokenConfigEntry {
{chain: 13, addr: "0000000000000000000000005096db80b21ef45230c9e423c373f1fc9c0198dd", symbol: "WEMIX", coinGeckoId: "wemix-token", decimals: 8, price: 1.74},
{chain: 15, addr: "0000000000000000000000000000000000000000000000000000000000000000", symbol: "NEAR", coinGeckoId: "near", decimals: 8, price: 3.85},
{chain: 32, addr: "00881043998ff2b738519d444d2dd0da3da4545de08290c1076746538d5333df", symbol: "Sei", coinGeckoId: "sei-network", decimals: 6, price: 0.0},
// BLAST (tokens over $50,000 24h volume)
{chain: 36, addr: "0000000000000000000000004300000000000000000000000000000000000003", symbol: "USDB", coinGeckoId: "usdb", decimals: 18, price: 1.00},
{chain: 36, addr: "0000000000000000000000004300000000000000000000000000000000000004", symbol: "WETH", coinGeckoId: "weth", decimals: 18, price: 3157.42},
{chain: 36, addr: "0000000000000000000000002416092f143378750bb29b79ed961ab195cceea5", symbol: "EZETH", coinGeckoId: "renzo-restaked-eth", decimals: 18, price: 3092.32},
{chain: 36, addr: "0000000000000000000000004fee793d435c6d2c10c135983bb9d6d4fc7b9bbd", symbol: "USD+", coinGeckoId: "usd", decimals: 18, price: 1.00},
{chain: 36, addr: "000000000000000000000000818a92bc81aad0053d72ba753fb5bc3d0c5c0923", symbol: "JUICE", coinGeckoId: "juice-finance", decimals: 18, price: 0.1051},
{chain: 36, addr: "0000000000000000000000009e20461bc2c4c980f62f1b279d71734207a6a356", symbol: "OMNI", coinGeckoId: "omnicat", decimals: 18, price: 0.0004575},
{chain: 36, addr: "000000000000000000000000764933fbad8f5d04ccd088602096655c2ed9879f", symbol: "AI", coinGeckoId: "any-inu", decimals: 18, price: 0.00002742},
{chain: 36, addr: "0000000000000000000000005ffd9ebd27f2fcab044c0f0a26a45cb62fa29c06", symbol: "PAC", coinGeckoId: "pacmoon", decimals: 18, price: 0.05459},
{chain: 36, addr: "00000000000000000000000020fe91f17ec9080e3cac2d688b4ecb48c5ac3a9c", symbol: "YES", coinGeckoId: "yes-money", decimals: 18, price: 3.96},
{chain: 36, addr: "00000000000000000000000076da31d7c9cbeae102aff34d3398bc450c8374c1", symbol: "MIM", coinGeckoId: "magic-internet-money", decimals: 18, price: 0.9935},
{chain: 36, addr: "00000000000000000000000015d24de366f69b835be19f7cf9447e770315dd80", symbol: "KAP", coinGeckoId: "kapital-dao", decimals: 18, price: 0.1143},
{chain: 36, addr: "000000000000000000000000b9dfcd4cf589bb8090569cb52fac1b88dbe4981f", symbol: "BAG", coinGeckoId: "bag", decimals: 18, price: 0.002972},
{chain: 36, addr: "00000000000000000000000068449870eea84453044bd430822827e21fd8f101", symbol: "ZAI", coinGeckoId: "zaibot", decimals: 18, price: 0.2348},
{chain: 36, addr: "00000000000000000000000047c337bd5b9344a6f3d6f58c474d9d8cd419d8ca", symbol: "DACKIE", coinGeckoId: "dackieswap", decimals: 18, price: 0.006554},
{chain: 36, addr: "000000000000000000000000d43d8adac6a4c7d9aeece7c3151fca8f23752cf8", symbol: "ANDY", coinGeckoId: "andyerc", decimals: 9, price: 0.1165},
{chain: 36, addr: "00000000000000000000000087e154e86fb691ab8a27116e93ed8d54e2b8c18c", symbol: "TES", coinGeckoId: "titan-trading-token", decimals: 18, price: 0.867},
{chain: 36, addr: "000000000000000000000000870a8f46b62b8bdeda4c02530c1750cddf2ed32e", symbol: "USDC+", coinGeckoId: "usdc-plus-overnight", decimals: 18, price: 1.00},
{chain: 36, addr: "00000000000000000000000042e12d42b3d6c4a74a88a61063856756ea2db357", symbol: "ORBIT", coinGeckoId: "orbit-protocol", decimals: 18, price: 0.3074},
// SCROLL (tokens over $50,000 24h volume)
{chain: 34, addr: "0000000000000000000000000018d96c579121a94307249d47f053e2d687b5e7", symbol: "MVX", coinGeckoId: "metavault-trade", decimals: 18, price: 2.06},
{chain: 34, addr: "00000000000000000000000047c337bd5b9344a6f3d6f58c474d9d8cd419d8ca", symbol: "DACKIE", coinGeckoId: "dackieswap", decimals: 18, price: 0.00655},
{chain: 34, addr: "0000000000000000000000005300000000000000000000000000000000000004", symbol: "WETH", coinGeckoId: "bridged-wrapped-ether-scroll", decimals: 18, price: 3145.98},
{chain: 34, addr: "000000000000000000000000f55bec9cafdbe8730f096aa55dad6d22d44099df", symbol: "USDT", coinGeckoId: "bridged-tether-scroll", decimals: 6, price: 1.00},
{chain: 34, addr: "00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4", symbol: "USDC", coinGeckoId: "bridged-usd-coin-scroll", decimals: 6, price: 1.00},
{chain: 34, addr: "000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215", symbol: "AXLUSDC", coinGeckoId: "bridged-axelar-wrapped-usd-coin-scroll", decimals: 6, price: 1.01},
{chain: 34, addr: "0000000000000000000000003c1bca5a656e69edcd0d4e36bebb3fcdaca60cf1", symbol: "WBTC", coinGeckoId: "bridged-wrapped-bitcoin-scroll", decimals: 8, price: 64415.17},
{chain: 34, addr: "00000000000000000000000060d01ec2d5e98ac51c8b4cf84dfcce98d527c747", symbol: "IZI", coinGeckoId: "izumi-finance", decimals: 18, price: 0.0142},
{chain: 34, addr: "0000000000000000000000000a3bb08b3a15a19b4de82f8acfc862606fb69a2d", symbol: "IUSD", coinGeckoId: "izumi-bond-usd", decimals: 18, price: 0.9195},
{chain: 34, addr: "000000000000000000000000f610a9dfb7c89644979b4a0f27063e9e7d7cda32", symbol: "WSTETH", coinGeckoId: "bridged-wrapped-lido-staked-ether-scroll", decimals: 18, price: 3659.28},
{chain: 34, addr: "000000000000000000000000cA77eB3fEFe3725Dc33bccB54eDEFc3D9f764f97", symbol: "DAI", coinGeckoId: "dai", decimals: 18, price: 1.00},
{chain: 34, addr: "00000000000000000000000053878B874283351D26d206FA512aEcE1Bef6C0dD", symbol: "RETH", coinGeckoId: "rocket-pool-eth", decimals: 18, price: 3475.55},
}
}

View File

@ -147,7 +147,7 @@ func GuardianOptionAccountant(
}
if websocket == "" {
return errors.New("if accountantContract is specified, accountantWS is required")
return errors.New("if either accountantContract or accountantNttContract is specified, accountantWS is required")
}
if contract != "" {
if wormchainConn == nil {

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
package evm
import (
"errors"
"github.com/certusone/wormhole/node/pkg/common"
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
"github.com/certusone/wormhole/node/pkg/query"
@ -20,6 +22,10 @@ type WatcherConfig struct {
L1FinalizerRequired watchers.NetworkID // (optional)
l1Finalizer interfaces.L1Finalizer
CcqBackfillCache bool
// These parameters are currently only used for Linea and should be set via SetLineaParams()
LineaRollUpUrl string
LineaRollUpContract string
}
func (wc *WatcherConfig) GetNetworkID() watchers.NetworkID {
@ -57,5 +63,12 @@ func (wc *WatcherConfig) Create(
watcher := NewEthWatcher(wc.Rpc, eth_common.HexToAddress(wc.Contract), string(wc.NetworkID), wc.ChainID, msgC, setWriteC, obsvReqC, queryReqC, queryResponseC, devMode, wc.CcqBackfillCache)
watcher.SetL1Finalizer(wc.l1Finalizer)
if wc.ChainID == vaa.ChainIDLinea {
if err := watcher.SetLineaParams(wc.LineaRollUpUrl, wc.LineaRollUpContract); err != nil {
return nil, nil, err
}
} else if wc.LineaRollUpUrl != "" || wc.LineaRollUpContract != "" {
return nil, nil, errors.New("LineaRollUpUrl and LineaRollUpContract may only be specified for Linea")
}
return watcher, watcher.Run, nil
}

View File

@ -0,0 +1,218 @@
// A block is considered finalized on Linea when it is marked finalized by the LineaRollup contract on Ethereum.
//
// For a discussion of finality on Linea, see here:
// https://www.notion.so/wormholefoundation/Testnet-Info-V2-633e4aa64a634d56a7ce07a103789774?pvs=4#03513c2eb3654d33aff2206a562d25b1
//
// The LineaRollup proxy contract on ethereum is available at the following addresses:
// Mainnet: 0xd19d4B5d358258f05D7B411E21A1460D11B0876F
// Testnet: 0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5
//
// To generate the golang abi for the LineaRollup contract:
// - Grab the ABIs from the LineaRollup contract (not the proxy) (0x934Dd4C63E285551CEceF8459103554D0096c179 on Ethereum mainnet) and put it in /tmp/LineaRollup.abi.
// - mkdir node/pkg/watchers/evm/connectors/lineaabi
// - Install abigen: go install github.com/ethereum/go-ethereum/cmd/abigen@latest
// - abigen --abi /tmp/LineaRollup.abi --pkg lineaabi --out node/pkg/watchers/evm/connectors/lineaabi/LineaRollup.go
package connectors
import (
"context"
"fmt"
"time"
"github.com/certusone/wormhole/node/pkg/common"
rollUpAbi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/lineaabi"
ethereum "github.com/ethereum/go-ethereum"
ethBind "github.com/ethereum/go-ethereum/accounts/abi/bind"
ethCommon "github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
ethClient "github.com/ethereum/go-ethereum/ethclient"
ethRpc "github.com/ethereum/go-ethereum/rpc"
"go.uber.org/zap"
)
// LineaConnector listens for new finalized blocks for Linea by reading the roll up contract on Ethereum.
type LineaConnector struct {
Connector
logger *zap.Logger
// These are used for querying the roll up contract.
rollUpRawClient *ethRpc.Client
rollUpClient *ethClient.Client
// These are used to subscribe for new block finalized events from the roll up contract.
rollUpFilterer *rollUpAbi.LineaabiFilterer
rollUpCaller *rollUpAbi.LineaabiCaller
latestBlockNum uint64
latestFinalizedBlockNum uint64
}
// NewLineaConnector creates a new Linea poll connector using the specified roll up contract.
func NewLineaConnector(
ctx context.Context,
logger *zap.Logger,
baseConnector Connector,
rollUpUrl string,
rollUpAddress string,
) (*LineaConnector, error) {
rollUpRawClient, err := ethRpc.DialContext(ctx, rollUpUrl)
if err != nil {
return nil, fmt.Errorf("failed to create roll up raw client for url %s: %w", rollUpUrl, err)
}
rollUpClient := ethClient.NewClient(rollUpRawClient)
addr := ethCommon.HexToAddress(rollUpAddress)
rollUpFilterer, err := rollUpAbi.NewLineaabiFilterer(addr, rollUpClient)
if err != nil {
return nil, fmt.Errorf("failed to create roll up filter for url %s: %w", rollUpUrl, err)
}
rollUpCaller, err := rollUpAbi.NewLineaabiCaller(addr, rollUpClient)
if err != nil {
return nil, fmt.Errorf("failed to create roll up caller for url %s: %w", rollUpUrl, err)
}
logger.Info("Using roll up for Linea", zap.String("rollUpUrl", rollUpUrl), zap.String("rollUpAddress", rollUpAddress))
connector := &LineaConnector{
Connector: baseConnector,
logger: logger,
rollUpRawClient: rollUpRawClient,
rollUpClient: rollUpClient,
rollUpFilterer: rollUpFilterer,
rollUpCaller: rollUpCaller,
}
return connector, nil
}
// SubscribeForBlocks starts polling. It implements the standard connector interface.
func (c *LineaConnector) SubscribeForBlocks(ctx context.Context, errC chan error, sink chan<- *NewBlock) (ethereum.Subscription, error) {
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
// Use the standard geth head sink to get latest blocks.
headSink := make(chan *ethTypes.Header, 2)
headerSubscription, err := c.Connector.Client().SubscribeNewHead(ctx, headSink)
if err != nil {
return nil, fmt.Errorf("failed to subscribe for latest blocks: %w", err)
}
// Subscribe to data finalized events from the roll up contract.
dataFinalizedChan := make(chan *rollUpAbi.LineaabiDataFinalized, 2)
dataFinalizedSub, err := c.rollUpFilterer.WatchDataFinalized(&ethBind.WatchOpts{Context: timeout}, dataFinalizedChan, nil, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to subscribe for events from roll up contract: %w", err)
}
// Get the current latest block on Linea.
latestBlock, err := GetBlockByFinality(timeout, c.logger, c.Connector, Latest)
if err != nil {
return nil, fmt.Errorf("failed to get current latest block: %w", err)
}
c.latestBlockNum = latestBlock.Number.Uint64()
// Get and publish the current latest finalized block.
opts := &ethBind.CallOpts{Context: timeout}
initialBlock, err := c.rollUpCaller.CurrentL2BlockNumber(opts)
if err != nil {
return nil, fmt.Errorf("failed to get initial block: %w", err)
}
c.latestFinalizedBlockNum = initialBlock.Uint64()
if c.latestFinalizedBlockNum > c.latestBlockNum {
return nil, fmt.Errorf("latest finalized block reported by L1 (%d) is ahead of latest block reported by L2 (%d), L2 node seems to be stuck",
c.latestFinalizedBlockNum, c.latestBlockNum)
}
c.logger.Info("queried initial finalized block", zap.Uint64("initialBlock", c.latestFinalizedBlockNum), zap.Uint64("latestBlock", c.latestBlockNum))
if err = c.postFinalizedAndSafe(ctx, c.latestFinalizedBlockNum, sink); err != nil {
return nil, fmt.Errorf("failed to post initial block: %w", err)
}
common.RunWithScissors(ctx, errC, "linea_block_poller", func(ctx context.Context) error {
for {
select {
case <-ctx.Done():
dataFinalizedSub.Unsubscribe()
return nil
case err := <-dataFinalizedSub.Err():
errC <- fmt.Errorf("finalized data watcher posted an error: %w", err)
dataFinalizedSub.Unsubscribe()
return nil
case evt := <-dataFinalizedChan:
if err := c.processDataFinalizedEvent(ctx, sink, evt); err != nil {
errC <- fmt.Errorf("failed to process block finalized event: %w", err)
dataFinalizedSub.Unsubscribe()
return nil
}
case ev := <-headSink:
if ev == nil {
c.logger.Error("new latest header event is nil")
continue
}
if ev.Number == nil {
c.logger.Error("new latest header block number is nil")
continue
}
c.latestBlockNum = ev.Number.Uint64()
sink <- &NewBlock{
Number: ev.Number,
Time: ev.Time,
Hash: ev.Hash(),
Finality: Latest,
}
}
}
})
return headerSubscription, nil
}
// processDataFinalizedEvent handles a DataFinalized event published by the roll up contract.
func (c *LineaConnector) processDataFinalizedEvent(ctx context.Context, sink chan<- *NewBlock, evt *rollUpAbi.LineaabiDataFinalized) error {
latestFinalizedBlockNum := evt.LastBlockFinalized.Uint64()
// Leaving this log info in for now because these events come very infrequently.
c.logger.Info("processing data finalized event",
zap.Uint64("latestFinalizedBlockNum", latestFinalizedBlockNum),
zap.Uint64("prevFinalizedBlockNum", c.latestFinalizedBlockNum),
)
if latestFinalizedBlockNum > c.latestBlockNum {
return fmt.Errorf("latest finalized block reported by L1 (%d) is ahead of latest block reported by L2 (%d), L2 node seems to be stuck",
latestFinalizedBlockNum, c.latestBlockNum)
}
for blockNum := c.latestFinalizedBlockNum + 1; blockNum <= latestFinalizedBlockNum; blockNum++ {
if err := c.postFinalizedAndSafe(ctx, blockNum, sink); err != nil {
c.latestFinalizedBlockNum = blockNum - 1
return fmt.Errorf("failed to post block %d: %w", blockNum, err)
}
}
c.latestFinalizedBlockNum = latestFinalizedBlockNum
return nil
}
// postFinalizedAndSafe publishes a block as finalized and safe. It takes a block number and looks it up on chain to publish the current values.
func (c *LineaConnector) postFinalizedAndSafe(ctx context.Context, blockNum uint64, sink chan<- *NewBlock) error {
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
block, err := GetBlockByNumberUint64(timeout, c.logger, c.Connector, blockNum, Finalized)
if err != nil {
return fmt.Errorf("failed to get block %d: %w", blockNum, err)
}
// Publish the finalized block.
sink <- block
// Publish same thing for the safe block.
sink <- block.Copy(Safe)
return nil
}

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,7 @@ package evm
import (
"context"
"errors"
"fmt"
"math"
"math/big"
@ -136,6 +137,10 @@ type (
ccqBatchSize int64
ccqBackfillCache bool
ccqLogger *zap.Logger
// These parameters are currently only used for Linea and should be set via SetLineaParams()
lineaRollUpUrl string
lineaRollUpContract string
}
pendingKey struct {
@ -242,6 +247,19 @@ func (w *Watcher) Run(parentCtx context.Context) error {
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
return fmt.Errorf("dialing eth client failed: %w", err)
}
} else if w.chainID == vaa.ChainIDLinea {
baseConnector, err := connectors.NewEthereumBaseConnector(timeout, w.networkName, w.url, w.contract, logger)
if err != nil {
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
return fmt.Errorf("dialing eth client failed: %w", err)
}
w.ethConn, err = connectors.NewLineaConnector(ctx, logger, baseConnector, w.lineaRollUpUrl, w.lineaRollUpContract)
if err != nil {
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
return fmt.Errorf("failed to create Linea poller: %w", err)
}
} else {
// Everything else is instant finality.
logger.Info("assuming instant finality")
@ -699,41 +717,61 @@ func fetchCurrentGuardianSet(ctx context.Context, ethConn connectors.Connector)
// getFinality determines if the chain supports "finalized" and "safe". This is hard coded so it requires thought to change something. However, it also reads the RPC
// to make sure the node actually supports the expected values, and returns an error if it doesn't. Note that we do not support using safe mode but not finalized mode.
func (w *Watcher) getFinality(ctx context.Context) (bool, bool, error) {
// TODO: Need to handle finality for Linea before it can be deployed in Mainnet.
finalized := false
safe := false
// Tilt supports polling for both finalized and safe.
if w.unsafeDevMode {
finalized = true
safe = true
// The following chains support polling for both finalized and safe.
} else if w.chainID == vaa.ChainIDAcala ||
w.chainID == vaa.ChainIDArbitrum ||
w.chainID == vaa.ChainIDArbitrumSepolia ||
w.chainID == vaa.ChainIDBase ||
w.chainID == vaa.ChainIDBaseSepolia ||
w.chainID == vaa.ChainIDBlast ||
w.chainID == vaa.ChainIDBSC ||
w.chainID == vaa.ChainIDEthereum ||
w.chainID == vaa.ChainIDHolesky ||
w.chainID == vaa.ChainIDKarura ||
w.chainID == vaa.ChainIDMantle ||
w.chainID == vaa.ChainIDMoonbeam ||
w.chainID == vaa.ChainIDOptimism ||
w.chainID == vaa.ChainIDSepolia ||
w.chainID == vaa.ChainIDHolesky ||
w.chainID == vaa.ChainIDArbitrumSepolia ||
w.chainID == vaa.ChainIDBaseSepolia ||
w.chainID == vaa.ChainIDOptimismSepolia ||
w.chainID == vaa.ChainIDSepolia ||
w.chainID == vaa.ChainIDXLayer {
finalized = true
safe = true
} else if w.chainID == vaa.ChainIDScroll {
// As of 11/10/2023 Scroll supports polling for finalized but not safe.
finalized = true
} else if w.chainID == vaa.ChainIDPolygon ||
w.chainID == vaa.ChainIDPolygonSepolia {
// The following chains have their own specialized finalizers.
} else if w.chainID == vaa.ChainIDCelo ||
w.chainID == vaa.ChainIDLinea {
return false, false, nil
// Polygon now supports polling for finalized but not safe.
// https://forum.polygon.technology/t/optimizing-decentralized-apps-ux-with-milestones-a-significantly-accelerated-finality-solution/13154
} else if w.chainID == vaa.ChainIDPolygon ||
w.chainID == vaa.ChainIDPolygonSepolia {
finalized = true
} else if w.chainID == vaa.ChainIDBerachain {
// Berachain supports instant finality: https://docs.berachain.com/faq/
// As of 11/10/2023 Scroll supports polling for finalized but not safe.
} else if w.chainID == vaa.ChainIDScroll {
finalized = true
// The following chains support instant finality.
} else if w.chainID == vaa.ChainIDAvalanche ||
w.chainID == vaa.ChainIDBerachain || // Berachain supports instant finality: https://docs.berachain.com/faq/
w.chainID == vaa.ChainIDOasis ||
w.chainID == vaa.ChainIDAurora ||
w.chainID == vaa.ChainIDFantom ||
w.chainID == vaa.ChainIDKlaytn {
return false, false, nil
// Anything else is undefined / not supported.
} else {
return false, false, fmt.Errorf("unsupported chain: %s", w.chainID.String())
}
// If finalized / safe should be supported, read the RPC to make sure they actually are.
@ -935,3 +973,22 @@ func (w *Watcher) waitForBlockTime(ctx context.Context, logger *zap.Logger, errC
func msgIdFromLogEvent(chainID vaa.ChainID, ev *ethabi.AbiLogMessagePublished) string {
return fmt.Sprintf("%v/%v/%v", uint16(chainID), PadAddress(ev.Sender), ev.Sequence)
}
// SetLineaParams is used to enable polling on Linea using the roll up contract on Ethereum.
func (w *Watcher) SetLineaParams(lineaRollUpUrl string, lineaRollUpContract string) error {
if w.chainID != vaa.ChainIDLinea {
return errors.New("function only allowed for Linea")
}
if w.unsafeDevMode && lineaRollUpUrl == "" && lineaRollUpContract == "" {
return nil
}
if lineaRollUpUrl == "" {
return fmt.Errorf("lineaRollUpUrl must be set")
}
if lineaRollUpContract == "" {
return fmt.Errorf("lineaRollUpContract must be set")
}
w.lineaRollUpUrl = lineaRollUpUrl
w.lineaRollUpContract = lineaRollUpContract
return nil
}

View File

@ -40,7 +40,7 @@ service NodePrivilegedService {
// ChainGovernorReleasePendingVAA release a VAA from the chain governor pending list, publishing it immediately.
rpc ChainGovernorReleasePendingVAA (ChainGovernorReleasePendingVAARequest) returns (ChainGovernorReleasePendingVAAResponse);
// ChainGovernorResetReleaseTimer resets the release timer for a chain governor pending VAA to the configured maximum.
rpc ChainGovernorResetReleaseTimer (ChainGovernorResetReleaseTimerRequest) returns (ChainGovernorResetReleaseTimerResponse);
@ -51,10 +51,10 @@ service NodePrivilegedService {
rpc SignExistingVAA (SignExistingVAARequest) returns (SignExistingVAAResponse);
// DumpRPCs returns the RPCs being used by the guardian
rpc DumpRPCs (DumpRPCsRequest) returns (DumpRPCsResponse);
rpc DumpRPCs (DumpRPCsRequest) returns (DumpRPCsResponse);
// GetMissingVAAs returns the VAAs from a cloud function that need to be reobserved.
rpc GetAndObserveMissingVAAs (GetAndObserveMissingVAAsRequest) returns (GetAndObserveMissingVAAsResponse);
rpc GetAndObserveMissingVAAs (GetAndObserveMissingVAAsRequest) returns (GetAndObserveMissingVAAsResponse);
}
message InjectGovernanceVAARequest {
@ -85,7 +85,7 @@ message GovernanceMessage {
GuardianSetUpdate guardian_set = 10;
ContractUpgrade contract_upgrade = 11;
// Token bridge, NFT module, and Wormhole Relayer module (for the first two)
// Token bridge, NFT module, and Wormhole Relayer module (for the first two)
BridgeRegisterChain bridge_register_chain = 12;
BridgeUpgradeContract bridge_contract_upgrade = 13;
@ -117,6 +117,10 @@ message GovernanceMessage {
IbcUpdateChannelChain ibc_update_channel_chain = 21;
// Wormhole Relayer module
WormholeRelayerSetDefaultDeliveryProvider wormhole_relayer_set_default_delivery_provider = 22;
// Generic governance
EvmCall evm_call = 28;
SolanaCall solana_call = 29;
}
}
@ -191,7 +195,7 @@ message AccountantModifyBalance {
// ContractUpgrade represents a Wormhole contract update to be submitted to and signed by the node.
message ContractUpgrade {
// ID of the chain where the Wormhole contract should be updated (uint8).
// ID of the chain where the Wormhole contract should be updated (uint16).
uint32 chain_id = 1;
// Hex-encoded address (without leading 0x) address of the new program/contract.
@ -417,3 +421,30 @@ message GetAndObserveMissingVAAsRequest {
message GetAndObserveMissingVAAsResponse {
string response =1;
}
// EvmCall represents a generic EVM call that can be executed by the generalized governance contract.
message EvmCall {
// ID of the chain where the action should be executed (uint16).
uint32 chain_id = 1;
// Address of the governance contract (eth address starting with 0x)
string governance_contract = 2;
// Address of the governed contract (eth address starting with 0x)
string target_contract = 3;
// ABI-encoded calldata to be passed on to the governed contract (hex encoded)
string abi_encoded_call = 4;
}
// SolanaCall represents a generic Solana call that can be executed by the generalized governance contract.
message SolanaCall {
// ID of the chain where the action should be executed (uint16).
uint32 chain_id = 1;
// Address of the governance contract (solana address)
string governance_contract = 2;
// Encoded instruction data to be passed on to the governed contract (hex encoded)
string encoded_instruction = 3;
}

View File

@ -4,8 +4,9 @@ import (
"bytes"
"encoding/binary"
"fmt"
"math"
"github.com/ethereum/go-ethereum/common"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)
@ -55,6 +56,13 @@ var WormholeRelayerModule = [32]byte{
}
var WormholeRelayerModuleStr = string(WormholeRelayerModule[:])
var GeneralPurposeGovernanceModule = [32]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x6C,
0x50, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x47, 0x6F, 0x76, 0x65, 0x72, 0x6E, 0x61, 0x6E,
0x63, 0x65,
}
var GeneralPurposeGovernanceModuleStr = string(GeneralPurposeGovernanceModule[:])
type GovernanceAction uint8
var (
@ -99,6 +107,10 @@ var (
// Wormhole relayer governance actions
WormholeRelayerSetDefaultDeliveryProvider GovernanceAction = 3
// General purpose governance
GeneralPurposeGovernanceEvmAction GovernanceAction = 1
GeneralPurposeGovernanceSolanaAction GovernanceAction = 2
)
type (
@ -110,7 +122,7 @@ type (
// BodyGuardianSetUpdate is a governance message to set a new guardian set
BodyGuardianSetUpdate struct {
Keys []common.Address
Keys []ethcommon.Address
NewIndex uint32
}
@ -216,6 +228,24 @@ type (
ChainID ChainID
NewDefaultDeliveryProviderAddress Address
}
// BodyGeneralPurposeGovernanceEvm is a general purpose governance message for EVM chains
BodyGeneralPurposeGovernanceEvm struct {
ChainID ChainID
GovernanceContract ethcommon.Address
TargetContract ethcommon.Address
Payload []byte
}
// BodyGeneralPurposeGovernanceSolana is a general purpose governance message for Solana chains
BodyGeneralPurposeGovernanceSolana struct {
ChainID ChainID
GovernanceContract Address
// NOTE: unlike in EVM, no target contract in the schema here, the
// instruction encodes the target contract address (unlike in EVM, where
// an abi encoded calldata doesn't include the target contract address)
Instruction []byte
}
)
func (b BodyContractUpgrade) Serialize() []byte {
@ -402,6 +432,31 @@ func (r BodyWormholeRelayerSetDefaultDeliveryProvider) Serialize() []byte {
return serializeBridgeGovernanceVaa(WormholeRelayerModuleStr, WormholeRelayerSetDefaultDeliveryProvider, r.ChainID, payload.Bytes())
}
func (r BodyGeneralPurposeGovernanceEvm) Serialize() ([]byte, error) {
payload := &bytes.Buffer{}
payload.Write(r.GovernanceContract[:])
payload.Write(r.TargetContract[:])
// write payload len as uint16
if len(r.Payload) > math.MaxUint16 {
return nil, fmt.Errorf("payload too long; expected at most %d bytes", math.MaxUint16)
}
MustWrite(payload, binary.BigEndian, uint16(len(r.Payload)))
payload.Write(r.Payload)
return serializeBridgeGovernanceVaa(GeneralPurposeGovernanceModuleStr, GeneralPurposeGovernanceEvmAction, r.ChainID, payload.Bytes()), nil
}
func (r BodyGeneralPurposeGovernanceSolana) Serialize() []byte {
payload := &bytes.Buffer{}
payload.Write(r.GovernanceContract[:])
// NOTE: unlike in EVM, we don't write the payload length here, because we're using
// a custom instruction encoding (there is no standard encoding like evm ABI
// encoding), generated by an external tool. That tool length-prefixes all
// the relevant dynamic fields.
payload.Write(r.Instruction)
return serializeBridgeGovernanceVaa(GeneralPurposeGovernanceModuleStr, GeneralPurposeGovernanceSolanaAction, r.ChainID, payload.Bytes())
}
func EmptyPayloadVaa(module string, actionId GovernanceAction, chainId ChainID) []byte {
return serializeBridgeGovernanceVaa(module, actionId, chainId, []byte{})
}

View File

@ -669,11 +669,13 @@ func New(
// Wrap the standard cosmos-sdk antehandlers with additional antehandlers:
// - wormhole allowlist antehandler
// - wormhole ibc error antehandler
// - default ibc antehandler
func WrapAnteHandler(originalHandler sdk.AnteHandler, wormKeeper wormholemodulekeeper.Keeper, ibcKeeper *ibckeeper.Keeper) sdk.AnteHandler {
whHandler := wormholemoduleante.NewWormholeAllowlistDecorator(wormKeeper)
whIbcHandler := wormholemoduleante.NewWormholeIbcErrorDecorator()
ibcHandler := ibcante.NewAnteDecorator(ibcKeeper)
newHandlers := sdk.ChainAnteDecorators(whHandler, ibcHandler)
newHandlers := sdk.ChainAnteDecorators(whHandler, whIbcHandler, ibcHandler)
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
newCtx, err := originalHandler(ctx, tx, simulate)
if err != nil {

View File

@ -38,7 +38,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/coinbase/rosetta-sdk-go v0.7.0 // indirect
@ -49,8 +49,7 @@ require (
github.com/cosmos/gogoproto v1.4.3 // indirect
github.com/cosmos/gorocksdb v1.2.0 // indirect
github.com/cosmos/iavl v0.19.4 // indirect
github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect
github.com/cosmos/ledger-go v0.9.2 // indirect
github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect
github.com/creachadair/taskgroup v0.3.2 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@ -122,7 +121,8 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tendermint/btcd v0.1.1 // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/zondax/hid v0.9.0 // indirect
github.com/zondax/hid v0.9.2 // indirect
github.com/zondax/ledger-go v0.14.3 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.2.0 // indirect

View File

@ -332,6 +332,7 @@ github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
@ -579,6 +580,8 @@ github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ
github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8=
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw=
github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M=
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
@ -2118,6 +2121,10 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI=
gitlab.com/bosi/decorder v0.2.1/go.mod h1:6C/nhLSbF6qZbYD8bRmISBwc6vcWdNsiIBkRvjJFrH0=
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=

View File

@ -1,11 +1,47 @@
package ante
import (
"errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
ibcchanneltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
"github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
)
type WormholeIbcErrorDecorator struct{}
func NewWormholeIbcErrorDecorator() WormholeIbcErrorDecorator {
return WormholeIbcErrorDecorator{}
}
func (wh WormholeIbcErrorDecorator) AnteHandle(request sdk.Request, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Request, err error) {
// ignore all blocks except 3_151_174
if request.BlockHeight() != 3_151_174 {
return next(request, tx, simulate)
}
// if we're on block 3_151_174, we need to reject the IBC Channel Open Init transaction
// the transaction should have a single message
msgs := tx.GetMsgs()
if len(msgs) != 1 {
return next(request, tx, simulate)
}
// ensure the single message in the tx is an IBC Channel Open Init message
switch msgs[0].(type) {
case *ibcchanneltypes.MsgChannelOpenInit:
// we've verified it's the IBC Channel Open Init message.
// fail with the proper error message
// failing early uses 55613 gas in total. We need to use 59774 gas to get the same apphash.
// 59774 - 55613 = 4161
request.GasMeter().ConsumeGas(4161, "consuming extra gas so the transaction uses 59,774 gas")
return request, errors.New("failed to execute message; message index: 0: route not found to module: wasm: route not found")
default:
return next(request, tx, simulate)
}
}
// Reject all messages if we're expecting a software update.
type WormholeAllowlistDecorator struct {
k keeper.Keeper