[pyth-cosmwasm] sec review update (#649)

* remove owner

* address sec review feedback

* update storage for pricefeeds

* deprecate owner

* remove owner field
This commit is contained in:
Dev Kalra 2023-03-03 20:11:15 +05:30 committed by GitHub
parent bbc140f2e4
commit 16ac58539e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 119 deletions

View File

@ -19,10 +19,9 @@ use {
state::{ state::{
config, config,
config_read, config_read,
price_info, price_feed_bucket,
price_info_read, price_feed_read_bucket,
ConfigInfo, ConfigInfo,
PriceInfo,
PythDataSource, PythDataSource,
}, },
}, },
@ -44,7 +43,6 @@ use {
QueryRequest, QueryRequest,
Response, Response,
StdResult, StdResult,
Timestamp,
WasmMsg, WasmMsg,
WasmQuery, WasmQuery,
}, },
@ -93,12 +91,11 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Respons
pub fn instantiate( pub fn instantiate(
deps: DepsMut, deps: DepsMut,
_env: Env, _env: Env,
info: MessageInfo, _info: MessageInfo,
msg: InstantiateMsg, msg: InstantiateMsg,
) -> StdResult<Response> { ) -> StdResult<Response> {
// Save general wormhole and pyth info // Save general wormhole and pyth info
let state = ConfigInfo { let state = ConfigInfo {
owner: info.sender,
wormhole_contract: deps.api.addr_validate(msg.wormhole_contract.as_ref())?, wormhole_contract: deps.api.addr_validate(msg.wormhole_contract.as_ref())?,
data_sources: msg.data_sources.iter().cloned().collect(), data_sources: msg.data_sources.iter().cloned().collect(),
chain_id: msg.chain_id, chain_id: msg.chain_id,
@ -385,9 +382,7 @@ fn process_batch_attestation(
for price_attestation in batch_attestation.price_attestations.iter() { for price_attestation in batch_attestation.price_attestations.iter() {
let price_feed = create_price_feed_from_price_attestation(price_attestation); let price_feed = create_price_feed_from_price_attestation(price_attestation);
let attestation_time = Timestamp::from_seconds(price_attestation.attestation_time as u64); if update_price_feed_if_new(deps, env, price_feed)? {
if update_price_feed_if_new(deps, env, price_feed, attestation_time)? {
new_attestations_cnt += 1; new_attestations_cnt += 1;
} }
} }
@ -440,37 +435,28 @@ fn create_price_feed_from_price_attestation(price_attestation: &PriceAttestation
/// in the bucket won't be parsed. /// in the bucket won't be parsed.
fn update_price_feed_if_new( fn update_price_feed_if_new(
deps: &mut DepsMut, deps: &mut DepsMut,
env: &Env, _env: &Env,
price_feed: PriceFeed, new_price_feed: PriceFeed,
attestation_time: Timestamp,
) -> StdResult<bool> { ) -> StdResult<bool> {
let mut is_new_price = true; let mut is_new_price = true;
price_info(deps.storage).update( price_feed_bucket(deps.storage).update(
price_feed.id.as_ref(), new_price_feed.id.as_ref(),
|maybe_price_info| -> StdResult<PriceInfo> { |maybe_price_feed| -> StdResult<PriceFeed> {
match maybe_price_info { match maybe_price_feed {
Some(price_info) => { Some(price_feed) => {
// This check ensures that a price won't be updated with the same or older // This check ensures that a price won't be updated with the same or older
// message. Attestation_time is guaranteed increasing in // message. Publish_TIme is guaranteed increasing in
// solana // solana
if price_info.attestation_time < attestation_time { if price_feed.get_price_unchecked().publish_time
Ok(PriceInfo { < new_price_feed.get_price_unchecked().publish_time
arrival_time: env.block.time, {
arrival_block: env.block.height, Ok(new_price_feed)
price_feed,
attestation_time,
})
} else { } else {
is_new_price = false; is_new_price = false;
Ok(price_info) Ok(price_feed)
} }
} }
None => Ok(PriceInfo { None => Ok(new_price_feed),
arrival_time: env.block.time,
arrival_block: env.block.height,
price_feed,
attestation_time,
}),
} }
}, },
)?; )?;
@ -488,10 +474,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
/// Get the most recent value of the price feed indicated by `feed_id`. /// Get the most recent value of the price feed indicated by `feed_id`.
pub fn query_price_feed(deps: &Deps, feed_id: &[u8]) -> StdResult<PriceFeedResponse> { pub fn query_price_feed(deps: &Deps, feed_id: &[u8]) -> StdResult<PriceFeedResponse> {
match price_info_read(deps.storage).load(feed_id) { match price_feed_read_bucket(deps.storage).load(feed_id) {
Ok(price_info) => Ok(PriceFeedResponse { Ok(price_feed) => Ok(PriceFeedResponse { price_feed }),
price_feed: price_info.price_feed,
}),
Err(_) => Err(PythContractError::PriceFeedNotFound)?, Err(_) => Err(PythContractError::PriceFeedNotFound)?,
} }
} }
@ -547,6 +531,7 @@ mod test {
SystemResult, SystemResult,
Uint128, Uint128,
}, },
pyth_sdk::UnixTimestamp,
pyth_sdk_cw::PriceIdentifier, pyth_sdk_cw::PriceIdentifier,
pyth_wormhole_attester_sdk::PriceAttestation, pyth_wormhole_attester_sdk::PriceAttestation,
std::time::Duration, std::time::Duration,
@ -656,7 +641,6 @@ mod test {
fn create_zero_config_info() -> ConfigInfo { fn create_zero_config_info() -> ConfigInfo {
ConfigInfo { ConfigInfo {
owner: Addr::unchecked(String::default()),
wormhole_contract: Addr::unchecked(String::default()), wormhole_contract: Addr::unchecked(String::default()),
data_sources: HashSet::default(), data_sources: HashSet::default(),
governance_source: PythDataSource { governance_source: PythDataSource {
@ -671,11 +655,12 @@ mod test {
} }
} }
fn create_price_feed(expo: i32) -> PriceFeed { fn create_price_feed(expo: i32, publish_time: UnixTimestamp) -> PriceFeed {
PriceFeed::new( PriceFeed::new(
PriceIdentifier::new([0u8; 32]), PriceIdentifier::new([0u8; 32]),
Price { Price {
expo, expo,
publish_time,
..Default::default() ..Default::default()
}, },
Price { Price {
@ -695,21 +680,10 @@ mod test {
}]) }])
} }
/// Updates the price feed with the given attestation time stamp and /// Updates the price feed with the given publish time stamp and
/// returns the update status (true means updated, false means ignored) /// returns the update status (true means updated, false means ignored)
fn do_update_price_feed( fn do_update_price_feed(deps: &mut DepsMut, env: &Env, price_feed: PriceFeed) -> bool {
deps: &mut DepsMut, update_price_feed_if_new(deps, env, price_feed).unwrap()
env: &Env,
price_feed: PriceFeed,
attestation_time_seconds: u64,
) -> bool {
update_price_feed_if_new(
deps,
env,
price_feed,
Timestamp::from_seconds(attestation_time_seconds),
)
.unwrap()
} }
fn apply_price_update( fn apply_price_update(
@ -854,10 +828,9 @@ mod test {
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap(); process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
let stored_price_feed = price_info_read(&deps.storage) let stored_price_feed = price_feed_read_bucket(&deps.storage)
.load(&[0u8; 32]) .load(&[0u8; 32])
.unwrap() .unwrap();
.price_feed;
let price = stored_price_feed.get_price_unchecked(); let price = stored_price_feed.get_price_unchecked();
let ema_price = stored_price_feed.get_ema_price_unchecked(); let ema_price = stored_price_feed.get_ema_price_unchecked();
@ -907,10 +880,9 @@ mod test {
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap(); process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
let stored_price_feed = price_info_read(&deps.storage) let stored_price_feed = price_feed_read_bucket(&deps.storage)
.load(&[0u8; 32]) .load(&[0u8; 32])
.unwrap() .unwrap();
.price_feed;
let price = stored_price_feed.get_price_unchecked(); let price = stored_price_feed.get_price_unchecked();
let ema_price = stored_price_feed.get_ema_price_unchecked(); let ema_price = stored_price_feed.get_ema_price_unchecked();
@ -1005,15 +977,14 @@ mod test {
#[test] #[test]
fn test_update_price_feed_if_new_first_price_ok() { fn test_update_price_feed_if_new_first_price_ok() {
let (mut deps, env) = setup_test(); let (mut deps, env) = setup_test();
let price_feed = create_price_feed(3); let price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, price_feed, 100); let changed = do_update_price_feed(&mut deps.as_mut(), &env, price_feed);
assert!(changed); assert!(changed);
let stored_price_feed = price_info(&mut deps.storage) let stored_price_feed = price_feed_bucket(&mut deps.storage)
.load(price_feed.id.as_ref()) .load(price_feed.id.as_ref())
.unwrap() .unwrap();
.price_feed;
assert_eq!(stored_price_feed, price_feed); assert_eq!(stored_price_feed, price_feed);
} }
@ -1021,20 +992,18 @@ mod test {
#[test] #[test]
fn test_update_price_feed_if_new_ignore_duplicate_time() { fn test_update_price_feed_if_new_ignore_duplicate_time() {
let (mut deps, env) = setup_test(); let (mut deps, env) = setup_test();
let time = 100;
let first_price_feed = create_price_feed(3); let first_price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed, time); let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed);
assert!(changed); assert!(changed);
let second_price_feed = create_price_feed(4); let second_price_feed = create_price_feed(4, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed, time); let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed);
assert!(!changed); assert!(!changed);
let stored_price_feed = price_info(&mut deps.storage) let stored_price_feed = price_feed_bucket(&mut deps.storage)
.load(first_price_feed.id.as_ref()) .load(first_price_feed.id.as_ref())
.unwrap() .unwrap();
.price_feed;
assert_eq!(stored_price_feed, first_price_feed); assert_eq!(stored_price_feed, first_price_feed);
} }
@ -1042,18 +1011,17 @@ mod test {
fn test_update_price_feed_if_new_ignore_older() { fn test_update_price_feed_if_new_ignore_older() {
let (mut deps, env) = setup_test(); let (mut deps, env) = setup_test();
let first_price_feed = create_price_feed(3); let first_price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed, 100); let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed);
assert!(changed); assert!(changed);
let second_price_feed = create_price_feed(4); let second_price_feed = create_price_feed(4, 90);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed, 90); let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed);
assert!(!changed); assert!(!changed);
let stored_price_feed = price_info(&mut deps.storage) let stored_price_feed = price_feed_bucket(&mut deps.storage)
.load(first_price_feed.id.as_ref()) .load(first_price_feed.id.as_ref())
.unwrap() .unwrap();
.price_feed;
assert_eq!(stored_price_feed, first_price_feed); assert_eq!(stored_price_feed, first_price_feed);
} }
@ -1061,18 +1029,17 @@ mod test {
fn test_update_price_feed_if_new_accept_newer() { fn test_update_price_feed_if_new_accept_newer() {
let (mut deps, env) = setup_test(); let (mut deps, env) = setup_test();
let first_price_feed = create_price_feed(3); let first_price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed, 100); let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed);
assert!(changed); assert!(changed);
let second_price_feed = create_price_feed(4); let second_price_feed = create_price_feed(4, 110);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed, 110); let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed);
assert!(changed); assert!(changed);
let stored_price_feed = price_info(&mut deps.storage) let stored_price_feed = price_feed_bucket(&mut deps.storage)
.load(first_price_feed.id.as_ref()) .load(first_price_feed.id.as_ref())
.unwrap() .unwrap();
.price_feed;
assert_eq!(stored_price_feed, second_price_feed); assert_eq!(stored_price_feed, second_price_feed);
} }
@ -1082,24 +1049,20 @@ mod test {
let address = b"123".as_ref(); let address = b"123".as_ref();
let dummy_price_info = PriceInfo { let dummy_price_feed = PriceFeed::new(
price_feed: PriceFeed::new( PriceIdentifier::new([0u8; 32]),
PriceIdentifier::new([0u8; 32]), Price {
Price { price: 300,
price: 300, conf: 301,
conf: 301, expo: 302,
expo: 302, publish_time: 303,
publish_time: 303, },
}, Price {
Price { ..Default::default()
..Default::default() },
}, );
), price_feed_bucket(&mut deps.storage)
..Default::default() .save(address, &dummy_price_feed)
};
price_info(&mut deps.storage)
.save(address, &dummy_price_info)
.unwrap(); .unwrap();
let price_feed = query_price_feed(&deps.as_ref(), address) let price_feed = query_price_feed(&deps.as_ref(), address)

View File

@ -4,7 +4,6 @@ use {
Binary, Binary,
Coin, Coin,
Storage, Storage,
Timestamp,
}, },
cosmwasm_storage::{ cosmwasm_storage::{
bucket, bucket,
@ -29,7 +28,7 @@ use {
}; };
pub static CONFIG_KEY: &[u8] = b"config"; pub static CONFIG_KEY: &[u8] = b"config";
pub static PRICE_INFO_KEY: &[u8] = b"price_info_v4"; pub static PRICE_FEED_KEY: &[u8] = b"price_feed";
/// A `PythDataSource` identifies a specific contract (given by its Wormhole `emitter`) on /// A `PythDataSource` identifies a specific contract (given by its Wormhole `emitter`) on
/// a specific blockchain (given by `chain_id`). /// a specific blockchain (given by `chain_id`).
@ -41,7 +40,6 @@ pub struct PythDataSource {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ConfigInfo { pub struct ConfigInfo {
pub owner: Addr,
pub wormhole_contract: Addr, pub wormhole_contract: Addr,
pub data_sources: HashSet<PythDataSource>, pub data_sources: HashSet<PythDataSource>,
pub governance_source: PythDataSource, pub governance_source: PythDataSource,
@ -65,15 +63,6 @@ pub struct ConfigInfo {
pub fee: Coin, pub fee: Coin,
} }
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct PriceInfo {
pub arrival_time: Timestamp,
pub arrival_block: u64,
pub attestation_time: Timestamp,
pub price_feed: PriceFeed,
}
pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> { pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> {
singleton(storage, CONFIG_KEY) singleton(storage, CONFIG_KEY)
} }
@ -82,10 +71,10 @@ pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
singleton_read(storage, CONFIG_KEY) singleton_read(storage, CONFIG_KEY)
} }
pub fn price_info(storage: &mut dyn Storage) -> Bucket<PriceInfo> { pub fn price_feed_bucket(storage: &mut dyn Storage) -> Bucket<PriceFeed> {
bucket(storage, PRICE_INFO_KEY) bucket(storage, PRICE_FEED_KEY)
} }
pub fn price_info_read(storage: &dyn Storage) -> ReadonlyBucket<PriceInfo> { pub fn price_feed_read_bucket(storage: &dyn Storage) -> ReadonlyBucket<PriceFeed> {
bucket_read(storage, PRICE_INFO_KEY) bucket_read(storage, PRICE_FEED_KEY)
} }