diff --git a/src/app/hardfork_test/src/internal/hardfork/config_validation.go b/src/app/hardfork_test/src/internal/hardfork/config_validation.go index c6c5c210384..f0eeb08cf75 100644 --- a/src/app/hardfork_test/src/internal/hardfork/config_validation.go +++ b/src/app/hardfork_test/src/internal/hardfork/config_validation.go @@ -1,20 +1,38 @@ package hardfork import ( + "bytes" + "encoding/json" "fmt" "os" + "time" "github.com/MinaProtocol/mina/src/app/hardfork_test/src/internal/client" ) -// ValidatePreforkLedgerHashes validates the generated prefork ledger hashes -func (t *HardforkTest) ValidatePreforkLedgerHashes( +type HashAndS3 struct { + S3DataHash string `json:"s3_data_hash"` + Hash string `json:"hash"` +} + +type RuntimeGenesisLedgerHashes struct { + EpochData struct { + Staking HashAndS3 `json:"staking"` + Next HashAndS3 `json:"next"` + } `json:"epoch_data"` + Ledger HashAndS3 `json:"ledger"` +} + +func (t *HardforkTest) ValidateRuntimeGenesisLedgerHashes( latestNonEmptyBlock client.BlockData, genesisEpochStaking string, genesisEpochNext string, latestSnarkedHashPerEpoch map[int]string, - preforkHashesFile string, + ledgerHashesFile string, ) error { + + t.Logger.Info("Validating ledger hashes file generated by runtime-genesis-ledger") + // Calculate slot_tx_end_epoch // 48 as specififed by mina-local-network.sh slotTxEndEpoch := latestNonEmptyBlock.Slot / 48 @@ -31,163 +49,161 @@ func (t *HardforkTest) ValidatePreforkLedgerHashes( } // Read prefork hashes from file - preforkHashesData, err := os.ReadFile(preforkHashesFile) + ledgerHashesBytes, err := os.ReadFile(ledgerHashesFile) if err != nil { - return fmt.Errorf("failed to read prefork hashes file: %w", err) + return fmt.Errorf("failed to read ledger hashes file: %w", err) } - preforkHashesJson := string(preforkHashesData) + var hashes RuntimeGenesisLedgerHashes + dec := json.NewDecoder(bytes.NewReader(ledgerHashesBytes)) + dec.DisallowUnknownFields() - // Validate field values - if err := validateStringField(preforkHashesJson, "epoch_data.staking.hash", expectedStakingHash); err != nil { - return err + if err := dec.Decode(&hashes); err != nil { + return fmt.Errorf("failed to unmarshal ledger hashes file: %w", err) } - if err := validateStringField(preforkHashesJson, "epoch_data.next.hash", expectedNextHash); err != nil { - return err - } - if err := validateStringField(preforkHashesJson, "ledger.hash", latestNonEmptyBlock.StagedHash); err != nil { - return err - } - - ledger_fields := []string{"hash", "s3_data_hash"} - // Validate object structure - ensure only expected fields are present - if err := t.validateObjectFields(preforkHashesJson, "epoch_data.staking", ledger_fields); err != nil { - return err - } - if err := t.validateObjectFields(preforkHashesJson, "epoch_data.next", ledger_fields); err != nil { - return err - } - if err := t.validateObjectFields(preforkHashesJson, "ledger", ledger_fields); err != nil { - return err + if hashes.EpochData.Staking.Hash != expectedStakingHash { + return fmt.Errorf("Expected epoch_data.staking.hash `%s`, got `%s`", expectedStakingHash, hashes.EpochData.Staking.Hash) } - if err := t.validateObjectFields(preforkHashesJson, "epoch_data", []string{"staking", "next"}); err != nil { - return err + + if hashes.EpochData.Next.Hash != expectedNextHash { + return fmt.Errorf("Expected epoch_data.next.hash `%s`, got `%s`", expectedNextHash, hashes.EpochData.Next.Hash) } - // Validate root object contains only expected top-level fields - if err := t.validateRootObjectFields(preforkHashesJson, []string{"epoch_data", "ledger"}); err != nil { - return err + if hashes.Ledger.Hash != latestNonEmptyBlock.StagedHash { + return fmt.Errorf("Expected ledger.hash `%s`, got `%s`", latestNonEmptyBlock.StagedHash, hashes.Ledger.Hash) } - t.Logger.Info("Prefork ledger hashes validated successfully") + t.Logger.Info("Ledger hashes file validated successfully") return nil } -// ValidateForkConfigData validates the extracted fork config against expected values -func (t *HardforkTest) ValidateForkConfigData(latestNonEmptyBlock client.BlockData, forkConfigBytes []byte) error { - forkConfigJson := string(forkConfigBytes) +type EpochDataPrepatch struct { + Seed string `json:"seed"` + Accounts any `json:"accounts"` +} - // Validate field values - if err := validateIntField(forkConfigJson, "proof.fork.blockchain_length", latestNonEmptyBlock.BlockHeight); err != nil { - return err - } - if err := validateIntField(forkConfigJson, "proof.fork.global_slot_since_genesis", latestNonEmptyBlock.Slot); err != nil { - return err - } - if err := validateStringField(forkConfigJson, "proof.fork.state_hash", latestNonEmptyBlock.StateHash); err != nil { - return err - } - if err := validateStringField(forkConfigJson, "epoch_data.next.seed", latestNonEmptyBlock.NextEpochSeed); err != nil { - return err - } - if err := validateStringField(forkConfigJson, "epoch_data.staking.seed", latestNonEmptyBlock.CurEpochSeed); err != nil { - return err - } - if err := validateStringField(forkConfigJson, "ledger.hash", latestNonEmptyBlock.StagedHash); err != nil { - return err - } - if err := validateBoolField(forkConfigJson, "ledger.add_genesis_winner", false); err != nil { - return err +type LegacyPrepatchForkConfigView struct { + Proof struct { + Fork struct { + BlockChainLength int `json:"blockchain_length"` + GlobalSlotSinceGenesis int `json:"global_slot_since_genesis"` + StateHash string `json:"state_hash"` + } `json:"fork"` + } `json:"proof"` + EpochData struct { + Staking EpochDataPrepatch `json:"staking"` + Next EpochDataPrepatch `json:"next"` + } `json:"epoch_data"` + Ledger struct { + Hash string `json:"hash"` + Accounts any `json:"accounts"` + AddGenesisWinner bool `json:"add_genesis_winner"` + } `json:"ledger"` +} + +func (t *HardforkTest) ValidateLegacyPrepatchForkConfig(latestNonEmptyBlock client.BlockData, forkConfigBytes []byte) error { + + t.Logger.Info("Validating legacy prepatch fork config") + + var config LegacyPrepatchForkConfigView + dec := json.NewDecoder(bytes.NewReader(forkConfigBytes)) + dec.DisallowUnknownFields() + + if err := dec.Decode(&config); err != nil { + return fmt.Errorf("failed to unmarshal legacy prepatch fork config: %w", err) } - // Validate object structure - ensure only expected fields are present - if err := t.validateObjectFields(forkConfigJson, "proof.fork", []string{"blockchain_length", "global_slot_since_genesis", "state_hash"}); err != nil { - return err + if config.Proof.Fork.BlockChainLength != latestNonEmptyBlock.BlockHeight { + return fmt.Errorf("Expected proof.fork.blockchain_length to be %d, got %d", latestNonEmptyBlock.BlockHeight, config.Proof.Fork.BlockChainLength) } - if err := t.validateObjectFields(forkConfigJson, "epoch_data.staking", []string{"seed", "accounts"}); err != nil { - return err + + if config.Proof.Fork.GlobalSlotSinceGenesis != latestNonEmptyBlock.Slot { + return fmt.Errorf("Expected proof.fork.global_slot_since_genesis to be %d, got %d", latestNonEmptyBlock.Slot, config.Proof.Fork.GlobalSlotSinceGenesis) } - if err := t.validateObjectFields(forkConfigJson, "epoch_data.next", []string{"seed", "accounts"}); err != nil { - return err + + if config.Proof.Fork.StateHash != latestNonEmptyBlock.StateHash { + return fmt.Errorf("Expected proof.fork.state_hash to be `%s`, got `%s`", latestNonEmptyBlock.StateHash, config.Proof.Fork.StateHash) } - if err := t.validateObjectFields(forkConfigJson, "ledger", []string{"hash", "accounts", "add_genesis_winner"}); err != nil { - return err + + if config.EpochData.Staking.Seed != latestNonEmptyBlock.CurEpochSeed { + return fmt.Errorf("Expected proof.epoch_data.staking.seed to be `%s`, got `%s`", latestNonEmptyBlock.CurEpochSeed, config.EpochData.Staking.Seed) } - if err := t.validateObjectFields(forkConfigJson, "epoch_data", []string{"staking", "next"}); err != nil { - return err + + if config.EpochData.Next.Seed != latestNonEmptyBlock.NextEpochSeed { + return fmt.Errorf("Expected proof.epoch_data.next.seed to be `%s`, got `%s`", latestNonEmptyBlock.NextEpochSeed, config.EpochData.Next.Seed) } - if err := t.validateObjectFields(forkConfigJson, "proof", []string{"fork"}); err != nil { - return err + + if config.Ledger.Hash != latestNonEmptyBlock.StagedHash { + return fmt.Errorf("Expected ledger.hash to be `%s`, got `%s`", latestNonEmptyBlock.StagedHash, config.Ledger.Hash) } - // Validate root object contains only expected top-level fields - if err := t.validateRootObjectFields(forkConfigJson, []string{"proof", "epoch_data", "ledger"}); err != nil { - return err + if config.Ledger.AddGenesisWinner != false { + return fmt.Errorf("Expected ledger.add_genesis_winner to be false, got true") } - t.Logger.Info("Fork config data validated successfully") + t.Logger.Info("Legacy prepatch fork config validated successfully") return nil } -// ValidateForkRuntimeConfig validates that the runtime config has correct fork data -func (t *HardforkTest) ValidateForkRuntimeConfig(latestNonEmptyBlock client.BlockData, configData []byte, forkGenesisTs, mainGenesisTs int64) error { +type EpochDataPatched struct { + Hash string `json:"hash"` + S3DataHash string `json:"s3_data_hash"` + Seed string `json:"seed"` +} + +type LegacyPostpatchForkConfigView struct { + Proof struct { + Fork struct { + BlockChainLength int `json:"blockchain_length"` + GlobalSlotSinceGenesis int64 `json:"global_slot_since_genesis"` + StateHash string `json:"state_hash"` + } `json:"fork"` + } `json:"proof"` + EpochData struct { + Staking EpochDataPatched `json:"staking"` + Next EpochDataPatched `json:"next"` + } `json:"epoch_data"` + Genesis struct { + GenesisStateTimeStamp time.Time `json:"genesis_state_timestamp"` + } + Ledger struct { + Hash string `json:"hash"` + S3DataHash string `json:"s3_data_hash"` + AddGenesisWinner bool `json:"add_genesis_winner"` + } `json:"ledger"` +} + +func (t *HardforkTest) ValidateLegacyPostpatchForkConfig(latestNonEmptyBlock client.BlockData, forkConfigBytes []byte, forkGenesisTs, mainGenesisTs int64) error { + + t.Logger.Info("Validating legacy postpatch fork config") // Calculate expected genesis slot expectedGenesisSlot := (forkGenesisTs - mainGenesisTs) / int64(t.Config.MainSlot) - configJson := string(configData) + var config LegacyPostpatchForkConfigView + dec := json.NewDecoder(bytes.NewReader(forkConfigBytes)) + dec.DisallowUnknownFields() - // Validate field values - if err := validateIntField(configJson, "proof.fork.blockchain_length", latestNonEmptyBlock.BlockHeight); err != nil { - return err - } - if err := validateInt64Field(configJson, "proof.fork.global_slot_since_genesis", expectedGenesisSlot); err != nil { - return err - } - if err := validateStringField(configJson, "proof.fork.state_hash", latestNonEmptyBlock.StateHash); err != nil { - return err - } - if err := validateUnixTimestampField(configJson, "genesis.genesis_state_timestamp", forkGenesisTs); err != nil { - return err - } - if err := t.validateObjectFields(configJson, "genesis", []string{"genesis_state_timestamp"}); err != nil { - return err + if err := dec.Decode(&config); err != nil { + return fmt.Errorf("failed to unmarshal fork config: %w", err) } - // Validate object structure - ensure only expected fields are present - if err := t.validateObjectFields(configJson, "proof.fork", []string{"blockchain_length", "global_slot_since_genesis", "state_hash"}); err != nil { - return err - } - if err := t.validateObjectFields(configJson, "proof", []string{"fork"}); err != nil { - return err + if config.Proof.Fork.BlockChainLength != latestNonEmptyBlock.BlockHeight { + return fmt.Errorf("Expected proof.fork.blockchain_length to be %d, got %d", latestNonEmptyBlock.BlockHeight, config.Proof.Fork.BlockChainLength) } - epochFields := []string{"hash", "s3_data_hash", "seed"} - // Validate object structure - ensure only expected fields are present - if err := t.validateObjectFields(configJson, "epoch_data.staking", epochFields); err != nil { - return err - } - if err := t.validateObjectFields(configJson, "epoch_data.next", epochFields); err != nil { - return err - } - if err := t.validateObjectFields(configJson, "epoch_data", []string{"staking", "next"}); err != nil { - return err + if config.Proof.Fork.GlobalSlotSinceGenesis != expectedGenesisSlot { + return fmt.Errorf("Expected proof.fork.global_slot_since_genesis to be %d, got %d", expectedGenesisSlot, config.Proof.Fork.GlobalSlotSinceGenesis) } - // Validate ledger.add_genesis_winner is false - if err := validateBoolField(configJson, "ledger.add_genesis_winner", false); err != nil { - return err + if config.Proof.Fork.StateHash != latestNonEmptyBlock.StateHash { + return fmt.Errorf("Expected proof.fork.state_hash to be `%s`, got `%s`", latestNonEmptyBlock.StateHash, config.Proof.Fork.StateHash) } - ledgerFields := []string{"hash", "s3_data_hash", "add_genesis_winner"} - if err := t.validateObjectFields(configJson, "ledger", ledgerFields); err != nil { - return err + if config.Genesis.GenesisStateTimeStamp.Unix() != forkGenesisTs { + return fmt.Errorf("Expected genesis.genesis_state_timestamp to be `%d`(unix timestamp), got `%s`(RFC3339)", forkGenesisTs, config.Genesis.GenesisStateTimeStamp.Format(time.RFC3339)) } - // Validate root object contains only expected top-level fields - if err := t.validateRootObjectFields(configJson, []string{"proof", "epoch_data", "ledger", "genesis"}); err != nil { - return err - } - t.Logger.Info("Config for the fork is correct, starting a new network") + t.Logger.Info("Legacy postpatch fork config validated successfully") return nil } diff --git a/src/app/hardfork_test/src/internal/hardfork/json_validation.go b/src/app/hardfork_test/src/internal/hardfork/json_validation.go deleted file mode 100644 index 1c3a0bb8d83..00000000000 --- a/src/app/hardfork_test/src/internal/hardfork/json_validation.go +++ /dev/null @@ -1,167 +0,0 @@ -package hardfork - -import ( - "fmt" - "time" - - "github.com/tidwall/gjson" -) - -// validateStringField validates that a string field exists and matches the expected value -func validateStringField(json, path, expected string) error { - result := gjson.Get(json, path) - if !result.Exists() { - return fmt.Errorf("missing field: %s", path) - } - actual := result.String() - if actual != expected { - return fmt.Errorf("%s mismatch: expected %s, got %s", path, expected, actual) - } - return nil -} - -// validateIntField validates that an integer field exists and matches the expected value -func validateIntField(json, path string, expected int) error { - result := gjson.Get(json, path) - if !result.Exists() { - return fmt.Errorf("missing field: %s", path) - } - actual := result.Int() - if actual != int64(expected) { - return fmt.Errorf("%s mismatch: expected %d, got %d", path, expected, actual) - } - return nil -} - -// validateInt64Field validates that an int64 field exists and matches the expected value -func validateInt64Field(json, path string, expected int64) error { - result := gjson.Get(json, path) - if !result.Exists() { - return fmt.Errorf("missing field: %s", path) - } - actual := result.Int() - if actual != expected { - return fmt.Errorf("%s mismatch: expected %d, got %d", path, expected, actual) - } - return nil -} - -// validateBoolField validates that a boolean field exists and matches the expected value -func validateBoolField(json, path string, expected bool) error { - result := gjson.Get(json, path) - if !result.Exists() { - return fmt.Errorf("missing field: %s", path) - } - actual := result.Bool() - if actual != expected { - return fmt.Errorf("%s mismatch: expected %v, got %v", path, expected, actual) - } - return nil -} - -// validateUnixTimestampField validates that a timestamp field matches the expected Unix timestamp -// The field must be stored as an RFC3339 formatted string -func validateUnixTimestampField(json, path string, expectedUnixTs int64) error { - result := gjson.Get(json, path) - if !result.Exists() { - return fmt.Errorf("missing field: %s", path) - } - - if result.Type != gjson.String { - return fmt.Errorf("%s must be a string", path) - } - - timestampStr := result.String() - // Try parsing as RFC3339 timestamp - t, err := time.Parse(time.RFC3339, timestampStr) - if err != nil { - // Also try a common variant with space instead of 'T' - t, err = time.Parse("2006-01-02 15:04:05-07:00", timestampStr) - if err != nil { - return fmt.Errorf("%s is not a valid RFC3339 timestamp: %v", path, err) - } - } - - actualUnixTs := t.Unix() - if actualUnixTs != expectedUnixTs { - return fmt.Errorf("%s mismatch: expected %d, got %d", path, expectedUnixTs, actualUnixTs) - } - return nil -} - -// validateObjectFields validates that an object contains only the expected fields -func (t *HardforkTest) validateObjectFields(json, path string, expectedFields []string) error { - obj := gjson.Get(json, path) - if !obj.Exists() { - return fmt.Errorf("missing object: %s", path) - } - if !obj.IsObject() { - return fmt.Errorf("%s is not an object", path) - } - - // Create a map of expected fields for quick lookup - expectedMap := make(map[string]bool) - for _, field := range expectedFields { - expectedMap[field] = true - } - - // Check all fields in the object - fieldCount := 0 - var unexpectedFields []string - obj.ForEach(func(key, value gjson.Result) bool { - fieldCount++ - fieldName := key.String() - if !expectedMap[fieldName] { - unexpectedFields = append(unexpectedFields, fieldName) - t.Logger.Error("Unexpected field in %s: %s", path, fieldName) - } - return true // continue iteration - }) - - if len(unexpectedFields) > 0 { - return fmt.Errorf("%s contains unexpected fields: %v", path, unexpectedFields) - } - - if fieldCount != len(expectedFields) { - return fmt.Errorf("%s should contain exactly %d field(s), found %d", path, len(expectedFields), fieldCount) - } - - return nil -} - -// validateRootObjectFields validates that the root-level JSON contains only expected fields -func (t *HardforkTest) validateRootObjectFields(json string, expectedFields []string) error { - rootResult := gjson.Parse(json) - if !rootResult.IsObject() { - return fmt.Errorf("root is not an object") - } - - // Create a map of expected fields for quick lookup - expectedMap := make(map[string]bool) - for _, field := range expectedFields { - expectedMap[field] = true - } - - // Check all fields in the root object - fieldCount := 0 - var unexpectedFields []string - rootResult.ForEach(func(key, value gjson.Result) bool { - fieldCount++ - fieldName := key.String() - if !expectedMap[fieldName] { - unexpectedFields = append(unexpectedFields, fieldName) - t.Logger.Error("Unexpected field in root object: %s", fieldName) - } - return true - }) - - if len(unexpectedFields) > 0 { - return fmt.Errorf("root object contains unexpected fields: %v", unexpectedFields) - } - - if fieldCount != len(expectedFields) { - return fmt.Errorf("root object should contain exactly %d field(s), found %d", len(expectedFields), fieldCount) - } - - return nil -} diff --git a/src/app/hardfork_test/src/internal/hardfork/ledger.go b/src/app/hardfork_test/src/internal/hardfork/ledger.go index 73d34f551b5..8fe95e7cd95 100644 --- a/src/app/hardfork_test/src/internal/hardfork/ledger.go +++ b/src/app/hardfork_test/src/internal/hardfork/ledger.go @@ -43,26 +43,19 @@ func (t *HardforkTest) GenerateForkLedgers(executablePath, forkConfigPath, ledge return nil } -// GenerateAndValidatePreforkLedgers validates fork config and ledgers -// Note: Fork config extraction happens in RunMainNetworkPhase before nodes shutdown -func (t *HardforkTest) GenerateAndValidatePreforkLedgers(analysis *BlockAnalysisResult, forkConfigPath, preforkLedgersDir, preforkHashesFile string) error { +func (t *HardforkTest) GenerateAndValidateHashesAndLedgers(analysis *BlockAnalysisResult, forkConfigPath, preforkLedgersDir, prepatchForkConfig string) error { // Generate prefork ledgers using main network executable - if err := t.GenerateForkLedgers(t.Config.MainRuntimeGenesisLedger, forkConfigPath, preforkLedgersDir, preforkHashesFile); err != nil { + if err := t.GenerateForkLedgers(t.Config.MainRuntimeGenesisLedger, forkConfigPath, preforkLedgersDir, prepatchForkConfig); err != nil { return err } - // Validate prefork ledger hashes - if err := t.ValidatePreforkLedgerHashes( + return t.ValidateRuntimeGenesisLedgerHashes( analysis.LatestNonEmptyBlock, analysis.GenesisEpochStaking, analysis.GenesisEpochNext, analysis.LatestSnarkedHashPerEpoch, - preforkHashesFile, - ); err != nil { - return err - } - - return nil + prepatchForkConfig, + ) } // PatchForkConfigAndGenerateLedgersLegacy does the following: @@ -83,7 +76,7 @@ func (t *HardforkTest) PatchForkConfigAndGenerateLedgersLegacy(analysis *BlockAn } // Validate modified fork data - return t.ValidateForkRuntimeConfig(analysis.LatestNonEmptyBlock, runtimeConfigBytes, forkGenesisTs, mainGenesisTs) + return t.ValidateLegacyPostpatchForkConfig(analysis.LatestNonEmptyBlock, runtimeConfigBytes, forkGenesisTs, mainGenesisTs) } func (t *HardforkTest) AdvancedGenerateHardForkConfig(configDir string, clientPort int) error { diff --git a/src/app/hardfork_test/src/internal/hardfork/phases.go b/src/app/hardfork_test/src/internal/hardfork/phases.go index 9acf9f28630..0ddd6292ad7 100644 --- a/src/app/hardfork_test/src/internal/hardfork/phases.go +++ b/src/app/hardfork_test/src/internal/hardfork/phases.go @@ -118,35 +118,35 @@ func (t *HardforkTest) RunForkNetworkPhase(latestPreForkHeight int, forkData For func (t *HardforkTest) LegacyForkPhase(analysis *BlockAnalysisResult, forkConfigBytes []byte, mainGenesisTs int64) (*ForkData, error) { - if err := os.MkdirAll("fork_data/prefork", 0755); err != nil { + if err := os.MkdirAll("fork_data/prepatch", 0755); err != nil { return nil, err } // Define all fork_data file paths - preforkConfig := "fork_data/prefork/config.json" + prepatchConfig := "fork_data/prepatch/config.json" // Validate fork config data - if err := t.ValidateForkConfigData(analysis.LatestNonEmptyBlock, forkConfigBytes); err != nil { + if err := t.ValidateLegacyPrepatchForkConfig(analysis.LatestNonEmptyBlock, forkConfigBytes); err != nil { return nil, err } // Write fork config to file - if err := os.WriteFile(preforkConfig, forkConfigBytes, 0644); err != nil { + if err := os.WriteFile(prepatchConfig, forkConfigBytes, 0644); err != nil { return nil, err } { - preforkLedgersDir := "fork_data/prefork/hf_ledgers" - preforkHashesFile := "fork_data/prefork/hf_ledger_hashes.json" - if err := t.GenerateAndValidatePreforkLedgers(analysis, preforkConfig, preforkLedgersDir, preforkHashesFile); err != nil { + preforkLedgersDir := "fork_data/prepatch/hf_ledgers" + preforkHashesFile := "fork_data/prepatch/hf_ledger_hashes.json" + if err := t.GenerateAndValidateHashesAndLedgers(analysis, prepatchConfig, preforkLedgersDir, preforkHashesFile); err != nil { return nil, err } } - if err := os.MkdirAll("fork_data/postfork", 0755); err != nil { + if err := os.MkdirAll("fork_data/postpatch", 0755); err != nil { return nil, err } - postforkConfig := "fork_data/postfork/config.json" - forkLedgersDir := "fork_data/postfork/hf_ledgers" + postforkConfig := "fork_data/postpatch/config.json" + forkLedgersDir := "fork_data/postpatch/hf_ledgers" // Calculate fork genesis timestamp relative to now (before starting fork network) forkGenesisTs := time.Now().Unix() + int64(t.Config.ForkDelay*60) @@ -154,7 +154,7 @@ func (t *HardforkTest) LegacyForkPhase(analysis *BlockAnalysisResult, forkConfig { preforkGenesisConfigFile := fmt.Sprintf("%s/daemon.json", t.Config.Root) forkHashesFile := "fork_data/hf_ledger_hashes.json" - if err := t.PatchForkConfigAndGenerateLedgersLegacy(analysis, preforkConfig, forkLedgersDir, forkHashesFile, postforkConfig, preforkGenesisConfigFile, forkGenesisTs, mainGenesisTs); err != nil { + if err := t.PatchForkConfigAndGenerateLedgersLegacy(analysis, prepatchConfig, forkLedgersDir, forkHashesFile, postforkConfig, preforkGenesisConfigFile, forkGenesisTs, mainGenesisTs); err != nil { return nil, err } }