fix(drive-abci)!: cross-check the contested index of a prefunded voting balance at protocol v14 - #4281
fix(drive-abci)!: cross-check the contested index of a prefunded voting balance at protocol v14#4281QuantumExplorer wants to merge 1 commit into
Conversation
…ng balance at protocol v14
A document create transition supplies the index its prefunded voting
balance is for. That name keys the vote poll, its stored info, its
end-date entry and its prefunded specialized balance, while the contested
index tree the contender is inserted into always comes from
`DocumentType::find_contested_index`. Nothing tied the two together: the
transformer resolved whatever index was named, and the advanced structure
check validated only the amount, discarding the name.
On a contract whose non-contested index is a strict prefix of the
contested one, the mismatched vote poll's contenders path is a tree the
contested insert creates anyway, so the transition executes and the poll
registers one level above the contest it describes. When that poll ends,
`check_for_ended_vote_polls` finds no lock/abstain tallies and returns
`Err(CorruptedCodeExecution("expected a locked tally"))`, which propagates
through `run_dao_platform_events` -> `run_block_proposal` on a bare `?`
and halts every validator; the end-date entry is never cleaned up, so
every subsequent proposal fails the same way.
The same miss also hides a running contest from the transition: the stored
info is looked up under the supplied vote poll, so a wrong index name
returns `None` and skips the locked, join-window and already-a-contestant
checks. In the other direction, a document resolving to no contested index
could still open a contest and be stored as a contender.
Structure validation v1 compares the whole expected vote poll against the
supplied one and rejects a prefunded voting balance on a non-contested
document, reporting `DocumentContestIndexMismatchError` (40118) and
`DocumentContestNotRequiredError` (40119). It is gated by
`DRIVE_ABCI_VALIDATION_VERSIONS_V10` on protocol version 14; v9 stays
byte-identical for protocol version 13 chain replay.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesDocument contest validation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant DocumentCreateTransitionAction
participant DocumentCreateStructureValidationV1
participant ConsensusError
participant VotePollResolver
DocumentCreateTransitionAction->>DocumentCreateStructureValidationV1: validate prefunded contest index
DocumentCreateStructureValidationV1->>ConsensusError: reject mismatched index
ConsensusError-->>DocumentCreateTransitionAction: DocumentContestIndexMismatchError
DocumentCreateTransitionAction-->>VotePollResolver: no vote poll registered
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
⛔ Blockers found — Sonnet deferred (commit e768c06) |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v10.rs (1)
332-340: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFix the stale denomination comment.
Line 332 lists "0.1, 0.3, 0.5, 1.0 DASH", but the array holds 0.03, 0.1, 0.25, 0.5 and 1 DASH. Line 333 already states that 0.3 was retired, so the two comment lines contradict each other. Update the first line to match the values.
📝 Proposed comment fix
- // 0.1, 0.3, 0.5, 1.0 DASH in credits (1 DASH = 10^8 duffs, CREDITS_PER_DUFF = 1000). + // 0.03, 0.1, 0.25, 0.5, 1.0 DASH in credits (1 DASH = 10^8 duffs, CREDITS_PER_DUFF = 1000). // v13 revises the v8 set: adds 0.03 and 0.25 DASH, retires 0.3 DASH.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v10.rs` around lines 332 - 340, Update the leading denomination comment above shielded_identity_create_denominations to list 0.03, 0.1, 0.25, 0.5, and 1.0 DASH, matching the array values and the existing v13 revision note.packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs (1)
86-96: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider reporting the full mismatch, not only the index names.
The comparison at line 87 covers the whole
ContestedDocumentResourceVotePoll:contract_id,document_type_name,index_nameandindex_values. The error only carries the two index names. If the poll differs only inindex_valuesordocument_type_name, the emitted error reports the same string forexpected_index_nameandprovided_index_name, which is hard to act on.Two options: keep the struct comparison and extend the error payload, or narrow the check to
index_nameand let the existing per-field validations cover the other fields.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs` around lines 86 - 96, The ContestedDocumentResourceVotePoll mismatch handling around the provided and expected values reports only index names, obscuring differences in other fields. Update DocumentContestIndexMismatchError and its construction to carry the full expected and provided poll details, or narrow this check to index_name so document_type_name and index_values are handled by their existing validations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/rs-dpp/src/errors/consensus/state/state_error.rs`:
- Around line 101-105: Move the new DocumentContestIndexMismatchError and
DocumentContestNotRequiredError variants to the end of the StateError enum,
preserving the existing order of all prior variants and therefore their bincode
discriminants.
---
Nitpick comments:
In
`@packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rs`:
- Around line 86-96: The ContestedDocumentResourceVotePoll mismatch handling
around the provided and expected values reports only index names, obscuring
differences in other fields. Update DocumentContestIndexMismatchError and its
construction to carry the full expected and provided poll details, or narrow
this check to index_name so document_type_name and index_values are handled by
their existing validations.
In
`@packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v10.rs`:
- Around line 332-340: Update the leading denomination comment above
shielded_identity_create_denominations to list 0.03, 0.1, 0.25, 0.5, and 1.0
DASH, matching the array values and the existing v13 revision note.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 1114ecc6-730b-4dc6-924f-dfa7d60d902c
📒 Files selected for processing (14)
packages/rs-dpp/src/errors/consensus/codes.rspackages/rs-dpp/src/errors/consensus/state/document/document_contest_index_mismatch_error.rspackages/rs-dpp/src/errors/consensus/state/document/document_contest_not_required_error.rspackages/rs-dpp/src/errors/consensus/state/document/mod.rspackages/rs-dpp/src/errors/consensus/state/state_error.rspackages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/advanced_structure_v1/mod.rspackages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_create_transition_action/mod.rspackages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/creation.rspackages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rspackages/rs-drive-abci/tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-and-prefix-index.jsonpackages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/mod.rspackages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v10.rspackages/rs-platform-version/src/version/v14.rspackages/wasm-dpp/src/errors/consensus/consensus_error.rs
| #[error(transparent)] | ||
| DocumentContestIndexMismatchError(DocumentContestIndexMismatchError), | ||
|
|
||
| #[error(transparent)] | ||
| DocumentContestNotRequiredError(DocumentContestNotRequiredError), |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 'enum StateError|derive\(.*Encode|derive\(.*Decode|StateError.*serialize|serialize.*StateError|deserialize.*StateError' \
packages/rs-dpp packages/rs-platform-version
rg -n -C 3 'STATE_TRANSITION_SERIALIZATION_VERSIONS_V2|STATE_TRANSITION_SERIALIZATION_VERSIONS_V[0-9]+' \
packages/rs-platform-versionRepository: dashpay/platform
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== state_error.rs outline and relevant section =="
wc -l packages/rs-dpp/src/errors/consensus/state/state_error.rs
sed -n '1,180p' packages/rs-dpp/src/errors/consensus/state/state_error.rs
echo
echo "== targeted version constants =="
rg -n -C 3 'STAT.*STATE_ERROR|STATE_ERROR_SERIALIZATION|STATE_TRANSITION_SERIALIZATION_VERSIONS_V2|STATE_TRANSITION_SERIALIZATION_VERSIONS_V14|V14' packages/rs-platform-version || true
echo
echo "== StateError usages in platform-version =="
rg -n -C 3 'StateError' packages/rs-platform-version || trueRepository: dashpay/platform
Length of output: 26668
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== state_error.rs variants =="
python3 - <<'PY'
from pathlib import Path
p = Path("packages/rs-dpp/src/errors/consensus/state/state_error.rs")
text = p.read_text()
variants = []
enum = None
start = text.index("\n#[derive(")
for line in text[start:].splitlines():
if line.strip().startswith("pub enum "):
enum = line.strip()
elif enum and line.strip().startswith("#[error("):
variant = next(s.lstrip() for s in text[text.index(line):].splitlines() if s.strip().endswith(","))
variants.append(variant.replace(" ", ""))
print("variants:", "\n".join(variants))
print("count:", len(variants))
matches = []
for i, v in enumerate(variants, 1):
if "DocumentContest" in v or "DocumentContestIndexMismatch" in v or "DocumentContestNotRequired" in v:
print(f"{i}: {v}")
for j, vn in enumerate(variants, 1):
if vn > v:
matches.append((i, j, v, vn))
print("next_variant_count:", len(variants) - matches[0][0] if matches else "-")
print("next_variant_pairs:")
for a,b,c,d in matches:
print(f" after {a}: {b}: {d}")
PY
echo
echo "== state_error_ser definitions/usages =="
rg -n -C 5 'struct StateError|StateErrorSer|StateErrorDeserialize|PlatformSerialize|PlatformDeserialize|BinaryProtocol|protocol_version|format_version.*StateError|StateError.*protocol' packages/rs-dpp/packages/platform packages/rs-dpp/src || true
echo
echo "== state transition serialization constants full =="
sed -n '1,140p' packages/rs-platform-version/src/version/dpp_versions/dpp_state_transition_serialization_versions/v2.rsRepository: dashpay/platform
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== targeted state_error.rs sections =="
sed -n '1,180p' packages/rs-dpp/src/errors/consensus/state/state_error.rs
sed -n '180,371p' packages/rs-dpp/src/errors/consensus/state/state_error.rs
echo
echo "== targeted usages of StateErrorSer/StateErrorDeserialize/state_error serialization =="
rg -n -C 4 'StateErrorSer|StateErrorDeserialize|StateError\\[' packages/rs-dpp/packages packages/rs-dpp/src 2>/dev/null | head -n 200 || true
echo
echo "== derive macros and platform_serialize behavior references =="
rg -n -C 3 'derive\\(Platform|PlatformSerialize|PlatformDeserialize|#\\[platform_serialize|fn encode|fn decode' packages platform crates platform-serial 2>/dev/null | head -n 300 || true
echo
echo "== focused platform serialization derives =="
find . -path ./target -prune -o \( -path '*/platform_serialization_derive/src/*' -o -path '*/platform-serialization-derive/*' -o -path '*/platform-serial/*' -o -path '*/platform-serialization/*' \) -type f -print | sed -n '1,120p'Repository: dashpay/platform
Length of output: 17860
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Cargo references for platform_serialization_derive =="
rg -n 'platform_serialization_derive|platform-serialization|platform-serial' Cargo.toml Cargo.lock packages 2>/dev/null | head -n 120 || true
echo
echo "== repository files mentioning PlatformSerialize derive implementation =="
rg -n 'PlatformSerialize|platform_serialize|enum_variant_names|PlatformDeserialize|decode_enum|encode_enum' . \
--glob '!target/**' --glob '!docs/**' --glob '!*.log' \
| head -n 250 || true
echo
echo "== inspect source around PlatformSerialize attribute syntax from nearby error enums =="
rg -n -C 2 '#\\[platform_serialize\\(' packages/rs-dpp/src/errors | head -n 120 || true
echo
echo "== inspect bincode derive usage around StateError =="
grep -n 'bincode::Decode, Decode, PlatformDeserialize\\|Decode, PlatformDeserialize' packages/rs-dpp/src/errors/consensus/state/state_error.rs || true
echo
echo "== git diff stat and relevant diff for state_error if available =="
git diff -- packages/rs-dpp/src/errors/consensus/state/state_error.rs | sed -n '1,200p' || trueRepository: dashpay/platform
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== platform serialization enum derive =="
cat -n packages/rs-platform-serialization-derive/src/serialize_enum.rs
cat -n packages/rs-platform-serialization-derive/src/deserialize_enum.rs
echo
echo "== platform serialization lib dispatch =="
sed -n '88,185p' packages/rs-platform-serialization-derive/src/lib.rs
echo
sed -n '35,180p' packages/rs-platform-serialization/src/lib.rs
echo
echo "== enum_variant_names version in lockfile =="
rg -n 'name = "enum_variant_names"|enum_variant_names' Cargo.lock | head -n 10 || true
echo
echo "== StateError variant ordering as byte offsets in enum shape =="
python3 - <<'PY'
from pathlib import Path
p = Path("packages/rs-dpp/src/errors/consensus/state/state_error.rs")
text = p.read_text().splitlines()
start = next(i for i,l in enumerate(text) if l.strip().startswith('pub enum StateError'))
variants = []
idx = None
for i, line in enumerate(text[start:], start):
if line.strip().startswith('#['):
idx = i
elif idx is not None and '#[' not in line and '///' not in line and '*/' not in line and line.strip():
prefix = line.lstrip()
name = prefix.split('(', 1)[0].strip()
variants.append((name, start + idx + 1))
idx = None
for i, v in enumerate(variants, 1):
if 'DocumentContest' in v[0] or 'DocumentNotFoundError' in v[0] or 'DocumentNotForSaleError' in v[0] or 'Wrong' in v[0]:
print(f"{i}: {v[1]} {v[0]}")
PYRepository: dashpay/platform
Length of output: 35689
Append the new StateError variants instead of inserting them mid-variant list.
StateError is bincode::Encode/bincode::Decode and has the enum “do not change order of variants” constraint. V14 still uses STATE_TRANSITION_SERIALIZATION_VERSIONS_V2, so moving DocumentContestIndexMismatchError and DocumentContestNotRequired_ERROR shifts the encoded discriminants for every variant that follows.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/rs-dpp/src/errors/consensus/state/state_error.rs` around lines 101 -
105, Move the new DocumentContestIndexMismatchError and
DocumentContestNotRequiredError variants to the end of the StateError enum,
preserving the existing order of all prior variants and therefore their bincode
discriminants.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## v4.2-dev #4281 +/- ##
==========================================
Coverage 87.54% 87.54%
==========================================
Files 2679 2680 +1
Lines 341312 341697 +385
==========================================
+ Hits 298799 299145 +346
- Misses 42513 42552 +39
🚀 New features to boost your workflow:
|
thepastaclaw
left a comment
There was a problem hiding this comment.
Preliminary review — Codex only
The protocol-v14 vote-poll cross-check is correctly version-dispatched and has strong regression coverage. One blocking compatibility issue remains: the new StateError variants were inserted into a positionally serialized enum, shifting the wire discriminants of all existing variants after the insertion point. The pre-2080 Testnet carve-out does not expose the fix on a reachable historical configuration because validation v1 exists only at protocol v14, while the carve-out is intentional compatibility behavior covered by an existing test.
Source: reviewers codex/general=gpt-5.6-sol(completed); codex/security-auditor=gpt-5.6-sol(completed); codex/rust-quality=gpt-5.6-sol(completed); codex/ffi-engineer=gpt-5.6-sol(completed); verifier=codex/verifier=gpt-5.6-sol(completed); coordinator=openclaw-agent/cliproxy/gpt-5.6-sol(orchestration-only).
Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.
Review provenance
- Codex reviewers:
gpt-5.6-sol— general (completed),gpt-5.6-sol— security-auditor (completed),gpt-5.6-sol— rust-quality (completed),gpt-5.6-sol— ffi-engineer (completed) - Verifier:
gpt-5.6-sol— verifier - Sonnet: not run (deferred by blocker gate)
🔴 1 blocking
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `packages/rs-dpp/src/errors/consensus/state/state_error.rs`:
- [BLOCKING] packages/rs-dpp/src/errors/consensus/state/state_error.rs:101-105: Append the new StateError variants to preserve wire discriminants
StateError is encoded by ordinal variant position: its bincode and platform-serialization implementations write the variant index as a u32, and the enum explicitly prohibits reordering without a new serialization version. Inserting these variants here changes DocumentNotFoundError from ordinal 8 to 10 and shifts every subsequent existing variant by two. ErrorWithCode values do not protect this representation. Consensus errors are serialized into ABCI/DAPI responses and decoded by WASM and JavaScript clients, so upgraded and older components can fail decoding or interpret an existing error as a different variant. Move both new variants after InsufficientShieldedFeeError so every pre-existing discriminator remains unchanged; add a frozen-byte compatibility test for an existing variant after the current insertion point.
| #[error(transparent)] | ||
| DocumentContestIndexMismatchError(DocumentContestIndexMismatchError), | ||
|
|
||
| #[error(transparent)] | ||
| DocumentContestNotRequiredError(DocumentContestNotRequiredError), |
There was a problem hiding this comment.
🔴 Blocking: Append the new StateError variants to preserve wire discriminants
StateError is encoded by ordinal variant position: its bincode and platform-serialization implementations write the variant index as a u32, and the enum explicitly prohibits reordering without a new serialization version. Inserting these variants here changes DocumentNotFoundError from ordinal 8 to 10 and shifts every subsequent existing variant by two. ErrorWithCode values do not protect this representation. Consensus errors are serialized into ABCI/DAPI responses and decoded by WASM and JavaScript clients, so upgraded and older components can fail decoding or interpret an existing error as a different variant. Move both new variants after InsufficientShieldedFeeError so every pre-existing discriminator remains unchanged; add a frozen-byte compatibility test for an existing variant after the current insertion point.
source: ['codex']
Issue being fixed or feature implemented
A document create transition carries the index its prefunded voting balance is for, in
DocumentCreateTransitionV0::prefunded_voting_balance: Option<(String, Credits)>. That name is what keys the contested vote poll, its stored info, its end-date entry and its prefunded specialized balance — but the contested index tree the contender is actually inserted into always comes fromDocumentType::find_contested_index(). Nothing tied the two together:document_type_indexes.get(index_name));if let Some((_, paid_amount)) = ...).Three consequences, all verified against the pre-fix code by flipping only the validation version and re-running the tests added here:
1. Reachable chain halt. On a contract whose non-contested index is a strict prefix of the contested one (e.g.
[normalizedParentDomainName]next to the contested[normalizedParentDomainName, normalizedLabel]), the mismatched vote poll's contenders path is a tree the contested insert creates anyway. The transition therefore executes successfully and registers the poll one level above the contest it describes. When that poll's end date arrives, the tally query finds no lock/abstain sum trees andcheck_for_ended_vote_pollsreturnsErr(Drive(CorruptedCodeExecution("expected a locked tally"))). That is a per-block event handler with a bare?(run_dao_platform_events→run_block_proposal), so it is not contained into a per-state-transition result — it halts every validator at that block. The end-date entry is never cleaned up, so every subsequent block proposal fails identically. Any user can register such a contract.2. Contest checks silently skipped.
current_store_contest_infois fetched under the vote poll built from the submitter's own index name, so a mismatched name makes that lookup returnNone, andstate_v0/state_v1then skip theLocked, join-window and already-a-contestant checks entirely while the contender is still inserted into the real contest.3. Unnecessary contests. In the other direction, a document that resolves to no contested index at all could still supply a prefunded voting balance, storing an ordinary document as a contender that only becomes registered if it wins a masternode vote.
On the live DPNS contract the mismatch currently dies for an unrelated reason:
Index::extract_valuesis a flat map lookup, so theidentityIdindex's dottedrecords.identityproperty yieldsValue::Null→ an empty path segment → an unpaidInternalErrorfrom the estimated-cost path cache. That is an accident of DPNS's index shape, not a defense.What was done?
document_create_transition_structure_validationv1 (newadvanced_structure_v1module) compares the full expectedContestedDocumentResourceVotePoll— derived from the document's own properties viacontested_vote_poll_for_document_properties— against the one built from the prefunded voting balance, and rejects a prefunded voting balance on a document that resolves to no contested index. Everything else is carried over from v0 unchanged, including the testnet-before-epoch-2080 carve-out.Two new consensus errors:
DocumentContestIndexMismatchErrorDocumentContestNotRequiredErrorThe check changes accept/reject behavior, so it is gated by a new
DRIVE_ABCI_VALIDATION_VERSIONS_V10wired intov14.rs.DRIVE_ABCI_VALIDATION_VERSIONS_V9is shared with protocol version 13 and stays byte-identical for chain replay;advanced_structure_v0is untouched.How Has This Been Tested?
Five unit tests on the validation dispatcher (
advanced_structure_v1::tests): mismatched index name, prefunded balance on a non-contested document, the preserved not-paid-for behavior, the accepted matching case, and a test pinning that protocol version 13 does not cross-check — the last one is what documents the gap this PR closes.Four end-to-end tests through
process_raw_state_transitions:test_document_creation_on_contested_unique_index_should_fail_if_prefunding_another_index— DPNS contest funded foridentityId; asserts the paid consensus error and that neither a contender nor a document was created.test_contest_can_not_be_joined_after_the_join_window_by_prefunding_another_index— opens a real contest, advances past the one-week join window, and shows a mismatched late join is rejected and the contest keeps exactly its two contenders.contested_index_mismatch_chain_halt::prefunding_a_prefix_index_is_rejected— the halt scenario above, on a newdpns-contract-contested-unique-index-and-prefix-index.jsonfixture. Run against structure validation v0 this same test shows the transition executing and the resolver returningErr(... "expected a locked tally").contested_index_mismatch_chain_halt::prefunding_the_contested_index_resolves_successfully— control: the identical contest funded for the contested index executes and its poll resolves cleanly, so the index name is the only variable.Suites:
cargo test -p drive-abci --lib2638 passed / 0 failed,cargo test -p dpp3808 passed / 0 failed,cargo check --workspace --all-targetsclean,cargo clippyclean on the touched crates, andcargo check -p wasm-dpp --target wasm32-unknown-unknownclean.Worth knowing for anyone extending these tests: the default test
PlatformConfignetwork is Testnet, where the whole contested structure-validation block is skipped before epoch 2080 — a contested-validation test that forgetsnetwork: Network::Mainnetpasses while proving nothing.Breaking Changes
Consensus-breaking, gated at protocol version 14. From v14 on, a create transition whose prefunded voting balance names an index other than the contested index its document resolves to, or that supplies one for a non-contested document, is rejected with a paid consensus error instead of being accepted. Clients that derive the field through
DocumentType::prefunded_voting_balance_for_document(which returns the contested index) are unaffected. Protocol version 13 and below replay unchanged.Checklist:
For repository code-owners and collaborators only
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests