Skip to content

Commit 6a33192

Browse files
gupadhyayaGanesha UpadhyayaManav-Aggarwal
authored
fix: Fix vote extension issues, add custom logic to handle secp256k1, fix rpc nilptr issues (#1717)
<!-- 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 <!-- 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> --> Things fixed: * getAddress does not work for secp256k1, hence using the address directly from the validator set * additional logic to handle secp256k1 signing * move updating store height to before apply block so that, the rpc (like Status) is able to use the updated height instead of old height * change vote extension signing to sign on the bytes from `VoteExtensionSignBytes` instead of plain extension * fix rpc nilptr failures using normalized height * add the missing vote extension enable height from genesis * retain votes from proposed last commit <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced proposal voting process to support different private key types. - **Refactor** - Updated cryptographic package usage from `ed25519` to `cmcrypto`. - Refactored key handling in multiple functions to support `secp256k1`. - **Tests** - Updated test cases to reflect changes in key handling and cryptographic package usage. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ganesha Upadhyaya <[email protected]> Co-authored-by: Manav Aggarwal <[email protected]>
1 parent c5a936f commit 6a33192

File tree

12 files changed

+232
-122
lines changed

12 files changed

+232
-122
lines changed

block/manager.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"sync/atomic"
1111
"time"
1212

13+
secp256k1 "github.com/btcsuite/btcd/btcec/v2"
14+
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
1315
abci "github.com/cometbft/cometbft/abci/types"
1416
cmcrypto "github.com/cometbft/cometbft/crypto"
1517
"github.com/cometbft/cometbft/crypto/merkle"
@@ -18,6 +20,7 @@ import (
1820
cmtypes "github.com/cometbft/cometbft/types"
1921
ds "github.com/ipfs/go-datastore"
2022
"github.com/libp2p/go-libp2p/core/crypto"
23+
"github.com/libp2p/go-libp2p/core/crypto/pb"
2124
pkgErrors "github.com/pkg/errors"
2225

2326
goheaderstore "github.com/celestiaorg/go-header/store"
@@ -192,10 +195,7 @@ func NewManager(
192195
conf.DAMempoolTTL = defaultMempoolTTL
193196
}
194197

195-
proposerAddress, err := getAddress(proposerKey)
196-
if err != nil {
197-
return nil, err
198-
}
198+
proposerAddress := s.Validators.Proposer.Address.Bytes()
199199

200200
maxBlobSize, err := dalc.DA.MaxBlobSize(context.Background())
201201
if err != nil {
@@ -264,14 +264,6 @@ func NewManager(
264264
return agg, nil
265265
}
266266

267-
func getAddress(key crypto.PrivKey) ([]byte, error) {
268-
rawKey, err := key.GetPublic().Raw()
269-
if err != nil {
270-
return nil, err
271-
}
272-
return cmcrypto.AddressHash(rawKey), nil
273-
}
274-
275267
// SetDALC is used to set DataAvailabilityLayerClient used by Manager.
276268
func (m *Manager) SetDALC(dalc *da.DAClient) {
277269
m.dalc = dalc
@@ -749,8 +741,7 @@ func getRemainingSleep(start time.Time, interval, defaultSleep time.Duration) ti
749741
func (m *Manager) getCommit(header types.Header) (*types.Commit, error) {
750742
// note: for compatibility with tendermint light client
751743
consensusVote := header.MakeCometBFTVote()
752-
753-
sign, err := m.proposerKey.Sign(consensusVote)
744+
sign, err := m.sign(consensusVote)
754745
if err != nil {
755746
return nil, err
756747
}
@@ -851,7 +842,6 @@ func (m *Manager) publishBlock(ctx context.Context) error {
851842
// if call to applyBlock fails, we halt the node, see https://github.com/cometbft/cometbft/pull/496
852843
panic(err)
853844
}
854-
855845
// Before taking the hash, we need updated ISRs, hence after ApplyBlock
856846
block.SignedHeader.Header.DataHash, err = block.Data.Hash()
857847
if err != nil {
@@ -875,7 +865,7 @@ func (m *Manager) publishBlock(ctx context.Context) error {
875865
}
876866

877867
blockHeight := block.Height()
878-
// Update the stored height before submitting to the DA layer and committing to the DB
868+
// Update the store height before submitting to the DA layer and committing to the DB
879869
m.store.SetHeight(ctx, blockHeight)
880870

881871
blockHash := block.Hash().String()
@@ -930,6 +920,28 @@ func (m *Manager) publishBlock(ctx context.Context) error {
930920
return nil
931921
}
932922

923+
func (m *Manager) sign(payload []byte) ([]byte, error) {
924+
var sig []byte
925+
switch m.proposerKey.Type() {
926+
case pb.KeyType_Ed25519:
927+
return m.proposerKey.Sign(payload)
928+
case pb.KeyType_Secp256k1:
929+
k := m.proposerKey.(*crypto.Secp256k1PrivateKey)
930+
rawBytes, err := k.Raw()
931+
if err != nil {
932+
return nil, err
933+
}
934+
priv, _ := secp256k1.PrivKeyFromBytes(rawBytes)
935+
sig, err = ecdsa.SignCompact(priv, cmcrypto.Sha256(payload), false)
936+
if err != nil {
937+
return nil, err
938+
}
939+
return sig[1:], nil
940+
default:
941+
return nil, fmt.Errorf("unsupported private key type: %T", m.proposerKey)
942+
}
943+
}
944+
933945
func (m *Manager) processVoteExtension(ctx context.Context, block *types.Block, newHeight uint64) error {
934946
if !m.voteExtensionEnabled(newHeight) {
935947
return nil
@@ -939,9 +951,17 @@ func (m *Manager) processVoteExtension(ctx context.Context, block *types.Block,
939951
if err != nil {
940952
return fmt.Errorf("error returned by ExtendVote: %w", err)
941953
}
942-
sign, err := m.proposerKey.Sign(extension)
954+
955+
vote := &cmproto.Vote{
956+
Height: int64(newHeight),
957+
Round: 0,
958+
Extension: extension,
959+
}
960+
extSignBytes := cmtypes.VoteExtensionSignBytes(m.genesis.ChainID, vote)
961+
962+
sign, err := m.sign(extSignBytes)
943963
if err != nil {
944-
return fmt.Errorf("error signing vote extension: %w", err)
964+
return fmt.Errorf("failed to sign vote extension: %w", err)
945965
}
946966
extendedCommit := buildExtendedCommit(block, extension, sign)
947967
err = m.store.SaveExtendedCommit(ctx, newHeight, extendedCommit)

block/manager_test.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
"testing"
1010
"time"
1111

12+
cmcrypto "github.com/cometbft/cometbft/crypto"
1213
"github.com/cometbft/cometbft/crypto/ed25519"
14+
"github.com/cometbft/cometbft/crypto/secp256k1"
1315
cmtypes "github.com/cometbft/cometbft/types"
1416
ds "github.com/ipfs/go-datastore"
1517
"github.com/libp2p/go-libp2p/core/crypto"
@@ -54,7 +56,7 @@ func getBlockBiggerThan(blockHeight, limit uint64) (*types.Block, error) {
5456

5557
func TestInitialStateClean(t *testing.T) {
5658
require := require.New(t)
57-
genesisDoc, _ := types.GetGenesisWithPrivkey()
59+
genesisDoc, _ := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
5860
genesis := &cmtypes.GenesisDoc{
5961
ChainID: "myChain",
6062
InitialHeight: 1,
@@ -71,7 +73,7 @@ func TestInitialStateClean(t *testing.T) {
7173

7274
func TestInitialStateStored(t *testing.T) {
7375
require := require.New(t)
74-
genesisDoc, _ := types.GetGenesisWithPrivkey()
76+
genesisDoc, _ := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
7577
valset := types.GetRandomValidatorSet()
7678
genesis := &cmtypes.GenesisDoc{
7779
ChainID: "myChain",
@@ -102,8 +104,8 @@ func TestInitialStateStored(t *testing.T) {
102104

103105
func TestInitialStateUnexpectedHigherGenesis(t *testing.T) {
104106
require := require.New(t)
107+
genesisDoc, _ := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
105108
valset := types.GetRandomValidatorSet()
106-
genesisDoc, _ := types.GetGenesisWithPrivkey()
107109
genesis := &cmtypes.GenesisDoc{
108110
ChainID: "myChain",
109111
InitialHeight: 2,
@@ -128,6 +130,31 @@ func TestInitialStateUnexpectedHigherGenesis(t *testing.T) {
128130
require.EqualError(err, "genesis.InitialHeight (2) is greater than last stored state's LastBlockHeight (0)")
129131
}
130132

133+
func TestSignVerifySignature(t *testing.T) {
134+
require := require.New(t)
135+
m := getManager(t, goDATest.NewDummyDA())
136+
payload := []byte("test")
137+
cases := []struct {
138+
name string
139+
input cmcrypto.PrivKey
140+
}{
141+
{"ed25519", ed25519.GenPrivKey()},
142+
{"secp256k1", secp256k1.GenPrivKey()},
143+
}
144+
for _, c := range cases {
145+
t.Run(c.name, func(t *testing.T) {
146+
pubKey := c.input.PubKey()
147+
signingKey, err := types.PrivKeyToSigningKey(c.input)
148+
require.NoError(err)
149+
m.proposerKey = signingKey
150+
sig, err := m.sign(payload)
151+
require.NoError(err)
152+
ok := pubKey.VerifySignature(payload, sig)
153+
require.True(ok)
154+
})
155+
}
156+
}
157+
131158
func TestIsDAIncluded(t *testing.T) {
132159
require := require.New(t)
133160

@@ -426,7 +453,7 @@ func Test_isProposer(t *testing.T) {
426453
{
427454
name: "Signing key matches genesis proposer public key",
428455
args: func() args {
429-
genesisData, privKey := types.GetGenesisWithPrivkey()
456+
genesisData, privKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
430457
s, err := types.NewFromGenesisDoc(genesisData)
431458
require.NoError(err)
432459
signingKey, err := types.PrivKeyToSigningKey(privKey)
@@ -442,7 +469,7 @@ func Test_isProposer(t *testing.T) {
442469
{
443470
name: "Signing key does not match genesis proposer public key",
444471
args: func() args {
445-
genesisData, _ := types.GetGenesisWithPrivkey()
472+
genesisData, _ := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
446473
s, err := types.NewFromGenesisDoc(genesisData)
447474
require.NoError(err)
448475

@@ -460,7 +487,7 @@ func Test_isProposer(t *testing.T) {
460487
{
461488
name: "No validators found in genesis",
462489
args: func() args {
463-
genesisData, privKey := types.GetGenesisWithPrivkey()
490+
genesisData, privKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
464491
genesisData.Validators = nil
465492
s, err := types.NewFromGenesisDoc(genesisData)
466493
require.NoError(err)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ require (
3636

3737
require (
3838
github.com/BurntSushi/toml v1.4.0
39+
github.com/btcsuite/btcd/btcec/v2 v2.3.2
3940
github.com/celestiaorg/go-header v0.6.2
4041
github.com/ipfs/go-ds-badger4 v0.1.5
4142
github.com/mitchellh/mapstructure v1.5.0
@@ -44,7 +45,6 @@ require (
4445
require (
4546
github.com/benbjohnson/clock v1.3.5 // indirect
4647
github.com/beorn7/perks v1.0.1 // indirect
47-
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
4848
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
4949
github.com/celestiaorg/go-libp2p-messenger v0.2.0 // indirect
5050
github.com/cespare/xxhash v1.1.0 // indirect

node/full_client.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,22 @@ func (c *FullClient) BlockSearch(ctx context.Context, query string, page, perPag
707707

708708
// Status returns detailed information about current status of the node.
709709
func (c *FullClient) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
710-
latest, err := c.node.Store.GetBlock(ctx, c.node.Store.Height())
711-
if err != nil {
712-
return nil, fmt.Errorf("failed to find latest block: %w", err)
710+
var (
711+
latestBlockHash cmbytes.HexBytes
712+
latestAppHash cmbytes.HexBytes
713+
latestBlockTime time.Time
714+
715+
latestHeight = c.node.Store.Height()
716+
)
717+
718+
if latestHeight != 0 {
719+
latest, err := c.node.Store.GetBlock(ctx, latestHeight)
720+
if err != nil {
721+
return nil, fmt.Errorf("failed to find latest block: %w", err)
722+
}
723+
latestBlockHash = cmbytes.HexBytes(latest.SignedHeader.DataHash)
724+
latestAppHash = cmbytes.HexBytes(latest.SignedHeader.AppHash)
725+
latestBlockTime = latest.Time()
713726
}
714727

715728
initial, err := c.node.Store.GetBlock(ctx, uint64(c.node.GetGenesis().InitialHeight))
@@ -761,10 +774,10 @@ func (c *FullClient) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
761774
},
762775
},
763776
SyncInfo: ctypes.SyncInfo{
764-
LatestBlockHash: cmbytes.HexBytes(latest.SignedHeader.DataHash),
765-
LatestAppHash: cmbytes.HexBytes(latest.SignedHeader.AppHash),
766-
LatestBlockHeight: int64(latest.Height()),
767-
LatestBlockTime: latest.Time(),
777+
LatestBlockHash: latestBlockHash,
778+
LatestAppHash: latestAppHash,
779+
LatestBlockHeight: int64(latestHeight),
780+
LatestBlockTime: latestBlockTime,
768781
EarliestBlockHash: cmbytes.HexBytes(initial.SignedHeader.DataHash),
769782
EarliestAppHash: cmbytes.HexBytes(initial.SignedHeader.AppHash),
770783
EarliestBlockHeight: int64(initial.Height()),
@@ -822,10 +835,11 @@ func (c *FullClient) CheckTx(ctx context.Context, tx cmtypes.Tx) (*ctypes.Result
822835
}
823836

824837
// Header returns a cometbft ResultsHeader for the FullClient
825-
func (c *FullClient) Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
826-
blockMeta := c.getBlockMeta(ctx, *height)
838+
func (c *FullClient) Header(ctx context.Context, heightPtr *int64) (*ctypes.ResultHeader, error) {
839+
height := c.normalizeHeight(heightPtr)
840+
blockMeta := c.getBlockMeta(ctx, height)
827841
if blockMeta == nil {
828-
return nil, fmt.Errorf("block at height %d not found", *height)
842+
return nil, fmt.Errorf("block at height %d not found", height)
829843
}
830844
return &ctypes.ResultHeader{Header: &blockMeta.Header}, nil
831845
}
@@ -919,8 +933,8 @@ func (c *FullClient) normalizeHeight(height *int64) uint64 {
919933
return heightValue
920934
}
921935

922-
func (rpc *FullClient) getBlockMeta(ctx context.Context, n int64) *cmtypes.BlockMeta {
923-
b, err := rpc.node.Store.GetBlock(ctx, uint64(n))
936+
func (rpc *FullClient) getBlockMeta(ctx context.Context, n uint64) *cmtypes.BlockMeta {
937+
b, err := rpc.node.Store.GetBlock(ctx, n)
924938
if err != nil {
925939
return nil
926940
}

node/full_client_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func getRPC(t *testing.T) (*mocks.Application, *FullClient) {
6363
app.On("InitChain", mock.Anything, mock.Anything).Return(&abci.ResponseInitChain{}, nil)
6464
key, _, _ := crypto.GenerateEd25519Key(crand.Reader)
6565
ctx := context.Background()
66-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
66+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
6767
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
6868
require.NoError(err)
6969
node, err := newFullNode(
@@ -539,7 +539,7 @@ func TestTx(t *testing.T) {
539539
mockApp.On("PrepareProposal", mock.Anything, mock.Anything).Return(prepareProposalResponse).Maybe()
540540
mockApp.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
541541
key, _, _ := crypto.GenerateEd25519Key(crand.Reader)
542-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
542+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
543543
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
544544
require.NoError(err)
545545
ctx, cancel := context.WithCancel(context.Background())
@@ -777,7 +777,7 @@ func TestMempool2Nodes(t *testing.T) {
777777
assert := assert.New(t)
778778
require := require.New(t)
779779

780-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
780+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
781781
signingKey1, err := types.PrivKeyToSigningKey(genesisValidatorKey)
782782
require.NoError(err)
783783

@@ -869,7 +869,7 @@ func TestStatus(t *testing.T) {
869869
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(prepareProposalResponse).Maybe()
870870
app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
871871
key, _, _ := crypto.GenerateEd25519Key(crand.Reader)
872-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
872+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
873873
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
874874
require.NoError(err)
875875
pubKey := genesisDoc.Validators[0].PubKey
@@ -1013,7 +1013,7 @@ func TestFutureGenesisTime(t *testing.T) {
10131013
mockApp.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil)
10141014
mockApp.On("CheckTx", mock.Anything, mock.Anything).Return(&abci.ResponseCheckTx{}, nil)
10151015
key, _, _ := crypto.GenerateEd25519Key(crand.Reader)
1016-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
1016+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
10171017
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
10181018
require.NoError(err)
10191019
genesisTime := time.Now().Local().Add(time.Second * time.Duration(1))

node/full_node_integration_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func TestAggregatorMode(t *testing.T) {
5656
app.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil)
5757

5858
key, _, _ := crypto.GenerateEd25519Key(rand.Reader)
59-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
59+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
6060
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
6161
require.NoError(err)
6262
blockManagerConfig := config.BlockManagerConfig{
@@ -170,7 +170,7 @@ func TestLazyAggregator(t *testing.T) {
170170
app.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil)
171171

172172
key, _, _ := crypto.GenerateEd25519Key(rand.Reader)
173-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
173+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
174174
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
175175
require.NoError(err)
176176
blockManagerConfig := config.BlockManagerConfig{
@@ -328,7 +328,7 @@ func TestChangeValSet(t *testing.T) {
328328

329329
// tmpubKey1
330330
key, _, _ := crypto.GenerateEd25519Key(rand.Reader)
331-
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey()
331+
genesisDoc, genesisValidatorKey := types.GetGenesisWithPrivkey(types.DefaultSigningKeyType)
332332
signingKey, err := types.PrivKeyToSigningKey(genesisValidatorKey)
333333
require.NoError(err)
334334
tmPubKey1, err := cryptoenc.PubKeyToProto(genesisDoc.Validators[0].PubKey)

0 commit comments

Comments
 (0)