Skip to content

Commit f8d64da

Browse files
committed
fix: resolve protocol parameter type errors across era transitions
- Add era-safe GetEraById() to prevent crashes from unknown era IDs - Implement genesis protocol parameter bootstrapping for empty database - Fix database error handling in GetPParams - Support complete era transition chain (Byron -> Conway) Signed-off-by: GitHub Copilot <noreply@github.com> Signed-off-by: Chris Gianelloni <wolf31o2@blinklabs.io>
1 parent f5fa6a5 commit f8d64da

File tree

13 files changed

+325
-47
lines changed

13 files changed

+325
-47
lines changed

database/models/account_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ import (
2525

2626
func TestAccount_String(t *testing.T) {
2727
tests := []struct {
28-
name string
29-
stakingKey []byte
30-
wantErr bool
31-
wantErrMsg string
32-
validateAddr bool
33-
expectedHRP string
28+
name string
29+
stakingKey []byte
30+
wantErr bool
31+
wantErrMsg string
32+
validateAddr bool
33+
expectedHRP string
3434
}{
3535
{
3636
name: "valid staking key - 28 bytes",

database/pparams.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (d *Database) GetPParams(
3030
var err error
3131
if txn == nil {
3232
pparams, ppErr := d.metadata.GetPParams(epoch, nil)
33-
if err != nil {
33+
if ppErr != nil {
3434
return ret, ppErr
3535
}
3636
if len(pparams) == 0 {
@@ -41,7 +41,7 @@ func (d *Database) GetPParams(
4141
ret, err = decodeFunc(tmpPParams.Cbor)
4242
} else {
4343
pparams, ppErr := d.metadata.GetPParams(epoch, txn.Metadata())
44-
if err != nil {
44+
if ppErr != nil {
4545
return ret, ppErr
4646
}
4747
if len(pparams) == 0 {
@@ -98,6 +98,12 @@ func (d *Database) ApplyPParamUpdates(
9898
return err
9999
}
100100
// Update current pparams
101+
if *currentPParams == nil {
102+
return fmt.Errorf(
103+
"current PParams is nil - cannot apply protocol parameter updates for epoch %d",
104+
epoch,
105+
)
106+
}
101107
newPParams, err := updateFunc(
102108
*currentPParams,
103109
tmpPParamUpdate,

ledger/certs.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
package ledger
1616

1717
import (
18+
"errors"
1819
"fmt"
1920

2021
"github.com/blinklabs-io/dingo/database"
22+
"github.com/blinklabs-io/dingo/ledger/eras"
2123
"github.com/blinklabs-io/gouroboros/ledger"
2224
lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
2325
pcommon "github.com/blinklabs-io/gouroboros/protocol/common"
@@ -27,15 +29,15 @@ func (ls *LedgerState) processTransactionCertificates(
2729
txn *database.Txn,
2830
blockPoint pcommon.Point,
2931
certs []ledger.Certificate,
32+
blockEraId uint,
3033
) error {
3134
var tmpCert lcommon.Certificate
3235
for _, tmpCert = range certs {
33-
certDeposit, err := ls.currentEra.CertDepositFunc(
34-
tmpCert,
35-
ls.currentPParams,
36-
)
36+
// Calculate certificate deposits using the block's era functions
37+
// This ensures we use the correct era-specific logic instead of current era
38+
certDeposit, err := ls.calculateCertificateDeposit(tmpCert, blockEraId)
3739
if err != nil {
38-
return fmt.Errorf("failed load cert deposit func: %w", err)
40+
return fmt.Errorf("get certificate deposit: %w", err)
3941
}
4042
switch cert := tmpCert.(type) {
4143
case *lcommon.DeregistrationCertificate:
@@ -189,3 +191,36 @@ func (ls *LedgerState) processTransactionCertificates(
189191
}
190192
return nil
191193
}
194+
195+
// calculateCertificateDeposit calculates the certificate deposit using the appropriate era's
196+
// certificate deposit function. This ensures we use the correct era-specific logic instead
197+
// of always using the current era, which may not match the block's era for historical data.
198+
func (ls *LedgerState) calculateCertificateDeposit(
199+
cert lcommon.Certificate,
200+
blockEraId uint,
201+
) (uint64, error) {
202+
// Get the era descriptor for this block
203+
blockEra := eras.GetEraById(blockEraId)
204+
if blockEra == nil {
205+
return 0, fmt.Errorf("unknown era ID %d", blockEraId)
206+
}
207+
208+
// If this era doesn't support certificates (like Byron), return 0
209+
if blockEra.CertDepositFunc == nil {
210+
return 0, nil
211+
}
212+
213+
// Use the block era's certificate deposit function with current protocol parameters
214+
certDeposit, err := blockEra.CertDepositFunc(cert, ls.currentPParams)
215+
if err != nil {
216+
// Handle era type mismatch - this can happen when processing historical blocks
217+
// with newer protocol parameters, or when the certificate type didn't exist
218+
// in that era
219+
if errors.Is(err, eras.ErrIncompatibleProtocolParams) {
220+
return 0, nil
221+
}
222+
return 0, err
223+
}
224+
225+
return certDeposit, nil
226+
}

ledger/delta.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type TransactionRecord struct {
3030

3131
type LedgerDelta struct {
3232
Point ocommon.Point
33+
BlockEraId uint
3334
Transactions []TransactionRecord
3435
}
3536

@@ -100,6 +101,7 @@ func (d *LedgerDelta) apply(ls *LedgerState, txn *database.Txn) error {
100101
txn,
101102
d.Point,
102103
tr.Tx.Certificates(),
104+
d.BlockEraId,
103105
)
104106
if err != nil {
105107
return fmt.Errorf("process transaction certificates: %w", err)

ledger/eras/allegra.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func CertDepositAllegra(
125125
) (uint64, error) {
126126
tmpPparams, ok := pp.(*allegra.AllegraProtocolParameters)
127127
if !ok {
128-
return 0, errors.New("pparams are not expected type")
128+
return 0, ErrIncompatibleProtocolParams
129129
}
130130
switch cert.(type) {
131131
case *lcommon.PoolRegistrationCertificate:

ledger/eras/alonzo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func CertDepositAlonzo(
129129
) (uint64, error) {
130130
tmpPparams, ok := pp.(*alonzo.AlonzoProtocolParameters)
131131
if !ok {
132-
return 0, errors.New("pparams are not expected type")
132+
return 0, ErrIncompatibleProtocolParams
133133
}
134134
switch cert.(type) {
135135
case *lcommon.PoolRegistrationCertificate:

ledger/eras/babbage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func CertDepositBabbage(
125125
) (uint64, error) {
126126
tmpPparams, ok := pp.(*babbage.BabbageProtocolParameters)
127127
if !ok {
128-
return 0, errors.New("pparams are not expected type")
128+
return 0, ErrIncompatibleProtocolParams
129129
}
130130
switch cert.(type) {
131131
case *lcommon.PoolRegistrationCertificate:

ledger/eras/conway.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func CertDepositConway(
133133
) (uint64, error) {
134134
tmpPparams, ok := pp.(*conway.ConwayProtocolParameters)
135135
if !ok {
136-
return 0, errors.New("pparams are not expected type")
136+
return 0, ErrIncompatibleProtocolParams
137137
}
138138
switch cert.(type) {
139139
case *lcommon.PoolRegistrationCertificate:

ledger/eras/eras.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515
package eras
1616

1717
import (
18+
"errors"
19+
1820
"github.com/blinklabs-io/dingo/config/cardano"
1921
"github.com/blinklabs-io/gouroboros/ledger"
2022
lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
2123
)
2224

25+
var ErrIncompatibleProtocolParams = errors.New("pparams are not expected type")
26+
2327
type EraDesc struct {
2428
DecodePParamsFunc func([]byte) (lcommon.ProtocolParameters, error)
2529
DecodePParamsUpdateFunc func([]byte) (any, error)
@@ -57,3 +61,14 @@ var ProtocolMajorVersionToEra = map[uint]EraDesc{
5761
9: ConwayEraDesc,
5862
10: ConwayEraDesc,
5963
}
64+
65+
// GetEraById returns the era descriptor for the given era ID.
66+
// Returns nil if the era ID is not found.
67+
func GetEraById(eraId uint) *EraDesc {
68+
for i := range Eras {
69+
if Eras[i].Id == eraId {
70+
return &Eras[i]
71+
}
72+
}
73+
return nil
74+
}

0 commit comments

Comments
 (0)