mirror of https://github.com/status-im/go-waku.git
feat: handle chain forks
This commit is contained in:
parent
5de3d9f619
commit
c3ef173b2c
|
@ -28,7 +28,7 @@
|
||||||
];
|
];
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
# FIXME: This needs to be manually changed when updating modules.
|
# FIXME: This needs to be manually changed when updating modules.
|
||||||
vendorSha256 = "sha256-NtaKb8b3Pg8iSWZp4x27yFyNlrJVqaChWNyJz3yO59g=";
|
vendorSha256 = "sha256-17E63+sejYDLkYH43J02cE8K4JjTYoi+kpMyEt+diFU=";
|
||||||
# Fix for 'nix run' trying to execute 'go-waku'.
|
# Fix for 'nix run' trying to execute 'go-waku'.
|
||||||
meta = { mainProgram = "waku"; };
|
meta = { mainProgram = "waku"; };
|
||||||
};
|
};
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -39,6 +39,7 @@ require (
|
||||||
github.com/lib/pq v1.10.4
|
github.com/lib/pq v1.10.4
|
||||||
github.com/waku-org/go-noise v0.0.4
|
github.com/waku-org/go-noise v0.0.4
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.12
|
github.com/waku-org/go-zerokit-rln v0.1.12
|
||||||
|
github.com/wk8/go-ordered-map v1.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1577,6 +1577,8 @@ github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230331181847-cba74520bae9/go.
|
||||||
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||||
|
github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8=
|
||||||
|
github.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk=
|
||||||
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
|
|
|
@ -16,7 +16,7 @@ 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/keystore"
|
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
|
||||||
"github.com/waku-org/go-zerokit-rln/rln"
|
"github.com/waku-org/go-zerokit-rln/rln"
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
om "github.com/wk8/go-ordered-map"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,7 +49,6 @@ type DynamicGroupManager struct {
|
||||||
chainId *big.Int
|
chainId *big.Int
|
||||||
rlnContract *contracts.RLN
|
rlnContract *contracts.RLN
|
||||||
membershipFee *big.Int
|
membershipFee *big.Int
|
||||||
lastIndexLoaded int64
|
|
||||||
|
|
||||||
saveKeystore bool
|
saveKeystore bool
|
||||||
keystorePath string
|
keystorePath string
|
||||||
|
@ -58,6 +57,42 @@ type DynamicGroupManager struct {
|
||||||
rootTracker *group_manager.MerkleRootTracker
|
rootTracker *group_manager.MerkleRootTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handler(gm *DynamicGroupManager, events []*contracts.RLNMemberRegistered) error {
|
||||||
|
toRemoveTable := om.New()
|
||||||
|
toInsertTable := om.New()
|
||||||
|
for _, event := range events {
|
||||||
|
if event.Raw.Removed {
|
||||||
|
var indexes []uint64
|
||||||
|
i_idx, ok := toRemoveTable.Get(event.Raw.BlockNumber)
|
||||||
|
if ok {
|
||||||
|
indexes = i_idx.([]uint64)
|
||||||
|
}
|
||||||
|
indexes = append(indexes, event.Index.Uint64())
|
||||||
|
toRemoveTable.Set(event.Raw.BlockNumber, indexes)
|
||||||
|
} else {
|
||||||
|
var eventsPerBlock []*contracts.RLNMemberRegistered
|
||||||
|
i_evt, ok := toInsertTable.Get(event.Raw.BlockNumber)
|
||||||
|
if ok {
|
||||||
|
eventsPerBlock = i_evt.([]*contracts.RLNMemberRegistered)
|
||||||
|
}
|
||||||
|
eventsPerBlock = append(eventsPerBlock, event)
|
||||||
|
toInsertTable.Set(event.Raw.BlockNumber, eventsPerBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := gm.RemoveMembers(toRemoveTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gm.InsertMembers(toInsertTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type RegistrationHandler = func(tx *types.Transaction)
|
type RegistrationHandler = func(tx *types.Transaction)
|
||||||
|
|
||||||
func NewDynamicGroupManager(
|
func NewDynamicGroupManager(
|
||||||
|
@ -89,7 +124,6 @@ func NewDynamicGroupManager(
|
||||||
ethClientAddress: ethClientAddr,
|
ethClientAddress: ethClientAddr,
|
||||||
ethAccountPrivateKey: ethAccountPrivateKey,
|
ethAccountPrivateKey: ethAccountPrivateKey,
|
||||||
registrationHandler: registrationHandler,
|
registrationHandler: registrationHandler,
|
||||||
lastIndexLoaded: -1,
|
|
||||||
saveKeystore: saveKeystore,
|
saveKeystore: saveKeystore,
|
||||||
keystorePath: path,
|
keystorePath: path,
|
||||||
keystorePassword: password,
|
keystorePassword: password,
|
||||||
|
@ -142,11 +176,6 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rootTracker.Sync()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if gm.keystorePassword != "" && gm.keystorePath != "" {
|
if gm.keystorePassword != "" && gm.keystorePath != "" {
|
||||||
credentials, err := keystore.GetMembershipCredentials(gm.log,
|
credentials, err := keystore.GetMembershipCredentials(gm.log,
|
||||||
gm.keystorePath,
|
gm.keystorePath,
|
||||||
|
@ -200,10 +229,6 @@ func (gm *DynamicGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN,
|
||||||
return errors.New("no credentials available")
|
return errors.New("no credentials available")
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := func(pubkey r.IDCommitment, index r.MembershipIndex) error {
|
|
||||||
return gm.InsertMember(pubkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = gm.HandleGroupUpdates(ctx, handler); err != nil {
|
if err = gm.HandleGroupUpdates(ctx, handler); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -239,18 +264,38 @@ func (gm *DynamicGroupManager) persistCredentials() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gm *DynamicGroupManager) InsertMember(pubkey rln.IDCommitment) error {
|
func (gm *DynamicGroupManager) InsertMembers(toInsert *om.OrderedMap) error {
|
||||||
gm.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
|
for pair := toInsert.Oldest(); pair != nil; pair = pair.Next() {
|
||||||
// assuming all the members arrive in order
|
events := pair.Value.([]*contracts.RLNMemberRegistered) // TODO: should these be sortered by index? we assume all members arrive in order
|
||||||
err := gm.rln.InsertMember(pubkey)
|
for _, evt := range events {
|
||||||
if err != nil {
|
pubkey := rln.Bytes32(evt.Pubkey.Bytes())
|
||||||
gm.log.Error("inserting member into merkletree", zap.Error(err))
|
// TODO: should we track indexes to identify missing?
|
||||||
return err
|
err := gm.rln.InsertMember(pubkey)
|
||||||
}
|
if err != nil {
|
||||||
|
gm.log.Error("inserting member into merkletree", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = gm.rootTracker.Sync()
|
_, err := gm.rootTracker.UpdateLatestRoot(pair.Key.(uint64))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gm *DynamicGroupManager) RemoveMembers(toRemove *om.OrderedMap) error {
|
||||||
|
for pair := toRemove.Newest(); pair != nil; pair = pair.Prev() {
|
||||||
|
memberIndexes := pair.Value.([]uint64)
|
||||||
|
for _, index := range memberIndexes {
|
||||||
|
err := gm.rln.DeleteMember(uint(index))
|
||||||
|
if err != nil {
|
||||||
|
gm.log.Error("deleting member", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gm.rootTracker.Backfill(pair.Key.(uint64))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
package dynamic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"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/group_manager"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
|
"github.com/waku-org/go-zerokit-rln/rln"
|
||||||
|
)
|
||||||
|
|
||||||
|
func eventBuilder(blockNumber uint64, removed bool, pubkey int64, index int64) *contracts.RLNMemberRegistered {
|
||||||
|
return &contracts.RLNMemberRegistered{
|
||||||
|
Raw: types.Log{
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
Removed: removed,
|
||||||
|
},
|
||||||
|
Index: big.NewInt(index),
|
||||||
|
Pubkey: big.NewInt(pubkey),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandler(t *testing.T) {
|
||||||
|
// Create a RLN instance
|
||||||
|
rlnInstance, err := rln.NewRLN()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rootTracker, err := group_manager.NewMerkleRootTracker(5, rlnInstance)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_ = ctx
|
||||||
|
|
||||||
|
gm := &DynamicGroupManager{
|
||||||
|
rln: rlnInstance,
|
||||||
|
log: utils.Logger(),
|
||||||
|
cancel: cancel,
|
||||||
|
wg: sync.WaitGroup{},
|
||||||
|
rootTracker: rootTracker,
|
||||||
|
}
|
||||||
|
|
||||||
|
root0 := [32]byte{62, 31, 25, 34, 223, 182, 113, 211, 249, 18, 247, 234, 70, 30, 10, 136, 238, 132, 143, 221, 225, 43, 108, 24, 171, 26, 210, 197, 106, 231, 52, 33}
|
||||||
|
roots := gm.rootTracker.Roots()
|
||||||
|
require.Len(t, roots, 1)
|
||||||
|
require.Equal(t, roots[0], root0)
|
||||||
|
|
||||||
|
events := []*contracts.RLNMemberRegistered{eventBuilder(1, false, 0xaaaa, 1)}
|
||||||
|
|
||||||
|
err = handler(gm, events)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
roots = gm.rootTracker.Roots()
|
||||||
|
|
||||||
|
require.Len(t, roots, 2)
|
||||||
|
require.Equal(t, roots[0], root0)
|
||||||
|
require.Equal(t, roots[1], [32]byte{253, 232, 31, 10, 168, 25, 42, 0, 28, 221, 146, 119, 34, 212, 121, 51, 82, 55, 113, 181, 236, 3, 11, 190, 194, 144, 125, 59, 46, 171, 90, 43})
|
||||||
|
|
||||||
|
events = []*contracts.RLNMemberRegistered{
|
||||||
|
eventBuilder(1, false, 0xbbbb, 2),
|
||||||
|
eventBuilder(2, false, 0xcccc, 3),
|
||||||
|
eventBuilder(3, false, 0xdddd, 4),
|
||||||
|
eventBuilder(4, false, 0xeeee, 5),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler(gm, events)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Root[1] should become [0]
|
||||||
|
roots = gm.rootTracker.Roots()
|
||||||
|
require.Len(t, roots, 5)
|
||||||
|
require.Equal(t, roots[0], [32]byte{253, 232, 31, 10, 168, 25, 42, 0, 28, 221, 146, 119, 34, 212, 121, 51, 82, 55, 113, 181, 236, 3, 11, 190, 194, 144, 125, 59, 46, 171, 90, 43})
|
||||||
|
require.Len(t, rootTracker.Buffer(), 1)
|
||||||
|
require.Equal(t, rootTracker.Buffer()[0], [32]byte{62, 31, 25, 34, 223, 182, 113, 211, 249, 18, 247, 234, 70, 30, 10, 136, 238, 132, 143, 221, 225, 43, 108, 24, 171, 26, 210, 197, 106, 231, 52, 33})
|
||||||
|
|
||||||
|
// We detect a fork
|
||||||
|
//
|
||||||
|
// [0] -> [1] -> [2] -> [3] -> [4] Our chain
|
||||||
|
// \
|
||||||
|
// \--> Real chain
|
||||||
|
// We should restore the valid roots from the buffer at the state the moment the chain forked
|
||||||
|
// In this case, just adding the original merkle root from empty tree
|
||||||
|
validRootsBeforeFork := roots[0:3]
|
||||||
|
events = []*contracts.RLNMemberRegistered{eventBuilder(3, true, 0xdddd, 4)}
|
||||||
|
|
||||||
|
err = handler(gm, events)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
roots = gm.rootTracker.Roots()
|
||||||
|
require.Len(t, roots, 4)
|
||||||
|
require.Equal(t, roots[0], root0)
|
||||||
|
require.Equal(t, roots[1], validRootsBeforeFork[0])
|
||||||
|
require.Equal(t, roots[2], validRootsBeforeFork[1])
|
||||||
|
require.Equal(t, roots[3], validRootsBeforeFork[2])
|
||||||
|
require.Len(t, rootTracker.Buffer(), 0)
|
||||||
|
|
||||||
|
// Adding multiple events for same block
|
||||||
|
events = []*contracts.RLNMemberRegistered{
|
||||||
|
eventBuilder(3, false, 0xdddd, 4),
|
||||||
|
eventBuilder(3, false, 0xeeee, 5),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler(gm, events)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
roots = gm.rootTracker.Roots()
|
||||||
|
require.Len(t, roots, 5)
|
||||||
|
|
||||||
|
}
|
|
@ -94,22 +94,7 @@ func (gm *DynamicGroupManager) Register(ctx context.Context) (*r.MembershipIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
// the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
||||||
type RegistrationEventHandler = func(pubkey r.IDCommitment, index r.MembershipIndex) error
|
type RegistrationEventHandler = func(*DynamicGroupManager, []*contracts.RLNMemberRegistered) error
|
||||||
|
|
||||||
func (gm *DynamicGroupManager) processLogs(evt *contracts.RLNMemberRegistered, handler RegistrationEventHandler) error {
|
|
||||||
if evt == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubkey r.IDCommitment = r.Bytes32(evt.Pubkey.Bytes())
|
|
||||||
|
|
||||||
index := evt.Index.Int64()
|
|
||||||
if index <= gm.lastIndexLoaded {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
gm.lastIndexLoaded = index
|
|
||||||
return handler(pubkey, r.MembershipIndex(uint(evt.Index.Int64())))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleGroupUpdates mounts the supplied handler for the registration events emitting from the membership contract
|
// HandleGroupUpdates mounts the supplied handler for the registration events emitting from the membership contract
|
||||||
// It connects to the eth client, subscribes to the `MemberRegistered` event emitted from the `MembershipContract`
|
// It connects to the eth client, subscribes to the `MemberRegistered` event emitted from the `MembershipContract`
|
||||||
|
@ -128,36 +113,21 @@ func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler R
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, rlnContract *contracts.RLN, handler RegistrationEventHandler) error {
|
func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, rlnContract *contracts.RLN, handler RegistrationEventHandler) error {
|
||||||
logIterator, err := rlnContract.FilterMemberRegistered(&bind.FilterOpts{Start: 0, End: nil, Context: ctx})
|
events, err := gm.getEvents(ctx, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for {
|
return handler(gm, events)
|
||||||
if !logIterator.Next() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if logIterator.Error() != nil {
|
|
||||||
return logIterator.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = gm.processLogs(logIterator.Event, handler)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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, rlnContract *contracts.RLN, handler RegistrationEventHandler, log *zap.Logger, errCh chan<- error) {
|
||||||
defer gm.wg.Done()
|
defer gm.wg.Done()
|
||||||
|
|
||||||
// Watch for new events
|
// Watch for new events
|
||||||
logSink := make(chan *contracts.RLNMemberRegistered)
|
|
||||||
|
|
||||||
firstErr := true
|
firstErr := true
|
||||||
|
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) {
|
||||||
subs, err := rlnContract.WatchMemberRegistered(&bind.WatchOpts{Context: ctx, Start: nil}, logSink)
|
s, err := gm.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")
|
||||||
|
@ -169,16 +139,23 @@ func (gm *DynamicGroupManager) watchNewEvents(ctx context.Context, rlnContract *
|
||||||
}
|
}
|
||||||
firstErr = false
|
firstErr = false
|
||||||
close(errCh)
|
close(errCh)
|
||||||
return subs, err
|
return s, err
|
||||||
})
|
})
|
||||||
|
|
||||||
defer subs.Unsubscribe()
|
defer subs.Unsubscribe()
|
||||||
defer close(logSink)
|
defer close(headerCh)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case evt := <-logSink:
|
case h := <-headerCh:
|
||||||
err := gm.processLogs(evt, handler)
|
blk := h.Number.Uint64()
|
||||||
|
events, err := gm.getEvents(ctx, blk, &blk)
|
||||||
|
if err != nil {
|
||||||
|
gm.log.Error("obtaining rln events", zap.Error(err))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler(gm, events)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gm.log.Error("processing rln log", zap.Error(err))
|
gm.log.Error("processing rln log", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
@ -192,3 +169,26 @@ func (gm *DynamicGroupManager) watchNewEvents(ctx context.Context, rlnContract *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gm *DynamicGroupManager) getEvents(ctx context.Context, from uint64, to *uint64) ([]*contracts.RLNMemberRegistered, error) {
|
||||||
|
logIterator, err := gm.rlnContract.FilterMemberRegistered(&bind.FilterOpts{Start: from, End: to, Context: ctx})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []*contracts.RLNMemberRegistered
|
||||||
|
|
||||||
|
for {
|
||||||
|
if !logIterator.Next() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if logIterator.Error() != nil {
|
||||||
|
return nil, logIterator.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
results = append(results, logIterator.Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +1,133 @@
|
||||||
package group_manager
|
package group_manager
|
||||||
|
|
||||||
import "github.com/waku-org/go-zerokit-rln/rln"
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
type MerkleRootTracker struct {
|
"github.com/waku-org/go-zerokit-rln/rln"
|
||||||
rln *rln.RLN
|
)
|
||||||
acceptableRootWindowSize int
|
|
||||||
validMerkleRoots []rln.MerkleNode
|
type RootsPerBlock struct {
|
||||||
|
root rln.MerkleNode
|
||||||
|
blockNumber uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMerkleRootTracker(acceptableRootWindowSize int, rlnInstance *rln.RLN) *MerkleRootTracker {
|
type MerkleRootTracker struct {
|
||||||
return &MerkleRootTracker{
|
sync.RWMutex
|
||||||
|
|
||||||
|
rln *rln.RLN
|
||||||
|
acceptableRootWindowSize int
|
||||||
|
validMerkleRoots []RootsPerBlock
|
||||||
|
merkleRootBuffer []RootsPerBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxBufferSize = 20
|
||||||
|
|
||||||
|
func NewMerkleRootTracker(acceptableRootWindowSize int, rlnInstance *rln.RLN) (*MerkleRootTracker, error) {
|
||||||
|
result := &MerkleRootTracker{
|
||||||
acceptableRootWindowSize: acceptableRootWindowSize,
|
acceptableRootWindowSize: acceptableRootWindowSize,
|
||||||
rln: rlnInstance,
|
rln: rlnInstance,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MerkleRootTracker) Sync() error {
|
_, err := result.UpdateLatestRoot(0)
|
||||||
root, err := m.rln.GetMerkleRoot()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.validMerkleRoots = append(m.validMerkleRoots, root)
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MerkleRootTracker) Backfill(fromBlockNumber uint64) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
numBlocks := 0
|
||||||
|
for i := len(m.validMerkleRoots) - 1; i >= 0; i-- {
|
||||||
|
if m.validMerkleRoots[i].blockNumber >= fromBlockNumber {
|
||||||
|
numBlocks++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if numBlocks == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove last roots
|
||||||
|
rootsToPop := numBlocks
|
||||||
|
if len(m.validMerkleRoots) < rootsToPop {
|
||||||
|
rootsToPop = len(m.validMerkleRoots)
|
||||||
|
}
|
||||||
|
m.validMerkleRoots = m.validMerkleRoots[0 : len(m.validMerkleRoots)-rootsToPop]
|
||||||
|
|
||||||
|
if len(m.merkleRootBuffer) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backfill the tree's acceptable roots
|
||||||
|
rootsToRestore := numBlocks
|
||||||
|
bufferLen := len(m.merkleRootBuffer)
|
||||||
|
if bufferLen < rootsToRestore {
|
||||||
|
rootsToRestore = bufferLen
|
||||||
|
}
|
||||||
|
for i := 0; i < rootsToRestore; i++ {
|
||||||
|
x, newRootBuffer := m.merkleRootBuffer[len(m.merkleRootBuffer)-1], m.merkleRootBuffer[:len(m.merkleRootBuffer)-1] // Pop
|
||||||
|
m.validMerkleRoots = append([]RootsPerBlock{x}, m.validMerkleRoots...)
|
||||||
|
m.merkleRootBuffer = newRootBuffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MerkleRootTracker) UpdateLatestRoot(blockNumber uint64) (rln.MerkleNode, error) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
root, err := m.rln.GetMerkleRoot()
|
||||||
|
if err != nil {
|
||||||
|
return [32]byte{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.pushRoot(blockNumber, root)
|
||||||
|
|
||||||
|
return root, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MerkleRootTracker) pushRoot(blockNumber uint64, root [32]byte) {
|
||||||
|
m.validMerkleRoots = append(m.validMerkleRoots, RootsPerBlock{
|
||||||
|
root: root,
|
||||||
|
blockNumber: blockNumber,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Maintain valid merkle root window
|
||||||
if len(m.validMerkleRoots) > m.acceptableRootWindowSize {
|
if len(m.validMerkleRoots) > m.acceptableRootWindowSize {
|
||||||
|
m.merkleRootBuffer = append(m.merkleRootBuffer, m.validMerkleRoots[0])
|
||||||
m.validMerkleRoots = m.validMerkleRoots[1:]
|
m.validMerkleRoots = m.validMerkleRoots[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// Maintain merkle root buffer
|
||||||
|
if len(m.merkleRootBuffer) > maxBufferSize {
|
||||||
|
m.merkleRootBuffer = m.merkleRootBuffer[1:]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MerkleRootTracker) Roots() []rln.MerkleNode {
|
func (m *MerkleRootTracker) Roots() []rln.MerkleNode {
|
||||||
return m.validMerkleRoots
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
|
||||||
|
result := make([]rln.MerkleNode, len(m.validMerkleRoots))
|
||||||
|
for i := range m.validMerkleRoots {
|
||||||
|
result[i] = m.validMerkleRoots[i].root
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MerkleRootTracker) Buffer() []rln.MerkleNode {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
|
||||||
|
result := make([]rln.MerkleNode, len(m.merkleRootBuffer))
|
||||||
|
for i := range m.merkleRootBuffer {
|
||||||
|
result[i] = m.merkleRootBuffer[i].root
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,18 +45,9 @@ func (gm *StaticGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, r
|
||||||
gm.rln = rlnInstance
|
gm.rln = rlnInstance
|
||||||
gm.rootTracker = rootTracker
|
gm.rootTracker = rootTracker
|
||||||
|
|
||||||
err := rootTracker.Sync()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// add members to the Merkle tree
|
// add members to the Merkle tree
|
||||||
for _, member := range gm.group {
|
for i, member := range gm.group {
|
||||||
if err := rlnInstance.InsertMember(member); err != nil {
|
err := gm.insertMember(member, uint64(i+1))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rootTracker.Sync()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -67,8 +58,9 @@ func (gm *StaticGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, r
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gm *StaticGroupManager) InsertMember(pubkey rln.IDCommitment) error {
|
func (gm *StaticGroupManager) insertMember(pubkey rln.IDCommitment, index uint64) error {
|
||||||
gm.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
|
gm.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]), zap.Uint64("index", index))
|
||||||
|
|
||||||
// assuming all the members arrive in order
|
// assuming all the members arrive in order
|
||||||
err := gm.rln.InsertMember(pubkey)
|
err := gm.rln.InsertMember(pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,7 +68,7 @@ func (gm *StaticGroupManager) InsertMember(pubkey rln.IDCommitment) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = gm.rootTracker.Sync()
|
_, err = gm.rootTracker.UpdateLatestRoot(index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,9 +78,12 @@ func (s *WakuRLNRelaySuite) TestUpdateLogAndHasDuplicate() {
|
||||||
rlnInstance, err := r.NewRLN()
|
rlnInstance, err := r.NewRLN()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
rootTracker, err := group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnInstance)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
rlnRelay := &WakuRLNRelay{
|
rlnRelay := &WakuRLNRelay{
|
||||||
nullifierLog: make(map[r.Nullifier][]r.ProofMetadata),
|
nullifierLog: make(map[r.Nullifier][]r.ProofMetadata),
|
||||||
rootTracker: group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnInstance),
|
rootTracker: rootTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
epoch := r.GetCurrentEpoch()
|
epoch := r.GetCurrentEpoch()
|
||||||
|
@ -166,9 +169,12 @@ func (s *WakuRLNRelaySuite) TestValidateMessage() {
|
||||||
groupManager, err := static.NewStaticGroupManager(groupIDCommitments, idCredential, index, utils.Logger())
|
groupManager, err := static.NewStaticGroupManager(groupIDCommitments, idCredential, index, utils.Logger())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
rootTracker, err := group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnInstance)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
rlnRelay := &WakuRLNRelay{
|
rlnRelay := &WakuRLNRelay{
|
||||||
groupManager: groupManager,
|
groupManager: groupManager,
|
||||||
rootTracker: group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnInstance),
|
rootTracker: rootTracker,
|
||||||
RLN: rlnInstance,
|
RLN: rlnInstance,
|
||||||
nullifierLog: make(map[r.Nullifier][]r.ProofMetadata),
|
nullifierLog: make(map[r.Nullifier][]r.ProofMetadata),
|
||||||
log: utils.Logger(),
|
log: utils.Logger(),
|
||||||
|
@ -177,6 +183,9 @@ func (s *WakuRLNRelaySuite) TestValidateMessage() {
|
||||||
//get the current epoch time
|
//get the current epoch time
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
|
err = groupManager.Start(context.Background(), rlnInstance, rootTracker)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// create some messages from the same peer and append rln proof to them, except wm4
|
// create some messages from the same peer and append rln proof to them, except wm4
|
||||||
|
|
||||||
wm1 := &pb.WakuMessage{Payload: []byte("Valid message")}
|
wm1 := &pb.WakuMessage{Payload: []byte("Valid message")}
|
||||||
|
|
|
@ -62,11 +62,16 @@ func New(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rootTracker, err := group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnInstance)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// create the WakuRLNRelay
|
// create the WakuRLNRelay
|
||||||
rlnPeer := &WakuRLNRelay{
|
rlnPeer := &WakuRLNRelay{
|
||||||
RLN: rlnInstance,
|
RLN: rlnInstance,
|
||||||
groupManager: groupManager,
|
groupManager: groupManager,
|
||||||
rootTracker: group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnInstance),
|
rootTracker: rootTracker,
|
||||||
pubsubTopic: pubsubTopic,
|
pubsubTopic: pubsubTopic,
|
||||||
contentTopic: contentTopic,
|
contentTopic: contentTopic,
|
||||||
relay: relay,
|
relay: relay,
|
||||||
|
|
Loading…
Reference in New Issue