Skip to content

Commit c5a1d2e

Browse files
Reisenswimricky
authored andcommitted
fixup! pyth: introduce pyth accumulator library
1 parent fde7e90 commit c5a1d2e

File tree

3 files changed

+166
-8
lines changed

3 files changed

+166
-8
lines changed

runtime/src/bank.rs

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,7 +2486,7 @@ impl Bank {
24862486

24872487
/// Loads the Accumulator Sysvar from disk, creating an empty account for it if it does not
24882488
/// 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> {
24902490
from_account(
24912491
&self
24922492
.get_account(&sysvar::accumulator::id())
@@ -2524,7 +2524,6 @@ impl Bank {
25242524

25252525
// Find all accounts owned by the Message Buffer program using get_program_accounts, and
25262526
// extract the account data.
2527-
25282527
let message_buffer_pid = self.env_pubkey_or(
25292528
"MESSAGE_BUFFER_PID",
25302529
Pubkey::new_from_array(MESSAGE_BUFFER_PID),
@@ -2579,15 +2578,12 @@ impl Bank {
25792578
.sorted_unstable()
25802579
.dedup();
25812580

2582-
let pyth_pid = self.env_pubkey_or("PYTH_PID", Pubkey::new_from_array(pythnet::PYTH_PID))?;
2583-
25842581
// We now generate a Proof PDA (Owned by the System Program) to store the resulting Proof
25852582
// Set. The derivation includes the ring buffer index to simulate a ring buffer in order
25862583
// for RPC users to select the correct proof for an associated VAA.
25872584
let (accumulator_account, _) = Pubkey::find_program_address(
25882585
&[
25892586
b"AccumulatorState",
2590-
&pyth_pid.as_ref(),
25912587
&ring_index.to_be_bytes(),
25922588
],
25932589
&solana_sdk::system_program::id(),
@@ -2711,7 +2707,6 @@ impl Bank {
27112707
);
27122708

27132709
self.store_account_and_update_capitalization(&accumulator_sequence_addr, &sequence_account);
2714-
27152710
self.store_account_and_update_capitalization(&message_pda, &message_account);
27162711

27172712
Ok(())
@@ -14995,6 +14990,167 @@ pub(crate) mod tests {
1499514990
);
1499614991
}
1499714992

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+
1499815154
fn poh_estimate_offset(bank: &Bank) -> Duration {
1499915155
let mut epoch_start_slot = bank.epoch_schedule.get_first_slot_in_epoch(bank.epoch());
1500015156
if epoch_start_slot == bank.slot() {

sdk/program/src/sysvar/accumulator.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ use crate::sysvar::Sysvar;
4949
pub use {
5050
crate::{account_info::AccountInfo, program_error::ProgramError, slot_history::SlotHistory},
5151
pythnet_sdk::accumulators::merkle::MerkleAccumulator,
52+
pythnet_sdk::hashers::keccak256_160::Keccak160,
5253
};
5354

5455
crate::declare_sysvar_id!(
5556
"SysvarAccumu1ator11111111111111111111111111",
56-
MerkleAccumulator
57+
MerkleAccumulator<Keccak160>
5758
);
5859

59-
impl Sysvar for MerkleAccumulator {
60+
impl Sysvar for MerkleAccumulator<Keccak160> {
6061
fn size_of() -> usize {
6162
0
6263
}

sdk/program/src/sysvar/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ lazy_static! {
3535
slot_history::id(),
3636
stake_history::id(),
3737
instructions::id(),
38+
accumulator::id(),
3839
];
3940
}
4041

0 commit comments

Comments
 (0)