|
29 | 29 | },
|
30 | 30 | };
|
31 | 31 |
|
| 32 | +// The number of messages in a single update data is defined as a |
| 33 | +// u8 in the wire format. So, we can't have more than 255 messages. |
| 34 | +pub const MAX_MESSAGE_IN_SINGLE_UPDATE_DATA: usize = 255; |
| 35 | + |
32 | 36 | #[derive(Clone, PartialEq, Debug)]
|
33 | 37 | pub struct WormholeMerkleState {
|
34 | 38 | pub root: WormholeMerkleRoot,
|
@@ -87,39 +91,139 @@ pub fn construct_message_states_proofs(
|
87 | 91 | }
|
88 | 92 |
|
89 | 93 | pub fn construct_update_data(mut message_states: Vec<&MessageState>) -> Result<Vec<Vec<u8>>> {
|
90 |
| - message_states.sort_by_key( |
91 |
| - |m| m.proof_set.wormhole_merkle_proof.vaa.clone(), // FIXME: This is not efficient |
92 |
| - ); |
| 94 | + message_states.sort_by_key(|m| m.slot); |
93 | 95 |
|
94 | 96 | message_states
|
95 |
| - .group_by(|a, b| { |
96 |
| - a.proof_set.wormhole_merkle_proof.vaa == b.proof_set.wormhole_merkle_proof.vaa |
97 |
| - }) |
98 |
| - .map(|messages| { |
99 |
| - let vaa = messages |
100 |
| - .get(0) |
101 |
| - .ok_or(anyhow!("Empty message set"))? |
102 |
| - .proof_set |
103 |
| - .wormhole_merkle_proof |
104 |
| - .vaa |
105 |
| - .clone(); |
106 |
| - |
107 |
| - Ok(to_vec::<_, byteorder::BE>(&AccumulatorUpdateData::new( |
108 |
| - Proof::WormholeMerkle { |
109 |
| - vaa: vaa.into(), |
110 |
| - updates: messages |
111 |
| - .iter() |
112 |
| - .map(|message| { |
113 |
| - Ok(MerklePriceUpdate { |
114 |
| - message: to_vec::<_, byteorder::BE>(&message.message) |
115 |
| - .map_err(|e| anyhow!("Failed to serialize message: {}", e))? |
116 |
| - .into(), |
117 |
| - proof: message.proof_set.wormhole_merkle_proof.proof.clone(), |
118 |
| - }) |
119 |
| - }) |
120 |
| - .collect::<Result<_>>()?, |
121 |
| - }, |
122 |
| - ))?) |
| 97 | + .group_by(|a, b| a.slot == b.slot) // States on the same slot share the same merkle root |
| 98 | + .flat_map(|messages| { |
| 99 | + messages |
| 100 | + // Group messages by the number of messages in a single update data |
| 101 | + .chunks(MAX_MESSAGE_IN_SINGLE_UPDATE_DATA) |
| 102 | + .map(|messages| { |
| 103 | + let vaa = messages |
| 104 | + .get(0) |
| 105 | + .ok_or(anyhow!("Empty message set"))? |
| 106 | + .proof_set |
| 107 | + .wormhole_merkle_proof |
| 108 | + .vaa |
| 109 | + .clone(); |
| 110 | + |
| 111 | + Ok(to_vec::<_, byteorder::BE>(&AccumulatorUpdateData::new( |
| 112 | + Proof::WormholeMerkle { |
| 113 | + vaa: vaa.into(), |
| 114 | + updates: messages |
| 115 | + .iter() |
| 116 | + .map(|message| { |
| 117 | + Ok(MerklePriceUpdate { |
| 118 | + message: to_vec::<_, byteorder::BE>(&message.message) |
| 119 | + .map_err(|e| { |
| 120 | + anyhow!("Failed to serialize message: {}", e) |
| 121 | + })? |
| 122 | + .into(), |
| 123 | + proof: message |
| 124 | + .proof_set |
| 125 | + .wormhole_merkle_proof |
| 126 | + .proof |
| 127 | + .clone(), |
| 128 | + }) |
| 129 | + }) |
| 130 | + .collect::<Result<_>>()?, |
| 131 | + }, |
| 132 | + ))?) |
| 133 | + }) |
123 | 134 | })
|
124 | 135 | .collect::<Result<Vec<Vec<u8>>>>()
|
125 | 136 | }
|
| 137 | + |
| 138 | +#[cfg(test)] |
| 139 | +mod test { |
| 140 | + use { |
| 141 | + super::*, |
| 142 | + crate::store::types::ProofSet, |
| 143 | + pythnet_sdk::{ |
| 144 | + messages::{ |
| 145 | + Message, |
| 146 | + PriceFeedMessage, |
| 147 | + }, |
| 148 | + wire::from_slice, |
| 149 | + }, |
| 150 | + }; |
| 151 | + |
| 152 | + fn create_dummy_message_state(slot_and_pubtime: u64) -> MessageState { |
| 153 | + MessageState::new( |
| 154 | + Message::PriceFeedMessage(PriceFeedMessage { |
| 155 | + conf: 0, |
| 156 | + price: 0, |
| 157 | + feed_id: [0; 32], |
| 158 | + exponent: 0, |
| 159 | + ema_conf: 0, |
| 160 | + ema_price: 0, |
| 161 | + publish_time: slot_and_pubtime as i64, |
| 162 | + prev_publish_time: 0, |
| 163 | + }), |
| 164 | + vec![], |
| 165 | + ProofSet { |
| 166 | + wormhole_merkle_proof: WormholeMerkleMessageProof { |
| 167 | + vaa: vec![], |
| 168 | + proof: MerklePath::default(), |
| 169 | + }, |
| 170 | + }, |
| 171 | + slot_and_pubtime, |
| 172 | + 0, |
| 173 | + ) |
| 174 | + } |
| 175 | + |
| 176 | + #[test] |
| 177 | + fn test_construct_update_data_works_on_mixed_slot_and_big_size() { |
| 178 | + let mut message_states = vec![]; |
| 179 | + |
| 180 | + // Messages slot and publish_time 11 share the same merkle root |
| 181 | + for i in 0..MAX_MESSAGE_IN_SINGLE_UPDATE_DATA * 2 - 10 { |
| 182 | + message_states.push(create_dummy_message_state(11)); |
| 183 | + } |
| 184 | + |
| 185 | + // Messages on slot and publish_time 10 that share different root from the messages above |
| 186 | + for i in 0..MAX_MESSAGE_IN_SINGLE_UPDATE_DATA * 2 - 10 { |
| 187 | + message_states.push(create_dummy_message_state(10)); |
| 188 | + } |
| 189 | + |
| 190 | + let update_data = construct_update_data(message_states.iter().collect()).unwrap(); |
| 191 | + |
| 192 | + assert_eq!(update_data.len(), 4); |
| 193 | + |
| 194 | + // Construct method sorts the messages by slot. So, the first two update data should |
| 195 | + // contain messages on slot 10. |
| 196 | + for i in 0..4 { |
| 197 | + let update_data = &update_data[i]; |
| 198 | + let update_data = AccumulatorUpdateData::try_from_slice(update_data).unwrap(); |
| 199 | + let price_updates = match &update_data.proof { |
| 200 | + Proof::WormholeMerkle { updates, .. } => updates, |
| 201 | + }; |
| 202 | + |
| 203 | + let price_update_message = price_updates.first().unwrap().clone(); |
| 204 | + let price_update_message: Vec<u8> = price_update_message.message.into(); |
| 205 | + let price_update_message = |
| 206 | + from_slice::<byteorder::BE, Message>(price_update_message.as_ref()).unwrap(); |
| 207 | + |
| 208 | + match i { |
| 209 | + 0 => { |
| 210 | + assert_eq!(price_updates.len(), MAX_MESSAGE_IN_SINGLE_UPDATE_DATA); |
| 211 | + assert_eq!(price_update_message.publish_time(), 10); |
| 212 | + } |
| 213 | + 1 => { |
| 214 | + assert_eq!(price_updates.len(), MAX_MESSAGE_IN_SINGLE_UPDATE_DATA - 10); |
| 215 | + assert_eq!(price_update_message.publish_time(), 10); |
| 216 | + } |
| 217 | + 2 => { |
| 218 | + assert_eq!(price_updates.len(), MAX_MESSAGE_IN_SINGLE_UPDATE_DATA); |
| 219 | + assert_eq!(price_update_message.publish_time(), 11); |
| 220 | + } |
| 221 | + 3 => { |
| 222 | + assert_eq!(price_updates.len(), MAX_MESSAGE_IN_SINGLE_UPDATE_DATA - 10); |
| 223 | + assert_eq!(price_update_message.publish_time(), 11); |
| 224 | + } |
| 225 | + _ => panic!("Invalid index"), |
| 226 | + } |
| 227 | + } |
| 228 | + } |
| 229 | +} |
0 commit comments