Tests: Precompile Tests Phase 3 (PLT-372) - #3985
Conversation
Extends the precompile suite to the non-wasm precompiles generated for v6.7 and to the methods added to the existing ones since phase 2, so the suite tracks the deployed interface rather than the phase 2 snapshot. New specs: auth (0x100D), authz (0x100E), evidence (0x100F), mint (0x1012), params (0x1013), slashing (0x1014), upgrade (0x1015). Existing specs grow to cover the scoped module authorizations and the query surface they shipped alongside: bank spendableBalances/totalSupply/ params/denomMetadata/denomsMetadata; staking grant/delegate/redelegate/ undelegate/revokeWithAuthorization plus the remaining delegation and validator queries; gov vote and proposal authorizations plus proposal/votes/deposits/tally; distribution withdrawValidatorCommission, the withdraw authorizations, and the validator/delegator queries. Framework: the new addresses join PRECOMPILE_ADDRESSES, cosmosUtils gains a cosmosRest LCD reader for the modules cosmjs does not wrap, and the bootstrap pool grows 48 -> 80 with the claim budget recorded. Wasm-gated flows (wasmd, pointer addCW*, solo), ibc, and the unregistered feegrant precompile stay out of scope. Co-Authored-By: Cursor <cursoragent@cursor.com>
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d1c4802. Configure here.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3985 +/- ##
==========================================
- Coverage 58.98% 57.88% -1.10%
==========================================
Files 2310 2211 -99
Lines 197500 185930 -11570
==========================================
- Hits 116487 107626 -8861
+ Misses 70256 68486 -1770
+ Partials 10757 9818 -939
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Phase 3 of the precompile suite is well grounded — every new address, ABI field name, LCD gateway path and Go revert string I spot-checked matches the deployed interface, and the claimPool budget note (29) is accurate. One new assertion in slashing.spec.ts compares two unpinned reads of per-block-mutating signing info and will fail intermittently; two smaller test-strength notes are non-blocking.
Findings: 1 blocking | 2 non-blocking | 3 posted inline
Blockers
- None at the file/PR level.
- 1 blocking issue(s) flagged inline on specific lines.
Non-blocking
- None at the file/PR level.
- 2 suggestion(s)/nit(s) flagged inline on specific lines.
| ); | ||
| const first = listed.signingInfos[0]; | ||
| const viaOne: ethers.Result = await slashing.signingInfo(first.validatorAddress); | ||
| expect(asSigningInfo(viaOne)).to.deep.equal(asSigningInfo(first)); |
There was a problem hiding this comment.
[blocker] signingInfos() and signingInfo() are two separate eth_calls at latest, and the fields being compared move every block: HandleValidatorSignature bumps signInfo.IndexOffset for every validator in the last commit (sei-cosmos/x/slashing/keeper/infractions.go:72), and MissedBlocksCounter can change too. Whenever a block commits between the two calls this deep.equal fails on indexOffset — an intermittent CI failure with no real defect behind it.
The suite already has the fix pattern for exactly this: distribution.spec.ts pins both reads to one height ("Rewards grow every block — pin both reads to the same height or the byte-equality below races block production"). Take const blockTag = await provider.getBlockNumber() and pass { blockTag } to both calls, or restrict asSigningInfo to the fields that don't move (validatorAddress, startHeight, jailedUntil, tombstoned).
| // Supply can grow between reads (block rewards / mint); bracket the | ||
| // paginated totalSupply read with two supply(usei) reads. | ||
| const before: bigint = await bank.supply('usei'); | ||
| const [coins] = (await bank.totalSupply(new Uint8Array())) as [ |
There was a problem hiding this comment.
[suggestion] totalSupply(empty) reads only the first page (the precompile passes PageRequest{Key: …} with no limit, so the module default of 100 applies). Supply is keyed by denom, and factory/… sorts before usei, so on a chain that has accumulated ~100 tokenfactory denoms — one per run of the denomMetadata test below, plus anything else on the chain — usei falls off page one and this fails with "totalSupply must contain a usei entry" rather than a real defect. The denomsMetadata test right below already walks pages for this reason; consider doing the same here (or at least noting the single-page assumption).
|
|
||
| type DistrCoin = { amount: bigint; decimals: bigint; denom: string }; | ||
|
|
||
| function expectCoinArray(coins: readonly DistrCoin[], label: string): void { |
There was a problem hiding this comment.
[suggestion] expectCoinArray degenerates to to.be.an('array') when the array is empty — the per-coin loop never runs — which is the shape the PR description calls out as removed elsewhere ("bank.denomsMetadata asserting only 'is an array'"). Three of the four call sites have a fixture that guarantees content (the delegator has an accruing delegation to validator, which is bonded), so asserting coins.length > 0 there would make them able to fail. communityPool is the one where emptiness is genuinely chain-dependent (community_tax may be 0), so it's worth either asserting non-empty explicitly or saying in the label why it can't be.
slashing: signingInfos and signingInfo were two eth_calls at latest, and HandleValidatorSignature advances indexOffset for every validator in the last commit, so a block committing between them failed the equality with no defect behind it. Both reads now pin the same blockTag, which keeps the full-struct comparison rather than dropping the moving fields. bank: totalSupply sends no page limit, so the module default of 100 applies and every factory/... denom this suite mints sorts before usei. It now walks pages the way denomsMetadata already does instead of assuming usei is on page one. distribution: expectCoinArray degenerated to an "is an array" check on an empty array. It now requires non-empty by default, which the fixture guarantees for the delegation and validator reads, and communityPool opts out because sei-cosmos defaults community_tax to 0. gov: the dispatch section claimed gov has no view methods while a new case asserted proposal() answers under STATICCALL. The executor dispatches its queries before the readOnly check, so the label now says transaction methods, and the duplicate vote rejection that mined the same failing tx twice is gone. Co-Authored-By: Cursor <cursoragent@cursor.com>
|
Addressed all three of @seidroid's findings, plus the Bugbot note, in f996dac. Each claim was re-verified against the source rather than taken on the review's word. [blocker] [suggestion] [suggestion] One correction to the reasoning, since the conclusion is right but the mechanism isn't: the accruing delegation does not by itself guarantee [Bugbot, low]
|

Describe your changes and provide context
Phase 3 of the precompile test refactor (PLT-372), building on the phase 1 framework (#3777) and the phase 2 coverage (#3786).
Phase 2 covered the wasm-free precompiles as they existed then. Since that merged,
Generate v6.7 precompiles(#3961), the scoped module authorizations (#3893, #3846) and the module Msg rpc surface (#3825) landed. This phase brings the suite back in line with the deployed interface.New specs
auth.spec.tsaccount/accounts/params/nextAccountNumber, LCD parity incl. the nested base account module accounts wrap theirs inauthz.spec.tsgrants/granterGrants/granteeGrants; the non-empty fixture is astaking.grantStakingAuthorization, since authz itself cannot grantevidence.spec.tsevidence/allEvidenceagainst the module's own listmint.spec.tsparams/mintervs Sei's own/seichain/mint/v1beta1/...routesparams.spec.tsparams(subspace, key)vs the staking module, plus the unknown/empty subspace revertsslashing.spec.tsparams/signingInfo/signingInfos, grant + revoke of unjail authorization, and the unjail guardsupgrade.spec.tscurrentPlan/appliedPlan/upgradedConsensusState/moduleVersionsExisting specs extended
spendableBalances,totalSupply,params,denomMetadata(via a tokenfactory denom),denomsMetadata(pages until it reaches that denom, sonextKeyround-trips).grantStakingAuthorization→delegateWithAuthorization→ Cosmos-side delegation →revokeStakingAuthorization→ reverts;redelegate/undelegateWithAuthorization; and the remaining query surface (validators,validatorDelegations,unbondingDelegation,delegatorDelegations,redelegations,historicalInfo, …).grant/voteWithAuthorization/submitProposalWithAuthorization/revoke), plusproposal,proposals,getVote,votes,params,getDeposit,deposits,tallyResult.withdrawValidatorCommission, the withdraw authorizations, andparams,validatorOutstandingRewards,validatorCommission,validatorSlashes,delegationRewards,delegatorValidators,delegatorWithdrawAddress,communityPool.Framework
PRECOMPILE_ADDRESSES.cosmosUtils.cosmosRest— an LCD reader for the modules cosmjs does not wrap (mint, params, upgrade, evidence, slashing, authz, auth). Every path was taken from the module's decodedpb.gw.gogateway pattern rather than guessed, and every response field from its proto tag; the API server marshals withOrigName+EmitDefaults, so the reads are snake_case.claimPoolbudget (29) recorded next to it — a non-blocking note from the phase 2 review.Out of scope, unchanged: wasm-gated flows (wasmd, pointer
addCW*, solo), the ibc precompile, and feegrant (0x…1010), which is not registered and whose module was removed in #3958. oracle stays the phase 2 retirement assertion.Testing performed to validate your change
npx tsc --noEmitclean; no lint errors.historicalInfopinning an unreachable executor fallback instead of the querier'shistorical info for height %d not found;nextAccountNumberreadingnext_account_numberwhere the response field iscount; and mint's LCD path, which was three guesses deep with the real route last.historicalInfotry/catch asserting success in both branches,evidencehardcoding an empty chain in four places,authzindex-matching expirations across two independently ordered lists,bank.denomsMetadataasserting only "is an array" — and replaced receipt-only verification of the authorized reward withdrawal with theDelegationRewardsWithdrawncredit its non-authz sibling already pins.Not yet run against a live cluster. The remaining risk is concentrated in real chain responses, so this wants a
make docker-cluster-start+npm run precompile:cirun before merge; I will post the result on this PR.🤖 Generated with Cursor
Made with Cursor