[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::{
config,
config_read,
price_info,
price_info_read,
price_feed_bucket,
price_feed_read_bucket,
ConfigInfo,
PriceInfo,
PythDataSource,
},
},
@ -44,7 +43,6 @@ use {
QueryRequest,
Response,
StdResult,
Timestamp,
WasmMsg,
WasmQuery,
},
@ -93,12 +91,11 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Respons
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
_info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
// Save general wormhole and pyth info
let state = ConfigInfo {
owner: info.sender,
wormhole_contract: deps.api.addr_validate(msg.wormhole_contract.as_ref())?,
data_sources: msg.data_sources.iter().cloned().collect(),
chain_id: msg.chain_id,
@ -385,9 +382,7 @@ fn process_batch_attestation(
for price_attestation in batch_attestation.price_attestations.iter() {
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, attestation_time)? {
if update_price_feed_if_new(deps, env, price_feed)? {
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.
fn update_price_feed_if_new(
deps: &mut DepsMut,
env: &Env,
price_feed: PriceFeed,
attestation_time: Timestamp,
_env: &Env,
new_price_feed: PriceFeed,
) -> StdResult<bool> {
let mut is_new_price = true;
price_info(deps.storage).update(
price_feed.id.as_ref(),
|maybe_price_info| -> StdResult<PriceInfo> {
match maybe_price_info {
Some(price_info) => {
price_feed_bucket(deps.storage).update(
new_price_feed.id.as_ref(),
|maybe_price_feed| -> StdResult<PriceFeed> {
match maybe_price_feed {
Some(price_feed) => {
// 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
if price_info.attestation_time < attestation_time {
Ok(PriceInfo {
arrival_time: env.block.time,
arrival_block: env.block.height,
price_feed,
attestation_time,
})
if price_feed.get_price_unchecked().publish_time
< new_price_feed.get_price_unchecked().publish_time
{
Ok(new_price_feed)
} else {
is_new_price = false;
Ok(price_info)
Ok(price_feed)
}
}
None => Ok(PriceInfo {
arrival_time: env.block.time,
arrival_block: env.block.height,
price_feed,
attestation_time,
}),
None => Ok(new_price_feed),
}
},
)?;
@ -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`.
pub fn query_price_feed(deps: &Deps, feed_id: &[u8]) -> StdResult<PriceFeedResponse> {
match price_info_read(deps.storage).load(feed_id) {
Ok(price_info) => Ok(PriceFeedResponse {
price_feed: price_info.price_feed,
}),
match price_feed_read_bucket(deps.storage).load(feed_id) {
Ok(price_feed) => Ok(PriceFeedResponse { price_feed }),
Err(_) => Err(PythContractError::PriceFeedNotFound)?,
}
}
@ -547,6 +531,7 @@ mod test {
SystemResult,
Uint128,
},
pyth_sdk::UnixTimestamp,
pyth_sdk_cw::PriceIdentifier,
pyth_wormhole_attester_sdk::PriceAttestation,
std::time::Duration,
@ -656,7 +641,6 @@ mod test {
fn create_zero_config_info() -> ConfigInfo {
ConfigInfo {
owner: Addr::unchecked(String::default()),
wormhole_contract: Addr::unchecked(String::default()),
data_sources: HashSet::default(),
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(
PriceIdentifier::new([0u8; 32]),
Price {
expo,
publish_time,
..Default::default()
},
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)
fn do_update_price_feed(
deps: &mut DepsMut,
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 do_update_price_feed(deps: &mut DepsMut, env: &Env, price_feed: PriceFeed) -> bool {
update_price_feed_if_new(deps, env, price_feed).unwrap()
}
fn apply_price_update(
@ -854,10 +828,9 @@ mod test {
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])
.unwrap()
.price_feed;
.unwrap();
let price = stored_price_feed.get_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();
let stored_price_feed = price_info_read(&deps.storage)
let stored_price_feed = price_feed_read_bucket(&deps.storage)
.load(&[0u8; 32])
.unwrap()
.price_feed;
.unwrap();
let price = stored_price_feed.get_price_unchecked();
let ema_price = stored_price_feed.get_ema_price_unchecked();
@ -1005,15 +977,14 @@ mod test {
#[test]
fn test_update_price_feed_if_new_first_price_ok() {
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);
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())
.unwrap()
.price_feed;
.unwrap();
assert_eq!(stored_price_feed, price_feed);
}
@ -1021,20 +992,18 @@ mod test {
#[test]
fn test_update_price_feed_if_new_ignore_duplicate_time() {
let (mut deps, env) = setup_test();
let time = 100;
let first_price_feed = create_price_feed(3);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed, time);
let first_price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed);
assert!(changed);
let second_price_feed = create_price_feed(4);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed, time);
let second_price_feed = create_price_feed(4, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed);
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())
.unwrap()
.price_feed;
.unwrap();
assert_eq!(stored_price_feed, first_price_feed);
}
@ -1042,18 +1011,17 @@ mod test {
fn test_update_price_feed_if_new_ignore_older() {
let (mut deps, env) = setup_test();
let first_price_feed = create_price_feed(3);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed, 100);
let first_price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed);
assert!(changed);
let second_price_feed = create_price_feed(4);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed, 90);
let second_price_feed = create_price_feed(4, 90);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed);
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())
.unwrap()
.price_feed;
.unwrap();
assert_eq!(stored_price_feed, first_price_feed);
}
@ -1061,18 +1029,17 @@ mod test {
fn test_update_price_feed_if_new_accept_newer() {
let (mut deps, env) = setup_test();
let first_price_feed = create_price_feed(3);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed, 100);
let first_price_feed = create_price_feed(3, 100);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, first_price_feed);
assert!(changed);
let second_price_feed = create_price_feed(4);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed, 110);
let second_price_feed = create_price_feed(4, 110);
let changed = do_update_price_feed(&mut deps.as_mut(), &env, second_price_feed);
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())
.unwrap()
.price_feed;
.unwrap();
assert_eq!(stored_price_feed, second_price_feed);
}
@ -1082,24 +1049,20 @@ mod test {
let address = b"123".as_ref();
let dummy_price_info = PriceInfo {
price_feed: PriceFeed::new(
PriceIdentifier::new([0u8; 32]),
Price {
price: 300,
conf: 301,
expo: 302,
publish_time: 303,
},
Price {
..Default::default()
},
),
..Default::default()
};
price_info(&mut deps.storage)
.save(address, &dummy_price_info)
let dummy_price_feed = PriceFeed::new(
PriceIdentifier::new([0u8; 32]),
Price {
price: 300,
conf: 301,
expo: 302,
publish_time: 303,
},
Price {
..Default::default()
},
);
price_feed_bucket(&mut deps.storage)
.save(address, &dummy_price_feed)
.unwrap();
let price_feed = query_price_feed(&deps.as_ref(), address)

View File

@ -4,7 +4,6 @@ use {
Binary,
Coin,
Storage,
Timestamp,
},
cosmwasm_storage::{
bucket,
@ -29,7 +28,7 @@ use {
};
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 specific blockchain (given by `chain_id`).
@ -41,7 +40,6 @@ pub struct PythDataSource {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ConfigInfo {
pub owner: Addr,
pub wormhole_contract: Addr,
pub data_sources: HashSet<PythDataSource>,
pub governance_source: PythDataSource,
@ -65,15 +63,6 @@ pub struct ConfigInfo {
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> {
singleton(storage, CONFIG_KEY)
}
@ -82,10 +71,10 @@ pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
singleton_read(storage, CONFIG_KEY)
}
pub fn price_info(storage: &mut dyn Storage) -> Bucket<PriceInfo> {
bucket(storage, PRICE_INFO_KEY)
pub fn price_feed_bucket(storage: &mut dyn Storage) -> Bucket<PriceFeed> {
bucket(storage, PRICE_FEED_KEY)
}
pub fn price_info_read(storage: &dyn Storage) -> ReadonlyBucket<PriceInfo> {
bucket_read(storage, PRICE_INFO_KEY)
pub fn price_feed_read_bucket(storage: &dyn Storage) -> ReadonlyBucket<PriceFeed> {
bucket_read(storage, PRICE_FEED_KEY)
}