瀏覽代碼

initial commit for governance instructions

Jayant Krishnamurthy 2 年之前
父節點
當前提交
1d2dcfe3d7

+ 79 - 2
cosmwasm/contracts/pyth/src/contract.rs

@@ -2,7 +2,14 @@ use {
     crate::{
         error::PythContractError,
         governance::{
-            GovernanceAction::SetFee,
+            GovernanceAction::{
+                AuthorizeGovernanceDataSourceTransfer,
+                RequestGovernanceDataSourceTransfer,
+                SetDataSources,
+                SetFee,
+                SetValidPeriod,
+                UpgradeContract,
+            },
             GovernanceInstruction,
         },
         msg::{
@@ -52,6 +59,7 @@ use {
     std::{
         collections::HashSet,
         convert::TryFrom,
+        iter::FromIterator,
         time::Duration,
     },
     wormhole::{
@@ -85,6 +93,7 @@ pub fn instantiate(
             emitter:            msg.governance_emitter,
             pyth_emitter_chain: msg.governance_emitter_chain,
         },
+        governance_source_index:    msg.governance_source_index,
         governance_sequence_number: msg.governance_sequence_number,
         valid_time_period:          Duration::from_secs(msg.valid_time_period_secs as u64),
         fee:                        msg.fee,
@@ -173,6 +182,62 @@ fn execute_governance_instruction(
     }
 
     let response = match instruction.action {
+        UpgradeContract { .. } => {
+            // FIXME: implement this
+            Err(PythContractError::InvalidGovernancePayload)?
+        }
+        AuthorizeGovernanceDataSourceTransfer { claim_vaa } => {
+            let parsed_claim_vaa = parse_vaa(deps.branch(), env.block.time.seconds(), &claim_vaa)?;
+            let claim_vaa_instruction = GovernanceInstruction::deserialize(vaa.payload.as_slice())
+                .map_err(|_| PythContractError::InvalidGovernancePayload)?;
+
+            if claim_vaa_instruction.target_chain_id != state.chain_id
+                && claim_vaa_instruction.target_chain_id != 0
+            {
+                Err(PythContractError::InvalidGovernancePayload)?
+            }
+
+            match claim_vaa_instruction.action {
+                RequestGovernanceDataSourceTransfer {
+                    governance_data_source_index,
+                } => {
+                    if state.governance_source_index >= governance_data_source_index {
+                        Err(PythContractError::OldGovernanceMessage)?
+                    }
+
+                    updated_config.governance_source_index = governance_data_source_index;
+                    let new_governance_source = PythDataSource {
+                        emitter:            Binary::from(parsed_claim_vaa.emitter_address.clone()),
+                        pyth_emitter_chain: parsed_claim_vaa.emitter_chain,
+                    };
+                    updated_config.governance_source = new_governance_source;
+                    updated_config.governance_sequence_number = parsed_claim_vaa.sequence;
+
+                    Response::new()
+                        .add_attribute("action", "authorize_governance_data_source_transfer")
+                        .add_attribute(
+                            "new_governance_emitter_address",
+                            format!("{:?}", parsed_claim_vaa.emitter_address),
+                        )
+                        .add_attribute(
+                            "new_governance_emitter_chain",
+                            format!("{}", parsed_claim_vaa.emitter_chain),
+                        )
+                        .add_attribute(
+                            "new_governance_sequence_number",
+                            format!("{}", parsed_claim_vaa.sequence),
+                        )
+                }
+                _ => Err(PythContractError::InvalidGovernancePayload)?,
+            }
+        }
+        SetDataSources { data_sources } => {
+            updated_config.data_sources = HashSet::from_iter(data_sources.iter().cloned());
+
+            Response::new()
+                .add_attribute("action", "set_data_sources")
+                .add_attribute("new_data_sources", format!("{data_sources:?}"))
+        }
         SetFee { val, expo } => {
             updated_config.fee = Uint128::new(
                 (val as u128)
@@ -191,7 +256,18 @@ fn execute_governance_instruction(
                 .add_attribute("action", "set_fee")
                 .add_attribute("new_fee", format!("{}", updated_config.fee))
         }
-        _ => Err(PythContractError::InvalidGovernancePayload)?,
+        SetValidPeriod { valid_seconds } => {
+            updated_config.valid_time_period = Duration::from_secs(valid_seconds);
+
+            Response::new()
+                .add_attribute("action", "set_valid_period")
+                .add_attribute("new_valid_seconds", format!("{valid_seconds}"))
+        }
+        RequestGovernanceDataSourceTransfer { .. } => {
+            // RequestGovernanceDataSourceTransfer can only be part of the
+            // AuthorizeGovernanceDataSourceTransfer message.
+            Err(PythContractError::InvalidGovernancePayload)?
+        }
     };
 
     config(deps.storage).save(&updated_config)?;
@@ -565,6 +641,7 @@ mod test {
                 emitter:            Binary(vec![]),
                 pyth_emitter_chain: 0,
             },
+            governance_source_index:    0,
             governance_sequence_number: 0,
             chain_id:                   0,
             valid_time_period:          Duration::new(0, 0),

+ 69 - 11
cosmwasm/contracts/pyth/src/governance.rs

@@ -1,16 +1,27 @@
 use {
+    crate::{
+        governance::GovernanceAction::{
+            RequestGovernanceDataSourceTransfer,
+            SetValidPeriod,
+        },
+        state::PythDataSource,
+    },
     byteorder::{
         BigEndian,
         ReadBytesExt,
         WriteBytesExt,
     },
+    cosmwasm_std::Binary,
     p2w_sdk::ErrBox,
     schemars::JsonSchema,
     serde::{
         Deserialize,
         Serialize,
     },
-    std::io::Write,
+    std::{
+        convert::TryFrom,
+        io::Write,
+    },
 };
 
 const PYTH_GOVERNANCE_MAGIC: &[u8] = b"PTGM";
@@ -46,14 +57,14 @@ impl GovernanceModule {
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
 #[repr(u8)]
 pub enum GovernanceAction {
-    UpgradeContract,                       // 0
-    AuthorizeGovernanceDataSourceTransfer, // 1
-    SetDataSources,                        // 2
+    UpgradeContract { address: [u8; 20] }, // 0
+    AuthorizeGovernanceDataSourceTransfer { claim_vaa: Binary }, // 1
+    SetDataSources { data_sources: Vec<PythDataSource> }, // 2
     // Set the fee to val * (10 ** expo)
     SetFee { val: u64, expo: u64 }, // 3
     // Set the default valid period to the provided number of seconds
     SetValidPeriod { valid_seconds: u64 }, // 4
-    RequestGovernanceDataSourceTransfer,   // 5
+    RequestGovernanceDataSourceTransfer { governance_data_source_index: u32 }, // 5
 }
 
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
@@ -86,13 +97,50 @@ impl GovernanceInstruction {
         let target_chain_id: u16 = bytes.read_u16::<BigEndian>()?;
 
         let action: Result<GovernanceAction, String> = match action_type {
+            0 => {
+                let mut address: [u8; 20] = [0; 20];
+                bytes.read_exact(&mut address)?;
+                Ok(GovernanceAction::UpgradeContract { address })
+            }
+            1 => {
+                let mut payload: Vec<u8> = vec![];
+                bytes.read_to_end(&mut payload)?;
+                Ok(GovernanceAction::AuthorizeGovernanceDataSourceTransfer {
+                    claim_vaa: Binary::from(payload),
+                })
+            }
+            2 => {
+                let num_data_sources = bytes.read_u8()?;
+                let mut data_sources: Vec<PythDataSource> = vec![];
+                for _ in 0..num_data_sources {
+                    let chain_id = bytes.read_u16::<BigEndian>()?;
+                    let mut emitter_address: [u8; 32] = [0; 32];
+                    bytes.read_exact(&mut emitter_address)?;
+
+                    data_sources.push(PythDataSource {
+                        emitter:            Binary::from(&emitter_address),
+                        pyth_emitter_chain: chain_id,
+                    });
+                }
+
+                Ok(GovernanceAction::SetDataSources { data_sources })
+            }
             3 => {
                 let val = bytes.read_u64::<BigEndian>()?;
                 let expo = bytes.read_u64::<BigEndian>()?;
                 Ok(GovernanceAction::SetFee { val, expo })
             }
-            // TODO: add parsing for additional actions
-            _ => Err(format!("Bad governance action {action_type}",)),
+            4 => {
+                let valid_seconds = bytes.read_u64::<BigEndian>()?;
+                Ok(SetValidPeriod { valid_seconds })
+            }
+            5 => {
+                let governance_data_source_index = bytes.read_u32::<BigEndian>()?;
+                Ok(RequestGovernanceDataSourceTransfer {
+                    governance_data_source_index,
+                })
+            }
+            _ => Err(format!("Unknown governance action type: {action_type}",)),
         };
 
         Ok(GovernanceInstruction {
@@ -109,17 +157,24 @@ impl GovernanceInstruction {
         buf.write_u8(self.module.to_u8())?;
 
         match &self.action {
-            GovernanceAction::UpgradeContract => {
+            GovernanceAction::UpgradeContract { address } => {
                 buf.write_u8(0)?;
                 buf.write_u16::<BigEndian>(self.target_chain_id)?;
+                buf.write_all(address)?;
             }
-            GovernanceAction::AuthorizeGovernanceDataSourceTransfer => {
+            GovernanceAction::AuthorizeGovernanceDataSourceTransfer { claim_vaa } => {
                 buf.write_u8(1)?;
                 buf.write_u16::<BigEndian>(self.target_chain_id)?;
+                buf.write_all(claim_vaa.as_slice())?;
             }
-            GovernanceAction::SetDataSources => {
+            GovernanceAction::SetDataSources { data_sources } => {
                 buf.write_u8(2)?;
                 buf.write_u16::<BigEndian>(self.target_chain_id)?;
+                buf.write_u8(u8::try_from(data_sources.len())?)?;
+                for data_source in &data_sources {
+                    buf.write_u16::<BigEndian>(data_source.pyth_emitter_chain)?;
+                    buf.write_all(data_source.emitter.as_slice())?;
+                }
             }
             GovernanceAction::SetFee { val, expo } => {
                 buf.write_u8(3)?;
@@ -136,9 +191,12 @@ impl GovernanceInstruction {
 
                 buf.write_u64::<BigEndian>(*new_valid_period)?;
             }
-            GovernanceAction::RequestGovernanceDataSourceTransfer => {
+            GovernanceAction::RequestGovernanceDataSourceTransfer {
+                governance_data_source_index,
+            } => {
                 buf.write_u8(5)?;
                 buf.write_u16::<BigEndian>(self.target_chain_id)?;
+                buf.write_u32::<BigEndian>(*governance_data_source_index)?;
             }
         }
 

+ 1 - 0
cosmwasm/contracts/pyth/src/msg.rs

@@ -26,6 +26,7 @@ pub struct InstantiateMsg {
     pub pyth_emitter_chain:         u16,
     pub governance_emitter:         Binary,
     pub governance_emitter_chain:   u16,
+    pub governance_source_index:    u32,
     pub governance_sequence_number: u64,
     pub chain_id:                   u16,
     pub valid_time_period_secs:     u16,

+ 3 - 0
cosmwasm/contracts/pyth/src/state.rs

@@ -44,6 +44,9 @@ pub struct ConfigInfo {
     pub wormhole_contract:          Addr,
     pub data_sources:               HashSet<PythDataSource>,
     pub governance_source:          PythDataSource,
+    // Incrementing index for the number of times the governance data source has been changed
+    pub governance_source_index:    u32,
+    // The wormhole sequence number for governance messages
     pub governance_sequence_number: u64,
     // FIXME: This id needs to agree with the wormhole chain id.
     // We should read this directly from wormhole.