Jayant Krishnamurthy преди 2 години
родител
ревизия
7af0bfa219
променени са 3 файла, в които са добавени 214 реда и са изтрити 227 реда
  1. 205 223
      cosmwasm/contracts/pyth/src/contract.rs
  2. 9 1
      cosmwasm/contracts/pyth/src/governance.rs
  3. 0 3
      cosmwasm/contracts/pyth/src/msg.rs

+ 205 - 223
cosmwasm/contracts/pyth/src/contract.rs

@@ -123,11 +123,6 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S
         ExecuteMsg::ExecuteGovernanceInstruction { data } => {
             execute_governance_instruction(deps, env, info, &data)
         }
-        // TODO: remove these and invoke via governance
-        ExecuteMsg::AddDataSource { data_source } => add_data_source(deps, env, info, data_source),
-        ExecuteMsg::RemoveDataSource { data_source } => {
-            remove_data_source(deps, env, info, data_source)
-        }
     }
 }
 
@@ -160,6 +155,7 @@ fn execute_governance_instruction(
     _info: MessageInfo,
     data: &Binary,
 ) -> StdResult<Response> {
+    println!("here");
     let vaa = parse_vaa(deps.branch(), env.block.time.seconds(), data)?;
     let state = config_read(deps.storage).load()?;
 
@@ -172,11 +168,14 @@ fn execute_governance_instruction(
     } else {
         updated_config.governance_sequence_number = vaa.sequence;
     }
+    println!("trying to deserialize payload");
 
     let data = &vaa.payload;
     let instruction = GovernanceInstruction::deserialize(&data[..])
         .map_err(|_| PythContractError::InvalidGovernancePayload)?;
 
+    println!("Deserialized {instruction:?}");
+
     if instruction.target_chain_id != state.chain_id && instruction.target_chain_id != 0 {
         return Err(PythContractError::InvalidGovernancePayload)?;
     }
@@ -188,8 +187,11 @@ fn execute_governance_instruction(
         }
         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)?;
+            let claim_vaa_instruction =
+                GovernanceInstruction::deserialize(parsed_claim_vaa.payload.as_slice())
+                    .map_err(|_| PythContractError::InvalidGovernancePayload)?;
+
+            println!("claim VAA instruction: {claim_vaa_instruction:?}");
 
             if claim_vaa_instruction.target_chain_id != state.chain_id
                 && claim_vaa_instruction.target_chain_id != 0
@@ -232,6 +234,8 @@ fn execute_governance_instruction(
             }
         }
         SetDataSources { data_sources } => {
+            println!("setting data sources");
+
             updated_config.data_sources = HashSet::from_iter(data_sources.iter().cloned());
 
             Response::new()
@@ -275,61 +279,6 @@ fn execute_governance_instruction(
     Ok(response)
 }
 
-fn add_data_source(
-    deps: DepsMut,
-    _env: Env,
-    info: MessageInfo,
-    data_source: PythDataSource,
-) -> StdResult<Response> {
-    let mut state = config_read(deps.storage).load()?;
-
-    if state.owner != info.sender {
-        return Err(PythContractError::PermissionDenied)?;
-    }
-
-    if !state.data_sources.insert(data_source.clone()) {
-        return Err(PythContractError::DataSourceAlreadyExists)?;
-    }
-
-    config(deps.storage).save(&state)?;
-
-    Ok(Response::new()
-        .add_attribute("action", "add_data_source")
-        .add_attribute("data_source_emitter", format!("{}", data_source.emitter))
-        .add_attribute(
-            "data_source_emitter_chain",
-            format!("{}", data_source.pyth_emitter_chain),
-        ))
-}
-
-fn remove_data_source(
-    deps: DepsMut,
-    _env: Env,
-    info: MessageInfo,
-    data_source: PythDataSource,
-) -> StdResult<Response> {
-    let mut state = config_read(deps.storage).load()?;
-
-    if state.owner != info.sender {
-        return Err(PythContractError::PermissionDenied)?;
-    }
-
-    if !state.data_sources.remove(&data_source) {
-        return Err(PythContractError::DataSourceDoesNotExists)?;
-    }
-
-    config(deps.storage).save(&state)?;
-
-    Ok(Response::new()
-        .add_attribute("action", "remove_data_source")
-        .add_attribute("data_source_emitter", format!("{}", data_source.emitter))
-        .add_attribute(
-            "data_source_emitter_chain",
-            format!("{}", data_source.pyth_emitter_chain),
-        ))
-}
-
-
 /// Check that `vaa` is from a valid data source (and hence is a legitimate price update message).
 fn verify_vaa_from_data_source(state: &ConfigInfo, vaa: &ParsedVAA) -> StdResult<()> {
     let vaa_data_source = PythDataSource {
@@ -1004,167 +953,6 @@ mod test {
         );
     }
 
-    #[test]
-    fn test_add_data_source_ok_with_owner() {
-        let (mut deps, env) = setup_test();
-        config(&mut deps.storage)
-            .save(&ConfigInfo {
-                owner: Addr::unchecked("123"),
-                ..create_zero_config_info()
-            })
-            .unwrap();
-
-        let data_source = PythDataSource {
-            emitter:            vec![1u8].into(),
-            pyth_emitter_chain: 1,
-        };
-
-        assert!(add_data_source(
-            deps.as_mut(),
-            env.clone(),
-            mock_info("123", &[]),
-            data_source.clone()
-        )
-        .is_ok());
-
-        // Adding an existing data source should result an error
-        assert!(add_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_err());
-    }
-
-    #[test]
-    fn test_add_data_source_err_without_owner() {
-        let (mut deps, env) = setup_test();
-        config(&mut deps.storage)
-            .save(&ConfigInfo {
-                owner: Addr::unchecked("123"),
-                ..create_zero_config_info()
-            })
-            .unwrap();
-
-        let data_source = PythDataSource {
-            emitter:            vec![1u8].into(),
-            pyth_emitter_chain: 1,
-        };
-
-        assert!(add_data_source(deps.as_mut(), env, mock_info("321", &[]), data_source).is_err());
-    }
-
-    #[test]
-    fn test_remove_data_source_ok_with_owner() {
-        let (mut deps, env) = setup_test();
-        config(&mut deps.storage)
-            .save(&ConfigInfo {
-                owner: Addr::unchecked("123"),
-                data_sources: create_data_sources(vec![1u8], 1),
-                ..create_zero_config_info()
-            })
-            .unwrap();
-
-        let data_source = PythDataSource {
-            emitter:            vec![1u8].into(),
-            pyth_emitter_chain: 1,
-        };
-
-        assert!(remove_data_source(
-            deps.as_mut(),
-            env.clone(),
-            mock_info("123", &[]),
-            data_source.clone()
-        )
-        .is_ok());
-
-        // Removing a non existent data source should result an error
-        assert!(
-            remove_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_err()
-        );
-    }
-
-    #[test]
-    fn test_remove_data_source_err_without_owner() {
-        let (mut deps, env) = setup_test();
-        config(&mut deps.storage)
-            .save(&ConfigInfo {
-                owner: Addr::unchecked("123"),
-                data_sources: create_data_sources(vec![1u8], 1),
-                ..create_zero_config_info()
-            })
-            .unwrap();
-
-        let data_source = PythDataSource {
-            emitter:            vec![1u8].into(),
-            pyth_emitter_chain: 1,
-        };
-
-        assert!(
-            remove_data_source(deps.as_mut(), env, mock_info("321", &[]), data_source).is_err()
-        );
-    }
-
-    #[test]
-    fn test_verify_vaa_works_after_adding_data_source() {
-        let (mut deps, env) = setup_test();
-        config(&mut deps.storage)
-            .save(&ConfigInfo {
-                owner: Addr::unchecked("123"),
-                ..create_zero_config_info()
-            })
-            .unwrap();
-
-        let mut vaa = create_zero_vaa();
-        vaa.emitter_address = vec![1u8];
-        vaa.emitter_chain = 3;
-
-        // Should result an error because there is no data source
-        assert_eq!(
-            verify_vaa_from_data_source(&config_read(&deps.storage).load().unwrap(), &vaa),
-            Err(PythContractError::InvalidUpdateEmitter.into())
-        );
-
-        let data_source = PythDataSource {
-            emitter:            vec![1u8].into(),
-            pyth_emitter_chain: 3,
-        };
-        assert!(add_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_ok());
-
-        assert_eq!(
-            verify_vaa_from_data_source(&config_read(&deps.storage).load().unwrap(), &vaa),
-            Ok(())
-        );
-    }
-
-    #[test]
-    fn test_verify_vaa_err_after_removing_data_source() {
-        let (mut deps, env) = setup_test();
-        config(&mut deps.storage)
-            .save(&ConfigInfo {
-                owner: Addr::unchecked("123"),
-                data_sources: create_data_sources(vec![1u8], 3),
-                ..create_zero_config_info()
-            })
-            .unwrap();
-
-        let mut vaa = create_zero_vaa();
-        vaa.emitter_address = vec![1u8];
-        vaa.emitter_chain = 3;
-
-        assert_eq!(
-            verify_vaa_from_data_source(&config_read(&deps.storage).load().unwrap(), &vaa),
-            Ok(())
-        );
-
-        let data_source = PythDataSource {
-            emitter:            vec![1u8].into(),
-            pyth_emitter_chain: 3,
-        };
-        assert!(remove_data_source(deps.as_mut(), env, mock_info("123", &[]), data_source).is_ok());
-
-        // Should result an error because data source should not exist anymore
-        assert_eq!(
-            verify_vaa_from_data_source(&config_read(&deps.storage).load().unwrap(), &vaa),
-            Err(PythContractError::InvalidUpdateEmitter.into())
-        );
-    }
-
     /// Initialize the contract with `initial_config` then execute `vaa` as a governance instruction
     /// against it. Returns the response of the governance instruction along with the resulting config.
     fn apply_governance_vaa(
@@ -1265,6 +1053,166 @@ mod test {
         assert!(apply_governance_vaa(&test_config, &vaa_copy).is_err());
     }
 
+    #[test]
+    fn test_authorize_governance_transfer_success() {
+        let source_2 = PythDataSource {
+            emitter:            Binary::from([2u8; 32]),
+            pyth_emitter_chain: 4,
+        };
+
+        let test_config = governance_test_config();
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: test_config.chain_id,
+            action:          AuthorizeGovernanceDataSourceTransfer {
+                claim_vaa: to_binary(&ParsedVAA {
+                    emitter_address: source_2.emitter.to_vec(),
+                    emitter_chain: source_2.pyth_emitter_chain,
+                    sequence: 12,
+                    payload: GovernanceInstruction {
+                        module:          Target,
+                        target_chain_id: test_config.chain_id,
+                        action:          RequestGovernanceDataSourceTransfer {
+                            governance_data_source_index: 11,
+                        },
+                    }
+                    .serialize()
+                    .unwrap(),
+                    ..create_zero_vaa()
+                })
+                .unwrap(),
+            },
+        };
+
+        let test_vaa = governance_vaa(&test_instruction);
+        let (_response, result_config) = apply_governance_vaa(&test_config, &test_vaa).unwrap();
+        assert_eq!(result_config.governance_source, source_2);
+        assert_eq!(result_config.governance_source_index, 11);
+        assert_eq!(result_config.governance_sequence_number, 12);
+    }
+
+    #[test]
+    fn test_authorize_governance_transfer_bad_source_index() {
+        let source_2 = PythDataSource {
+            emitter:            Binary::from([2u8; 32]),
+            pyth_emitter_chain: 4,
+        };
+
+        let mut test_config = governance_test_config();
+        test_config.governance_source_index = 10;
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: test_config.chain_id,
+            action:          AuthorizeGovernanceDataSourceTransfer {
+                claim_vaa: to_binary(&ParsedVAA {
+                    emitter_address: source_2.emitter.to_vec(),
+                    emitter_chain: source_2.pyth_emitter_chain,
+                    sequence: 12,
+                    payload: GovernanceInstruction {
+                        module:          Target,
+                        target_chain_id: test_config.chain_id,
+                        action:          RequestGovernanceDataSourceTransfer {
+                            governance_data_source_index: 10,
+                        },
+                    }
+                    .serialize()
+                    .unwrap(),
+                    ..create_zero_vaa()
+                })
+                .unwrap(),
+            },
+        };
+
+        let test_vaa = governance_vaa(&test_instruction);
+        assert_eq!(
+            apply_governance_vaa(&test_config, &test_vaa),
+            Err(PythContractError::OldGovernanceMessage.into())
+        );
+    }
+
+    #[test]
+    fn test_authorize_governance_transfer_bad_target_chain() {
+        let source_2 = PythDataSource {
+            emitter:            Binary::from([2u8; 32]),
+            pyth_emitter_chain: 4,
+        };
+
+        let test_config = governance_test_config();
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: test_config.chain_id,
+            action:          AuthorizeGovernanceDataSourceTransfer {
+                claim_vaa: to_binary(&ParsedVAA {
+                    emitter_address: source_2.emitter.to_vec(),
+                    emitter_chain: source_2.pyth_emitter_chain,
+                    sequence: 12,
+                    payload: GovernanceInstruction {
+                        module:          Target,
+                        target_chain_id: test_config.chain_id + 1,
+                        action:          RequestGovernanceDataSourceTransfer {
+                            governance_data_source_index: 11,
+                        },
+                    }
+                    .serialize()
+                    .unwrap(),
+                    ..create_zero_vaa()
+                })
+                .unwrap(),
+            },
+        };
+
+        let test_vaa = governance_vaa(&test_instruction);
+        assert_eq!(
+            apply_governance_vaa(&test_config, &test_vaa),
+            Err(PythContractError::InvalidGovernancePayload.into())
+        );
+    }
+
+    #[test]
+    fn test_set_data_sources() {
+        let source_1 = PythDataSource {
+            emitter:            Binary::from([1u8; 32]),
+            pyth_emitter_chain: 2,
+        };
+        let source_2 = PythDataSource {
+            emitter:            Binary::from([2u8; 32]),
+            pyth_emitter_chain: 4,
+        };
+        let source_3 = PythDataSource {
+            emitter:            Binary::from([3u8; 32]),
+            pyth_emitter_chain: 6,
+        };
+
+        let mut test_config = governance_test_config();
+        test_config.data_sources = HashSet::from([source_1]);
+
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: test_config.chain_id,
+            action:          SetDataSources {
+                data_sources: vec![source_2.clone(), source_3.clone()],
+            },
+        };
+        let test_vaa = governance_vaa(&test_instruction);
+        assert_eq!(
+            apply_governance_vaa(&test_config, &test_vaa).map(|(_r, c)| c.data_sources),
+            Ok([source_2, source_3].iter().cloned().collect())
+        );
+
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: test_config.chain_id,
+            action:          SetDataSources {
+                data_sources: vec![],
+            },
+        };
+        let test_vaa = governance_vaa(&test_instruction);
+        assert_eq!(
+            apply_governance_vaa(&test_config, &test_vaa).map(|(_r, c)| c.data_sources),
+            Ok(HashSet::new())
+        );
+    }
+
     #[test]
     fn test_set_fee() {
         let mut test_config = governance_test_config();
@@ -1294,4 +1242,38 @@ mod test {
             Ok(Uint128::new(6))
         );
     }
+
+    #[test]
+    fn test_set_valid_period() {
+        let mut test_config = governance_test_config();
+        test_config.valid_time_period = Duration::from_secs(10);
+
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: 5,
+            action:          SetValidPeriod { valid_seconds: 20 },
+        };
+        let test_vaa = governance_vaa(&test_instruction);
+
+        assert_eq!(
+            apply_governance_vaa(&test_config, &test_vaa).map(|(_r, c)| c.valid_time_period),
+            Ok(Duration::from_secs(20))
+        );
+    }
+
+    #[test]
+    fn test_request_governance_transfer() {
+        let test_config = governance_test_config();
+
+        let test_instruction = GovernanceInstruction {
+            module:          Target,
+            target_chain_id: test_config.chain_id,
+            action:          RequestGovernanceDataSourceTransfer {
+                governance_data_source_index: 7,
+            },
+        };
+        let test_vaa = governance_vaa(&test_instruction);
+
+        assert!(apply_governance_vaa(&test_config, &test_vaa).is_err());
+    }
 }

+ 9 - 1
cosmwasm/contracts/pyth/src/governance.rs

@@ -171,8 +171,16 @@ impl GovernanceInstruction {
                 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 {
+                for data_source in data_sources {
                     buf.write_u16::<BigEndian>(data_source.pyth_emitter_chain)?;
+
+                    // The message format expects emitter addresses to be 32 bytes.
+                    // However, we don't maintain this invariant in the rust code (and we violate it in the tests).
+                    // This check gives you a reasonable error message if you happen to violate it in the tests.
+                    if data_source.emitter.len() != 32 {
+                        Err("Emitter addresses must be 32 bytes")?
+                    }
+
                     buf.write_all(data_source.emitter.as_slice())?;
                 }
             }

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

@@ -1,5 +1,4 @@
 use {
-    crate::state::PythDataSource,
     cosmwasm_std::{
         Binary,
         Uint128,
@@ -40,8 +39,6 @@ pub struct InstantiateMsg {
 pub enum ExecuteMsg {
     // TODO: add UpdatePriceFeeds if necessary
     UpdatePriceFeeds { data: Binary },
-    AddDataSource { data_source: PythDataSource },
-    RemoveDataSource { data_source: PythDataSource },
     ExecuteGovernanceInstruction { data: Binary },
 }