-
Notifications
You must be signed in to change notification settings - Fork 44
Description
Expected Behavior
When creating a data contract with keeps_history: true using the evo-sdk, the contract should be successfully created and the SDK should receive and verify the proof of creation without errors.
Current Behavior
Contract creation with history enabled succeeds on the network (the state transition is applied and the contract is stored), but the SDK throws a proof verification error:
Something went wrong:
incorrect proof error: proof did not contain contract with id 9G2kkdnRC3VTBRUUj65mY4XCreXDENNRGpvik1xZ4v7s expected to exist because of state transition (create)
The contract can be seen on platform explorer, proving it exists on the network. Only the client-side proof verification fails.
Possible Solution
The bug is caused by a mismatch between the proof query path and the contract storage path for history-enabled contracts:
Root Cause
1. Proof Generation Bug (packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs:48-49)
The proof generation for DataContractCreate state transitions always uses the non-historical path query:
StateTransition::DataContractCreate(st) => {
contract_ids_to_non_historical_path_query(&st.modified_data_ids())
}This should check if the contract keeps history and use the appropriate query:
- If
st.data_contract().config().keeps_history()is true: usecontract_ids_to_historical_path_query() - Otherwise: use
contract_ids_to_non_historical_path_query()
2. Verification Logic Issue (packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs:69-79)
The verification passes None for contract_known_keeps_history:
let (root_hash, contract) = Drive::verify_contract(
proof,
None, // ← Should pass Some(keeps_history) from the state transition
false,
true,
data_contract_create.data_contract().id().into_buffer(),
platform_version,
)?;This should be changed to:
Some(data_contract_create.data_contract().config().keeps_history())Why This Happens
- Contracts with history are stored at
contract_keeping_history_root_path()with time-encoded keys - Contracts without history are stored at
contract_root_path()with key[0] - The proof is generated with the wrong path (always non-historical)
- The verifier defaults to non-historical when
contract_known_keeps_historyisNone - Result: The proof doesn't contain the contract data at the expected path
Proposed Fix
File 1: packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs
Change lines 48-49 from:
StateTransition::DataContractCreate(st) => {
contract_ids_to_non_historical_path_query(&st.modified_data_ids())
}To:
StateTransition::DataContractCreate(st) => {
if st.data_contract().config().keeps_history() {
contract_ids_to_historical_path_query(&st.modified_data_ids())
} else {
contract_ids_to_non_historical_path_query(&st.modified_data_ids())
}
}File 2: packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs
Change the Drive::verify_contract call (around line 69-79) from:
let (root_hash, contract) = Drive::verify_contract(
proof,
None,
false,
true,
data_contract_create.data_contract().id().into_buffer(),
platform_version,
)?;To:
let (root_hash, contract) = Drive::verify_contract(
proof,
Some(data_contract_create.data_contract().config().keeps_history()),
false,
true,
data_contract_create.data_contract().id().into_buffer(),
platform_version,
)?;Steps to Reproduce
- Create a data contract configuration with
keeps_history: truein the config - Use evo-sdk or wasm-sdk to create the contract
- Call
sdk.contracts.create(contract)or equivalent - Observe the proof verification error
Example contract config:
{
"$format_version": "0",
"id": "...",
"ownerId": "...",
"config": {
"keepsHistory": true, // ← This triggers the bug
},
"documentSchemas": { ... }
}Context
This bug prevents the creation of data contracts with history enabled through the SDK, which is a critical feature for contracts that need to track changes over time. While the contracts do get created successfully on the network, the SDK reports an error which breaks the user's workflow and prevents them from proceeding with document operations.
The bug affects:
packages/js-evo-sdk- JavaScript/TypeScript SDKpackages/wasm-sdk- WebAssembly SDKpackages/rs-sdk- Rust SDK (underlying implementation)
All SDK layers are affected because they rely on the proof verification logic in rs-drive.
Your Environment
- Version used: Latest (2.1.3)
- Component: rs-drive proof generation and verification
- Affected SDKs: js-evo-sdk, wasm-sdk, rs-sdk
- Network: Testnet (and likely mainnet if history-enabled contracts are created)
Related Code Locations
Key files involved:
- Proof Generation (Bug): packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs:48-49
- Proof Verification (Bug): packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs:69-79
- Contract Verification Logic: packages/rs-drive/src/verify/contract/verify_contract/v0/mod.rs:45-88
- Contract Storage (Different paths): packages/rs-drive/src/drive/contract/insert/add_contract_to_storage/v0/mod.rs:43-141
- Example of Correct Logic: packages/rs-drive/src/drive/contract/prove/prove_contract/v0/mod.rs:30-57 (shows how to detect history-enabled contracts using
element.is_basic_tree())
Additional Notes
The existing prove_contract logic in rs-drive already has the correct pattern for detecting history-enabled contracts by checking if the element is a basic tree. This same logic should be applied to the state transition proof generation.
Contracts without history enabled (keepsHistory: false or omitted) work correctly - this bug only affects contracts with keepsHistory: true.