From 226794495df5d523fd640505c208f92850fc84a8 Mon Sep 17 00:00:00 2001 From: Pavel Strakhov Date: Tue, 10 Sep 2024 10:36:35 +0100 Subject: [PATCH] doc(target_chains/solana): add more comments and configure publish --- .../publish-pyth-price-publisher.yml | 24 +++++++++++++++++++ .../src/accounts/buffer.rs | 16 +++++++++---- .../src/processor/initialize.rs | 6 +++-- .../src/processor/initialize_publisher.rs | 15 ++++++++++-- .../src/processor/submit_prices.rs | 6 ++++- 5 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/publish-pyth-price-publisher.yml diff --git a/.github/workflows/publish-pyth-price-publisher.yml b/.github/workflows/publish-pyth-price-publisher.yml new file mode 100644 index 0000000000..bf5231eff8 --- /dev/null +++ b/.github/workflows/publish-pyth-price-publisher.yml @@ -0,0 +1,24 @@ +name: Publish Pyth Price Publisher to crates.io + +on: + push: + tags: + - pyth-price-publisher-v* +jobs: + publish-pyth-price-publisher: + name: Publish Pyth Price Publisher + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal + - name: Publish + run: cargo publish --token ${CARGO_REGISTRY_TOKEN} + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + working-directory: "target_chains/solana/programs/pyth-price-publisher" diff --git a/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs b/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs index a78f47a788..41ef8cc009 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs @@ -85,10 +85,13 @@ impl BufferedPrice { } } +/// Verifies the account magic. pub fn format_matches(data: &[u8]) -> bool { super::format(data).map_or(false, |f| f == FORMAT) } +/// Verifies the account size and header. Returns the header and the currently stored prices +/// (as indicated by the `num_prices` field in the header). pub fn read(data: &[u8]) -> Result<(&BufferHeader, &[BufferedPrice]), ReadAccountError> { if data.len() < size_of::() { return Err(ReadAccountError::DataTooShort); @@ -109,10 +112,13 @@ pub fn read(data: &[u8]) -> Result<(&BufferHeader, &[BufferedPrice]), ReadAccoun Ok((header, prices)) } +/// Returns the buffer size required to hold the specified number of prices. pub fn size(max_prices: usize) -> usize { size_of::() + max_prices * size_of::() } +/// Verifies the account size and header. Returns the header and the remaining buffer space. +/// The remaining space may contain some prices, as indicated by the `num_prices` field in the header. pub fn read_mut(data: &mut [u8]) -> Result<(&mut BufferHeader, &mut [u8]), ReadAccountError> { if data.len() < size_of::() { return Err(ReadAccountError::DataTooShort); @@ -125,6 +131,7 @@ pub fn read_mut(data: &mut [u8]) -> Result<(&mut BufferHeader, &mut [u8]), ReadA Ok((header, prices)) } +/// Initializes the buffer. Returns the header and the remaining buffer space. pub fn create( data: &mut [u8], publisher: [u8; 32], @@ -141,7 +148,8 @@ pub fn create( Ok((header, prices)) } -pub fn extend( +/// Removes prices for the other slot from the buffer (if any) and adds new prices. +pub fn update( header: &mut BufferHeader, prices: &mut [u8], current_slot: u64, @@ -193,17 +201,17 @@ fn test_extend_clears_old_prices() { assert!(read(&buf).unwrap().1.is_empty()); { let (header, prices) = read_mut(&mut buf).unwrap(); - extend(header, prices, 1, &vec![1; 40]).unwrap(); + update(header, prices, 1, &vec![1; 40]).unwrap(); } assert_eq!(read(&buf).unwrap().1.len(), 2); { let (header, prices) = read_mut(&mut buf).unwrap(); - extend(header, prices, 1, &vec![1; 60]).unwrap(); + update(header, prices, 1, &vec![1; 60]).unwrap(); } assert_eq!(read(&buf).unwrap().1.len(), 5); { let (header, prices) = read_mut(&mut buf).unwrap(); - extend(header, prices, 2, &vec![1; 60]).unwrap(); + update(header, prices, 2, &vec![1; 60]).unwrap(); } assert_eq!(read(&buf).unwrap().1.len(), 3); } diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs index 4aef549086..d852fdd255 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs @@ -22,8 +22,10 @@ use { }, }; -// Creates a config account that stores the authority pubkey. -// The authority is allowed to modify publisher configs. +/// Creates a config account that stores the authority pubkey. +/// The authority is the account that will be allowed to modify publisher configs. +/// See `Instruction` for the list of required accounts. +/// The config account must be a non-existing PDA account with an expected seed. pub fn initialize( program_id: &Pubkey, accounts: &[AccountInfo], diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs index f5abefda9d..d0c3c5a624 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs @@ -29,8 +29,19 @@ use { }, }; -// Creates a publisher config account and stores the buffer account pubkey in it. -// Verifies and initializes the buffer account. +/// Creates a publisher config account and stores the buffer account pubkey in it. +/// Verifies and initializes the buffer account. +/// See `Instruction` for the list of required accounts. +/// The config account must be an initialized PDA account with an expected seed. +/// The authority account that signed the instruction +/// must match the authority key stored in the config account. +/// The publisher config account must be a non-existing PDA account with an expected seed. +/// The buffer config must be an existing, zero-filled account owned by the program. +/// Note: we aren't using a PDA for the buffer because the program can't create +/// a PDA larger than 10240 bytes in a single transaction. +/// Note: currently, the publisher config can only be set once and can only contain +/// a single buffer key. If we need to modify the buffer key or create multiple buffer keys +/// per publisher, we'll need to upgrade the program. pub fn initialize_publisher( program_id: &Pubkey, accounts: &[AccountInfo], diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs index 90a5791fac..7abc53fd9b 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs @@ -27,6 +27,10 @@ use { /// its buffer account. The buffer account will be read and applied by the validator /// to read at the end of the slot. /// If there are old prices in the account, they will be removed before adding new data. +/// See `Instruction` for the list of required accounts. +/// The publisher config account must be an initialized PDA account with an expected seed +/// (depending on the publisher account that signed the instruction). +/// The buffer account must match the buffer key stored in the publisher config account. pub fn submit_prices( program_id: &Pubkey, accounts: &[AccountInfo], @@ -54,7 +58,7 @@ pub fn submit_prices( // Access and update PublisherPrices account with new data. let mut buffer_data = buffer.data.borrow_mut(); let (header, prices) = buffer::read_mut(*buffer_data)?; - buffer::extend(header, prices, Clock::get()?.slot, prices_data)?; + buffer::update(header, prices, Clock::get()?.slot, prices_data)?; Ok(()) }