fix(rln-relay): RLN DB should be aware of chain and contract address

This commit is contained in:
Richard Ramos 2023-08-23 10:45:30 -04:00 committed by richΛrd
parent 7e36f91a5a
commit 5422af8130
5 changed files with 82 additions and 16 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"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"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/keystore"
@ -97,12 +98,14 @@ func handler(gm *DynamicGroupManager, events []*contracts.RLNMemberRegistered) e
gm.lastBlockProcessed = lastBlockProcessed
err = gm.SetMetadata(RLNMetadata{
LastProcessedBlock: gm.lastBlockProcessed,
ChainID: gm.chainId,
ContractAddress: gm.membershipContractAddress,
})
if err != nil {
// this is not a fatal error, hence we don't raise an exception
gm.log.Warn("failed to persist rln metadata", zap.Error(err))
} else {
gm.log.Debug("rln metadata persisted", zap.Uint64("lastProcessedBlock", gm.lastBlockProcessed))
gm.log.Debug("rln metadata persisted", zap.Uint64("lastProcessedBlock", gm.lastBlockProcessed), zap.Uint64("chainID", gm.chainId.Uint64()), logging.HexBytes("contractAddress", gm.membershipContractAddress[:]))
}
return nil

View File

@ -43,6 +43,7 @@ func TestHandler(t *testing.T) {
log: utils.Logger(),
cancel: cancel,
wg: sync.WaitGroup{},
chainId: big.NewInt(1),
rootTracker: rootTracker,
}

View File

@ -3,27 +3,47 @@ package dynamic
import (
"encoding/binary"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// RLNMetadata persists attributes in the RLN database
type RLNMetadata struct {
LastProcessedBlock uint64
ChainID *big.Int
ContractAddress common.Address
}
// Serialize converts a RLNMetadata into a binary format expected by zerokit's RLN
func (r RLNMetadata) Serialize() []byte {
result := make([]byte, 8)
binary.LittleEndian.PutUint64(result, r.LastProcessedBlock)
chainID := r.ChainID
if chainID == nil {
chainID = big.NewInt(0)
}
var result []byte
result = binary.LittleEndian.AppendUint64(result, r.LastProcessedBlock)
result = binary.LittleEndian.AppendUint64(result, chainID.Uint64())
result = append(result, r.ContractAddress.Bytes()...)
return result
}
const lastProcessedBlockOffset = 0
const chainIDOffset = lastProcessedBlockOffset + 8
const contractAddressOffset = chainIDOffset + 8
const metadataByteLen = 8 + 8 + 20 // 2 uint64 fields and a 20bytes address
// DeserializeMetadata converts a byte slice into a RLNMetadata instance
func DeserializeMetadata(b []byte) (RLNMetadata, error) {
if len(b) != 8 {
if len(b) != metadataByteLen {
return RLNMetadata{}, errors.New("wrong size")
}
return RLNMetadata{
LastProcessedBlock: binary.LittleEndian.Uint64(b),
LastProcessedBlock: binary.LittleEndian.Uint64(b[lastProcessedBlockOffset:chainIDOffset]),
ChainID: new(big.Int).SetUint64(binary.LittleEndian.Uint64(b[chainIDOffset:contractAddressOffset])),
ContractAddress: common.BytesToAddress(b[contractAddressOffset:]),
}, nil
}

View File

@ -0,0 +1,33 @@
package dynamic
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestMetadata(t *testing.T) {
metadata := &RLNMetadata{
LastProcessedBlock: 128,
ChainID: big.NewInt(1155511),
ContractAddress: common.HexToAddress("0x9c09146844c1326c2dbc41c451766c7138f88155"),
}
serializedMetadata := metadata.Serialize()
unserializedMetadata, err := DeserializeMetadata(serializedMetadata)
require.NoError(t, err)
require.Equal(t, metadata.ChainID.Uint64(), unserializedMetadata.ChainID.Uint64())
require.Equal(t, metadata.LastProcessedBlock, unserializedMetadata.LastProcessedBlock)
require.Equal(t, metadata.ContractAddress.Hex(), unserializedMetadata.ContractAddress.Hex())
// Handle cases where the chainId is not specified (for some reason?)
metadata.ChainID = nil
serializedMetadata = metadata.Serialize()
unserializedMetadata, err = DeserializeMetadata(serializedMetadata)
require.NoError(t, err)
require.Equal(t, uint64(0), unserializedMetadata.ChainID.Uint64())
}

View File

@ -1,6 +1,7 @@
package dynamic
import (
"bytes"
"context"
"errors"
"time"
@ -20,7 +21,24 @@ type RegistrationEventHandler = func(*DynamicGroupManager, []*contracts.RLNMembe
// It connects to the eth client, subscribes to the `MemberRegistered` event emitted from the `MembershipContract`
// and collects all the events, for every received event, it calls the `handler`
func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler RegistrationEventHandler) error {
err := gm.loadOldEvents(ctx, gm.rlnContract, handler)
fromBlock := uint64(0)
metadata, err := gm.GetMetadata()
if err != nil {
gm.log.Warn("could not load last processed block from metadata. Starting onchain sync from scratch", zap.Error(err))
} else {
if gm.chainId.Uint64() != metadata.ChainID.Uint64() {
return errors.New("persisted data: chain id mismatch")
}
if !bytes.Equal(gm.membershipContractAddress[:], metadata.ContractAddress[:]) {
return errors.New("persisted data: contract address mismatch")
}
fromBlock = metadata.LastProcessedBlock
gm.log.Info("resuming onchain sync", zap.Uint64("fromBlock", fromBlock))
}
err = gm.loadOldEvents(ctx, gm.rlnContract, fromBlock, handler)
if err != nil {
return err
}
@ -32,16 +50,7 @@ func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler R
return <-errCh
}
func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, rlnContract *contracts.RLN, handler RegistrationEventHandler) error {
fromBlock := uint64(0)
metadata, err := gm.GetMetadata()
if err == nil {
fromBlock = metadata.LastProcessedBlock
gm.log.Info("resuming onchain sync", zap.Uint64("fromBlock", fromBlock))
} else {
gm.log.Warn("could not load last processed block from metadata. Starting onchain sync from scratch", zap.Error(err))
}
func (gm *DynamicGroupManager) loadOldEvents(ctx context.Context, rlnContract *contracts.RLN, fromBlock uint64, handler RegistrationEventHandler) error {
events, err := gm.getEvents(ctx, fromBlock, nil)
if err != nil {
return err