feat: use rln registry contract

This commit is contained in:
Richard Ramos 2023-08-31 11:49:38 -04:00 committed by richΛrd
parent ab3f21f209
commit 5fcfbb9897
7 changed files with 242 additions and 148 deletions

View File

@ -9,12 +9,11 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/ethclient"
cli "github.com/urfave/cli/v2" cli "github.com/urfave/cli/v2"
"github.com/waku-org/go-waku/logging" "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/group_manager/dynamic"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore" "github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/web3"
"github.com/waku-org/go-waku/waku/v2/utils" "github.com/waku-org/go-waku/waku/v2/utils"
"github.com/waku-org/go-zerokit-rln/rln" "github.com/waku-org/go-zerokit-rln/rln"
"go.uber.org/zap" "go.uber.org/zap"
@ -46,22 +45,12 @@ var Command = cli.Command{
} }
func execute(ctx context.Context) error { func execute(ctx context.Context) error {
ethClient, err := ethclient.Dial(options.ETHClientAddress)
if err != nil {
return err
}
rlnInstance, err := rln.NewRLN() rlnInstance, err := rln.NewRLN()
if err != nil { if err != nil {
return err return err
} }
chainID, err := ethClient.ChainID(ctx) web3Config, err := web3.BuildConfig(ctx, options.ETHClientAddress, options.MembershipContractAddress)
if err != nil {
return err
}
rlnContract, err := contracts.NewRLN(options.MembershipContractAddress, ethClient)
if err != nil { if err != nil {
return err return err
} }
@ -74,14 +63,14 @@ func execute(ctx context.Context) error {
} }
// register the rln-relay peer to the membership contract // register the rln-relay peer to the membership contract
membershipIndex, err := register(ctx, ethClient, rlnContract, identityCredential.IDCommitment, chainID) membershipIndex, err := register(ctx, web3Config, identityCredential.IDCommitment)
if err != nil { if err != nil {
return err return err
} }
// TODO: clean private key from memory // TODO: clean private key from memory
err = persistCredentials(identityCredential, membershipIndex, chainID) err = persistCredentials(identityCredential, membershipIndex, web3Config.ChainID)
if err != nil { if err != nil {
return err return err
} }
@ -98,7 +87,7 @@ func execute(ctx context.Context) error {
logger.Info("registered credentials into the membership contract", logging.HexString("idCommitment", identityCredential.IDCommitment[:]), zap.Uint("index", membershipIndex)) logger.Info("registered credentials into the membership contract", logging.HexString("idCommitment", identityCredential.IDCommitment[:]), zap.Uint("index", membershipIndex))
} }
ethClient.Close() web3Config.ETHClient.Close()
return nil return nil
} }

View File

@ -11,15 +11,14 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/waku-org/go-waku/logging" "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/web3"
"github.com/waku-org/go-zerokit-rln/rln" "github.com/waku-org/go-zerokit-rln/rln"
"go.uber.org/zap" "go.uber.org/zap"
) )
func getMembershipFee(ctx context.Context, rlnContract *contracts.RLN) (*big.Int, error) { func getMembershipFee(ctx context.Context, rlnContract web3.RLNContract) (*big.Int, error) {
return rlnContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: ctx}) return rlnContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: ctx})
} }
@ -70,14 +69,14 @@ func buildTransactor(ctx context.Context, membershipFee *big.Int, chainID *big.I
return auth, nil return auth, nil
} }
func register(ctx context.Context, ethClient *ethclient.Client, rlnContract *contracts.RLN, idComm rln.IDCommitment, chainID *big.Int) (rln.MembershipIndex, error) { func register(ctx context.Context, web3Config *web3.Config, idComm rln.IDCommitment) (rln.MembershipIndex, error) {
// check if the contract exists by calling a static function // check if the contract exists by calling a static function
membershipFee, err := getMembershipFee(ctx, rlnContract) membershipFee, err := getMembershipFee(ctx, web3Config.RLNContract)
if err != nil { if err != nil {
return 0, err return 0, err
} }
auth, err := buildTransactor(ctx, membershipFee, chainID) auth, err := buildTransactor(ctx, membershipFee, web3Config.ChainID)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -85,13 +84,13 @@ func register(ctx context.Context, ethClient *ethclient.Client, rlnContract *con
log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:])) log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:]))
// registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress // registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress
tx, err := rlnContract.Register(auth, rln.Bytes32ToBigInt(idComm)) tx, err := web3Config.RegistryContract.Register(auth, web3Config.RLNContract.StorageIndex, rln.Bytes32ToBigInt(idComm))
if err != nil { if err != nil {
return 0, fmt.Errorf("transaction error: %w", err) return 0, fmt.Errorf("transaction error: %w", err)
} }
explorerURL := "" explorerURL := ""
switch chainID.Int64() { switch web3Config.ChainID.Int64() {
case 1: case 1:
explorerURL = "https://etherscan.io" explorerURL = "https://etherscan.io"
case 5: case 5:
@ -108,7 +107,7 @@ func register(ctx context.Context, ethClient *ethclient.Client, rlnContract *con
logger.Warn("waiting for transaction to be mined...") logger.Warn("waiting for transaction to be mined...")
txReceipt, err := bind.WaitMined(ctx, ethClient, tx) txReceipt, err := bind.WaitMined(ctx, web3Config.ETHClient, tx)
if err != nil { if err != nil {
return 0, fmt.Errorf("transaction error: %w", err) return 0, fmt.Errorf("transaction error: %w", err)
} }
@ -118,7 +117,7 @@ func register(ctx context.Context, ethClient *ethclient.Client, rlnContract *con
} }
// the receipt topic holds the hash of signature of the raised events // the receipt topic holds the hash of signature of the raised events
evt, err := rlnContract.ParseMemberRegistered(*txReceipt.Logs[0]) evt, err := web3Config.RLNContract.ParseMemberRegistered(*txReceipt.Logs[0])
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -10,12 +10,12 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/waku-org/go-waku/logging" "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/contracts"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager" "github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore" "github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/web3"
"github.com/waku-org/go-zerokit-rln/rln" "github.com/waku-org/go-zerokit-rln/rln"
om "github.com/wk8/go-ordered-map" om "github.com/wk8/go-ordered-map"
"go.uber.org/zap" "go.uber.org/zap"
@ -38,17 +38,11 @@ type DynamicGroupManager struct {
identityCredential *rln.IdentityCredential identityCredential *rln.IdentityCredential
membershipIndex rln.MembershipIndex membershipIndex rln.MembershipIndex
membershipContractAddress common.Address web3Config *web3.Config
ethClientAddress string
ethClient *ethclient.Client
lastBlockProcessed uint64 lastBlockProcessed uint64
eventHandler RegistrationEventHandler eventHandler RegistrationEventHandler
chainId *big.Int
rlnContract *contracts.RLN
appKeystore *keystore.AppKeystore appKeystore *keystore.AppKeystore
keystorePassword string keystorePassword string
@ -99,14 +93,14 @@ func handler(gm *DynamicGroupManager, events []*contracts.RLNMemberRegistered) e
gm.lastBlockProcessed = lastBlockProcessed gm.lastBlockProcessed = lastBlockProcessed
err = gm.SetMetadata(RLNMetadata{ err = gm.SetMetadata(RLNMetadata{
LastProcessedBlock: gm.lastBlockProcessed, LastProcessedBlock: gm.lastBlockProcessed,
ChainID: gm.chainId, ChainID: gm.web3Config.ChainID,
ContractAddress: gm.membershipContractAddress, ContractAddress: gm.web3Config.RegistryContract.Address,
}) })
if err != nil { if err != nil {
// this is not a fatal error, hence we don't raise an exception // this is not a fatal error, hence we don't raise an exception
gm.log.Warn("failed to persist rln metadata", zap.Error(err)) gm.log.Warn("failed to persist rln metadata", zap.Error(err))
} else { } else {
gm.log.Debug("rln metadata persisted", zap.Uint64("lastProcessedBlock", gm.lastBlockProcessed), zap.Uint64("chainID", gm.chainId.Uint64()), logging.HexBytes("contractAddress", gm.membershipContractAddress[:])) gm.log.Debug("rln metadata persisted", zap.Uint64("lastProcessedBlock", gm.lastBlockProcessed), zap.Uint64("chainID", gm.web3Config.ChainID.Uint64()), logging.HexBytes("contractAddress", gm.web3Config.RegistryContract.Address.Bytes()))
} }
return nil return nil
@ -127,8 +121,7 @@ func NewDynamicGroupManager(
return &DynamicGroupManager{ return &DynamicGroupManager{
membershipIndex: membershipIndex, membershipIndex: membershipIndex,
membershipContractAddress: memContractAddr, web3Config: web3.NewConfig(ethClientAddr, memContractAddr),
ethClientAddress: ethClientAddr,
eventHandler: handler, eventHandler: handler,
appKeystore: appKeystore, appKeystore: appKeystore,
keystorePassword: keystorePassword, keystorePassword: keystorePassword,
@ -138,7 +131,7 @@ func NewDynamicGroupManager(
} }
func (gm *DynamicGroupManager) getMembershipFee(ctx context.Context) (*big.Int, error) { func (gm *DynamicGroupManager) getMembershipFee(ctx context.Context) (*big.Int, error) {
return gm.rlnContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: ctx}) return gm.web3Config.RLNContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: ctx})
} }
func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error { func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error {
@ -151,25 +144,14 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN,
gm.log.Info("mounting rln-relay in on-chain/dynamic mode") gm.log.Info("mounting rln-relay in on-chain/dynamic mode")
backend, err := ethclient.Dial(gm.ethClientAddress) err := gm.web3Config.Build(ctx)
if err != nil { if err != nil {
return err return err
} }
gm.ethClient = backend
gm.rln = rlnInstance gm.rln = rlnInstance
gm.rootTracker = rootTracker gm.rootTracker = rootTracker
gm.chainId, err = backend.ChainID(ctx)
if err != nil {
return err
}
gm.rlnContract, err = contracts.NewRLN(gm.membershipContractAddress, backend)
if err != nil {
return err
}
// check if the contract exists by calling a static function // check if the contract exists by calling a static function
_, err = gm.getMembershipFee(ctx) _, err = gm.getMembershipFee(ctx)
if err != nil { if err != nil {
@ -194,7 +176,7 @@ func (gm *DynamicGroupManager) loadCredential() error {
credentials, err := gm.appKeystore.GetMembershipCredentials( credentials, err := gm.appKeystore.GetMembershipCredentials(
gm.keystorePassword, gm.keystorePassword,
gm.membershipIndex, gm.membershipIndex,
keystore.NewMembershipContractInfo(gm.chainId, gm.membershipContractAddress)) keystore.NewMembershipContractInfo(gm.web3Config.ChainID, gm.web3Config.RegistryContract.Address))
if err != nil { if err != nil {
return err return err
} }
@ -281,7 +263,8 @@ func (gm *DynamicGroupManager) Stop() error {
if err != nil { if err != nil {
return err return err
} }
gm.ethClient.Close()
gm.web3Config.ETHClient.Close()
gm.wg.Wait() gm.wg.Wait()

View File

@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/contracts" "github.com/waku-org/go-waku/waku/v2/protocol/rln/contracts"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager" "github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/web3"
"github.com/waku-org/go-waku/waku/v2/utils" "github.com/waku-org/go-waku/waku/v2/utils"
"github.com/waku-org/go-zerokit-rln/rln" "github.com/waku-org/go-zerokit-rln/rln"
) )
@ -44,7 +45,9 @@ func TestHandler(t *testing.T) {
log: utils.Logger(), log: utils.Logger(),
cancel: cancel, cancel: cancel,
wg: sync.WaitGroup{}, wg: sync.WaitGroup{},
chainId: big.NewInt(1), web3Config: &web3.Config{
ChainID: big.NewInt(1),
},
rootTracker: rootTracker, rootTracker: rootTracker,
metrics: newMetrics(prometheus.DefaultRegisterer), metrics: newMetrics(prometheus.DefaultRegisterer),
} }

View File

@ -26,11 +26,11 @@ func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler R
if err != nil { if err != nil {
gm.log.Warn("could not load last processed block from metadata. Starting onchain sync from scratch", zap.Error(err)) gm.log.Warn("could not load last processed block from metadata. Starting onchain sync from scratch", zap.Error(err))
} else { } else {
if gm.chainId.Uint64() != metadata.ChainID.Uint64() { if gm.web3Config.ChainID.Cmp(metadata.ChainID) != 0 {
return errors.New("persisted data: chain id mismatch") return errors.New("persisted data: chain id mismatch")
} }
if !bytes.Equal(gm.membershipContractAddress[:], metadata.ContractAddress[:]) { if !bytes.Equal(gm.web3Config.RegistryContract.Address.Bytes(), metadata.ContractAddress.Bytes()) {
return errors.New("persisted data: contract address mismatch") return errors.New("persisted data: contract address mismatch")
} }
@ -38,7 +38,7 @@ func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler R
gm.log.Info("resuming onchain sync", zap.Uint64("fromBlock", fromBlock)) gm.log.Info("resuming onchain sync", zap.Uint64("fromBlock", fromBlock))
} }
err = gm.loadOldEvents(ctx, gm.rlnContract, fromBlock, handler) err = gm.loadOldEvents(ctx, fromBlock, handler)
if err != nil { if err != nil {
return err return err
} }
@ -46,11 +46,11 @@ func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler R
errCh := make(chan error) errCh := make(chan error)
gm.wg.Add(1) gm.wg.Add(1)
go gm.watchNewEvents(ctx, gm.rlnContract, handler, gm.log, errCh) go gm.watchNewEvents(ctx, handler, gm.log, errCh)
return <-errCh return <-errCh
} }
func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, rlnContract *contracts.RLN, fromBlock uint64, handler RegistrationEventHandler) error { func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, fromBlock uint64, handler RegistrationEventHandler) error {
events, err := gm.getEvents(ctx, fromBlock, nil) events, err := gm.getEvents(ctx, fromBlock, nil)
if err != nil { if err != nil {
return err return err
@ -58,14 +58,14 @@ func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, rlnContract *c
return handler(gm, events) return handler(gm, events)
} }
func (gm *DynamicGroupManager) watchNewEvents(ctx context.Context, rlnContract *contracts.RLN, handler RegistrationEventHandler, log *zap.Logger, errCh chan<- error) { func (gm *DynamicGroupManager) watchNewEvents(ctx context.Context, handler RegistrationEventHandler, log *zap.Logger, errCh chan<- error) {
defer gm.wg.Done() defer gm.wg.Done()
// Watch for new events // Watch for new events
firstErr := true firstErr := true
headerCh := make(chan *types.Header) headerCh := make(chan *types.Header)
subs := event.Resubscribe(2*time.Second, func(ctx context.Context) (event.Subscription, error) { subs := event.Resubscribe(2*time.Second, func(ctx context.Context) (event.Subscription, error) {
s, err := gm.ethClient.SubscribeNewHead(ctx, headerCh) s, err := gm.web3Config.ETHClient.SubscribeNewHead(ctx, headerCh)
if err != nil { if err != nil {
if err == rpc.ErrNotificationsUnsupported { if err == rpc.ErrNotificationsUnsupported {
err = errors.New("notifications not supported. The node must support websockets") err = errors.New("notifications not supported. The node must support websockets")
@ -123,7 +123,7 @@ func (gm *DynamicGroupManager) getEvents(ctx context.Context, from uint64, to *u
toBlock := to toBlock := to
if to == nil { if to == nil {
block, err := gm.ethClient.BlockByNumber(ctx, nil) block, err := gm.web3Config.ETHClient.BlockByNumber(ctx, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -179,7 +179,7 @@ func (gm *DynamicGroupManager) getEvents(ctx context.Context, from uint64, to *u
} }
func (gm *DynamicGroupManager) fetchEvents(ctx context.Context, from uint64, to *uint64) ([]*contracts.RLNMemberRegistered, error) { func (gm *DynamicGroupManager) fetchEvents(ctx context.Context, from uint64, to *uint64) ([]*contracts.RLNMemberRegistered, error) {
logIterator, err := gm.rlnContract.FilterMemberRegistered(&bind.FilterOpts{Start: from, End: to, Context: ctx}) logIterator, err := gm.web3Config.RLNContract.FilterMemberRegistered(&bind.FilterOpts{Start: from, End: to, Context: ctx})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -6,14 +6,16 @@ package rln
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand"
"encoding/hex"
"math/big" "math/big"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/waku-org/go-zerokit-rln/rln" "github.com/waku-org/go-zerokit-rln/rln"
"go.uber.org/zap"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -25,11 +27,11 @@ import (
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager" "github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic" "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/protocol/rln/keystore"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/web3"
"github.com/waku-org/go-waku/waku/v2/timesource" "github.com/waku-org/go-waku/waku/v2/timesource"
"github.com/waku-org/go-waku/waku/v2/utils" "github.com/waku-org/go-waku/waku/v2/utils"
) )
var membershipFee = big.NewInt(1000000000000000) // wei - 0.001 eth
const keystorePassword = "test" const keystorePassword = "test"
func TestWakuRLNRelayDynamicSuite(t *testing.T) { func TestWakuRLNRelayDynamicSuite(t *testing.T) {
@ -38,29 +40,27 @@ func TestWakuRLNRelayDynamicSuite(t *testing.T) {
type WakuRLNRelayDynamicSuite struct { type WakuRLNRelayDynamicSuite struct {
suite.Suite suite.Suite
web3Config *web3.Config
clientAddr string
backend *ethclient.Client
chainID *big.Int
rlnAddr common.Address
rlnContract *contracts.RLN
u1PrivKey *ecdsa.PrivateKey u1PrivKey *ecdsa.PrivateKey
u2PrivKey *ecdsa.PrivateKey u2PrivKey *ecdsa.PrivateKey
} }
// TODO: on teardown, remove credentials func TempFileName(prefix, suffix string) string {
randBytes := make([]byte, 16)
rand.Read(randBytes)
return filepath.Join(os.TempDir(), prefix+hex.EncodeToString(randBytes)+suffix)
}
func (s *WakuRLNRelayDynamicSuite) SetupTest() { func (s *WakuRLNRelayDynamicSuite) SetupTest() {
s.clientAddr = os.Getenv("GANACHE_NETWORK_RPC_URL") clientAddr := os.Getenv("GANACHE_NETWORK_RPC_URL")
if s.clientAddr == "" { if clientAddr == "" {
s.clientAddr = "ws://localhost:8545" clientAddr = "ws://localhost:8545"
} }
backend, err := ethclient.Dial(s.clientAddr) backend, err := ethclient.Dial(clientAddr)
s.Require().NoError(err) s.Require().NoError(err)
defer backend.Close()
chainID, err := backend.ChainID(context.TODO()) chainID, err := backend.ChainID(context.TODO())
s.Require().NoError(err) s.Require().NoError(err)
@ -72,9 +72,6 @@ func (s *WakuRLNRelayDynamicSuite) SetupTest() {
s.u2PrivKey, err = crypto.ToECDSA(common.FromHex("0xa00da43843ad6b5161ddbace48f293ac3f82f8a8257af34de4c32900bb6e9a97")) s.u2PrivKey, err = crypto.ToECDSA(common.FromHex("0xa00da43843ad6b5161ddbace48f293ac3f82f8a8257af34de4c32900bb6e9a97"))
s.Require().NoError(err) s.Require().NoError(err)
s.backend = backend
s.chainID = chainID
// Deploying contracts // Deploying contracts
auth, err := bind.NewKeyedTransactorWithChainID(s.u1PrivKey, chainID) auth, err := bind.NewKeyedTransactorWithChainID(s.u1PrivKey, chainID)
s.Require().NoError(err) s.Require().NoError(err)
@ -82,18 +79,21 @@ func (s *WakuRLNRelayDynamicSuite) SetupTest() {
poseidonHasherAddr, _, _, err := contracts.DeployPoseidonHasher(auth, backend) poseidonHasherAddr, _, _, err := contracts.DeployPoseidonHasher(auth, backend)
s.Require().NoError(err) s.Require().NoError(err)
rlnAddr, _, rlnContract, err := contracts.DeployRLN(auth, backend, membershipFee, big.NewInt(20), poseidonHasherAddr) registryAddress, tx, rlnRegistry, err := contracts.DeployRLNRegistry(auth, backend, poseidonHasherAddr)
s.Require().NoError(err) s.Require().NoError(err)
txReceipt, err := bind.WaitMined(context.TODO(), backend, tx)
s.Require().NoError(err)
s.Require().Equal(txReceipt.Status, types.ReceiptStatusSuccessful)
s.rlnAddr = rlnAddr tx, err = rlnRegistry.NewStorage(auth)
s.rlnContract = rlnContract s.Require().NoError(err)
} txReceipt, err = bind.WaitMined(context.TODO(), backend, tx)
s.Require().NoError(err)
s.Require().Equal(txReceipt.Status, types.ReceiptStatusSuccessful)
func (s *WakuRLNRelayDynamicSuite) removeCredentials(path string) { s.web3Config = web3.NewConfig(clientAddr, registryAddress)
err := os.Remove(path) err = s.web3Config.Build(context.TODO())
if err != nil { s.Require().NoError(err)
utils.Logger().Warn("could not remove credentials", zap.String("path", path))
}
} }
func (s *WakuRLNRelayDynamicSuite) generateCredentials(rlnInstance *rln.RLN) *rln.IdentityCredential { func (s *WakuRLNRelayDynamicSuite) generateCredentials(rlnInstance *rln.RLN) *rln.IdentityCredential {
@ -103,21 +103,22 @@ func (s *WakuRLNRelayDynamicSuite) generateCredentials(rlnInstance *rln.RLN) *rl
} }
func (s *WakuRLNRelayDynamicSuite) register(appKeystore *keystore.AppKeystore, identityCredential *rln.IdentityCredential, privKey *ecdsa.PrivateKey) rln.MembershipIndex { func (s *WakuRLNRelayDynamicSuite) register(appKeystore *keystore.AppKeystore, identityCredential *rln.IdentityCredential, privKey *ecdsa.PrivateKey) rln.MembershipIndex {
membershipFee, err := s.web3Config.RLNContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: context.TODO()})
s.Require().NoError(err)
auth, err := bind.NewKeyedTransactorWithChainID(privKey, s.chainID) auth, err := bind.NewKeyedTransactorWithChainID(privKey, s.web3Config.ChainID)
s.Require().NoError(err) s.Require().NoError(err)
auth.Value = membershipFee auth.Value = membershipFee
auth.Context = context.TODO() auth.Context = context.TODO()
tx, err := s.web3Config.RegistryContract.Register(auth, s.web3Config.RLNContract.StorageIndex, rln.Bytes32ToBigInt(identityCredential.IDCommitment))
tx, err := s.rlnContract.Register(auth, rln.Bytes32ToBigInt(identityCredential.IDCommitment))
s.Require().NoError(err)
txReceipt, err := bind.WaitMined(context.TODO(), s.backend, tx)
s.Require().NoError(err) s.Require().NoError(err)
txReceipt, err := bind.WaitMined(context.TODO(), s.web3Config.ETHClient, tx)
s.Require().NoError(err)
s.Require().Equal(txReceipt.Status, types.ReceiptStatusSuccessful) s.Require().Equal(txReceipt.Status, types.ReceiptStatusSuccessful)
evt, err := s.rlnContract.ParseMemberRegistered(*txReceipt.Logs[0]) evt, err := s.web3Config.RLNContract.ParseMemberRegistered(*txReceipt.Logs[0])
s.Require().NoError(err) s.Require().NoError(err)
membershipIndex := rln.MembershipIndex(uint(evt.Index.Int64())) membershipIndex := rln.MembershipIndex(uint(evt.Index.Int64()))
@ -125,7 +126,7 @@ func (s *WakuRLNRelayDynamicSuite) register(appKeystore *keystore.AppKeystore, i
membershipCredential := keystore.MembershipCredentials{ membershipCredential := keystore.MembershipCredentials{
IdentityCredential: identityCredential, IdentityCredential: identityCredential,
TreeIndex: membershipIndex, TreeIndex: membershipIndex,
MembershipContractInfo: keystore.NewMembershipContractInfo(s.chainID, s.rlnAddr), MembershipContractInfo: keystore.NewMembershipContractInfo(s.web3Config.ChainID, s.web3Config.RegistryContract.Address),
} }
err = appKeystore.AddMembershipCredentials(membershipCredential, keystorePassword) err = appKeystore.AddMembershipCredentials(membershipCredential, keystorePassword)
@ -143,14 +144,12 @@ func (s *WakuRLNRelayDynamicSuite) TestDynamicGroupManagement() {
s.Require().NoError(err) s.Require().NoError(err)
u1Credentials := s.generateCredentials(rlnInstance) u1Credentials := s.generateCredentials(rlnInstance)
keystorePath1 := "./test_onchain.json" appKeystore, err := keystore.New(s.tmpKeystorePath(), dynamic.RLNAppInfo, utils.Logger())
appKeystore, err := keystore.New(keystorePath1, dynamic.RLNAppInfo, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
membershipIndex := s.register(appKeystore, u1Credentials, s.u1PrivKey) membershipIndex := s.register(appKeystore, u1Credentials, s.u1PrivKey)
defer s.removeCredentials(keystorePath1)
gm, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.rlnAddr, membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, utils.Logger()) gm, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
// initialize the WakuRLNRelay // initialize the WakuRLNRelay
@ -166,12 +165,10 @@ func (s *WakuRLNRelayDynamicSuite) TestDynamicGroupManagement() {
s.Require().NoError(err) s.Require().NoError(err)
u2Credentials := s.generateCredentials(rlnInstance) u2Credentials := s.generateCredentials(rlnInstance)
keystorePath2 := "./test_onchain2.json" appKeystore2, err := keystore.New(s.tmpKeystorePath(), dynamic.RLNAppInfo, utils.Logger())
appKeystore2, err := keystore.New(keystorePath2, dynamic.RLNAppInfo, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
membershipIndex = s.register(appKeystore2, u2Credentials, s.u2PrivKey) membershipIndex = s.register(appKeystore2, u2Credentials, s.u2PrivKey)
defer s.removeCredentials(keystorePath2)
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
@ -189,24 +186,25 @@ func (s *WakuRLNRelayDynamicSuite) TestInsertKeyMembershipContract() {
credentials2 := s.generateCredentials(rlnInstance) credentials2 := s.generateCredentials(rlnInstance)
credentials3 := s.generateCredentials(rlnInstance) credentials3 := s.generateCredentials(rlnInstance)
keystorePath1 := "./test_onchain.json" appKeystore, err := keystore.New(s.tmpKeystorePath(), dynamic.RLNAppInfo, utils.Logger())
appKeystore, err := keystore.New(keystorePath1, dynamic.RLNAppInfo, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
s.register(appKeystore, credentials1, s.u1PrivKey) s.register(appKeystore, credentials1, s.u1PrivKey)
defer s.removeCredentials(keystorePath1)
// Batch Register // Batch Register
auth, err := bind.NewKeyedTransactorWithChainID(s.u2PrivKey, s.chainID) auth, err := bind.NewKeyedTransactorWithChainID(s.u2PrivKey, s.web3Config.ChainID)
s.Require().NoError(err)
membershipFee, err := s.web3Config.RLNContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: context.TODO()})
s.Require().NoError(err) s.Require().NoError(err)
auth.Value = membershipFee.Mul(big.NewInt(2), membershipFee) auth.Value = membershipFee.Mul(big.NewInt(2), membershipFee)
auth.Context = context.TODO() auth.Context = context.TODO()
tx, err := s.rlnContract.RegisterBatch(auth, []*big.Int{rln.Bytes32ToBigInt(credentials2.IDCommitment), rln.Bytes32ToBigInt(credentials3.IDCommitment)}) tx, err := s.web3Config.RegistryContract.Register1(auth, s.web3Config.RLNContract.StorageIndex, []*big.Int{rln.Bytes32ToBigInt(credentials2.IDCommitment), rln.Bytes32ToBigInt(credentials3.IDCommitment)})
s.Require().NoError(err) s.Require().NoError(err)
txReceipt, err := bind.WaitMined(context.TODO(), s.backend, tx) txReceipt, err := bind.WaitMined(context.TODO(), s.web3Config.ETHClient, tx)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(txReceipt.Status, types.ReceiptStatusSuccessful) s.Require().Equal(txReceipt.Status, types.ReceiptStatusSuccessful)
} }
@ -219,10 +217,7 @@ func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() {
credentials1 := s.generateCredentials(rlnInstance) credentials1 := s.generateCredentials(rlnInstance)
credentials2 := s.generateCredentials(rlnInstance) credentials2 := s.generateCredentials(rlnInstance)
err = rlnInstance.InsertMember(credentials1.IDCommitment) err = rlnInstance.InsertMembers(1, []rln.IDCommitment{credentials1.IDCommitment, credentials2.IDCommitment})
s.Require().NoError(err)
err = rlnInstance.InsertMember(credentials2.IDCommitment)
s.Require().NoError(err) s.Require().NoError(err)
// get the Merkle root // get the Merkle root
@ -230,21 +225,17 @@ func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() {
s.Require().NoError(err) s.Require().NoError(err)
// register the members to the contract // register the members to the contract
keystorePath1 := "./test_onchain.json" appKeystore, err := keystore.New(s.tmpKeystorePath(), dynamic.RLNAppInfo, utils.Logger())
appKeystore, err := keystore.New(keystorePath1, dynamic.RLNAppInfo, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
membershipIndex := s.register(appKeystore, credentials1, s.u1PrivKey) membershipIndex := s.register(appKeystore, credentials1, s.u1PrivKey)
membershipIndex = s.register(appKeystore, credentials2, s.u1PrivKey) membershipIndex = s.register(appKeystore, credentials2, s.u1PrivKey)
defer s.removeCredentials(keystorePath1)
// mount the rln relay protocol in the on-chain/dynamic mode // mount the rln relay protocol in the on-chain/dynamic mode
gm, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.rlnAddr, membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, utils.Logger()) gm, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
rlnRelay, err := New(gm, "test-merkle-tree.db", timesource.NewDefaultClock(), prometheus.DefaultRegisterer, utils.Logger()) rlnRelay, err := New(gm, s.tmpRLNDBPath(), timesource.NewDefaultClock(), prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
err = rlnRelay.Start(context.TODO()) err = rlnRelay.Start(context.TODO())
@ -257,7 +248,6 @@ func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() {
// is expected to be the same as the calculatedRoot i.e., the one calculated outside of the mountRlnRelayDynamic proc // is expected to be the same as the calculatedRoot i.e., the one calculated outside of the mountRlnRelayDynamic proc
calculatedRoot, err := rlnRelay.RLN.GetMerkleRoot() calculatedRoot, err := rlnRelay.RLN.GetMerkleRoot()
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(expectedRoot, calculatedRoot) s.Require().Equal(expectedRoot, calculatedRoot)
} }
@ -270,17 +260,15 @@ func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() {
// Register credentials1 in contract and keystore1 // Register credentials1 in contract and keystore1
credentials1 := s.generateCredentials(rlnInstance) credentials1 := s.generateCredentials(rlnInstance)
keystorePath1 := "./test_onchain.json" appKeystore, err := keystore.New(s.tmpKeystorePath(), dynamic.RLNAppInfo, utils.Logger())
appKeystore, err := keystore.New(keystorePath1, dynamic.RLNAppInfo, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
membershipGroupIndex := s.register(appKeystore, credentials1, s.u1PrivKey) membershipGroupIndex := s.register(appKeystore, credentials1, s.u1PrivKey)
defer s.removeCredentials(keystorePath1)
// mount the rln relay protocol in the on-chain/dynamic mode // mount the rln relay protocol in the on-chain/dynamic mode
gm1, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.rlnAddr, membershipGroupIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, utils.Logger()) gm1, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipGroupIndex, appKeystore, keystorePassword, prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
rlnRelay1, err := New(gm1, "test-correct-registration-1.db", timesource.NewDefaultClock(), prometheus.DefaultRegisterer, utils.Logger()) rlnRelay1, err := New(gm1, s.tmpRLNDBPath(), timesource.NewDefaultClock(), prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
err = rlnRelay1.Start(context.TODO()) err = rlnRelay1.Start(context.TODO())
s.Require().NoError(err) s.Require().NoError(err)
@ -289,17 +277,16 @@ func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() {
// Register credentials2 in contract and keystore2 // Register credentials2 in contract and keystore2
credentials2 := s.generateCredentials(rlnInstance) credentials2 := s.generateCredentials(rlnInstance)
keystorePath2 := "./test_onchain2.json" appKeystore2, err := keystore.New(s.tmpKeystorePath(), dynamic.RLNAppInfo, utils.Logger())
appKeystore2, err := keystore.New(keystorePath2, dynamic.RLNAppInfo, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
membershipGroupIndex = s.register(appKeystore2, credentials2, s.u2PrivKey) membershipGroupIndex = s.register(appKeystore2, credentials2, s.u2PrivKey)
defer s.removeCredentials(keystorePath2)
// mount the rln relay protocol in the on-chain/dynamic mode // mount the rln relay protocol in the on-chain/dynamic mode
gm2, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.rlnAddr, membershipGroupIndex, appKeystore2, keystorePassword, prometheus.DefaultRegisterer, utils.Logger()) gm2, err := dynamic.NewDynamicGroupManager(s.web3Config.ETHClientAddress, s.web3Config.RegistryContract.Address, membershipGroupIndex, appKeystore2, keystorePassword, prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
rlnRelay2, err := New(gm2, "test-correct-registration-2.db", timesource.NewDefaultClock(), prometheus.DefaultRegisterer, utils.Logger()) rlnRelay2, err := New(gm2, s.tmpRLNDBPath(), timesource.NewDefaultClock(), prometheus.DefaultRegisterer, utils.Logger())
s.Require().NoError(err) s.Require().NoError(err)
err = rlnRelay2.Start(context.TODO()) err = rlnRelay2.Start(context.TODO())
s.Require().NoError(err) s.Require().NoError(err)
@ -311,7 +298,18 @@ func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() {
idx1 := rlnRelay1.groupManager.MembershipIndex() idx1 := rlnRelay1.groupManager.MembershipIndex()
idx2 := rlnRelay2.groupManager.MembershipIndex() idx2 := rlnRelay2.groupManager.MembershipIndex()
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(rln.MembershipIndex(1), idx1)
s.Require().Equal(rln.MembershipIndex(0), idx1) s.Require().Equal(rln.MembershipIndex(2), idx2)
s.Require().Equal(rln.MembershipIndex(1), idx2) }
func (s *WakuRLNRelayDynamicSuite) tmpKeystorePath() string {
keystoreDir, err := os.MkdirTemp("", "keystore_dir")
s.Require().NoError(err)
return filepath.Join(keystoreDir, "keystore.json")
}
func (s *WakuRLNRelayDynamicSuite) tmpRLNDBPath() string {
dbPath, err := os.MkdirTemp("", "rln_db")
s.Require().NoError(err)
return dbPath
} }

View File

@ -0,0 +1,122 @@
package web3
import (
"context"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/contracts"
)
// RegistryContract contains an instance of the RLN Registry contract and its address
type RegistryContract struct {
*contracts.RLNRegistry
Address common.Address
}
// RLNContract contains an instance of the RLN contract, its address and the storage index within the registry
// that represents this contract
type RLNContract struct {
*contracts.RLN
Address common.Address
StorageIndex uint16
}
// Config is a helper struct that contains attributes for interaction with RLN smart contracts
type Config struct {
configured bool
ETHClientAddress string
ETHClient *ethclient.Client
ChainID *big.Int
RegistryContract RegistryContract
RLNContract RLNContract
}
// NewConfig creates an instance of web3 Config
func NewConfig(ethClientAddress string, registryAddress common.Address) *Config {
return &Config{
ETHClientAddress: ethClientAddress,
RegistryContract: RegistryContract{
Address: registryAddress,
},
}
}
// BuildConfig returns an instance of Web3Config with all the required elements for interaction with the RLN smart contracts
func BuildConfig(ctx context.Context, ethClientAddress string, registryAddress common.Address) (*Config, error) {
ethClient, err := ethclient.Dial(ethClientAddress)
if err != nil {
return nil, err
}
chainID, err := ethClient.ChainID(ctx)
if err != nil {
return nil, err
}
rlnRegistry, err := contracts.NewRLNRegistry(registryAddress, ethClient)
if err != nil {
return nil, err
}
storageIndex, err := rlnRegistry.UsingStorageIndex(&bind.CallOpts{Context: ctx})
if err != nil {
return nil, err
}
rlnContractAddress, err := rlnRegistry.Storages(&bind.CallOpts{Context: ctx}, storageIndex)
if err != nil {
return nil, err
}
rlnContract, err := contracts.NewRLN(rlnContractAddress, ethClient)
if err != nil {
return nil, err
}
return &Config{
configured: true,
ETHClientAddress: ethClientAddress,
ETHClient: ethClient,
ChainID: chainID,
RegistryContract: RegistryContract{
RLNRegistry: rlnRegistry,
Address: registryAddress,
},
RLNContract: RLNContract{
RLN: rlnContract,
Address: rlnContractAddress,
StorageIndex: storageIndex,
},
}, nil
}
// Build sets up the Config object by instantiating the eth client and contracts
func (w *Config) Build(ctx context.Context) error {
if w.configured {
return errors.New("already configured")
}
if w.ETHClientAddress == "" {
return errors.New("no eth client address")
}
var zeroAddr common.Address
if w.RegistryContract.Address == zeroAddr {
return errors.New("no registry contract address")
}
newW, err := BuildConfig(ctx, w.ETHClientAddress, w.RegistryContract.Address)
if err != nil {
return err
}
*w = *newW
w.configured = true
return nil
}