mirror of https://github.com/status-im/go-waku.git
331 lines
10 KiB
Go
331 lines
10 KiB
Go
//go:build include_onchain_tests
|
|
// +build include_onchain_tests
|
|
|
|
package rln
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"errors"
|
|
"math/big"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"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"
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
"github.com/stretchr/testify/suite"
|
|
"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"
|
|
)
|
|
|
|
var MEMBERSHIP_FEE = big.NewInt(1000000000000000) // wei - 0.001 eth
|
|
|
|
func TestWakuRLNRelayDynamicSuite(t *testing.T) {
|
|
suite.Run(t, new(WakuRLNRelayDynamicSuite))
|
|
}
|
|
|
|
type WakuRLNRelayDynamicSuite struct {
|
|
suite.Suite
|
|
|
|
clientAddr string
|
|
|
|
backend *ethclient.Client
|
|
chainID *big.Int
|
|
rlnAddr common.Address
|
|
rlnContract *contracts.RLN
|
|
|
|
u1PrivKey *ecdsa.PrivateKey
|
|
u2PrivKey *ecdsa.PrivateKey
|
|
u3PrivKey *ecdsa.PrivateKey
|
|
u4PrivKey *ecdsa.PrivateKey
|
|
u5PrivKey *ecdsa.PrivateKey
|
|
}
|
|
|
|
func (s *WakuRLNRelayDynamicSuite) SetupTest() {
|
|
|
|
s.clientAddr = os.Getenv("GANACHE_NETWORK_RPC_URL")
|
|
if s.clientAddr == "" {
|
|
s.clientAddr = "ws://localhost:8545"
|
|
}
|
|
|
|
backend, err := ethclient.Dial(s.clientAddr)
|
|
s.Require().NoError(err)
|
|
|
|
chainID, err := backend.ChainID(context.TODO())
|
|
s.Require().NoError(err)
|
|
|
|
// TODO: obtain account list from ganache mnemonic or from eth_accounts
|
|
|
|
s.u1PrivKey, err = crypto.ToECDSA(common.FromHex("0x156ec84a451d8a2d0062993242b6c4e863647f5544ff8030f23578d4142f43f8"))
|
|
s.Require().NoError(err)
|
|
s.u2PrivKey, err = crypto.ToECDSA(common.FromHex("0xa00da43843ad6b5161ddbace48f293ac3f82f8a8257af34de4c32900bb6e9a97"))
|
|
s.Require().NoError(err)
|
|
s.u3PrivKey, err = crypto.ToECDSA(common.FromHex("0xa4c8d3ed78cd722521fac9d734c45187a4f5e887570be1f707a7bbce054c01ea"))
|
|
s.Require().NoError(err)
|
|
s.u4PrivKey, err = crypto.ToECDSA(common.FromHex("0x6b11ba548a7fd1958eb156877cc7bdd02d99d876b55381aa9b106c16b0b7a805"))
|
|
s.Require().NoError(err)
|
|
s.u5PrivKey, err = crypto.ToECDSA(common.FromHex("0x0410196287d0af405e5c16f610de52416bd48be74836dbca93d73e24bffb5a81"))
|
|
s.Require().NoError(err)
|
|
|
|
s.backend = backend
|
|
s.chainID = chainID
|
|
|
|
// Deploying contracts
|
|
auth, err := bind.NewKeyedTransactorWithChainID(s.u1PrivKey, chainID)
|
|
s.Require().NoError(err)
|
|
|
|
poseidonHasherAddr, _, _, err := contracts.DeployPoseidonHasher(auth, backend)
|
|
s.Require().NoError(err)
|
|
|
|
rlnAddr, _, rlnContract, err := contracts.DeployRLN(auth, backend, MEMBERSHIP_FEE, big.NewInt(20), poseidonHasherAddr)
|
|
s.Require().NoError(err)
|
|
|
|
s.rlnAddr = rlnAddr
|
|
s.rlnContract = rlnContract
|
|
}
|
|
|
|
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)
|
|
|
|
auth.Value = MEMBERSHIP_FEE
|
|
auth.Context = context.TODO()
|
|
|
|
tx, err := s.rlnContract.Register(auth, commitment)
|
|
s.Require().NoError(err)
|
|
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() {
|
|
// Create a RLN instance
|
|
rlnInstance, err := rln.NewRLN()
|
|
s.Require().NoError(err)
|
|
|
|
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(s.clientAddr, s.u1PrivKey, s.rlnAddr, 0, "./test_onchain.json", "", 0, false, nil, utils.Logger())
|
|
s.Require().NoError(err)
|
|
|
|
// initialize the WakuRLNRelay
|
|
rlnRelay := &WakuRLNRelay{
|
|
rootTracker: rt,
|
|
groupManager: gm,
|
|
relay: relay,
|
|
RLN: rlnInstance,
|
|
log: utils.Logger(),
|
|
nullifierLog: make(map[rln.MerkleNode][]rln.ProofMetadata),
|
|
}
|
|
|
|
// generate another membership key pair
|
|
keyPair2, err := rlnInstance.MembershipKeyGen()
|
|
s.Require().NoError(err)
|
|
|
|
err = rlnRelay.Start(context.Background())
|
|
s.Require().NoError(err)
|
|
|
|
// register user
|
|
gm.Register(context.TODO())
|
|
|
|
handler := func(evt *contracts.RLNMemberRegistered) error {
|
|
pubkey := rln.BigIntToBytes32(evt.Pubkey)
|
|
if !bytes.Equal(pubkey[:], keyPair2.IDCommitment[:]) {
|
|
return errors.New("not found")
|
|
}
|
|
|
|
return rlnInstance.InsertMember(pubkey)
|
|
}
|
|
|
|
// register member with contract
|
|
s.register(s.u2PrivKey, rln.Bytes32ToBigInt(keyPair2.IDCommitment), handler)
|
|
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
|
|
func (s *WakuRLNRelayDynamicSuite) TestInsertKeyMembershipContract() {
|
|
|
|
s.register(s.u1PrivKey, big.NewInt(20), nil)
|
|
|
|
// Batch Register
|
|
auth, err := bind.NewKeyedTransactorWithChainID(s.u2PrivKey, s.chainID)
|
|
s.Require().NoError(err)
|
|
|
|
auth.Value = MEMBERSHIP_FEE.Mul(big.NewInt(2), MEMBERSHIP_FEE)
|
|
auth.Context = context.TODO()
|
|
|
|
tx, err := s.rlnContract.RegisterBatch(auth, []*big.Int{big.NewInt(20), big.NewInt(21)})
|
|
s.Require().NoError(err)
|
|
|
|
_, err = bind.WaitMined(context.TODO(), s.backend, tx)
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
func (s *WakuRLNRelayDynamicSuite) TestMerkleTreeConstruction() {
|
|
// Create a RLN instance
|
|
rlnInstance, err := rln.NewRLN()
|
|
s.Require().NoError(err)
|
|
|
|
keyPair1, err := rlnInstance.MembershipKeyGen()
|
|
s.Require().NoError(err)
|
|
|
|
keyPair2, err := rlnInstance.MembershipKeyGen()
|
|
s.Require().NoError(err)
|
|
|
|
err = rlnInstance.InsertMember(keyPair1.IDCommitment)
|
|
s.Require().NoError(err)
|
|
|
|
err = rlnInstance.InsertMember(keyPair2.IDCommitment)
|
|
s.Require().NoError(err)
|
|
|
|
// get the Merkle root
|
|
expectedRoot, err := rlnInstance.GetMerkleRoot()
|
|
s.Require().NoError(err)
|
|
|
|
// register the members to the contract
|
|
s.register(s.u1PrivKey, rln.Bytes32ToBigInt(keyPair1.IDCommitment), nil)
|
|
s.register(s.u1PrivKey, rln.Bytes32ToBigInt(keyPair2.IDCommitment), nil)
|
|
|
|
// Creating relay
|
|
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)
|
|
|
|
// 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
|
|
gm, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.u1PrivKey, s.rlnAddr, 0, "./test_onchain.json", "", 0, false, nil, utils.Logger())
|
|
s.Require().NoError(err)
|
|
|
|
rlnRelay, err := New(relay, gm, "test-merkle-tree.db", 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
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// rln pks are inserted into the rln peer's Merkle tree and the resulting root
|
|
// is expected to be the same as the calculatedRoot i.e., the one calculated outside of the mountRlnRelayDynamic proc
|
|
calculatedRoot, err := rlnRelay.RLN.GetMerkleRoot()
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal(expectedRoot, calculatedRoot)
|
|
}
|
|
|
|
func (s *WakuRLNRelayDynamicSuite) TestCorrectRegistrationOfPeers() {
|
|
|
|
// Node 1 ============================================================
|
|
port1, err := tests.FindFreePort(s.T(), "", 5)
|
|
s.Require().NoError(err)
|
|
|
|
host1, err := tests.MakeHost(context.Background(), port1, rand.Reader)
|
|
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
|
|
gm1, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.u1PrivKey, s.rlnAddr, 0, "./test_onchain.json", "", 0, false, nil, utils.Logger())
|
|
s.Require().NoError(err)
|
|
|
|
rlnRelay1, err := New(relay1, gm1, "test-correct-registration-1.db", 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.Background(), port2, rand.Reader)
|
|
s.Require().NoError(err)
|
|
|
|
relay2 := relay.NewWakuRelay(nil, 0, timesource.NewDefaultClock(), utils.Logger())
|
|
relay2.SetHost(host2)
|
|
err = relay2.Start(context.TODO())
|
|
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
|
|
gm2, err := dynamic.NewDynamicGroupManager(s.clientAddr, s.u2PrivKey, s.rlnAddr, 0, "./test_onchain.json", "", 0, false, nil, utils.Logger())
|
|
s.Require().NoError(err)
|
|
|
|
rlnRelay2, err := New(relay2, gm2, "test-correct-registration-2.db", 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
|
|
idx1, err := rlnRelay1.groupManager.MembershipIndex()
|
|
s.Require().NoError(err)
|
|
idx2, err := rlnRelay2.groupManager.MembershipIndex()
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal(rln.MembershipIndex(0), idx1)
|
|
s.Require().Equal(rln.MembershipIndex(1), idx2)
|
|
}
|