Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ledger): Decode block header as proper era type #951

Merged
merged 3 commits into from
Mar 14, 2025
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
1 change: 1 addition & 0 deletions ledger/allegra.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
// Allegra functions
var (
NewAllegraBlockFromCbor = allegra.NewAllegraBlockFromCbor
NewAllegraBlockHeaderFromCbor = allegra.NewAllegraBlockHeaderFromCbor
NewAllegraTransactionFromCbor = allegra.NewAllegraTransactionFromCbor
NewAllegraTransactionBodyFromCbor = allegra.NewAllegraTransactionBodyFromCbor
)
8 changes: 8 additions & 0 deletions ledger/allegra/allegra.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ func NewAllegraBlockFromCbor(data []byte) (*AllegraBlock, error) {
return &allegraBlock, nil
}

func NewAllegraBlockHeaderFromCbor(data []byte) (*AllegraBlockHeader, error) {
var allegraBlockHeader AllegraBlockHeader
if _, err := cbor.Decode(data, &allegraBlockHeader); err != nil {
return nil, fmt.Errorf("Allegra block header decode error: %w", err)
}
return &allegraBlockHeader, nil
}

func NewAllegraTransactionBodyFromCbor(
data []byte,
) (*AllegraTransactionBody, error) {
Expand Down
1 change: 1 addition & 0 deletions ledger/alonzo.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
// Alonzo functions
var (
NewAlonzoBlockFromCbor = alonzo.NewAlonzoBlockFromCbor
NewAlonzoBlockHeaderFromCbor = alonzo.NewAlonzoBlockHeaderFromCbor
NewAlonzoTransactionFromCbor = alonzo.NewAlonzoTransactionFromCbor
NewAlonzoTransactionBodyFromCbor = alonzo.NewAlonzoTransactionBodyFromCbor
NewAlonzoTransactionOutputFromCbor = alonzo.NewAlonzoTransactionOutputFromCbor
Expand Down
8 changes: 8 additions & 0 deletions ledger/alonzo/alonzo.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,14 @@ func NewAlonzoBlockFromCbor(data []byte) (*AlonzoBlock, error) {
return &alonzoBlock, nil
}

func NewAlonzoBlockHeaderFromCbor(data []byte) (*AlonzoBlockHeader, error) {
var alonzoBlockHeader AlonzoBlockHeader
if _, err := cbor.Decode(data, &alonzoBlockHeader); err != nil {
return nil, fmt.Errorf("Alonzo block header decode error: %w", err)
}
return &alonzoBlockHeader, nil
}

func NewAlonzoTransactionBodyFromCbor(
data []byte,
) (*AlonzoTransactionBody, error) {
Expand Down
54 changes: 30 additions & 24 deletions ledger/babbage/babbage.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,33 +145,39 @@ func (b *BabbageBlock) Utxorpc() *utxorpc.Block {
type BabbageBlockHeader struct {
cbor.StructAsArray
cbor.DecodeStoreCbor
hash string
Body struct {
cbor.StructAsArray
BlockNumber uint64
Slot uint64
PrevHash common.Blake2b256
IssuerVkey common.IssuerVkey
VrfKey []byte
VrfResult common.VrfResult
BlockBodySize uint64
BlockBodyHash common.Blake2b256
OpCert struct {
cbor.StructAsArray
HotVkey []byte
SequenceNumber uint32
KesPeriod uint32
Signature []byte
}
ProtoVersion struct {
cbor.StructAsArray
Major uint64
Minor uint64
}
}
hash string
Body BabbageBlockHeaderBody
Signature []byte
}

type BabbageBlockHeaderBody struct {
cbor.StructAsArray
BlockNumber uint64
Slot uint64
PrevHash common.Blake2b256
IssuerVkey common.IssuerVkey
VrfKey []byte
VrfResult common.VrfResult
BlockBodySize uint64
BlockBodyHash common.Blake2b256
OpCert BabbageOpCert
ProtoVersion BabbageProtoVersion
}

type BabbageOpCert struct {
cbor.StructAsArray
HotVkey []byte
SequenceNumber uint32
KesPeriod uint32
Signature []byte
}

type BabbageProtoVersion struct {
cbor.StructAsArray
Major uint64
Minor uint64
}

func (h *BabbageBlockHeader) UnmarshalCBOR(cborData []byte) error {
return h.UnmarshalCbor(cborData, h)
}
Expand Down
16 changes: 12 additions & 4 deletions ledger/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,21 @@ func NewBlockHeaderFromCbor(blockType uint, data []byte) (BlockHeader, error) {
return NewByronEpochBoundaryBlockHeaderFromCbor(data)
case BlockTypeByronMain:
return NewByronMainBlockHeaderFromCbor(data)
// TODO: break into separate cases and parse as specific block header types (#844)
case BlockTypeShelley, BlockTypeAllegra, BlockTypeMary, BlockTypeAlonzo:
case BlockTypeShelley:
return NewShelleyBlockHeaderFromCbor(data)
case BlockTypeBabbage, BlockTypeConway:
case BlockTypeAllegra:
return NewAllegraBlockHeaderFromCbor(data)
case BlockTypeMary:
return NewMaryBlockHeaderFromCbor(data)
case BlockTypeAlonzo:
return NewAlonzoBlockHeaderFromCbor(data)
case BlockTypeBabbage:
return NewBabbageBlockHeaderFromCbor(data)
case BlockTypeConway:
return NewConwayBlockHeaderFromCbor(data)
default:
return nil, fmt.Errorf("unknown node-to-node block type: %d", blockType)
}
return nil, fmt.Errorf("unknown node-to-node block type: %d", blockType)
}

func DetermineBlockType(data []byte) (uint, error) {
Expand Down
142 changes: 142 additions & 0 deletions ledger/block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package ledger

import (
"fmt"
"testing"

"github.com/blinklabs-io/gouroboros/ledger/allegra"
"github.com/blinklabs-io/gouroboros/ledger/babbage"
"github.com/blinklabs-io/gouroboros/ledger/common"
"github.com/blinklabs-io/gouroboros/ledger/mary"
"github.com/blinklabs-io/gouroboros/ledger/shelley"
"github.com/fxamacker/cbor/v2"
)

func mockShelleyCBOR() []byte {
shelleyHeader := shelley.ShelleyBlockHeader{
Body: shelley.ShelleyBlockHeaderBody{
BlockNumber: 12345,
Slot: 67890,
PrevHash: common.Blake2b256{},
IssuerVkey: common.IssuerVkey{},
VrfKey: []byte{0x01, 0x02},
NonceVrf: common.VrfResult{},
LeaderVrf: common.VrfResult{},
BlockBodySize: 512,
BlockBodyHash: common.Blake2b256{},
OpCertHotVkey: []byte{0x03, 0x04},
OpCertSequenceNumber: 10,
OpCertKesPeriod: 20,
OpCertSignature: []byte{0x05, 0x06},
ProtoMajorVersion: 1,
ProtoMinorVersion: 0,
},
Signature: []byte{0x07, 0x08},
}

// Convert to CBOR
data, err := cbor.Marshal(shelleyHeader)
if err != nil {
fmt.Printf("CBOR Encoding Error: %v\n", err)
}
return data
}

func mockAllegraCBOR() []byte {
allegraHeader := allegra.AllegraBlockHeader{ShelleyBlockHeader: ShelleyBlockHeader{}}
data, _ := cbor.Marshal(allegraHeader)
return data
}

func mockMaryCBOR() []byte {
maryHeader := mary.MaryBlockHeader{ShelleyBlockHeader: ShelleyBlockHeader{}}
data, _ := cbor.Marshal(maryHeader)
return data
}

func mockAlonzoCBOR() []byte {
alonzoHeader := AlonzoBlockHeader{ShelleyBlockHeader: ShelleyBlockHeader{}}
data, _ := cbor.Marshal(alonzoHeader)
return data
}

func mockBabbageCBOR() []byte {
babbageHeader := babbage.BabbageBlockHeader{
Body: babbage.BabbageBlockHeaderBody{
BlockNumber: 54321,
Slot: 98765,
PrevHash: common.Blake2b256{},
IssuerVkey: common.IssuerVkey{},
VrfKey: []byte{0x09, 0x10},
VrfResult: common.VrfResult{},
BlockBodySize: 1024,
BlockBodyHash: common.Blake2b256{},
OpCert: babbage.BabbageOpCert{
HotVkey: []byte{0x11, 0x12},
SequenceNumber: 30,
KesPeriod: 40,
Signature: []byte{0x13, 0x14},
},
ProtoVersion: babbage.BabbageProtoVersion{
Major: 2,
Minor: 0,
},
},
Signature: []byte{0x15, 0x16},
}

// Convert to CBOR
data, err := cbor.Marshal(babbageHeader)
if err != nil {
fmt.Printf("CBOR Encoding Error for Babbage: %v\n", err)
}
return data
}

func mockConwayCBOR() []byte {
conwayHeader := ConwayBlockHeader{BabbageBlockHeader: BabbageBlockHeader{}}
data, _ := cbor.Marshal(conwayHeader)
return data
}

func TestNewBlockHeaderFromCbor(t *testing.T) {
tests := []struct {
name string
blockType uint
data []byte
expectErr bool
expectedFn string
}{
{"Shelley Block", BlockTypeShelley, mockShelleyCBOR(), false, "NewShelleyBlockHeaderFromCbor"},
{"Allegra Block", BlockTypeAllegra, mockAllegraCBOR(), false, "NewAllegraBlockHeaderFromCbor"},
{"Mary Block", BlockTypeMary, mockMaryCBOR(), false, "NewMaryBlockHeaderFromCbor"},
{"Alonzo Block", BlockTypeAlonzo, mockAlonzoCBOR(), false, "NewAlonzoBlockHeaderFromCbor"},
{"Babbage Block", BlockTypeBabbage, mockBabbageCBOR(), false, "NewBabbageBlockHeaderFromCbor"},
{"Conway Block", BlockTypeConway, mockConwayCBOR(), false, "NewConwayBlockHeaderFromCbor"},
{"Invalid Block Type", 9999, []byte{0xFF, 0x00, 0x00}, true, "UnknownFunction"},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fmt.Printf("\n Running Test: %s\n", test.name)

header, err := NewBlockHeaderFromCbor(test.blockType, test.data)

if test.expectErr {
if err == nil {
t.Errorf("Expected error for %s, but got none!", test.name)
} else {
fmt.Printf("Expected failure for %s: %v\n", test.name, err)
}
} else {
if err != nil {
t.Errorf("Unexpected error for %s: %v", test.name, err)
} else if header == nil {
t.Errorf("Expected non-nil block header for %s, but got nil", test.name)
} else {
fmt.Printf("Test Passed: %s → %s executed successfully!\n", test.name, test.expectedFn)
}
}
})
}
}
1 change: 1 addition & 0 deletions ledger/mary.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
// Mary functions
var (
NewMaryBlockFromCbor = mary.NewMaryBlockFromCbor
NewMaryBlockHeaderFromCbor = mary.NewMaryBlockHeaderFromCbor
NewMaryTransactionFromCbor = mary.NewMaryTransactionFromCbor
NewMaryTransactionBodyFromCbor = mary.NewMaryTransactionBodyFromCbor
NewMaryTransactionOutputFromCbor = mary.NewMaryTransactionOutputFromCbor
Expand Down
8 changes: 8 additions & 0 deletions ledger/mary/mary.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,14 @@ func NewMaryBlockFromCbor(data []byte) (*MaryBlock, error) {
return &maryBlock, nil
}

func NewMaryBlockHeaderFromCbor(data []byte) (*MaryBlockHeader, error) {
var maryBlockHeader MaryBlockHeader
if _, err := cbor.Decode(data, &maryBlockHeader); err != nil {
return nil, fmt.Errorf("Mary block header decode error: %w", err)
}
return &maryBlockHeader, nil
}

func NewMaryTransactionBodyFromCbor(data []byte) (*MaryTransactionBody, error) {
var maryTx MaryTransactionBody
if _, err := cbor.Decode(data, &maryTx); err != nil {
Expand Down
39 changes: 20 additions & 19 deletions ledger/shelley/shelley.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,28 @@ func (b *ShelleyBlock) Utxorpc() *utxorpc.Block {
type ShelleyBlockHeader struct {
cbor.StructAsArray
cbor.DecodeStoreCbor
hash string
Body struct {
cbor.StructAsArray
BlockNumber uint64
Slot uint64
PrevHash common.Blake2b256
IssuerVkey common.IssuerVkey
VrfKey []byte
NonceVrf common.VrfResult
LeaderVrf common.VrfResult
BlockBodySize uint64
BlockBodyHash common.Blake2b256
OpCertHotVkey []byte
OpCertSequenceNumber uint32
OpCertKesPeriod uint32
OpCertSignature []byte
ProtoMajorVersion uint64
ProtoMinorVersion uint64
}
hash string
Body ShelleyBlockHeaderBody
Signature []byte
}
type ShelleyBlockHeaderBody struct {
cbor.StructAsArray
BlockNumber uint64
Slot uint64
PrevHash common.Blake2b256
IssuerVkey common.IssuerVkey
VrfKey []byte
NonceVrf common.VrfResult
LeaderVrf common.VrfResult
BlockBodySize uint64
BlockBodyHash common.Blake2b256
OpCertHotVkey []byte
OpCertSequenceNumber uint32
OpCertKesPeriod uint32
OpCertSignature []byte
ProtoMajorVersion uint64
ProtoMinorVersion uint64
}

func (h *ShelleyBlockHeader) UnmarshalCBOR(cborData []byte) error {
return h.UnmarshalCbor(cborData, h)
Expand Down