Skip to content

Commit 7cc9556

Browse files
committed
Only generate keys for registered proposers
1 parent 2f6b7b3 commit 7cc9556

File tree

5 files changed

+137
-15
lines changed

5 files changed

+137
-15
lines changed

rolling-shutter/keyperimpl/gnosis/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ type GnosisConfig struct {
100100
EncryptedGasLimit uint64 `shconfig:",required"`
101101
MinGasPerTransaction uint64 `shconfig:",required"`
102102
SecondsPerSlot uint64 `shconfig:",required"`
103+
SlotsPerEpoch uint64 `shconfig:",required"`
103104
GenesisSlotTimestamp uint64 `shconfig:",required"`
104105
}
105106

@@ -110,6 +111,7 @@ func NewGnosisConfig() *GnosisConfig {
110111
EncryptedGasLimit: 0,
111112
MinGasPerTransaction: 0,
112113
SecondsPerSlot: 0,
114+
SlotsPerEpoch: 0,
113115
GenesisSlotTimestamp: 0,
114116
}
115117
c.Init()
@@ -140,6 +142,7 @@ func (c *GnosisConfig) SetExampleValues() error {
140142
c.EncryptedGasLimit = 1_000_000
141143
c.MinGasPerTransaction = 21_000
142144
c.SecondsPerSlot = 5
145+
c.SlotsPerEpoch = 16
143146
c.GenesisSlotTimestamp = 1665410700
144147
return nil
145148
}

rolling-shutter/keyperimpl/gnosis/keyper.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ const (
4141
)
4242

4343
type Keyper struct {
44-
core *keyper.KeyperCore
45-
config *Config
46-
dbpool *pgxpool.Pool
47-
client *ethclient.Client
44+
core *keyper.KeyperCore
45+
config *Config
46+
dbpool *pgxpool.Pool
47+
beaconAPIClient *beaconapiclient.Client
48+
4849
chainSyncClient *chainsync.Client
4950
sequencerSyncer *SequencerSyncer
5051
validatorSyncer *ValidatorSyncer
@@ -94,7 +95,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error {
9495
if err != nil {
9596
return errors.Wrap(err, "failed to connect to database")
9697
}
97-
beaconAPIClient, err := beaconapiclient.New(kpr.config.BeaconAPIURL)
98+
kpr.beaconAPIClient, err = beaconapiclient.New(kpr.config.BeaconAPIURL)
9899
if err != nil {
99100
return errors.Wrap(err, "failed to initialize beacon API client")
100101
}
@@ -117,7 +118,7 @@ func (kpr *Keyper) Start(ctx context.Context, runner service.Runner) error {
117118
kpr.validatorSyncer = &ValidatorSyncer{
118119
Contract: validatorRegistryContract,
119120
DBPool: kpr.dbpool,
120-
BeaconAPIClient: beaconAPIClient,
121+
BeaconAPIClient: kpr.beaconAPIClient,
121122
ChainID: chainID.Uint64(),
122123
}
123124

rolling-shutter/keyperimpl/gnosis/newslot.go

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
corekeyperdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/database"
1717
"github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/epochkghandler"
1818
gnosisdatabase "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database"
19+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley"
1920
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/broker"
2021
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage"
2122
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/slotticker"
@@ -72,16 +73,60 @@ func (kpr *Keyper) maybeTriggerDecryption(ctx context.Context, slot uint64) erro
7273
if err != nil {
7374
return errors.Wrapf(err, "failed to query keyper set for block %d", nextBlock)
7475
}
75-
if keyperSet.Contains(kpr.config.GetAddress()) {
76-
return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet)
76+
77+
// don't trigger if we're not part of the keyper set
78+
if !keyperSet.Contains(kpr.config.GetAddress()) {
79+
log.Debug().
80+
Uint64("slot", slot).
81+
Int64("block-number", nextBlock).
82+
Int64("keyper-set-index", keyperSet.KeyperConfigIndex).
83+
Str("address", kpr.config.GetAddress().Hex()).
84+
Msg("skipping slot as not part of keyper set")
85+
return nil
7786
}
78-
log.Debug().
79-
Uint64("slot", slot).
80-
Int64("block-number", nextBlock).
81-
Int64("keyper-set-index", keyperSet.KeyperConfigIndex).
82-
Str("address", kpr.config.GetAddress().Hex()).
83-
Msg("skipping slot as not part of keyper set")
84-
return nil
87+
88+
// don't trigger if the block proposer is not part of the validator registry
89+
isRegistered, err := kpr.isProposerRegistered(ctx, slot)
90+
if err != nil {
91+
return err
92+
}
93+
if !isRegistered {
94+
log.Debug().
95+
Uint64("slot", slot).
96+
Msg("skipping slot as proposer is not registered")
97+
return nil
98+
}
99+
100+
return kpr.triggerDecryption(ctx, slot, nextBlock, &keyperSet)
101+
}
102+
103+
func (kpr *Keyper) isProposerRegistered(ctx context.Context, slot uint64) (bool, error) {
104+
epoch := medley.SlotToEpoch(slot, kpr.config.Gnosis.SlotsPerEpoch)
105+
proposerDuties, err := kpr.beaconAPIClient.GetProposerDutiesByEpoch(ctx, epoch)
106+
if err != nil {
107+
return false, err
108+
}
109+
if proposerDuties == nil {
110+
return false, errors.Errorf("no proposer duties found for slot %d in epoch %d", slot, epoch)
111+
}
112+
proposerDuty, err := proposerDuties.GetDutyForSlot(slot)
113+
if err != nil {
114+
return false, err
115+
}
116+
proposerIndex := proposerDuty.ValidatorIndex
117+
if proposerIndex > math.MaxInt64 {
118+
return false, errors.New("proposer index too big")
119+
}
120+
121+
db := gnosisdatabase.New(kpr.dbpool)
122+
isRegistered, err := db.IsValidatorRegistered(ctx, int64(proposerDuty.ValidatorIndex))
123+
if err == pgx.ErrNoRows {
124+
return false, nil
125+
}
126+
if err != nil {
127+
return false, errors.Wrapf(err, "failed to query registration status for validator %d", proposerDuty.ValidatorIndex)
128+
}
129+
return isRegistered, nil
85130
}
86131

87132
func (kpr *Keyper) getTxPointer(ctx context.Context, eon int64, slot int64, keyperConfigIndex int64) (int64, error) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package beaconapiclient
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
10+
"github.com/pkg/errors"
11+
)
12+
13+
type GetProposerDutiesResponse struct {
14+
ExecutionOptimistic bool `json:"execution_optimistic"`
15+
DependentRoot string `json:"dependent_root"`
16+
Data []ProposerDuty `json:"data"`
17+
}
18+
19+
type ProposerDuty struct {
20+
// Pubkey blst.P1Affine `json:"pubkey"`
21+
Pubkey string `json:"pubkey"`
22+
ValidatorIndex uint64 `json:"validator_index,string"`
23+
Slot uint64 `json:"slot,string"`
24+
}
25+
26+
func (c *Client) GetProposerDutiesByEpoch(
27+
ctx context.Context,
28+
epoch uint64,
29+
) (*GetProposerDutiesResponse, error) {
30+
path := c.url.JoinPath("/eth/v1/validator/duties/proposer/", fmt.Sprint(epoch))
31+
req, err := http.NewRequestWithContext(ctx, "GET", path.String(), http.NoBody)
32+
if err != nil {
33+
return nil, err
34+
}
35+
res, err := http.DefaultClient.Do(req)
36+
if err != nil {
37+
return nil, errors.Wrapf(err, "failed to get proposer duties for epoch %d from consensus node", epoch)
38+
}
39+
defer res.Body.Close()
40+
41+
if res.StatusCode == http.StatusNotFound {
42+
return nil, nil
43+
}
44+
if res.StatusCode != http.StatusOK {
45+
return nil, errors.Wrapf(err, "failed to get proposer duties for epoch %d from consensus node", epoch)
46+
}
47+
48+
body, err := io.ReadAll(res.Body)
49+
if err != nil {
50+
return nil, errors.Wrapf(err, "failed to read consensus client response body")
51+
}
52+
53+
response := new(GetProposerDutiesResponse)
54+
err = json.Unmarshal(body, response)
55+
if err != nil {
56+
return nil, errors.Wrapf(err, "failed to unmarshal consensus client response body")
57+
}
58+
59+
return response, nil
60+
}
61+
62+
func (r *GetProposerDutiesResponse) GetDutyForSlot(slot uint64) (ProposerDuty, error) {
63+
for _, duty := range r.Data {
64+
if duty.Slot == slot {
65+
return duty, nil
66+
}
67+
}
68+
return ProposerDuty{}, errors.Errorf("consensus client response does not contain duty for slot %d", slot)
69+
}

rolling-shutter/medley/slots.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ package medley
33
func BlockTimestampToSlot(blockTimestamp uint64, genesisSlotTimestamp uint64, secondsPerSlot uint64) uint64 {
44
return (blockTimestamp - genesisSlotTimestamp) / secondsPerSlot
55
}
6+
7+
func SlotToEpoch(slot uint64, slotsPerEpoch uint64) uint64 {
8+
return slot / slotsPerEpoch
9+
}

0 commit comments

Comments
 (0)