From a00a13fdd73fad396cd2e45442e3a19a3db20775 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Sat, 25 Oct 2025 16:40:05 +0200 Subject: [PATCH 1/3] fix: shuttermint block processing When handling blocks, we didnt' call the BeforeSaveHook, leading to some outgoing messages to be dropped. For clarity, we now also sync the shuttermint state cache with the db at each block (even though this is not strictly necessary). --- rolling-shutter/keyper/smobserver/smdriver.go | 10 +++++++++- rolling-shutter/keyper/smobserver/smstate.go | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rolling-shutter/keyper/smobserver/smdriver.go b/rolling-shutter/keyper/smobserver/smdriver.go index 67d5bd75..532dd2b4 100644 --- a/rolling-shutter/keyper/smobserver/smdriver.go +++ b/rolling-shutter/keyper/smobserver/smdriver.go @@ -165,6 +165,10 @@ func (smdrv *ShuttermintDriver) handleBlock( block *coretypes.ResultBlockResults, lastCommittedHeight int64, ) error { + err := smdrv.shuttermintState.Load(ctx, queries) + if err != nil { + return err + } oldMeta, err := queries.TMGetSyncMeta(ctx) if err != nil { return err @@ -217,7 +221,11 @@ func (smdrv *ShuttermintDriver) handleBlock( return err } - return nil + err = smdrv.shuttermintState.BeforeSaveHook(ctx, queries) + if err != nil { + return err + } + return smdrv.shuttermintState.Save(ctx, queries) } func (smdrv *ShuttermintDriver) innerHandleTransactions( diff --git a/rolling-shutter/keyper/smobserver/smstate.go b/rolling-shutter/keyper/smobserver/smstate.go index 5196de3d..55993da6 100644 --- a/rolling-shutter/keyper/smobserver/smstate.go +++ b/rolling-shutter/keyper/smobserver/smstate.go @@ -218,7 +218,10 @@ func (st *ShuttermintState) sendPolyEvals(ctx context.Context, queries *database currentEon = eval.Eon } - fmt.Printf("SEND POLY EVALS: eon=%d receiver=%s\n", eval.Eon, eval.ReceiverAddress) + log.Info(). + Int64("eon", eval.Eon). + Str("receiverAddress", eval.ReceiverAddress). + Msg("sending poly eval") receiver, err := shdb.DecodeAddress(eval.ReceiverAddress) if err != nil { return err From 9e457853b98022cedd6ede0d35e3af5cf47c8979 Mon Sep 17 00:00:00 2001 From: Jannik Luhn Date: Mon, 27 Oct 2025 16:31:31 +0100 Subject: [PATCH 2/3] feat: add DKG message received/sent metrics --- .../keyper/keypermetrics/metrics.go | 29 +++++++++++ rolling-shutter/keyper/smobserver/smstate.go | 49 ++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/rolling-shutter/keyper/keypermetrics/metrics.go b/rolling-shutter/keyper/keypermetrics/metrics.go index c5e1c2bb..927d541e 100644 --- a/rolling-shutter/keyper/keypermetrics/metrics.go +++ b/rolling-shutter/keyper/keypermetrics/metrics.go @@ -14,6 +14,13 @@ import ( "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/chainsync" ) +const ( + DKGMessageTypePolyEval = "poly_eval" + DKGMessageTypePolyCommitment = "poly_commitment" + DKGMessageTypeAccusation = "accusation" + DKGMessageTypeApology = "apology" +) + var MetricsKeyperCurrentBlockL1 = prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: "shutter", @@ -71,6 +78,26 @@ var MetricsKeyperCurrentPhase = prometheus.NewGaugeVec( []string{"eon", "phase"}, ) +var MetricsKeyperDKGMessagesSent = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: "shutter", + Subsystem: "keyper", + Name: "dkg_messages_sent", + Help: "Number of DKG messages scheduled for sending for the given eon, partitioned by message type", + }, + []string{"eon", "message_type"}, +) + +var MetricsKeyperDKGMessagesReceived = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: "shutter", + Subsystem: "keyper", + Name: "dkg_messages_received", + Help: "Number of DKG messages received for the given eon, partitioned by message type", + }, + []string{"eon", "message_type"}, +) + var MetricsKeyperCurrentBatchConfigIndex = prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: "shutter", @@ -128,6 +155,8 @@ func InitMetrics(dbpool *pgxpool.Pool, config kprconfig.Config) { prometheus.MustRegister(MetricsKeyperDKGStatus) prometheus.MustRegister(MetricsKeyperEthAddress) prometheus.MustRegister(MetricsExecutionClientVersion) + prometheus.MustRegister(MetricsKeyperDKGMessagesSent) + prometheus.MustRegister(MetricsKeyperDKGMessagesReceived) ctx := context.Background() queries := database.New(dbpool) diff --git a/rolling-shutter/keyper/smobserver/smstate.go b/rolling-shutter/keyper/smobserver/smstate.go index 55993da6..12157b20 100644 --- a/rolling-shutter/keyper/smobserver/smstate.go +++ b/rolling-shutter/keyper/smobserver/smstate.go @@ -202,11 +202,22 @@ func (st *ShuttermintState) sendPolyEvals(ctx context.Context, queries *database receivers = nil encryptedEvals = nil }() - return queries.ScheduleShutterMessage( + eonLabel := strconv.FormatInt(currentEon, 10) + err := queries.ScheduleShutterMessage( ctx, fmt.Sprintf("poly eval (eon=%d)", currentEon), shmsg.NewPolyEval(uint64(currentEon), receivers, encryptedEvals), ) + if err != nil { + return err + } + for range receivers { + keypermetrics.MetricsKeyperDKGMessagesSent.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypePolyEval, + ).Inc() + } + return nil } for _, eval := range evals { @@ -389,6 +400,7 @@ func (st *ShuttermintState) startPhase1Dealing( log.Fatal().Err(err).Msg("aborting due to unexpected error") } dkg.markDirty() + eonLabel := strconv.FormatUint(eon, 10) err = queries.ScheduleShutterMessage( ctx, fmt.Sprintf("poly commitment (eon=%d)", eon), @@ -397,6 +409,10 @@ func (st *ShuttermintState) startPhase1Dealing( if err != nil { return err } + keypermetrics.MetricsKeyperDKGMessagesSent.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypePolyCommitment, + ).Inc() for _, eval := range polyEvals { err = queries.InsertPolyEval(ctx, database.InsertPolyEvalParams{ @@ -416,9 +432,14 @@ func (st *ShuttermintState) startPhase2Accusing( ) error { accusations := dkg.pure.StartPhase2Accusing() dkg.markDirty() + eonLabel := strconv.FormatUint(eon, 10) if len(accusations) > 0 { var accused []common.Address for _, a := range accusations { + keypermetrics.MetricsKeyperDKGMessagesSent.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypeAccusation, + ).Inc() accused = append(accused, dkg.keypers[a.Accused]) } err := queries.ScheduleShutterMessage( @@ -441,11 +462,16 @@ func (st *ShuttermintState) startPhase3Apologizing( ) error { apologies := dkg.pure.StartPhase3Apologizing() dkg.markDirty() + eonLabel := strconv.FormatUint(eon, 10) if len(apologies) > 0 { var accusers []common.Address var polyEvals []*big.Int for _, a := range apologies { + keypermetrics.MetricsKeyperDKGMessagesSent.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypeApology, + ).Inc() accusers = append(accusers, dkg.keypers[a.Accuser]) polyEvals = append(polyEvals, a.Eval) } @@ -624,6 +650,10 @@ func (st *ShuttermintState) handlePolyCommitment( return nil } dkg.markDirty() + keypermetrics.MetricsKeyperDKGMessagesReceived.WithLabelValues( + strconv.FormatUint(e.Eon, 10), + keypermetrics.DKGMessageTypePolyCommitment, + ).Inc() return nil } @@ -645,6 +675,7 @@ func (st *ShuttermintState) handlePolyEval( Msg("event for non existent eon received") return nil } + eonLabel := strconv.FormatUint(e.Eon, 10) sender, err := medley.FindAddressIndex(dkg.keypers, e.Sender) if err != nil { return nil @@ -681,6 +712,10 @@ func (st *ShuttermintState) handlePolyEval( } log.Info().Str("event", e.String()).Int("keyper", sender). Msg("got poly eval message") + keypermetrics.MetricsKeyperDKGMessagesReceived.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypePolyEval, + ).Inc() dkg.markDirty() return nil } @@ -700,6 +735,7 @@ func (st *ShuttermintState) handleAccusation( Msg("received accusation in wrong phase") return nil } + eonLabel := strconv.FormatUint(e.Eon, 10) sender, err := medley.FindAddressIndex(dkg.keypers, e.Sender) if err != nil { log.Info().Str("event", e.String()). @@ -724,7 +760,12 @@ func (st *ShuttermintState) handleAccusation( if err != nil { log.Info().Str("event", e.String()).Err(err). Msg("cannot handle accusation") + continue } + keypermetrics.MetricsKeyperDKGMessagesReceived.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypeAccusation, + ).Inc() } dkg.markDirty() return nil @@ -744,6 +785,7 @@ func (st *ShuttermintState) handleApology( Msg("Warning: received apology in wrong phase") return nil } + eonLabel := strconv.FormatUint(e.Eon, 10) sender, err := medley.FindAddressIndex(dkg.keypers, e.Sender) if err != nil { log.Info().Str("event", e.String()).Msg("failed to handle apology. bad sender") @@ -766,7 +808,12 @@ func (st *ShuttermintState) handleApology( }) if err != nil { log.Info().Str("event", e.String()).Err(err).Msg("failed to handle apology") + continue } + keypermetrics.MetricsKeyperDKGMessagesReceived.WithLabelValues( + eonLabel, + keypermetrics.DKGMessageTypeApology, + ).Inc() } dkg.markDirty() return nil From 5ed81375ab909a8ba149461f7b27b1f965dfc5b8 Mon Sep 17 00:00:00 2001 From: ylembachar Date: Tue, 4 Nov 2025 10:21:59 +0100 Subject: [PATCH 3/3] add circleci config to allow releases from branches --- .circleci/config.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d2e42f2d..4cddce9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -227,8 +227,10 @@ jobs: CIRCLE_BRANCH=${BRANCHES##*/} echo "CIRCLE_BRANCH is empty, trying to set it from git, result: $CIRCLE_BRANCH" fi - if [[ $CIRCLE_BRANCH != "main" && ! $CIRCLE_TAG =~ (a|b|rc) ]]; then - echo "Final release tags are only allowed on main branch." + if [[ $CIRCLE_BRANCH != "main" && \ + ! $CIRCLE_BRANCH =~ ^release/v[0-9]+\.[0-9]+(\.[0-9]+)?$ && \ + ! $CIRCLE_TAG =~ (a|b|rc) ]]; then + echo "Final release tags are only allowed from main or release/vX.Y(.Z) branches." exit 1 else DOCKER_TAG=${DOCKER_BASE}/keyper:${CIRCLE_TAG}