terra_rust_api/messages/
oracle.rs

1use crate::core_types::{Coin, MsgInternal};
2
3use crate::messages::Message;
4use crypto::digest::Digest;
5use crypto::sha2::Sha256;
6use serde::{Deserialize, Serialize};
7use std::ops::Add;
8
9#[derive(Deserialize, Serialize, Debug)]
10
11/// used in feeder oracle
12pub struct MsgAggregateExchangeRatePreVote {
13    pub feeder: String,
14    pub hash: String,
15    pub validator: String,
16}
17
18impl MsgInternal for MsgAggregateExchangeRatePreVote {}
19impl MsgAggregateExchangeRatePreVote {
20    /// Create a pre vote message
21    pub fn create(hash: String, feeder: String, validator: String) -> anyhow::Result<Message> {
22        let internal = MsgAggregateExchangeRatePreVote {
23            feeder,
24            hash,
25            validator,
26        };
27        Ok(Message {
28            s_type: "oracle/MsgAggregateExchangeRatePrevote".into(),
29            value: serde_json::to_value(internal)?,
30        })
31    }
32}
33
34#[derive(Deserialize, Serialize, Debug)]
35
36/// used in feeder oracle to submit exchange rates
37pub struct MsgAggregateExchangeRateVote {
38    pub exchange_rates: String,
39    pub feeder: String,
40    /// The salt is used in the next round's 'PreVote'
41    pub salt: String,
42    pub validator: String,
43}
44
45/// put out into a separate function to facilitate better testing
46fn generate_hash<'a>(salt: &'a str, exchange_string: &'a str, validator: &'a str) -> String {
47    let mut sha = Sha256::new();
48    let mut to_hash: String = String::new();
49    to_hash = to_hash.add(salt);
50    to_hash = to_hash.add(":");
51    to_hash = to_hash.add(exchange_string);
52    to_hash = to_hash.add(":");
53    to_hash = to_hash.add(validator);
54    sha.input_str(&to_hash);
55    let full_hash = sha.result_str();
56    full_hash.split_at(40).0.parse().unwrap()
57}
58impl MsgInternal for MsgAggregateExchangeRateVote {}
59impl MsgAggregateExchangeRateVote {
60    fn generate_hash(&self, previous_salt: &str) -> String {
61        generate_hash(previous_salt, &self.exchange_rates, &self.validator)
62    }
63
64    pub fn create_internal(
65        salt: String,
66        exchange_rates: Vec<Coin>,
67        feeder: String,
68        validator: String,
69    ) -> MsgAggregateExchangeRateVote {
70        let mut new_rates: Vec<Coin> = Vec::with_capacity(exchange_rates.len());
71        for rate in exchange_rates {
72            new_rates.push(rate);
73        }
74        new_rates.sort_by(|a, b| a.denom.cmp(&b.denom));
75        let coins = new_rates
76            .iter()
77            .map(|f| f.to_string())
78            .collect::<Vec<String>>()
79            .join(",");
80        MsgAggregateExchangeRateVote {
81            salt,
82            exchange_rates: coins,
83            feeder,
84            validator,
85        }
86    }
87    /// Create a vote message
88    pub fn create(
89        salt: String,
90        exchange_rates: Vec<Coin>,
91        feeder: String,
92        validator: String,
93    ) -> anyhow::Result<Message> {
94        let internal =
95            MsgAggregateExchangeRateVote::create_internal(salt, exchange_rates, feeder, validator);
96        Ok(Message {
97            s_type: "oracle/MsgAggregateExchangeRateVote".into(),
98            value: serde_json::to_value(internal)?,
99        })
100    }
101    /// Create a vote message from internal message
102    pub fn create_from_internal(internal: MsgAggregateExchangeRateVote) -> anyhow::Result<Message> {
103        //  let internal =
104        //      MsgAggregateExchangeRateVote::create_internal(salt, exchange_rates, feeder, validator);
105        Ok(Message {
106            s_type: "oracle/MsgAggregateExchangeRateVote".into(),
107            value: serde_json::to_value(internal)?,
108        })
109    }
110
111    /// Pre-Vote messages are like a 'linked list'.
112    /// they use the salt of the previous 'RateVote' to hash the current prices, to ensure continuity
113    pub fn gen_pre_vote(&self, previous_salt: &str) -> anyhow::Result<Message> {
114        MsgAggregateExchangeRatePreVote::create(
115            self.generate_hash(previous_salt),
116            self.feeder.clone(),
117            self.validator.clone(),
118        )
119    }
120}
121
122#[derive(Deserialize, Serialize, Debug)]
123/// used in feeder oracle
124
125pub struct MsgDelegateFeedConsent {
126    pub delegate: String,
127    pub operator: String,
128}
129impl MsgInternal for MsgDelegateFeedConsent {}
130impl MsgDelegateFeedConsent {
131    /// Create a pre vote message
132    pub fn create(operator: String, delegate: String) -> anyhow::Result<Message> {
133        let internal = MsgDelegateFeedConsent { delegate, operator };
134        Ok(Message {
135            s_type: "oracle/MsgDelegateFeedConsent".into(),
136            value: serde_json::to_value(internal)?,
137        })
138    }
139}
140
141#[cfg(test)]
142mod tst {
143    use super::*;
144    #[test]
145    pub fn test_agg() -> anyhow::Result<()> {
146        let exchange_rate_str = "22.540203133218404887uaud,21.645596278923282692ucad,15.966787551658593971uchf,113.167767068332957759ucny,14.449845494375560683ueur,12.582839885411827405ugbp,135.474594500430895984uhkd,1300.213822842493250029uinr,1900.8256376511075722ujpy,20351.150811544637337767ukrw,49749.106615326838874584umnt,12.154984433357638529usdr,23.143090361112943758usgd,0.0uthb,17.444833658754882816uusd";
147        let exchange_rates = Coin::parse_coins(exchange_rate_str)?;
148        let salt = String::from("df59");
149        let feeder = String::from("terra1824vxwh43h9d3qczj4jvc3qphlf2evfp9w0ph9");
150        let validator = String::from("terravaloper1usws7c2c6cs7nuc8vma9qzaky5pkgvm2ujy8ny");
151        let hash = "36681b69da96623a6ae12c2a51448b7426fdd64e";
152        let coins = exchange_rates
153            .iter()
154            .map(|f| f.to_string())
155            .collect::<Vec<String>>()
156            .join(",");
157        assert_eq!(coins, exchange_rate_str);
158
159        assert_eq!(generate_hash(&salt, exchange_rate_str, &validator), hash);
160        let vote_1 = MsgAggregateExchangeRateVote::create_internal(
161            salt.clone(),
162            exchange_rates,
163            feeder,
164            validator,
165        );
166
167        assert_eq!(vote_1.generate_hash(&salt), hash);
168        //        let pre_vote = vote_1.gen_pre_vote(&salt);
169        //        assert_eq!(pre_vote.s_type, "oracle/MsgAggregateExchangeRatePrevote");
170
171        Ok(())
172    }
173    #[test]
174    pub fn tst_hash() -> anyhow::Result<()> {
175        let exchange_rates= "22.540203133218404887uaud,21.645596278923282692ucad,15.966787551658593971uchf,113.167767068332957759ucny,14.449845494375560683ueur,12.582839885411827405ugbp,135.474594500430895984uhkd,1300.213822842493250029uinr,1900.8256376511075722ujpy,20351.150811544637337767ukrw,49749.106615326838874584umnt,12.154984433357638529usdr,23.143090361112943758usgd,0.0uthb,17.444833658754882816uusd";
176        let salt = "df59";
177        let validator = "terravaloper1usws7c2c6cs7nuc8vma9qzaky5pkgvm2ujy8ny";
178        let hash = "36681b69da96623a6ae12c2a51448b7426fdd64e";
179        assert_eq!(hash, generate_hash(salt, exchange_rates, validator));
180        let exchange_rates2="22.548222362821767308uaud,21.653297230216244188ucad,15.972468127589880034uchf,113.208029274598674692ucny,14.454986380997906048ueur,12.58731653903855345ugbp,135.522792907175522923uhkd,1300.676405771192471765uinr,1901.501902950678788445ujpy,20358.286256112132944846ukrw,49766.806079087327983387umnt,12.159308866922868479usdr,23.151324082621141584usgd,0.0uthb,17.451040085807700841uusd";
181        let salt2 = "6dd4";
182        let validator2 = "terravaloper1usws7c2c6cs7nuc8vma9qzaky5pkgvm2ujy8ny";
183        let hash2 = "54a849b1b3b510f5f0b7c5405ed2cc74cd283251";
184        assert_eq!(hash2, generate_hash(salt2, exchange_rates2, validator2));
185
186        Ok(())
187    }
188}