@@ -2486,7 +2486,7 @@ impl Bank {
2486
2486
2487
2487
/// Loads the Accumulator Sysvar from disk, creating an empty account for it if it does not
2488
2488
/// exist already. See `clock` to see a similar sysvar this is based on.
2489
- pub fn accumulator(&self) -> sysvar::accumulator::MerkleAccumulator {
2489
+ pub fn accumulator(&self) -> sysvar::accumulator::MerkleAccumulator<pythnet_sdk::hashers::keccak256_160::Keccak160> {
2490
2490
from_account(
2491
2491
&self
2492
2492
.get_account(&sysvar::accumulator::id())
@@ -2524,7 +2524,6 @@ impl Bank {
2524
2524
2525
2525
// Find all accounts owned by the Message Buffer program using get_program_accounts, and
2526
2526
// extract the account data.
2527
-
2528
2527
let message_buffer_pid = self.env_pubkey_or(
2529
2528
"MESSAGE_BUFFER_PID",
2530
2529
Pubkey::new_from_array(MESSAGE_BUFFER_PID),
@@ -2579,15 +2578,12 @@ impl Bank {
2579
2578
.sorted_unstable()
2580
2579
.dedup();
2581
2580
2582
- let pyth_pid = self.env_pubkey_or("PYTH_PID", Pubkey::new_from_array(pythnet::PYTH_PID))?;
2583
-
2584
2581
// We now generate a Proof PDA (Owned by the System Program) to store the resulting Proof
2585
2582
// Set. The derivation includes the ring buffer index to simulate a ring buffer in order
2586
2583
// for RPC users to select the correct proof for an associated VAA.
2587
2584
let (accumulator_account, _) = Pubkey::find_program_address(
2588
2585
&[
2589
2586
b"AccumulatorState",
2590
- &pyth_pid.as_ref(),
2591
2587
&ring_index.to_be_bytes(),
2592
2588
],
2593
2589
&solana_sdk::system_program::id(),
@@ -2711,7 +2707,6 @@ impl Bank {
2711
2707
);
2712
2708
2713
2709
self.store_account_and_update_capitalization(&accumulator_sequence_addr, &sequence_account);
2714
-
2715
2710
self.store_account_and_update_capitalization(&message_pda, &message_account);
2716
2711
2717
2712
Ok(())
@@ -14995,6 +14990,167 @@ pub(crate) mod tests {
14995
14990
);
14996
14991
}
14997
14992
14993
+ #[test]
14994
+ fn test_update_accumulator_sysvar() {
14995
+ let leader_pubkey = solana_sdk::pubkey::new_rand();
14996
+ let GenesisConfigInfo {
14997
+ mut genesis_config,
14998
+ ..
14999
+ } = create_genesis_config_with_leader(5, &leader_pubkey, 3);
15000
+ genesis_config
15001
+ .accounts
15002
+ .remove(&feature_set::enable_accumulator_sysvar::id())
15003
+ .unwrap();
15004
+ let slots_in_epoch = 32;
15005
+ genesis_config.epoch_schedule = EpochSchedule::new(slots_in_epoch);
15006
+ let mut bank = Bank::new_for_tests(&genesis_config);
15007
+ // Advance past slot 0, which has special handling.
15008
+ bank = new_from_parent(&Arc::new(bank));
15009
+ bank = new_from_parent(&Arc::new(bank));
15010
+
15011
+ // Create Message Account Bytes
15012
+ //
15013
+ // NOTE: This was serialized by hand, but should be replaced with the pythnet-sdk
15014
+ // serializer once implemented. This contains a MessageBuffer with two 127 byte long
15015
+ // messages: [1u8; 127] and [2u8; 127].
15016
+ let preimage = b"account:MessageBuffer";
15017
+ let mut sighash = [0u8; 8];
15018
+ sighash.copy_from_slice(&hashv(&[preimage]).to_bytes()[..8]);
15019
+
15020
+ let mut message_buffer_bytes = vec![];
15021
+ message_buffer_bytes.extend_from_slice(&sighash);
15022
+
15023
+ #[rustfmt::skip]
15024
+ message_buffer_bytes.extend_from_slice(&[
15025
+ // Bump
15026
+ 0,
15027
+ // Version
15028
+ 1,
15029
+ // Header Length
15030
+ 10, 2,
15031
+ // Offsets
15032
+ 127, 0,
15033
+ 254, 0,
15034
+
15035
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15036
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15037
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15038
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15039
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15041
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15042
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15043
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15044
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15045
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15046
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15047
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15048
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15049
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15050
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15051
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15052
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
15053
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
15054
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
15055
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
15056
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
15057
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
15058
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
15059
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
15060
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
15061
+ 2, 2, 2, 2, 2, 2
15062
+ ]);
15063
+
15064
+ // Create a Message account.
15065
+ let price_message_key = keypair_from_seed(&[1u8; 32]).unwrap();
15066
+ let mut price_message_account = bank
15067
+ .get_account(&price_message_key.pubkey())
15068
+ .unwrap_or_default();
15069
+ price_message_account.set_lamports(1_000_000_000);
15070
+ price_message_account.set_owner(Pubkey::new_from_array(pythnet_sdk::MESSAGE_BUFFER_PID));
15071
+ price_message_account.set_data(message_buffer_bytes);
15072
+
15073
+ // Store Message account so the accumulator sysvar updater can find it.
15074
+ bank.store_account(
15075
+ &price_message_key.pubkey(),
15076
+ &price_message_account,
15077
+ );
15078
+
15079
+ // Derive the Wormhole Message Account that will be generated by the sysvar updater.
15080
+ let (wormhole_message_pubkey, _bump) = Pubkey::find_program_address(
15081
+ &[b"AccumulatorMessage", &(bank.clock().slot as u32).to_be_bytes()],
15082
+ &Pubkey::new_from_array(pythnet_sdk::pythnet::WORMHOLE_PID),
15083
+ );
15084
+
15085
+ // Account Data should be empty at this point. Check account data is [].
15086
+ let wormhole_message_account = bank
15087
+ .get_account(&wormhole_message_pubkey)
15088
+ .unwrap_or_default();
15089
+ assert_eq!(wormhole_message_account.data().len(), 0);
15090
+
15091
+ // Run accumulator, the feature is disabled so account data should still be empty. Check
15092
+ // account data is still [].
15093
+ bank.update_accumulator();
15094
+ let wormhole_message_account = bank
15095
+ .get_account(&wormhole_message_pubkey)
15096
+ .unwrap_or_default();
15097
+ assert_eq!(wormhole_message_account.data().len(), 0);
15098
+ assert_eq!(bank
15099
+ .feature_set
15100
+ .is_active(&feature_set::enable_accumulator_sysvar::id()), false);
15101
+
15102
+ // Enable Accumulator Feature (42 = lamport balance, and the meaning of the universe).
15103
+ let feature_id = feature_set::enable_accumulator_sysvar::id();
15104
+ let feature = Feature { activated_at: Some(30) };
15105
+ bank.store_account(&feature_id, &feature::create_account(&feature, 42));
15106
+ bank.compute_active_feature_set(true);
15107
+ for _ in 0..2*slots_in_epoch {
15108
+ bank = new_from_parent(&Arc::new(bank));
15109
+ }
15110
+
15111
+ // Feature should now be enabled on the new bank.
15112
+ assert_eq!(bank
15113
+ .feature_set
15114
+ .is_active(&feature_set::enable_accumulator_sysvar::id()), true);
15115
+
15116
+ // Run accumulator again, the feature is now enabled so account data should contain a
15117
+ // wormhole message.
15118
+ let (wormhole_message_pubkey, _bump) = Pubkey::find_program_address(
15119
+ &[b"AccumulatorMessage", &(bank.clock().slot as u32).to_be_bytes()],
15120
+ &Pubkey::new_from_array(pythnet_sdk::pythnet::WORMHOLE_PID),
15121
+ );
15122
+
15123
+ bank.update_accumulator();
15124
+
15125
+ let wormhole_message_account = bank
15126
+ .get_account(&wormhole_message_pubkey)
15127
+ .unwrap_or_default();
15128
+
15129
+ assert_ne!(wormhole_message_account.data().len(), 0);
15130
+ assert_eq!(wormhole_message_account.data(), vec![
15131
+ 109, 115, 117, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15132
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 205, 169, 100, 100, 0, 0, 0, 0, 35, 0, 0, 0,
15133
+ 0, 0, 0, 0, 26, 0, 225, 1, 250, 237, 172, 88, 81, 227, 43, 155, 35, 181, 249, 65, 26,
15134
+ 140, 43, 172, 74, 174, 62, 212, 221, 123, 129, 29, 209, 167, 46, 164, 170, 113, 29, 0,
15135
+ 0, 0, 65, 85, 87, 86, 0, 0, 0, 0, 66, 81, 247, 26, 153, 42, 255, 65, 58, 116, 230, 225,
15136
+ 135, 123, 152, 229, 157, 49, 120, 78, 52
15137
+ ]);
15138
+
15139
+ // TODO: Should be done here.
15140
+ //
15141
+ // 1. Deserialize the above structure that contains merkle data.
15142
+ // 2. Verify the hashes verify in the merkle tree.
15143
+ // 3. Verify the AccumulatorState account.
15144
+ // 4. Verify the wormhole sequence is incrementing.
15145
+ // 5. Verify the ring buffer actually cycles (advance slot 10_000 times)
15146
+ //
15147
+ // TODO: Should be done as additional tests.
15148
+ //
15149
+ // 1. Verify the accumulator state stays intact after the bank is advanced.
15150
+ // 2. Intentionally add corrupted accounts that do not appear in the accumulator.
15151
+ // 3. Check if message offset is > message size to prevent validator crash.
15152
+ }
15153
+
14998
15154
fn poh_estimate_offset(bank: &Bank) -> Duration {
14999
15155
let mut epoch_start_slot = bank.epoch_schedule.get_first_slot_in_epoch(bank.epoch());
15000
15156
if epoch_start_slot == bank.slot() {
0 commit comments