Skip to content

Commit 4ce27c9

Browse files
feat: added v2 message handling
1 parent c74827e commit 4ce27c9

File tree

5 files changed

+236
-59
lines changed

5 files changed

+236
-59
lines changed

rolling-shutter/keyperimpl/gnosis/validatorsyncer.go

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gnosis
22

33
import (
44
"context"
5+
"fmt"
56
"math"
67
"math/big"
78

@@ -24,8 +25,7 @@ import (
2425
)
2526

2627
const (
27-
ValidatorRegistrationMessageVersion = 0
28-
maxRequestBlockRange = 10_000
28+
maxRequestBlockRange = 10_000
2929
)
3030

3131
type ValidatorSyncer struct {
@@ -150,61 +150,83 @@ func (v *ValidatorSyncer) filterEvents(
150150
Uint("log-index", event.Raw.Index).
151151
Logger()
152152

153-
msg := new(validatorregistry.LegacyRegistrationMessage)
153+
msg := new(validatorregistry.AggregateRegistrationMessage)
154154
err := msg.Unmarshal(event.Message)
155155
if err != nil {
156156
evLog.Warn().
157157
Err(err).
158158
Msg("failed to unmarshal registration message")
159159
continue
160160
}
161-
evLog = evLog.With().Uint64("validator-index", msg.ValidatorIndex).Logger()
162161

163162
if !checkStaticRegistrationMessageFields(msg, v.ChainID, event.Raw.Address, evLog) {
164163
continue
165164
}
166165

167-
latestNonce, err := db.GetValidatorRegistrationNonceBefore(ctx, database.GetValidatorRegistrationNonceBeforeParams{
168-
ValidatorIndex: int64(msg.ValidatorIndex),
169-
BlockNumber: int64(event.Raw.BlockNumber),
170-
TxIndex: int64(event.Raw.TxIndex),
171-
LogIndex: int64(event.Raw.Index),
172-
})
173-
if err != nil && err != pgx.ErrNoRows {
174-
return nil, errors.Wrapf(err, "failed to query latest nonce for validator %d", msg.ValidatorIndex)
175-
}
176-
if err == pgx.ErrNoRows {
177-
latestNonce = -1
178-
}
179-
if msg.Nonce > math.MaxInt64 || int64(msg.Nonce) <= latestNonce {
180-
evLog.Warn().
181-
Uint64("nonce", msg.Nonce).
182-
Int64("latest-nonce", latestNonce).
183-
Msg("ignoring registration message with invalid nonce")
184-
continue
185-
}
166+
pubKeys := make([]*blst.P1Affine, 0)
167+
for _, validatorIndex := range msg.ValidatorIndices() {
168+
evLog = evLog.With().Int64("validator-index", validatorIndex).Logger()
169+
latestNonce, err := db.GetValidatorRegistrationNonceBefore(ctx, database.GetValidatorRegistrationNonceBeforeParams{
170+
ValidatorIndex: validatorIndex,
171+
BlockNumber: int64(event.Raw.BlockNumber),
172+
TxIndex: int64(event.Raw.TxIndex),
173+
LogIndex: int64(event.Raw.Index),
174+
})
175+
if err != nil && err != pgx.ErrNoRows {
176+
return nil, errors.Wrapf(err, "failed to query latest nonce for validator %d", msg.ValidatorIndex)
177+
}
178+
if err == pgx.ErrNoRows {
179+
latestNonce = -1
180+
}
186181

187-
validator, err := v.BeaconAPIClient.GetValidatorByIndex(ctx, "head", msg.ValidatorIndex)
188-
if err != nil {
189-
return nil, errors.Wrapf(err, "failed to get validator %d", msg.ValidatorIndex)
190-
}
191-
if validator == nil {
192-
evLog.Warn().Msg("ignoring registration message for unknown validator")
193-
continue
194-
}
195-
pubkey, err := validator.Data.Validator.GetPubkey()
196-
if err != nil {
197-
return nil, errors.Wrapf(err, "failed to get pubkey of validator %d", msg.ValidatorIndex)
182+
if msg.Nonce > math.MaxInt32 || int64(msg.Nonce) <= latestNonce {
183+
evLog.Warn().
184+
Uint32("nonce", msg.Nonce).
185+
Int64("latest-nonce", latestNonce).
186+
Msg("ignoring registration message with invalid nonce")
187+
continue
188+
}
189+
190+
validator, err := v.BeaconAPIClient.GetValidatorByIndex(ctx, "head", uint64(validatorIndex))
191+
if err != nil {
192+
return nil, errors.Wrapf(err, "failed to get validator %d", msg.ValidatorIndex)
193+
}
194+
if validator == nil {
195+
evLog.Warn().Msg("ignoring registration message for unknown validator")
196+
continue
197+
}
198+
pubkey, err := validator.Data.Validator.GetPubkey()
199+
if err != nil {
200+
return nil, errors.Wrapf(err, "failed to get pubkey of validator %d", msg.ValidatorIndex)
201+
}
202+
pubKeys = append(pubKeys, pubkey)
198203
}
204+
199205
sig := new(blst.P2Affine).Uncompress(event.Signature)
200206
if sig == nil {
201207
evLog.Warn().Msg("ignoring registration message with undecodable signature")
202208
continue
203209
}
204-
validSignature := validatorregistry.VerifySignature(sig, pubkey, msg)
205-
if !validSignature {
206-
evLog.Warn().Msg("ignoring registration message with invalid signature")
207-
continue
210+
211+
if msg.Version == validatorregistry.LegacyValidatorRegistrationMessageVersion {
212+
msg := new(validatorregistry.LegacyRegistrationMessage)
213+
err := msg.Unmarshal(event.Message)
214+
if err != nil {
215+
evLog.Warn().
216+
Err(err).
217+
Msg("failed to unmarshal registration message")
218+
continue
219+
}
220+
if validSignature := validatorregistry.VerifySignature(sig, pubKeys[0], msg); !validSignature {
221+
evLog.Warn().Msg("ignoring registration message with invalid signature")
222+
continue
223+
}
224+
} else {
225+
validSignature := validatorregistry.VerifyAggregateSignature(sig, pubKeys, msg)
226+
if !validSignature {
227+
evLog.Warn().Msg("ignoring registration message with invalid signature")
228+
continue
229+
}
208230
}
209231

210232
filteredEvents = append(filteredEvents, event)
@@ -215,37 +237,41 @@ func (v *ValidatorSyncer) filterEvents(
215237
func (v *ValidatorSyncer) insertEvents(ctx context.Context, tx pgx.Tx, events []*validatorRegistryBindings.ValidatorregistryUpdated) error {
216238
db := database.New(tx)
217239
for _, event := range events {
218-
msg := new(validatorregistry.LegacyRegistrationMessage)
240+
msg := new(validatorregistry.AggregateRegistrationMessage)
219241
err := msg.Unmarshal(event.Message)
220242
if err != nil {
221243
return errors.Wrap(err, "failed to unmarshal registration message")
222244
}
223-
err = db.InsertValidatorRegistration(ctx, database.InsertValidatorRegistrationParams{
224-
BlockNumber: int64(event.Raw.BlockNumber),
225-
BlockHash: event.Raw.BlockHash.Bytes(),
226-
TxIndex: int64(event.Raw.TxIndex),
227-
LogIndex: int64(event.Raw.Index),
228-
ValidatorIndex: int64(msg.ValidatorIndex),
229-
Nonce: int64(msg.Nonce),
230-
IsRegistration: msg.IsRegistration,
231-
})
232-
if err != nil {
233-
return errors.Wrap(err, "failed to insert validator registration into db")
245+
for _, validatorIndex := range msg.ValidatorIndices() {
246+
err = db.InsertValidatorRegistration(ctx, database.InsertValidatorRegistrationParams{
247+
BlockNumber: int64(event.Raw.BlockNumber),
248+
BlockHash: event.Raw.BlockHash.Bytes(),
249+
TxIndex: int64(event.Raw.TxIndex),
250+
LogIndex: int64(event.Raw.Index),
251+
ValidatorIndex: validatorIndex,
252+
Nonce: int64(msg.Nonce),
253+
IsRegistration: msg.IsRegistration,
254+
})
255+
if err != nil {
256+
return errors.Wrap(err, "failed to insert validator registration into db")
257+
}
234258
}
235259
}
236260
return nil
237261
}
238262

239263
func checkStaticRegistrationMessageFields(
240-
msg *validatorregistry.LegacyRegistrationMessage,
264+
msg *validatorregistry.AggregateRegistrationMessage,
241265
chainID uint64,
242266
validatorRegistryAddress common.Address,
243267
logger zerolog.Logger,
244268
) bool {
245-
if msg.Version != ValidatorRegistrationMessageVersion {
269+
if msg.Version != validatorregistry.AggregateValidatorRegistrationMessageVersion &&
270+
msg.Version != validatorregistry.LegacyValidatorRegistrationMessageVersion {
246271
logger.Warn().
247272
Uint8("version", msg.Version).
248-
Uint8("expected-version", ValidatorRegistrationMessageVersion).
273+
Str("expected-version", fmt.Sprintf("%d or %d", validatorregistry.LegacyValidatorRegistrationMessageVersion,
274+
validatorregistry.AggregateValidatorRegistrationMessageVersion)).
249275
Uint64("validator-index", msg.ValidatorIndex).
250276
Msg("ignoring registration message with invalid version")
251277
return false
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package gnosis
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"encoding/json"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/ethereum/go-ethereum/common"
12+
"github.com/ethereum/go-ethereum/core/types"
13+
validatorRegistryBindings "github.com/shutter-network/gnosh-contracts/gnoshcontracts/validatorregistry"
14+
"github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis/database"
15+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/beaconapiclient"
16+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testsetup"
17+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/validatorregistry"
18+
blst "github.com/supranational/blst/bindings/go"
19+
"gotest.tools/assert"
20+
)
21+
22+
func TestLegacyValidatorRegisterFilterEvent(t *testing.T) {
23+
if testing.Short() {
24+
t.Skip("skipping integration test")
25+
}
26+
msg := &validatorregistry.LegacyRegistrationMessage{
27+
Version: 0,
28+
ChainID: 2,
29+
ValidatorRegistryAddress: common.HexToAddress("0x1234567890123456789012345678901234567890"),
30+
ValidatorIndex: 3,
31+
Nonce: 0,
32+
IsRegistration: true,
33+
}
34+
ctx := context.Background()
35+
36+
var ikm [32]byte
37+
privkey := blst.KeyGen(ikm[:])
38+
pubkey := new(blst.P1Affine).From(privkey)
39+
40+
sig := validatorregistry.CreateSignature(privkey, msg)
41+
url := mockBeaconClient(t, hex.EncodeToString(pubkey.Compress()))
42+
43+
cl, err := beaconapiclient.New(url)
44+
assert.NilError(t, err)
45+
46+
dbpool, dbclose := testsetup.NewTestDBPool(ctx, t, database.Definition)
47+
t.Cleanup(dbclose)
48+
49+
vs := ValidatorSyncer{
50+
BeaconAPIClient: cl,
51+
DBPool: dbpool,
52+
ChainID: msg.ChainID,
53+
}
54+
55+
events := []*validatorRegistryBindings.ValidatorregistryUpdated{&validatorRegistryBindings.ValidatorregistryUpdated{
56+
Signature: sig.Compress(),
57+
Message: msg.Marshal(),
58+
Raw: types.Log{
59+
Address: common.HexToAddress("0x1234567890123456789012345678901234567890"),
60+
},
61+
}}
62+
63+
finalEvents, err := vs.filterEvents(ctx, events)
64+
assert.NilError(t, err)
65+
66+
assert.DeepEqual(t, finalEvents, events)
67+
}
68+
69+
func TestAggregateValidatorRegisterFilterEvent(t *testing.T) {
70+
if testing.Short() {
71+
t.Skip("skipping integration test")
72+
}
73+
msg := &validatorregistry.AggregateRegistrationMessage{
74+
Version: 1,
75+
ChainID: 2,
76+
ValidatorRegistryAddress: common.HexToAddress("0x1234567890123456789012345678901234567890"),
77+
ValidatorIndex: 3,
78+
Nonce: 0,
79+
Count: 1,
80+
IsRegistration: true,
81+
}
82+
ctx := context.Background()
83+
84+
var ikm [32]byte
85+
var sks []*blst.SecretKey
86+
var pks []*blst.P1Affine
87+
for i := 0; i < int(msg.Count); i++ {
88+
privkey := blst.KeyGen(ikm[:])
89+
pubkey := new(blst.P1Affine).From(privkey)
90+
sks = append(sks, privkey)
91+
pks = append(pks, pubkey)
92+
}
93+
94+
sig := validatorregistry.CreateAggregateSignature(sks, msg)
95+
url := mockBeaconClient(t, hex.EncodeToString(pks[0].Compress()))
96+
97+
cl, err := beaconapiclient.New(url)
98+
assert.NilError(t, err)
99+
100+
dbpool, dbclose := testsetup.NewTestDBPool(ctx, t, database.Definition)
101+
t.Cleanup(dbclose)
102+
103+
vs := ValidatorSyncer{
104+
BeaconAPIClient: cl,
105+
DBPool: dbpool,
106+
ChainID: msg.ChainID,
107+
}
108+
109+
events := []*validatorRegistryBindings.ValidatorregistryUpdated{&validatorRegistryBindings.ValidatorregistryUpdated{
110+
Signature: sig.Compress(),
111+
Message: msg.Marshal(),
112+
Raw: types.Log{
113+
Address: common.HexToAddress("0x1234567890123456789012345678901234567890"),
114+
},
115+
}}
116+
117+
finalEvents, err := vs.filterEvents(ctx, events)
118+
assert.NilError(t, err)
119+
120+
assert.DeepEqual(t, finalEvents, events)
121+
}
122+
123+
func mockBeaconClient(t *testing.T, pubKeyHex string) string {
124+
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
125+
x := beaconapiclient.GetValidatorByIndexResponse{
126+
Finalized: true,
127+
Data: beaconapiclient.ValidatorData{
128+
Validator: beaconapiclient.Validator{
129+
PubkeyHex: pubKeyHex,
130+
},
131+
},
132+
}
133+
res, err := json.Marshal(x)
134+
assert.NilError(t, err)
135+
w.WriteHeader(http.StatusOK)
136+
w.Write(res)
137+
})).URL
138+
}

rolling-shutter/medley/validatorregistry/signature.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
package validatorregistry
22

33
import (
4-
"fmt"
5-
64
"github.com/ethereum/go-ethereum/crypto"
75
blst "github.com/supranational/blst/bindings/go"
86
)
97

108
var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")
119

1210
func VerifyAggregateSignature(sig *blst.P2Affine, pks []*blst.P1Affine, msg *AggregateRegistrationMessage) bool {
13-
if msg.Version < 1 {
11+
if msg.Version < AggregateValidatorRegistrationMessageVersion {
1412
return false
1513
}
1614
if len(pks) != int(msg.Count) {
17-
fmt.Println(len(pks), int(msg.Count))
1815
return false
1916
}
2017
msgHash := crypto.Keccak256(msg.Marshal())
@@ -38,7 +35,7 @@ func CreateAggregateSignature(sks []*blst.SecretKey, msg *AggregateRegistrationM
3835
sig := aff.Sign(sk, msgHash, dst)
3936
ok := aggregate.Add(sig, true)
4037
if !ok {
41-
panic("failure")
38+
return nil
4239
}
4340
}
4441
return aggregate.ToAffine()

rolling-shutter/medley/validatorregistry/signature_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ func TestAggSignature(t *testing.T) {
4646
var sks []*blst.SecretKey
4747
var pks []*blst.P1Affine
4848
for i := 0; i < int(msg.Count); i++ {
49-
5049
privkey := blst.KeyGen(ikm[:])
5150
pubkey := new(blst.P1Affine).From(privkey)
5251
sks = append(sks, privkey)

0 commit comments

Comments
 (0)