Skip to content

Commit f3dce0e

Browse files
committed
near: make codehash safe for repeat upgrades, document
1 parent 177b5af commit f3dce0e

File tree

3 files changed

+40
-14
lines changed

3 files changed

+40
-14
lines changed

target_chains/near/receiver/src/governance.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,6 @@ impl Pyth {
380380
}
381381
}
382382

383-
#[private]
384-
pub fn set_upgrade_hash(&mut self, codehash: [u8; 32]) {
385-
self.codehash = codehash;
386-
}
387-
388383
#[private]
389384
#[handle_result]
390385
pub fn authorize_gov_source_transfer(&mut self, claim_vaa: Vec<u8>) -> Result<(), Error> {
@@ -469,11 +464,14 @@ impl Pyth {
469464
/// this method as a normal public method.
470465
#[handle_result]
471466
pub(crate) fn upgrade(&mut self, new_code: Vec<u8>) -> Result<Promise, Error> {
472-
let signature = env::sha256(&new_code);
467+
let signature = TryInto::<[u8; 32]>::try_into(env::sha256(&new_code)).unwrap();
468+
let default = <[u8; 32] as Default>::default();
469+
ensure!(signature != default, UnauthorizedUpgrade);
473470
ensure!(signature == self.codehash, UnauthorizedUpgrade);
474471

475472
Ok(Promise::new(env::current_account_id())
476473
.deploy_contract(new_code)
474+
.then(Self::ext(env::current_account_id()).migrate())
477475
.then(Self::ext(env::current_account_id()).refund_upgrade(
478476
env::predecessor_account_id(),
479477
env::attached_deposit(),
@@ -502,6 +500,10 @@ impl Pyth {
502500
.then_some(())
503501
.ok_or(UnknownSource(source.emitter))
504502
}
503+
504+
pub fn set_upgrade_hash(&mut self, codehash: [u8; 32]) {
505+
self.codehash = codehash;
506+
}
505507
}
506508

507509
#[cfg(test)]
@@ -541,7 +543,6 @@ mod tests {
541543

542544
let mut contract = Pyth::new(
543545
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
544-
[0; 32],
545546
Source::default(),
546547
Source::default(),
547548
0.into(),
@@ -561,7 +562,6 @@ mod tests {
561562

562563
let mut contract = Pyth::new(
563564
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
564-
[0; 32],
565565
Source::default(),
566566
Source::default(),
567567
0.into(),
@@ -580,7 +580,6 @@ mod tests {
580580

581581
let mut contract = Pyth::new(
582582
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
583-
[0; 32],
584583
Source::default(),
585584
Source::default(),
586585
0.into(),
@@ -599,7 +598,6 @@ mod tests {
599598

600599
let mut contract = Pyth::new(
601600
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
602-
[0; 32],
603601
Source::default(),
604602
Source::default(),
605603
0.into(),

target_chains/near/receiver/src/lib.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ impl Pyth {
123123
#[allow(clippy::new_without_default)]
124124
pub fn new(
125125
wormhole: AccountId,
126-
codehash: [u8; 32],
127126
initial_source: Source,
128127
gov_source: Source,
129128
update_fee: U128,
@@ -140,14 +139,44 @@ impl Pyth {
140139
gov_source,
141140
sources,
142141
wormhole,
143-
codehash,
142+
codehash: Default::default(),
144143
update_fee: update_fee.into(),
145144
}
146145
}
147146

147+
#[private]
148148
#[init(ignore_state)]
149149
pub fn migrate() -> Self {
150-
let state: Self = env::state_read().expect("Failed to read state");
150+
// This currently deserializes and produces the same state, I.E migration is a no-op to the
151+
// current state. We only update the codehash to prevent re-upgrading.
152+
//
153+
// In the case where we want to actually migrate to a new state, we can do this by defining
154+
// the old State struct here and then deserializing into that, then migrating into the new
155+
// state, example code for the future reader:
156+
//
157+
// ```rust
158+
// pub fn migrate() -> Self {
159+
// pub struct OldPyth {
160+
// sources: UnorderedSet<Source>,
161+
// gov_source: Source,
162+
// executed_governance_vaa: u64,
163+
// executed_governance_change_vaa: u64,
164+
// prices: UnorderedMap<PriceIdentifier, PriceFeed>,
165+
// wormhole: AccountId,
166+
// codehash: [u8; 32],
167+
// stale_threshold: Duration,
168+
// update_fee: u128,
169+
// }
170+
//
171+
// // Construct new Pyth State from old, perform any migrations needed.
172+
// let old: OldPyth = env::state_read().expect("Failed to read state");
173+
// Self {
174+
// ...
175+
// }
176+
// }
177+
// ```
178+
let mut state: Self = env::state_read().expect("Failed to read state");
179+
state.codehash = Default::default();
151180
state
152181
}
153182

target_chains/near/receiver/src/tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ mod tests {
1616
fn create_contract() -> Pyth {
1717
Pyth::new(
1818
"wormhole.near".parse().unwrap(),
19-
[0; 32],
2019
Source::default(),
2120
Source::default(),
2221
1.into(),

0 commit comments

Comments
 (0)