diff --git a/.codeclimate.yml b/.codeclimate.yml index a81ce5906..b17126039 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,16 +1,16 @@ plugins: - golint: - enabled: true +# golint: +# enabled: true gofmt: enabled: true govet: enabled: true - # golangci-lint: - #enabled: true +# golangci-lint: +# enabled: true exclude_patterns: - "." - "**/*.pb.go" - "**/rln/contracts/*.go" - "**/bindata.go" - "./examples/waku-csharp" - - "./examples/swift-waku" \ No newline at end of file + - "./examples/swift-waku" diff --git a/cmd/waku/flags_rln.go b/cmd/waku/flags_rln.go index fb0e16f22..3d73d6e1b 100644 --- a/cmd/waku/flags_rln.go +++ b/cmd/waku/flags_rln.go @@ -17,11 +17,12 @@ func rlnFlags() []cli.Flag { Usage: "Enable spam protection through rln-relay", Destination: &options.RLNRelay.Enable, }, - &cli.UintFlag{ - Name: "rln-relay-membership-index", - Value: 0, - Usage: "the index of credentials to use", - Destination: &options.RLNRelay.MembershipIndex, + &cli.GenericFlag{ + Name: "rln-relay-cred-index", + Usage: "the index of the onchain commitment to use", + Value: &wcli.OptionalUint{ + Value: &options.RLNRelay.MembershipIndex, + }, }, &cli.BoolFlag{ Name: "rln-relay-dynamic", diff --git a/cmd/waku/node_rln.go b/cmd/waku/node_rln.go index 42b5dd4a0..72192258c 100644 --- a/cmd/waku/node_rln.go +++ b/cmd/waku/node_rln.go @@ -17,7 +17,7 @@ func checkForRLN(logger *zap.Logger, options NodeOptions, nodeOpts *[]node.WakuN failOnErr(errors.New("relay not available"), "Could not enable RLN Relay") } if !options.RLNRelay.Dynamic { - *nodeOpts = append(*nodeOpts, node.WithStaticRLNRelay(rln.MembershipIndex(options.RLNRelay.MembershipIndex), nil)) + *nodeOpts = append(*nodeOpts, node.WithStaticRLNRelay((*rln.MembershipIndex)(options.RLNRelay.MembershipIndex), nil)) } else { // TODO: too many parameters in this function // consider passing a config struct instead @@ -26,7 +26,7 @@ func checkForRLN(logger *zap.Logger, options NodeOptions, nodeOpts *[]node.WakuN options.RLNRelay.CredentialsPassword, options.RLNRelay.TreePath, options.RLNRelay.MembershipContractAddress, - rln.MembershipIndex(options.RLNRelay.MembershipIndex), + options.RLNRelay.MembershipIndex, nil, options.RLNRelay.ETHClientAddress, )) diff --git a/cmd/waku/options.go b/cmd/waku/options.go index 1d35a5a0e..a4e73e7ef 100644 --- a/cmd/waku/options.go +++ b/cmd/waku/options.go @@ -40,7 +40,7 @@ type RLNRelayOptions struct { CredentialsPath string CredentialsPassword string TreePath string - MembershipIndex uint + MembershipIndex *uint Dynamic bool ETHClientAddress string MembershipContractAddress common.Address diff --git a/examples/chat2/exec.go b/examples/chat2/exec.go index 0898f4378..08e04eddd 100644 --- a/examples/chat2/exec.go +++ b/examples/chat2/exec.go @@ -54,13 +54,13 @@ func execute(options Options) { options.RLNRelay.CredentialsPassword, "", // Will use default tree path options.RLNRelay.MembershipContractAddress, - uint(options.RLNRelay.MembershipIndex), + options.RLNRelay.MembershipIndex, spamHandler, options.RLNRelay.ETHClientAddress, )) } else { opts = append(opts, node.WithStaticRLNRelay( - uint(options.RLNRelay.MembershipIndex), + options.RLNRelay.MembershipIndex, spamHandler)) } } diff --git a/examples/chat2/flags.go b/examples/chat2/flags.go index 725f30a87..514ae64dc 100644 --- a/examples/chat2/flags.go +++ b/examples/chat2/flags.go @@ -36,8 +36,7 @@ func getFlags() []cli.Flag { testCT, err := protocol.NewContentTopic("toy-chat", 3, "mingde", "proto") if err != nil { - fmt.Println("Invalid contentTopic") - return nil + panic("invalid contentTopic") } testnetContentTopic := testCT.String() @@ -190,11 +189,12 @@ func getFlags() []cli.Flag { Usage: "Enable spam protection through rln-relay", Destination: &options.RLNRelay.Enable, }, - &cli.UintFlag{ - Name: "rln-relay-membership-index", - Value: 0, - Usage: "the index of credentials to use", - Destination: &options.RLNRelay.MembershipIndex, + &cli.GenericFlag{ + Name: "rln-relay-cred-index", + Usage: "the index of the onchain commitment to use", + Value: &wcli.OptionalUint{ + Value: &options.RLNRelay.MembershipIndex, + }, }, &cli.BoolFlag{ Name: "rln-relay-dynamic", diff --git a/examples/chat2/options.go b/examples/chat2/options.go index 766e692a0..7af0c7938 100644 --- a/examples/chat2/options.go +++ b/examples/chat2/options.go @@ -31,7 +31,7 @@ type RLNRelayOptions struct { Enable bool CredentialsPath string CredentialsPassword string - MembershipIndex uint + MembershipIndex *uint Dynamic bool ETHClientAddress string MembershipContractAddress common.Address diff --git a/waku/cliutils/cli.go b/waku/cliutils/cli.go index 6ef2f89f7..c45a0fdd8 100644 --- a/waku/cliutils/cli.go +++ b/waku/cliutils/cli.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "errors" "fmt" + "strconv" "strings" "github.com/ethereum/go-ethereum/common" @@ -73,3 +74,33 @@ func (v *ChoiceValue) String() string { } return *v.Value } + +// OptionalUint represents a urfave/cli flag to store uint values that can be +// optionally set and not have any default value assigned to it +type OptionalUint struct { + Value **uint +} + +// Set assigns a value to the flag only if it represents a valid uint value +func (v *OptionalUint) Set(value string) error { + if value != "" { + uintVal, err := strconv.ParseUint(value, 10, 0) + if err != nil { + return err + } + uVal := uint(uintVal) + *v.Value = &uVal + } else { + v.Value = nil + } + + return nil +} + +// String returns the string representation of the OptionalUint flag, if set +func (v *OptionalUint) String() string { + if v.Value == nil { + return "" + } + return fmt.Sprintf("%d", *v.Value) +} diff --git a/waku/v2/node/wakunode2_rln.go b/waku/v2/node/wakunode2_rln.go index 7ea5c18d5..5b637478d 100644 --- a/waku/v2/node/wakunode2_rln.go +++ b/waku/v2/node/wakunode2_rln.go @@ -38,14 +38,18 @@ func (w *WakuNode) setupRLNRelay() error { if !w.opts.rlnRelayDynamic { w.log.Info("setting up waku-rln-relay in off-chain mode") + index := uint(0) + if w.opts.rlnRelayMemIndex != nil { + index = *w.opts.rlnRelayMemIndex + } + // set up rln relay inputs - groupKeys, idCredential, err := static.Setup(w.opts.rlnRelayMemIndex) + groupKeys, idCredential, err := static.Setup(index) if err != nil { return err } - groupManager, err = static.NewStaticGroupManager(groupKeys, idCredential, w.opts.rlnRelayMemIndex, rlnInstance, - rootTracker, w.log) + groupManager, err = static.NewStaticGroupManager(groupKeys, idCredential, index, rlnInstance, rootTracker, w.log) if err != nil { return err } diff --git a/waku/v2/node/wakuoptions.go b/waku/v2/node/wakuoptions.go index b142a70dc..57829670b 100644 --- a/waku/v2/node/wakuoptions.go +++ b/waku/v2/node/wakuoptions.go @@ -94,7 +94,7 @@ type WakuNodeParameters struct { enablePeerExchange bool enableRLN bool - rlnRelayMemIndex uint + rlnRelayMemIndex *uint rlnRelayDynamic bool rlnSpamHandler func(message *pb.WakuMessage) error rlnETHClientAddress string diff --git a/waku/v2/node/wakuoptions_rln.go b/waku/v2/node/wakuoptions_rln.go index 1bd1735ab..0f2df3e60 100644 --- a/waku/v2/node/wakuoptions_rln.go +++ b/waku/v2/node/wakuoptions_rln.go @@ -11,7 +11,7 @@ import ( // WithStaticRLNRelay enables the Waku V2 RLN protocol in offchain mode // Requires the `gowaku_rln` build constrain (or the env variable RLN=true if building go-waku) -func WithStaticRLNRelay(memberIndex r.MembershipIndex, spamHandler rln.SpamHandler) WakuNodeOption { +func WithStaticRLNRelay(memberIndex *r.MembershipIndex, spamHandler rln.SpamHandler) WakuNodeOption { return func(params *WakuNodeParameters) error { params.enableRLN = true params.rlnRelayDynamic = false @@ -23,7 +23,7 @@ func WithStaticRLNRelay(memberIndex r.MembershipIndex, spamHandler rln.SpamHandl // WithDynamicRLNRelay enables the Waku V2 RLN protocol in onchain mode. // Requires the `gowaku_rln` build constrain (or the env variable RLN=true if building go-waku) -func WithDynamicRLNRelay(keystorePath string, keystorePassword string, treePath string, membershipContract common.Address, membershipIndex uint, spamHandler rln.SpamHandler, ethClientAddress string) WakuNodeOption { +func WithDynamicRLNRelay(keystorePath string, keystorePassword string, treePath string, membershipContract common.Address, membershipIndex *uint, spamHandler rln.SpamHandler, ethClientAddress string) WakuNodeOption { return func(params *WakuNodeParameters) error { params.enableRLN = true params.rlnRelayDynamic = true diff --git a/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go b/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go index 302115ec4..be2db0426 100644 --- a/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go +++ b/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go @@ -37,8 +37,9 @@ type DynamicGroupManager struct { lastBlockProcessed uint64 - appKeystore *keystore.AppKeystore - keystorePassword string + appKeystore *keystore.AppKeystore + keystorePassword string + membershipIndexToLoad *uint } func (gm *DynamicGroupManager) handler(events []*contracts.RLNMemberRegistered) error { @@ -104,7 +105,7 @@ type RegistrationHandler = func(tx *types.Transaction) func NewDynamicGroupManager( ethClientAddr string, memContractAddr common.Address, - membershipIndex uint, + membershipIndexToLoad *uint, appKeystore *keystore.AppKeystore, keystorePassword string, reg prometheus.Registerer, @@ -116,11 +117,11 @@ func NewDynamicGroupManager( web3Config := web3.NewConfig(ethClientAddr, memContractAddr) return &DynamicGroupManager{ - membershipIndex: membershipIndex, - appKeystore: appKeystore, - keystorePassword: keystorePassword, - MembershipFetcher: NewMembershipFetcher(web3Config, rlnInstance, rootTracker, log), - metrics: newMetrics(reg), + membershipIndexToLoad: membershipIndexToLoad, + appKeystore: appKeystore, + keystorePassword: keystorePassword, + MembershipFetcher: NewMembershipFetcher(web3Config, rlnInstance, rootTracker, log), + metrics: newMetrics(reg), }, nil } @@ -170,7 +171,7 @@ func (gm *DynamicGroupManager) loadCredential(ctx context.Context) error { credentials, err := gm.appKeystore.GetMembershipCredentials( gm.keystorePassword, - gm.membershipIndex, + gm.membershipIndexToLoad, keystore.NewMembershipContractInfo(gm.web3Config.ChainID, gm.web3Config.RegistryContract.Address)) if err != nil { return err @@ -191,6 +192,7 @@ func (gm *DynamicGroupManager) loadCredential(ctx context.Context) error { } gm.identityCredential = credentials.IdentityCredential + gm.membershipIndex = credentials.TreeIndex return nil } diff --git a/waku/v2/protocol/rln/keystore/keystore.go b/waku/v2/protocol/rln/keystore/keystore.go index 385102615..0b1e24267 100644 --- a/waku/v2/protocol/rln/keystore/keystore.go +++ b/waku/v2/protocol/rln/keystore/keystore.go @@ -81,10 +81,31 @@ func getKey(treeIndex rln.MembershipIndex, filterMembershipContract MembershipCo } // GetMembershipCredentials decrypts and retrieves membership credentials from the keystore applying filters -func (k *AppKeystore) GetMembershipCredentials(keystorePassword string, treeIndex rln.MembershipIndex, filterMembershipContract MembershipContractInfo) (*MembershipCredentials, error) { - key, err := getKey(treeIndex, filterMembershipContract) - if err != nil { - return nil, err +func (k *AppKeystore) GetMembershipCredentials(keystorePassword string, index *rln.MembershipIndex, filterMembershipContract MembershipContractInfo) (*MembershipCredentials, error) { + // If there is only one, and index to laod nil, assume 0, + // if there is more than one, complain if the index to load is nil + + var key Key + var err error + if len(k.Credentials) == 1 { + // Only one credential, the tree index does not matter. + k.logger.Warn("automatically loading the only credential found on the keystore") + for k := range k.Credentials { + key = k // Obtain the first c + break + } + } else { + treeIndex := uint(0) + if index != nil { + treeIndex = *index + } else { + return nil, errors.New("the index of the onchain commitment to use was not specified") + } + + key, err = getKey(treeIndex, filterMembershipContract) + if err != nil { + return nil, err + } } credential, ok := k.Credentials[key] @@ -108,7 +129,7 @@ func (k *AppKeystore) GetMembershipCredentials(keystorePassword string, treeInde // AddMembershipCredentials inserts a membership credential to the keystore matching the application, appIdentifier and version filters. func (k *AppKeystore) AddMembershipCredentials(newCredential MembershipCredentials, password string) error { - credentials, err := k.GetMembershipCredentials(password, newCredential.TreeIndex, newCredential.MembershipContractInfo) + credentials, err := k.GetMembershipCredentials(password, &newCredential.TreeIndex, newCredential.MembershipContractInfo) if err != nil { return err } @@ -118,7 +139,7 @@ func (k *AppKeystore) AddMembershipCredentials(newCredential MembershipCredentia return err } - if credentials != nil { + if credentials != nil && credentials.TreeIndex == newCredential.TreeIndex && credentials.MembershipContractInfo.Equals(newCredential.MembershipContractInfo) { return errors.New("credential already present") } diff --git a/waku/v2/protocol/rln/onchain_test.go b/waku/v2/protocol/rln/onchain_test.go index 41299a9b7..3eaf057e5 100644 --- a/waku/v2/protocol/rln/onchain_test.go +++ b/waku/v2/protocol/rln/onchain_test.go @@ -149,7 +149,7 @@ func (s *WakuRLNRelayDynamicSuite) TestDynamicGroupManagement() { membershipIndex := s.register(appKeystore, u1Credentials, s.u1PrivKey) - gm, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, rlnInstance, rt, utils.Logger()) + gm, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, &membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, rlnInstance, rt, utils.Logger()) s.Require().NoError(err) // initialize the WakuRLNRelay @@ -236,7 +236,7 @@ func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() { rlnInstance, rootTracker, err := GetRLNInstanceAndRootTracker(s.tmpRLNDBPath()) s.Require().NoError(err) // mount the rln relay protocol in the on-chain/dynamic mode - gm, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, rlnInstance, rootTracker, utils.Logger()) + gm, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, &membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, rlnInstance, rootTracker, utils.Logger()) s.Require().NoError(err) rlnRelay := New(group_manager.Details{ @@ -275,7 +275,7 @@ func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() { // mount the rln relay protocol in the on-chain/dynamic mode rootInstance, rootTracker, err := GetRLNInstanceAndRootTracker(s.tmpRLNDBPath()) s.Require().NoError(err) - gm1, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipGroupIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, rootInstance, rootTracker, utils.Logger()) + gm1, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, &membershipGroupIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, rootInstance, rootTracker, utils.Logger()) s.Require().NoError(err) rlnRelay1 := New(group_manager.Details{ @@ -299,7 +299,7 @@ func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() { // mount the rln relay protocol in the on-chain/dynamic mode rootInstance, rootTracker, err = GetRLNInstanceAndRootTracker(s.tmpRLNDBPath()) s.Require().NoError(err) - gm2, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipGroupIndex, appKeystore2, keystorePassword, prometheus.DefaultRegisterer, rootInstance, rootTracker, utils.Logger()) + gm2, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, &membershipGroupIndex, appKeystore2, keystorePassword, prometheus.DefaultRegisterer, rootInstance, rootTracker, utils.Logger()) s.Require().NoError(err) rlnRelay2 := New(group_manager.Details{