Conversation
- Guard the HTTP tree fetcher against SSRF via redirects by re-validating every redirect target against the HTTPS scheme and host allowlist - Apply both after and before cursors in GetMerkleRewards instead of dropping before when after is set - Reject MerkleDistributor event poolId/week arguments that overflow int64 - Put the MERKLE_DISTRIBUTOR_ADDR TODO comment on its own line in settings.sample.yaml
Review fixes for the Merkle claims indexing feature: - Replace the per-leaf claim Upsert loop in handleRootSet with a single INSERT ... SELECT unnest(...) ON CONFLICT statement; claimed_at and claim_tx are still preserved on conflict since they are not in the SET list. Covered by a new 150-leaf handler test that also exercises redelivery through the conflict path. - Replace the single-column merkle_claims account index with a composite (account, pool_id, epoch DESC) index matching the merkleRewards query and cursor order, and add a partial index on account WHERE claimed_at IS NULL for the unclaimed-by-account hot path. Indexes do not affect SQLBoiler models, so no regen needed. - Resolve MerkleReward.pool through a new MerklePoolByID dataloader instead of one query per reward row. - Log fetchDurationMs and dbWriteDurationMs when a root is set, and document why the tree-file/event root mismatch path intentionally returns an error (Kafka redelivery is the operator alert). - Hoist the tree fetcher's 10s timeout into merkle.FetchTimeout with a comment noting it spans the full body read of up to 50 MiB. - Document and pin the RootSet root wire format: even as an indexed parameter, bytes32 decodes via abi.ParseTopicsIntoMap/toGoType to a [32]byte and serializes as a JSON number array (verified against go-ethereum v1.15.11, contract-event-processor's version), so the Root field stays [32]byte and the consumer needs no wire change.
rewards(user).totalTokens now merges legacy per-device reward sums with merkle_claims amounts for the account, so user-level earnings keep growing after the merkle cutover. Merkle amounts count as earned at RootSet, claimed or not. History edges and totalCount stay legacy-only; per-epoch merkle data lives in the merkleRewards query.
|
Added in latest push: |
Brings feature/merkle-claims-indexing (cf6065c, 11 commits) into r2-device-definitions with its history intact. Conflict resolution: - settings.sample.yaml was the only textual conflict. Kept r2's DEFINITIONS_CATALOG_URL in place of TABLELAND_API_GATEWAY (that setting no longer exists in config.Settings), kept ETHEREUM_RPC_URL, and appended #186's MERKLE_DISTRIBUTOR_ADDR and MERKLE_TREE_ALLOWED_HOST with their comments. Files changed on both sides that git merged cleanly, checked by hand: - internal/config/settings.go: DefinitionsCatalogURL and DefinitionsMinCount from r2, MerkleDistributorAddr and MerkleTreeAllowedHost from #186. - internal/services/contracts_events_consumer.go: r2's connection SACD and PermissionsRenounced handling alongside #186's merkle handler, the shared HTTP client (merkle.FetchTimeout, still 10s) and the MerkleDistributor case in the contract address switch. - graph/resolver.go: connectionsacd and merkle repositories both wired. - gqlgen.yml: Connection.sacds resolver and the MerklePool/MerkleReward resolvers both present. - models/boil_table_names.go: ConnectionSacds plus MerkleClaims, MerklePools and MerkleRoots. - charts/identity-api/values.yaml and values-prod.yaml: r2's image tags and DEFINITIONS_CATALOG_URL/DEFINITIONS_MIN_COUNT plus #186's MERKLE_DISTRIBUTOR_ADDR/MERKLE_TREE_ALLOWED_HOST. Regeneration: - go run github.com/99designs/gqlgen generate (v0.17.89, pinned in go.mod) reproduced graph/generated.go, graph/model/models_gen.go and the resolver files byte for byte, so they are committed as merged. - go -C graph run github.com/DIMO-Network/server-garage/cmd/mcpgen -schema ./schema/ -prefix identity -out mcp_tools_gen.go -package graph (the go:generate directive in graph/resolver.go), and the go mod tidy that gqlgen runs, produce changes r2-device-definitions already needed before this merge. Those land in the next commit. - sqlboiler models are not regenerated (needs a live database); boil_table_names.go lists both sides' tables. Migration check: - r2-device-definitions carries migrations/00049_create_connection_sacds.sql (#185, on main, released in v0.6.6 and v0.6.7). #186 adds migrations/00049_create_merkle_tables.sql, which was never built from main. goose rejects duplicate versions, so a follow-up commit renumbers the merkle migration to 00050.
- graph/mcp_tools_gen.go: rerun mcpgen (go:generate in graph/resolver.go). CondensedSchema now includes Connection.sacds and VehiclesFilter.connection, which this branch added to the schema without regenerating. The merkle additions were already present. - go.mod, go.sum: go mod tidy, which gqlgen generate runs. Drops goqu, httpmock, retry-go and x/time, which nothing imports since the Tableland client was removed, and records go-sqlmock as indirect. go mod tidy on this branch before the merge gives the same diff. - internal/config/settings.go: gofmt -s. The Settings struct has been misaligned since DefinitionsMinCount was added.
|
Reopened. Folding this into #188 was a mistake: #188 is scoped to device definitions and has been reset to its pre-merge commit, so none of this PR's code is in it. This branch is unchanged. Before merging, note that migrations/00049_create_merkle_tables.sql collides with 00049_create_connection_sacds.sql, which is now on main, so it needs renumbering to 00050. |
Summary
proofsURI(HTTPS-only, host allowlist, redirect-guarded, 50MB cap), recomputes the merkle root from leaves and verifies it byte-equal to the on-chain root before serving anything, then upserts all leaves+proofs in one transaction (claim state preserved on root overwrite).SELECT FOR UPDATE(Kafka redelivery safe).merklePools,merklePool(poolId),merkleRewards(account, poolId, claimed)— Relay connections; serves everything a client needs to callclaim()/claimBatch().pkg/merkletreecopied from rewards-api (swap to module import once published).Companion PRs: dimo-rewards (contract), rewards-api (producer), contract-event-processor (config), transactions (SDK), dimo-driver (UI).
Test plan
go test ./...exit 0MERKLE_DISTRIBUTOR_ADDR+ tree host after deployment (placeholders in chart values)