@@ -14,14 +14,11 @@ use {
1414 byteorder:: { ByteOrder , LittleEndian , ReadBytesExt } ,
1515 itertools:: Itertools ,
1616 pyth_oracle:: {
17- solana_program:: account_info:: AccountInfo , PriceAccount , PriceAccountFlags , PythAccount ,
17+ solana_program:: { account_info:: AccountInfo , message } , PriceAccount , PriceAccountFlags , PythAccount ,
1818 PythOracleSerialize ,
1919 } ,
2020 pythnet_sdk:: {
21- accumulators:: { merkle:: MerkleAccumulator , Accumulator } ,
22- hashers:: { keccak256_160:: Keccak160 , Hasher } ,
23- wormhole:: { AccumulatorSequenceTracker , MessageData , PostedMessageUnreliableData } ,
24- ACCUMULATOR_EMITTER_ADDRESS ,
21+ accumulators:: { merkle:: MerkleAccumulator , Accumulator } , hashers:: { keccak256_160:: Keccak160 , Hasher } , publisher_stake_caps:: StakeCapParameters , wormhole:: { AccumulatorSequenceTracker , MessageData , PostedMessageUnreliableData } , ACCUMULATOR_EMITTER_ADDRESS
2522 } ,
2623 solana_sdk:: {
2724 account:: { AccountSharedData , ReadableAccount , WritableAccount } ,
@@ -694,119 +691,76 @@ fn test_accumulator_v2_mixed() {
694691 test_accumulator_v2 ( [ true , true , false , false ] ) ;
695692}
696693
697- fn test_accumulator_v2 ( generate_buffers : [ bool ; 4 ] ) {
698- let leader_pubkey = solana_sdk:: pubkey:: new_rand ( ) ;
699- let GenesisConfigInfo {
700- mut genesis_config, ..
701- } = create_genesis_config_with_leader ( 5 , & leader_pubkey, 3 ) ;
702-
703- // Set epoch length to 32 so we can advance epochs quickly. We also skip past slot 0 here
704- // due to slot 0 having special handling.
705- let slots_in_epoch = 32 ;
706- genesis_config. epoch_schedule = EpochSchedule :: new ( slots_in_epoch) ;
707- let mut bank = create_new_bank_for_tests_with_index ( & genesis_config) ;
694+ fn generate_price ( bank : & Bank , seeds : & [ u8 ] , generate_buffers : bool , publishers : & [ Pubkey ] ) -> ( Pubkey , Vec < Vec < u8 > > ) {
695+ let ( price_feed_key, _bump) = Pubkey :: find_program_address ( & [ seeds] , & ORACLE_PID ) ;
696+ let mut price_feed_account =
697+ AccountSharedData :: new ( 42 , size_of :: < PriceAccount > ( ) , & ORACLE_PID ) ;
698+
699+ let messages = {
700+ let price_feed_info_key = & price_feed_key. to_bytes ( ) . into ( ) ;
701+ let price_feed_info_lamports = & mut 0 ;
702+ let price_feed_info_owner = & ORACLE_PID . to_bytes ( ) . into ( ) ;
703+ let price_feed_info_data = price_feed_account. data_mut ( ) ;
704+ let price_feed_info = AccountInfo :: new (
705+ price_feed_info_key,
706+ false ,
707+ true ,
708+ price_feed_info_lamports,
709+ price_feed_info_data,
710+ price_feed_info_owner,
711+ false ,
712+ Epoch :: default ( ) ,
713+ ) ;
708714
709- let generate_price = |seeds, generate_buffers : bool | {
710- let ( price_feed_key, _bump) = Pubkey :: find_program_address ( & [ seeds] , & ORACLE_PID ) ;
711- let mut price_feed_account =
712- AccountSharedData :: new ( 42 , size_of :: < PriceAccount > ( ) , & ORACLE_PID ) ;
713-
714- let messages = {
715- let price_feed_info_key = & price_feed_key. to_bytes ( ) . into ( ) ;
716- let price_feed_info_lamports = & mut 0 ;
717- let price_feed_info_owner = & ORACLE_PID . to_bytes ( ) . into ( ) ;
718- let price_feed_info_data = price_feed_account. data_mut ( ) ;
719- let price_feed_info = AccountInfo :: new (
720- price_feed_info_key,
721- false ,
722- true ,
723- price_feed_info_lamports,
724- price_feed_info_data,
725- price_feed_info_owner,
726- false ,
727- Epoch :: default ( ) ,
715+ let mut price_account = PriceAccount :: initialize ( & price_feed_info, 0 ) . unwrap ( ) ;
716+ if !generate_buffers {
717+ price_account. flags . insert (
718+ PriceAccountFlags :: ACCUMULATOR_V2 | PriceAccountFlags :: MESSAGE_BUFFER_CLEARED ,
728719 ) ;
729-
730- let mut price_account = PriceAccount :: initialize ( & price_feed_info, 0 ) . unwrap ( ) ;
731- if !generate_buffers {
732- price_account. flags . insert (
733- PriceAccountFlags :: ACCUMULATOR_V2 | PriceAccountFlags :: MESSAGE_BUFFER_CLEARED ,
734- ) ;
735- }
736-
737- vec ! [
738- price_account
739- . as_price_feed_message( & price_feed_key. to_bytes( ) . into( ) )
740- . to_bytes( ) ,
741- price_account
742- . as_twap_message( & price_feed_key. to_bytes( ) . into( ) )
743- . to_bytes( ) ,
744- ]
745- } ;
746-
747- bank. store_account ( & price_feed_key, & price_feed_account) ;
748-
749- if generate_buffers {
750- let message_buffer_bytes = create_message_buffer_bytes ( messages. clone ( ) ) ;
751-
752- let mut seed = vec ! [ 1 ; 32 ] ;
753- seed[ ..seeds. len ( ) ] . copy_from_slice ( seeds) ;
754- // Create a Message account.
755- let price_message_key = keypair_from_seed ( & seed) . unwrap ( ) ;
756- let mut price_message_account = bank
757- . get_account ( & price_message_key. pubkey ( ) )
758- . unwrap_or_default ( ) ;
759-
760- price_message_account. set_lamports ( 1_000_000_000 ) ;
761- price_message_account
762- . set_owner ( Pubkey :: new_from_array ( pythnet_sdk:: MESSAGE_BUFFER_PID ) ) ;
763- price_message_account. set_data ( message_buffer_bytes) ;
764-
765- // Store Message account so the accumulator sysvar updater can find it.
766- bank. store_account ( & price_message_key. pubkey ( ) , & price_message_account) ;
720+ }
721+ price_account. num_ = publishers. len ( ) as u32 ;
722+ for ( i, publisher) in publishers. into_iter ( ) . enumerate ( ) {
723+ price_account. comp_ [ i] . pub_ = publisher. to_bytes ( ) . into ( ) ;
767724 }
768725
769- ( price_feed_key, messages)
726+ vec ! [
727+ price_account
728+ . as_price_feed_message( & price_feed_key. to_bytes( ) . into( ) )
729+ . to_bytes( ) ,
730+ price_account
731+ . as_twap_message( & price_feed_key. to_bytes( ) . into( ) )
732+ . to_bytes( ) ,
733+ ]
770734 } ;
771735
772- assert ! ( bank
773- . feature_set
774- . is_active( & feature_set:: enable_accumulator_sysvar:: id( ) ) ) ;
775- assert ! ( bank
776- . feature_set
777- . is_active( & feature_set:: move_accumulator_to_end_of_block:: id( ) ) ) ;
778- assert ! ( bank
779- . feature_set
780- . is_active( & feature_set:: undo_move_accumulator_to_end_of_block:: id( ) ) ) ;
781- assert ! ( bank
782- . feature_set
783- . is_active( & feature_set:: redo_move_accumulator_to_end_of_block:: id( ) ) ) ;
784-
785- let prices_with_messages = [
786- generate_price ( b"seeds_1" , generate_buffers[ 0 ] ) ,
787- generate_price ( b"seeds_2" , generate_buffers[ 1 ] ) ,
788- generate_price ( b"seeds_3" , generate_buffers[ 2 ] ) ,
789- generate_price ( b"seeds_4" , generate_buffers[ 3 ] ) ,
790- ] ;
791-
792- bank = new_from_parent ( & Arc :: new ( bank) ) ; // Advance slot 1.
793- bank = new_from_parent ( & Arc :: new ( bank) ) ; // Advance slot 2.
736+ bank. store_account ( & price_feed_key, & price_feed_account) ;
794737
795- let messages = prices_with_messages
796- . iter ( )
797- . flat_map ( |( _, messages) | messages)
798- . map ( |message| & message[ ..] )
799- . sorted_unstable ( )
800- . dedup ( )
801- . collect :: < Vec < _ > > ( ) ;
802- assert_eq ! ( messages. len( ) , 8 ) ;
738+ if generate_buffers {
739+ let message_buffer_bytes = create_message_buffer_bytes ( messages. clone ( ) ) ;
740+
741+ let mut seed = vec ! [ 1 ; 32 ] ;
742+ seed[ ..seeds. len ( ) ] . copy_from_slice ( seeds) ;
743+ // Create a Message account.
744+ let price_message_key = keypair_from_seed ( & seed) . unwrap ( ) ;
745+ let mut price_message_account = bank
746+ . get_account ( & price_message_key. pubkey ( ) )
747+ . unwrap_or_default ( ) ;
748+
749+ price_message_account. set_lamports ( 1_000_000_000 ) ;
750+ price_message_account
751+ . set_owner ( Pubkey :: new_from_array ( pythnet_sdk:: MESSAGE_BUFFER_PID ) ) ;
752+ price_message_account. set_data ( message_buffer_bytes) ;
753+
754+ // Store Message account so the accumulator sysvar updater can find it.
755+ bank. store_account ( & price_message_key. pubkey ( ) , & price_message_account) ;
756+
757+ }
803758
804- // Trigger Aggregation. We freeze instead of new_from_parent so
805- // we can keep access to the bank.
806- let sequence_tracker_before_bank_freeze = get_acc_sequence_tracker ( & bank) ;
807- bank. freeze ( ) ;
759+ ( price_feed_key, messages)
760+ }
808761
809- // Get the wormhole message generated by freezed. We don't need
762+ fn check_accumulator_state_matches_messages ( bank : & Bank , sequence_tracker_before_bank_freeze : & AccumulatorSequenceTracker , messages : & [ & [ u8 ] ] ) {
763+ // Get the wormhole message generated by freezed. We don't need
810764 // to offset the ring index as our test is always below 10K slots.
811765 let wormhole_message_account = get_wormhole_message_account ( & bank, bank. slot ( ) as u32 ) ;
812766 assert_ne ! ( wormhole_message_account. data( ) . len( ) , 0 ) ;
@@ -883,6 +837,176 @@ fn test_accumulator_v2(generate_buffers: [bool; 4]) {
883837 sequence_tracker_before_bank_freeze. sequence + 1
884838 ) ;
885839}
840+
841+ fn test_accumulator_v2 ( generate_buffers : [ bool ; 4 ] ) {
842+ let leader_pubkey = solana_sdk:: pubkey:: new_rand ( ) ;
843+ let GenesisConfigInfo {
844+ mut genesis_config, ..
845+ } = create_genesis_config_with_leader ( 5 , & leader_pubkey, 3 ) ;
846+
847+ genesis_config
848+ . accounts
849+ . remove ( & feature_set:: add_publisher_stake_caps_to_the_accumulator:: id ( ) )
850+ . unwrap ( ) ;
851+
852+ // Set epoch length to 32 so we can advance epochs quickly. We also skip past slot 0 here
853+ // due to slot 0 having special handling.
854+ let slots_in_epoch = 32 ;
855+ genesis_config. epoch_schedule = EpochSchedule :: new ( slots_in_epoch) ;
856+ let mut bank = create_new_bank_for_tests_with_index ( & genesis_config) ;
857+
858+ assert ! ( bank
859+ . feature_set
860+ . is_active( & feature_set:: enable_accumulator_sysvar:: id( ) ) ) ;
861+ assert ! ( bank
862+ . feature_set
863+ . is_active( & feature_set:: move_accumulator_to_end_of_block:: id( ) ) ) ;
864+ assert ! ( bank
865+ . feature_set
866+ . is_active( & feature_set:: undo_move_accumulator_to_end_of_block:: id( ) ) ) ;
867+ assert ! ( bank
868+ . feature_set
869+ . is_active( & feature_set:: redo_move_accumulator_to_end_of_block:: id( ) ) ) ;
870+
871+ let prices_with_messages = [
872+ generate_price ( & bank, b"seeds_1" , generate_buffers[ 0 ] , & [ ] ) ,
873+ generate_price ( & bank, b"seeds_2" , generate_buffers[ 1 ] , & [ ] ) ,
874+ generate_price ( & bank, b"seeds_3" , generate_buffers[ 2 ] , & [ ] ) ,
875+ generate_price ( & bank, b"seeds_4" , generate_buffers[ 3 ] , & [ ] )
876+ ] ;
877+
878+ bank = new_from_parent ( & Arc :: new ( bank) ) ; // Advance slot 1.
879+ bank = new_from_parent ( & Arc :: new ( bank) ) ; // Advance slot 2.
880+
881+ let messages = prices_with_messages
882+ . iter ( )
883+ . flat_map ( |( _, messages) | messages)
884+ . map ( |message| & message[ ..] )
885+ . sorted_unstable ( )
886+ . dedup ( )
887+ . collect :: < Vec < _ > > ( ) ;
888+ assert_eq ! ( messages. len( ) , 8 ) ;
889+
890+ // Trigger Aggregation. We freeze instead of new_from_parent so
891+ // we can keep access to the bank.
892+ let sequence_tracker_before_bank_freeze = get_acc_sequence_tracker ( & bank) ;
893+ bank. freeze ( ) ;
894+
895+ check_accumulator_state_matches_messages ( & bank, & sequence_tracker_before_bank_freeze, & messages) ;
896+ }
897+
898+ #[ test]
899+ fn test_publisher_stake_caps ( ) {
900+ let leader_pubkey = solana_sdk:: pubkey:: new_rand ( ) ;
901+ let GenesisConfigInfo {
902+ mut genesis_config, ..
903+ } = create_genesis_config_with_leader ( 5 , & leader_pubkey, 3 ) ;
904+
905+ genesis_config
906+ . accounts
907+ . remove ( & feature_set:: add_publisher_stake_caps_to_the_accumulator:: id ( ) )
908+ . unwrap ( ) ;
909+
910+ // Set epoch length to 32 so we can advance epochs quickly. We also skip past slot 0 here
911+ // due to slot 0 having special handling.
912+ let slots_in_epoch = 32 ;
913+ genesis_config. epoch_schedule = EpochSchedule :: new ( slots_in_epoch) ;
914+ let mut bank = create_new_bank_for_tests_with_index ( & genesis_config) ;
915+
916+ assert ! ( bank
917+ . feature_set
918+ . is_active( & feature_set:: enable_accumulator_sysvar:: id( ) ) ) ;
919+ assert ! ( bank
920+ . feature_set
921+ . is_active( & feature_set:: move_accumulator_to_end_of_block:: id( ) ) ) ;
922+ assert ! ( bank
923+ . feature_set
924+ . is_active( & feature_set:: undo_move_accumulator_to_end_of_block:: id( ) ) ) ;
925+ assert ! ( bank
926+ . feature_set
927+ . is_active( & feature_set:: redo_move_accumulator_to_end_of_block:: id( ) ) ) ;
928+
929+ let new_m = 1_000_000_000_000 ;
930+ let new_z = 3 ;
931+
932+ let mut publishers_with_expected_caps : [ ( Pubkey , u64 , u64 ) ; 4 ] = [
933+ ( solana_sdk:: pubkey:: new_rand ( ) , StakeCapParameters :: default ( ) . m * 5 / 4 , new_m / 3 + new_m / 4 ) ,
934+ ( solana_sdk:: pubkey:: new_rand ( ) , StakeCapParameters :: default ( ) . m * 3 / 4 , new_m / 3 + new_m / 4 ) ,
935+ ( solana_sdk:: pubkey:: new_rand ( ) , StakeCapParameters :: default ( ) . m * 3 / 4 , new_m / 3 + new_m / 4 ) ,
936+ ( solana_sdk:: pubkey:: new_rand ( ) , StakeCapParameters :: default ( ) . m * 5 / 4 , new_m / 3 + new_m / 4 )
937+ ] ;
938+
939+ let expected_caps = [ [
940+ StakeCapParameters :: default ( ) . m * 5 / 4 ,
941+ StakeCapParameters :: default ( ) . m * 3 / 4 ,
942+ StakeCapParameters :: default ( ) . m * 2 / 4 ,
943+ StakeCapParameters :: default ( ) . m * 1 / 4 ,
944+ ] , [ new_m / 3 + new_m / 4 , new_m / 3 + new_m / 4 , new_m / 3 + new_m / 4 , new_m / 3 + new_m / 4 ] ] ;
945+
946+ let prices_with_messages = [
947+ generate_price ( & bank, b"seeds_1" , false , & [ publishers_with_expected_caps[ 0 ] . 0 ] ) ,
948+ generate_price ( & bank, b"seeds_2" , false , & [ publishers_with_expected_caps[ 1 ] . 0 , publishers_with_expected_caps[ 2 ] . 0 ] ) ,
949+ generate_price ( & bank, b"seeds_3" , true , & [ publishers_with_expected_caps[ 3 ] . 0 ] ) ,
950+ generate_price ( & bank, b"seeds_4" , true , & [ publishers_with_expected_caps[ 3 ] . 0 , publishers_with_expected_caps[ 1 ] . 0 , publishers_with_expected_caps[ 0 ] . 0 , publishers_with_expected_caps[ 2 ] . 0 ] )
951+ ] ;
952+
953+ publishers_with_expected_caps. sort_by_key ( |( pk, _, _) | * pk) ;
954+
955+ bank = new_from_parent ( & Arc :: new ( bank) ) ; // Advance slot 1.
956+ bank = new_from_parent ( & Arc :: new ( bank) ) ; // Advance slot 2.
957+
958+
959+ let mut messages = prices_with_messages
960+ . iter ( )
961+ . flat_map ( |( _, messages) | messages)
962+ . map ( |message| & message[ ..] )
963+ . sorted_unstable ( )
964+ . dedup ( )
965+ . collect :: < Vec < _ > > ( ) ;
966+ assert_eq ! ( messages. len( ) , 8 ) ;
967+
968+ let feature_id = feature_set:: add_publisher_stake_caps_to_the_accumulator:: id ( ) ;
969+ let feature = Feature {
970+ activated_at : Some ( 30 ) ,
971+ } ;
972+ bank. store_account ( & feature_id, & feature:: create_account ( & feature, 42 ) ) ;
973+
974+ bank. compute_active_feature_set ( true ) ;
975+
976+ // Trigger Aggregation. We freeze instead of new_from_parent so
977+ // we can keep access to the bank.
978+ let sequence_tracker_before_bank_freeze = get_acc_sequence_tracker ( & bank) ;
979+ bank. freeze ( ) ;
980+
981+ check_accumulator_state_matches_messages ( & bank, & sequence_tracker_before_bank_freeze, & messages) ;
982+
983+ // Enable Publisher Stake Caps
984+
985+ for _ in 0 ..slots_in_epoch {
986+ bank = new_from_parent ( & Arc :: new ( bank) ) ;
987+ }
988+
989+ let publisher_caps_message = {
990+ let mut result = vec ! [ ] ;
991+ result. extend_from_slice ( & bank. unix_timestamp_from_genesis ( ) . to_be_bytes ( ) ) ;
992+ result. extend_from_slice ( & 4u16 . to_be_bytes ( ) ) ;
993+ for ( pk, m, _) in publishers_with_expected_caps {
994+ result. extend_from_slice ( & pk. to_bytes ( ) ) ;
995+ result. extend_from_slice ( & m. to_be_bytes ( ) ) ; }
996+ result
997+ } ;
998+
999+
1000+ messages. push ( & publisher_caps_message) ;
1001+
1002+ let sequence_tracker_before_bank_freeze = get_acc_sequence_tracker ( & bank) ;
1003+ bank. freeze ( ) ;
1004+ check_accumulator_state_matches_messages ( & bank, & sequence_tracker_before_bank_freeze, & messages) ;
1005+
1006+
1007+ }
1008+
1009+
8861010#[ test]
8871011fn test_get_accumulator_keys ( ) {
8881012 use pythnet_sdk:: { pythnet, ACCUMULATOR_EMITTER_ADDRESS , MESSAGE_BUFFER_PID } ;
0 commit comments