11package hardfork
22
33import (
4+ "bytes"
5+ "encoding/json"
46 "fmt"
57 "os"
8+ "time"
69
710 "github.com/MinaProtocol/mina/src/app/hardfork_test/src/internal/client"
811)
912
10- // ValidatePreforkLedgerHashes validates the generated prefork ledger hashes
11- func (t * HardforkTest ) ValidatePreforkLedgerHashes (
13+ type HashAndS3 struct {
14+ S3DataHash string `json:"s3_data_hash"`
15+ Hash string `json:"hash"`
16+ }
17+
18+ type RuntimeGenesisLedgerHashes struct {
19+ EpochData struct {
20+ Staking HashAndS3 `json:"staking"`
21+ Next HashAndS3 `json:"next"`
22+ } `json:"epoch_data"`
23+ Ledger HashAndS3 `json:"ledger"`
24+ }
25+
26+ func (t * HardforkTest ) ValidateRuntimeGenesisLedgerHashes (
1227 latestNonEmptyBlock client.BlockData ,
1328 genesisEpochStaking string ,
1429 genesisEpochNext string ,
1530 latestSnarkedHashPerEpoch map [int ]string ,
16- preforkHashesFile string ,
31+ ledgerHashesFile string ,
1732) error {
33+
34+ t .Logger .Info ("Validating ledger hashes file generated by runtime-genesis-ledger" )
35+
1836 // Calculate slot_tx_end_epoch
1937 // 48 as specififed by mina-local-network.sh
2038 slotTxEndEpoch := latestNonEmptyBlock .Slot / 48
@@ -31,163 +49,161 @@ func (t *HardforkTest) ValidatePreforkLedgerHashes(
3149 }
3250
3351 // Read prefork hashes from file
34- preforkHashesData , err := os .ReadFile (preforkHashesFile )
52+ ledgerHashesBytes , err := os .ReadFile (ledgerHashesFile )
3553 if err != nil {
36- return fmt .Errorf ("failed to read prefork hashes file: %w" , err )
54+ return fmt .Errorf ("failed to read ledger hashes file: %w" , err )
3755 }
3856
39- preforkHashesJson := string (preforkHashesData )
57+ var hashes RuntimeGenesisLedgerHashes
58+ dec := json .NewDecoder (bytes .NewReader (ledgerHashesBytes ))
59+ dec .DisallowUnknownFields ()
4060
41- // Validate field values
42- if err := validateStringField (preforkHashesJson , "epoch_data.staking.hash" , expectedStakingHash ); err != nil {
43- return err
61+ if err := dec .Decode (& hashes ); err != nil {
62+ return fmt .Errorf ("failed to unmarshal ledger hashes file: %w" , err )
4463 }
45- if err := validateStringField (preforkHashesJson , "epoch_data.next.hash" , expectedNextHash ); err != nil {
46- return err
47- }
48- if err := validateStringField (preforkHashesJson , "ledger.hash" , latestNonEmptyBlock .StagedHash ); err != nil {
49- return err
50- }
51-
52- ledger_fields := []string {"hash" , "s3_data_hash" }
5364
54- // Validate object structure - ensure only expected fields are present
55- if err := t .validateObjectFields (preforkHashesJson , "epoch_data.staking" , ledger_fields ); err != nil {
56- return err
57- }
58- if err := t .validateObjectFields (preforkHashesJson , "epoch_data.next" , ledger_fields ); err != nil {
59- return err
60- }
61- if err := t .validateObjectFields (preforkHashesJson , "ledger" , ledger_fields ); err != nil {
62- return err
65+ if hashes .EpochData .Staking .Hash != expectedStakingHash {
66+ return fmt .Errorf ("Expected epoch_data.staking.hash `%s`, got `%s`" , expectedStakingHash , hashes .EpochData .Staking .Hash )
6367 }
64- if err := t .validateObjectFields (preforkHashesJson , "epoch_data" , []string {"staking" , "next" }); err != nil {
65- return err
68+
69+ if hashes .EpochData .Next .Hash != expectedNextHash {
70+ return fmt .Errorf ("Expected epoch_data.next.hash `%s`, got `%s`" , expectedNextHash , hashes .EpochData .Next .Hash )
6671 }
6772
68- // Validate root object contains only expected top-level fields
69- if err := t .validateRootObjectFields (preforkHashesJson , []string {"epoch_data" , "ledger" }); err != nil {
70- return err
73+ if hashes .Ledger .Hash != latestNonEmptyBlock .StagedHash {
74+ return fmt .Errorf ("Expected ledger.hash `%s`, got `%s`" , latestNonEmptyBlock .StagedHash , hashes .Ledger .Hash )
7175 }
7276
73- t .Logger .Info ("Prefork ledger hashes validated successfully" )
77+ t .Logger .Info ("Ledger hashes file validated successfully" )
7478 return nil
7579}
7680
77- // ValidateForkConfigData validates the extracted fork config against expected values
78- func (t * HardforkTest ) ValidateForkConfigData (latestNonEmptyBlock client.BlockData , forkConfigBytes []byte ) error {
79- forkConfigJson := string (forkConfigBytes )
81+ type EpochDataPrepatch struct {
82+ Seed string `json:"seed"`
83+ Accounts any `json:"accounts"`
84+ }
8085
81- // Validate field values
82- if err := validateIntField (forkConfigJson , "proof.fork.blockchain_length" , latestNonEmptyBlock .BlockHeight ); err != nil {
83- return err
84- }
85- if err := validateIntField (forkConfigJson , "proof.fork.global_slot_since_genesis" , latestNonEmptyBlock .Slot ); err != nil {
86- return err
87- }
88- if err := validateStringField (forkConfigJson , "proof.fork.state_hash" , latestNonEmptyBlock .StateHash ); err != nil {
89- return err
90- }
91- if err := validateStringField (forkConfigJson , "epoch_data.next.seed" , latestNonEmptyBlock .NextEpochSeed ); err != nil {
92- return err
93- }
94- if err := validateStringField (forkConfigJson , "epoch_data.staking.seed" , latestNonEmptyBlock .CurEpochSeed ); err != nil {
95- return err
96- }
97- if err := validateStringField (forkConfigJson , "ledger.hash" , latestNonEmptyBlock .StagedHash ); err != nil {
98- return err
99- }
100- if err := validateBoolField (forkConfigJson , "ledger.add_genesis_winner" , false ); err != nil {
101- return err
86+ type LegacyPrepatchForkConfigView struct {
87+ Proof struct {
88+ Fork struct {
89+ BlockChainLength int `json:"blockchain_length"`
90+ GlobalSlotSinceGenesis int `json:"global_slot_since_genesis"`
91+ StateHash string `json:"state_hash"`
92+ } `json:"fork"`
93+ } `json:"proof"`
94+ EpochData struct {
95+ Staking EpochDataPrepatch `json:"staking"`
96+ Next EpochDataPrepatch `json:"next"`
97+ } `json:"epoch_data"`
98+ Ledger struct {
99+ Hash string `json:"hash"`
100+ Accounts any `json:"accounts"`
101+ AddGenesisWinner bool `json:"add_genesis_winner"`
102+ } `json:"ledger"`
103+ }
104+
105+ func (t * HardforkTest ) ValidateLegacyPrepatchForkConfig (latestNonEmptyBlock client.BlockData , forkConfigBytes []byte ) error {
106+
107+ t .Logger .Info ("Validating legacy prepatch fork config" )
108+
109+ var config LegacyPrepatchForkConfigView
110+ dec := json .NewDecoder (bytes .NewReader (forkConfigBytes ))
111+ dec .DisallowUnknownFields ()
112+
113+ if err := dec .Decode (& config ); err != nil {
114+ return fmt .Errorf ("failed to unmarshal legacy prepatch fork config: %w" , err )
102115 }
103116
104- // Validate object structure - ensure only expected fields are present
105- if err := t .validateObjectFields (forkConfigJson , "proof.fork" , []string {"blockchain_length" , "global_slot_since_genesis" , "state_hash" }); err != nil {
106- return err
117+ if config .Proof .Fork .BlockChainLength != latestNonEmptyBlock .BlockHeight {
118+ return fmt .Errorf ("Expected proof.fork.blockchain_length to be %d, got %d" , latestNonEmptyBlock .BlockHeight , config .Proof .Fork .BlockChainLength )
107119 }
108- if err := t .validateObjectFields (forkConfigJson , "epoch_data.staking" , []string {"seed" , "accounts" }); err != nil {
109- return err
120+
121+ if config .Proof .Fork .GlobalSlotSinceGenesis != latestNonEmptyBlock .Slot {
122+ return fmt .Errorf ("Expected proof.fork.global_slot_since_genesis to be %d, got %d" , latestNonEmptyBlock .Slot , config .Proof .Fork .GlobalSlotSinceGenesis )
110123 }
111- if err := t .validateObjectFields (forkConfigJson , "epoch_data.next" , []string {"seed" , "accounts" }); err != nil {
112- return err
124+
125+ if config .Proof .Fork .StateHash != latestNonEmptyBlock .StateHash {
126+ return fmt .Errorf ("Expected proof.fork.state_hash to be `%s`, got `%s`" , latestNonEmptyBlock .StateHash , config .Proof .Fork .StateHash )
113127 }
114- if err := t .validateObjectFields (forkConfigJson , "ledger" , []string {"hash" , "accounts" , "add_genesis_winner" }); err != nil {
115- return err
128+
129+ if config .EpochData .Staking .Seed != latestNonEmptyBlock .CurEpochSeed {
130+ return fmt .Errorf ("Expected proof.epoch_data.staking.seed to be `%s`, got `%s`" , latestNonEmptyBlock .CurEpochSeed , config .EpochData .Staking .Seed )
116131 }
117- if err := t .validateObjectFields (forkConfigJson , "epoch_data" , []string {"staking" , "next" }); err != nil {
118- return err
132+
133+ if config .EpochData .Next .Seed != latestNonEmptyBlock .NextEpochSeed {
134+ return fmt .Errorf ("Expected proof.epoch_data.next.seed to be `%s`, got `%s`" , latestNonEmptyBlock .NextEpochSeed , config .EpochData .Next .Seed )
119135 }
120- if err := t .validateObjectFields (forkConfigJson , "proof" , []string {"fork" }); err != nil {
121- return err
136+
137+ if config .Ledger .Hash != latestNonEmptyBlock .StagedHash {
138+ return fmt .Errorf ("Expected ledger.hash to be `%s`, got `%s`" , latestNonEmptyBlock .StagedHash , config .Ledger .Hash )
122139 }
123140
124- // Validate root object contains only expected top-level fields
125- if err := t .validateRootObjectFields (forkConfigJson , []string {"proof" , "epoch_data" , "ledger" }); err != nil {
126- return err
141+ if config .Ledger .AddGenesisWinner != false {
142+ return fmt .Errorf ("Expected ledger.add_genesis_winner to be false, got true" )
127143 }
128144
129- t .Logger .Info ("Fork config data validated successfully" )
145+ t .Logger .Info ("Legacy prepatch fork config validated successfully" )
130146 return nil
131147}
132148
133- // ValidateForkRuntimeConfig validates that the runtime config has correct fork data
134- func (t * HardforkTest ) ValidateForkRuntimeConfig (latestNonEmptyBlock client.BlockData , configData []byte , forkGenesisTs , mainGenesisTs int64 ) error {
149+ type EpochDataPatched struct {
150+ Hash string `json:"hash"`
151+ S3DataHash string `json:"s3_data_hash"`
152+ Seed string `json:"seed"`
153+ }
154+
155+ type LegacyPostpatchForkConfigView struct {
156+ Proof struct {
157+ Fork struct {
158+ BlockChainLength int `json:"blockchain_length"`
159+ GlobalSlotSinceGenesis int64 `json:"global_slot_since_genesis"`
160+ StateHash string `json:"state_hash"`
161+ } `json:"fork"`
162+ } `json:"proof"`
163+ EpochData struct {
164+ Staking EpochDataPatched `json:"staking"`
165+ Next EpochDataPatched `json:"next"`
166+ } `json:"epoch_data"`
167+ Genesis struct {
168+ GenesisStateTimeStamp time.Time `json:"genesis_state_timestamp"`
169+ }
170+ Ledger struct {
171+ Hash string `json:"hash"`
172+ S3DataHash string `json:"s3_data_hash"`
173+ AddGenesisWinner bool `json:"add_genesis_winner"`
174+ } `json:"ledger"`
175+ }
176+
177+ func (t * HardforkTest ) ValidateLegacyPostpatchForkConfig (latestNonEmptyBlock client.BlockData , forkConfigBytes []byte , forkGenesisTs , mainGenesisTs int64 ) error {
178+
179+ t .Logger .Info ("Validating legacy postpatch fork config" )
135180 // Calculate expected genesis slot
136181 expectedGenesisSlot := (forkGenesisTs - mainGenesisTs ) / int64 (t .Config .MainSlot )
137182
138- configJson := string (configData )
183+ var config LegacyPostpatchForkConfigView
184+ dec := json .NewDecoder (bytes .NewReader (forkConfigBytes ))
185+ dec .DisallowUnknownFields ()
139186
140- // Validate field values
141- if err := validateIntField (configJson , "proof.fork.blockchain_length" , latestNonEmptyBlock .BlockHeight ); err != nil {
142- return err
143- }
144- if err := validateInt64Field (configJson , "proof.fork.global_slot_since_genesis" , expectedGenesisSlot ); err != nil {
145- return err
146- }
147- if err := validateStringField (configJson , "proof.fork.state_hash" , latestNonEmptyBlock .StateHash ); err != nil {
148- return err
149- }
150- if err := validateUnixTimestampField (configJson , "genesis.genesis_state_timestamp" , forkGenesisTs ); err != nil {
151- return err
152- }
153- if err := t .validateObjectFields (configJson , "genesis" , []string {"genesis_state_timestamp" }); err != nil {
154- return err
187+ if err := dec .Decode (& config ); err != nil {
188+ return fmt .Errorf ("failed to unmarshal fork config: %w" , err )
155189 }
156190
157- // Validate object structure - ensure only expected fields are present
158- if err := t .validateObjectFields (configJson , "proof.fork" , []string {"blockchain_length" , "global_slot_since_genesis" , "state_hash" }); err != nil {
159- return err
160- }
161- if err := t .validateObjectFields (configJson , "proof" , []string {"fork" }); err != nil {
162- return err
191+ if config .Proof .Fork .BlockChainLength != latestNonEmptyBlock .BlockHeight {
192+ return fmt .Errorf ("Expected proof.fork.blockchain_length to be %d, got %d" , latestNonEmptyBlock .BlockHeight , config .Proof .Fork .BlockChainLength )
163193 }
164194
165- epochFields := []string {"hash" , "s3_data_hash" , "seed" }
166- // Validate object structure - ensure only expected fields are present
167- if err := t .validateObjectFields (configJson , "epoch_data.staking" , epochFields ); err != nil {
168- return err
169- }
170- if err := t .validateObjectFields (configJson , "epoch_data.next" , epochFields ); err != nil {
171- return err
172- }
173- if err := t .validateObjectFields (configJson , "epoch_data" , []string {"staking" , "next" }); err != nil {
174- return err
195+ if config .Proof .Fork .GlobalSlotSinceGenesis != expectedGenesisSlot {
196+ return fmt .Errorf ("Expected proof.fork.global_slot_since_genesis to be %d, got %d" , expectedGenesisSlot , config .Proof .Fork .GlobalSlotSinceGenesis )
175197 }
176198
177- // Validate ledger.add_genesis_winner is false
178- if err := validateBoolField (configJson , "ledger.add_genesis_winner" , false ); err != nil {
179- return err
199+ if config .Proof .Fork .StateHash != latestNonEmptyBlock .StateHash {
200+ return fmt .Errorf ("Expected proof.fork.state_hash to be `%s`, got `%s`" , latestNonEmptyBlock .StateHash , config .Proof .Fork .StateHash )
180201 }
181202
182- ledgerFields := []string {"hash" , "s3_data_hash" , "add_genesis_winner" }
183- if err := t .validateObjectFields (configJson , "ledger" , ledgerFields ); err != nil {
184- return err
203+ if config .Genesis .GenesisStateTimeStamp .Unix () != forkGenesisTs {
204+ return fmt .Errorf ("Expected genesis.genesis_state_timestamp to be `%d`(unix timestamp), got `%s`(RFC3339)" , forkGenesisTs , config .Genesis .GenesisStateTimeStamp .Format (time .RFC3339 ))
185205 }
186206
187- // Validate root object contains only expected top-level fields
188- if err := t .validateRootObjectFields (configJson , []string {"proof" , "epoch_data" , "ledger" , "genesis" }); err != nil {
189- return err
190- }
191- t .Logger .Info ("Config for the fork is correct, starting a new network" )
207+ t .Logger .Info ("Legacy postpatch fork config validated successfully" )
192208 return nil
193209}
0 commit comments