Skip to content

Commit c2275e2

Browse files
committed
fixup! pyth: introduce pyth accumulator library
1 parent e732e43 commit c2275e2

File tree

3 files changed

+75
-61
lines changed

3 files changed

+75
-61
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runtime/src/bank.rs

Lines changed: 71 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ mod transaction_account_state_info;
181181
pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0;
182182

183183
pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5;
184+
pub const ACCUMULATOR_RING_SIZE: u32 = 10_000;
184185

185186
#[derive(Clone, Debug, PartialEq)]
186187
pub struct RentDebit {
@@ -1519,13 +1520,17 @@ impl Bank {
15191520
}
15201521
bank.update_stake_history(None);
15211522
}
1522-
bank.update_accumulator();
15231523
bank.update_clock(None);
15241524

15251525
bank.update_rent();
15261526
bank.update_epoch_schedule();
15271527
bank.update_recent_blockhashes();
15281528
bank.fill_missing_sysvar_cache_entries();
1529+
1530+
// Update the accumulator last to make sure that the solana bank is in a
1531+
// fully updated state before the accumulator is used.
1532+
bank.update_accumulator();
1533+
15291534
bank
15301535
}
15311536

@@ -1878,9 +1883,12 @@ impl Bank {
18781883
|_| {
18791884
new.update_slot_hashes();
18801885
new.update_stake_history(Some(parent_epoch));
1881-
new.update_accumulator();
18821886
new.update_clock(Some(parent_epoch));
18831887
new.update_fees();
1888+
1889+
// Update the accumulator last to make sure that all sysvars are in a
1890+
// fully updated state before the accumulator sysvar updates.
1891+
new.update_accumulator();
18841892
},
18851893
(),
18861894
"update_sysvars",
@@ -2596,6 +2604,8 @@ impl Bank {
25962604
let acc_state_magic = &mut b"PAS1".to_vec();
25972605
let accounts_data = &mut accounts.clone().collect::<Vec<_>>().try_to_vec()?;
25982606
data.append(acc_state_magic);
2607+
data.append(&mut self.clock().slot.try_to_vec()?);
2608+
data.append(&mut ACCUMULATOR_RING_SIZE.try_to_vec()?);
25992609
data.append(accounts_data);
26002610
let owner = solana_sdk::system_program::id();
26012611
let balance = self.get_minimum_balance_for_rent_exemption(data.len());
@@ -2666,7 +2676,7 @@ impl Bank {
26662676
sequence: sequence.sequence,
26672677
emitter_chain: 26,
26682678
emitter_address: ACCUMULATOR_EMITTER_ADDRESS,
2669-
payload: acc.serialize(ring_index),
2679+
payload: acc.serialize(self.clock().slot, ACCUMULATOR_RING_SIZE),
26702680
},
26712681
};
26722682

@@ -15142,18 +15152,13 @@ pub(crate) mod tests {
1514215152
true
1514315153
);
1514415154

15145-
// get the timestamp & slot for the message
15146-
let message_time = bank.clock().unix_timestamp;
15147-
let message_slot = bank.clock().slot;
15148-
let ring_index = (message_slot % 10_000) as u32;
15149-
15150-
let mut sequence_tracker = get_acc_sequence_tracker(&bank);
15151-
let message_sequence = sequence_tracker.sequence;
15152-
15153-
// Run accumulator again, the feature is now enabled so account data should contain a
15154-
// wormhole message.
15155+
// The current sequence value will be used in the message when the bank advances, so we snapshot
15156+
// it here before advancing the slot so we can assert the correct sequence is present in the message.
15157+
let sequence_tracker_before_bank_advance = get_acc_sequence_tracker(&bank);
1515515158
bank = new_from_parent(&Arc::new(bank));
1515615159

15160+
// get the timestamp & slot for the message
15161+
let ring_index = (bank.clock().slot % ACCUMULATOR_RING_SIZE as u64) as u32;
1515715162
let wormhole_message_account = get_wormhole_message_account(&bank, ring_index);
1515815163

1515915164
assert_ne!(wormhole_message_account.data().len(), 0);
@@ -15165,7 +15170,8 @@ pub(crate) mod tests {
1516515170
let accumulator_elements = messages.clone().into_iter().sorted_unstable().dedup();
1516615171
let expected_accumulator =
1516715172
MerkleAccumulator::<Keccak160>::from_set(accumulator_elements).unwrap();
15168-
let expected_wormhole_message_payload = expected_accumulator.serialize(ring_index);
15173+
let expected_wormhole_message_payload =
15174+
expected_accumulator.serialize(bank.clock().slot, ACCUMULATOR_RING_SIZE);
1516915175
assert_eq!(
1517015176
wormhole_message.message.payload,
1517115177
expected_wormhole_message_payload
@@ -15177,79 +15183,90 @@ pub(crate) mod tests {
1517715183
consistency_level: 1,
1517815184
vaa_time: 1u32,
1517915185
vaa_signature_account: Pubkey::default().to_bytes(),
15180-
submission_time: message_time as u32,
15186+
submission_time: bank.clock().unix_timestamp as u32,
1518115187
nonce: 0,
15182-
sequence: message_sequence,
15188+
sequence: sequence_tracker_before_bank_advance.sequence, // sequence is incremented after the message is processed
1518315189
emitter_chain: 26,
1518415190
emitter_address: ACCUMULATOR_EMITTER_ADDRESS,
15185-
payload: expected_accumulator.serialize(ring_index),
15191+
payload: expected_wormhole_message_payload,
1518615192
},
1518715193
};
1518815194

15189-
let expected_wormhole_message_data = expected_wormhole_message.try_to_vec().unwrap();
15190-
1519115195
assert_eq!(
1519215196
wormhole_message_account.data().to_vec(),
15193-
expected_wormhole_message_data
15197+
expected_wormhole_message.try_to_vec().unwrap()
1519415198
);
1519515199

1519615200
// verify hashes verify in accumulator
1519715201
for msg in messages {
1519815202
let msg_hash = Keccak160::hashv(&[[0u8].as_ref(), msg]);
15203+
let msg_proof = expected_accumulator.prove(msg).unwrap();
15204+
1519915205
assert!(expected_accumulator.nodes.contains(&msg_hash));
15200-
let msg_proof = expected_accumulator.prove(msg);
15201-
assert!(msg_proof.is_some());
15202-
let msg_proof = msg_proof.unwrap();
1520315206
assert!(expected_accumulator.check(msg_proof, msg));
1520415207
}
1520515208

1520615209
// verify accumulator state account
1520715210
let accumulator_state = get_accumulator_state(&bank, ring_index);
1520815211
let acc_state_magic = &accumulator_state[..4];
15212+
let acc_state_slot = LittleEndian::read_u64(&accumulator_state[4..12]);
15213+
let acc_state_ring_size = LittleEndian::read_u32(&accumulator_state[12..16]);
15214+
1520915215
assert_eq!(acc_state_magic, b"PAS1");
15210-
let mut cursor = std::io::Cursor::new(&accumulator_state[4..]);
15216+
assert_eq!(acc_state_slot, bank.clock().slot);
15217+
assert_eq!(acc_state_ring_size, ACCUMULATOR_RING_SIZE);
15218+
15219+
let mut cursor = std::io::Cursor::new(&accumulator_state[16..]);
1521115220
let num_elems = cursor.read_u32::<LittleEndian>().unwrap();
1521215221
for _ in 0..(num_elems as usize) {
1521315222
let element_len = cursor.read_u32::<LittleEndian>().unwrap();
1521415223
let mut element_data = vec![0u8; element_len as usize];
1521515224
cursor.read_exact(&mut element_data).unwrap();
1521615225

1521715226
let elem_hash = Keccak160::hashv(&[[0u8].as_ref(), element_data.as_slice()]);
15218-
assert!(expected_accumulator.nodes.contains(&elem_hash));
15219-
let elem_proof = expected_accumulator.prove(element_data.as_slice());
15220-
assert!(elem_proof.is_some());
15227+
let elem_proof = expected_accumulator.prove(element_data.as_slice()).unwrap();
1522115228

15222-
let elem_proof = elem_proof.unwrap();
15229+
assert!(expected_accumulator.nodes.contains(&elem_hash));
1522315230
assert!(expected_accumulator.check(elem_proof, element_data.as_slice()));
1522415231
}
1522515232

1522615233
// verify sequence_tracker increments
15227-
sequence_tracker = get_acc_sequence_tracker(&bank);
15228-
assert_eq!(sequence_tracker.sequence, message_sequence + 1);
15234+
assert_eq!(
15235+
get_acc_sequence_tracker(&bank).sequence,
15236+
sequence_tracker_before_bank_advance.sequence + 1
15237+
);
1522915238

1523015239
// verify ring buffer cycles
15231-
let target_slot = bank.slot() + 9998;
15232-
// advance 9998 slots using warp_from_parent since doing large loops
15233-
// with new_from_parent takes a long time
15234-
let warped_bank = Bank::warp_from_parent(&Arc::new(bank), &Pubkey::default(), target_slot);
15240+
let ring_index_before_buffer_cycle =
15241+
(bank.clock().slot % ACCUMULATOR_RING_SIZE as u64) as u32;
15242+
let target_slot = bank.slot() + ACCUMULATOR_RING_SIZE as u64;
15243+
// advance ACCUMULATOR_RING_SIZE slots using warp_from_parent since doing large loops
15244+
// with new_from_parent takes a long time. warp_from_parent results in a bank that is frozen.
15245+
bank = Bank::warp_from_parent(&Arc::new(bank), &Pubkey::default(), target_slot);
15246+
15247+
// accumulator messages should still be the same before looping around
15248+
let ring_index_after_buffer_cycle =
15249+
(bank.clock().slot % ACCUMULATOR_RING_SIZE as u64) as u32;
15250+
assert_eq!(
15251+
ring_index_before_buffer_cycle,
15252+
ring_index_after_buffer_cycle
15253+
);
1523515254

15236-
// advance one more slot
15237-
bank = new_from_parent(&Arc::new(warped_bank));
15255+
let accumulator_state_after_skip =
15256+
get_accumulator_state(&bank, ring_index_after_buffer_cycle);
15257+
assert_eq!(
15258+
&accumulator_state[16..],
15259+
&accumulator_state_after_skip[16..]
15260+
);
1523815261

15239-
// accumulator state should still be the same before looping around
15240-
let accumulator_state_after_skip = get_accumulator_state(&bank, ring_index);
15241-
assert_eq!(&accumulator_state[4..], &accumulator_state_after_skip[4..]);
15262+
// insert new message to make sure the update is written in the right position
15263+
// in the ring buffer and overwrites the existing message
1524215264

15243-
let message_time_1 = bank.clock().unix_timestamp;
15244-
let message_slot_1 = bank.clock().slot;
15245-
let ring_index_1 = (message_slot_1 % 10_000) as u32;
15246-
assert_eq!(ring_index, ring_index_1);
15265+
// advance the bank to unfreeze it (to be able to store accounts). see the comment on warp_from_parent above.
15266+
bank = new_from_parent(&Arc::new(bank));
1524715267

15248-
sequence_tracker = get_acc_sequence_tracker(&bank);
15249-
let message_sequence_1 = sequence_tracker.sequence;
15268+
let wh_sequence_before_acc_update = get_acc_sequence_tracker(&bank).sequence;
1525015269

15251-
// insert new message to verify ring buffer cycles and overwrites old message
15252-
// at ring index
1525315270
let message_0 = vec![1u8; 127];
1525415271
let message_1 = vec![2u8; 127];
1525515272
let message_2 = vec![3u8; 254];
@@ -15265,7 +15282,8 @@ pub(crate) mod tests {
1526515282
// Run accumulator, update clock & other sysvars etc
1526615283
bank = new_from_parent(&Arc::new(bank));
1526715284

15268-
let updated_wormhole_message_account = get_wormhole_message_account(&bank, ring_index_1);
15285+
let ring_index = (bank.clock().slot % ACCUMULATOR_RING_SIZE as u64) as u32;
15286+
let updated_wormhole_message_account = get_wormhole_message_account(&bank, ring_index);
1526915287

1527015288
let updated_wormhole_message =
1527115289
PostedMessageUnreliableData::deserialize(&mut updated_wormhole_message_account.data())
@@ -15283,10 +15301,9 @@ pub(crate) mod tests {
1528315301

1528415302
let expected_accumulator =
1528515303
MerkleAccumulator::<Keccak160>::from_set(updated_accumulator_elements).unwrap();
15286-
let expected_wormhole_message_payload = expected_accumulator.serialize(ring_index);
1528715304
assert_eq!(
1528815305
updated_wormhole_message.message.payload,
15289-
expected_wormhole_message_payload
15306+
expected_accumulator.serialize(bank.clock().slot, ACCUMULATOR_RING_SIZE)
1529015307
);
1529115308

1529215309
let expected_wormhole_message = PostedMessageUnreliableData {
@@ -15295,20 +15312,18 @@ pub(crate) mod tests {
1529515312
consistency_level: 1,
1529615313
vaa_time: 1u32,
1529715314
vaa_signature_account: Pubkey::default().to_bytes(),
15298-
submission_time: message_time_1 as u32,
15315+
submission_time: bank.clock().unix_timestamp as u32,
1529915316
nonce: 0,
15300-
sequence: message_sequence_1,
15317+
sequence: wh_sequence_before_acc_update,
1530115318
emitter_chain: 26,
1530215319
emitter_address: ACCUMULATOR_EMITTER_ADDRESS,
15303-
payload: expected_accumulator.serialize(ring_index_1),
15320+
payload: expected_accumulator.serialize(bank.clock().slot, ACCUMULATOR_RING_SIZE),
1530415321
},
1530515322
};
1530615323

15307-
let expected_wormhole_message_data = expected_wormhole_message.try_to_vec().unwrap();
15308-
1530915324
assert_eq!(
1531015325
updated_wormhole_message_account.data(),
15311-
expected_wormhole_message_data
15326+
expected_wormhole_message.try_to_vec().unwrap()
1531215327
);
1531315328

1531415329
// TODO: Should be done as additional tests.

sdk/program/src/sysvar/accumulator.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@
4848
use crate::sysvar::Sysvar;
4949
pub use {
5050
crate::{account_info::AccountInfo, program_error::ProgramError, slot_history::SlotHistory},
51-
pythnet_sdk::accumulators::merkle::MerkleAccumulator,
52-
pythnet_sdk::hashers::keccak256_160::Keccak160,
51+
pythnet_sdk::{accumulators::merkle::MerkleAccumulator, hashers::keccak256_160::Keccak160},
5352
};
5453

5554
crate::declare_sysvar_id!(

0 commit comments

Comments
 (0)