Repository: rollkit/rollkit
Pull request #2164: fix: header signature cleanup
Author: login: facundomedica
Created: 2025-04-10T10:47:19Z | Merged: 2025-04-11T09:18:21Z

<!--
Please read and fill out this form before submitting your PR.

Please make sure you have reviewed our contributors guide before submitting your
first PR.

NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/
-->

## Overview

Previously the header signature verification was failing given that different sign bytes were being used. Some cleanup was done too, like using signer.Signer in the header and removing types.Signer.

Also GetAddress() was added to signer.Signer.

<!-- 
Please provide an explanation of the PR, including the appropriate context,
background, goal, and rationale. If there is an issue with this information,
please provide a tl;dr and link the issue. 

Ex: Closes #<issue number>
-->



Diff:
diff --git a/block/manager.go b/block/manager.go
index b0d283078d..511111cf1b 100644
--- a/block/manager.go
+++ b/block/manager.go
@@ -7,6 +7,7 @@ import (
 	"encoding/hex"
 	"errors"
 	"fmt"
+	"strings"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -189,6 +190,7 @@ func getInitialState(ctx context.Context, genesis genesis.Genesis, signer signer
 			if err != nil {
 				return types.State{}, fmt.Errorf("failed to get public key: %w", err)
 			}
+
 			signature, err = getSignature(header, signer)
 			if err != nil {
 				return types.State{}, fmt.Errorf("failed to get header signature: %w", err)
@@ -997,7 +999,7 @@ func (m *Manager) RetrieveLoop(ctx context.Context) {
 		err := m.processNextDAHeader(ctx)
 		if err != nil && ctx.Err() == nil {
 			// if the requested da height is not yet available, wait silently, otherwise log the error and wait
-			if !errors.Is(err, ErrHeightFromFutureStr) {
+			if !m.areAllErrorsHeightFromFuture(err) {
 				m.logger.Error("failed to retrieve block from DALC", "daHeight", daHeight, "errors", err.Error())
 			}
 			continue
@@ -1099,6 +1101,30 @@ func (m *Manager) isUsingExpectedCentralizedSequencer(header *types.SignedHeader
 	return bytes.Equal(header.ProposerAddress, m.genesis.ProposerAddress) && header.ValidateBasic() == nil
 }
 
+// areAllErrorsHeightFromFuture checks if all errors in a joined error are ErrHeightFromFutureStr
+func (m *Manager) areAllErrorsHeightFromFuture(err error) bool {
+	if err == nil {
+		return false
+	}
+
+	// Check if the error itself is ErrHeightFromFutureStr
+	if strings.Contains(err.Error(), ErrHeightFromFutureStr.Error()) {
+		return true
+	}
+
+	// If it's a joined error, check each error recursively
+	if joinedErr, ok := err.(interface{ Unwrap() []error }); ok {
+		for _, e := range joinedErr.Unwrap() {
+			if !m.areAllErrorsHeightFromFuture(e) {
+				return false
+			}
+		}
+		return true
+	}
+
+	return false
+}
+
 func (m *Manager) fetchHeaders(ctx context.Context, daHeight uint64) (coreda.ResultRetrieve, error) {
 	var err error
 	ctx, cancel := context.WithTimeout(ctx, 60*time.Second) //TODO: make this configurable
@@ -1114,14 +1140,6 @@ func (m *Manager) getSignature(header types.Header) (types.Signature, error) {
 	return getSignature(header, m.proposerKey)
 }
 
-func getSignature(header types.Header, proposerKey signer.Signer) (types.Signature, error) {
-	b, err := header.MarshalBinary()
-	if err != nil {
-		return nil, err
-	}
-	return proposerKey.Sign(b)
-}
-
 func (m *Manager) getTxsFromBatch() (*BatchData, error) {
 	batch := m.bq.Next()
 	if batch == nil {
@@ -1523,10 +1541,21 @@ func (m *Manager) execCommit(ctx context.Context, newState types.State, h *types
 func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, lastState types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) {
 	data := batchData.Data
 	batchdata := convertBatchDataToBytes(data)
+
 	key, err := m.proposerKey.GetPublic()
 	if err != nil {
-		return nil, nil, err
+		return nil, nil, fmt.Errorf("failed to get proposer public key: %w", err)
 	}
+
+	// check that the proposer address is the same as the genesis proposer address
+	address, err := m.proposerKey.GetAddress()
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to get proposer address: %w", err)
+	}
+	if !bytes.Equal(m.genesis.ProposerAddress, address) {
+		return nil, nil, fmt.Errorf("proposer address is not the same as the genesis proposer address %x != %x", address, m.genesis.ProposerAddress)
+	}
+
 	header := &types.SignedHeader{
 		Header: types.Header{
 			Version: types.Version{
@@ -1661,3 +1690,11 @@ func bytesToBatchData(data []byte) ([][]byte, error) {
 
 	return result, nil
 }
+
+func getSignature(header types.Header, proposerKey signer.Signer) (types.Signature, error) {
+	b, err := header.MarshalBinary()
+	if err != nil {
+		return nil, err
+	}
+	return proposerKey.Sign(b)
+}
diff --git a/node/full.go b/node/full.go
index 5d59dc8945..50cba63d16 100644
--- a/node/full.go
+++ b/node/full.go
@@ -184,22 +184,13 @@ func initBlockManager(
 	gasPrice float64,
 	gasMultiplier float64,
 ) (*block.Manager, error) {
-
 	logger.Debug("Proposer address", "address", genesis.ProposerAddress)
 
-	rollGen := genesispkg.NewGenesis(
-		genesis.ChainID,
-		genesis.InitialHeight,
-		genesis.GenesisDAStartHeight,
-		genesis.ProposerAddress,
-		nil,
-	)
-
 	blockManager, err := block.NewManager(
 		ctx,
 		signer,
 		nodeConfig,
-		rollGen,
+		genesis,
 		store,
 		exec,
 		sequencer,
diff --git a/pkg/signer/file/local.go b/pkg/signer/file/local.go
index 928081821e..bf5695476d 100644
--- a/pkg/signer/file/local.go
+++ b/pkg/signer/file/local.go
@@ -4,6 +4,7 @@ import (
 	"crypto/aes"
 	"crypto/cipher"
 	"crypto/rand"
+	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -272,6 +273,14 @@ func (s *FileSystemSigner) GetPublic() (crypto.PubKey, error) {
 	return s.publicKey, nil
 }
 
+// GetAddress returns the address of the signer
+func (s *FileSystemSigner) GetAddress() ([]byte, error) {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+
+	return getAddress(s.publicKey)
+}
+
 // deriveKeyArgon2 uses Argon2id for key derivation
 func deriveKeyArgon2(passphrase, salt []byte, keyLen uint32) []byte {
 	// Using some default parameters:
@@ -303,3 +312,14 @@ func zeroBytes(b []byte) {
 		b[i] = 0
 	}
 }
+
+// getAddress returns the Ed25519 address of the signer.
+func getAddress(pubKey crypto.PubKey) ([]byte, error) {
+	bz, err := pubKey.Raw()
+	if err != nil {
+		return nil, err
+	}
+
+	address := sha256.Sum256(bz)
+	return address[:], nil
+}
diff --git a/pkg/signer/noop/signer.go b/pkg/signer/noop/signer.go
index 02698e41b5..1718d88022 100644
--- a/pkg/signer/noop/signer.go
+++ b/pkg/signer/noop/signer.go
@@ -1,6 +1,9 @@
 package noop
 
 import (
+	"crypto/sha256"
+	"fmt"
+
 	"github.com/libp2p/go-libp2p/core/crypto"
 
 	"github.com/rollkit/rollkit/pkg/signer"
@@ -11,19 +14,30 @@ import (
 type NoopSigner struct {
 	privKey crypto.PrivKey
 	pubKey  crypto.PubKey
+	address []byte
 }
 
 // NewNoopSigner creates a new signer with a fresh Ed25519 key pair.
 func NewNoopSigner(privKey crypto.PrivKey) (signer.Signer, error) {
-
-	return &NoopSigner{
+	sig := &NoopSigner{
 		privKey: privKey,
 		pubKey:  privKey.GetPublic(),
-	}, nil
+	}
+
+	address, err := getAddress(sig.pubKey)
+	if err != nil {
+		return nil, err
+	}
+	sig.address = address
+
+	return sig, nil
 }
 
 // Sign implements the Signer interface by signing the message with the Ed25519 private key.
 func (n *NoopSigner) Sign(message []byte) ([]byte, error) {
+	if n.privKey == nil {
+		return nil, fmt.Errorf("private key not loaded")
+	}
 	return n.privKey.Sign(message)
 }
 
@@ -31,3 +45,19 @@ func (n *NoopSigner) Sign(message []byte) ([]byte, error) {
 func (n *NoopSigner) GetPublic() (crypto.PubKey, error) {
 	return n.pubKey, nil
 }
+
+// GetAddress implements the Signer interface by returning the Ed25519 address.
+func (n *NoopSigner) GetAddress() ([]byte, error) {
+	return n.address, nil
+}
+
+// getAddress returns the Ed25519 address of the signer.
+func getAddress(pubKey crypto.PubKey) ([]byte, error) {
+	bz, err := pubKey.Raw()
+	if err != nil {
+		return nil, err
+	}
+
+	address := sha256.Sum256(bz)
+	return address[:], nil
+}
diff --git a/pkg/signer/signer.go b/pkg/signer/signer.go
index ccbb50216d..09c6b951b4 100644
--- a/pkg/signer/signer.go
+++ b/pkg/signer/signer.go
@@ -11,4 +11,7 @@ type Signer interface {
 
 	// GetPublic returns the public key paired with this private key.
 	GetPublic() (crypto.PubKey, error)
+
+	// GetAddress returns the address of the signer.
+	GetAddress() ([]byte, error)
 }
diff --git a/types/header.go b/types/header.go
index c7ac7c394c..f19be2eece 100644
--- a/types/header.go
+++ b/types/header.go
@@ -8,10 +8,6 @@ import (
 	"time"
 
 	"github.com/celestiaorg/go-header"
-	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/timestamppb"
-
-	v1 "github.com/rollkit/rollkit/types/pb/rollkit/v1"
 )
 
 // Hash is a 32-byte array which is used to represent a hash result.
@@ -123,23 +119,6 @@ func (h *Header) ValidateBasic() error {
 	return nil
 }
 
-// Vote returns a vote for the header.
-func (h *Header) Vote() ([]byte, error) {
-	v := &v1.Vote{
-		ChainId:          h.ChainID(),
-		Height:           h.Height(),
-		BlockIdHash:      h.Hash(),
-		ValidatorAddress: h.ProposerAddress,
-		Timestamp:        timestamppb.New(h.Time()),
-	}
-
-	bz, err := proto.Marshal(v)
-	if err != nil {
-		return nil, err
-	}
-	return bz, nil
-}
-
 var _ header.Header[*Header] = &Header{}
 var _ encoding.BinaryMarshaler = &Header{}
 var _ encoding.BinaryUnmarshaler = &Header{}
diff --git a/types/serialization.go b/types/serialization.go
index 969ae42ce1..4f5c6198bd 100644
--- a/types/serialization.go
+++ b/types/serialization.go
@@ -80,7 +80,8 @@ func (sh *SignedHeader) ToProto() (*pb.SignedHeader, error) {
 	}, nil
 }
 
-// FromProto fills SignedHeader with data from protobuf representation.
+// FromProto fills SignedHeader with data from protobuf representation. The contained
+// Signer can only be used to verify signatures, not to sign messages.
 func (sh *SignedHeader) FromProto(other *pb.SignedHeader) error {
 	err := sh.Header.FromProto(other.Header)
 	if err != nil {
diff --git a/types/signed_header.go b/types/signed_header.go
index f894572aaa..c3cb862442 100644
--- a/types/signed_header.go
+++ b/types/signed_header.go
@@ -106,18 +106,15 @@ func (sh *SignedHeader) ValidateBasic() error {
 		return ErrProposerAddressMismatch
 	}
 
-	signature := sh.Signature
-
-	vote, err := sh.Header.Vote()
+	bz, err := sh.Header.MarshalBinary()
 	if err != nil {
 		return err
 	}
 
-	verified, err := sh.Signer.PubKey.Verify(vote, signature)
+	verified, err := sh.Signer.PubKey.Verify(bz, sh.Signature)
 	if err != nil {
 		return err
 	}
-
 	if !verified {
 		return ErrSignatureVerificationFailed
 	}
diff --git a/types/signed_header_test.go b/types/signed_header_test.go
index 3866417da6..bddfada5b7 100644
--- a/types/signed_header_test.go
+++ b/types/signed_header_test.go
@@ -99,7 +99,7 @@ func testVerify(t *testing.T, trusted *SignedHeader, untrustedAdj *SignedHeader,
 			if shouldRecomputeCommit {
 				signature, err := GetSignature(preparedHeader.Header, privKey)
 				require.NoError(t, err)
-				preparedHeader.Signature = *signature
+				preparedHeader.Signature = signature
 			}
 
 			err := trusted.Verify(preparedHeader)
@@ -213,7 +213,7 @@ func testValidateBasic(t *testing.T, untrustedAdj *SignedHeader, privKey crypto.
 			if shouldRecomputeCommit {
 				signature, err := GetSignature(preparedHeader.Header, privKey)
 				require.NoError(t, err)
-				preparedHeader.Signature = *signature
+				preparedHeader.Signature = signature
 			}
 
 			err := preparedHeader.ValidateBasic()
diff --git a/types/utils.go b/types/utils.go
index 3625e6c0b4..08a4f30d51 100644
--- a/types/utils.go
+++ b/types/utils.go
@@ -2,7 +2,6 @@ package types
 
 import (
 	cryptoRand "crypto/rand"
-	"errors"
 	"math/rand"
 	"time"
 
@@ -15,26 +14,6 @@ import (
 // DefaultSigningKeyType is the key type used by the sequencer signing key
 const DefaultSigningKeyType = "ed25519"
 
-// ValidatorConfig carries all necessary state for generating a Validator
-type ValidatorConfig struct {
-	PrivKey     crypto.PrivKey
-	VotingPower int64
-}
-
-// GetValidatorSetCustom returns a validator set based on the provided validatorConfig.
-func GetValidatorSetCustom(config ValidatorConfig) (Signer, error) {
-	if config.PrivKey == nil {
-		panic(errors.New("private key is nil"))
-	}
-	pubKey := config.PrivKey.GetPublic()
-
-	signer, err := NewSigner(pubKey)
-	if err != nil {
-		return Signer{}, err
-	}
-	return signer, nil
-}
-
 // BlockConfig carries all necessary state for block generation
 type BlockConfig struct {
 	Height       uint64
@@ -178,7 +157,7 @@ func GetRandomSignedHeaderCustom(config *HeaderConfig, chainID string) (*SignedH
 	if err != nil {
 		return nil, err
 	}
-	signedHeader.Signature = *signature
+	signedHeader.Signature = signature
 	return signedHeader, nil
 }
 
@@ -194,7 +173,7 @@ func GetRandomNextSignedHeader(signedHeader *SignedHeader, privKey crypto.PrivKe
 	if err != nil {
 		return nil, err
 	}
-	newSignedHeader.Signature = *signature
+	newSignedHeader.Signature = signature
 	return newSignedHeader, nil
 }
 
@@ -228,7 +207,7 @@ func GetFirstSignedHeader(privkey crypto.PrivKey, chainID string) (*SignedHeader
 	if err != nil {
 		return nil, err
 	}
-	signedHeader.Signature = *signature
+	signedHeader.Signature = signature
 	return &signedHeader, nil
 }
 
@@ -239,7 +218,7 @@ func GetGenesisWithPrivkey(chainID string) (genesis.Genesis, crypto.PrivKey, cry
 		panic(err)
 	}
 
-	signer, err := NewSigner(pubKey)
+	signer, err := NewSigner(privKey.GetPublic())
 	if err != nil {
 		panic(err)
 	}
@@ -270,17 +249,12 @@ func GetRandomBytes(n uint) []byte {
 }
 
 // GetSignature returns a signature from the given private key over the given header
-func GetSignature(header Header, privKey crypto.PrivKey) (*Signature, error) {
-	consensusVote, err := header.Vote()
-	if err != nil {
-		return nil, err
-	}
-	sign, err := privKey.Sign(consensusVote)
+func GetSignature(header Header, proposerKey crypto.PrivKey) (Signature, error) {
+	b, err := header.MarshalBinary()
 	if err != nil {
 		return nil, err
 	}
-	signature := Signature(sign)
-	return &signature, nil
+	return proposerKey.Sign(b)
 }
 
 func getBlockDataWith(nTxs int) *Data {


Reviews:
user: login: tac0turtle
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T11:15:06Z
user: login: tac0turtle
body: left some comments, this seems incorrect or im missing something 
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T11:16:27Z
user: login: tac0turtle
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T11:18:30Z
user: login: facundomedica
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T12:55:09Z
user: login: facundomedica
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T12:55:57Z
user: login: tac0turtle
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T18:21:59Z
user: login: tac0turtle
body: i think we can remove the noop changes for getAddress, otherwise nice cleanup. Should we add a verification test to make sure it works. 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T18:23:55Z

Review comments:
diff_hunk: @@ -58,29 +61,52 @@ func (d *Data) UnmarshalBinary(data []byte) error {
 
 // ToProto converts SignedHeader into protobuf representation and returns it.
 func (sh *SignedHeader) ToProto() (*pb.SignedHeader, error) {
-	if sh.Signer.PubKey == nil {
+	if sh.Signer == nil {
+		fmt.Println("signer is nil")
+		return &pb.SignedHeader{
+			Header:    sh.Header.ToProto(),
+			Signature: sh.Signature[:],
+			Signer:    &pb.Signer{},
+		}, nil
+	}
+
+	pubkey, err := sh.Signer.GetPublic()
+	if err != nil {
+		fmt.Println("error getting public key", err)
path: types/serialization.go
user: login: tac0turtle
body: Should remove the prints 
created_at: 2025-04-10T10:51:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -180,28 +180,26 @@ func getInitialState(ctx context.Context, genesis genesis.Genesis, signer signer
 			}}
 
 		var signature types.Signature
-		var pubKey crypto.PubKey
 		// The signer is only provided in aggregator nodes. This enables the creation of a signed genesis header,
 		// which includes a public key and a cryptographic signature for the header.
 		// In a full node (non-aggregator), the signer will be nil, and only an unsigned genesis header will be initialized locally.
 		if signer != nil {
-			pubKey, err = signer.GetPublic()
+			signature, err = types.GetSignature(header, signer)
 			if err != nil {
-				return types.State{}, fmt.Errorf("failed to get public key: %w", err)
+				return types.State{}, fmt.Errorf("failed to get header signature: %w", err)
 			}
-			signature, err = getSignature(header, signer)
+		} else {
+			// If the signer is not provided, create a noop signer from the proposer address
+			signer, err = noopsigner.NewNoopSignerFromAddress(genesis.ProposerAddress)
 			if err != nil {
-				return types.State{}, fmt.Errorf("failed to get header signature: %w", err)
+				return types.State{}, fmt.Errorf("failed to create noop signer: %w", err)
path: block/manager.go
user: login: tac0turtle
body: we should try to avoid this. Proposer address should always be present. Otherwise fail
created_at: 2025-04-10T11:04:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -901,6 +899,7 @@ func (m *Manager) HeaderStoreRetrieveLoop(ctx context.Context) {
 				}
 				// early validation to reject junk headers
 				if !m.isUsingExpectedCentralizedSequencer(header) {
+					fmt.Println("not using expected sequencer 1")
path: block/manager.go
user: login: tac0turtle
body: ```suggestion
```
created_at: 2025-04-10T11:06:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,29 +61,52 @@ func (d *Data) UnmarshalBinary(data []byte) error {
 
 // ToProto converts SignedHeader into protobuf representation and returns it.
 func (sh *SignedHeader) ToProto() (*pb.SignedHeader, error) {
-	if sh.Signer.PubKey == nil {
+	if sh.Signer == nil {
+		fmt.Println("signer is nil")
path: types/serialization.go
user: login: tac0turtle
body: ```suggestion
```
created_at: 2025-04-10T11:07:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,18 +104,26 @@ func (sh *SignedHeader) ValidateBasic() error {
 	}
 
 	// Check that the proposer address in the signed header matches the proposer address in the validator set
-	if !bytes.Equal(sh.ProposerAddress, sh.Signer.Address) {
+	signerAddress, err := sh.Signer.GetAddress()
+	if err != nil {
+		return err
+	}
+	if !bytes.Equal(sh.ProposerAddress, signerAddress) {
 		return ErrProposerAddressMismatch
 	}
 
 	signature := sh.Signature
 
-	vote, err := sh.Header.Vote()
+	bz, err := sh.Header.MarshalBinary()
 	if err != nil {
 		return err
 	}
 
-	verified, err := sh.Signer.PubKey.Verify(vote, signature)
+	pubkey, err := sh.Signer.GetPublic()
path: types/signed_header.go
user: login: tac0turtle
body: this is incorrect as how will non signing nodes have the public key here? 
created_at: 2025-04-10T11:15:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -180,28 +180,26 @@ func getInitialState(ctx context.Context, genesis genesis.Genesis, signer signer
 			}}
 
 		var signature types.Signature
-		var pubKey crypto.PubKey
 		// The signer is only provided in aggregator nodes. This enables the creation of a signed genesis header,
 		// which includes a public key and a cryptographic signature for the header.
 		// In a full node (non-aggregator), the signer will be nil, and only an unsigned genesis header will be initialized locally.
 		if signer != nil {
-			pubKey, err = signer.GetPublic()
+			signature, err = types.GetSignature(header, signer)
 			if err != nil {
-				return types.State{}, fmt.Errorf("failed to get public key: %w", err)
+				return types.State{}, fmt.Errorf("failed to get header signature: %w", err)
 			}
-			signature, err = getSignature(header, signer)
+		} else {
+			// If the signer is not provided, create a noop signer from the proposer address
+			signer, err = noopsigner.NewNoopSignerFromAddress(genesis.ProposerAddress)
 			if err != nil {
-				return types.State{}, fmt.Errorf("failed to get header signature: %w", err)
+				return types.State{}, fmt.Errorf("failed to create noop signer: %w", err)
path: block/manager.go
user: login: tac0turtle
body: this seems very hacky. signer is meant for signing. Noopsigner is for testing, not for production. why cant we use the signer how it was before? It seems like that part wasnt the issue 
created_at: 2025-04-10T11:18:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -901,6 +899,7 @@ func (m *Manager) HeaderStoreRetrieveLoop(ctx context.Context) {
 				}
 				// early validation to reject junk headers
 				if !m.isUsingExpectedCentralizedSequencer(header) {
+					fmt.Println("not using expected sequencer 1")
path: block/manager.go
user: login: facundomedica
body: whoops, my debugging technique has been discovered
created_at: 2025-04-10T12:55:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,18 +104,26 @@ func (sh *SignedHeader) ValidateBasic() error {
 	}
 
 	// Check that the proposer address in the signed header matches the proposer address in the validator set
-	if !bytes.Equal(sh.ProposerAddress, sh.Signer.Address) {
+	signerAddress, err := sh.Signer.GetAddress()
+	if err != nil {
+		return err
+	}
+	if !bytes.Equal(sh.ProposerAddress, signerAddress) {
 		return ErrProposerAddressMismatch
 	}
 
 	signature := sh.Signature
 
-	vote, err := sh.Header.Vote()
+	bz, err := sh.Header.MarshalBinary()
 	if err != nil {
 		return err
 	}
 
-	verified, err := sh.Signer.PubKey.Verify(vote, signature)
+	pubkey, err := sh.Signer.GetPublic()
path: types/signed_header.go
user: login: facundomedica
body: Pubkey of the proposer is included in the signed header afaik
created_at: 2025-04-10T12:55:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,23 +14,50 @@ import (
 type NoopSigner struct {
 	privKey crypto.PrivKey
 	pubKey  crypto.PubKey
+	address []byte
 }
 
 // NewNoopSigner creates a new signer with a fresh Ed25519 key pair.
 func NewNoopSigner(privKey crypto.PrivKey) (signer.Signer, error) {
-
-	return &NoopSigner{
+	sig := &NoopSigner{
 		privKey: privKey,
 		pubKey:  privKey.GetPublic(),
-	}, nil
+	}
+
+	address, err := getAddress(sig.pubKey)
+	if err != nil {
+		return nil, err
+	}
+	sig.address = address
+
+	return sig, nil
 }
 
 // Sign implements the Signer interface by signing the message with the Ed25519 private key.
 func (n *NoopSigner) Sign(message []byte) ([]byte, error) {
+	if n.privKey == nil {
+		return nil, fmt.Errorf("private key not loaded")
+	}
 	return n.privKey.Sign(message)
 }
 
 // GetPublic implements the Signer interface by returning the Ed25519 public key.
 func (n *NoopSigner) GetPublic() (crypto.PubKey, error) {
 	return n.pubKey, nil
 }
+
+// GetAddress implements the Signer interface by returning the Ed25519 address.
+func (n *NoopSigner) GetAddress() ([]byte, error) {
path: pkg/signer/noop/signer.go
user: login: tac0turtle
body: this still needed? 
created_at: 2025-04-10T18:21:59Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-10T10:47:27Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on base/target branches other than the default branch.
> 
> 
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=rollkit/rollkit&utm_content=2164):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-04-10T10:51:07Z
body: ## [Codecov](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) Report
Attention: Patch coverage is `25.67568%` with `55 lines` in your changes missing coverage. Please review.
> Project coverage is 26.79%. Comparing base [(`a45bb29`)](https://app.codecov.io/gh/rollkit/rollkit/commit/a45bb29abb25b4acb79ae6571bcb009380bf64e8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) to head [(`05ee30a`)](https://app.codecov.io/gh/rollkit/rollkit/commit/05ee30aab2f738581bdc74e46db884d2bdbe3d31?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit).
> Report is 1 commits behind head on feature/exec_api.

| [Files with missing lines](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) | Patch % | Lines |
|---|---|---|
| [block/manager.go](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&filepath=block%2Fmanager.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit#diff-YmxvY2svbWFuYWdlci5nbw==) | 0.00% | [30 Missing :warning: ](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) |
| [pkg/signer/file/local.go](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&filepath=pkg%2Fsigner%2Ffile%2Flocal.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit#diff-cGtnL3NpZ25lci9maWxlL2xvY2FsLmdv) | 0.00% | [12 Missing :warning: ](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) |
| [pkg/signer/noop/signer.go](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&filepath=pkg%2Fsigner%2Fnoop%2Fsigner.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit#diff-cGtnL3NpZ25lci9ub29wL3NpZ25lci5nbw==) | 50.00% | [8 Missing and 3 partials :warning: ](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) |
| [types/utils.go](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&filepath=types%2Futils.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit#diff-dHlwZXMvdXRpbHMuZ28=) | 71.42% | [2 Missing :warning: ](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit) |

<details><summary>Additional details and impacted files</summary>


```diff
@@                 Coverage Diff                  @@
##           feature/exec_api    #2164      +/-   ##
====================================================
- Coverage             27.11%   26.79%   -0.32%     
====================================================
  Files                    59       59              
  Lines                  6790     6803      +13     
====================================================
- Hits                   1841     1823      -18     
- Misses                 4751     4781      +30     
- Partials                198      199       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/rollkit/rollkit/pull/2164?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=rollkit).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE