diff --git a/cmd/waku/server/rest/debug.go b/cmd/waku/server/rest/debug.go index 6606f5d0..28333988 100644 --- a/cmd/waku/server/rest/debug.go +++ b/cmd/waku/server/rest/debug.go @@ -1,10 +1,15 @@ package rest import ( + "context" + "encoding/hex" + "fmt" + "math/big" "net/http" "github.com/go-chi/chi/v5" "github.com/waku-org/go-waku/waku/v2/node" + "github.com/waku-org/go-zerokit-rln/rln" ) type DebugService struct { @@ -20,8 +25,15 @@ type InfoReply struct { ListenAddresses []string `json:"listenAddresses,omitempty"` } +// Run as: +// ./build/waku --rest --rest-address=0.0.0.0 --rln-relay=true --rln-relay-dynamic=true --rln-relay-eth-client-address=wss://sepolia.infura.io/ws/v3/4576482c0f474483ac709755f2663b20 --rln-relay-eth-contract-address=0xF471d71E9b1455bBF4b85d475afb9BB0954A29c4 + +// Example usage (replace by your public commitment) +// curl http://localhost:8645/debug/v1/merkleProof/15506699537643273163469326218249028331014943998871371946527772121417127479348 + const routeDebugInfoV1 = "/debug/v1/info" const routeDebugVersionV1 = "/debug/v1/version" +const routeDebugGetMerkleProofV1 = "/debug/v1/merkleProof/{commitment}" func NewDebugService(node *node.WakuNode, m *chi.Mux) *DebugService { d := &DebugService{ @@ -31,6 +43,7 @@ func NewDebugService(node *node.WakuNode, m *chi.Mux) *DebugService { m.Get(routeDebugInfoV1, d.getV1Info) m.Get(routeDebugVersionV1, d.getV1Version) + m.Get(routeDebugGetMerkleProofV1, d.getV1MerkleProof) return d } @@ -50,3 +63,102 @@ func (d *DebugService) getV1Version(w http.ResponseWriter, req *http.Request) { response := VersionResponse(node.GetVersionInfo().String()) writeErrOrResponse(w, nil, response) } + +type MerkleProofResponse struct { + MerkleRoot string `json:"root"` + MerkePathElements []string `json:"pathElements"` + MerkePathIndexes []uint `json:"pathIndexes"` + LeafIndex uint64 `json:"leafIndex"` + CommitmentId string `json:"commitmentId"` +} + +func (d *DebugService) getV1MerkleProof(w http.ResponseWriter, req *http.Request) { + commitmentReq := chi.URLParam(req, "commitment") + if commitmentReq == "" { + writeErrResponse(w, nil, fmt.Errorf("commitment is empty"), http.StatusBadRequest) + return + } + + commitmentReqBig := new(big.Int) + commitmentReqBig, ok := commitmentReqBig.SetString(commitmentReq, 10) + if !ok { + writeErrResponse(w, nil, fmt.Errorf("commitment is not a number"), http.StatusBadRequest) + return + } + + fmt.Println("commitmentReq: ", commitmentReq) + + // Some dirty way to access rln and group manager + rlnInstance := d.node.RLNInstance + groupManager := d.node.GroupManager + + ready, err := groupManager.IsReady(context.Background()) + if err != nil { + writeErrResponse(w, nil, fmt.Errorf("could not check if service was ready"), http.StatusBadRequest) + return + } + if !ready { + writeErrResponse(w, nil, fmt.Errorf("service is not ready"), http.StatusBadRequest) + return + } + + found := false + membershipIndex := uint(0) + for leafIdx := uint(0); leafIdx < rlnInstance.LeavesSet(); leafIdx++ { + + leaf, err := rlnInstance.GetLeaf(leafIdx) + if err != nil { + writeErrResponse(w, nil, fmt.Errorf("could not get leaf"), http.StatusBadRequest) + return + } + + leafBig := rln.Bytes32ToBigInt(leaf) + + if leafBig.Cmp(commitmentReqBig) == 0 { + found = true + membershipIndex = leafIdx + break + } + } + if !found { + writeErrResponse(w, nil, fmt.Errorf("commitment not found: %s", commitmentReqBig.String()), http.StatusBadRequest) + return + } + + fmt.Println("membershipIndex: ", membershipIndex) + + merkleProof, err := rlnInstance.GetMerkleProof(rln.MembershipIndex(membershipIndex)) + if err != nil { + writeErrOrResponse(w, err, nil) + return + } + + elementsStr := make([]string, 0) + indexesStr := make([]uint, 0) + + for _, path := range merkleProof.PathElements { + fmt.Println("path: ", path) + elementsStr = append(elementsStr, "0x"+hex.EncodeToString(path[:])) + } + for _, index := range merkleProof.PathIndexes { + indexesStr = append(indexesStr, uint(index)) + } + + // TODO: Not nide to get proof and root in different non atomic calls. In an unlikely edge case the tree can change between the two calls + // if a membership is added. Proof of concept by now. + merkleRoot, err := rlnInstance.GetMerkleRoot() + if err != nil { + writeErrOrResponse(w, err, nil) + return + } + + fmt.Println("merkleRoot: ", merkleRoot) + + writeErrOrResponse(w, nil, MerkleProofResponse{ + MerkleRoot: "0x" + hex.EncodeToString(merkleRoot[:]), + MerkePathElements: elementsStr, + MerkePathIndexes: indexesStr, + LeafIndex: uint64(membershipIndex), + CommitmentId: commitmentReqBig.String(), + }) +} diff --git a/go.mod b/go.mod index 699d4571..7edc94ab 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/libp2p/go-libp2p-mplex v0.9.0 github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0 github.com/waku-org/go-noise v0.0.4 - github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59 + github.com/waku-org/go-zerokit-rln v0.1.14-0.20240116153345-7e086e8f8930 github.com/wk8/go-ordered-map v1.0.0 ) @@ -73,9 +73,9 @@ require ( github.com/rjeczalik/notify v0.9.3 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/status-im/status-go/extkeys v1.1.2 // indirect - github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b // indirect - github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 // indirect - github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1 // indirect + github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116135015-f6f595c7b8ef // indirect + github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240116134931-a8b8c6ab4b80 // indirect + github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240116135046-2875fec12afc // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/fx v1.20.1 // indirect go.uber.org/mock v0.3.0 // indirect diff --git a/go.sum b/go.sum index 7b802cc2..80c0c4a6 100644 --- a/go.sum +++ b/go.sum @@ -1540,14 +1540,14 @@ github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0 h1:R github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0/go.mod h1:EhZP9fee0DYjKH/IOQvoNSy1tSHp2iZadsHGphcAJgY= github.com/waku-org/go-noise v0.0.4 h1:ZfQDcCw8pazm89EBl5SXY7GGAnzDQb9AHFXlw3Ktbvk= github.com/waku-org/go-noise v0.0.4/go.mod h1:+PWRfs2eSOVwKrPcQlfhwDngSh3faL/1QoxvoqggEKc= -github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59 h1:jisj+OCI6QydLtFq3Pyhu49wl9ytPN7oAHjMfepHDrA= -github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59/go.mod h1:1PdBdPzyTaKt3VnpAHk3zj+r9dXPFOr3IHZP9nFle6E= -github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b h1:KgZVhsLkxsj5gb/FfndSCQu6VYwALrCOgYI3poR95yE= -github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= -github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 h1:Sd7QD/1Yo2o2M1MY49F8Zr4KNBPUEK5cz5HoXQVJbrs= -github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4= -github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1 h1:4HSdWMFMufpRo3ECTX6BrvA+VzKhXZf7mS0rTa5cCWU= -github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y= +github.com/waku-org/go-zerokit-rln v0.1.14-0.20240116153345-7e086e8f8930 h1:R9s+zA28gCX3e6b3iZKwLX7oRh3G15r1eSuOglSwUfk= +github.com/waku-org/go-zerokit-rln v0.1.14-0.20240116153345-7e086e8f8930/go.mod h1:IOXLch4yJlN8WMf3S8kbGiZPMV1xYQ+xQ3D1yOGUAxg= +github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116135015-f6f595c7b8ef h1:MAkZryAeRhiH3TKHRK2h+WztZI1VqfQ/oeXMIxKZNy0= +github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240116135015-f6f595c7b8ef/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= +github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240116134931-a8b8c6ab4b80 h1:3KObRaYJnTI41U0reNBk7DYr5PVCTq8T9gJLXGfPfaY= +github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240116134931-a8b8c6ab4b80/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4= +github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240116135046-2875fec12afc h1:YjIgIrqlNVC+hXNy2ykYy3JvBKima64J9jXbKM/ULpw= +github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240116135046-2875fec12afc/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y= 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/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= diff --git a/waku/v2/node/wakunode2.go b/waku/v2/node/wakunode2.go index 378ac06b..b79845b3 100644 --- a/waku/v2/node/wakunode2.go +++ b/waku/v2/node/wakunode2.go @@ -42,10 +42,12 @@ import ( "github.com/waku-org/go-waku/waku/v2/protocol/pb" "github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange" "github.com/waku-org/go-waku/waku/v2/protocol/relay" + "github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic" "github.com/waku-org/go-waku/waku/v2/protocol/store" "github.com/waku-org/go-waku/waku/v2/rendezvous" "github.com/waku-org/go-waku/waku/v2/service" "github.com/waku-org/go-waku/waku/v2/timesource" + r "github.com/waku-org/go-zerokit-rln/rln" "github.com/waku-org/go-waku/waku/v2/utils" ) @@ -84,11 +86,14 @@ type RLNRelay interface { } type WakuNode struct { - host host.Host - opts *WakuNodeParameters - log *zap.Logger - timesource timesource.Timesource - metrics Metrics + // TODO: quick and dirty way to expose + RLNInstance *r.RLN + GroupManager *dynamic.DynamicGroupManager + host host.Host + opts *WakuNodeParameters + log *zap.Logger + timesource timesource.Timesource + metrics Metrics peerstore peerstore.Peerstore peerConnector *peermanager.PeerConnectionStrategy @@ -286,10 +291,12 @@ func New(opts ...WakuNodeOption) (*WakuNode, error) { relay.WithMaxMsgSize(w.opts.maxMsgSizeBytes)) if w.opts.enableRelay { - err = w.setupRLNRelay() + err, rlnInstance, groupManager := w.setupRLNRelay() if err != nil { return nil, err } + w.RLNInstance = rlnInstance + w.GroupManager = groupManager } w.opts.legacyFilterOpts = append(w.opts.legacyFilterOpts, legacy_filter.WithPeerManager(w.peermanager)) diff --git a/waku/v2/node/wakunode2_rln.go b/waku/v2/node/wakunode2_rln.go index 5932a9ac..c72b8097 100644 --- a/waku/v2/node/wakunode2_rln.go +++ b/waku/v2/node/wakunode2_rln.go @@ -21,22 +21,22 @@ func (w *WakuNode) RLNRelay() RLNRelay { return w.rlnRelay } -func (w *WakuNode) setupRLNRelay() error { +func (w *WakuNode) setupRLNRelay() (error, *r.RLN, *dynamic.DynamicGroupManager) { var err error if !w.opts.enableRLN { - return nil + return nil, nil, nil } if !w.opts.enableRelay { - return errors.New("rln requires relay") + return errors.New("rln requires relay"), nil, nil } var groupManager group_manager.GroupManager rlnInstance, rootTracker, err := rln.GetRLNInstanceAndRootTracker(w.opts.rlnTreePath) if err != nil { - return err + return err, nil, nil } if !w.opts.rlnRelayDynamic { w.log.Info("setting up waku-rln-relay in off-chain mode") @@ -49,12 +49,12 @@ func (w *WakuNode) setupRLNRelay() error { // set up rln relay inputs groupKeys, idCredential, err := static.Setup(index) if err != nil { - return err + return err, nil, nil } groupManager, err = static.NewStaticGroupManager(groupKeys, idCredential, index, rlnInstance, rootTracker, w.log) if err != nil { - return err + return err, nil, nil } } else { w.log.Info("setting up waku-rln-relay in on-chain mode") @@ -63,7 +63,7 @@ func (w *WakuNode) setupRLNRelay() error { if w.opts.keystorePath != "" { appKeystore, err = keystore.New(w.opts.keystorePath, dynamic.RLNAppInfo, w.log) if err != nil { - return err + return err, nil, nil } } @@ -79,7 +79,7 @@ func (w *WakuNode) setupRLNRelay() error { w.log, ) if err != nil { - return err + return err, nil, nil } } @@ -93,7 +93,7 @@ func (w *WakuNode) setupRLNRelay() error { w.Relay().RegisterDefaultValidator(w.rlnRelay.Validator(w.opts.rlnSpamHandler)) - return nil + return nil, rlnInstance, groupManager.(*dynamic.DynamicGroupManager) } func (w *WakuNode) startRlnRelay(ctx context.Context) error { diff --git a/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go b/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go index 41970984..dfb6f91c 100644 --- a/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go +++ b/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go @@ -52,10 +52,10 @@ func (gm *DynamicGroupManager) handler(events []*contracts.RLNMemberRegistered, toRemoveTable := om.New() toInsertTable := om.New() - if gm.lastBlockProcessed == 0 { - gm.lastBlockProcessed = latestProcessBlock - } - lastBlockProcessed := gm.lastBlockProcessed + //if gm.lastBlockProcessed == 0 { + // gm.lastBlockProcessed = latestProcessBlock + //} + //lastBlockProcessed := gm.lastBlockProcessed for _, event := range events { if event.Raw.Removed { var indexes []uint @@ -74,9 +74,9 @@ func (gm *DynamicGroupManager) handler(events []*contracts.RLNMemberRegistered, eventsPerBlock = append(eventsPerBlock, event) toInsertTable.Set(event.Raw.BlockNumber, eventsPerBlock) - if event.Raw.BlockNumber > lastBlockProcessed { - lastBlockProcessed = event.Raw.BlockNumber - } + //if event.Raw.BlockNumber > lastBlockProcessed { + // lastBlockProcessed = event.Raw.BlockNumber + //} } } @@ -90,7 +90,9 @@ func (gm *DynamicGroupManager) handler(events []*contracts.RLNMemberRegistered, return err } - gm.lastBlockProcessed = lastBlockProcessed + // TODO: Commented code above is a bug I think. lastBlockProcessed was not being + // updated properly. + gm.lastBlockProcessed = latestProcessBlock err = gm.SetMetadata(RLNMetadata{ LastProcessedBlock: gm.lastBlockProcessed, ChainID: gm.web3Config.ChainID, @@ -310,6 +312,8 @@ func (gm *DynamicGroupManager) IsReady(ctx context.Context) (bool, error) { return false, fmt.Errorf("could not retrieve latest block: %w", err) } + fmt.Println("latestBlockNumber: ", latestBlockNumber, " gm.lastBlockProcessed: ", gm.lastBlockProcessed) + gm.lastBlockProcessedMutex.RLock() allBlocksProcessed := gm.lastBlockProcessed >= latestBlockNumber gm.lastBlockProcessedMutex.RUnlock() @@ -325,3 +329,7 @@ func (gm *DynamicGroupManager) IsReady(ctx context.Context) (bool, error) { return syncProgress == nil, nil // syncProgress only has a value while node is syncing } + +func GetRLN(gm *DynamicGroupManager) *rln.RLN { + return gm.rln +}