From e297d96d101857f98e0fbba10168b6dc7b55d9c0 Mon Sep 17 00:00:00 2001 From: Bruce Riley Date: Thu, 26 Sep 2024 11:35:55 -0500 Subject: [PATCH 1/7] go-sdk: Add xLabs public RPC --- sdk/mainnet_consts.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/mainnet_consts.go b/sdk/mainnet_consts.go index 968a718842..aec129a723 100644 --- a/sdk/mainnet_consts.go +++ b/sdk/mainnet_consts.go @@ -15,6 +15,7 @@ var PublicRPCEndpoints = []string{ "https://wormhole-v2-mainnet-api.mcf.rocks", "https://wormhole-v2-mainnet-api.chainlayer.network", "https://wormhole-v2-mainnet-api.staking.fund", + "https://guardian.mainnet.xlabs.xyz", } type ( From eb7b219a2da572c0faaf849f4eb66c13ee7b432d Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:09:05 -0600 Subject: [PATCH 2/7] Doc: Create guardian.md (#4125) * Doc: Create guardian.md * Review rework * Add reobservation --- docs/guardian.md | 132 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs/guardian.md diff --git a/docs/guardian.md b/docs/guardian.md new file mode 100644 index 0000000000..1f186daf42 --- /dev/null +++ b/docs/guardian.md @@ -0,0 +1,132 @@ +# Wormhole Guardian Node + +The guardian node software is responsible for monitoring Wormhole connected chains, reporting on-chain observations, +aggregating those observations with those reported by other guardians until quorum is reached, and publishing signed VAAs. + +For details on how to set up and run a guardian node see [operations.md](operations.md). + +## Components + +### Watchers + +The watchers are responsible for monitoring the connected chains for Wormhole messages. They listen for messages published +by the Wormhole core contract and post them to the processor. They also handle requests to re-observe messages that may have +been missed. Additionally they monitor the latest block posted on chain, which is published in gossip heartbeat messages. + +Each watcher listens to one chain, and there are a number of different watcher types: + +1. EVM - the EVM watcher connects to one of the EVM chains using the `go-ethereum` library. It uses the EVM subscription + mechanism to listen for new logs from the Wormhole core contract as well as the latest blocks. Additionally, it polls for + new finalized and safe blocks (as supported on each chain). + +2. Solana - the Solana watcher monitors a Solana runtime based chain, namely Solana and Pyth. It uses the `solana-go` library. + For each chain, there are two watchers, one listening for new confirmed slots, and one listening for new finalized slots. + For Pyth, it uses a web socket subscription to listen for messages. For Solana, it polls for slots. + +3. Cosmwasm - the Cosmwasm watcher connects to the various Cosmwasm chains (one chain per watcher instance). It subscribes + for events from the Wormhole core contract and polls for new blocks. + +4. Sui / Aptos / Algorand / Near - there are bespoke watchers for each of these chains that listen / poll for messages from + the Wormhole core contract and poll for new blocks. + +5. IBC - there is a Cosmwasm based watcher that listens to the IBC relayer contract on Gateway and publishes messages. The + IBC watcher is different from the others in that a single watcher instance monitors _all_ IBC connected chains. + +### P2P + +The p2p package is responsible for listening to messages from and publishing messages to the libp2p based gossip network. +All of the guardians communicate over gossip. Most importantly, they publish observations they make and signed VAAs +for all observations that they observe reaching quorum. Additionally, they routinely publish heartbeats and various other +status events (such as from the governor). + +The p2p package primarily posts events to and receives events from the processor package using golang channels. + +The p2p package also maintains a separate pair of topics used for receiving and sending Wormhole Queries messages. +The guardian joins both of these topics, but it only subscribes to the request topic. This means a given guardian +does not see the Queries responses from other guardians. The guardian also applies libp2p filters so that it only +processes requests from a select set of peers. + +### Processor + +The processor takes messages observed from the watchers and observations gossipped by other guardians. It aggregates these +until an observation is seen by a quorum of guardians, including itself. It then publishes the signed VAA. The processor +stores signed VAAs in an on-disk badgerDB instance. The processor also interfaces with the governor and accountant packages +before publishing an observation. + +### Governor + +The governor is responsible for verifying that a given token bridge transfer will not exceed a daily limit for the given +chain and is not too large. For a detailed descriptions of the governor, see [governor.md](governor.md). + +### Accountant + +The accountant package is responsible for interfacing with the accountant contracts on Gateway to verify that a given +transfer will not exceed the available notional value for a chain. The accountant package interfaces with two contracts. +The accountant contract is used to monitor Token Bridge transfers and the NTT-accountant is used to monitor NTT transfers. + +### Query Support + +The query package is used to process Wormhole Queries (also known as CCQ, which stands for Cross Chain Queries) requests, +forwarding them to the appropriate watcher for processing, and posting the responses. It receives requests and publishes +responses over a separate set of P2P topics, via the p2p package. + +Queries are currently supported on EVM and Solana. + +### Admin Interface + +The guardian supports a variety of admin commands to do things like request re-observation or sign a given payload using its key. + +### Public RPC Endpoint + +Certain guardians may be configured as a public RPC endpoint. If this feature is enabled, the guardian will listen for https requests +for things like the status of a given VAA. + +For a list of the guardian public RPC endpoints see `PublicRPCEndpoints` in [sdk/mainnet_consts.go](../sdk/mainnet_consts.go) + +## Observation Lifecycle + +An observation transitions through the following steps: + +1. The watcher observes a message published by the Wormhole core contract. + +2. On certain chains that do not have instant finality, the watcher waits until the block in which the message was + published reaches the appropriate state, such as the block is finalized, safe, or the integrator requested instant + finality. +3. When the watcher determines the block containing the message has reached the designated finality, it posts it to the + processor via a golang channel. + +4. The processor performs integrity checks on the message (via the governor and accountant). Either of these + components may cause the observation to be delayed (the governor up to twenty-four hours, the accountant until + a quorum of guardians report the observation). + +5. Once the message clears the integrity checks, the processor signs the observation using the guardian key and + posts it to an internal golang channel for batch publishing. + +6. The processor batches observations, waiting no more than a second before publishing a batch. The batches are posted + to the p2p package for publishing as a signed observation batch. The processor also allows for certain observations + (specifically Pyth messages) to be published without batching. These are published immediately with a batch size of one. + +7. The p2p package posts the signed observation batches to gossip. + +8. Other guardians receive the observation. Each guardian aggregates the observation until it reaches quorum and + it has made the observation itself. + +9. Once an observation reaches quorum, the processor generates a VAA and posts it to the p2p package for publishing + as a signed VAA with quorum. + +10. The p2p package posts the signed VAA to gossip. + +## Reobservation Requests + +The guardian supports requesting that a missed observation be reobserved. These requests can originate from three sources: + +1. From the guardian operator via an admin command. + +2. From another guardian via gossip message. + +3. Generated internally from the processor or accountant packages. + +An observation request contains the chain ID and the transaction ID of the missed observation. The request is forwarded +to the watcher, based on the chain ID. The watcher queries for the transaction using the transaction ID and then publishes +the reobserved message to the processor. Note that there is a throttling mechanism in place to avoid requesting reobservation +of the same transaction too frequently. From 818fda34f7f218fe9f23fdaf2d760b11ad174ce8 Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:35:38 -0600 Subject: [PATCH 3/7] Node: Set mainnet gossip cutover time (#4127) --- node/pkg/p2p/gossip_cutover.go | 2 +- node/pkg/processor/cutover.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/pkg/p2p/gossip_cutover.go b/node/pkg/p2p/gossip_cutover.go index bf607320c5..3f337769dd 100644 --- a/node/pkg/p2p/gossip_cutover.go +++ b/node/pkg/p2p/gossip_cutover.go @@ -10,7 +10,7 @@ import ( ) // The format of this time is very picky. Please use the exact format specified by cutOverFmtStr! -const mainnetCutOverTimeStr = "" +const mainnetCutOverTimeStr = "2024-10-29T09:00:00-0500" const testnetCutOverTimeStr = "2024-09-24T09:00:00-0500" const devnetCutOverTimeStr = "2024-08-01T00:00:00-0500" const cutOverFmtStr = "2006-01-02T15:04:05-0700" diff --git a/node/pkg/processor/cutover.go b/node/pkg/processor/cutover.go index 7d7969796f..49c9dac235 100644 --- a/node/pkg/processor/cutover.go +++ b/node/pkg/processor/cutover.go @@ -10,7 +10,7 @@ import ( ) // The format of this time is very picky. Please use the exact format specified by cutOverFmtStr! -const mainnetCutOverTimeStr = "" +const mainnetCutOverTimeStr = "2024-10-29T09:00:00-0500" const testnetCutOverTimeStr = "2024-09-24T09:00:00-0500" const devnetCutOverTimeStr = "2024-08-01T00:00:00-0500" const cutOverFmtStr = "2006-01-02T15:04:05-0700" From c9690c9f858c3234857a3f8255d8a102df30bfe6 Mon Sep 17 00:00:00 2001 From: John Saigle <4022790+johnsaigle@users.noreply.github.com> Date: Sun, 6 Oct 2024 16:39:20 -0400 Subject: [PATCH 4/7] eth: fix unbound variable errors in shell utilities (#4103) The `set -u` options in these scripts caused them to fail with 'unbound variable' errors when CLI args or env variables were unset. This commit fixes the validation so that the scripts output usage info or helpful errors instead of exiting with unbound variable errors that the user must read the source to diagnose. for the script `ethereum/sh/upgrade_all_testnet.sh`, the commit updates a variable name that appears incorrect. --- ethereum/anvil_fork | 5 +++-- ethereum/sh/upgrade.sh | 22 ++++++++++++++++++---- ethereum/sh/upgrade_all_testnet.sh | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ethereum/anvil_fork b/ethereum/anvil_fork index 9d5044c181..ad9644782d 100755 --- a/ethereum/anvil_fork +++ b/ethereum/anvil_fork @@ -5,11 +5,12 @@ set -euo pipefail # This script forks a chain using anvil with the mnemonic that is used in the # testing environment. -if [ -z "$1" ]; then +CHAIN_NAME="${1:-}" + +if [ -z "$CHAIN_NAME" ]; then echo "Usage: $0 " >&2 exit 1 fi -CHAIN_NAME="$1" DOCKER_ARGS="-p 8545:8545" ./foundry anvil --host 0.0.0.0 --base-fee 0 --fork-url $(worm info rpc mainnet $CHAIN_NAME) --mnemonic "myth like bonus scare over problem client lizard pioneer submit female collect" diff --git a/ethereum/sh/upgrade.sh b/ethereum/sh/upgrade.sh index 42e8c87b90..947b166181 100755 --- a/ethereum/sh/upgrade.sh +++ b/ethereum/sh/upgrade.sh @@ -8,14 +8,28 @@ set -euo pipefail -network=$1 -module=$2 -chain=$3 +network="${1:-}" +module="${2:-}" +chain="${3:-}" + +if [ -z "$network" ] || [ -z "$module" ] || [ -z "$chain" ]; then + echo "Usage: MNEMONIC=... $0 " >&2 + exit 1 +fi + +if [ -z "${MNEMONIC:-}" ]; then + echo "MNEMONIC unset" + exit 1 +fi secret=$MNEMONIC guardian_secret="" if [ "$network" = testnet ]; then + if [ -z "${GUARDIAN_MNEMONIC:-}" ]; then + echo "GUARDIAN_MNEMONIC unset" + exit 1 + fi guardian_secret=$GUARDIAN_MNEMONIC fi @@ -114,4 +128,4 @@ if [ "$network" = testnet ]; then worm submit $(worm generate upgrade -c "$chain" -a "$new_implementation" -m "$module" -g "$guardian_secret") -n "$network" else echo "../scripts/contract-upgrade-governance.sh -c $chain -m $verify_module -a $new_implementation" -fi \ No newline at end of file +fi diff --git a/ethereum/sh/upgrade_all_testnet.sh b/ethereum/sh/upgrade_all_testnet.sh index de3c283023..334fcde598 100755 --- a/ethereum/sh/upgrade_all_testnet.sh +++ b/ethereum/sh/upgrade_all_testnet.sh @@ -21,7 +21,7 @@ set -uo pipefail network=testnet for module in ${MODULES[@]}; do - for chain in ${chains[@]}; do + for chain in ${CHAINS[@]}; do echo "Upgrading ${chain} ${module} ********************************************************************" ./sh/upgrade.sh "$network" "$module" "$chain" done From f475e0d7e47cd7ec148b58c1b139bd32594976b1 Mon Sep 17 00:00:00 2001 From: Dirk Brink Date: Tue, 8 Oct 2024 06:29:29 -0700 Subject: [PATCH 5/7] node/hack: Print potential USD stablecoins that appear depegged (#4130) * node/hack: Print potential USD stablecoins that appear depegged * PR feedback --- node/hack/governor/src/index.ts | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/node/hack/governor/src/index.ts b/node/hack/governor/src/index.ts index be14c0defc..3ce80de907 100644 --- a/node/hack/governor/src/index.ts +++ b/node/hack/governor/src/index.ts @@ -14,6 +14,29 @@ import { arrayify, zeroPad } from "ethers/lib/utils"; const MinNotional = 0; // Price change tolerance in %. Fallback to 30% const PriceDeltaTolerance = process.env.PRICE_TOLERANCE ? Math.min(100, Math.max(0, parseInt(process.env.PRICE_TOLERANCE))) : 30; +// The percentage by which the price deviates from $1 to be considered depegged +const usdDepegPercentage = process.env.DEPEG_PERCENTAGE ? Math.min(100, Math.max(0, parseInt(process.env.DEPEG_PERCENTAGE))) : 10; +const usdPeggedStablecoins = [ + "USD", // Matches with USDT, USDC, BUSD, etc. + "PAX", // Pax Dollar + "DAI", // Dai + "RSV", // Reserve + "VAI", // Vai + "FRAX", // Frax + "FEI", // Fei +]; +const expectedUSDDepeggs = [ + "2-00000000000000000000000045804880de22913dafe09f4980848ece6ecbaf78-PAXG", // This is PaxGold and not pegged to $1 + "2-000000000000000000000000d13cfd3133239a3c73a9e535a5c4dadee36b395c-VAI", // This is Vaiot, not the VAI stablecoin + "5-000000000000000000000000ee327f889d5947c1dc1934bb208a1e792f953e96-frxETH", // Frax ETH + "23-0000000000000000000000009d2f299715d94d8a7e6f5eaa8e654e8c74a988a7-FXS", // Frax Share + "2-0000000000000000000000003432b6a60d23ca0dfca7761b7ab56459d9c964d0-FXS", // Frax Share + "23-00000000000000000000000051318b7d00db7acc4026c88c3952b66278b6a67f-PLS", // Plutus DAO + "3-0100000000000000000000000000000000000000000000000000000075757364-UST", // Terra USD + "2-000000000000000000000000dfdb7f72c1f195c5951a234e8db9806eb0635346-NFD", // Feisty Doge NFT + "2-00000000000000000000000000c5ca160a968f47e7272a0cfcda36428f386cb6-USDEBT", // US Debt Meme coin + "4-00000000000000000000000011a38e06699b238d6d9a0c7a01f3ac63a07ad318-USDFI", // USDFI is a protocol, not a stablecoin +] const axios = require("axios"); const fs = require("fs"); @@ -54,6 +77,7 @@ if (fs.existsSync(IncludeFileName)) { var existingTokenPrices = {}; var existingTokenKeys: string[] = []; var newTokenKeys = {}; +var depeggedUSDStablecoins = []; fs.readFile("../../pkg/governor/generated_mainnet_tokens.go", "utf8", function(_, doc) { var matches = doc.matchAll(/{chain: (?[0-9]+).+addr: "(?[0-9a-fA-F]+)".*symbol: "(?.*)", coin.*price: (?.*)}.*\n/g); @@ -174,6 +198,18 @@ axios } } + // This token looks like a USD stablecoin + if (usdPeggedStablecoins.findIndex(element => data.Symbol.toLowerCase().includes(element.toLowerCase()) || data.CoinGeckoId.toLowerCase().includes(element.toLowerCase())) != -1 ) { + // The token price has deviated significantly from $1 + if (data.TokenPrice > 1 * ((100 + usdDepegPercentage) / 100) || data.TokenPrice < 1 * ((100 - usdDepegPercentage) / 100)) { + var uniqueIdentifier = chain + "-" + wormholeAddr + "-" + data.Symbol; + // Skip tokens that are not expected to be pegged to $1 + if (!expectedUSDDepeggs.includes(uniqueIdentifier)) { + depeggedUSDStablecoins.push(uniqueIdentifier + " = " + data.TokenPrice); + } + } + } + // This is a new token if (existingTokenPrices[chain] == undefined || existingTokenPrices[chain][wormholeAddr] == undefined) { addedTokens.push(chain + "-" + wormholeAddr + "-" + data.Symbol); @@ -268,6 +304,9 @@ axios changedContent += "\n\nTokens with invalid symbols = " + failedInputValidationTokens.length + ":\n--\n\n"; changedContent += JSON.stringify(failedInputValidationTokens, null, 1); + changedContent += "\n\nPotentially depegged USD stablecoins (>" + usdDepegPercentage + "%) = " + depeggedUSDStablecoins.length + ":\n-- = \n\n"; + changedContent += JSON.stringify(depeggedUSDStablecoins, null, 1); + changedContent += "\n\nTokens with significant price drops (>" + PriceDeltaTolerance + "%) = " + significantPriceChanges.length + ":\n\n" changedContent += JSON.stringify(significantPriceChanges, null, 1); changedContent += "\n```"; From ddf973fff4b7fdcdcffa5d6dcb3c4e73a96b2acf Mon Sep 17 00:00:00 2001 From: Dirk Brink Date: Tue, 8 Oct 2024 07:23:09 -0700 Subject: [PATCH 6/7] whitepapers: Clarify CCQ sol_pda support (#4129) --- whitepapers/0013_ccq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/whitepapers/0013_ccq.md b/whitepapers/0013_ccq.md index 775fef8b60..d9f1e29575 100644 --- a/whitepapers/0013_ccq.md +++ b/whitepapers/0013_ccq.md @@ -301,7 +301,7 @@ Currently the supported query types on EVM are `eth_call`, `eth_call_by_timestam #### Solana Queries -Currently the only supported query type on Solana is `sol_account`. +Currently the supported query types on Solana are `sol_account` and `sol_pda`. 1. sol_account (query type 4) - this query is used to read data for one or more accounts on Solana. From 4894961064a1c4d1c0adbd85acb890cc0d0a1a1d Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:04:27 -0600 Subject: [PATCH 7/7] GitHub: Update code owners for go sdk (#4133) --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 60f3d728e6..e9f7b5034b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -23,14 +23,14 @@ /lp_ui/ @evan-gray @kev1n-peters /relayer/generic_relayer @nonergodic @gator-boi /scripts/ @evan-gray @kcsongor -/sdk/ @bruce-riley @evan-gray @kev1n-peters @SEJeff +/sdk/ @bruce-riley @evan-gray @kev1n-peters @panoel @SEJeff /sdk/js-proto-node/ @evan-gray @kev1n-peters /sdk/js-proto-web/ @evan-gray @kev1n-peters /sdk/js-query/ @evan-gray @kev1n-peters @bruce-riley /sdk/js-wasm/ @evan-gray @kev1n-peters /sdk/js/ @evan-gray @kev1n-peters @panoel /sdk/rust/ @a5-pickle -/sdk/vaa/ @bruce-riley @SEJeff +/sdk/vaa/ @bruce-riley @evan-gray @panoel @SEJeff /spydk/ @evan-gray /testing/ @a5-pickle @evan-gray /wormchain/contracts/tools/ @evan-gray @kev1n-peters @panoel