1use super::*;
2
3#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, Hash)]
4pub enum OracleServiceTarget {
5 LVM(LyquidID),
7 EVM {
9 target: Address,
11 eth_contract: Address,
13 },
14}
15
16#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, Hash)]
17pub struct OracleTarget {
18 pub target: OracleServiceTarget,
20 pub seq_id: SequenceBackendID,
22}
23
24impl OracleTarget {
25 pub fn cipher(&self) -> Cipher {
26 match self.target {
27 OracleServiceTarget::EVM { .. } => Cipher::Secp256k1,
28 OracleServiceTarget::LVM(_) => Cipher::Ed25519,
29 }
30 }
31}
32
33#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
35pub struct OracleHeader {
36 pub proposer: NodeID,
38 pub target: OracleTarget,
40 pub config_hash: HashBytes,
42 pub epoch: u32,
44 pub nonce: HashBytes,
46}
47
48#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
49pub struct OracleEpochInfo {
50 pub epoch: u32,
51 pub config_hash: HashBytes,
52 pub change_count: u32,
53 pub config: Option<OracleConfig>,
54}
55
56pub type SignerID = u32;
57
58#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
59pub struct OracleSigner {
60 pub id: SignerID,
61 pub key: Bytes,
62}
63
64#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
65pub struct OracleConfig {
66 pub committee: Vec<OracleSigner>,
67 pub threshold: u16,
68}
69
70impl OracleConfig {
71 pub fn to_hash(&self) -> Hash {
72 blake3::hash(&encode_object(self))
73 }
74}
75
76#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
77pub struct OracleConfigDelta {
78 pub upsert: Vec<OracleSigner>,
79 pub remove: Vec<SignerID>,
80 pub threshold: Option<u16>,
81}
82
83#[derive(Serialize, Deserialize)]
84pub struct ValidatePreimage {
85 pub header: OracleHeader,
86 pub params: CallParams,
87 pub approval: bool,
88}
89
90impl ValidatePreimage {
91 const PREFIX: &'static [u8] = b"lyquor_validate_preimage_v1\0";
92
93 pub fn to_preimage(&self) -> Vec<u8> {
94 encode_object_with_prefix(Self::PREFIX, self)
95 }
96
97 pub fn to_hash(&self) -> Hash {
98 blake3::hash(&self.to_preimage())
99 }
100}
101
102#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
104pub struct OracleCert {
105 pub header: OracleHeader,
106 pub signers: Vec<SignerID>,
108 pub signatures: Vec<Bytes>,
110}
111
112pub mod eth {
113 use crate::{Address, decode_by_fields};
114 use alloy_sol_types::{SolValue, sol};
115 sol! {
116 struct OracleHeader {
117 bytes32 topic;
118 bytes32 group;
119 bytes32 proposer;
120 address target;
121 bytes32 seqId;
122 address ethContract;
123 bytes32 configHash;
124 uint32 epoch;
125 bytes32 nonce;
126 }
127
128 struct OracleSigner {
129 uint32 id;
130 bytes32 nodeID;
131 }
132
133 #[derive(Default)]
134 struct OracleConfig {
135 OracleSigner[] committee;
136 uint16 threshold;
137 }
138
139 #[derive(Default)]
140 struct OracleConfigDelta {
141 OracleSigner[] upsert;
142 uint32[] remove;
143 bool thresholdChanged;
144 uint16 threshold;
145 }
146
147 enum ABI {
148 Lyquor,
149 Eth
150 }
151
152 struct CallParams {
153 address origin;
154 address caller;
155 string group;
156 string method;
157 bytes input;
158 ABI abi_;
159 }
160
161 struct ValidatePreimage {
162 OracleHeader header;
163 CallParams params;
164 bool approval; }
166 }
167
168 impl OracleConfig {
169 pub fn to_hash(&self) -> super::Hash {
170 alloy_primitives::keccak256(SolValue::abi_encode(self)).0.into()
171 }
172 }
173
174 impl TryFrom<super::OracleConfigDelta> for OracleConfigDelta {
175 type Error = ();
176
177 fn try_from(delta: super::OracleConfigDelta) -> Result<Self, Self::Error> {
178 let upsert = delta
179 .upsert
180 .into_iter()
181 .map(|signer| {
182 let key = signer.key.as_ref();
183 if key.len() != 32 {
184 return Err(());
185 }
186 let node_id = <[u8; 32]>::try_from(key).map_err(|_| ())?.into();
187 Ok(OracleSigner {
188 id: signer.id,
189 nodeID: node_id,
190 })
191 })
192 .collect::<Result<Vec<_>, ()>>()?;
193
194 Ok(Self {
195 upsert,
196 remove: delta.remove,
197 thresholdChanged: delta.threshold.is_some(),
198 threshold: delta.threshold.unwrap_or(0),
199 })
200 }
201 }
202
203 impl ValidatePreimage {
204 const PREFIX: &'static [u8] = b"lyquor_validate_preimage_v1\0";
205
206 pub fn to_preimage(&self) -> Vec<u8> {
207 let mut buf = Vec::from(Self::PREFIX);
208 buf.extend_from_slice(&SolValue::abi_encode(self));
209 buf
210 }
211
212 pub fn to_hash(&self) -> super::Hash {
213 alloy_primitives::keccak256(self.to_preimage()).0.into()
214 }
215 }
216
217 impl From<OracleHeader> for super::OracleHeader {
218 fn from(oh: OracleHeader) -> Self {
219 Self {
220 proposer: <[u8; 32]>::from(oh.proposer).into(),
221 target: super::OracleTarget {
222 target: super::OracleServiceTarget::EVM {
223 target: oh.target,
224 eth_contract: oh.ethContract,
225 },
226 seq_id: <[u8; 32]>::from(oh.seqId).into(),
227 },
228 config_hash: <[u8; 32]>::from(oh.configHash).into(),
229 epoch: oh.epoch,
230 nonce: <[u8; 32]>::from(oh.nonce).into(),
231 }
232 }
233 }
234
235 impl From<super::OracleConfig> for OracleConfig {
236 fn from(oc: super::OracleConfig) -> Self {
237 Self {
238 committee: oc
239 .committee
240 .into_iter()
241 .map(|s| {
242 let key = s.key;
243 OracleSigner {
244 id: s.id,
245 nodeID: <[u8; 32]>::try_from(key.as_ref()).unwrap().into(),
246 }
247 })
248 .collect(),
249 threshold: oc.threshold,
250 }
251 }
252 }
253
254 impl From<OracleConfig> for super::OracleConfig {
255 fn from(oc: OracleConfig) -> Self {
256 Self {
257 committee: oc
258 .committee
259 .into_iter()
260 .map(|s| super::OracleSigner {
261 id: s.id,
262 key: s.nodeID.as_slice().to_vec().into(),
263 })
264 .collect(),
265 threshold: oc.threshold,
266 }
267 }
268 }
269
270 impl TryFrom<super::ValidatePreimage> for ValidatePreimage {
271 type Error = ();
272 fn try_from(om: super::ValidatePreimage) -> Result<Self, ()> {
273 let params = om.params;
274 let is_epoch_advance = params.origin == Address::ZERO &&
275 params.abi == super::InputABI::Lyquor &&
276 params.group == "oracle::internal" &&
277 params.method == "__lyquor_oracle_on_epoch_advance";
278 let (topic, input) = if is_epoch_advance {
279 let payload = decode_by_fields!(
280 params.input.as_ref(),
281 topic: String,
282 config_delta: super::OracleConfigDelta,
283 change_count: u32
284 )
285 .ok_or(())?;
286 let config_delta = OracleConfigDelta::try_from(payload.config_delta)?;
287 let topic = payload.topic;
288 let input = (config_delta, payload.change_count).abi_encode_params();
289 (topic, input)
290 } else {
291 (
292 params
293 .group
294 .split_once("::")
295 .map(|(topic, _)| topic)
296 .unwrap_or(params.group.as_str())
297 .to_string(),
298 params.input.to_vec(),
299 )
300 };
301 let topic_hash = alloy_primitives::keccak256(topic.as_bytes());
302 let group_hash = alloy_primitives::keccak256(params.group.as_bytes());
303 let (target, eth_contract) = match om.header.target.target {
304 super::OracleServiceTarget::LVM(_) => return Err(()),
305 super::OracleServiceTarget::EVM { target, eth_contract } => (target, eth_contract),
306 };
307 let params = CallParams {
308 origin: params.origin,
309 caller: params.caller,
310 group: params.group,
311 method: params.method,
312 input: input.into(),
313 abi_: match params.abi {
314 super::InputABI::Lyquor => ABI::Lyquor,
315 super::InputABI::Eth => ABI::Eth,
316 },
317 };
318 let header = OracleHeader {
319 topic: topic_hash,
320 group: group_hash,
321 proposer: <[u8; 32]>::from(om.header.proposer).into(),
322 target,
323 seqId: <[u8; 32]>::from(om.header.target.seq_id).into(),
324 ethContract: eth_contract,
325 configHash: <[u8; 32]>::from(om.header.config_hash).into(),
326 epoch: om.header.epoch,
327 nonce: <[u8; 32]>::from(om.header.nonce).into(),
328 };
329
330 Ok(Self {
331 header,
332 params,
333 approval: om.approval,
334 })
335 }
336 }
337}