Skip to content

Commit 3f952dc

Browse files
committed
fix(pyth): keep router work out of chain upgrade
Restores the v2.1.0 upgrade handler and wasm generation script to main so the PR no longer changes chain-upgrade behavior. The router verifier is a fresh contract deployment path, not a software-upgrade migration path. Renames the reused VAA parser module from wormhole to vaa and updates comments so the code does not imply Wormhole contract verification. Signed-off-by: Joseph Chalabi <chalabi.joseph@gmail.com>
1 parent 016b36b commit 3f952dc

8 files changed

Lines changed: 111 additions & 15 deletions

File tree

contracts/pyth/src/accumulator.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! PNAU (Pyth Network Accumulator Update) parser
22
//!
33
//! Parses the accumulator format returned by Pyth Hermes v2 API.
4-
//! This format wraps a Wormhole VAA containing a Merkle root,
4+
//! This format wraps a router-signed VAA-format message containing a Merkle root,
55
//! along with price updates and their Merkle proofs.
66
//!
77
//! The MerklePriceUpdate section uses big-endian for length prefixes.
@@ -14,8 +14,8 @@ use sha3::{Digest, Keccak256};
1414
/// Magic bytes identifying PNAU format
1515
pub const PNAU_MAGIC: &[u8] = b"PNAU";
1616

17-
/// Wormhole Merkle update type
18-
pub const UPDATE_TYPE_WORMHOLE_MERKLE: u8 = 0;
17+
/// Merkle proof update type used by Pyth accumulator payloads.
18+
pub const UPDATE_TYPE_MERKLE_PROOF: u8 = 0;
1919

2020
/// Merkle tree constants (matching Pyth's implementation)
2121
const MERKLE_LEAF_PREFIX: u8 = 0;
@@ -24,7 +24,7 @@ const MERKLE_NODE_PREFIX: u8 = 1;
2424
/// Parsed PNAU accumulator update
2525
#[derive(Debug)]
2626
pub struct AccumulatorUpdate {
27-
/// The embedded Wormhole VAA (contains signed Merkle root)
27+
/// The embedded router-signed VAA-format message.
2828
pub vaa: Binary,
2929
/// Merkle root from the VAA payload
3030
pub merkle_root: [u8; 20],
@@ -49,7 +49,7 @@ pub struct PriceUpdateWithProof {
4949
/// - Minor version (1 byte) [offset 5]
5050
/// - Trailing length (1 byte) [offset 6]
5151
/// - Trailing data (trailing_len bytes) [offset 7 to 7+trailing_len-1]
52-
/// - Proof discriminant (1 byte): 0=WormholeMerkle [offset 7+trailing_len]
52+
/// - Proof discriminant (1 byte): 0=Merkle proof update [offset 7+trailing_len]
5353
/// - VAA length (2 bytes, big-endian) [offset 8+trailing_len]
5454
/// - VAA data (vaa_len bytes)
5555
/// - Number of updates (1 byte)
@@ -96,10 +96,10 @@ pub fn parse_accumulator_update(data: &[u8]) -> StdResult<AccumulatorUpdate> {
9696
let update_type = data[offset];
9797
offset += 1;
9898

99-
// Only support WormholeMerkle updates
100-
if update_type != UPDATE_TYPE_WORMHOLE_MERKLE {
99+
// Only support Merkle proof updates carrying the router-signed VAA message.
100+
if update_type != UPDATE_TYPE_MERKLE_PROOF {
101101
return Err(StdError::msg(format!(
102-
"Unsupported update type: {}, expected WormholeMerkle (0)",
102+
"Unsupported update type: {}, expected Merkle proof update (0)",
103103
update_type
104104
)));
105105
}
@@ -148,11 +148,11 @@ pub fn parse_accumulator_update(data: &[u8]) -> StdResult<AccumulatorUpdate> {
148148
})
149149
}
150150

151-
/// Extract the Merkle root from a Wormhole VAA payload
151+
/// Extract the Merkle root from a router-signed VAA-format payload.
152152
fn extract_merkle_root_from_vaa(vaa: &[u8]) -> StdResult<[u8; 20]> {
153153
// VAA structure:
154154
// - Version (1 byte)
155-
// - Guardian set index (4 bytes)
155+
// - Router set index, stored in the VAA guardian_set_index field (4 bytes)
156156
// - Signature count (1 byte)
157157
// - Signatures (66 bytes each)
158158
// - Body starts after signatures
@@ -181,7 +181,7 @@ fn extract_merkle_root_from_vaa(vaa: &[u8]) -> StdResult<[u8; 20]> {
181181
let payload = &vaa[payload_offset..];
182182

183183
// Payload for Merkle root:
184-
// - Magic "AUWV" (4 bytes) - Accumulator Update Wormhole Verification
184+
// - Magic "AUWV" (4 bytes)
185185
// - Update type (1 byte)
186186
// - Slot (8 bytes)
187187
// - Ring size (4 bytes)
@@ -261,7 +261,7 @@ fn parse_price_update(data: &[u8], mut offset: usize) -> StdResult<(PriceUpdateW
261261
/// Verify a Merkle proof for a price update
262262
///
263263
/// The proof demonstrates that the message is included in the tree
264-
/// whose root was signed by Wormhole guardians.
264+
/// whose root was signed by Pyth routers.
265265
pub fn verify_merkle_proof(
266266
message_data: &[u8],
267267
proof: &[[u8; 20]],

contracts/pyth/src/integration_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn setup_contract(deps: &mut MockDeps) -> Addr {
7676
}
7777

7878
/// Helper to simulate a price update by directly modifying state
79-
/// (Used to test query responses without needing Wormhole mock)
79+
/// (Used to test query responses without needing a signed PNAU fixture)
8080
fn simulate_price_update(deps: &mut MockDeps, price: u128, conf: u128, publish_time: i64) {
8181
let mut price_feed = PRICE_FEED.load(&deps.storage).unwrap();
8282
price_feed.prev_publish_time = price_feed.publish_time;

contracts/pyth/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub mod oracle;
66
pub mod pyth;
77
pub mod router;
88
pub mod state;
9-
pub mod wormhole;
9+
pub mod vaa;
1010

1111
#[cfg(test)]
1212
mod integration_tests;

contracts/pyth/src/router.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
error::ContractError,
77
msg::{RouterAddress, RouterVerifierConfigMsg},
88
state::RouterVerifierConfig,
9-
wormhole::ParsedVAA,
9+
vaa::ParsedVAA,
1010
};
1111

1212
const ROUTER_COUNT: usize = 5;

script/wasm2go.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env bash
2+
3+
# Generate Go source file containing wasm contract byte arrays.
4+
# Usage: script/wasm2go.sh
5+
#
6+
# Reads .wasm files from .cache/cosmwasm/artifacts/ and writes a Go source file
7+
# to upgrades/software/v2.0.0/contracts.go with byte slice variables.
8+
9+
set -euo pipefail
10+
11+
ARTIFACTS_DIR=".cache/cosmwasm/artifacts"
12+
OUTPUT_FILE="upgrades/software/v2.1.0/contracts.go"
13+
14+
if [[ ! -d "$ARTIFACTS_DIR" ]]; then
15+
echo "error: $ARTIFACTS_DIR does not exist. Run 'make build-contracts' first." >&2
16+
exit 1
17+
fi
18+
19+
wormhole_wasm="$ARTIFACTS_DIR/wormhole.wasm"
20+
pyth_wasm="$ARTIFACTS_DIR/pyth.wasm"
21+
22+
for f in "$wormhole_wasm" "$pyth_wasm"; do
23+
if [[ ! -f "$f" ]]; then
24+
echo "error: $f not found. Run 'make build-contracts' first." >&2
25+
exit 1
26+
fi
27+
done
28+
29+
# Convert a binary file to a Go byte slice literal.
30+
# Outputs one long line of comma-separated hex bytes with a trailing comma.
31+
file_to_go_bytes() {
32+
xxd -i < "$1" | tr -d '\n ' | sed 's/$/,/'
33+
}
34+
35+
{
36+
cat <<'HEADER'
37+
// Code generated by script/wasm2go.sh from .cache/cosmwasm/artifacts. DO NOT EDIT.
38+
39+
// nolint revive
40+
package v2_1_0
41+
HEADER
42+
43+
printf '\nvar wormholeContract = []byte{\n\t'
44+
file_to_go_bytes "$wormhole_wasm"
45+
printf '\n}\n'
46+
47+
printf '\nvar pythContract = []byte{\n\t'
48+
file_to_go_bytes "$pyth_wasm"
49+
printf '\n}\n'
50+
} > "$OUTPUT_FILE"
51+
52+
gofmt -w "$OUTPUT_FILE"
53+
54+
echo "Generated $OUTPUT_FILE"
55+
echo " wormhole: $(wc -c < "$wormhole_wasm" | tr -d ' ') bytes"
56+
echo " pyth: $(wc -c < "$pyth_wasm" | tr -d ' ') bytes"

upgrades/software/v2.1.0/contracts.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

upgrades/software/v2.1.0/upgrade.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import (
1010
sdkmath "cosmossdk.io/math"
1111
storetypes "cosmossdk.io/store/types"
1212
upgradetypes "cosmossdk.io/x/upgrade/types"
13+
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
14+
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
1315
sdk "github.com/cosmos/cosmos-sdk/types"
1416
"github.com/cosmos/cosmos-sdk/types/module"
1517
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
1618
etypes "pkg.akt.dev/go/node/escrow/module"
1719
mvbeta "pkg.akt.dev/go/node/market/v1beta5"
20+
otypes "pkg.akt.dev/go/node/oracle/v2"
1821
"pkg.akt.dev/go/sdkutil"
1922

2023
apptypes "pkg.akt.dev/node/v2/app/types"
@@ -57,6 +60,31 @@ func (up *upgrade) UpgradeHandler() upgradetypes.UpgradeHandler {
5760
return toVM, err
5861
}
5962

63+
msgServer := wasmkeeper.NewMsgServerImpl(up.Keepers.Cosmos.Wasm)
64+
govAddr := up.Keepers.Cosmos.Wasm.GetAuthority()
65+
66+
contractAddr := "akash1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqyagled"
67+
68+
_, err = msgServer.StoreAndMigrateContract(ctx, &wasmtypes.MsgStoreAndMigrateContract{
69+
Authority: govAddr,
70+
WASMByteCode: pythContract,
71+
Contract: contractAddr,
72+
InstantiatePermission: &wasmtypes.AllowNobody,
73+
Msg: []byte("{}"),
74+
})
75+
if err != nil {
76+
return toVM, err
77+
}
78+
79+
oparams := otypes.DefaultParams()
80+
oparams.MinPriceSources = 1
81+
// Set the pyth contract as an authorized oracle price source
82+
oparams.Sources = []string{contractAddr}
83+
err = up.Keepers.Akash.Oracle.SetParams(sctx, oparams)
84+
if err != nil {
85+
return toVM, err
86+
}
87+
6088
if sctx.ChainID() == "akashnet-2" {
6189
feePool, err := up.Keepers.Cosmos.Distr.FeePool.Get(ctx)
6290
if err != nil {

0 commit comments

Comments
 (0)