Skip to content

Commit 2a37394

Browse files
committed
legacy hf test: refactor hashes, pre&post-patch config validation
1 parent ff4e053 commit 2a37394

File tree

4 files changed

+151
-309
lines changed

4 files changed

+151
-309
lines changed
Lines changed: 134 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
11
package hardfork
22

33
import (
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

Comments
 (0)