Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rln subcommands #654

Merged
merged 3 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ Icon
# Nix
result

# Solidity files
waku/v2/protocol/rln/contracts/*.abi
waku/v2/protocol/rln/contracts/*.sol
waku/v2/protocol/rln/contracts/*.bin


# Thumbnails
._*

Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "waku/v2/protocol/rln/contracts/rln-contract"]
path = waku/v2/protocol/rln/contracts/rln-contract
url = [email protected]:vacp2p/rln-contract.git
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err we should use the waku-org/waku-rln-contract now!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using waku-rln-contract WakuRlnRegistry introduces a lot of breaking changes, due to how the bindings for go are modified and I end up not being able to retrieve the events:

waku/v2/protocol/rln/group_manager/dynamic/web3.go:17:73: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/web3.go:110:102: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/web3.go:111:27: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/web3.go:172:104: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/web3.go:173:37: gm.rlnContract.FilterMemberRegistered undefined (type *contracts.RLN has no field or method FilterMemberRegistered)
waku/v2/protocol/rln/group_manager/dynamic/dynamic.go:58:59: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/dynamic.go:73:36: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/dynamic.go:76:42: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/dynamic.go:151:24: gm.rlnContract.MEMBERSHIPDEPOSIT undefined (type *contracts.RLN has no field or method MEMBERSHIPDEPOSIT)
waku/v2/protocol/rln/group_manager/dynamic/dynamic.go:231:38: undefined: contracts.RLNMemberRegistered
waku/v2/protocol/rln/group_manager/dynamic/web3.go:173:37: too many errors

Notice how I can't easily filter events, nor obtain the fee.
Since @alrevuelta is still using vacp2p/rln-contract, I'll figure out how to solve the errors introduced by changing the contract in a separate PR.

9 changes: 0 additions & 9 deletions cmd/waku/flags_rln.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,6 @@ func rlnFlags() []cli.Flag {
Usage: "the index of credentials to use",
Destination: &options.RLNRelay.CredentialsIndex,
},
// TODO: this is a good candidate option for subcommands
// TODO: consider accepting a private key file and passwd
&cli.GenericFlag{
Name: "rln-relay-eth-account-private-key",
Usage: "Ethereum account private key used for registering in member contract",
Value: &wcli.PrivateKeyValue{
Value: &options.RLNRelay.ETHPrivateKey,
},
},
&cli.StringFlag{
Name: "rln-relay-eth-client-address",
Usage: "Ethereum testnet client address",
Expand Down
2 changes: 2 additions & 0 deletions cmd/waku/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
cli "github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"github.com/waku-org/go-waku/cmd/waku/keygen"
"github.com/waku-org/go-waku/cmd/waku/rlngenerate"
"github.com/waku-org/go-waku/waku/v2/node"
)

Expand Down Expand Up @@ -114,6 +115,7 @@ func main() {
},
Commands: []*cli.Command{
&keygen.Command,
&rlngenerate.Command,
},
}

Expand Down
9 changes: 0 additions & 9 deletions cmd/waku/node_rln.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package main

import (
"crypto/ecdsa"
"errors"

"github.com/waku-org/go-waku/waku/v2/node"
Expand All @@ -20,12 +19,6 @@ func checkForRLN(logger *zap.Logger, options NodeOptions, nodeOpts *[]node.WakuN
if !options.RLNRelay.Dynamic {
*nodeOpts = append(*nodeOpts, node.WithStaticRLNRelay(options.RLNRelay.PubsubTopic, options.RLNRelay.ContentTopic, rln.MembershipIndex(options.RLNRelay.MembershipGroupIndex), nil))
} else {

var ethPrivKey *ecdsa.PrivateKey
if options.RLNRelay.ETHPrivateKey != nil {
ethPrivKey = options.RLNRelay.ETHPrivateKey
}

// TODO: too many parameters in this function
// consider passing a config struct instead
*nodeOpts = append(*nodeOpts, node.WithDynamicRLNRelay(
Expand All @@ -39,8 +32,6 @@ func checkForRLN(logger *zap.Logger, options NodeOptions, nodeOpts *[]node.WakuN
rln.MembershipIndex(options.RLNRelay.MembershipGroupIndex),
nil,
options.RLNRelay.ETHClientAddress,
ethPrivKey,
nil,
))
}
}
Expand Down
1 change: 0 additions & 1 deletion cmd/waku/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ type RLNRelayOptions struct {
PubsubTopic string
ContentTopic string
Dynamic bool
ETHPrivateKey *ecdsa.PrivateKey
ETHClientAddress string
MembershipContractAddress common.Address
}
Expand Down
19 changes: 19 additions & 0 deletions cmd/waku/rlngenerate/command_no_rln.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//go:build !gowaku_rln
// +build !gowaku_rln

package rlngenerate

import (
"errors"

cli "github.com/urfave/cli/v2"
)

// Command generates a key file used to generate the node's peerID, encrypted with an optional password
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node's peerId maynot be tied to RLN membership right.
A user has the flexibility to use different public/private key pair for RLN and peerID generation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. the peerID is not associated to rln. User is able to choose which credential to use via the membership group and keystore index flags

var Command = cli.Command{
Name: "generate-rln-credentials",
Usage: "Generate credentials for usage with RLN",
Action: func(cCtx *cli.Context) error {
return errors.New("not available. Execute `make RLN=true` to add RLN support to go-waku")
},
}
142 changes: 142 additions & 0 deletions cmd/waku/rlngenerate/command_rln.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//go:build gowaku_rln
// +build gowaku_rln

package rlngenerate

import (
"context"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/ethclient"
cli "github.com/urfave/cli/v2"
"github.com/waku-org/go-waku/logging"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/contracts"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
"github.com/waku-org/go-waku/waku/v2/utils"
"github.com/waku-org/go-zerokit-rln/rln"
"go.uber.org/zap"
)

var options Options
var logger = utils.Logger().Named("rln-credentials")

// Command generates a key file used to generate the node's peerID, encrypted with an optional password
var Command = cli.Command{
Name: "generate-rln-credentials",
Usage: "Generate credentials for usage with RLN",
Action: func(cCtx *cli.Context) error {
err := verifyFlags()
if err != nil {
logger.Error("validating option flags", zap.Error(err))
return cli.Exit(err, 1)
}

err = execute(context.Background())
if err != nil {
logger.Error("registering RLN credentials", zap.Error(err))
return cli.Exit(err, 1)
}

return nil
},
Flags: flags,
}

func verifyFlags() error {
if options.CredentialsPath == "" {
logger.Warn("keystore: no credentials path set, using default path", zap.String("path", keystore.RLN_CREDENTIALS_FILENAME))
options.CredentialsPath = keystore.RLN_CREDENTIALS_FILENAME
}

if options.CredentialsPassword == "" {
logger.Warn("keystore: no credentials password set, using default password", zap.String("password", keystore.RLN_CREDENTIALS_PASSWORD))
options.CredentialsPassword = keystore.RLN_CREDENTIALS_PASSWORD
}

if options.ETHPrivateKey == nil {
return errors.New("a private key must be specified")
}

return nil
}

func execute(ctx context.Context) error {
ethClient, err := ethclient.Dial(options.ETHClientAddress)
if err != nil {
return err
}

rlnInstance, err := rln.NewRLN()
if err != nil {
return err
}

chainID, err := ethClient.ChainID(ctx)
if err != nil {
return err
}

rlnContract, err := contracts.NewRLN(options.MembershipContractAddress, ethClient)
if err != nil {
return err
}

// prepare rln membership key pair
logger.Info("generating rln credential")
identityCredential, err := rlnInstance.MembershipKeyGen()
if err != nil {
return err
}

// register the rln-relay peer to the membership contract
membershipIndex, err := register(ctx, ethClient, rlnContract, identityCredential.IDCommitment, chainID)
if err != nil {
return err
}

// TODO: clean private key from memory

err = persistCredentials(identityCredential, membershipIndex, chainID)
if err != nil {
return err
}

if logger.Level() == zap.DebugLevel {
logger.Info("registered credentials into the membership contract",
logging.HexString("IDCommitment", identityCredential.IDCommitment[:]),
logging.HexString("IDNullifier", identityCredential.IDNullifier[:]),
logging.HexString("IDSecretHash", identityCredential.IDSecretHash[:]),
logging.HexString("IDTrapDoor", identityCredential.IDTrapdoor[:]),
zap.Uint("index", membershipIndex),
)
} else {
logger.Info("registered credentials into the membership contract", logging.HexString("idCommitment", identityCredential.IDCommitment[:]), zap.Uint("index", membershipIndex))
}

ethClient.Close()

return nil
}

func persistCredentials(identityCredential *rln.IdentityCredential, membershipIndex rln.MembershipIndex, chainID *big.Int) error {
membershipGroup := keystore.MembershipGroup{
TreeIndex: membershipIndex,
MembershipContract: keystore.MembershipContract{
ChainId: fmt.Sprintf("0x%X", chainID.Int64()),
Address: options.MembershipContractAddress.String(),
},
}

membershipGroupIndex, err := keystore.AddMembershipCredentials(options.CredentialsPath, identityCredential, membershipGroup, options.CredentialsPassword, dynamic.RLNAppInfo, keystore.DefaultSeparator)
if err != nil {
return fmt.Errorf("failed to persist credentials: %w", err)
}

// TODO: obtain keystore index?
logger.Info("persisted credentials succesfully", zap.Uint("membershipGroupIndex", membershipGroupIndex))

return nil
}
75 changes: 75 additions & 0 deletions cmd/waku/rlngenerate/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//go:build gowaku_rln
// +build gowaku_rln

package rlngenerate

import (
cli "github.com/urfave/cli/v2"
wcli "github.com/waku-org/go-waku/waku/cliutils"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
)

var flags = []cli.Flag{
&cli.PathFlag{
Name: "cred-path",
Usage: "RLN relay membership credentials file",
Value: keystore.RLN_CREDENTIALS_FILENAME,
Destination: &options.CredentialsPath,
},
&cli.StringFlag{
Name: "cred-password",
Value: keystore.RLN_CREDENTIALS_PASSWORD,
Usage: "Password for encrypting RLN credentials",
Destination: &options.CredentialsPassword,
},
&cli.GenericFlag{
Name: "eth-account-private-key",
Usage: "Ethereum account private key used for registering in member contract",
Value: &wcli.PrivateKeyValue{
Value: &options.ETHPrivateKey,
},
},
&cli.StringFlag{
Name: "eth-client-address",
Usage: "Ethereum testnet client address",
Value: "ws://localhost:8545",
Destination: &options.ETHClientAddress,
},
&cli.GenericFlag{
Name: "eth-contract-address",
Usage: "Address of membership contract",
Value: &wcli.AddressValue{
Value: &options.MembershipContractAddress,
},
},
&cli.StringFlag{
Name: "eth-nonce",
Value: "",
Usage: "Set an specific ETH transaction nonce. Leave empty to calculate the nonce automatically",
Destination: &options.ETHNonce,
},
&cli.Uint64Flag{
Name: "eth-gas-limit",
Value: 0,
Usage: "Gas limit to set for the transaction execution (0 = estimate)",
Destination: &options.ETHGasLimit,
},
&cli.StringFlag{
Name: "eth-gas-price",
Value: "",
Usage: "Gas price in wei to use for the transaction execution (empty = gas price oracle)",
Destination: &options.ETHGasPrice,
},
&cli.StringFlag{
Name: "eth-gas-fee-cap",
Value: "",
Usage: "Gas fee cap in wei to use for the 1559 transaction execution (empty = gas price oracle)",
Destination: &options.ETHGasFeeCap,
},
&cli.StringFlag{
Name: "eth-gas-tip-cap",
Value: "",
Usage: "Gas priority fee cap in wei to use for the 1559 transaction execution (empty = gas price oracle)",
Destination: &options.ETHGasTipCap,
},
}
21 changes: 21 additions & 0 deletions cmd/waku/rlngenerate/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package rlngenerate

import (
"crypto/ecdsa"

"github.com/ethereum/go-ethereum/common"
)

// Options are settings used to create RLN credentials.
type Options struct {
CredentialsPath string
CredentialsPassword string
ETHPrivateKey *ecdsa.PrivateKey
ETHClientAddress string
MembershipContractAddress common.Address
ETHGasLimit uint64
ETHNonce string
ETHGasPrice string
ETHGasFeeCap string
ETHGasTipCap string
}
Loading