From 3716ebdf1d26d9b71340e2d01201b0c4e2af8b71 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Sun, 11 Sep 2022 17:08:58 -0400 Subject: [PATCH] fix: chat2 feedback --- examples/chat2/chat.go | 3 +++ examples/chat2/exec.go | 10 ++++++++-- examples/chat2/flags.go | 8 ++++---- examples/chat2/options.go | 2 +- examples/chat2/rln-credentials.go | 10 ++++++++-- waku/node.go | 2 +- waku/node_no_rln.go | 7 +++++-- waku/node_rln.go | 11 +++++++---- waku/options.go | 2 +- waku/rln-credentials.go | 18 +++++++++++++----- waku/v2/node/wakunode2_rln.go | 5 +---- waku/v2/node/wakuoptions.go | 2 ++ waku/v2/node/wakuoptions_rln.go | 3 ++- waku/v2/protocol/rln/rln_relay_builder.go | 2 ++ waku/v2/protocol/rln/waku_rln_relay.go | 6 +++++- waku/v2/protocol/rln/web3.go | 18 ++++++++++++++---- waku/v2/rest/relay_api.yaml | 1 + waku_rln.go | 2 +- 18 files changed, 79 insertions(+), 33 deletions(-) diff --git a/examples/chat2/chat.go b/examples/chat2/chat.go index e3d9ebee..eb3d3269 100644 --- a/examples/chat2/chat.go +++ b/examples/chat2/chat.go @@ -240,6 +240,9 @@ func (c *Chat) SendMessage(line string) { err := c.publish(tCtx, line) if err != nil { + if err.Error() == "validation failed" { + err = errors.New("message rate violation!") + } c.ui.ErrorMessage(err) } } diff --git a/examples/chat2/exec.go b/examples/chat2/exec.go index 10bc4242..b4bed9dd 100644 --- a/examples/chat2/exec.go +++ b/examples/chat2/exec.go @@ -6,6 +6,7 @@ import ( "net" tea "github.com/charmbracelet/bubbletea" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/multiformats/go-multiaddr" "github.com/status-im/go-waku/waku/v2/node" @@ -42,8 +43,12 @@ func execute(options Options) { return nil } + registrationHandler := func(tx *types.Transaction) { + fmt.Println(fmt.Sprintf("You are registered to the rln membership contract, find details of your registration transaction in https://goerli.etherscan.io/tx/%s", tx.Hash())) + } + if options.RLNRelay.Dynamic { - idKey, idCommitment, index, err := getMembershipCredentials(options.RLNRelay.CredentialsFile, options.RLNRelay.IDKey, options.RLNRelay.IDCommitment, options.RLNRelay.MembershipIndex) + idKey, idCommitment, index, err := getMembershipCredentials(options.RLNRelay.CredentialsPath, options.RLNRelay.IDKey, options.RLNRelay.IDCommitment, options.RLNRelay.MembershipIndex) if err != nil { fmt.Println(err) return @@ -60,6 +65,7 @@ func execute(options Options) { options.RLNRelay.ETHClientAddress, options.RLNRelay.ETHPrivateKey, options.RLNRelay.MembershipContractAddress, + registrationHandler, )) } else { opts = append(opts, node.WithStaticRLNRelay( @@ -113,7 +119,7 @@ func execute(options Options) { if options.RLNRelay.Enable && options.RLNRelay.Dynamic { if options.RLNRelay.IDKey == "" && options.RLNRelay.IDCommitment == "" { // Write membership credentials file only if the idkey and commitment are not specified - err := writeRLNMembershipCredentialsToFile(options.RLNRelay.CredentialsFile, wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex()) + err := writeRLNMembershipCredentialsToFile(options.RLNRelay.CredentialsPath, wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex()) if err != nil { fmt.Println(err.Error()) return diff --git a/examples/chat2/flags.go b/examples/chat2/flags.go index fbc55a48..b00e4ca7 100644 --- a/examples/chat2/flags.go +++ b/examples/chat2/flags.go @@ -214,10 +214,10 @@ func getFlags() []cli.Flag { Destination: &options.RLNRelay.IDCommitment, }, &cli.PathFlag{ - Name: "rln-relay-membership-credentials-file", - Usage: "RLN relay membership credentials file", - Value: "rlnCredentials.txt", - Destination: &options.RLNRelay.CredentialsFile, + Name: "rln-relay-cred-path", + Usage: "The path for persisting rln-relay credential", + Value: "", + Destination: &options.RLNRelay.CredentialsPath, }, // TODO: this is a good candidate option for subcommands // TODO: consider accepting a private key file and passwd diff --git a/examples/chat2/options.go b/examples/chat2/options.go index 0c46a815..302140ac 100644 --- a/examples/chat2/options.go +++ b/examples/chat2/options.go @@ -29,7 +29,7 @@ type RelayOptions struct { type RLNRelayOptions struct { Enable bool - CredentialsFile string + CredentialsPath string MembershipIndex int PubsubTopic string ContentTopic string diff --git a/examples/chat2/rln-credentials.go b/examples/chat2/rln-credentials.go index 14195f8f..2a37f89f 100644 --- a/examples/chat2/rln-credentials.go +++ b/examples/chat2/rln-credentials.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "github.com/ethereum/go-ethereum/common" "github.com/status-im/go-rln/rln" @@ -21,6 +22,8 @@ type membershipCredentials struct { Index rln.MembershipIndex `json:"rlnIndex"` } +const RLN_CREDENTIALS_FILENAME = "rlnCredentials.txt" + func fileExists(path string) bool { if _, err := os.Stat(path); err == nil { return false @@ -34,6 +37,8 @@ func fileExists(path string) bool { } func writeRLNMembershipCredentialsToFile(path string, keyPair rln.MembershipKeyPair, idx rln.MembershipIndex) error { + path = filepath.Join(path, RLN_CREDENTIALS_FILENAME) + if fileExists(path) { return nil } @@ -49,8 +54,8 @@ func writeRLNMembershipCredentialsToFile(path string, keyPair rln.MembershipKeyP return ioutil.WriteFile(path, credentialsJSON, 0600) } -func loadMembershipCredentialsFromFile(path string) (rln.MembershipKeyPair, rln.MembershipIndex, error) { - src, err := ioutil.ReadFile(path) +func loadMembershipCredentialsFromFile(rlnCredentialsPath string) (rln.MembershipKeyPair, rln.MembershipIndex, error) { + src, err := ioutil.ReadFile(rlnCredentialsPath) if err != nil { return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err } @@ -72,6 +77,7 @@ func getMembershipCredentials(path string, rlnIDKey string, rlnIDCommitment stri var osErr error if !valuesWereInput { + path = filepath.Join(path, RLN_CREDENTIALS_FILENAME) if _, osErr = os.Stat(path); osErr == nil { if keyPair, index, err := loadMembershipCredentialsFromFile(path); err != nil { return nil, nil, rln.MembershipIndex(0), fmt.Errorf("could not read membership credentials file: %w", err) diff --git a/waku/node.go b/waku/node.go index 63200c1a..7a0286f9 100644 --- a/waku/node.go +++ b/waku/node.go @@ -263,7 +263,7 @@ func Execute(options Options) { nodeOpts = append(nodeOpts, node.WithDiscoveryV5(options.DiscV5.Port, bootnodes, options.DiscV5.AutoUpdate, pubsub.WithDiscoveryOpts(discovery.Limit(45), discovery.TTL(time.Duration(20)*time.Second)))) } - checkForRLN(options, &nodeOpts) + checkForRLN(logger, options, &nodeOpts) wakuNode, err := node.New(ctx, nodeOpts...) diff --git a/waku/node_no_rln.go b/waku/node_no_rln.go index 0651f702..265a63ea 100644 --- a/waku/node_no_rln.go +++ b/waku/node_no_rln.go @@ -3,9 +3,12 @@ package waku -import "github.com/status-im/go-waku/waku/v2/node" +import ( + "github.com/status-im/go-waku/waku/v2/node" + "go.uber.org/zap" +) -func checkForRLN(options Options, nodeOpts *[]node.WakuNodeOption) { +func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeOption) { // Do nothing } diff --git a/waku/node_rln.go b/waku/node_rln.go index 60340b68..c956ac30 100644 --- a/waku/node_rln.go +++ b/waku/node_rln.go @@ -6,13 +6,15 @@ package waku import ( "crypto/ecdsa" "errors" + "github.com/status-im/go-rln/rln" "github.com/status-im/go-waku/waku/v2/node" + "go.uber.org/zap" ) var loadedCredentialsFromFile bool = false -func checkForRLN(options Options, nodeOpts *[]node.WakuNodeOption) { +func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeOption) { if options.RLNRelay.Enable { if !options.Relay.Enable { failOnErr(errors.New("relay not available"), "Could not enable RLN Relay") @@ -26,7 +28,7 @@ func checkForRLN(options Options, nodeOpts *[]node.WakuNodeOption) { ethPrivKey = options.RLNRelay.ETHPrivateKey } - loaded, idKey, idCommitment, membershipIndex, err := getMembershipCredentials(options) + loaded, idKey, idCommitment, membershipIndex, err := getMembershipCredentials(logger, options) failOnErr(err, "Invalid membership credentials") loadedCredentialsFromFile = loaded @@ -41,14 +43,15 @@ func checkForRLN(options Options, nodeOpts *[]node.WakuNodeOption) { options.RLNRelay.ETHClientAddress, ethPrivKey, options.RLNRelay.MembershipContractAddress, + nil, )) } } } func onStartRLN(wakuNode *node.WakuNode, options Options) { - if options.RLNRelay.Enable && options.RLNRelay.Dynamic && !loadedCredentialsFromFile { - err := writeRLNMembershipCredentialsToFile(wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex(), options.RLNRelay.CredentialsFile, []byte(options.KeyPasswd), options.Overwrite) + if options.RLNRelay.Enable && options.RLNRelay.Dynamic && !loadedCredentialsFromFile && options.RLNRelay.CredentialsPath != "" { + err := writeRLNMembershipCredentialsToFile(wakuNode.RLNRelay().MembershipKeyPair(), wakuNode.RLNRelay().MembershipIndex(), options.RLNRelay.CredentialsPath, []byte(options.KeyPasswd), options.Overwrite) failOnErr(err, "Could not write membership credentials file") } } diff --git a/waku/options.go b/waku/options.go index 4dc87e4f..3f861679 100644 --- a/waku/options.go +++ b/waku/options.go @@ -44,7 +44,7 @@ type RelayOptions struct { type RLNRelayOptions struct { Enable bool - CredentialsFile string + CredentialsPath string MembershipIndex int PubsubTopic string ContentTopic string diff --git a/waku/rln-credentials.go b/waku/rln-credentials.go index 62d53d0d..ad556175 100644 --- a/waku/rln-credentials.go +++ b/waku/rln-credentials.go @@ -8,10 +8,12 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/status-im/go-rln/rln" + "go.uber.org/zap" ) type membershipCredentials struct { @@ -19,7 +21,11 @@ type membershipCredentials struct { Index rln.MembershipIndex `json:"index"` } +const RLN_CREDENTIALS_FILENAME = "rlnCredentials.txt" + func writeRLNMembershipCredentialsToFile(keyPair rln.MembershipKeyPair, idx rln.MembershipIndex, path string, passwd []byte, overwrite bool) error { + path = filepath.Join(path, RLN_CREDENTIALS_FILENAME) + if err := checkForFileExistence(path, overwrite); err != nil { return err } @@ -45,8 +51,8 @@ func writeRLNMembershipCredentialsToFile(keyPair rln.MembershipKeyPair, idx rln. return ioutil.WriteFile(path, output, 0600) } -func loadMembershipCredentialsFromFile(path string, passwd string) (rln.MembershipKeyPair, rln.MembershipIndex, error) { - src, err := ioutil.ReadFile(path) +func loadMembershipCredentialsFromFile(credentialsFilePath string, passwd string) (rln.MembershipKeyPair, rln.MembershipIndex, error) { + src, err := ioutil.ReadFile(credentialsFilePath) if err != nil { return rln.MembershipKeyPair{}, rln.MembershipIndex(0), err } @@ -71,11 +77,13 @@ func loadMembershipCredentialsFromFile(path string, passwd string) (rln.Membersh return credentials.Keypair, credentials.Index, err } -func getMembershipCredentials(options Options) (fromFile bool, idKey *rln.IDKey, idCommitment *rln.IDCommitment, index rln.MembershipIndex, err error) { - if _, err = os.Stat(options.RLNRelay.CredentialsFile); err == nil { - if keyPair, index, err := loadMembershipCredentialsFromFile(options.RLNRelay.CredentialsFile, options.KeyPasswd); err != nil { +func getMembershipCredentials(logger *zap.Logger, options Options) (fromFile bool, idKey *rln.IDKey, idCommitment *rln.IDCommitment, index rln.MembershipIndex, err error) { + credentialsFilePath := filepath.Join(options.RLNRelay.CredentialsPath, RLN_CREDENTIALS_FILENAME) + if _, err = os.Stat(credentialsFilePath); err == nil { + if keyPair, index, err := loadMembershipCredentialsFromFile(credentialsFilePath, options.KeyPasswd); err != nil { return false, nil, nil, rln.MembershipIndex(0), fmt.Errorf("could not read membership credentials file: %w", err) } else { + logger.Info("loaded rln credentials", zap.String("filepath", credentialsFilePath)) return true, &keyPair.IDKey, &keyPair.IDCommitment, index, nil } } diff --git a/waku/v2/node/wakunode2_rln.go b/waku/v2/node/wakunode2_rln.go index e9e9becc..53b659c6 100644 --- a/waku/v2/node/wakunode2_rln.go +++ b/waku/v2/node/wakunode2_rln.go @@ -54,9 +54,6 @@ func (w *WakuNode) mountRlnRelay() error { w.rlnRelay = rlnRelay - w.log.Info("membership id key", zap.String("IDKey", hex.EncodeToString(memKeyPair.IDKey[:]))) - w.log.Info("membership id commitment key", zap.String("IDCommitment", hex.EncodeToString(memKeyPair.IDCommitment[:]))) - // check the correct construction of the tree by comparing the calculated root against the expected root // no error should happen as it is already captured in the unit tests root, err := rlnRelay.RLN.GetMerkleRoot() @@ -84,7 +81,7 @@ func (w *WakuNode) mountRlnRelay() error { // mount the rln relay protocol in the on-chain/dynamic mode var err error - w.rlnRelay, err = rln.RlnRelayDynamic(context.Background(), w.relay, w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.log) + w.rlnRelay, err = rln.RlnRelayDynamic(context.Background(), w.relay, w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.opts.rlnRegistrationHandler, w.log) if err != nil { return err } diff --git a/waku/v2/node/wakuoptions.go b/waku/v2/node/wakuoptions.go index 83faa5bf..26ed1034 100644 --- a/waku/v2/node/wakuoptions.go +++ b/waku/v2/node/wakuoptions.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p-core/crypto" @@ -93,6 +94,7 @@ type WakuNodeParameters struct { rlnETHPrivateKey *ecdsa.PrivateKey rlnETHClientAddress string rlnMembershipContractAddress common.Address + rlnRegistrationHandler func(tx *types.Transaction) keepAliveInterval time.Duration diff --git a/waku/v2/node/wakuoptions_rln.go b/waku/v2/node/wakuoptions_rln.go index c3ee7865..7da1763e 100644 --- a/waku/v2/node/wakuoptions_rln.go +++ b/waku/v2/node/wakuoptions_rln.go @@ -27,7 +27,7 @@ func WithStaticRLNRelay(pubsubTopic string, contentTopic string, memberIndex r.M // WithStaticRLNRelay 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(pubsubTopic string, contentTopic string, memberIndex r.MembershipIndex, idKey *r.IDKey, idCommitment *r.IDCommitment, spamHandler rln.SpamHandler, ethClientAddress string, ethPrivateKey *ecdsa.PrivateKey, membershipContractAddress common.Address) WakuNodeOption { +func WithDynamicRLNRelay(pubsubTopic string, contentTopic string, memberIndex r.MembershipIndex, idKey *r.IDKey, idCommitment *r.IDCommitment, spamHandler rln.SpamHandler, ethClientAddress string, ethPrivateKey *ecdsa.PrivateKey, membershipContractAddress common.Address, registrationHandler rln.RegistrationHandler) WakuNodeOption { return func(params *WakuNodeParameters) error { params.enableRLN = true params.rlnRelayDynamic = true @@ -40,6 +40,7 @@ func WithDynamicRLNRelay(pubsubTopic string, contentTopic string, memberIndex r. params.rlnETHClientAddress = ethClientAddress params.rlnETHPrivateKey = ethPrivateKey params.rlnMembershipContractAddress = membershipContractAddress + params.rlnRegistrationHandler = registrationHandler return nil } } diff --git a/waku/v2/protocol/rln/rln_relay_builder.go b/waku/v2/protocol/rln/rln_relay_builder.go index 59ea3193..e50babc5 100644 --- a/waku/v2/protocol/rln/rln_relay_builder.go +++ b/waku/v2/protocol/rln/rln_relay_builder.go @@ -85,6 +85,7 @@ func RlnRelayDynamic( pubsubTopic string, contentTopic string, spamHandler SpamHandler, + registrationHandler RegistrationHandler, log *zap.Logger, ) (*WakuRLNRelay, error) { log = log.Named("rln-dynamic") @@ -114,6 +115,7 @@ func RlnRelayDynamic( contentTopic: contentTopic, log: log, nullifierLog: make(map[r.Epoch][]r.ProofMetadata), + registrationHandler: registrationHandler, } // prepare rln membership key pair diff --git a/waku/v2/protocol/rln/waku_rln_relay.go b/waku/v2/protocol/rln/waku_rln_relay.go index 917eb2eb..18dfed85 100644 --- a/waku/v2/protocol/rln/waku_rln_relay.go +++ b/waku/v2/protocol/rln/waku_rln_relay.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" proto "github.com/golang/protobuf/proto" "github.com/libp2p/go-libp2p-core/peer" @@ -26,6 +27,8 @@ const MAX_CLOCK_GAP_SECONDS = 20 // maximum allowed gap between the epochs of messages' RateLimitProofs const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / r.EPOCH_UNIT_SECONDS) +type RegistrationHandler = func(tx *types.Transaction) + type WakuRLNRelay struct { ctx context.Context @@ -50,7 +53,8 @@ type WakuRLNRelay struct { // the log of nullifiers and Shamir shares of the past messages grouped per epoch nullifierLog map[r.Epoch][]r.ProofMetadata - log *zap.Logger + registrationHandler RegistrationHandler + log *zap.Logger } func (rln *WakuRLNRelay) Stop() { diff --git a/waku/v2/protocol/rln/web3.go b/waku/v2/protocol/rln/web3.go index 919d5cb1..687af943 100644 --- a/waku/v2/protocol/rln/web3.go +++ b/waku/v2/protocol/rln/web3.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" r "github.com/status-im/go-rln/rln" "github.com/status-im/go-waku/waku/v2/protocol/rln/contracts" "go.uber.org/zap" @@ -23,7 +24,7 @@ func toBigInt(i []byte) *big.Int { return result } -func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey *ecdsa.PrivateKey, ethClientAddress string, membershipContractAddress common.Address, log *zap.Logger) (*r.MembershipIndex, error) { +func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey *ecdsa.PrivateKey, ethClientAddress string, membershipContractAddress common.Address, registrationHandler RegistrationHandler, log *zap.Logger) (*r.MembershipIndex, error) { backend, err := ethclient.Dial(ethClientAddress) if err != nil { return nil, err @@ -57,6 +58,10 @@ func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey * log.Info("transaction broadcasted", zap.String("transactionHash", tx.Hash().Hex())) + if registrationHandler != nil { + registrationHandler(tx) + } + txReceipt, err := bind.WaitMined(ctx, backend, tx) if err != nil { return nil, err @@ -95,7 +100,7 @@ func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey * // into the membership contract whose address is in rlnPeer.membershipContractAddress func (rln *WakuRLNRelay) Register(ctx context.Context) (*r.MembershipIndex, error) { pk := rln.membershipKeyPair.IDCommitment - return register(ctx, pk, rln.ethAccountPrivateKey, rln.ethClientAddress, rln.membershipContractAddress, rln.log) + return register(ctx, pk, rln.ethAccountPrivateKey, rln.ethClientAddress, rln.membershipContractAddress, rln.registrationHandler, rln.log) } // the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface @@ -146,7 +151,7 @@ func (rln *WakuRLNRelay) HandleGroupUpdates(handler RegistrationEventHandler, er select { case <-doneCh: return - case <-errCh: + case err := <-errCh: errChan <- err return } @@ -179,7 +184,11 @@ func (rln *WakuRLNRelay) watchNewEvents(rlnContract *contracts.RLN, handler Regi logSink := make(chan *contracts.RLNMemberRegistered) subs, err := rlnContract.WatchMemberRegistered(&bind.WatchOpts{Context: rln.ctx, Start: nil}, logSink) if err != nil { + if err == rpc.ErrNotificationsUnsupported { + err = errors.New("notifications not supported. The node must support websockets") + } errCh <- err + return } defer subs.Unsubscribe() @@ -197,10 +206,11 @@ func (rln *WakuRLNRelay) watchNewEvents(rlnContract *contracts.RLN, handler Regi close(logSink) return case err := <-subs.Err(): + close(logSink) if err != nil { rln.log.Error("watching new events", zap.Error(err)) + errCh <- err } - close(logSink) return } } diff --git a/waku/v2/rest/relay_api.yaml b/waku/v2/rest/relay_api.yaml index f84b4e7d..3b356bb3 100644 --- a/waku/v2/rest/relay_api.yaml +++ b/waku/v2/rest/relay_api.yaml @@ -118,6 +118,7 @@ components: properties: payload: type: string + format: byte contentTopic: $ref: '#/components/schemas/ContentTopic' version: diff --git a/waku_rln.go b/waku_rln.go index 745fb2f3..29f69f45 100644 --- a/waku_rln.go +++ b/waku_rln.go @@ -53,7 +53,7 @@ func rlnFlags() []cli.Flag { Name: "rln-relay-membership-credentials-file", Usage: "RLN relay membership credentials file", Value: "rlnCredentials.txt", - Destination: &options.RLNRelay.CredentialsFile, + Destination: &options.RLNRelay.CredentialsPath, }, // TODO: this is a good candidate option for subcommands // TODO: consider accepting a private key file and passwd