From 79f7c8ba09fd4f4df705ddc87b9891ad555fd56c Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Wed, 30 Oct 2024 15:43:36 -0500 Subject: [PATCH] feat: track and apply on-chain pparam updates --- go.mod | 2 +- go.sum | 4 ++-- state/eras/allegra.go | 31 +++++++++++++++++++++++++++---- state/eras/alonzo.go | 31 +++++++++++++++++++++++++++---- state/eras/babbage.go | 31 +++++++++++++++++++++++++++---- state/eras/conway.go | 31 +++++++++++++++++++++++++++---- state/eras/eras.go | 10 ++++++---- state/eras/mary.go | 31 +++++++++++++++++++++++++++---- state/eras/shelley.go | 33 +++++++++++++++++++++++++++++---- state/state.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 216 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 719d485a..73b38bdb 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22 toolchain go1.22.6 require ( - github.com/blinklabs-io/gouroboros v0.103.0 + github.com/blinklabs-io/gouroboros v0.103.1 github.com/blinklabs-io/ouroboros-mock v0.3.5 github.com/dgraph-io/badger/v4 v4.3.1 github.com/glebarez/sqlite v1.11.0 diff --git a/go.sum b/go.sum index 7a873e4c..80cea93e 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blinklabs-io/gouroboros v0.103.0 h1:mA/RJ7CeZEE41RQNeMoip/2BbcB1WC2O8Nd+92m4vHY= -github.com/blinklabs-io/gouroboros v0.103.0/go.mod h1:wjiNCbZ2uQy9DGfLCgEgqagHxNBAv5UYsOdRBgoi3SU= +github.com/blinklabs-io/gouroboros v0.103.1 h1:D/3Hlr09kw/cYM8gt6t7jVlTfDCXjb25nHL5V2l/3kc= +github.com/blinklabs-io/gouroboros v0.103.1/go.mod h1:wjiNCbZ2uQy9DGfLCgEgqagHxNBAv5UYsOdRBgoi3SU= github.com/blinklabs-io/ouroboros-mock v0.3.5 h1:/KWbSoH8Pjrd9uxOH7mVbI7XFsDCNW/O9FtLlvJDUpQ= github.com/blinklabs-io/ouroboros-mock v0.3.5/go.mod h1:JtUQ3Luo22hCnGBxuxNp6JaUx63VxidxWwmcaVMremw= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= diff --git a/state/eras/allegra.go b/state/eras/allegra.go index c50fe168..b243e1ed 100644 --- a/state/eras/allegra.go +++ b/state/eras/allegra.go @@ -24,10 +24,12 @@ import ( ) var AllegraEraDesc = EraDesc{ - Id: allegra.EraIdAllegra, - Name: allegra.EraNameAllegra, - DecodePParamsFunc: DecodePParamsAllegra, - HardForkFunc: HardForkAllegra, + Id: allegra.EraIdAllegra, + Name: allegra.EraNameAllegra, + DecodePParamsFunc: DecodePParamsAllegra, + DecodePParamsUpdateFunc: DecodePParamsUpdateAllegra, + PParamsUpdateFunc: PParamsUpdateAllegra, + HardForkFunc: HardForkAllegra, } func DecodePParamsAllegra(data []byte) (any, error) { @@ -38,6 +40,27 @@ func DecodePParamsAllegra(data []byte) (any, error) { return ret, nil } +func DecodePParamsUpdateAllegra(data []byte) (any, error) { + var ret allegra.AllegraProtocolParameterUpdate + if _, err := cbor.Decode(data, &ret); err != nil { + return nil, err + } + return ret, nil +} + +func PParamsUpdateAllegra(currentPParams any, pparamsUpdate any) (any, error) { + allegraPParams, ok := currentPParams.(allegra.AllegraProtocolParameters) + if !ok { + return nil, fmt.Errorf("current PParams (%T) is not expected type", currentPParams) + } + allegraPParamsUpdate, ok := pparamsUpdate.(allegra.AllegraProtocolParameterUpdate) + if !ok { + return nil, fmt.Errorf("PParams update (%T) is not expected type", pparamsUpdate) + } + allegraPParams.Update(&allegraPParamsUpdate) + return allegraPParams, nil +} + func HardForkAllegra(nodeConfig *cardano.CardanoNodeConfig, prevPParams any) (any, error) { shelleyPParams, ok := prevPParams.(shelley.ShelleyProtocolParameters) if !ok { diff --git a/state/eras/alonzo.go b/state/eras/alonzo.go index ac7e2031..9f2c995e 100644 --- a/state/eras/alonzo.go +++ b/state/eras/alonzo.go @@ -24,10 +24,12 @@ import ( ) var AlonzoEraDesc = EraDesc{ - Id: alonzo.EraIdAlonzo, - Name: alonzo.EraNameAlonzo, - DecodePParamsFunc: DecodePParamsAlonzo, - HardForkFunc: HardForkAlonzo, + Id: alonzo.EraIdAlonzo, + Name: alonzo.EraNameAlonzo, + DecodePParamsFunc: DecodePParamsAlonzo, + DecodePParamsUpdateFunc: DecodePParamsUpdateAlonzo, + PParamsUpdateFunc: PParamsUpdateAlonzo, + HardForkFunc: HardForkAlonzo, } func DecodePParamsAlonzo(data []byte) (any, error) { @@ -38,6 +40,27 @@ func DecodePParamsAlonzo(data []byte) (any, error) { return ret, nil } +func DecodePParamsUpdateAlonzo(data []byte) (any, error) { + var ret alonzo.AlonzoProtocolParameterUpdate + if _, err := cbor.Decode(data, &ret); err != nil { + return nil, err + } + return ret, nil +} + +func PParamsUpdateAlonzo(currentPParams any, pparamsUpdate any) (any, error) { + alonzoPParams, ok := currentPParams.(alonzo.AlonzoProtocolParameters) + if !ok { + return nil, fmt.Errorf("current PParams (%T) is not expected type", currentPParams) + } + alonzoPParamsUpdate, ok := pparamsUpdate.(alonzo.AlonzoProtocolParameterUpdate) + if !ok { + return nil, fmt.Errorf("PParams update (%T) is not expected type", pparamsUpdate) + } + alonzoPParams.Update(&alonzoPParamsUpdate) + return alonzoPParams, nil +} + func HardForkAlonzo(nodeConfig *cardano.CardanoNodeConfig, prevPParams any) (any, error) { maryPParams, ok := prevPParams.(mary.MaryProtocolParameters) if !ok { diff --git a/state/eras/babbage.go b/state/eras/babbage.go index 4af5351b..88eef10d 100644 --- a/state/eras/babbage.go +++ b/state/eras/babbage.go @@ -24,10 +24,12 @@ import ( ) var BabbageEraDesc = EraDesc{ - Id: babbage.EraIdBabbage, - Name: babbage.EraNameBabbage, - DecodePParamsFunc: DecodePParamsBabbage, - HardForkFunc: HardForkBabbage, + Id: babbage.EraIdBabbage, + Name: babbage.EraNameBabbage, + DecodePParamsFunc: DecodePParamsBabbage, + DecodePParamsUpdateFunc: DecodePParamsUpdateBabbage, + PParamsUpdateFunc: PParamsUpdateBabbage, + HardForkFunc: HardForkBabbage, } func DecodePParamsBabbage(data []byte) (any, error) { @@ -38,6 +40,27 @@ func DecodePParamsBabbage(data []byte) (any, error) { return ret, nil } +func DecodePParamsUpdateBabbage(data []byte) (any, error) { + var ret babbage.BabbageProtocolParameterUpdate + if _, err := cbor.Decode(data, &ret); err != nil { + return nil, err + } + return ret, nil +} + +func PParamsUpdateBabbage(currentPParams any, pparamsUpdate any) (any, error) { + babbagePParams, ok := currentPParams.(babbage.BabbageProtocolParameters) + if !ok { + return nil, fmt.Errorf("current PParams (%T) is not expected type", currentPParams) + } + babbagePParamsUpdate, ok := pparamsUpdate.(babbage.BabbageProtocolParameterUpdate) + if !ok { + return nil, fmt.Errorf("PParams update (%T) is not expected type", pparamsUpdate) + } + babbagePParams.Update(&babbagePParamsUpdate) + return babbagePParams, nil +} + func HardForkBabbage(nodeConfig *cardano.CardanoNodeConfig, prevPParams any) (any, error) { alonzoPParams, ok := prevPParams.(alonzo.AlonzoProtocolParameters) if !ok { diff --git a/state/eras/conway.go b/state/eras/conway.go index 7a5c3f4c..2a82f968 100644 --- a/state/eras/conway.go +++ b/state/eras/conway.go @@ -24,10 +24,12 @@ import ( ) var ConwayEraDesc = EraDesc{ - Id: conway.EraIdConway, - Name: conway.EraNameConway, - DecodePParamsFunc: DecodePParamsConway, - HardForkFunc: HardForkConway, + Id: conway.EraIdConway, + Name: conway.EraNameConway, + DecodePParamsFunc: DecodePParamsConway, + DecodePParamsUpdateFunc: DecodePParamsUpdateConway, + PParamsUpdateFunc: PParamsUpdateConway, + HardForkFunc: HardForkConway, } func DecodePParamsConway(data []byte) (any, error) { @@ -38,6 +40,27 @@ func DecodePParamsConway(data []byte) (any, error) { return ret, nil } +func DecodePParamsUpdateConway(data []byte) (any, error) { + var ret conway.ConwayProtocolParameterUpdate + if _, err := cbor.Decode(data, &ret); err != nil { + return nil, err + } + return ret, nil +} + +func PParamsUpdateConway(currentPParams any, pparamsUpdate any) (any, error) { + conwayPParams, ok := currentPParams.(conway.ConwayProtocolParameters) + if !ok { + return nil, fmt.Errorf("current PParams (%T) is not expected type", currentPParams) + } + conwayPParamsUpdate, ok := pparamsUpdate.(conway.ConwayProtocolParameterUpdate) + if !ok { + return nil, fmt.Errorf("PParams update (%T) is not expected type", pparamsUpdate) + } + conwayPParams.Update(&conwayPParamsUpdate) + return conwayPParams, nil +} + func HardForkConway(nodeConfig *cardano.CardanoNodeConfig, prevPParams any) (any, error) { babbagePParams, ok := prevPParams.(babbage.BabbageProtocolParameters) if !ok { diff --git a/state/eras/eras.go b/state/eras/eras.go index 41b73f2e..9c366d8a 100644 --- a/state/eras/eras.go +++ b/state/eras/eras.go @@ -17,10 +17,12 @@ package eras import "github.com/blinklabs-io/node/config/cardano" type EraDesc struct { - Id uint - Name string - DecodePParamsFunc func([]byte) (any, error) - HardForkFunc func(*cardano.CardanoNodeConfig, any) (any, error) + Id uint + Name string + DecodePParamsFunc func([]byte) (any, error) + DecodePParamsUpdateFunc func([]byte) (any, error) + PParamsUpdateFunc func(any, any) (any, error) + HardForkFunc func(*cardano.CardanoNodeConfig, any) (any, error) } var Eras = []EraDesc{ diff --git a/state/eras/mary.go b/state/eras/mary.go index 8a6875c1..b3020498 100644 --- a/state/eras/mary.go +++ b/state/eras/mary.go @@ -24,10 +24,12 @@ import ( ) var MaryEraDesc = EraDesc{ - Id: mary.EraIdMary, - Name: mary.EraNameMary, - DecodePParamsFunc: DecodePParamsMary, - HardForkFunc: HardForkMary, + Id: mary.EraIdMary, + Name: mary.EraNameMary, + DecodePParamsFunc: DecodePParamsMary, + DecodePParamsUpdateFunc: DecodePParamsUpdateMary, + PParamsUpdateFunc: PParamsUpdateMary, + HardForkFunc: HardForkMary, } func DecodePParamsMary(data []byte) (any, error) { @@ -38,6 +40,27 @@ func DecodePParamsMary(data []byte) (any, error) { return ret, nil } +func DecodePParamsUpdateMary(data []byte) (any, error) { + var ret mary.MaryProtocolParameterUpdate + if _, err := cbor.Decode(data, &ret); err != nil { + return nil, err + } + return ret, nil +} + +func PParamsUpdateMary(currentPParams any, pparamsUpdate any) (any, error) { + maryPParams, ok := currentPParams.(mary.MaryProtocolParameters) + if !ok { + return nil, fmt.Errorf("current PParams (%T) is not expected type", currentPParams) + } + maryPParamsUpdate, ok := pparamsUpdate.(mary.MaryProtocolParameterUpdate) + if !ok { + return nil, fmt.Errorf("PParams update (%T) is not expected type", pparamsUpdate) + } + maryPParams.Update(&maryPParamsUpdate) + return maryPParams, nil +} + func HardForkMary(nodeConfig *cardano.CardanoNodeConfig, prevPParams any) (any, error) { allegraPParams, ok := prevPParams.(allegra.AllegraProtocolParameters) if !ok { diff --git a/state/eras/shelley.go b/state/eras/shelley.go index 2a9fc1d2..c001141e 100644 --- a/state/eras/shelley.go +++ b/state/eras/shelley.go @@ -15,16 +15,20 @@ package eras import ( + "fmt" + "github.com/blinklabs-io/gouroboros/cbor" "github.com/blinklabs-io/gouroboros/ledger/shelley" "github.com/blinklabs-io/node/config/cardano" ) var ShelleyEraDesc = EraDesc{ - Id: shelley.EraIdShelley, - Name: shelley.EraNameShelley, - DecodePParamsFunc: DecodePParamsShelley, - HardForkFunc: HardForkShelley, + Id: shelley.EraIdShelley, + Name: shelley.EraNameShelley, + DecodePParamsFunc: DecodePParamsShelley, + DecodePParamsUpdateFunc: DecodePParamsUpdateShelley, + PParamsUpdateFunc: PParamsUpdateShelley, + HardForkFunc: HardForkShelley, } func DecodePParamsShelley(data []byte) (any, error) { @@ -35,6 +39,27 @@ func DecodePParamsShelley(data []byte) (any, error) { return ret, nil } +func DecodePParamsUpdateShelley(data []byte) (any, error) { + var ret shelley.ShelleyProtocolParameterUpdate + if _, err := cbor.Decode(data, &ret); err != nil { + return nil, err + } + return ret, nil +} + +func PParamsUpdateShelley(currentPParams any, pparamsUpdate any) (any, error) { + shelleyPParams, ok := currentPParams.(shelley.ShelleyProtocolParameters) + if !ok { + return nil, fmt.Errorf("current PParams (%T) is not expected type", currentPParams) + } + shelleyPParamsUpdate, ok := pparamsUpdate.(shelley.ShelleyProtocolParameterUpdate) + if !ok { + return nil, fmt.Errorf("PParams update (%T) is not expected type", pparamsUpdate) + } + shelleyPParams.Update(&shelleyPParamsUpdate) + return shelleyPParams, nil +} + func HardForkShelley(nodeConfig *cardano.CardanoNodeConfig, prevPParams any) (any, error) { // There's no Byron protocol parameters to upgrade from, so this is mostly // a dummy call for consistency diff --git a/state/state.go b/state/state.go index 4fa2128b..66ca39c9 100644 --- a/state/state.go +++ b/state/state.go @@ -323,6 +323,49 @@ func (ls *LedgerState) handleEventChainSyncBlock(e ChainsyncEvent) error { if e.Point.Slot > ls.currentEpoch.StartSlot+uint64( ls.currentEpoch.LengthInSlots, ) { + // Check for pparam updates that apply at the end of the epoch + var pparamUpdates []models.PParamUpdate + result := txn.Metadata().Where("epoch = ?", ls.currentEpoch.EpochId).Order("id DESC").Find(&pparamUpdates) + if result.Error != nil { + return result.Error + } + if len(pparamUpdates) > 0 { + // We only want the latest for the epoch + pparamUpdate := pparamUpdates[0] + if eras.Eras[ls.currentEraId].DecodePParamsUpdateFunc != nil { + tmpPParamUpdate, err := eras.Eras[ls.currentEraId].DecodePParamsUpdateFunc(pparamUpdate.Cbor) + if err != nil { + return err + } + if eras.Eras[ls.currentEraId].PParamsUpdateFunc != nil { + // Update current pparams + newPParams, err := eras.Eras[ls.currentEraId].PParamsUpdateFunc(ls.currentPParams, tmpPParamUpdate) + if err != nil { + return err + } + ls.currentPParams = newPParams + ls.config.Logger.Debug( + "updated protocol params", + "pparams", + fmt.Sprintf("%#v", ls.currentPParams), + ) + // Write pparams update to DB + pparamsCbor, err := cbor.Encode(&ls.currentPParams) + if err != nil { + return err + } + tmpPParams := models.PParams{ + AddedSlot: e.Point.Slot, + Epoch: ls.currentEpoch.EpochId + 1, + EraId: uint(ls.currentEraId), + Cbor: pparamsCbor, + } + if result := txn.Metadata().Create(&tmpPParams); result.Error != nil { + return result.Error + } + } + } + } // Create next epoch record newEpoch := models.Epoch{ EpochId: ls.currentEpoch.EpochId + 1,