From dc051108fc719a4ebae54b9d1335c3ed829d0e64 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 27 Oct 2026 10:29:25 +0100 Subject: [PATCH 1/2] fix(syncing): reject DA signed data without metadata tryDecodeData used Data accessors (DACommitment, Height) that dereference Metadata, but a decoded blob may carry nil Metadata. Guard for it after signature validation and skip the blob instead of dereferencing. Adds a regression test for a validly-signed blob with no metadata. --- block/internal/syncing/da_retriever.go | 7 +++++++ block/internal/syncing/da_retriever_test.go | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/block/internal/syncing/da_retriever.go b/block/internal/syncing/da_retriever.go index 7f395d041f..8139d567f3 100644 --- a/block/internal/syncing/da_retriever.go +++ b/block/internal/syncing/da_retriever.go @@ -393,6 +393,13 @@ func (r *daRetriever) tryDecodeData(bz []byte, daHeight uint64) *types.Data { return nil } + // Reject data without metadata before using accessors (DACommitment, + // Height) that assume it is present. + if signedData.Data.Metadata == nil { + r.logger.Debug().Msg("invalid signed data: missing metadata") + return nil + } + // Mark as DA included dataHash := signedData.Data.DACommitment().String() r.cache.SetDataDAIncluded(dataHash, daHeight, signedData.Height()) diff --git a/block/internal/syncing/da_retriever_test.go b/block/internal/syncing/da_retriever_test.go index 2feb4577aa..87fa77d112 100644 --- a/block/internal/syncing/da_retriever_test.go +++ b/block/internal/syncing/da_retriever_test.go @@ -230,6 +230,26 @@ func TestDARetriever_tryDecodeData_InvalidSignature(t *testing.T) { assert.Nil(t, r.tryDecodeData(db, 55)) } +func TestDARetriever_tryDecodeData_NilMetadata(t *testing.T) { + addr, pub, signer := buildSyncTestSigner(t) + gen := genesis.Genesis{ChainID: "tchain", InitialHeight: 1, StartTime: time.Now().Add(-time.Second), ProposerAddress: addr} + r := newTestDARetriever(t, nil, config.DefaultConfig(), gen) + + // A validly-signed blob whose Data carries no metadata must be rejected, + // not dereferenced: the accessors below (DACommitment, Height) assume + // Metadata is set. + d := &types.Data{Txs: types.Txs{types.Tx([]byte{0x01})}} + payload, err := d.MarshalBinary() + require.NoError(t, err) + sig, err := signer.Sign(t.Context(), payload) + require.NoError(t, err) + sd := &types.SignedData{Data: *d, Signature: sig, Signer: types.Signer{PubKey: pub, Address: addr}} + db, err := sd.MarshalBinary() + require.NoError(t, err) + + assert.Nil(t, r.tryDecodeData(db, 99)) +} + func TestDARetriever_validateBlobResponse(t *testing.T) { r := &daRetriever{logger: zerolog.Nop()} // StatusSuccess -> nil From af242086f598c630ee5162933adb430bf3e4e290 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 27 Oct 2026 15:18:53 +0100 Subject: [PATCH 2/2] fix(syncing): drop redundant embedded field from selector staticcheck QF1008: SignedData embeds Data, so signedData.Metadata is enough. --- block/internal/syncing/da_retriever.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/internal/syncing/da_retriever.go b/block/internal/syncing/da_retriever.go index 8139d567f3..7d2dceeede 100644 --- a/block/internal/syncing/da_retriever.go +++ b/block/internal/syncing/da_retriever.go @@ -395,7 +395,7 @@ func (r *daRetriever) tryDecodeData(bz []byte, daHeight uint64) *types.Data { // Reject data without metadata before using accessors (DACommitment, // Height) that assume it is present. - if signedData.Data.Metadata == nil { + if signedData.Metadata == nil { r.logger.Debug().Msg("invalid signed data: missing metadata") return nil }