Skip to content

Commit 05b0a80

Browse files
authored
Merge pull request #483 from shutter-network/access-node
Add access node
2 parents 313f2b3 + 81eb591 commit 05b0a80

11 files changed

+481
-17
lines changed

rolling-shutter/cmd/command.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/chain"
88
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/collator"
99
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/cryptocmd"
10+
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/gnosisaccessnode"
1011
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/gnosiskeyper"
1112
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/mocknode"
1213
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/mocksequencer"
@@ -31,6 +32,7 @@ func Subcommands() []*cobra.Command {
3132
snapshot.Cmd(),
3233
snapshotkeyper.Cmd(),
3334
gnosiskeyper.Cmd(),
35+
gnosisaccessnode.Cmd(),
3436
cryptocmd.Cmd(),
3537
proxy.Cmd(),
3638
mocksequencer.Cmd(),
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package gnosisaccessnode
2+
3+
import (
4+
"context"
5+
6+
"github.com/rs/zerolog/log"
7+
"github.com/spf13/cobra"
8+
9+
"github.com/shutter-network/rolling-shutter/rolling-shutter/cmd/shversion"
10+
"github.com/shutter-network/rolling-shutter/rolling-shutter/gnosisaccessnode"
11+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration/command"
12+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service"
13+
)
14+
15+
func Cmd() *cobra.Command {
16+
builder := command.Build(
17+
main,
18+
command.Usage(
19+
"Run an access node for the keyper network of Shutterized Gnosis Chain",
20+
`This command runs a node that only relays messages, but doesn't create any on
21+
its own. It is intended to be a stable node to connect to to receive messages.`,
22+
),
23+
command.WithGenerateConfigSubcommand(),
24+
command.WithDumpConfigSubcommand(),
25+
)
26+
return builder.Command()
27+
}
28+
29+
func main(config *gnosisaccessnode.Config) error {
30+
log.Info().
31+
Str("version", shversion.Version()).
32+
Msg("starting access node")
33+
34+
accessNode := gnosisaccessnode.New(config)
35+
return service.RunWithSighandler(context.Background(), accessNode)
36+
}

rolling-shutter/docs/rolling-shutter.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ A collection of commands to run and interact with Rolling Shutter nodes
1717
* [rolling-shutter chain](rolling-shutter_chain.md) - Run a node for Shutter's Tendermint chain
1818
* [rolling-shutter collator](rolling-shutter_collator.md) - Run a collator node
1919
* [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions
20+
* [rolling-shutter gnosis-access-node](rolling-shutter_gnosis-access-node.md) - Run an access node for the keyper network of Shutterized Gnosis Chain
2021
* [rolling-shutter gnosiskeyper](rolling-shutter_gnosiskeyper.md) - Run a Shutter keyper for Gnosis Chain
2122
* [rolling-shutter keyper](rolling-shutter_keyper.md) - Run a Shutter keyper node
2223
* [rolling-shutter mocknode](rolling-shutter_mocknode.md) - Run a Shutter mock node
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## rolling-shutter gnosis-access-node
2+
3+
Run an access node for the keyper network of Shutterized Gnosis Chain
4+
5+
### Synopsis
6+
7+
This command runs a node that only relays messages, but doesn't create any on
8+
its own. It is intended to be a stable node to connect to to receive messages.
9+
10+
```
11+
rolling-shutter gnosis-access-node [flags]
12+
```
13+
14+
### Options
15+
16+
```
17+
--config string config file
18+
-h, --help help for gnosis-access-node
19+
```
20+
21+
### Options inherited from parent commands
22+
23+
```
24+
--logformat string set log format, possible values: min, short, long, max (default "long")
25+
--loglevel string set log level, possible values: warn, info, debug (default "info")
26+
--no-color do not write colored logs
27+
```
28+
29+
### SEE ALSO
30+
31+
* [rolling-shutter](rolling-shutter.md) - A collection of commands to run and interact with Rolling Shutter nodes
32+
* [rolling-shutter gnosis-access-node dump-config](rolling-shutter_gnosis-access-node_dump-config.md) - Dump a 'gnosis-access-node' configuration file, based on given config and env vars
33+
* [rolling-shutter gnosis-access-node generate-config](rolling-shutter_gnosis-access-node_generate-config.md) - Generate a 'gnosis-access-node' configuration file
34+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## rolling-shutter gnosis-access-node dump-config
2+
3+
Dump a 'gnosis-access-node' configuration file, based on given config and env vars
4+
5+
```
6+
rolling-shutter gnosis-access-node dump-config [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
--config string config file
13+
-h, --help help for dump-config
14+
--output string output file
15+
```
16+
17+
### Options inherited from parent commands
18+
19+
```
20+
--logformat string set log format, possible values: min, short, long, max (default "long")
21+
--loglevel string set log level, possible values: warn, info, debug (default "info")
22+
--no-color do not write colored logs
23+
```
24+
25+
### SEE ALSO
26+
27+
* [rolling-shutter gnosis-access-node](rolling-shutter_gnosis-access-node.md) - Run an access node for the keyper network of Shutterized Gnosis Chain
28+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## rolling-shutter gnosis-access-node generate-config
2+
3+
Generate a 'gnosis-access-node' configuration file
4+
5+
```
6+
rolling-shutter gnosis-access-node generate-config [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
-h, --help help for generate-config
13+
--output string output file
14+
```
15+
16+
### Options inherited from parent commands
17+
18+
```
19+
--config string config file
20+
--logformat string set log format, possible values: min, short, long, max (default "long")
21+
--loglevel string set log level, possible values: warn, info, debug (default "info")
22+
--no-color do not write colored logs
23+
```
24+
25+
### SEE ALSO
26+
27+
* [rolling-shutter gnosis-access-node](rolling-shutter_gnosis-access-node.md) - Run an access node for the keyper network of Shutterized Gnosis Chain
28+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package gnosisaccessnode
2+
3+
import (
4+
"io"
5+
6+
gnosiskeyper "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis"
7+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/configuration"
8+
"github.com/shutter-network/rolling-shutter/rolling-shutter/medley/metricsserver"
9+
"github.com/shutter-network/rolling-shutter/rolling-shutter/p2p"
10+
)
11+
12+
type Config struct {
13+
InstanceID uint64
14+
15+
GnosisNode *configuration.EthnodeConfig
16+
Contracts *gnosiskeyper.GnosisContractsConfig
17+
P2P *p2p.Config
18+
Metrics *metricsserver.MetricsConfig
19+
20+
MaxNumKeysPerMessage uint64
21+
}
22+
23+
func (c *Config) Init() {
24+
c.GnosisNode = configuration.NewEthnodeConfig()
25+
c.Contracts = gnosiskeyper.NewGnosisContractsConfig()
26+
c.P2P = p2p.NewConfig()
27+
c.Metrics = metricsserver.NewConfig()
28+
}
29+
30+
func (c *Config) Name() string {
31+
return "gnosis-access-node"
32+
}
33+
34+
func (c *Config) Validate() error {
35+
if err := c.P2P.Validate(); err != nil {
36+
return err
37+
}
38+
if err := c.Metrics.Validate(); err != nil {
39+
return err
40+
}
41+
return nil
42+
}
43+
44+
func (c *Config) SetDefaultValues() error {
45+
return nil
46+
}
47+
48+
func (c *Config) SetExampleValues() error { //nolint:unparam
49+
c.InstanceID = 1000
50+
c.MaxNumKeysPerMessage = 500
51+
return nil
52+
}
53+
54+
func (c *Config) TOMLWriteHeader(_ io.Writer) (int, error) {
55+
return 0, nil
56+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package gnosisaccessnode
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"math"
7+
8+
pubsub "github.com/libp2p/go-libp2p-pubsub"
9+
"github.com/pkg/errors"
10+
11+
"github.com/shutter-network/shutter/shlib/shcrypto"
12+
13+
"github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/gnosis"
14+
"github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg"
15+
)
16+
17+
type DecryptionKeysHandler struct {
18+
config *Config
19+
storage *Storage
20+
}
21+
22+
func NewDecryptionKeysHandler(config *Config, storage *Storage) *DecryptionKeysHandler {
23+
return &DecryptionKeysHandler{
24+
config: config,
25+
storage: storage,
26+
}
27+
}
28+
29+
func (*DecryptionKeysHandler) MessagePrototypes() []p2pmsg.Message {
30+
return []p2pmsg.Message{&p2pmsg.DecryptionKeys{}}
31+
}
32+
33+
func (handler *DecryptionKeysHandler) ValidateMessage(_ context.Context, msg p2pmsg.Message) (pubsub.ValidationResult, error) {
34+
key := msg.(*p2pmsg.DecryptionKeys)
35+
result, err := handler.validateCommonFields(key)
36+
if result != pubsub.ValidationAccept || err != nil {
37+
return result, err
38+
}
39+
result, err = handler.validateGnosisFields(key)
40+
if result != pubsub.ValidationAccept || err != nil {
41+
return result, err
42+
}
43+
return pubsub.ValidationAccept, nil
44+
}
45+
46+
func (handler *DecryptionKeysHandler) validateCommonFields(keys *p2pmsg.DecryptionKeys) (pubsub.ValidationResult, error) {
47+
if keys.InstanceID != handler.config.InstanceID {
48+
return pubsub.ValidationReject,
49+
errors.Errorf("instance ID mismatch (want=%d, have=%d)", handler.config.InstanceID, keys.GetInstanceID())
50+
}
51+
if keys.Eon > math.MaxInt64 {
52+
return pubsub.ValidationReject, errors.Errorf("eon %d overflows int64", keys.Eon)
53+
}
54+
55+
if len(keys.Keys) == 0 {
56+
return pubsub.ValidationReject, errors.New("no keys in message")
57+
}
58+
if len(keys.Keys) > int(handler.config.MaxNumKeysPerMessage) {
59+
return pubsub.ValidationReject, errors.Errorf(
60+
"too many keys in message (%d > %d)",
61+
len(keys.Keys),
62+
handler.config.MaxNumKeysPerMessage,
63+
)
64+
}
65+
66+
eonKey, ok := handler.storage.GetEonKey(keys.Eon)
67+
if !ok {
68+
return pubsub.ValidationReject, errors.Errorf("no eon key found for eon %d", keys.Eon)
69+
}
70+
71+
for i, k := range keys.Keys {
72+
epochSecretKey, err := k.GetEpochSecretKey()
73+
if err != nil {
74+
return pubsub.ValidationReject, err
75+
}
76+
ok, err := shcrypto.VerifyEpochSecretKey(epochSecretKey, eonKey, k.Identity)
77+
if err != nil {
78+
return pubsub.ValidationReject, errors.Wrapf(err, "error while checking epoch secret key for identity %x", k.Identity)
79+
}
80+
if !ok {
81+
return pubsub.ValidationReject, errors.Errorf("epoch secret key for identity %x is not valid", k.Identity)
82+
}
83+
84+
if i > 0 && bytes.Compare(k.Identity, keys.Keys[i-1].Identity) < 0 {
85+
return pubsub.ValidationReject, errors.Errorf("keys not ordered")
86+
}
87+
}
88+
89+
return pubsub.ValidationAccept, nil
90+
}
91+
92+
func (handler *DecryptionKeysHandler) validateGnosisFields(keys *p2pmsg.DecryptionKeys) (pubsub.ValidationResult, error) {
93+
res, err := gnosis.ValidateDecryptionKeysBasic(keys)
94+
if res != pubsub.ValidationAccept || err != nil {
95+
return res, err
96+
}
97+
extra := keys.Extra.(*p2pmsg.DecryptionKeys_Gnosis).Gnosis
98+
99+
keyperSet, ok := handler.storage.GetKeyperSet(keys.Eon)
100+
if !ok {
101+
return pubsub.ValidationReject, errors.Errorf("no keyper set found for eon %d", keys.Eon)
102+
}
103+
104+
res, err = gnosis.ValidateDecryptionKeysSignatures(keys, extra, keyperSet)
105+
if res != pubsub.ValidationAccept || err != nil {
106+
return res, err
107+
}
108+
109+
return pubsub.ValidationAccept, nil
110+
}
111+
112+
func (handler *DecryptionKeysHandler) HandleMessage(_ context.Context, _ p2pmsg.Message) ([]p2pmsg.Message, error) {
113+
return nil, nil
114+
}

0 commit comments

Comments
 (0)