Skip to content

Commit a57c097

Browse files
authored
fix(ledger/byron): ByronGenesis.FtsSeed can be object (#1190)
Signed-off-by: Akhil Repala <[email protected]>
1 parent bb1271a commit a57c097

File tree

2 files changed

+118
-3
lines changed

2 files changed

+118
-3
lines changed

ledger/byron/genesis.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package byron
1717
import (
1818
"encoding/base64"
1919
"encoding/json"
20+
"errors"
2021
"io"
2122
"os"
2223
"slices"
@@ -25,10 +26,14 @@ import (
2526
"github.com/blinklabs-io/gouroboros/ledger/common"
2627
)
2728

29+
type ByronGenesisFtsSeed struct {
30+
Value string
31+
IsObject bool
32+
}
2833
type ByronGenesis struct {
2934
AvvmDistr map[string]string `json:"avvmDistr"`
3035
BlockVersionData ByronGenesisBlockVersionData `json:"blockVersionData"`
31-
FtsSeed string `json:"ftsSeed"`
36+
FtsSeed ByronGenesisFtsSeed `json:"ftsSeed"`
3237
ProtocolConsts ByronGenesisProtocolConsts `json:"protocolConsts"`
3338
StartTime int `json:"startTime"`
3439
BootStakeholders map[string]int `json:"bootStakeholders"`
@@ -193,3 +198,48 @@ func NewByronGenesisFromFile(path string) (ByronGenesis, error) {
193198
defer f.Close()
194199
return NewByronGenesisFromReader(f)
195200
}
201+
202+
// UnmarshalJSON accepts: "string", {}, or null
203+
// Tries each expected shape and accepts the first that parses cleanly
204+
func (f *ByronGenesisFtsSeed) UnmarshalJSON(b []byte) error {
205+
// Try string
206+
var s string
207+
if err := json.Unmarshal(b, &s); err == nil {
208+
f.Value = s
209+
f.IsObject = false
210+
return nil
211+
}
212+
213+
// Try empty object
214+
var m map[string]any
215+
if err := json.Unmarshal(b, &m); err == nil {
216+
if len(m) == 0 {
217+
f.Value = ""
218+
f.IsObject = true
219+
return nil
220+
}
221+
return errors.New("ftsSeed: non-empty object not supported")
222+
}
223+
224+
// Try null
225+
var v any
226+
if err := json.Unmarshal(b, &v); err == nil && v == nil {
227+
f.Value = ""
228+
f.IsObject = false
229+
return nil
230+
}
231+
232+
return errors.New("ftsSeed: expected string, empty object, or null")
233+
}
234+
235+
func (f ByronGenesisFtsSeed) MarshalJSON() ([]byte, error) {
236+
if f.IsObject {
237+
// serialize as empty object
238+
return []byte(`{}`), nil
239+
}
240+
if f.Value == "" {
241+
// serialize as null
242+
return []byte(`null`), nil
243+
}
244+
return json.Marshal(f.Value)
245+
}

ledger/byron/genesis_test.go

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const byronGenesisConfig = `
5555
"updateProposalThd": "100000000000000",
5656
"updateVoteThd": "1000000000000"
5757
},
58-
"ftsSeed": "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
58+
"ftsSeed": "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
5959
"protocolConsts": {
6060
"k": 2160,
6161
"protocolMagic": 764824073,
@@ -128,7 +128,10 @@ var expectedGenesisObj = byron.ByronGenesis{
128128
UpdateProposalThd: 100000000000000,
129129
UpdateVoteThd: 1000000000000,
130130
},
131-
FtsSeed: "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
131+
FtsSeed: byron.ByronGenesisFtsSeed{
132+
Value: "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
133+
IsObject: false,
134+
},
132135
ProtocolConsts: byron.ByronGenesisProtocolConsts{
133136
K: 2160,
134137
ProtocolMagic: 764824073,
@@ -431,3 +434,65 @@ func TestNewByronGenesisFromReader(t *testing.T) {
431434
)
432435
}
433436
}
437+
438+
func TestGenesis_FtsSeed_EmptyObject(t *testing.T) {
439+
jsonData := `{
440+
"avvmDistr": { "addr1": "1000" },
441+
"blockVersionData": {
442+
"heavyDelThd": "1",
443+
"maxBlockSize": "2",
444+
"maxHeaderSize": "3",
445+
"maxProposalSize": "4",
446+
"maxTxSize": "5",
447+
"mpcThd": "6",
448+
"scriptVersion": 1,
449+
"slotDuration": "7",
450+
"softforkRule": {
451+
"initThd": "8",
452+
"minThd": "9",
453+
"thdDecrement": "10"
454+
},
455+
"txFeePolicy": {
456+
"multiplier": "11",
457+
"summand": "12"
458+
},
459+
"unlockStakeEpoch": "13",
460+
"updateImplicit": "14",
461+
"updateProposalThd": "15",
462+
"updateVoteThd": "16"
463+
},
464+
"ftsSeed": {},
465+
"protocolConsts": {
466+
"k": 1,
467+
"protocolMagic": 42,
468+
"vssMinTtl": 2,
469+
"vssMaxTtl": 10
470+
},
471+
"startTime": 100000,
472+
"bootStakeholders": { "stakeholder1": 1 },
473+
"heavyDelegation": {
474+
"key1": {
475+
"cert": "cert-val",
476+
"delegatePk": "delegate-pk",
477+
"issuerPk": "issuer-pk",
478+
"omega": 5
479+
}
480+
},
481+
"nonAvvmBalances": { "addr2": "2000" },
482+
"vssCerts": {
483+
"cert1": {
484+
"expiryEpoch": 5,
485+
"signature": "sig",
486+
"signingKey": "sign-key",
487+
"vssKey": "vss-key"
488+
}
489+
}
490+
}`
491+
got, err := byron.NewByronGenesisFromReader(strings.NewReader(jsonData))
492+
if err != nil {
493+
t.Fatalf("unexpected error: %v", err)
494+
}
495+
if got.FtsSeed.Value != "" || !got.FtsSeed.IsObject {
496+
t.Fatalf("ftsSeed not parsed as empty object; got=%#v", got.FtsSeed)
497+
}
498+
}

0 commit comments

Comments
 (0)