feat: use generic flags and convert values to specific data types

This commit is contained in:
Richard Ramos 2022-08-15 13:13:45 -04:00
parent 4589b6c31c
commit 39f70f71a9
14 changed files with 266 additions and 114 deletions

3
.golangci.yaml Normal file
View File

@ -0,0 +1,3 @@
run:
build-tags:
- gowaku_rln

View File

@ -83,7 +83,7 @@ generate:
coverage:
${GOBIN} test -timeout 300s -count 1 -coverprofile=coverage.out ./...
${GOBIN} test -count 1 -coverprofile=coverage.out ./...
${GOBIN} tool cover -html=coverage.out -o=coverage.html
# build a docker image for the fleet

2
go.mod
View File

@ -41,6 +41,7 @@ require (
github.com/flynn/noise v1.0.0
github.com/gorilla/mux v1.8.0
github.com/status-im/go-rln v0.0.9
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/text v0.3.7
)
@ -155,7 +156,6 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect

View File

@ -34,7 +34,7 @@ func lightpushPublish(msg pb.WakuMessage, pubsubTopic string, peerID string, ms
}
lpOptions = append(lpOptions, lightpush.WithPeer(p))
} else {
lpOptions = append(lpOptions, lightpush.WithAutomaticPeerSelection(wakuNode.Host()))
lpOptions = append(lpOptions, lightpush.WithAutomaticPeerSelection())
}
hash, err := wakuNode.Lightpush().PublishToTopic(ctx, &msg, pubsubTopic, lpOptions...)

89
waku.go
View File

@ -2,9 +2,11 @@ package main
import (
"os"
"time"
logging "github.com/ipfs/go-log"
"github.com/status-im/go-waku/waku"
"github.com/status-im/go-waku/waku/cliutils"
"github.com/status-im/go-waku/waku/v2/utils"
"github.com/urfave/cli/v2"
)
@ -12,6 +14,10 @@ import (
var options waku.Options
func main() {
// Defaults
options.LogLevel = "INFO"
options.LogEncoding = "console"
cliFlags := []cli.Flag{
&cli.IntFlag{
Name: "tcp-port",
@ -53,14 +59,14 @@ func main() {
Usage: "Enable secure websockets support",
Destination: &options.Websocket.Secure,
},
&cli.StringFlag{
&cli.PathFlag{
Name: "websocket-secure-key-path",
Aliases: []string{"wss-key"},
Value: "/path/to/key.txt",
Usage: "Secure websocket key path",
Destination: &options.Websocket.KeyPath,
},
&cli.StringFlag{
&cli.PathFlag{
Name: "websocket-secure-cert-path",
Aliases: []string{"wss-cert"},
Value: "/path/to/cert.txt",
@ -73,12 +79,15 @@ func main() {
Usage: "The domain name resolving to the node's public IPv4 address",
Destination: &options.Dns4DomainName,
},
&cli.StringFlag{
&cli.GenericFlag{
Name: "nodekey",
Usage: "P2P node private key as hex. Can also be set with GOWAKU-NODEKEY env variable (default random)",
Destination: &options.NodeKey,
Value: &cliutils.PrivateKeyValue{
Value: &options.NodeKey,
},
&cli.StringFlag{
EnvVars: []string{"GOWAKU-NODEKEY"},
},
&cli.PathFlag{
Name: "key-file",
Value: "./nodekey",
Usage: "Path to a file containing the private key for the P2P node",
@ -100,14 +109,16 @@ func main() {
Usage: "When generating a keyfile, overwrite the nodekey file if it already exists",
Destination: &options.Overwrite,
},
&cli.StringSliceFlag{
&cli.GenericFlag{
Name: "staticnode",
Usage: "Multiaddr of peer to directly connect with. Option may be repeated",
Destination: &options.StaticNodes,
Value: &cliutils.MultiaddrSlice{
Values: &options.StaticNodes,
},
&cli.IntFlag{
},
&cli.DurationFlag{
Name: "keep-alive",
Value: 20,
Value: 20 * time.Second,
Usage: "Interval in seconds for pinging peers to keep the connection alive.",
Destination: &options.KeepAlive,
},
@ -135,7 +146,7 @@ func main() {
Value: "any",
Destination: &options.NAT, // TODO: accept none,any,upnp,extaddr
},
&cli.StringFlag{
&cli.PathFlag{
Name: "db-path",
Aliases: []string{"dbpath"},
Value: "./store.db",
@ -152,12 +163,14 @@ func main() {
Usage: "Display listening addresses according to current configuration",
Destination: &options.ShowAddresses,
},
&cli.StringFlag{
&cli.GenericFlag{
Name: "log-level",
Aliases: []string{"l"},
Value: "INFO",
Usage: "Define the logging level, supported strings are: DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL, and their lower-case forms.",
Destination: &options.LogLevel,
Value: &cliutils.ChoiceValue{
Choices: []string{"DEBUG", "INFO", "WARN", "ERROR", "DPANIC", "PANIC", "FATAL"},
Value: &options.LogLevel,
},
Usage: "Define the logging level,",
},
&cli.BoolFlag{
Name: "version",
@ -165,11 +178,13 @@ func main() {
Usage: "prints the version",
Destination: &options.Version,
},
&cli.StringFlag{
&cli.GenericFlag{
Name: "log-encoding",
Value: "console",
Usage: "Define the encoding used for the logs: console, json",
Destination: &options.LogEncoding,
Usage: "Define the encoding used for the logs",
Value: &cliutils.ChoiceValue{
Choices: []string{"console", "json"},
Value: &options.LogEncoding,
},
},
&cli.BoolFlag{
Name: "relay",
@ -205,11 +220,11 @@ func main() {
Usage: "Fix the gaps in message history",
Destination: &options.Store.ShouldResume,
},
&cli.IntFlag{
Name: "store-seconds",
Value: (86400 * 30), // 30 days
&cli.DurationFlag{
Name: "store-duration",
Value: time.Hour * 24 * 30,
Usage: "maximum number of seconds before a message is removed from the store",
Destination: &options.Store.RetentionMaxSeconds,
Destination: &options.Store.RetentionTime,
},
&cli.IntFlag{
Name: "store-capacity",
@ -217,10 +232,12 @@ func main() {
Usage: "maximum number of messages to store",
Destination: &options.Store.RetentionMaxMessages,
},
&cli.StringSliceFlag{
&cli.GenericFlag{
Name: "storenode",
Usage: "Multiaddr of a peer that supports store protocol. Option may be repeated",
Destination: &options.Store.Nodes,
Value: &cliutils.MultiaddrSlice{
Values: &options.Store.Nodes,
},
},
&cli.BoolFlag{
Name: "swap",
@ -256,14 +273,16 @@ func main() {
Usage: "Don't accept filter subscribers",
Destination: &options.Filter.DisableFullNode,
},
&cli.StringSliceFlag{
&cli.GenericFlag{
Name: "filternode",
Usage: "Multiaddr of a peer that supports filter protocol. Option may be repeated",
Destination: &options.Filter.Nodes,
Value: &cliutils.MultiaddrSlice{
Values: &options.Filter.Nodes,
},
&cli.IntFlag{
},
&cli.DurationFlag{
Name: "filter-timeout",
Value: 14400,
Value: 14400 * time.Second,
Usage: "Timeout for filter node in seconds",
Destination: &options.Filter.Timeout,
},
@ -272,10 +291,12 @@ func main() {
Usage: "Enable lightpush protocol",
Destination: &options.LightPush.Enable,
},
&cli.StringSliceFlag{
&cli.GenericFlag{
Name: "lightpushnode",
Usage: "Multiaddr of a peer that supports lightpush protocol. Option may be repeated",
Destination: &options.LightPush.Nodes,
Value: &cliutils.MultiaddrSlice{
Values: &options.LightPush.Nodes,
},
},
&cli.BoolFlag{
Name: "discv5-discovery",
@ -303,17 +324,19 @@ func main() {
Usage: "Enable rendezvous protocol for peer discovery",
Destination: &options.Rendezvous.Enable,
},
&cli.StringSliceFlag{
&cli.GenericFlag{
Name: "rendezvous-node",
Usage: "Multiaddr of a waku2 rendezvous node. Option may be repeated",
Destination: &options.Rendezvous.Nodes,
Value: &cliutils.MultiaddrSlice{
Values: &options.Rendezvous.Nodes,
},
},
&cli.BoolFlag{
Name: "rendezvous-server",
Usage: "Node will act as rendezvous server",
Destination: &options.RendezvousServer.Enable,
},
&cli.StringFlag{
&cli.PathFlag{
Name: "rendezvous-db-path",
Value: "/tmp/rendezvous",
Usage: "Path where peer records database will be stored",

75
waku/cliutils/cli.go Normal file
View File

@ -0,0 +1,75 @@
package cliutils
import (
"crypto/ecdsa"
"errors"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
type AddressValue struct {
Value *common.Address
}
func (v *AddressValue) Set(value string) error {
if !common.IsHexAddress(value) {
return errors.New("invalid ethereum address")
}
*v.Value = common.HexToAddress(value)
return nil
}
func (v *AddressValue) String() string {
if v.Value == nil {
return ""
}
return (*v.Value).Hex()
}
type PrivateKeyValue struct {
Value **ecdsa.PrivateKey
}
func (v *PrivateKeyValue) Set(value string) error {
prvKey, err := crypto.ToECDSA(common.FromHex(value))
if err != nil {
return errors.New("invalid private key")
}
*v.Value = prvKey
return nil
}
func (v *PrivateKeyValue) String() string {
if v.Value == nil || *v.Value == nil {
return ""
}
return "0x" + common.Bytes2Hex(crypto.FromECDSA(*v.Value))
}
type ChoiceValue struct {
Choices []string // the choices that this value can take
Value *string // the actual value
}
func (v *ChoiceValue) Set(value string) error {
for _, choice := range v.Choices {
if strings.Compare(choice, value) == 0 {
*v.Value = value
return nil
}
}
return fmt.Errorf("%s is not a valid option. need %+v", value, v.Choices)
}
func (v *ChoiceValue) String() string {
if v.Value == nil {
return ""
}
return *v.Value
}

View File

@ -0,0 +1,56 @@
package cliutils
import (
"strings"
"github.com/multiformats/go-multiaddr"
)
type MultiaddrSlice struct {
Values *[]multiaddr.Multiaddr
}
func (k *MultiaddrSlice) Set(value string) error {
ma, err := multiaddr.NewMultiaddr(value)
if err != nil {
return err
}
*k.Values = append(*k.Values, ma)
return nil
}
func (v *MultiaddrSlice) String() string {
if v.Values == nil {
return ""
}
var output []string
for _, v := range *v.Values {
output = append(output, v.String())
}
return strings.Join(output, ", ")
}
type MultiaddrValue struct {
Value **multiaddr.Multiaddr
}
func (v *MultiaddrValue) Set(value string) error {
ma, err := multiaddr.NewMultiaddr(value)
if err != nil {
return err
}
*v.Value = new(multiaddr.Multiaddr)
**v.Value = ma
return nil
}
func (v *MultiaddrValue) String() string {
if v.Value == nil || *v.Value == nil {
return ""
}
return (**v.Value).String()
}

View File

@ -120,7 +120,7 @@ func Execute(options Options) {
node.WithLogger(logger),
node.WithPrivateKey(prvKey),
node.WithHostAddress(hostAddr),
node.WithKeepAlive(time.Duration(options.KeepAlive) * time.Second),
node.WithKeepAlive(options.KeepAlive),
}
if options.AdvertiseAddress != "" {
@ -198,13 +198,13 @@ func Execute(options Options) {
}
if options.Filter.Enable {
nodeOpts = append(nodeOpts, node.WithWakuFilter(!options.Filter.DisableFullNode, filter.WithTimeout(time.Duration(options.Filter.Timeout)*time.Second)))
nodeOpts = append(nodeOpts, node.WithWakuFilter(!options.Filter.DisableFullNode, filter.WithTimeout(options.Filter.Timeout)))
}
if options.Store.Enable {
if options.Store.PersistMessages {
nodeOpts = append(nodeOpts, node.WithWakuStore(true, options.Store.ShouldResume))
dbStore, err := persistence.NewDBStore(logger, persistence.WithDB(db), persistence.WithRetentionPolicy(options.Store.RetentionMaxMessages, options.Store.RetentionMaxSecondsDuration()))
dbStore, err := persistence.NewDBStore(logger, persistence.WithDB(db), persistence.WithRetentionPolicy(options.Store.RetentionMaxMessages, options.Store.RetentionTime))
failOnErr(err, "DBStore")
nodeOpts = append(nodeOpts, node.WithMessageProvider(dbStore))
} else {
@ -261,10 +261,10 @@ func Execute(options Options) {
failOnErr(err, "Wakunode")
addPeers(wakuNode, options.Rendezvous.Nodes.Value(), string(rendezvous.RendezvousID_v001))
addPeers(wakuNode, options.Store.Nodes.Value(), string(store.StoreID_v20beta4))
addPeers(wakuNode, options.LightPush.Nodes.Value(), string(lightpush.LightPushID_v20beta1))
addPeers(wakuNode, options.Filter.Nodes.Value(), string(filter.FilterID_v20beta1))
addPeers(wakuNode, options.Rendezvous.Nodes, string(rendezvous.RendezvousID_v001))
addPeers(wakuNode, options.Store.Nodes, string(store.StoreID_v20beta4))
addPeers(wakuNode, options.LightPush.Nodes, string(lightpush.LightPushID_v20beta1))
addPeers(wakuNode, options.Filter.Nodes, string(filter.FilterID_v20beta1))
if err = wakuNode.Start(); err != nil {
logger.Fatal("starting waku node", zap.Error(err))
@ -289,9 +289,9 @@ func Execute(options Options) {
}
}
for _, n := range options.StaticNodes.Value() {
go func(node string) {
err = wakuNode.DialPeer(ctx, node)
for _, n := range options.StaticNodes {
go func(node multiaddr.Multiaddr) {
err = wakuNode.DialPeerWithMultiAddress(ctx, node)
if err != nil {
logger.Error("dialing peer", zap.Error(err))
}
@ -359,16 +359,9 @@ func Execute(options Options) {
}
}
func addPeers(wakuNode *node.WakuNode, addresses []string, protocols ...string) {
for _, addrString := range addresses {
if addrString == "" {
continue
}
addr, err := multiaddr.NewMultiaddr(addrString)
failOnErr(err, "invalid multiaddress")
_, err = wakuNode.AddPeer(addr, protocols...)
func addPeers(wakuNode *node.WakuNode, addresses []multiaddr.Multiaddr, protocols ...string) {
for _, addr := range addresses {
_, err := wakuNode.AddPeer(addr, protocols...)
failOnErr(err, "error adding peer")
}
}
@ -442,11 +435,11 @@ func writePrivateKeyToFile(path string, passwd []byte, overwrite bool) error {
func getPrivKey(options Options) (*ecdsa.PrivateKey, error) {
var prvKey *ecdsa.PrivateKey
var err error
if options.NodeKey != "" {
if prvKey, err = crypto.ToECDSA(common.FromHex(options.NodeKey)); err != nil {
return nil, fmt.Errorf("error converting key into valid ecdsa key: %w", err)
}
if options.NodeKey != nil {
prvKey = options.NodeKey
} else {
// TODO: once https://github.com/urfave/cli/issues/1272 is fixed, remove env variable logic
keyString := os.Getenv("GOWAKU-NODEKEY")
if keyString != "" {
if prvKey, err = crypto.ToECDSA(common.FromHex(keyString)); err != nil {

View File

@ -6,8 +6,6 @@ package waku
import (
"crypto/ecdsa"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/go-rln/rln"
"github.com/status-im/go-waku/waku/v2/node"
)
@ -24,10 +22,8 @@ func checkForRLN(options Options, nodeOpts *[]node.WakuNodeOption) {
} else {
var ethPrivKey *ecdsa.PrivateKey
if options.RLNRelay.ETHPrivateKey != "" {
k, err := crypto.ToECDSA(common.FromHex(options.RLNRelay.ETHPrivateKey))
failOnErr(err, "Invalid private key")
ethPrivKey = k
if options.RLNRelay.ETHPrivateKey != nil {
ethPrivKey = options.RLNRelay.ETHPrivateKey
}
loaded, idKey, idCommitment, membershipIndex, err := getMembershipCredentials(options)
@ -44,7 +40,7 @@ func checkForRLN(options Options, nodeOpts *[]node.WakuNodeOption) {
nil,
options.RLNRelay.ETHClientAddress,
ethPrivKey,
common.HexToAddress(options.RLNRelay.MembershipContractAddress),
options.RLNRelay.MembershipContractAddress,
))
}
}

View File

@ -1,8 +1,11 @@
package waku
import (
"crypto/ecdsa"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/multiformats/go-multiaddr"
"github.com/urfave/cli/v2"
)
@ -10,7 +13,7 @@ import (
// discovering new nodes
type RendezvousOptions struct {
Enable bool
Nodes cli.StringSlice
Nodes []multiaddr.Multiaddr
}
// RendezvousServerOptions are settings to enable the waku node to act as a
@ -48,9 +51,9 @@ type RLNRelayOptions struct {
Dynamic bool
IDKey string
IDCommitment string
ETHPrivateKey string
ETHPrivateKey *ecdsa.PrivateKey
ETHClientAddress string
MembershipContractAddress string
MembershipContractAddress common.Address
}
// FilterOptions are settings used to enable filter protocol. This is a protocol
@ -60,8 +63,8 @@ type RLNRelayOptions struct {
type FilterOptions struct {
Enable bool
DisableFullNode bool
Nodes cli.StringSlice
Timeout int
Nodes []multiaddr.Multiaddr
Timeout time.Duration
}
// LightpushOptions are settings used to enable the lightpush protocol. This is
@ -72,7 +75,7 @@ type FilterOptions struct {
// broadcasted
type LightpushOptions struct {
Enable bool
Nodes cli.StringSlice
Nodes []multiaddr.Multiaddr
}
// StoreOptions are settings used for enabling the store protocol, used to
@ -82,9 +85,9 @@ type StoreOptions struct {
Enable bool
PersistMessages bool
ShouldResume bool
RetentionMaxSeconds int
RetentionTime time.Duration
RetentionMaxMessages int
Nodes cli.StringSlice
Nodes []multiaddr.Multiaddr
}
// SwapOptions are settings used for configuring the swap protocol
@ -95,10 +98,6 @@ type SwapOptions struct {
DisconnectThreshold int
}
func (s *StoreOptions) RetentionMaxSecondsDuration() time.Duration {
return time.Duration(s.RetentionMaxSeconds) * time.Second
}
// DNSDiscoveryOptions are settings used for enabling DNS-based discovery
// protocol that stores merkle trees in DNS records which contain connection
// information for nodes. It's very useful for bootstrapping a p2p network.
@ -152,17 +151,17 @@ type Options struct {
Port int
Address string
Dns4DomainName string
NodeKey string
NodeKey *ecdsa.PrivateKey
KeyFile string
KeyPasswd string
GenerateKey bool
Overwrite bool
StaticNodes cli.StringSlice
KeepAlive int
StaticNodes []multiaddr.Multiaddr
KeepAlive time.Duration
UseDB bool
DBPath string
AdvertiseAddress string
Version bool
Version bool // TODO: use vflag from urcli
ShowAddresses bool
LogLevel string
LogEncoding string

View File

@ -28,9 +28,9 @@ func WithPeer(p peer.ID) LightPushOption {
// WithAutomaticPeerSelection is an option used to randomly select a peer from the peer store
// to push a waku message to
func WithAutomaticPeerSelection(host host.Host) LightPushOption {
func WithAutomaticPeerSelection() LightPushOption {
return func(params *LightPushParameters) {
p, err := utils.SelectPeer(host, string(LightPushID_v20beta1), params.log)
p, err := utils.SelectPeer(params.host, string(LightPushID_v20beta1), params.log)
if err == nil {
params.selectedPeer = *p
} else {
@ -72,6 +72,6 @@ func WithAutomaticRequestId() LightPushOption {
func DefaultOptions(host host.Host) []LightPushOption {
return []LightPushOption{
WithAutomaticRequestId(),
WithAutomaticPeerSelection(host),
WithAutomaticPeerSelection(),
}
}

View File

@ -19,7 +19,7 @@ func TestLightPushOption(t *testing.T) {
options := []LightPushOption{
WithPeer("QmWLxGxG65CZ7vRj5oNXCJvbY9WkF9d9FxuJg8cg8Y7q3"),
WithAutomaticPeerSelection(host),
WithAutomaticPeerSelection(),
WithFastestPeerSelection(context.Background()),
WithRequestId([]byte("requestId")),
WithAutomaticRequestId(),

View File

@ -144,7 +144,7 @@ func (rln *WakuRLNRelay) HandleGroupUpdates(handler RegistrationEventHandler) er
func (rln *WakuRLNRelay) loadOldEvents(rlnContract *contracts.RLN, handler RegistrationEventHandler) error {
// Get old events should
logIterator, err := rlnContract.FilterMemberRegistered(&bind.FilterOpts{Start: 0, End: nil, Context: rln.ctx}, []*big.Int{}, []*big.Int{})
logIterator, err := rlnContract.FilterMemberRegistered(&bind.FilterOpts{Start: 0, End: nil, Context: rln.ctx})
if err != nil {
return err
}
@ -160,7 +160,7 @@ func (rln *WakuRLNRelay) loadOldEvents(rlnContract *contracts.RLN, handler Regis
func (rln *WakuRLNRelay) watchNewEvents(rlnContract *contracts.RLN, handler RegistrationEventHandler, log *zap.Logger) error {
// Watch for new events
logSink := make(chan *contracts.RLNMemberRegistered)
subs, err := rlnContract.WatchMemberRegistered(&bind.WatchOpts{Context: rln.ctx, Start: nil}, logSink, []*big.Int{}, []*big.Int{})
subs, err := rlnContract.WatchMemberRegistered(&bind.WatchOpts{Context: rln.ctx, Start: nil}, logSink)
if err != nil {
return err
}

View File

@ -3,7 +3,10 @@
package main
import "github.com/urfave/cli/v2"
import (
wcli "github.com/status-im/go-waku/waku/cliutils"
"github.com/urfave/cli/v2"
)
func rlnFlags() []cli.Flag {
return []cli.Flag{
@ -46,7 +49,7 @@ func rlnFlags() []cli.Flag {
Usage: "Rln relay identity commitment key as a Hex string",
Destination: &options.RLNRelay.IDCommitment,
},
&cli.StringFlag{
&cli.PathFlag{
Name: "rln-relay-membership-credentials-file",
Usage: "RLN relay membership credentials file",
Value: "rlnCredentials.txt",
@ -54,10 +57,12 @@ func rlnFlags() []cli.Flag {
},
// TODO: this is a good candidate option for subcommands
// TODO: consider accepting a private key file and passwd
&cli.StringFlag{
&cli.GenericFlag{
Name: "eth-private-key",
Usage: "Ethereum Goerli testnet account private key used for registering in member contract",
Destination: &options.RLNRelay.ETHPrivateKey,
Usage: "Ethereum account private key used for registering in member contract",
Value: &wcli.PrivateKeyValue{
Value: &options.RLNRelay.ETHPrivateKey,
},
},
&cli.StringFlag{
Name: "eth-client-address",
@ -65,10 +70,12 @@ func rlnFlags() []cli.Flag {
Value: "ws://localhost:8545",
Destination: &options.RLNRelay.ETHClientAddress,
},
&cli.StringFlag{
&cli.GenericFlag{
Name: "eth-mem-contract-address",
Usage: "Address of membership contract on an Ethereum testnet",
Destination: &options.RLNRelay.MembershipContractAddress,
Usage: "Address of membership contract ",
Value: &wcli.AddressValue{
Value: &options.RLNRelay.MembershipContractAddress,
},
},
}
}