@@ -669,6 +669,295 @@ fn test_update_accumulator_end_of_block() {
669669 ) ;
670670}
671671
672+ // This test will
673+ #[ test]
674+ fn test_accumulator_v2 ( ) {
675+ let leader_pubkey = solana_sdk:: pubkey:: new_rand ( ) ;
676+ let GenesisConfigInfo {
677+ mut genesis_config, ..
678+ } = create_genesis_config_with_leader ( 5 , & leader_pubkey, 3 ) ;
679+
680+ // Set epoch length to 32 so we can advance epochs quickly. We also skip past slot 0 here
681+ // due to slot 0 having special handling.
682+ let slots_in_epoch = 32 ;
683+ genesis_config. epoch_schedule = EpochSchedule :: new ( slots_in_epoch) ;
684+ let mut bank = Bank :: new_for_tests ( & genesis_config) ;
685+ bank = new_from_parent ( & Arc :: new ( bank) ) ;
686+ bank = new_from_parent ( & Arc :: new ( bank) ) ;
687+
688+ let generate_price = |seeds, generate_buffers| {
689+ let ( price_feed_key, _bump) = Pubkey :: find_program_address ( & [ seeds] , & ORACLE_PUBKEY ) ;
690+ let mut price_feed_account =
691+ AccountSharedData :: new ( 42 , size_of :: < PriceAccount > ( ) , & ORACLE_PUBKEY ) ;
692+
693+ let price_account = PriceAccount :: initialize (
694+ & AccountInfo :: new (
695+ & price_feed_key. to_bytes ( ) . into ( ) ,
696+ false ,
697+ true ,
698+ & mut 0 ,
699+ & mut price_feed_account. data_mut ( ) ,
700+ & ORACLE_PUBKEY . to_bytes ( ) . into ( ) ,
701+ false ,
702+ Epoch :: default ( ) ,
703+ ) ,
704+ 0 ,
705+ )
706+ . unwrap ( ) ;
707+
708+ bank. store_account ( & price_feed_key, & price_feed_account) ;
709+
710+ if generate_buffers {
711+ // Insert into message buffer in reverse order to test that accumulator
712+ // sorts first.
713+ let message_0 = vec ! [ 1u8 ; 127 ] ; // Price Account
714+ let message_1 = vec ! [ 2u8 ; 127 ] ; // Twap
715+ let messages = vec ! [ message_1, message_0] ;
716+ let message_buffer_bytes = create_message_buffer_bytes ( messages. clone ( ) ) ;
717+
718+ // Create a Message account.
719+ let price_message_key = keypair_from_seed ( & [ 1u8 ; 32 ] ) . unwrap ( ) ;
720+ let mut price_message_account = bank
721+ . get_account ( & price_message_key. pubkey ( ) )
722+ . unwrap_or_default ( ) ;
723+ price_message_account. set_lamports ( 1_000_000_000 ) ;
724+ price_message_account
725+ . set_owner ( Pubkey :: new_from_array ( pythnet_sdk:: MESSAGE_BUFFER_PID ) ) ;
726+ price_message_account. set_data ( message_buffer_bytes) ;
727+
728+ // Store Message account so the accumulator sysvar updater can find it.
729+ bank. store_account ( & price_message_key. pubkey ( ) , & price_message_account) ;
730+ }
731+ } ;
732+
733+ // TODO: New test functionality here.
734+ // 1. Create Price Feed Accounts owned by ORACLE_PUBKEY
735+ // 2. Populate Price Feed Accounts
736+ // 3. Call update_v2()
737+ // - Cases:
738+ // - No V1 Messages, Only Price Accounts with no V2
739+ // - No V1 Messages, Some Price Accounts with no V2
740+ // - Some V1 Messages, No Price Accounts with no V2
741+ // - Some V1 Messages, Some Price Accounts with no V2
742+ // - Simulate PriceUpdate that WOULD trigger a real V1 aggregate before End of Slot
743+ // - Simulate PriceUpdate that doesn't trigger a real V1 aggregate, only V2.
744+
745+ // Case 1:
746+ {
747+ let price_pubkeys = [
748+ generate_price ( & [ "seeds_1" ] , false ) ,
749+ generate_price ( & [ "seeds_2" ] , false ) ,
750+ generate_price ( & [ "seeds_3" ] , false ) ,
751+ generate_price ( & [ "seeds_4" ] , false ) ,
752+ ] ;
753+ }
754+
755+ // Derive the Wormhole Message Account that will be generated by the sysvar updater.
756+ let ( wormhole_message_pubkey, _bump) = Pubkey :: find_program_address (
757+ & [ b"AccumulatorMessage" , & ( bank. slot ( ) as u32 ) . to_be_bytes ( ) ] ,
758+ & Pubkey :: new_from_array ( pythnet_sdk:: pythnet:: WORMHOLE_PID ) ,
759+ ) ;
760+
761+ // Account Data should be empty at this point. Check account data is [].
762+ let wormhole_message_account = bank
763+ . get_account ( & wormhole_message_pubkey)
764+ . unwrap_or_default ( ) ;
765+ assert_eq ! ( wormhole_message_account. data( ) . len( ) , 0 ) ;
766+
767+ // Run accumulator by creating a new bank from parent, the feature is
768+ // disabled so account data should still be empty. Check account data is
769+ // still [].
770+ bank = new_from_parent ( & Arc :: new ( bank) ) ;
771+
772+ assert_eq ! (
773+ bank. feature_set
774+ . is_active( & feature_set:: enable_accumulator_sysvar:: id( ) ) ,
775+ false
776+ ) ;
777+ assert_eq ! (
778+ bank. feature_set
779+ . is_active( & feature_set:: move_accumulator_to_end_of_block:: id( ) ) ,
780+ false
781+ ) ;
782+
783+ let wormhole_message_account = bank
784+ . get_account ( & wormhole_message_pubkey)
785+ . unwrap_or_default ( ) ;
786+ assert_eq ! ( wormhole_message_account. data( ) . len( ) , 0 ) ;
787+
788+ bank. compute_active_feature_set ( true ) ;
789+ for _ in 0 ..slots_in_epoch {
790+ bank = new_from_parent ( & Arc :: new ( bank) ) ;
791+ }
792+
793+ // The current sequence value will be used in the message when the bank advances, so we snapshot
794+ // it here before freezing the bank so we can assert the correct sequence is present in the message.
795+ let sequence_tracker_before_bank_freeze = get_acc_sequence_tracker ( & bank) ;
796+ // Freeze the bank to make sure accumulator is updated
797+ bank. freeze ( ) ;
798+
799+ // get the timestamp & slot for the message
800+ let ring_index = ( bank. slot ( ) % ACCUMULATOR_RING_SIZE as u64 ) as u32 ;
801+ let wormhole_message_account = get_wormhole_message_account ( & bank, ring_index) ;
802+
803+ assert_ne ! ( wormhole_message_account. data( ) . len( ) , 0 ) ;
804+
805+ let wormhole_message =
806+ PostedMessageUnreliableData :: deserialize ( & mut wormhole_message_account. data ( ) ) . unwrap ( ) ;
807+
808+ let messages = messages. iter ( ) . map ( |m| m. as_slice ( ) ) . collect :: < Vec < _ > > ( ) ;
809+ let accumulator_elements = messages. clone ( ) . into_iter ( ) . sorted_unstable ( ) . dedup ( ) ;
810+ let expected_accumulator =
811+ MerkleAccumulator :: < Keccak160 > :: from_set ( accumulator_elements) . unwrap ( ) ;
812+ let expected_wormhole_message_payload =
813+ expected_accumulator. serialize ( bank. slot ( ) , ACCUMULATOR_RING_SIZE ) ;
814+ assert_eq ! (
815+ wormhole_message. message. payload,
816+ expected_wormhole_message_payload
817+ ) ;
818+
819+ let expected_wormhole_message = PostedMessageUnreliableData {
820+ message : MessageData {
821+ vaa_version : 1 ,
822+ consistency_level : 1 ,
823+ submission_time : bank. clock ( ) . unix_timestamp as u32 ,
824+ sequence : sequence_tracker_before_bank_freeze. sequence , // sequence is incremented after the message is processed
825+ emitter_chain : 26 ,
826+ emitter_address : ACCUMULATOR_EMITTER_ADDRESS ,
827+ payload : expected_wormhole_message_payload,
828+ ..Default :: default ( )
829+ } ,
830+ } ;
831+
832+ assert_eq ! (
833+ wormhole_message_account. data( ) . to_vec( ) ,
834+ expected_wormhole_message. try_to_vec( ) . unwrap( )
835+ ) ;
836+
837+ // verify hashes verify in accumulator
838+ for msg in messages {
839+ let msg_hash = Keccak160 :: hashv ( & [ [ 0u8 ] . as_ref ( ) , msg] ) ;
840+ let msg_proof = expected_accumulator. prove ( msg) . unwrap ( ) ;
841+
842+ assert ! ( expected_accumulator. nodes. contains( & msg_hash) ) ;
843+ assert ! ( expected_accumulator. check( msg_proof, msg) ) ;
844+ }
845+
846+ // verify accumulator state account
847+ let accumulator_state = get_accumulator_state ( & bank, ring_index) ;
848+ let acc_state_magic = & accumulator_state[ ..4 ] ;
849+ let acc_state_slot = LittleEndian :: read_u64 ( & accumulator_state[ 4 ..12 ] ) ;
850+ let acc_state_ring_size = LittleEndian :: read_u32 ( & accumulator_state[ 12 ..16 ] ) ;
851+
852+ assert_eq ! ( acc_state_magic, b"PAS1" ) ;
853+ assert_eq ! ( acc_state_slot, bank. slot( ) ) ;
854+ assert_eq ! ( acc_state_ring_size, ACCUMULATOR_RING_SIZE ) ;
855+
856+ let mut cursor = std:: io:: Cursor :: new ( & accumulator_state[ 16 ..] ) ;
857+ let num_elems = cursor. read_u32 :: < LittleEndian > ( ) . unwrap ( ) ;
858+ for _ in 0 ..( num_elems as usize ) {
859+ let element_len = cursor. read_u32 :: < LittleEndian > ( ) . unwrap ( ) ;
860+ let mut element_data = vec ! [ 0u8 ; element_len as usize ] ;
861+ cursor. read_exact ( & mut element_data) . unwrap ( ) ;
862+
863+ let elem_hash = Keccak160 :: hashv ( & [ [ 0u8 ] . as_ref ( ) , element_data. as_slice ( ) ] ) ;
864+ let elem_proof = expected_accumulator. prove ( element_data. as_slice ( ) ) . unwrap ( ) ;
865+
866+ assert ! ( expected_accumulator. nodes. contains( & elem_hash) ) ;
867+ assert ! ( expected_accumulator. check( elem_proof, element_data. as_slice( ) ) ) ;
868+ }
869+
870+ // verify sequence_tracker increments
871+ assert_eq ! (
872+ get_acc_sequence_tracker( & bank) . sequence,
873+ sequence_tracker_before_bank_freeze. sequence + 1
874+ ) ;
875+
876+ // verify ring buffer cycles
877+ let ring_index_before_buffer_cycle = ( bank. slot ( ) % ACCUMULATOR_RING_SIZE as u64 ) as u32 ;
878+ let target_slot = bank. slot ( ) + ACCUMULATOR_RING_SIZE as u64 ;
879+ // advance ACCUMULATOR_RING_SIZE slots using warp_from_parent since doing large loops
880+ // with new_from_parent takes a long time. warp_from_parent results in a bank that is frozen.
881+ bank = Bank :: warp_from_parent ( & Arc :: new ( bank) , & Pubkey :: default ( ) , target_slot) ;
882+
883+ // accumulator messages should still be the same before looping around
884+ let ring_index_after_buffer_cycle = ( bank. slot ( ) % ACCUMULATOR_RING_SIZE as u64 ) as u32 ;
885+ assert_eq ! (
886+ ring_index_before_buffer_cycle,
887+ ring_index_after_buffer_cycle
888+ ) ;
889+
890+ let accumulator_state_after_skip = get_accumulator_state ( & bank, ring_index_after_buffer_cycle) ;
891+ assert_eq ! (
892+ & accumulator_state[ 16 ..] ,
893+ & accumulator_state_after_skip[ 16 ..]
894+ ) ;
895+
896+ // insert new message to make sure the update is written in the right position
897+ // in the ring buffer and overwrites the existing message
898+
899+ // advance the bank to unfreeze it (to be able to store accounts). see the comment on warp_from_parent above.
900+ bank = new_from_parent ( & Arc :: new ( bank) ) ;
901+
902+ let wh_sequence_before_acc_update = get_acc_sequence_tracker ( & bank) . sequence ;
903+
904+ let message_0 = vec ! [ 1u8 ; 127 ] ;
905+ let message_1 = vec ! [ 2u8 ; 127 ] ;
906+ let message_2 = vec ! [ 3u8 ; 254 ] ;
907+
908+ let updated_messages = vec ! [ message_1. clone( ) , message_2. clone( ) , message_0. clone( ) ] ;
909+
910+ let updated_message_buffer_bytes = create_message_buffer_bytes ( updated_messages. clone ( ) ) ;
911+ price_message_account. set_data ( updated_message_buffer_bytes) ;
912+
913+ // Store Message account so the accumulator sysvar updater can find it.
914+ bank. store_account ( & price_message_key. pubkey ( ) , & price_message_account) ;
915+
916+ // Freeze the bank to run accumulator
917+ bank. freeze ( ) ;
918+
919+ let ring_index = ( bank. slot ( ) % ACCUMULATOR_RING_SIZE as u64 ) as u32 ;
920+ let updated_wormhole_message_account = get_wormhole_message_account ( & bank, ring_index) ;
921+
922+ let updated_wormhole_message =
923+ PostedMessageUnreliableData :: deserialize ( & mut updated_wormhole_message_account. data ( ) )
924+ . unwrap ( ) ;
925+
926+ let updated_messages = updated_messages
927+ . iter ( )
928+ . map ( |m| m. as_slice ( ) )
929+ . collect :: < Vec < _ > > ( ) ;
930+ let updated_accumulator_elements = updated_messages
931+ . clone ( )
932+ . into_iter ( )
933+ . sorted_unstable ( )
934+ . dedup ( ) ;
935+
936+ let expected_accumulator =
937+ MerkleAccumulator :: < Keccak160 > :: from_set ( updated_accumulator_elements) . unwrap ( ) ;
938+ assert_eq ! (
939+ updated_wormhole_message. message. payload,
940+ expected_accumulator. serialize( bank. slot( ) , ACCUMULATOR_RING_SIZE )
941+ ) ;
942+
943+ let expected_wormhole_message = PostedMessageUnreliableData {
944+ message : MessageData {
945+ vaa_version : 1 ,
946+ consistency_level : 1 ,
947+ submission_time : bank. clock ( ) . unix_timestamp as u32 ,
948+ sequence : wh_sequence_before_acc_update,
949+ emitter_chain : 26 ,
950+ emitter_address : ACCUMULATOR_EMITTER_ADDRESS ,
951+ payload : expected_accumulator. serialize ( bank. slot ( ) , ACCUMULATOR_RING_SIZE ) ,
952+ ..Default :: default ( )
953+ } ,
954+ } ;
955+
956+ assert_eq ! (
957+ updated_wormhole_message_account. data( ) ,
958+ expected_wormhole_message. try_to_vec( ) . unwrap( )
959+ ) ;
960+ }
672961#[ test]
673962fn test_get_accumulator_keys ( ) {
674963 use pythnet_sdk:: { pythnet, ACCUMULATOR_EMITTER_ADDRESS , MESSAGE_BUFFER_PID } ;
0 commit comments