Skip to content

feat: add publisher caps to the validator #412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 51 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
5cca0d9
feat(program): match version with pythnet
Reisen Jul 5, 2024
a8e1d32
feat: validator access point (wip)
Riateche Jul 5, 2024
4245d37
feat: add price account flags
Riateche Jul 5, 2024
762ad54
feat(program): fill out validator aggregation code
Reisen Jul 8, 2024
6eaf432
feat: don't aggregate on v2, clear message buffer, report validator a…
Riateche Jul 8, 2024
27212ee
feat: return price feed messages from validator aggregation
Riateche Jul 8, 2024
847b3f5
test: enable v2 aggregation
Riateche Jul 10, 2024
c9aa1e3
test: test aggregation toggle
Reisen Jul 10, 2024
f0ac267
fix: don't aggregate in validator if already aggregated in this slot
Riateche Jul 10, 2024
17a1cf6
chore: reexport solana_program and add derive debug
Riateche Jul 11, 2024
ce71807
chore: add comments
Riateche Jul 11, 2024
9ee8058
fix: don't revert if c_upd_aggregate returns false
Riateche Jul 12, 2024
9f03544
fix: make update_price_cumulative infallible; don't generate v2 messa…
Riateche Jul 12, 2024
a59effe
chore: remove publisher sorting hack
Riateche Jul 12, 2024
9523209
chore: remove AggregationOutcome
Riateche Jul 12, 2024
064a173
fix: remove debug_assert
Riateche Jul 12, 2024
70c9559
fix: allow clearing message buffers regardless of message_sent_ flag
Riateche Jul 16, 2024
5d1b09d
test: add test_upd_price_with_validator
Riateche Jul 16, 2024
3c9feb8
checkpoint
guibescos Jul 16, 2024
b13edd7
fix import
guibescos Jul 16, 2024
7d0958f
go
guibescos Jul 16, 2024
afe2eda
try this
guibescos Jul 16, 2024
fdcad70
go
guibescos Jul 16, 2024
881a2c0
try again
guibescos Jul 16, 2024
4d13547
Checkpoint
guibescos Jul 17, 2024
f6f4e3b
go
guibescos Jul 17, 2024
fa751b4
rename
guibescos Jul 17, 2024
2c78d8c
add test
guibescos Jul 17, 2024
80a3ccf
cargo lock
guibescos Jul 17, 2024
f853cd0
go
guibescos Jul 17, 2024
170afde
go
guibescos Jul 17, 2024
ef90813
format
guibescos Jul 18, 2024
6ad4893
rename
guibescos Jul 18, 2024
a0b470b
bump again
guibescos Jul 18, 2024
14c719d
add z
guibescos Jul 18, 2024
706a29c
go
guibescos Jul 18, 2024
d949c27
chore: add missing imports and fix warnings
Riateche Jul 19, 2024
fe64764
add m
guibescos Jul 19, 2024
ee9a478
Merge branch 'accumulator-v2' into feat/publisher-caps
guibescos Jul 22, 2024
91b235b
Clippy
guibescos Jul 22, 2024
9d58ce6
Merge branch 'main' into feat/publisher-caps
guibescos Jul 24, 2024
b2320d9
Update program/rust/src/accounts/price.rs
guibescos Jul 24, 2024
0783aa5
Update program/rust/src/accounts/price.rs
guibescos Jul 24, 2024
02bbe21
Remove vector
guibescos Jul 24, 2024
d7fa02d
fix: remove unwrap
guibescos Jul 25, 2024
dbbbc06
add comments
guibescos Jul 25, 2024
546efd7
fix: undo this
guibescos Jul 25, 2024
486e558
another comment
guibescos Jul 25, 2024
f6de9ed
add comment
guibescos Jul 25, 2024
632ee45
use checked indexing
guibescos Jul 31, 2024
8195a38
fix: use crates version of pythnet-sdk
guibescos Jul 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
458 changes: 284 additions & 174 deletions Cargo.lock

Large diffs are not rendered by default.

19 changes: 13 additions & 6 deletions program/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,42 @@ publish = false
bindgen = "0.60.1"

[dependencies]
solana-program = "=1.13.3"
solana-program = "=1.14.17"
bytemuck = "1.11.0"
thiserror = "1.0"
num-derive = "0.3"
num-traits = "0.2"
byteorder = "1.4.3"
serde = { version = "1.0", features = ["derive"], optional = true }
strum = { version = "0.24.1", features = ["derive"], optional = true }
pythnet-sdk = { git = "https://github.com/pyth-network/pyth-crosschain", rev="60144002053a93f424be70decd8a8ccb8d618d81"}
pythnet-sdk = { git = "https://github.com/pyth-network/pyth-crosschain", branch="feat/publisher-caps"}
solana-sdk = { version = "=1.14.17", optional = true }
bitflags = { version = "2.6.0", features = ["bytemuck"] }

[dev-dependencies]
solana-program-test = "=1.13.3"
solana-sdk = "=1.13.3"
solana-program-test = "=1.14.17"
solana-sdk = "=1.14.17"
tokio = "1.14.1"
hex = "0.3.1"
quickcheck = "1"
rand = "0.8.5"
quickcheck_macros = "1"
bincode = "1.3.3"
serde = { version = "1.0", features = ["derive"] }
pythnet-sdk = { git = "https://github.com/pyth-network/pyth-crosschain", rev="60144002053a93f424be70decd8a8ccb8d618d81", features = ["quickcheck"]}
pythnet-sdk = { git = "https://github.com/pyth-network/pyth-crosschain", branch="feat/publisher-caps", features = ["quickcheck"]}
serde_json = "1.0"
test-generator = "0.3.1"
csv = "1.1"

# Downgrade to be compatible with Rust 1.60
tracing-subscriber = "=0.3.0"
time-macros = "=0.2.3"
time = "=0.3.7"

[features]
check = [] # Skips make build in build.rs, use with cargo-clippy and cargo-check
debug = []
library = []
library = ["solana-sdk"]

[lib]
crate-type = ["cdylib", "lib"]
6 changes: 3 additions & 3 deletions program/rust/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use {
std::borrow::BorrowMut,
};


mod mapping;
mod permission;
mod price;
Expand All @@ -44,6 +43,8 @@ mod product;
#[cfg(feature = "strum")]
pub use price::MessageType;
#[cfg(test)]
pub use price::PriceCumulative;
#[cfg(test)]
pub use product::{
account_has_key_values,
create_pc_str_t,
Expand All @@ -53,14 +54,13 @@ pub use {
permission::PermissionAccount,
price::{
PriceAccount,
PriceAccountFlags,
PriceComponent,
PriceCumulative,
PriceEma,
PriceInfo,
PythOracleSerialize,
},
product::{
read_pc_str_t,
update_product_metadata,
ProductAccount,
},
Expand Down
63 changes: 47 additions & 16 deletions program/rust/src/accounts/price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use {
},
pythnet_sdk::messages::{
PriceFeedMessage,
PublisherStakeCapsMessage,
TwapMessage,
},
solana_program::pubkey::Pubkey,
Expand All @@ -25,14 +26,12 @@ mod price_pythnet {

use {
super::*,
crate::{
c_oracle_header::{
PC_MAX_SEND_LATENCY,
PC_NUM_COMP_PYTHNET,
PC_STATUS_TRADING,
},
error::OracleError,
crate::c_oracle_header::{
PC_MAX_SEND_LATENCY,
PC_NUM_COMP_PYTHNET,
PC_STATUS_TRADING,
},
bitflags::bitflags,
};

/// Pythnet-only extended price account format. This extension is
Expand Down Expand Up @@ -65,8 +64,9 @@ mod price_pythnet {
pub message_sent_: u8,
/// Configurable max latency in slots between send and receive
pub max_latency_: u8,
/// Various flags
pub flags: PriceAccountFlags,
/// Unused placeholder for alignment
pub unused_2_: i8,
pub unused_3_: i32,
/// Corresponding product account
pub product_account: Pubkey,
Expand All @@ -91,6 +91,18 @@ mod price_pythnet {
pub price_cumulative: PriceCumulative,
}

bitflags! {
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct PriceAccountFlags: u8 {
/// If set, the program doesn't do accumulation, but validator does.
const ACCUMULATOR_V2 = 0b1;
/// If unset, the program will remove old messages from its message buffer account
/// and set this flag.
const MESSAGE_BUFFER_CLEARED = 0b10;
}
}

impl PriceAccountPythnet {
pub fn as_price_feed_message(&self, key: &Pubkey) -> PriceFeedMessage {
let (price, conf, publish_time) = if self.agg_.status_ == PC_STATUS_TRADING {
Expand All @@ -100,7 +112,7 @@ mod price_pythnet {
};

PriceFeedMessage {
id: key.to_bytes(),
feed_id: key.to_bytes(),
price,
conf,
exponent: self.exponent,
Expand All @@ -111,17 +123,14 @@ mod price_pythnet {
}
}
/// This function gets triggered when there's a succesful aggregation and updates the cumulative sums
pub fn update_price_cumulative(&mut self) -> Result<(), OracleError> {
pub fn update_price_cumulative(&mut self) {
if self.agg_.status_ == PC_STATUS_TRADING {
self.price_cumulative.update(
self.agg_.price_,
self.agg_.conf_,
self.agg_.pub_slot_.saturating_sub(self.prev_slot_),
self.max_latency_,
); // pub_slot should always be >= prev_slot, but we protect ourselves against underflow just in case
Ok(())
} else {
Err(OracleError::NeedsSuccesfulAggregation)
}
}

Expand All @@ -133,7 +142,7 @@ mod price_pythnet {
};

TwapMessage {
id: key.to_bytes(),
feed_id: key.to_bytes(),
cumulative_price: self.price_cumulative.price,
cumulative_conf: self.price_cumulative.conf,
num_down_slots: self.price_cumulative.num_down_slots,
Expand Down Expand Up @@ -262,7 +271,7 @@ impl PythOracleSerialize for PriceFeedMessage {
bytes[i..i + 1].clone_from_slice(&[DISCRIMINATOR]);
i += 1;

bytes[i..i + 32].clone_from_slice(&self.id[..]);
bytes[i..i + 32].clone_from_slice(&self.feed_id[..]);
i += 32;

bytes[i..i + 8].clone_from_slice(&self.price.to_be_bytes());
Expand Down Expand Up @@ -305,7 +314,7 @@ impl PythOracleSerialize for TwapMessage {
bytes[i..i + 1].clone_from_slice(&[DISCRIMINATOR]);
i += 1;

bytes[i..i + 32].clone_from_slice(&self.id[..]);
bytes[i..i + 32].clone_from_slice(&self.feed_id[..]);
i += 32;

bytes[i..i + 16].clone_from_slice(&self.cumulative_price.to_be_bytes());
Expand All @@ -332,3 +341,25 @@ impl PythOracleSerialize for TwapMessage {
bytes.to_vec()
}
}

impl PythOracleSerialize for PublisherStakeCapsMessage {
fn to_bytes(self) -> Vec<u8> {
const DISCRIMINATOR: u8 = 2;
let mut result: Vec<u8> = Vec::new();

result.extend_from_slice(&[DISCRIMINATOR]);
result.extend_from_slice(&self.publish_time.to_be_bytes());
result.extend_from_slice(
&TryInto::<u16>::try_into(self.caps.as_ref().len())
.unwrap()
.to_be_bytes(),
);

for cap in self.caps {
result.extend_from_slice(&cap.publisher);
result.extend_from_slice(&cap.cap.to_be_bytes());
}

result
}
}
10 changes: 9 additions & 1 deletion program/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![deny(warnings)]
// #![deny(warnings)]
// Allow non upper case globals from C
#![allow(non_upper_case_globals)]

Expand All @@ -10,6 +10,12 @@ mod instruction;
mod processor;
mod utils;

#[cfg(any(test, feature = "library"))]
pub mod validator;

#[cfg(feature = "library")]
pub use solana_program;

#[cfg(test)]
mod tests;

Expand All @@ -31,11 +37,13 @@ pub use accounts::{
MappingAccount,
PermissionAccount,
PriceAccount,
PriceAccountFlags,
PriceComponent,
PriceEma,
PriceInfo,
ProductAccount,
PythAccount,
PythOracleSerialize,
};
use {
crate::error::OracleError,
Expand Down
5 changes: 5 additions & 0 deletions program/rust/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ mod upd_permissions;
mod upd_price;
mod upd_product;

#[cfg(test)]
pub use add_publisher::{
DISABLE_ACCUMULATOR_V2,
ENABLE_ACCUMULATOR_V2,
};
pub use {
add_price::add_price,
add_product::add_product,
Expand Down
26 changes: 19 additions & 7 deletions program/rust/src/processor/add_publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use {
crate::{
accounts::{
PriceAccount,
PriceAccountFlags,
PriceComponent,
PythAccount,
},
Expand Down Expand Up @@ -33,6 +34,13 @@ use {
std::mem::size_of,
};

pub const ENABLE_ACCUMULATOR_V2: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
];
pub const DISABLE_ACCUMULATOR_V2: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
];

/// Add publisher to symbol account
// account[0] funding account [signer writable]
// account[1] price account [signer writable]
Expand Down Expand Up @@ -62,14 +70,19 @@ pub fn add_publisher(
&cmd_args.header,
)?;


let mut price_data = load_checked::<PriceAccount>(price_account, cmd_args.header.version)?;

// Use the call with the default pubkey (000..) as a trigger to sort the publishers as a
// migration step from unsorted list to sorted list.
if cmd_args.publisher == Pubkey::default() {
let num_comps = try_convert::<u32, usize>(price_data.num_)?;
sort_price_comps(&mut price_data.comp_, num_comps)?;
if cmd_args.publisher == Pubkey::from(ENABLE_ACCUMULATOR_V2) {
// Hack: we use add_publisher instruction to configure the `ACCUMULATOR_V2` flag. Using a new
// instruction would be cleaner but it would require more work in the tooling.
// These special cases can be removed along with the v1 aggregation code once the transition
// is complete.
price_data.flags.insert(PriceAccountFlags::ACCUMULATOR_V2);
return Ok(());
} else if cmd_args.publisher == Pubkey::from(DISABLE_ACCUMULATOR_V2) {
price_data
.flags
.remove(PriceAccountFlags::ACCUMULATOR_V2 | PriceAccountFlags::MESSAGE_BUFFER_CLEARED);
return Ok(());
}

Expand Down Expand Up @@ -224,7 +237,6 @@ mod test {
let mut rust_std_sorted_comps = comps.get(..num_comps).unwrap().to_vec();
rust_std_sorted_comps.sort_by_key(|x| x.pub_);


assert_eq!(sort_price_comps(&mut comps, num_comps), Ok(()));
assert_eq!(comps.get(..num_comps).unwrap(), rust_std_sorted_comps);
}
Expand Down
Loading
Loading