11use clap:: Parser ;
22use sp1_sdk:: { ProverClient , SP1Stdin , include_elf} ;
3+ use ssz:: { Decode , Encode } ;
34use tracing:: { error, info} ;
45
5- use ream_consensus:: electra:: beacon_state:: BeaconState ;
6+ use ream_consensus:: electra:: { beacon_block :: SignedBeaconBlock , beacon_state:: BeaconState } ;
67use ream_lib:: { file:: read_file, input:: OperationInput } ;
78
89mod cli;
910use cli:: operation:: OperationName ;
1011
1112/// The ELF (executable and linkable format) file for the Succinct RISC-V zkVM.
12- pub const REAM_ELF : & [ u8 ] = include_elf ! ( "ream-operations" ) ;
13+ pub const OPERATIONS_ELF : & [ u8 ] = include_elf ! ( "ream-operations" ) ;
1314
1415/// The arguments for the command.
1516#[ derive( Parser , Debug ) ]
1617#[ clap( author, version, about, long_about = None ) ]
1718struct Args {
18- /// Argument for zkVMs
19-
19+ // Argument for zkVMs
2020 #[ clap( long) ]
2121 execute : bool ,
2222
2323 #[ clap( long) ]
2424 prove : bool ,
2525
26- /// Argument for STFs
26+ // Argument for STFs
27+
28+ // EF test (default flow)
29+ #[ clap( long, conflicts_with = "replay" ) ]
30+ ef_test : bool ,
2731
2832 #[ clap( flatten) ]
2933 fork : cli:: fork:: ForkArgs ,
@@ -32,19 +36,20 @@ struct Args {
3236 operation : cli:: operation:: OperationArgs ,
3337
3438 #[ clap( long) ]
35- excluded_cases : Vec < String > ,
39+ excluded_cases : Option < Vec < String > > ,
40+
41+ // Replay test: requires state and block file paths
42+ #[ clap( long, conflicts_with = "ef_test" ) ]
43+ replay : bool ,
44+
45+ #[ clap( long, requires = "replay" ) ]
46+ state_path : Option < String > ,
47+
48+ #[ clap( long, requires = "replay" ) ]
49+ block_path : Option < String > ,
3650}
3751
3852fn main ( ) {
39- let test_case_dir = std:: path:: PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) )
40- . join ( "mainnet" )
41- . join ( "tests" )
42- . join ( "mainnet" ) ;
43- if !std:: path:: Path :: new ( & test_case_dir) . exists ( ) {
44- eprintln ! ( "Error: You must first download test data via `make download`" ) ;
45- std:: process:: exit ( 1 ) ;
46- }
47-
4853 if std:: env:: var ( "RUST_LOG" ) . is_err ( ) {
4954 unsafe {
5055 std:: env:: set_var ( "RUST_LOG" , "info" ) ;
@@ -63,115 +68,149 @@ fn main() {
6368 std:: process:: exit ( 1 ) ;
6469 }
6570
66- let fork = args. fork . fork ;
67- let operation_name = args. operation . operation_name ;
68- let excluded_cases = args. excluded_cases ;
69-
70- // Load the test assets.
71- // These assets are from consensus-specs repo.
72- let base_dir = test_case_dir
73- . join ( format ! ( "{}" , fork) )
74- . join ( "operations" )
75- . join ( format ! ( "{}" , operation_name) )
76- . join ( "pyspec_tests" ) ;
77-
78- let test_cases = ream_lib:: file:: get_test_cases ( & base_dir) ;
79- for test_case in test_cases {
80- if excluded_cases. contains ( & test_case) {
81- info ! ( "Skipping test case: {}" , test_case) ;
82- continue ;
83- }
71+ if args. ef_test == args. replay {
72+ error ! ( "Error: You must specify either --ef-test or --replay" ) ;
73+ std:: process:: exit ( 1 ) ;
74+ }
8475
85- info ! ( "{}" , "-" . repeat( 50 ) ) ;
86- info ! ( "[{}] Test case: {}" , operation_name, test_case) ;
76+ if args. replay {
77+ info ! ( "Executing with replay test..." ) ;
78+ todo ! ( ) ;
79+ }
8780
88- let case_dir = & base_dir . join ( & test_case ) ;
89- let input_path = & case_dir . join ( format ! ( "{}.ssz_snappy" , operation_name . to_input_name ( ) ) ) ;
81+ if args . ef_test {
82+ info ! ( "Executing with EF test..." ) ;
9083
91- let pre_state: BeaconState = read_file ( & case_dir. join ( "pre.ssz_snappy" ) ) ;
92- let input = match operation_name {
93- OperationName :: Attestation => OperationInput :: Attestation ( read_file ( input_path) ) ,
94- OperationName :: AttesterSlashing => {
95- OperationInput :: AttesterSlashing ( read_file ( input_path) )
96- }
97- OperationName :: BlockHeader => OperationInput :: BeaconBlock ( read_file ( input_path) ) ,
98- OperationName :: BLSToExecutionChange => {
99- OperationInput :: SignedBLSToExecutionChange ( read_file ( input_path) )
100- }
101- OperationName :: Deposit => OperationInput :: Deposit ( read_file ( input_path) ) ,
102- OperationName :: ExecutionPayload => {
103- OperationInput :: BeaconBlockBody ( read_file ( input_path) )
104- }
105- OperationName :: ProposerSlashing => {
106- OperationInput :: ProposerSlashing ( read_file ( input_path) )
107- }
108- OperationName :: SyncAggregate => OperationInput :: SyncAggregate ( read_file ( input_path) ) ,
109- OperationName :: VoluntaryExit => {
110- OperationInput :: SignedVoluntaryExit ( read_file ( input_path) )
111- }
112- OperationName :: Withdrawals => OperationInput :: ExecutionPayload ( read_file ( input_path) ) ,
113- } ;
114- let post_state_opt: Option < BeaconState > = {
115- if case_dir. join ( "post.ssz_snappy" ) . exists ( ) {
116- Some ( read_file ( & case_dir. join ( "post.ssz_snappy" ) ) )
117- } else {
118- None
119- }
120- } ;
84+ let test_case_dir = std:: path:: PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) )
85+ . join ( "mainnet" )
86+ . join ( "tests" )
87+ . join ( "mainnet" ) ;
88+ if !std:: path:: Path :: new ( & test_case_dir) . exists ( ) {
89+ eprintln ! ( "Error: You must first download test data via `make download`" ) ;
90+ std:: process:: exit ( 1 ) ;
91+ }
12192
122- // Setup the prover client.
123- let client = ProverClient :: from_env ( ) ;
93+ let fork = args. fork . fork ;
94+ let operation_name = args
95+ . operation
96+ . operation_name
97+ . expect ( "Operation name is required" ) ;
98+ let excluded_cases = args. excluded_cases . unwrap_or_default ( ) ;
99+
100+ // Load the test assets.
101+ // These assets are from consensus-specs repo.
102+ let base_dir = test_case_dir
103+ . join ( format ! ( "{}" , fork) )
104+ . join ( "operations" )
105+ . join ( format ! ( "{}" , operation_name) )
106+ . join ( "pyspec_tests" ) ;
107+
108+ let test_cases = ream_lib:: file:: get_test_cases ( & base_dir) ;
109+ for test_case in test_cases {
110+ if excluded_cases. contains ( & test_case) {
111+ info ! ( "Skipping test case: {}" , test_case) ;
112+ continue ;
113+ }
124114
125- // Setup the inputs.
126- let mut stdin = SP1Stdin :: new ( ) ;
127- stdin. write ( & pre_state) ;
128- stdin. write ( & input) ;
115+ info ! ( "{}" , "-" . repeat( 50 ) ) ;
116+ info ! ( "[{}] Test case: {}" , operation_name, test_case) ;
129117
130- if args. execute {
131- // Execute the program
132- let ( output, report) = client. execute ( REAM_ELF , & stdin) . run ( ) . unwrap ( ) ;
133- info ! ( "Program executed successfully." ) ;
118+ let case_dir = & base_dir. join ( & test_case) ;
119+ let input_path =
120+ & case_dir. join ( format ! ( "{}.ssz_snappy" , operation_name. to_input_name( ) ) ) ;
134121
135- // Decode the output
136- let result: BeaconState = ssz:: Decode :: from_ssz_bytes ( output. as_slice ( ) ) . unwrap ( ) ;
122+ println ! ( "case path: {:?}" , case_dir) ;
137123
138- // Match `post_state_opt`: some test cases should not mutate beacon state.
139- match post_state_opt {
140- Some ( post_state ) => {
141- assert_eq ! ( result , post_state ) ;
142- info ! ( "Execution is correct!: State mutated" ) ;
124+ let pre_state : BeaconState = read_file ( & case_dir . join ( "pre.ssz_snappy" ) ) ;
125+ let input = match operation_name {
126+ OperationName :: Attestation => OperationInput :: Attestation ( read_file ( input_path ) ) ,
127+ OperationName :: AttesterSlashing => {
128+ OperationInput :: AttesterSlashing ( read_file ( input_path ) )
143129 }
144- None => {
145- assert_eq ! ( result, pre_state) ;
146- info ! ( "Execution is correct!: State should not be mutated" ) ;
130+ OperationName :: BlockHeader => OperationInput :: BeaconBlock ( read_file ( input_path) ) ,
131+ OperationName :: BLSToExecutionChange => {
132+ OperationInput :: SignedBLSToExecutionChange ( read_file ( input_path) )
133+ }
134+ OperationName :: Deposit => OperationInput :: Deposit ( read_file ( input_path) ) ,
135+ OperationName :: ExecutionPayload => {
136+ OperationInput :: BeaconBlockBody ( read_file ( input_path) )
137+ }
138+ OperationName :: ProposerSlashing => {
139+ OperationInput :: ProposerSlashing ( read_file ( input_path) )
140+ }
141+ OperationName :: SyncAggregate => {
142+ OperationInput :: SyncAggregate ( read_file ( input_path) )
143+ }
144+ OperationName :: VoluntaryExit => {
145+ OperationInput :: SignedVoluntaryExit ( read_file ( input_path) )
146+ }
147+ OperationName :: Withdrawals => {
148+ OperationInput :: ExecutionPayload ( read_file ( input_path) )
149+ }
150+ } ;
151+ let post_state_opt: Option < BeaconState > = {
152+ if case_dir. join ( "post.ssz_snappy" ) . exists ( ) {
153+ Some ( read_file ( & case_dir. join ( "post.ssz_snappy" ) ) )
154+ } else {
155+ None
156+ }
157+ } ;
158+
159+ // Setup the prover client.
160+ let client = ProverClient :: from_env ( ) ;
161+
162+ // Setup the inputs.
163+ let mut stdin = SP1Stdin :: new ( ) ;
164+
165+ stdin. write ( & pre_state) ;
166+ stdin. write ( & input) ;
167+
168+ if args. execute {
169+ // Execute the program
170+ let ( output, report) = client. execute ( OPERATIONS_ELF , & stdin) . run ( ) . unwrap ( ) ;
171+ info ! ( "Program executed successfully." ) ;
172+
173+ // Decode the output
174+ let result: BeaconState = ssz:: Decode :: from_ssz_bytes ( output. as_slice ( ) ) . unwrap ( ) ;
175+
176+ // Match `post_state_opt`: some test cases should not mutate beacon state.
177+ match post_state_opt {
178+ Some ( post_state) => {
179+ assert_eq ! ( result, post_state) ;
180+ info ! ( "Execution is correct!: State mutated" ) ;
181+ }
182+ None => {
183+ assert_eq ! ( result, pre_state) ;
184+ info ! ( "Execution is correct!: State should not be mutated" ) ;
185+ }
147186 }
148- }
149187
150- // Record the number of cycles executed.
151- info ! ( "----- Cycle Tracker -----" ) ;
152- info ! ( "[{}] Test case: {}" , operation_name, test_case) ;
153- info ! ( "Number of cycles: {}" , report. total_instruction_count( ) ) ;
154- info ! ( "Number of syscall count: {}" , report. total_syscall_count( ) ) ;
155- for ( key, value) in report. cycle_tracker . iter ( ) {
156- info ! ( "{}: {}" , key, value) ;
188+ // Record the number of cycles executed.
189+ info ! ( "----- Cycle Tracker -----" ) ;
190+ info ! ( "[{}] Test case: {}" , operation_name, test_case) ;
191+ info ! ( "Number of cycles: {}" , report. total_instruction_count( ) ) ;
192+ info ! ( "Number of syscall count: {}" , report. total_syscall_count( ) ) ;
193+ for ( key, value) in report. cycle_tracker . iter ( ) {
194+ info ! ( "{}: {}" , key, value) ;
195+ }
196+ info ! ( "----- Cycle Tracker End -----" ) ;
197+ } else {
198+ // Setup the program for proving.
199+ let ( pk, vk) = client. setup ( OPERATIONS_ELF ) ;
200+
201+ // Generate the proof
202+ let proof = client
203+ . prove ( & pk, & stdin)
204+ . run ( )
205+ . expect ( "failed to generate proof" ) ;
206+
207+ info ! ( "Successfully generated proof!" ) ;
208+
209+ // Verify the proof.
210+ client. verify ( & proof, & vk) . expect ( "failed to verify proof" ) ;
211+ info ! ( "Successfully verified proof!" ) ;
157212 }
158- info ! ( "----- Cycle Tracker End -----" ) ;
159- } else {
160- // Setup the program for proving.
161- let ( pk, vk) = client. setup ( REAM_ELF ) ;
162-
163- // Generate the proof
164- let proof = client
165- . prove ( & pk, & stdin)
166- . run ( )
167- . expect ( "failed to generate proof" ) ;
168-
169- info ! ( "Successfully generated proof!" ) ;
170-
171- // Verify the proof.
172- client. verify ( & proof, & vk) . expect ( "failed to verify proof" ) ;
173- info ! ( "Successfully verified proof!" ) ;
213+ info ! ( "{}" , "-" . repeat( 50 ) ) ;
174214 }
175- info ! ( "{}" , "-" . repeat( 50 ) ) ;
176215 }
177216}
0 commit comments