diff --git a/block/manager.go b/block/manager.go index b059995fd6..757e38c1e6 100644 --- a/block/manager.go +++ b/block/manager.go @@ -750,7 +750,7 @@ func (m *Manager) execValidate(lastState types.State, header *types.SignedHeader return nil } -func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, lastState types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) { +func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, _ types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) { // Use when batchData is set to data IDs from the DA layer // batchDataIDs := convertBatchDataToBytes(batchData.Data) @@ -818,12 +818,19 @@ func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignatur return header, blockData, nil } +type headerContextKey struct{} + +// HeaderContextKey is used to store the header in the context. +// This is useful if the execution client needs to access the header during transaction execution. +var HeaderContextKey = headerContextKey{} + func (m *Manager) execApplyBlock(ctx context.Context, lastState types.State, header *types.SignedHeader, data *types.Data) (types.State, error) { rawTxs := make([][]byte, len(data.Txs)) for i := range data.Txs { rawTxs[i] = data.Txs[i] } + ctx = context.WithValue(ctx, HeaderContextKey, header) newStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash) if err != nil { return types.State{}, fmt.Errorf("failed to execute transactions: %w", err) diff --git a/block/publish_block2_test.go b/block/publish_block2_test.go index 1b9ad02d9a..434cf1107a 100644 --- a/block/publish_block2_test.go +++ b/block/publish_block2_test.go @@ -70,8 +70,8 @@ func TestSlowConsumers(t *testing.T) { manager, headerSync, dataSync := setupBlockManager(t, ctx, workDir, dbm, blockTime, noopSigner) var lastCapturedDataPayload *types.Data var lastCapturedHeaderPayload *types.SignedHeader - manager.dataBroadcaster = capturingTailBroadcaster[*types.Data](spec.dataConsumerDelay, &lastCapturedDataPayload, dataSync) - manager.headerBroadcaster = capturingTailBroadcaster[*types.SignedHeader](spec.headerConsumerDelay, &lastCapturedHeaderPayload, headerSync) + manager.dataBroadcaster = capturingTailBroadcaster(spec.dataConsumerDelay, &lastCapturedDataPayload, dataSync) + manager.headerBroadcaster = capturingTailBroadcaster(spec.headerConsumerDelay, &lastCapturedHeaderPayload, headerSync) blockTime := manager.config.Node.BlockTime.Duration aggCtx, aggCancel := context.WithCancel(ctx) @@ -104,8 +104,8 @@ func TestSlowConsumers(t *testing.T) { var firstCapturedDataPayload *types.Data var firstCapturedHeaderPayload *types.SignedHeader - manager.dataBroadcaster = capturingHeadBroadcaster[*types.Data](0, &firstCapturedDataPayload, dataSync) - manager.headerBroadcaster = capturingHeadBroadcaster[*types.SignedHeader](0, &firstCapturedHeaderPayload, headerSync) + manager.dataBroadcaster = capturingHeadBroadcaster(0, &firstCapturedDataPayload, dataSync) + manager.headerBroadcaster = capturingHeadBroadcaster(0, &firstCapturedHeaderPayload, headerSync) go manager.AggregationLoop(ctx, errChan) select { case err := <-errChan: @@ -229,7 +229,6 @@ func (m mockExecutor) GetTxs(ctx context.Context) ([][]byte, error) { func (m mockExecutor) ExecuteTxs(ctx context.Context, txs [][]byte, blockHeight uint64, timestamp time.Time, prevStateRoot []byte) (updatedStateRoot []byte, maxBytes uint64, err error) { return bytesN(32), 10_000, nil - } func (m mockExecutor) SetFinal(ctx context.Context, blockHeight uint64) error { diff --git a/block/publish_block_test.go b/block/publish_block_test.go index 67512c38eb..ebbdb3a6df 100644 --- a/block/publish_block_test.go +++ b/block/publish_block_test.go @@ -310,7 +310,7 @@ func Test_publishBlock_EmptyBatch(t *testing.T) { // We should also expect ExecuteTxs to be called with an empty transaction list newAppHash := []byte("newAppHash") - mockExec.On("ExecuteTxs", ctx, mock.Anything, currentHeight+1, mock.AnythingOfType("time.Time"), m.lastState.AppHash).Return(newAppHash, uint64(100), nil).Once() + mockExec.On("ExecuteTxs", mock.Anything, mock.Anything, currentHeight+1, mock.AnythingOfType("time.Time"), m.lastState.AppHash).Return(newAppHash, uint64(100), nil).Once() // SetHeight should be called mockStore.On("SetHeight", ctx, currentHeight+1).Return(nil).Once() @@ -380,7 +380,7 @@ func Test_publishBlock_Success(t *testing.T) { sampleTxs := [][]byte{[]byte("tx1"), []byte("tx2")} // No longer mocking GetTxs since it's handled by reaper.go newAppHash := []byte("newAppHash") - mockExec.On("ExecuteTxs", t.Context(), mock.Anything, newHeight, mock.AnythingOfType("time.Time"), manager.lastState.AppHash).Return(newAppHash, uint64(100), nil).Once() + mockExec.On("ExecuteTxs", mock.Anything, mock.Anything, newHeight, mock.AnythingOfType("time.Time"), manager.lastState.AppHash).Return(newAppHash, uint64(100), nil).Once() // No longer mocking SubmitBatchTxs since it's handled by reaper.go batchTimestamp := lastHeader.Time().Add(1 * time.Second) diff --git a/types/signed_header.go b/types/signed_header.go index 5996b90294..bec2fb025e 100644 --- a/types/signed_header.go +++ b/types/signed_header.go @@ -16,6 +16,12 @@ var ( ErrLastCommitHashMismatch = errors.New("last commit hash mismatch") ) +var _ header.Header[*SignedHeader] = &SignedHeader{} + +// SignatureVerified is a custom signature verifiers. +// If set, ValidateBasic will use this function to verify the signature. +type SignatureVerifier func(header *Header) ([]byte, error) + // SignedHeader combines Header and its signature. // // Used mostly for gossiping. @@ -24,6 +30,8 @@ type SignedHeader struct { // Note: This is backwards compatible as ABCI exported types are not affected. Signature Signature Signer Signer + + verifier SignatureVerifier } // New creates a new SignedHeader. @@ -36,6 +44,15 @@ func (sh *SignedHeader) IsZero() bool { return sh == nil } +// SetCustomVerifier sets a custom signature verifier for the SignedHeader. +func (sh *SignedHeader) SetCustomVerifier(verifier SignatureVerifier) error { + if sh.verifier != nil { + return errors.New("custom verifier already set") + } + sh.verifier = verifier + return nil +} + // Verify verifies the signed header. func (sh *SignedHeader) Verify(untrstH *SignedHeader) error { // go-header ensures untrustH already passed ValidateBasic. @@ -106,9 +123,20 @@ func (sh *SignedHeader) ValidateBasic() error { return ErrProposerAddressMismatch } - bz, err := sh.Header.MarshalBinary() - if err != nil { - return err + var ( + bz []byte + err error + ) + if sh.verifier == nil { + bz, err = sh.Header.MarshalBinary() + if err != nil { + return err + } + } else { + bz, err = sh.verifier(&sh.Header) + if err != nil { + return fmt.Errorf("custom signature verification failed: %w", err) + } } verified, err := sh.Signer.PubKey.Verify(bz, sh.Signature) @@ -120,10 +148,3 @@ func (sh *SignedHeader) ValidateBasic() error { } return nil } - -// Validate performs basic validation of a signed header. -func (sh *SignedHeader) Validate() error { - return sh.ValidateBasic() -} - -var _ header.Header[*SignedHeader] = &SignedHeader{} diff --git a/types/state.go b/types/state.go index 18cac326c5..438f5f226b 100644 --- a/types/state.go +++ b/types/state.go @@ -12,7 +12,7 @@ import ( // The Consensus.App version will be set during the Handshake, once // we hear from the app what protocol version it is running. var InitStateVersion = Version{ - Block: 1, + Block: 11, // Block version is set to 11, to be compatible with CometBFT blocks for IBC. App: 0, }