Skip to content

Commit 86b4959

Browse files
authored
fix(nonce): use per-network genesis nonce seed instead of hardcoded mainnet (#118)
The nonce evolution was seeded with mainnet's Shelley genesis hash for ALL networks. Preview (and preprod) have different genesis nonces, so every computed epoch nonce was wrong from epoch 0 onwards. Add per-network genesis nonce constants (verified against Koios): mainnet: 1a3be38b... (epoch 208) preprod: 162d29c4... (epoch 4) preview: 363498d1... (epoch 0) Add shelleyStartForNetwork() and genesisNonceForNetwork() helpers. Replace all hardcoded ShelleyGenesisHash references in ComputeEpochNonce, BackfillNonces, and NonceIntegrityCheck with network-aware lookups. Add PreviewShelleyStartEpoch = 0 (preview has no Byron era).
1 parent 82ad6b3 commit 86b4959

File tree

3 files changed

+52
-27
lines changed

3 files changed

+52
-27
lines changed

comprehensive_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,8 @@ func TestHashConcatNonCommutative(t *testing.T) {
342342
}
343343

344344
func TestInitialNonceModes(t *testing.T) {
345-
full := initialNonce(true)
346-
lite := initialNonce(false)
345+
full := initialNonce(true, MainnetNetworkMagic)
346+
lite := initialNonce(false, MainnetNetworkMagic)
347347

348348
if len(full) != 32 {
349349
t.Fatalf("full mode nonce length: %d", len(full))
@@ -1639,7 +1639,7 @@ func TestResyncFromDB_BlockCountMismatch(t *testing.T) {
16391639
}
16401640

16411641
// Also need a previous epoch nonce for the recompute to start from
1642-
prevNonce := initialNonce(true)
1642+
prevNonce := initialNonce(true, MainnetNetworkMagic)
16431643
if err := store.UpsertEvolvingNonce(ctx, epoch-1, prevNonce, 100); err != nil {
16441644
t.Fatalf("UpsertEvolvingNonce prev: %v", err)
16451645
}

leaderlog.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ const (
4343
// PreprodShelleyStartEpoch is the first Shelley epoch on preprod
4444
PreprodShelleyStartEpoch = 4
4545

46+
// PreviewShelleyStartEpoch — preview has no Byron era, starts at epoch 0
47+
PreviewShelleyStartEpoch = 0
48+
4649
// BabbageStartEpoch is the first Babbage (CPraos) epoch on mainnet (Vasil hard fork)
4750
BabbageStartEpoch = 365
4851

nonce.go

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,40 @@ import (
1616
"golang.org/x/crypto/blake2b"
1717
)
1818

19-
// ShelleyGenesisHash is the hash of the Shelley genesis block on mainnet.
20-
// Used as the initial eta_v seed for full chain sync nonce evolution.
21-
const ShelleyGenesisHash = "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81"
19+
// Network-specific genesis nonces — the initial eta_v seed for nonce evolution.
20+
// Each network's first post-Byron epoch nonce is determined by its genesis config.
21+
const (
22+
MainnetGenesisNonce = "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81"
23+
PreprodGenesisNonce = "162d29c4e1cf6b8a84f2d692e67a3ac6bc7851bc3e6e4afe64d15778bed8bd86"
24+
PreviewGenesisNonce = "363498d1024f84bb39d3fa9593ce391483cb40d479b87233f868d6e57c3a400d"
25+
)
26+
27+
// ShelleyGenesisHash is an alias for MainnetGenesisNonce (backward compat).
28+
const ShelleyGenesisHash = MainnetGenesisNonce
29+
30+
// genesisNonceForNetwork returns the initial eta_v seed for the given network.
31+
func genesisNonceForNetwork(networkMagic int) string {
32+
switch networkMagic {
33+
case PreprodNetworkMagic:
34+
return PreprodGenesisNonce
35+
case PreviewNetworkMagic:
36+
return PreviewGenesisNonce
37+
default:
38+
return MainnetGenesisNonce
39+
}
40+
}
41+
42+
// shelleyStartForNetwork returns the first post-Byron epoch for the given network.
43+
func shelleyStartForNetwork(networkMagic int) int {
44+
switch networkMagic {
45+
case PreprodNetworkMagic:
46+
return PreprodShelleyStartEpoch
47+
case PreviewNetworkMagic:
48+
return PreviewShelleyStartEpoch
49+
default:
50+
return ShelleyStartEpoch
51+
}
52+
}
2253

2354
// knownEpochNonces overrides self-computed nonces for epochs that require
2455
// protocol parameters we don't track (e.g., extra_entropy).
@@ -64,17 +95,17 @@ func NewNonceTracker(store Store, koiosClient *koios.Client, epoch, networkMagic
6495
nt.blockCount = blockCount
6596
log.Printf("Restored evolving nonce for epoch %d (block count: %d)", epoch, blockCount)
6697
} else {
67-
nt.evolvingNonce = initialNonce(fullMode)
98+
nt.evolvingNonce = initialNonce(fullMode, networkMagic)
6899
log.Printf("Starting fresh nonce tracking for epoch %d (full=%v)", epoch, fullMode)
69100
}
70101

71102
return nt
72103
}
73104

74-
// initialNonce returns the initial eta_v seed based on mode.
75-
func initialNonce(fullMode bool) []byte {
105+
// initialNonce returns the initial eta_v seed based on mode and network.
106+
func initialNonce(fullMode bool, networkMagic int) []byte {
76107
if fullMode {
77-
seed, _ := hex.DecodeString(ShelleyGenesisHash)
108+
seed, _ := hex.DecodeString(genesisNonceForNetwork(networkMagic))
78109
return seed
79110
}
80111
return make([]byte, 32)
@@ -301,7 +332,7 @@ func (nt *NonceTracker) ResyncFromDB() {
301332
}
302333
}
303334
if etaV == nil {
304-
etaV = initialNonce(nt.fullMode)
335+
etaV = initialNonce(nt.fullMode, nt.networkMagic)
305336
}
306337

307338
vrfBlocks, vrfErr := nt.store.GetVrfOutputsForEpoch(ctx, epoch)
@@ -344,7 +375,7 @@ func (nt *NonceTracker) RecomputeCurrentEpochNonce(ctx context.Context, epoch in
344375
}
345376
}
346377
if etaV == nil {
347-
etaV = initialNonce(nt.fullMode)
378+
etaV = initialNonce(nt.fullMode, nt.networkMagic)
348379
log.Printf("RecomputeCurrentEpochNonce: no previous epoch nonce, using initial seed")
349380
}
350381

@@ -500,15 +531,12 @@ func (nt *NonceTracker) ComputeEpochNonce(ctx context.Context, targetEpoch int)
500531
return nonce, nil
501532
}
502533

503-
shelleyStart := ShelleyStartEpoch
504-
if nt.networkMagic == PreprodNetworkMagic {
505-
shelleyStart = PreprodShelleyStartEpoch
506-
}
534+
shelleyStart := shelleyStartForNetwork(nt.networkMagic)
507535
if targetEpoch <= shelleyStart {
508536
return nil, fmt.Errorf("cannot compute nonce for epoch %d (shelley starts at %d)", targetEpoch, shelleyStart)
509537
}
510538

511-
genesisHash, _ := hex.DecodeString(ShelleyGenesisHash)
539+
genesisHash, _ := hex.DecodeString(genesisNonceForNetwork(nt.networkMagic))
512540
etaV := make([]byte, 32)
513541
copy(etaV, genesisHash)
514542
eta0 := make([]byte, 32)
@@ -616,12 +644,9 @@ func (nt *NonceTracker) ComputeEpochNonce(ctx context.Context, targetEpoch int)
616644
// already have a final_nonce. After completion, verifies the latest nonce
617645
// against Koios as an integrity check.
618646
func (nt *NonceTracker) BackfillNonces(ctx context.Context) error {
619-
shelleyStart := ShelleyStartEpoch
620-
if nt.networkMagic == PreprodNetworkMagic {
621-
shelleyStart = PreprodShelleyStartEpoch
622-
}
647+
shelleyStart := shelleyStartForNetwork(nt.networkMagic)
623648

624-
genesisHash, _ := hex.DecodeString(ShelleyGenesisHash)
649+
genesisHash, _ := hex.DecodeString(genesisNonceForNetwork(nt.networkMagic))
625650
etaV := make([]byte, 32)
626651
copy(etaV, genesisHash)
627652
etaC := make([]byte, 32)
@@ -790,12 +815,9 @@ type IntegrityReport struct {
790815
// compares each against both the locally stored nonce and the Koios API.
791816
// This is the definitive end-to-end verification of the nonce pipeline.
792817
func (nt *NonceTracker) NonceIntegrityCheck(ctx context.Context) (*IntegrityReport, error) {
793-
shelleyStart := ShelleyStartEpoch
794-
if nt.networkMagic == PreprodNetworkMagic {
795-
shelleyStart = PreprodShelleyStartEpoch
796-
}
818+
shelleyStart := shelleyStartForNetwork(nt.networkMagic)
797819

798-
genesisHash, _ := hex.DecodeString(ShelleyGenesisHash)
820+
genesisHash, _ := hex.DecodeString(genesisNonceForNetwork(nt.networkMagic))
799821
etaV := make([]byte, 32)
800822
copy(etaV, genesisHash)
801823
etaC := make([]byte, 32)

0 commit comments

Comments
 (0)