go-waku/waku/v2/protocol/rln/group_manager/dynamic/web3.go

194 lines
5.6 KiB
Go
Raw Normal View History

2023-04-04 21:02:12 +00:00
package dynamic
2022-07-28 14:04:33 +00:00
import (
"context"
"crypto/ecdsa"
"errors"
"math/big"
"time"
2022-07-28 14:04:33 +00:00
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/event"
2022-09-11 21:08:58 +00:00
"github.com/ethereum/go-ethereum/rpc"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/contracts"
r "github.com/waku-org/go-zerokit-rln/rln"
2022-07-28 14:04:33 +00:00
"go.uber.org/zap"
)
2022-08-04 21:39:12 +00:00
var MEMBERSHIP_FEE = big.NewInt(1000000000000000) // wei - 0.001 eth
2022-07-28 14:04:33 +00:00
func toBigInt(i []byte) *big.Int {
result := new(big.Int)
result.SetBytes(i[:])
return result
}
2023-04-05 19:44:46 +00:00
func register(ctx context.Context, backend *ethclient.Client, idComm r.IDCommitment, ethAccountPrivateKey *ecdsa.PrivateKey, rlnContract *contracts.RLN, chainID *big.Int, registrationHandler RegistrationHandler, log *zap.Logger) (*r.MembershipIndex, error) {
2022-07-28 14:04:33 +00:00
auth, err := bind.NewKeyedTransactorWithChainID(ethAccountPrivateKey, chainID)
if err != nil {
return nil, err
}
auth.Value = MEMBERSHIP_FEE
auth.Context = ctx
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[:]))
if err != nil {
return nil, err
}
log.Info("transaction broadcasted", zap.String("transactionHash", tx.Hash().Hex()))
2022-09-11 21:08:58 +00:00
if registrationHandler != nil {
registrationHandler(tx)
}
2022-07-28 14:04:33 +00:00
txReceipt, err := bind.WaitMined(ctx, backend, tx)
if err != nil {
return nil, err
}
if txReceipt.Status != types.ReceiptStatusSuccessful {
return nil, errors.New("transaction reverted")
}
// the receipt topic holds the hash of signature of the raised events
evt, err := rlnContract.ParseMemberRegistered(*txReceipt.Logs[0])
if err != nil {
return nil, err
}
2022-10-05 22:08:01 +00:00
var eventIdComm r.IDCommitment = r.Bytes32(evt.Pubkey.Bytes())
2022-07-28 14:04:33 +00:00
log.Debug("the identity commitment key extracted from tx log", zap.Binary("eventIdComm", eventIdComm[:]))
if eventIdComm != idComm {
return nil, errors.New("invalid id commitment key")
}
result := new(r.MembershipIndex)
*result = r.MembershipIndex(uint(evt.Index.Int64()))
// debug "the index of registered identity commitment key", eventIndex=eventIndex
log.Debug("the index of registered identity commitment key", zap.Uint("eventIndex", uint(*result)))
return result, nil
}
// Register registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
// into the membership contract whose address is in rlnPeer.membershipContractAddress
2023-04-04 21:02:12 +00:00
func (gm *DynamicGroupManager) Register(ctx context.Context) (*r.MembershipIndex, error) {
2023-04-05 19:44:46 +00:00
return register(ctx,
gm.ethClient,
gm.identityCredential.IDCommitment,
gm.ethAccountPrivateKey,
gm.rlnContract,
gm.chainId,
gm.registrationHandler,
gm.log)
2022-07-28 14:04:33 +00:00
}
// 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
2023-04-04 21:02:12 +00:00
func (gm *DynamicGroupManager) processLogs(evt *contracts.RLNMemberRegistered, handler RegistrationEventHandler) error {
2022-07-28 14:04:33 +00:00
if evt == nil {
2022-08-09 00:02:08 +00:00
return nil
2022-07-28 14:04:33 +00:00
}
2022-10-05 22:08:01 +00:00
var pubkey r.IDCommitment = r.Bytes32(evt.Pubkey.Bytes())
2022-07-28 14:04:33 +00:00
2023-01-06 22:37:57 +00:00
index := evt.Index.Int64()
2023-04-04 21:02:12 +00:00
if index <= gm.lastIndexLoaded {
return nil
}
2022-07-28 14:04:33 +00:00
2023-04-04 21:02:12 +00:00
gm.lastIndexLoaded = index
2023-01-06 22:37:57 +00:00
return handler(pubkey, r.MembershipIndex(uint(evt.Index.Int64())))
2022-07-28 14:04:33 +00:00
}
// 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`
// and collects all the events, for every received event, it calls the `handler`
2023-04-04 21:02:12 +00:00
func (gm *DynamicGroupManager) HandleGroupUpdates(ctx context.Context, handler RegistrationEventHandler) error {
defer gm.wg.Done()
2023-04-05 19:44:46 +00:00
err := gm.loadOldEvents(ctx, gm.rlnContract, handler)
2022-08-09 00:02:08 +00:00
if err != nil {
2023-04-04 07:02:52 +00:00
return err
2022-08-09 00:02:08 +00:00
}
2022-07-28 14:04:33 +00:00
errCh := make(chan error)
2023-04-05 19:44:46 +00:00
go gm.watchNewEvents(ctx, gm.rlnContract, handler, gm.log, errCh)
2023-04-04 07:02:52 +00:00
return <-errCh
2022-07-28 14:04:33 +00:00
}
2023-04-04 21:02:12 +00:00
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})
2022-07-28 14:04:33 +00:00
if err != nil {
return err
}
for {
if !logIterator.Next() {
2022-07-28 14:04:33 +00:00
break
}
if logIterator.Error() != nil {
return logIterator.Error()
}
2023-04-04 21:02:12 +00:00
err = gm.processLogs(logIterator.Event, handler)
if err != nil {
return err
}
2022-07-28 14:04:33 +00:00
}
return nil
}
2023-04-04 21:02:12 +00:00
func (gm *DynamicGroupManager) watchNewEvents(ctx context.Context, rlnContract *contracts.RLN, handler RegistrationEventHandler, log *zap.Logger, errCh chan<- error) {
2022-07-28 14:04:33 +00:00
// Watch for new events
logSink := make(chan *contracts.RLNMemberRegistered)
2023-04-04 07:02:52 +00:00
firstErr := true
subs := event.Resubscribe(2*time.Second, func(ctx context.Context) (event.Subscription, error) {
2023-04-04 21:02:12 +00:00
subs, err := rlnContract.WatchMemberRegistered(&bind.WatchOpts{Context: ctx, Start: nil}, logSink)
if err != nil {
if err == rpc.ErrNotificationsUnsupported {
err = errors.New("notifications not supported. The node must support websockets")
}
2023-04-04 07:02:52 +00:00
if firstErr {
errCh <- err
}
2023-04-04 21:02:12 +00:00
gm.log.Error("subscribing to rln events", zap.Error(err))
2022-09-11 21:08:58 +00:00
}
2023-04-04 07:02:52 +00:00
firstErr = false
close(errCh)
return subs, err
})
2022-07-28 14:04:33 +00:00
2023-04-04 07:02:52 +00:00
defer subs.Unsubscribe()
defer close(logSink)
2022-07-28 14:04:33 +00:00
for {
select {
case evt := <-logSink:
2023-04-04 21:02:12 +00:00
err := gm.processLogs(evt, handler)
if err != nil {
2023-04-04 21:02:12 +00:00
gm.log.Error("processing rln log", zap.Error(err))
}
2023-04-04 21:02:12 +00:00
case <-ctx.Done():
return
2022-07-28 14:04:33 +00:00
case err := <-subs.Err():
2022-08-18 16:27:10 +00:00
if err != nil {
2023-04-04 21:02:12 +00:00
gm.log.Error("watching new events", zap.Error(err))
2022-08-18 16:27:10 +00:00
}
return
2022-07-28 14:04:33 +00:00
}
}
}