Skip to content

Commit 6a0f367

Browse files
committed
remove need for pubkey and add some tests
1 parent da7b970 commit 6a0f367

8 files changed

Lines changed: 46 additions & 50 deletions

File tree

block/internal/syncing/assert.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"errors"
55
"fmt"
66

7+
"github.com/libp2p/go-libp2p/core/crypto"
8+
79
"github.com/evstack/ev-node/pkg/genesis"
810
"github.com/evstack/ev-node/types"
911
)
1012

11-
func assertExpectedProposer(genesis genesis.Genesis, height uint64, proposerAddr []byte, signer types.Signer) error {
12-
if err := genesis.ValidateProposer(height, proposerAddr, signer.PubKey); err != nil {
13+
func assertExpectedProposer(genesis genesis.Genesis, height uint64, proposerAddr []byte, pubKey crypto.PubKey) error {
14+
if err := genesis.ValidateProposer(height, proposerAddr, pubKey); err != nil {
1315
return fmt.Errorf("unexpected proposer at height %d: %w", height, err)
1416
}
1517

@@ -21,7 +23,7 @@ func assertValidSignedData(signedData *types.SignedData, genesis genesis.Genesis
2123
return errors.New("empty signed data")
2224
}
2325

24-
if err := assertExpectedProposer(genesis, signedData.Height(), signedData.Signer.Address, signedData.Signer); err != nil {
26+
if err := assertExpectedProposer(genesis, signedData.Height(), signedData.Signer.Address, signedData.Signer.PubKey); err != nil {
2527
return err
2628
}
2729

block/internal/syncing/da_retriever.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ func (r *daRetriever) tryDecodeData(bz []byte, daHeight uint64) *types.Data {
357357

358358
// assertExpectedProposer validates the proposer schedule entry for the header height.
359359
func (r *daRetriever) assertExpectedProposer(header *types.SignedHeader) error {
360-
return assertExpectedProposer(r.genesis, header.Height(), header.ProposerAddress, header.Signer)
360+
return assertExpectedProposer(r.genesis, header.Height(), header.ProposerAddress, header.Signer.PubKey)
361361
}
362362

363363
// assertValidSignedData validates signed data using the configured signature provider

block/internal/syncing/p2p_handler.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,5 @@ func (h *P2PHandler) ProcessHeight(ctx context.Context, height uint64, heightInC
127127

128128
// assertExpectedProposer validates the proposer schedule entry for the header height.
129129
func (h *P2PHandler) assertExpectedProposer(header *types.SignedHeader) error {
130-
if err := assertExpectedProposer(h.genesis, header.Height(), header.ProposerAddress, header.Signer); err != nil {
131-
return err
132-
}
133-
134-
return nil
130+
return assertExpectedProposer(h.genesis, header.Height(), header.ProposerAddress, header.Signer.PubKey)
135131
}

block/internal/syncing/raft_retriever.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func (r *raftRetriever) consumeRaftBlock(ctx context.Context, state *raft.RaftBl
125125
r.logger.Debug().Err(err).Msg("invalid header structure")
126126
return nil
127127
}
128-
if err := assertExpectedProposer(r.genesis, header.Height(), header.ProposerAddress, header.Signer); err != nil {
128+
if err := assertExpectedProposer(r.genesis, header.Height(), header.ProposerAddress, header.Signer.PubKey); err != nil {
129129
r.logger.Debug().Err(err).Msg("unexpected proposer")
130130
return nil
131131
}

docs/guides/operations/proposer-key-rotation.md

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Use this guide to rotate a sequencer proposer key without restarting the chain.
1111

1212
## How proposer rotation is stored in genesis
1313

14-
`proposer_address`, `proposer_schedule[].address`, and `proposer_schedule[].pub_key` are base64-encoded strings in JSON.
14+
`proposer_address` and `proposer_schedule[].address` are base64-encoded strings in JSON.
1515

1616
```json
1717
{
@@ -20,13 +20,11 @@ Use this guide to rotate a sequencer proposer key without restarting the chain.
2020
"proposer_schedule": [
2121
{
2222
"start_height": 1,
23-
"address": "0FQmA4Hn9dn8m4ZpM4+fV4e8KhkWjI4V2Vt1j9Qm5pA=",
24-
"pub_key": "5l6vM0b0GqQYQw4x0cI6q7N2vD1cE+oV6rN5eQ7v6dM="
23+
"address": "0FQmA4Hn9dn8m4ZpM4+fV4e8KhkWjI4V2Vt1j9Qm5pA="
2524
},
2625
{
2726
"start_height": 125000,
28-
"address": "Y7z5v9mQm4Nw6mD0a2yR9kD2B0qv5iJj1Q1R7gD4B7Q=",
29-
"pub_key": "9r5mM4XjKx6h6sJv2Jf6dB5nQ0eU9l8cM1qT2wV3yZQ="
27+
"address": "Y7z5v9mQm4Nw6mD0a2yR9kD2B0qv5iJj1Q1R7gD4B7Q="
3028
}
3129
]
3230
}
@@ -36,7 +34,6 @@ Rules enforced by `ev-node`:
3634

3735
- `proposer_schedule[0].start_height` must equal `initial_height`
3836
- schedule entries must be strictly increasing by `start_height`
39-
- every `address` must match its `pub_key`
4037
- if `proposer_address` is set, it must match the first schedule entry
4138

4239
Keep all earlier schedule entries. Fresh full nodes need them to validate historical blocks.
@@ -53,7 +50,7 @@ INITIAL_HEIGHT="$(jq -r '.initial_height' "$GENESIS")"
5350

5451
## 2. Get the current and replacement proposer public keys
5552

56-
For a file-based signer, the signer public key is stored in `signer.json` as base64:
53+
For a file-based signer, the signer public key is stored in `signer.json` as base64. You only put the derived address into genesis, but you still need the public key once to compute that address.
5754

5855
```bash
5956
OLD_SIGNER_DIR="$HOME/.evnode/config"
@@ -95,23 +92,19 @@ Create an explicit schedule with the current proposer at `initial_height` and th
9592
```bash
9693
jq \
9794
--arg old_addr "$OLD_PROPOSER_ADDRESS" \
98-
--arg old_pub "$OLD_PROPOSER_PUBKEY" \
9995
--arg new_addr "$NEW_PROPOSER_ADDRESS" \
100-
--arg new_pub "$NEW_PROPOSER_PUBKEY" \
10196
--argjson initial_height "$INITIAL_HEIGHT" \
10297
--argjson activation_height "$ACTIVATION_HEIGHT" \
10398
'
10499
.proposer_address = $old_addr
105100
| .proposer_schedule = [
106101
{
107102
start_height: $initial_height,
108-
address: $old_addr,
109-
pub_key: $old_pub
103+
address: $old_addr
110104
},
111105
{
112106
start_height: $activation_height,
113-
address: $new_addr,
114-
pub_key: $new_pub
107+
address: $new_addr
115108
}
116109
]
117110
' "$GENESIS" > "$GENESIS.tmp" && mv "$GENESIS.tmp" "$GENESIS"
@@ -124,14 +117,12 @@ Append the new entry. Do not replace older entries, and make sure `ACTIVATION_HE
124117
```bash
125118
jq \
126119
--arg new_addr "$NEW_PROPOSER_ADDRESS" \
127-
--arg new_pub "$NEW_PROPOSER_PUBKEY" \
128120
--argjson activation_height "$ACTIVATION_HEIGHT" \
129121
'
130122
.proposer_schedule += [
131123
{
132124
start_height: $activation_height,
133-
address: $new_addr,
134-
pub_key: $new_pub
125+
address: $new_addr
135126
}
136127
]
137128
' "$GENESIS" > "$GENESIS.tmp" && mv "$GENESIS.tmp" "$GENESIS"

pkg/genesis/genesis.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ func (g Genesis) Validate() error {
6363
return fmt.Errorf("proposer_address cannot be empty when proposer_schedule is unset")
6464
}
6565
} else {
66-
if err := g.ProposerSchedule[0].validate(g.InitialHeight, true); err != nil {
66+
if err := g.ProposerSchedule[0].validate(g.InitialHeight); err != nil {
6767
return fmt.Errorf("invalid proposer_schedule[0]: %w", err)
6868
}
6969
if g.ProposerSchedule[0].StartHeight != g.InitialHeight {
7070
return fmt.Errorf("proposer_schedule[0].start_height must equal initial_height (%d), got %d", g.InitialHeight, g.ProposerSchedule[0].StartHeight)
7171
}
7272
for i := 1; i < len(g.ProposerSchedule); i++ {
73-
if err := g.ProposerSchedule[i].validate(g.InitialHeight, true); err != nil {
73+
if err := g.ProposerSchedule[i].validate(g.InitialHeight); err != nil {
7474
return fmt.Errorf("invalid proposer_schedule[%d]: %w", i, err)
7575
}
7676
if g.ProposerSchedule[i].StartHeight <= g.ProposerSchedule[i-1].StartHeight {

pkg/genesis/proposer_schedule.go

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import (
88
"github.com/libp2p/go-libp2p/core/crypto"
99
)
1010

11-
// ProposerScheduleEntry declares the proposer key that becomes active at start_height.
11+
// ProposerScheduleEntry declares the proposer address that becomes active at start_height.
12+
// PubKey is optional and can be used to pin the exact key material for a schedule entry.
1213
type ProposerScheduleEntry struct {
1314
StartHeight uint64 `json:"start_height"`
1415
Address []byte `json:"address"`
@@ -33,8 +34,8 @@ func NewProposerScheduleEntry(startHeight uint64, pubKey crypto.PubKey) (Propose
3334
}, nil
3435
}
3536

36-
// PublicKey unmarshals the configured proposer public key. Legacy single-proposer
37-
// configs may omit the pubkey and will return nil, nil here.
37+
// PublicKey unmarshals the configured proposer public key. Address-only schedule
38+
// entries may omit the pubkey and will return nil, nil here.
3839
func (e ProposerScheduleEntry) PublicKey() (crypto.PubKey, error) {
3940
if len(e.PubKey) == 0 {
4041
return nil, nil
@@ -48,7 +49,7 @@ func (e ProposerScheduleEntry) PublicKey() (crypto.PubKey, error) {
4849
return pubKey, nil
4950
}
5051

51-
func (e ProposerScheduleEntry) validate(initialHeight uint64, requirePubKey bool) error {
52+
func (e ProposerScheduleEntry) validate(initialHeight uint64) error {
5253
if e.StartHeight < initialHeight {
5354
return fmt.Errorf("proposer schedule start_height must be >= initial_height (%d), got %d", initialHeight, e.StartHeight)
5455
}
@@ -58,9 +59,6 @@ func (e ProposerScheduleEntry) validate(initialHeight uint64, requirePubKey bool
5859
}
5960

6061
if len(e.PubKey) == 0 {
61-
if requirePubKey {
62-
return fmt.Errorf("proposer schedule pub_key cannot be empty")
63-
}
6462
return nil
6563
}
6664

@@ -92,7 +90,7 @@ func (g Genesis) EffectiveProposerSchedule() []ProposerScheduleEntry {
9290

9391
return []ProposerScheduleEntry{{
9492
StartHeight: g.InitialHeight,
95-
Address: cloneBytes(g.ProposerAddress),
93+
Address: bytes.Clone(g.ProposerAddress),
9694
}}
9795
}
9896

@@ -104,7 +102,7 @@ func (g Genesis) InitialProposerAddress() []byte {
104102
return nil
105103
}
106104

107-
return cloneBytes(entry.Address)
105+
return bytes.Clone(entry.Address)
108106
}
109107

110108
func (g Genesis) normalized() Genesis {
@@ -146,8 +144,8 @@ func (g Genesis) ProposerAtHeight(height uint64) (ProposerScheduleEntry, error)
146144

147145
return ProposerScheduleEntry{
148146
StartHeight: entry.StartHeight,
149-
Address: cloneBytes(entry.Address),
150-
PubKey: cloneBytes(entry.PubKey),
147+
Address: bytes.Clone(entry.Address),
148+
PubKey: bytes.Clone(entry.PubKey),
151149
}, nil
152150
}
153151

@@ -183,16 +181,6 @@ func (g Genesis) ValidateProposer(height uint64, address []byte, pubKey crypto.P
183181
return nil
184182
}
185183

186-
func cloneBytes(src []byte) []byte {
187-
if src == nil {
188-
return nil
189-
}
190-
191-
out := make([]byte, len(src))
192-
copy(out, src)
193-
return out
194-
}
195-
196184
func proposerKeyAddress(pubKey crypto.PubKey) []byte {
197185
if pubKey == nil {
198186
return nil

pkg/genesis/proposer_schedule_test.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestGenesisProposerAtHeight(t *testing.T) {
5151
require.Equal(t, entry2.Address, proposer.Address)
5252
}
5353

54-
func TestGenesisValidateProposerSchedule(t *testing.T) {
54+
func TestGenesisValidateProposerScheduleWithPinnedPubKey(t *testing.T) {
5555
entry1, pubKey1 := makeProposerScheduleEntry(t, 1)
5656
entry2, pubKey2 := makeProposerScheduleEntry(t, 20)
5757

@@ -69,6 +69,25 @@ func TestGenesisValidateProposerSchedule(t *testing.T) {
6969
require.Error(t, genesis.ValidateProposer(21, entry2.Address, pubKey1))
7070
}
7171

72+
func TestGenesisValidateAddressOnlyProposerSchedule(t *testing.T) {
73+
entry1, pubKey1 := makeProposerScheduleEntry(t, 1)
74+
entry2, pubKey2 := makeProposerScheduleEntry(t, 20)
75+
entry1.PubKey = nil
76+
entry2.PubKey = nil
77+
78+
genesis := Genesis{
79+
ChainID: "test-chain",
80+
StartTime: time.Now().UTC(),
81+
InitialHeight: 1,
82+
ProposerSchedule: []ProposerScheduleEntry{entry1, entry2},
83+
DAEpochForcedInclusion: 1,
84+
}
85+
86+
require.NoError(t, genesis.Validate())
87+
require.NoError(t, genesis.ValidateProposer(1, entry1.Address, pubKey1))
88+
require.NoError(t, genesis.ValidateProposer(21, entry2.Address, pubKey2))
89+
}
90+
7291
func TestLoadGenesisNormalizesLegacyProposerAddressFromSchedule(t *testing.T) {
7392
entry1, _ := makeProposerScheduleEntry(t, 1)
7493
entry2, _ := makeProposerScheduleEntry(t, 50)

0 commit comments

Comments
 (0)