Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
57 changes: 56 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,53 @@ func NewByronGenesisFromFile(path string) (ByronGenesis, error) {
defer f.Close()
return NewByronGenesisFromReader(f)
}

// UnmarshalJSON accepts: "string", {}, or null
func (f *ByronGenesisFtsSeed) UnmarshalJSON(b []byte) error {
var first byte
for _, c := range b {
if c > ' ' {
first = c
break
}
}
switch first {
case '"':
// Normal string case
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
f.Value = s
f.IsObject = false
return nil
case '{':
// empty object case
var m map[string]any
if err := json.Unmarshal(b, &m); err != nil {
return err
}
if len(m) > 0 {
return errors.New("ftsSeed: expected empty object or string")
}
f.Value = ""
f.IsObject = true
return nil
case 'n':
return nil
default:
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