-
Notifications
You must be signed in to change notification settings - Fork 50
fix(drive): consolidate historical contract proof verification retry logic #3165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3.1-dev
Are you sure you want to change the base?
Changes from all commits
8197175
63e16fe
1c6f1a2
73c264e
1ab6d20
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,6 +59,8 @@ use base64::Engine; | |
| #[cfg(feature = "server")] | ||
| use dpp::block::block_info::BlockInfo; | ||
| use dpp::data_contract::accessors::v0::DataContractV0Getters; | ||
| use dpp::data_contract::accessors::v0::DataContractV0Setters; | ||
| use dpp::data_contract::config::v0::DataContractConfigSettersV0; | ||
| use dpp::data_contract::config::v1::DataContractConfigSettersV1; | ||
| use dpp::data_contract::conversion::value::v0::DataContractValueConversionMethodsV0; | ||
| use dpp::data_contract::document_type::methods::DocumentTypeV0Methods; | ||
|
|
@@ -918,6 +920,14 @@ pub fn setup_withdrawal_tests( | |
| #[cfg(feature = "server")] | ||
| /// Sets up the References contract to test queries on. | ||
| pub fn setup_references_tests(_count: u32, _seed: u64) -> (Drive, DataContract) { | ||
| setup_references_tests_with_keeps_history(_count, _seed, false) | ||
| } | ||
|
|
||
| pub fn setup_references_tests_with_keeps_history( | ||
| _count: u32, | ||
| _seed: u64, | ||
| keeps_history: bool, | ||
| ) -> (Drive, DataContract) { | ||
| let drive = setup_drive(Some(DriveConfig::default())); | ||
|
|
||
| let db_transaction = drive.grove.start_transaction(); | ||
|
|
@@ -939,7 +949,9 @@ pub fn setup_references_tests(_count: u32, _seed: u64) -> (Drive, DataContract) | |
| "tests/supporting_files/contract/references/references_with_contract_history.json", | ||
| None, | ||
| None, | ||
| None::<fn(&mut DataContract)>, | ||
| Some(|contract: &mut DataContract| { | ||
| contract.config_mut().set_keeps_history(keeps_history); | ||
| }), | ||
| Some(&db_transaction), | ||
| None, | ||
| ); | ||
|
|
@@ -4687,6 +4699,119 @@ mod tests { | |
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_contract_keeps_history_verify_with_unknown_history_flag() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PROJ-002 (LOW) — Test does not verify retry was actually exercised Test 1 asserts the end result is correct but doesn't prove the retry path was taken. If a GroveDb change made non-historical queries accidentally succeed on historical proofs, this test would still pass while the retry logic becomes dead code. Consider capturing the tracing debug log ( 🤖 Claudius |
||
| // Regression test: when contract_known_keeps_history is None, | ||
| // verification must still succeed for historical contracts. | ||
| let (drive, contract) = setup_references_tests_with_keeps_history(10, 3334, true); | ||
| let platform_version = PlatformVersion::latest(); | ||
|
|
||
| // Apply an update so the contract has an actual history entry and latest historical path. | ||
| let mut latest_contract = contract.clone(); | ||
| latest_contract.set_version(contract.version() + 1); | ||
| drive | ||
| .apply_contract( | ||
| &latest_contract, | ||
| BlockInfo { | ||
| time_ms: 1, | ||
| ..Default::default() | ||
| }, | ||
| true, | ||
| StorageFlags::optional_default_as_cow(), | ||
| None, | ||
| platform_version, | ||
| ) | ||
| .expect("contract update should be applied"); | ||
|
|
||
| let root_hash = drive | ||
| .grove | ||
| .root_hash(None, &platform_version.drive.grove_version) | ||
| .unwrap() | ||
| .expect("there is always a root hash"); | ||
|
|
||
| let contract_proof = drive | ||
| .prove_contract(latest_contract.id().into_buffer(), None, platform_version) | ||
| .expect("expected to get proof"); | ||
|
|
||
| // Test 1: None (unknown) - verification must succeed, and may use retry behavior. | ||
| let (proof_root_hash, proof_contract) = Drive::verify_contract( | ||
| contract_proof.as_slice(), | ||
| None, | ||
| false, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PROJ-003 (INFO) — No coverage for All four sub-cases use 🤖 Claudius |
||
| false, | ||
| latest_contract.id().into_buffer(), | ||
| platform_version, | ||
| ) | ||
| .expect("verification with None should succeed"); | ||
| assert_eq!(root_hash, proof_root_hash); | ||
| assert_eq!( | ||
| latest_contract, | ||
| proof_contract.expect("contract exists and was updated — None retry must return it") | ||
| ); | ||
|
|
||
| // Test 2: Some(true) - direct historical, must succeed since proof was generated | ||
| // for a historical contract | ||
| let (proof_root_hash_2, proof_contract_2) = Drive::verify_contract( | ||
| contract_proof.as_slice(), | ||
| Some(true), | ||
| false, | ||
| false, | ||
| latest_contract.id().into_buffer(), | ||
| platform_version, | ||
| ) | ||
| .expect("verification with Some(true) should succeed for historical contract"); | ||
| assert_eq!(root_hash, proof_root_hash_2); | ||
| assert_eq!( | ||
| latest_contract, | ||
| proof_contract_2.expect("expected contract with explicit history flag") | ||
| ); | ||
|
|
||
| // Test 3: Some(false) - explicit non-historical contract must verify to existing value. | ||
| let (non_historical_drive, non_historical_contract) = | ||
| setup_references_tests_with_keeps_history(10, 3334, false); | ||
| let non_historical_root_hash = non_historical_drive | ||
| .grove | ||
| .root_hash(None, &platform_version.drive.grove_version) | ||
| .unwrap() | ||
| .expect("there is always a root hash"); | ||
| let non_historical_proof = non_historical_drive | ||
| .prove_contract( | ||
| non_historical_contract.id().into_buffer(), | ||
| None, | ||
| platform_version, | ||
| ) | ||
| .expect("expected to get proof"); | ||
| let (proof_root_hash_3, proof_contract_3) = Drive::verify_contract( | ||
| non_historical_proof.as_slice(), | ||
| Some(false), | ||
| false, | ||
| false, | ||
| non_historical_contract.id().into_buffer(), | ||
| platform_version, | ||
| ) | ||
| .expect("verification with Some(false) should return the existing contract"); | ||
| assert_eq!(non_historical_root_hash, proof_root_hash_3); | ||
| assert_eq!( | ||
| non_historical_contract, | ||
| proof_contract_3.expect("expected contract with explicit non-history flag") | ||
| ); | ||
|
|
||
| // Test 4: verify_contract_return_serialization with None | ||
| let (proof_root_hash_4, proof_contract_4) = Drive::verify_contract_return_serialization( | ||
| contract_proof.as_slice(), | ||
| None, | ||
| false, | ||
| false, | ||
| latest_contract.id().into_buffer(), | ||
| platform_version, | ||
| ) | ||
| .expect("return_serialization with None should succeed for historical contract"); | ||
| assert_eq!(root_hash, proof_root_hash_4); | ||
| let (deserialized, _bytes) = | ||
| proof_contract_4.expect("contract exists — return_serialization must return it"); | ||
| assert_eq!(latest_contract, deserialized); | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PROJ-001 (LOW) — Add test for non-existent contract with The test covers the happy path (historical contract exists, Empirical testing confirms this case is safe (absence proof verifies on both paths, retry returns #[test]
fn test_verify_non_existent_contract_with_unknown_history_flag() {
let (drive, _contract) = setup_references_tests(10, 3334);
let platform_version = PlatformVersion::latest();
let fake_contract_id = [255u8; 32];
let contract_proof = drive
.prove_contract(fake_contract_id, None, platform_version)
.expect("expected to get proof for non-existent contract");
let (_, proof_returned_contract) = Drive::verify_contract(
contract_proof.as_slice(),
None,
false,
false,
fake_contract_id,
platform_version,
)
.expect("expected verification to succeed for non-existent contract");
assert!(
proof_returned_contract.is_none(),
"expected None for a non-existent contract, got Some",
);
}No such test exists in the codebase. 🤖 Claudius |
||
| #[cfg(feature = "server")] | ||
| #[test] | ||
| fn test_dpns_query_first_version() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SEC-001 (LOW) — Restore diagnostic context in retry log
The old code logged the original result before retrying:
The new code only logs
contract_id, losing the original result and path query context that helps diagnose verification issues.Suggestion: log the original result at debug level before retrying:
🤖 Claudius