Преглед изворни кода

feat(target-chains/solana): Implement governance for the solana pull oracle (#1175)

* Do it

* Go

* Address some feedback

* Update size of config
guibescos пре 1 година
родитељ
комит
9875bc2ba4

+ 2 - 2
target_chains/solana/Cargo.lock

@@ -6219,7 +6219,7 @@ dependencies = [
 [[package]]
 name = "wormhole-core"
 version = "0.1.0"
-source = "git+https://github.com/guibescos/wormhole?branch=reisen/sdk-solana#61bb2fb691a8df0aa0e42a21632e43b392ffa90f"
+source = "git+https://github.com/guibescos/wormhole?branch=reisen/sdk-solana#b0da20525fe68408ed2c8b331eb5f63101381936"
 dependencies = [
  "borsh 0.9.3",
  "bstr 0.2.17",
@@ -6249,7 +6249,7 @@ dependencies = [
 [[package]]
 name = "wormhole-solana"
 version = "0.1.0"
-source = "git+https://github.com/guibescos/wormhole?branch=reisen/sdk-solana#61bb2fb691a8df0aa0e42a21632e43b392ffa90f"
+source = "git+https://github.com/guibescos/wormhole?branch=reisen/sdk-solana#b0da20525fe68408ed2c8b331eb5f63101381936"
 dependencies = [
  "borsh 0.9.3",
  "bstr 0.2.17",

+ 1 - 1
target_chains/solana/programs/pyth-solana-receiver/Cargo.toml

@@ -13,7 +13,7 @@ no-entrypoint = []
 no-idl = []
 no-log-ix-name = []
 cpi = ["no-entrypoint"]
-default = []
+default = ["mainnet"]
 mainnet = ["wormhole-anchor-sdk/mainnet"]
 devnet = ["wormhole-anchor-sdk/solana-devnet"]
 test-bpf = []

+ 6 - 0
target_chains/solana/programs/pyth-solana-receiver/src/error.rs

@@ -22,4 +22,10 @@ pub enum ReceiverError {
     InvalidPriceUpdate,
     #[msg("Received an invalid accumulator message type")]
     InvalidAccumulatorMessageType,
+    #[msg("The signer is not authorized to perform this governance action")]
+    GovernanceAuthorityMismatch,
+    #[msg("The signer is not authorized to accept the governance authority")]
+    TargetGovernanceAuthorityMismatch,
+    #[msg("The governance authority needs to request a transfer first")]
+    NonexistentGovernanceAuthorityTransferRequest,
 }

+ 90 - 1
target_chains/solana/programs/pyth-solana-receiver/src/lib.rs

@@ -1,3 +1,5 @@
+use state::config::Config;
+
 pub mod error;
 pub mod state;
 
@@ -18,7 +20,10 @@ use {
         },
         ACCUMULATOR_EMITTER_ADDRESS,
     },
-    state::AnchorVaa,
+    state::{
+        anchor_vaa::AnchorVaa,
+        config::DataSource,
+    },
 };
 
 declare_id!("DvPfMBZJJwKgJsv2WJA8bFwUMn8nFd5Xpioc6foC3rse");
@@ -27,6 +32,53 @@ declare_id!("DvPfMBZJJwKgJsv2WJA8bFwUMn8nFd5Xpioc6foC3rse");
 pub mod pyth_solana_receiver {
     use super::*;
 
+    pub fn initialize(ctx: Context<Initialize>, initial_config: Config) -> Result<()> {
+        let config = &mut ctx.accounts.config;
+        **config = initial_config;
+        Ok(())
+    }
+
+    pub fn request_governance_authority_transfer(
+        ctx: Context<Governance>,
+        target_governance_authority: Pubkey,
+    ) -> Result<()> {
+        let config = &mut ctx.accounts.config;
+        config.target_governance_authority = Some(target_governance_authority);
+        Ok(())
+    }
+
+    pub fn authorize_governance_authority_transfer(
+        ctx: Context<AuthorizeGovernanceAuthorityTransfer>,
+    ) -> Result<()> {
+        let config = &mut ctx.accounts.config;
+        config.governance_authority = config.target_governance_authority.ok_or(error!(
+            ReceiverError::NonexistentGovernanceAuthorityTransferRequest
+        ))?;
+        config.target_governance_authority = None;
+        Ok(())
+    }
+
+    pub fn set_data_sources(
+        ctx: Context<Governance>,
+        valid_data_sources: Vec<DataSource>,
+    ) -> Result<()> {
+        let config = &mut ctx.accounts.config;
+        config.valid_data_sources = valid_data_sources;
+        Ok(())
+    }
+
+    pub fn set_fee(ctx: Context<Governance>, single_update_fee_in_lamports: u64) -> Result<()> {
+        let config = &mut ctx.accounts.config;
+        config.single_update_fee_in_lamports = single_update_fee_in_lamports;
+        Ok(())
+    }
+
+    pub fn set_wormhole_address(ctx: Context<Governance>, wormhole: Pubkey) -> Result<()> {
+        let config = &mut ctx.accounts.config;
+        config.wormhole = wormhole;
+        Ok(())
+    }
+
     /// Verify the updates using the posted_vaa account. This should be called after the client
     /// has already called verify_signatures & post_vaa. Wormhole's verify_signatures & post_vaa
     /// will perform the necessary checks so that we can assume that the posted_vaa account is
@@ -103,6 +155,42 @@ pub mod pyth_solana_receiver {
     }
 }
 
+
+pub const CONFIG_SEED: &str = "config";
+
+#[derive(Accounts)]
+#[instruction(initial_config : Config)]
+pub struct Initialize<'info> {
+    #[account(mut)]
+    pub payer:          Signer<'info>,
+    #[account(init, space = Config::LEN, payer=payer, seeds = [CONFIG_SEED.as_ref()], bump)]
+    pub config:         Account<'info, Config>,
+    pub system_program: Program<'info, System>,
+}
+
+#[derive(Accounts)]
+pub struct Governance<'info> {
+    #[account(constraint =
+        payer.key() == config.governance_authority @
+        ReceiverError::GovernanceAuthorityMismatch
+    )]
+    pub payer:  Signer<'info>,
+    #[account(seeds = [CONFIG_SEED.as_ref()], bump)]
+    pub config: Account<'info, Config>,
+}
+
+#[derive(Accounts)]
+pub struct AuthorizeGovernanceAuthorityTransfer<'info> {
+    #[account(constraint =
+        payer.key() == config.target_governance_authority.ok_or(error!(ReceiverError::NonexistentGovernanceAuthorityTransferRequest))? @
+        ReceiverError::TargetGovernanceAuthorityMismatch
+    )]
+    pub payer:  Signer<'info>,
+    #[account(seeds = [CONFIG_SEED.as_ref()], bump)]
+    pub config: Account<'info, Config>,
+}
+
+
 #[derive(Accounts)]
 pub struct PostUpdates<'info> {
     #[account(mut)]
@@ -119,6 +207,7 @@ pub struct PostUpdates<'info> {
     pub posted_vaa: UncheckedAccount<'info>,
 }
 
+
 impl crate::accounts::PostUpdates {
     pub fn populate(payer: &Pubkey, posted_vaa: &Pubkey) -> Self {
         crate::accounts::PostUpdates {

+ 0 - 0
target_chains/solana/programs/pyth-solana-receiver/src/state.rs → target_chains/solana/programs/pyth-solana-receiver/src/state/anchor_vaa.rs


+ 64 - 0
target_chains/solana/programs/pyth-solana-receiver/src/state/config.rs

@@ -0,0 +1,64 @@
+use {
+    anchor_lang::prelude::*,
+    solana_program::pubkey::Pubkey,
+};
+
+#[account]
+pub struct Config {
+    pub governance_authority:          Pubkey,
+    pub target_governance_authority:   Option<Pubkey>,
+    pub wormhole:                      Pubkey,
+    pub valid_data_sources:            Vec<DataSource>,
+    pub single_update_fee_in_lamports: u64,
+}
+
+#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
+pub struct DataSource {
+    pub chain:   u16,
+    pub emitter: Pubkey,
+}
+
+impl Config {
+    pub const LEN: usize = 370; // This is two times the current size of a Config account with 2 data sources, to leave space for more fields
+}
+
+#[cfg(test)]
+pub mod tests {
+    use {
+        super::DataSource,
+        crate::state::config::Config,
+        anchor_lang::{
+            AnchorSerialize,
+            Discriminator,
+        },
+        solana_program::pubkey::Pubkey,
+    };
+
+    #[test]
+    fn check_size() {
+        let test_config = Config {
+            governance_authority:          Pubkey::new_unique(),
+            target_governance_authority:   Some(Pubkey::new_unique()),
+            wormhole:                      Pubkey::new_unique(),
+            valid_data_sources:            vec![
+                DataSource {
+                    chain:   1,
+                    emitter: Pubkey::new_unique(),
+                },
+                DataSource {
+                    chain:   2,
+                    emitter: Pubkey::new_unique(),
+                },
+            ],
+            single_update_fee_in_lamports: 0,
+        };
+
+        assert_eq!(
+            test_config.try_to_vec().unwrap().len(),
+            32 + 1 + 32 + 32 + 4 + 1 + 33 + 1 + 33 + 8
+        );
+        assert!(
+            Config::discriminator().len() + test_config.try_to_vec().unwrap().len() <= Config::LEN
+        );
+    }
+}

+ 2 - 0
target_chains/solana/programs/pyth-solana-receiver/src/state/mod.rs

@@ -0,0 +1,2 @@
+pub mod anchor_vaa;
+pub mod config;