diff --git a/.gitignore b/.gitignore index ecc96878..3fbe5faa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ nodekey rlnCredentials.txt rlnCredentials.json +test_onchain.json *.bkp *.log diff --git a/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go b/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go index 214bf838..55ab0b38 100644 --- a/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go +++ b/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go @@ -45,6 +45,8 @@ type DynamicGroupManager struct { // TODO may need to make ethAccountPrivateKey mandatory ethAccountPrivateKey *ecdsa.PrivateKey + eventHandler RegistrationEventHandler + registrationHandler RegistrationHandler chainId *big.Int rlnContract *contracts.RLN @@ -124,6 +126,7 @@ func NewDynamicGroupManager( ethClientAddress: ethClientAddr, ethAccountPrivateKey: ethAccountPrivateKey, registrationHandler: registrationHandler, + eventHandler: handler, saveKeystore: saveKeystore, keystorePath: path, keystorePassword: password, @@ -176,7 +179,7 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, return err } - if gm.keystorePassword != "" && gm.keystorePath != "" { + if gm.identityCredential == nil && gm.keystorePassword != "" && gm.keystorePath != "" { credentials, err := keystore.GetMembershipCredentials(gm.log, gm.keystorePath, gm.keystorePassword, @@ -229,7 +232,7 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, return errors.New("no credentials available") } - if err = gm.HandleGroupUpdates(ctx, handler); err != nil { + if err = gm.HandleGroupUpdates(ctx, gm.eventHandler); err != nil { return err } @@ -309,6 +312,11 @@ func (gm *DynamicGroupManager) IdentityCredentials() (rln.IdentityCredential, er return *gm.identityCredential, nil } +func (gm *DynamicGroupManager) SetCredentials(identityCredential *rln.IdentityCredential, index *rln.MembershipIndex) { + gm.identityCredential = identityCredential + gm.membershipIndex = index +} + func (gm *DynamicGroupManager) MembershipIndex() (rln.MembershipIndex, error) { if gm.membershipIndex == nil { return 0, errors.New("membership index has not been setup") diff --git a/waku/v2/protocol/rln/group_manager/dynamic/web3.go b/waku/v2/protocol/rln/group_manager/dynamic/web3.go index 1407398e..62d7d1fa 100644 --- a/waku/v2/protocol/rln/group_manager/dynamic/web3.go +++ b/waku/v2/protocol/rln/group_manager/dynamic/web3.go @@ -17,7 +17,7 @@ import ( "go.uber.org/zap" ) -func toBigInt(i []byte) *big.Int { +func ToBigInt(i []byte) *big.Int { result := new(big.Int) result.SetBytes(i[:]) return result @@ -34,7 +34,7 @@ func register(ctx context.Context, backend *ethclient.Client, membershipFee *big log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:])) // registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress - tx, err := rlnContract.Register(auth, toBigInt(idComm[:])) + tx, err := rlnContract.Register(auth, ToBigInt(idComm[:])) if err != nil { return nil, err } @@ -190,7 +190,7 @@ func (gm *DynamicGroupManager) getEvents(ctx context.Context, from uint64, to *u return nil, err } - blockNumber := block.Number().Uint64() - 50 // Keep a buffer to retrieve latest block + blockNumber := block.Number().Uint64() toBlock = &blockNumber } @@ -221,6 +221,8 @@ func (gm *DynamicGroupManager) getEvents(ctx context.Context, from uint64, to *u results = append(results, evts...) + currentBlockNum = end + if batchSize < maxBatchSize { // update the batchSize with additive increase batchSize = batchSize + additiveFactor @@ -230,15 +232,6 @@ func (gm *DynamicGroupManager) getEvents(ctx context.Context, from uint64, to *u } } - if to == nil { - evts, err := gm.fetchEvents(ctx, *toBlock, nil) - if err != nil { - return nil, err - } - - results = append(results, evts...) - } - return results, nil } diff --git a/waku/v2/protocol/rln/onchain_test.go b/waku/v2/protocol/rln/onchain_test.go index c6f3f1b3..1929d36a 100644 --- a/waku/v2/protocol/rln/onchain_test.go +++ b/waku/v2/protocol/rln/onchain_test.go @@ -4,15 +4,18 @@ package rln import ( + "bytes" "context" "crypto/ecdsa" "crypto/rand" - r "github.com/waku-org/go-zerokit-rln/rln" + "errors" "math/big" - "sync" "testing" "time" + "github.com/waku-org/go-zerokit-rln/rln" + r "github.com/waku-org/go-zerokit-rln/rln" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -21,12 +24,16 @@ import ( "github.com/waku-org/go-waku/tests" "github.com/waku-org/go-waku/waku/v2/protocol/relay" "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/dynamic" "github.com/waku-org/go-waku/waku/v2/timesource" "github.com/waku-org/go-waku/waku/v2/utils" ) const ETH_CLIENT_ADDRESS = "ws://localhost:8545" +var MEMBERSHIP_FEE = big.NewInt(1000000000000000) // wei - 0.001 eth + func TestWakuRLNRelayDynamicSuite(t *testing.T) { suite.Run(t, new(WakuRLNRelayDynamicSuite)) } @@ -83,7 +90,7 @@ func (s *WakuRLNRelayDynamicSuite) SetupTest() { s.rlnContract = rlnContract } -func (s *WakuRLNRelayDynamicSuite) register(privKey *ecdsa.PrivateKey, commitment *big.Int) { +func (s *WakuRLNRelayDynamicSuite) register(privKey *ecdsa.PrivateKey, commitment *big.Int, handler func(evt *contracts.RLNMemberRegistered) error) { auth, err := bind.NewKeyedTransactorWithChainID(privKey, s.chainID) s.Require().NoError(err) @@ -92,8 +99,16 @@ func (s *WakuRLNRelayDynamicSuite) register(privKey *ecdsa.PrivateKey, commitmen tx, err := s.rlnContract.Register(auth, commitment) s.Require().NoError(err) - _, err = bind.WaitMined(context.TODO(), s.backend, tx) + receipt, err := bind.WaitMined(context.TODO(), s.backend, tx) s.Require().NoError(err) + + evt, err := s.rlnContract.ParseMemberRegistered(*receipt.Logs[0]) + s.Require().NoError(err) + + if handler != nil { + err = handler(evt) + s.Require().NoError(err) + } } func (s *WakuRLNRelayDynamicSuite) TestDynamicGroupManagement() { @@ -101,62 +116,62 @@ func (s *WakuRLNRelayDynamicSuite) TestDynamicGroupManagement() { rlnInstance, err := r.NewRLN() s.Require().NoError(err) - keyPair, err := rlnInstance.MembershipKeyGen() + port, err := tests.FindFreePort(s.T(), "", 5) + s.Require().NoError(err) + + host, err := tests.MakeHost(context.Background(), port, rand.Reader) + s.Require().NoError(err) + + relay := relay.NewWakuRelay(nil, 0, timesource.NewDefaultClock(), utils.Logger()) + relay.SetHost(host) + err = relay.Start(context.TODO()) + defer relay.Stop() + s.Require().NoError(err) + + rt, err := group_manager.NewMerkleRootTracker(5, rlnInstance) + s.Require().NoError(err) + + gm, err := dynamic.NewDynamicGroupManager(ETH_CLIENT_ADDRESS, s.u1PrivKey, s.rlnAddr, "./test_onchain.json", "", false, nil, utils.Logger()) s.Require().NoError(err) // initialize the WakuRLNRelay - rlnPeer := &WakuRLNRelay{ - ctx: context.TODO(), - membershipIndex: r.MembershipIndex(0), - membershipContractAddress: s.rlnAddr, - ethClientAddress: ETH_CLIENT_ADDRESS, - ethAccountPrivateKey: s.u1PrivKey, - RLN: rlnInstance, - log: utils.Logger(), - nullifierLog: make(map[r.Epoch][]r.ProofMetadata), - membershipKeyPair: keyPair, + rlnRelay := &WakuRLNRelay{ + rootTracker: rt, + groupManager: gm, + relay: relay, + RLN: rlnInstance, + log: utils.Logger(), + nullifierLog: make(map[r.MerkleNode][]r.ProofMetadata), } // generate another membership key pair keyPair2, err := rlnInstance.MembershipKeyGen() s.Require().NoError(err) - wg := &sync.WaitGroup{} - wg.Add(1) + err = rlnRelay.Start(context.Background()) + s.Require().NoError(err) - handler := func(pubkey r.IDCommitment, index r.MembershipIndex) error { - if pubkey == keyPair.IDCommitment || pubkey == keyPair2.IDCommitment { - wg.Done() + // register user + gm.Register(context.TODO()) + + handler := func(evt *contracts.RLNMemberRegistered) error { + pubkey := rln.Bytes32(evt.Pubkey.Bytes()) + if !bytes.Equal(pubkey[:], keyPair2.IDCommitment[:]) { + return errors.New("not found") } return rlnInstance.InsertMember(pubkey) } - errChan := make(chan error, 1) + // register member with contract + s.register(s.u2PrivKey, dynamic.ToBigInt(keyPair2.IDCommitment[:]), handler) - // mount the handler for listening to the contract events - go rlnPeer.HandleGroupUpdates(handler, errChan) - - // Register first member - s.register(s.u1PrivKey, toBigInt(keyPair.IDCommitment[:])) - - // Register second member - s.register(s.u2PrivKey, toBigInt(keyPair2.IDCommitment[:])) - - wg.Wait() - - timer1 := time.NewTimer(2 * time.Second) - select { - case <-timer1.C: - return - case err = <-errChan: - s.Require().NoError(err) - } + time.Sleep(2 * time.Second) } func (s *WakuRLNRelayDynamicSuite) TestInsertKeyMembershipContract() { - s.register(s.u1PrivKey, big.NewInt(20)) + s.register(s.u1PrivKey, big.NewInt(20), nil) // Batch Register auth, err := bind.NewKeyedTransactorWithChainID(s.u2PrivKey, s.chainID) @@ -172,31 +187,6 @@ func (s *WakuRLNRelayDynamicSuite) TestInsertKeyMembershipContract() { s.Require().NoError(err) } -func (s *WakuRLNRelayDynamicSuite) TestRegistrationProcedure() { - // Create a RLN instance - rlnInstance, err := r.NewRLN() - s.Require().NoError(err) - - keyPair, err := rlnInstance.MembershipKeyGen() - s.Require().NoError(err) - - // initialize the WakuRLNRelay - rlnPeer := &WakuRLNRelay{ - ctx: context.TODO(), - membershipIndex: r.MembershipIndex(0), - membershipContractAddress: s.rlnAddr, - ethClientAddress: ETH_CLIENT_ADDRESS, - ethAccountPrivateKey: s.u1PrivKey, - RLN: rlnInstance, - log: utils.Logger(), - nullifierLog: make(map[r.Epoch][]r.ProofMetadata), - membershipKeyPair: keyPair, - } - - _, err = rlnPeer.Register(context.TODO()) - s.Require().NoError(err) -} - func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() { // Create a RLN instance rlnInstance, err := r.NewRLN() @@ -219,26 +209,40 @@ func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() { s.Require().NoError(err) // register the members to the contract - s.register(s.u1PrivKey, toBigInt(keyPair1.IDCommitment[:])) - s.register(s.u1PrivKey, toBigInt(keyPair2.IDCommitment[:])) + s.register(s.u1PrivKey, dynamic.ToBigInt(keyPair1.IDCommitment[:]), nil) + s.register(s.u1PrivKey, dynamic.ToBigInt(keyPair2.IDCommitment[:]), nil) + // Creating relay port, err := tests.FindFreePort(s.T(), "", 5) s.Require().NoError(err) - host, err := tests.MakeHost(context.TODO(), port, rand.Reader) + host, err := tests.MakeHost(context.Background(), port, rand.Reader) s.Require().NoError(err) - relay := relay.NewWakuRelay(host, nil, 0, timesource.NewDefaultClock(), utils.Logger()) + relay := relay.NewWakuRelay(nil, 0, timesource.NewDefaultClock(), utils.Logger()) + relay.SetHost(host) err = relay.Start(context.TODO()) - s.Require().NoError(err) defer relay.Stop() + s.Require().NoError(err) + + // Subscribing to topic sub, err := relay.SubscribeToTopic(context.TODO(), RLNRELAY_PUBSUB_TOPIC) s.Require().NoError(err) defer sub.Unsubscribe() // mount the rln relay protocol in the on-chain/dynamic mode - rlnRelay, err := RlnRelayDynamic(context.TODO(), relay, ETH_CLIENT_ADDRESS, nil, s.rlnAddr, keyPair1, r.MembershipIndex(0), RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, nil, timesource.NewDefaultClock(), utils.Logger()) + gm, err := dynamic.NewDynamicGroupManager(ETH_CLIENT_ADDRESS, s.u1PrivKey, s.rlnAddr, "./test_onchain.json", "", false, nil, utils.Logger()) + s.Require().NoError(err) + + rlnRelay, err := New(relay, gm, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, timesource.NewDefaultClock(), utils.Logger()) + s.Require().NoError(err) + + // PreRegistering the keypair + membershipIndex := rln.MembershipIndex(0) + gm.SetCredentials(keyPair1, &membershipIndex) + + err = rlnRelay.Start(context.TODO()) s.Require().NoError(err) // wait for the event to reach the group handler @@ -258,46 +262,63 @@ func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() { port1, err := tests.FindFreePort(s.T(), "", 5) s.Require().NoError(err) - host1, err := tests.MakeHost(context.TODO(), port1, rand.Reader) + host1, err := tests.MakeHost(context.Background(), port1, rand.Reader) s.Require().NoError(err) - relay1 := relay.NewWakuRelay(host1, nil, 0, timesource.NewDefaultClock(), utils.Logger()) - relay1.Start(context.TODO()) - s.Require().NoError(err) + relay1 := relay.NewWakuRelay(nil, 0, timesource.NewDefaultClock(), utils.Logger()) + relay1.SetHost(host1) + err = relay1.Start(context.TODO()) defer relay1.Stop() + s.Require().NoError(err) sub1, err := relay1.SubscribeToTopic(context.TODO(), RLNRELAY_PUBSUB_TOPIC) s.Require().NoError(err) defer sub1.Unsubscribe() // mount the rln relay protocol in the on-chain/dynamic mode - rlnRelay1, err := RlnRelayDynamic(context.TODO(), relay1, ETH_CLIENT_ADDRESS, s.u1PrivKey, s.rlnAddr, nil, r.MembershipIndex(0), RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, nil, timesource.NewDefaultClock(), utils.Logger()) + gm1, err := dynamic.NewDynamicGroupManager(ETH_CLIENT_ADDRESS, s.u1PrivKey, s.rlnAddr, "./test_onchain.json", "", false, nil, utils.Logger()) + s.Require().NoError(err) + + rlnRelay1, err := New(relay1, gm1, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, timesource.NewDefaultClock(), utils.Logger()) + s.Require().NoError(err) + err = rlnRelay1.Start(context.TODO()) s.Require().NoError(err) // Node 2 ============================================================ port2, err := tests.FindFreePort(s.T(), "", 5) s.Require().NoError(err) - host2, err := tests.MakeHost(context.TODO(), port2, rand.Reader) + host2, err := tests.MakeHost(context.Background(), port2, rand.Reader) s.Require().NoError(err) - relay2 := relay.NewWakuRelay(host2, nil, 0, timesource.NewDefaultClock(), utils.Logger()) + relay2 := relay.NewWakuRelay(nil, 0, timesource.NewDefaultClock(), utils.Logger()) + relay2.SetHost(host2) err = relay2.Start(context.TODO()) - s.Require().NoError(err) defer relay2.Stop() + s.Require().NoError(err) sub2, err := relay2.SubscribeToTopic(context.TODO(), RLNRELAY_PUBSUB_TOPIC) s.Require().NoError(err) defer sub2.Unsubscribe() // mount the rln relay protocol in the on-chain/dynamic mode - rlnRelay2, err := RlnRelayDynamic(context.TODO(), relay2, ETH_CLIENT_ADDRESS, s.u2PrivKey, s.rlnAddr, nil, r.MembershipIndex(0), RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, nil, timesource.NewDefaultClock(), utils.Logger()) + gm2, err := dynamic.NewDynamicGroupManager(ETH_CLIENT_ADDRESS, s.u2PrivKey, s.rlnAddr, "./test_onchain.json", "", false, nil, utils.Logger()) + s.Require().NoError(err) + + rlnRelay2, err := New(relay2, gm2, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, timesource.NewDefaultClock(), utils.Logger()) + s.Require().NoError(err) + err = rlnRelay2.Start(context.TODO()) s.Require().NoError(err) // ================================== // the two nodes should be registered into the contract // since nodes are spun up sequentially // the first node has index 0 whereas the second node gets index 1 - s.Require().Equal(r.MembershipIndex(0), rlnRelay1.membershipIndex) - s.Require().Equal(r.MembershipIndex(1), rlnRelay2.membershipIndex) + idx1, err := rlnRelay1.groupManager.MembershipIndex() + s.Require().NoError(err) + idx2, err := rlnRelay2.groupManager.MembershipIndex() + s.Require().NoError(err) + + s.Require().Equal(r.MembershipIndex(0), idx1) + s.Require().Equal(r.MembershipIndex(1), idx2) }