near: make codehash safe for repeat upgrades, document

This commit is contained in:
Reisen 2023-04-03 15:42:41 +02:00 committed by Reisen
parent 177b5af94b
commit f3dce0e316
3 changed files with 40 additions and 14 deletions

View File

@ -380,11 +380,6 @@ impl Pyth {
}
}
#[private]
pub fn set_upgrade_hash(&mut self, codehash: [u8; 32]) {
self.codehash = codehash;
}
#[private]
#[handle_result]
pub fn authorize_gov_source_transfer(&mut self, claim_vaa: Vec<u8>) -> Result<(), Error> {
@ -469,11 +464,14 @@ impl Pyth {
/// this method as a normal public method.
#[handle_result]
pub(crate) fn upgrade(&mut self, new_code: Vec<u8>) -> Result<Promise, Error> {
let signature = env::sha256(&new_code);
let signature = TryInto::<[u8; 32]>::try_into(env::sha256(&new_code)).unwrap();
let default = <[u8; 32] as Default>::default();
ensure!(signature != default, UnauthorizedUpgrade);
ensure!(signature == self.codehash, UnauthorizedUpgrade);
Ok(Promise::new(env::current_account_id())
.deploy_contract(new_code)
.then(Self::ext(env::current_account_id()).migrate())
.then(Self::ext(env::current_account_id()).refund_upgrade(
env::predecessor_account_id(),
env::attached_deposit(),
@ -502,6 +500,10 @@ impl Pyth {
.then_some(())
.ok_or(UnknownSource(source.emitter))
}
pub fn set_upgrade_hash(&mut self, codehash: [u8; 32]) {
self.codehash = codehash;
}
}
#[cfg(test)]
@ -541,7 +543,6 @@ mod tests {
let mut contract = Pyth::new(
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
[0; 32],
Source::default(),
Source::default(),
0.into(),
@ -561,7 +562,6 @@ mod tests {
let mut contract = Pyth::new(
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
[0; 32],
Source::default(),
Source::default(),
0.into(),
@ -580,7 +580,6 @@ mod tests {
let mut contract = Pyth::new(
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
[0; 32],
Source::default(),
Source::default(),
0.into(),
@ -599,7 +598,6 @@ mod tests {
let mut contract = Pyth::new(
near_sdk::AccountId::new_unchecked("pyth.near".to_owned()),
[0; 32],
Source::default(),
Source::default(),
0.into(),

View File

@ -123,7 +123,6 @@ impl Pyth {
#[allow(clippy::new_without_default)]
pub fn new(
wormhole: AccountId,
codehash: [u8; 32],
initial_source: Source,
gov_source: Source,
update_fee: U128,
@ -140,14 +139,44 @@ impl Pyth {
gov_source,
sources,
wormhole,
codehash,
codehash: Default::default(),
update_fee: update_fee.into(),
}
}
#[private]
#[init(ignore_state)]
pub fn migrate() -> Self {
let state: Self = env::state_read().expect("Failed to read state");
// This currently deserializes and produces the same state, I.E migration is a no-op to the
// current state. We only update the codehash to prevent re-upgrading.
//
// In the case where we want to actually migrate to a new state, we can do this by defining
// the old State struct here and then deserializing into that, then migrating into the new
// state, example code for the future reader:
//
// ```rust
// pub fn migrate() -> Self {
// pub struct OldPyth {
// sources: UnorderedSet<Source>,
// gov_source: Source,
// executed_governance_vaa: u64,
// executed_governance_change_vaa: u64,
// prices: UnorderedMap<PriceIdentifier, PriceFeed>,
// wormhole: AccountId,
// codehash: [u8; 32],
// stale_threshold: Duration,
// update_fee: u128,
// }
//
// // Construct new Pyth State from old, perform any migrations needed.
// let old: OldPyth = env::state_read().expect("Failed to read state");
// Self {
// ...
// }
// }
// ```
let mut state: Self = env::state_read().expect("Failed to read state");
state.codehash = Default::default();
state
}

View File

@ -16,7 +16,6 @@ mod tests {
fn create_contract() -> Pyth {
Pyth::new(
"wormhole.near".parse().unwrap(),
[0; 32],
Source::default(),
Source::default(),
1.into(),