Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion ledger/byron/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package byron
import (
"encoding/base64"
"encoding/json"
"errors"
"io"
"os"
"slices"
Expand All @@ -25,10 +26,14 @@ import (
"github.com/blinklabs-io/gouroboros/ledger/common"
)

type ByronGenesisFtsSeed struct {
Value string
IsObject bool
}
type ByronGenesis struct {
AvvmDistr map[string]string `json:"avvmDistr"`
BlockVersionData ByronGenesisBlockVersionData `json:"blockVersionData"`
FtsSeed string `json:"ftsSeed"`
FtsSeed ByronGenesisFtsSeed `json:"ftsSeed"`
ProtocolConsts ByronGenesisProtocolConsts `json:"protocolConsts"`
StartTime int `json:"startTime"`
BootStakeholders map[string]int `json:"bootStakeholders"`
Expand Down Expand Up @@ -193,3 +198,48 @@ func NewByronGenesisFromFile(path string) (ByronGenesis, error) {
defer f.Close()
return NewByronGenesisFromReader(f)
}

// UnmarshalJSON accepts: "string", {}, or null
// Tries each expected shape and accepts the first that parses cleanly
func (f *ByronGenesisFtsSeed) UnmarshalJSON(b []byte) error {
// Try string
var s string
if err := json.Unmarshal(b, &s); err == nil {
f.Value = s
f.IsObject = false
return nil
}

// Try empty object
var m map[string]any
if err := json.Unmarshal(b, &m); err == nil {
if len(m) == 0 {
f.Value = ""
f.IsObject = true
return nil
}
return errors.New("ftsSeed: non-empty object not supported")
}

// Try null
var v any
if err := json.Unmarshal(b, &v); err == nil && v == nil {
f.Value = ""
f.IsObject = false
return nil
}

return errors.New("ftsSeed: expected string, empty object, or null")
}

func (f ByronGenesisFtsSeed) MarshalJSON() ([]byte, error) {
if f.IsObject {
// serialize as empty object
return []byte(`{}`), nil
}
if f.Value == "" {
// serialize as null
return []byte(`null`), nil
}
return json.Marshal(f.Value)
}
69 changes: 67 additions & 2 deletions ledger/byron/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const byronGenesisConfig = `
"updateProposalThd": "100000000000000",
"updateVoteThd": "1000000000000"
},
"ftsSeed": "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
"ftsSeed": "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
"protocolConsts": {
"k": 2160,
"protocolMagic": 764824073,
Expand Down Expand Up @@ -128,7 +128,10 @@ var expectedGenesisObj = byron.ByronGenesis{
UpdateProposalThd: 100000000000000,
UpdateVoteThd: 1000000000000,
},
FtsSeed: "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
FtsSeed: byron.ByronGenesisFtsSeed{
Value: "76617361206f7061736120736b6f766f726f64612047677572646120626f726f64612070726f766f6461",
IsObject: false,
},
ProtocolConsts: byron.ByronGenesisProtocolConsts{
K: 2160,
ProtocolMagic: 764824073,
Expand Down Expand Up @@ -431,3 +434,65 @@ func TestNewByronGenesisFromReader(t *testing.T) {
)
}
}

func TestGenesis_FtsSeed_EmptyObject(t *testing.T) {
jsonData := `{
"avvmDistr": { "addr1": "1000" },
"blockVersionData": {
"heavyDelThd": "1",
"maxBlockSize": "2",
"maxHeaderSize": "3",
"maxProposalSize": "4",
"maxTxSize": "5",
"mpcThd": "6",
"scriptVersion": 1,
"slotDuration": "7",
"softforkRule": {
"initThd": "8",
"minThd": "9",
"thdDecrement": "10"
},
"txFeePolicy": {
"multiplier": "11",
"summand": "12"
},
"unlockStakeEpoch": "13",
"updateImplicit": "14",
"updateProposalThd": "15",
"updateVoteThd": "16"
},
"ftsSeed": {},
"protocolConsts": {
"k": 1,
"protocolMagic": 42,
"vssMinTtl": 2,
"vssMaxTtl": 10
},
"startTime": 100000,
"bootStakeholders": { "stakeholder1": 1 },
"heavyDelegation": {
"key1": {
"cert": "cert-val",
"delegatePk": "delegate-pk",
"issuerPk": "issuer-pk",
"omega": 5
}
},
"nonAvvmBalances": { "addr2": "2000" },
"vssCerts": {
"cert1": {
"expiryEpoch": 5,
"signature": "sig",
"signingKey": "sign-key",
"vssKey": "vss-key"
}
}
}`
got, err := byron.NewByronGenesisFromReader(strings.NewReader(jsonData))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got.FtsSeed.Value != "" || !got.FtsSeed.IsObject {
t.Fatalf("ftsSeed not parsed as empty object; got=%#v", got.FtsSeed)
}
}
Loading