diff --git a/packages/rs-drive-proof-verifier/src/from_request.rs b/packages/rs-drive-proof-verifier/src/from_request.rs index 5d3f03aef00..939b8a55671 100644 --- a/packages/rs-drive-proof-verifier/src/from_request.rs +++ b/packages/rs-drive-proof-verifier/src/from_request.rs @@ -27,10 +27,9 @@ use drive::query::{ VotePollsByEndDateDriveQuery, }; -use crate::Error; +use crate::{proved_request_limit, Error}; const BINCODE_CONFIG: dpp::bincode::config::Configuration = dpp::bincode::config::standard(); - /// Convert a gRPC request into a query object. /// /// This trait is implemented on Drive queries that can be created from gRPC requests. @@ -86,7 +85,7 @@ impl TryFromRequest for ContestedDocumentV let result = match grpc_request.version.ok_or(Error::EmptyVersion)? { get_contested_resource_vote_state_request::Version::V0(v) => { ContestedDocumentVotePollDriveQuery { - limit: v.count.map(|v| v as u16), + limit: Some(proved_request_limit(v.count)?), vote_poll: ContestedDocumentResourceVotePoll { contract_id: Identifier::from_bytes(&v.contract_id).map_err(|e| { Error::RequestError { @@ -196,7 +195,7 @@ impl TryFromRequest } })?, offset: None, - limit: value.limit.map(|x| x as u16), + limit: Some(proved_request_limit(value.limit)?), start_at, order_ascending: value.order_ascending, }) @@ -251,7 +250,7 @@ impl TryFromRequest error: format!("cannot decode contestant_id: {}", e), } })?, - limit: v.count.map(|v| v as u16), + limit: Some(proved_request_limit(v.count)?), offset: None, start_at: v .start_at_identifier_info @@ -321,7 +320,7 @@ impl TryFromRequest for VotePollsByDocumentTypeQue .transpose()?, start_index_values: bincode_decode_values(req.start_index_values.iter())?, end_index_values: bincode_decode_values(req.end_index_values.iter())?, - limit: req.count.map(|v| v as u16), + limit: Some(proved_request_limit(req.count)?), order_ascending: req.order_ascending, }, }; @@ -367,7 +366,7 @@ impl TryFromRequest for VotePollsByEndDateDriveQue end_time: v .end_time_info .map(|v| (v.end_time_ms, v.end_time_included)), - limit: v.limit.map(|v| v as u16), + limit: Some(proved_request_limit(v.limit)?), offset: v.offset.map(|v| v as u16), order_ascending: v.ascending, }, @@ -472,6 +471,15 @@ mod tests { use dpp::identifier::Identifier; use dpp::platform_value::Value; + fn sample_vote_poll() -> ContestedDocumentResourceVotePoll { + ContestedDocumentResourceVotePoll { + contract_id: Identifier::from_bytes(&[1u8; 32]).unwrap(), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: vec![Value::Text("dash".to_string())], + } + } + // --------------------------------------------------------------- // Helper: to_bytes32 // --------------------------------------------------------------- @@ -642,6 +650,202 @@ mod tests { assert_eq!(roundtripped, id); } + #[test] + fn test_contested_resources_request_preserves_omitted_limit_when_encoding() { + let query = VotePollsByDocumentTypeQuery { + contract_id: Identifier::from_bytes(&[1u8; 32]).unwrap(), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + start_index_values: vec![Value::Text("dash".to_string())], + end_index_values: vec![], + start_at_value: None, + limit: None, + order_ascending: true, + }; + + let request = query.try_to_request().expect("request should encode"); + let proto::get_contested_resources_request::Version::V0(v0) = + request.version.expect("request should contain a version"); + + assert_eq!(v0.count, None); + } + + #[test] + fn test_contested_resources_request_defaults_limit_when_decoding() { + let request: GetContestedResourcesRequest = GetContestedResourcesRequestV0 { + prove: true, + contract_id: Identifier::from_bytes(&[2u8; 32]).unwrap().to_vec(), + count: None, + document_type_name: "domain".to_string(), + end_index_values: vec![], + start_index_values: bincode_encode_values([&Value::Text("dash".to_string())]) + .expect("start index values should encode"), + index_name: "parentNameAndLabel".to_string(), + order_ascending: true, + start_at_value_info: None, + } + .into(); + + let query = + VotePollsByDocumentTypeQuery::try_from_request(request).expect("request should decode"); + + assert_eq!(query.limit, Some(crate::DEFAULT_QUERY_LIMIT)); + } + + #[test] + fn test_contested_resource_vote_state_request_preserves_omitted_limit_when_encoding() { + let query = ContestedDocumentVotePollDriveQuery { + vote_poll: sample_vote_poll(), + result_type: ContestedDocumentVotePollDriveQueryResultType::Documents, + offset: None, + limit: None, + start_at: None, + allow_include_locked_and_abstaining_vote_tally: false, + }; + + let request = query.try_to_request().expect("request should encode"); + let proto::get_contested_resource_vote_state_request::Version::V0(v0) = + request.version.expect("request should contain a version"); + + assert_eq!(v0.count, None); + } + + #[test] + fn test_contested_resource_vote_state_request_defaults_limit_when_decoding() { + let request: GetContestedResourceVoteStateRequest = + proto::get_contested_resource_vote_state_request::GetContestedResourceVoteStateRequestV0 { + prove: true, + contract_id: sample_vote_poll().contract_id.to_vec(), + count: None, + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: bincode_encode_values([&Value::Text("dash".to_string())]) + .expect("index values should encode"), + result_type: get_contested_resource_vote_state_request_v0::ResultType::Documents.into(), + start_at_identifier_info: None, + allow_include_locked_and_abstaining_vote_tally: false, + } + .into(); + + let query = ContestedDocumentVotePollDriveQuery::try_from_request(request) + .expect("request should decode"); + + assert_eq!(query.limit, Some(crate::DEFAULT_QUERY_LIMIT)); + } + + #[test] + fn test_contested_resource_identity_votes_request_preserves_omitted_limit_when_encoding() { + let query = ContestedResourceVotesGivenByIdentityQuery { + identity_id: Identifier::from_bytes(&[9u8; 32]).unwrap(), + offset: None, + limit: None, + start_at: None, + order_ascending: true, + }; + + let request = query.try_to_request().expect("request should encode"); + let proto::get_contested_resource_identity_votes_request::Version::V0(v0) = + request.version.expect("request should contain a version"); + + assert_eq!(v0.limit, None); + } + + #[test] + fn test_contested_resource_identity_votes_request_defaults_limit_when_decoding() { + let request: GetContestedResourceIdentityVotesRequest = + proto::get_contested_resource_identity_votes_request::GetContestedResourceIdentityVotesRequestV0 { + prove: true, + identity_id: Identifier::from_bytes(&[8u8; 32]).unwrap().to_vec(), + offset: None, + limit: None, + start_at_vote_poll_id_info: None, + order_ascending: true, + } + .into(); + + let query = ContestedResourceVotesGivenByIdentityQuery::try_from_request(request) + .expect("request should decode"); + + assert_eq!(query.limit, Some(crate::DEFAULT_QUERY_LIMIT)); + } + + #[test] + fn test_contested_resource_voters_for_identity_request_preserves_omitted_limit_when_encoding() { + let query = ContestedDocumentVotePollVotesDriveQuery { + vote_poll: sample_vote_poll(), + contestant_id: Identifier::from_bytes(&[3u8; 32]).unwrap(), + offset: None, + limit: None, + start_at: None, + order_ascending: true, + }; + + let request = query.try_to_request().expect("request should encode"); + let proto::get_contested_resource_voters_for_identity_request::Version::V0(v0) = + request.version.expect("request should contain a version"); + + assert_eq!(v0.count, None); + } + + #[test] + fn test_contested_resource_voters_for_identity_request_defaults_limit_when_decoding() { + let request: GetContestedResourceVotersForIdentityRequest = + proto::get_contested_resource_voters_for_identity_request::GetContestedResourceVotersForIdentityRequestV0 { + prove: true, + contract_id: sample_vote_poll().contract_id.to_vec(), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: bincode_encode_values([&Value::Text("dash".to_string())]) + .expect("index values should encode"), + contestant_id: Identifier::from_bytes(&[4u8; 32]).unwrap().to_vec(), + start_at_identifier_info: None, + count: None, + order_ascending: true, + } + .into(); + + let query = ContestedDocumentVotePollVotesDriveQuery::try_from_request(request) + .expect("request should decode"); + + assert_eq!(query.limit, Some(crate::DEFAULT_QUERY_LIMIT)); + } + + #[test] + fn test_vote_polls_by_end_date_request_preserves_omitted_limit_when_encoding() { + let query = VotePollsByEndDateDriveQuery { + start_time: Some((1000, true)), + end_time: Some((2000, false)), + limit: None, + offset: None, + order_ascending: true, + }; + + let request = query.try_to_request().expect("request should encode"); + let proto::get_vote_polls_by_end_date_request::Version::V0(v0) = + request.version.expect("request should contain a version"); + + assert_eq!(v0.limit, None); + } + + #[test] + fn test_vote_polls_by_end_date_request_defaults_limit_when_decoding() { + let request: GetVotePollsByEndDateRequest = + proto::get_vote_polls_by_end_date_request::GetVotePollsByEndDateRequestV0 { + prove: true, + start_time_info: None, + end_time_info: None, + limit: None, + offset: None, + ascending: true, + } + .into(); + + let query = + VotePollsByEndDateDriveQuery::try_from_request(request).expect("request should decode"); + + assert_eq!(query.limit, Some(crate::DEFAULT_QUERY_LIMIT)); + } + // --------------------------------------------------------------- // Error path: SingleDocumentByContender is rejected in try_to_request // --------------------------------------------------------------- diff --git a/packages/rs-drive-proof-verifier/src/lib.rs b/packages/rs-drive-proof-verifier/src/lib.rs index 5790b842a93..ab8b95fe381 100644 --- a/packages/rs-drive-proof-verifier/src/lib.rs +++ b/packages/rs-drive-proof-verifier/src/lib.rs @@ -21,6 +21,41 @@ pub mod from_request; /// Implementation of unproved verification pub mod unproved; +/// Default query limit applied by Platform when proved paginated requests omit `count` or `limit`. +/// +/// Proof verification must mirror this behavior; otherwise a valid proof for a default-bounded +/// server query is reconstructed locally as an unbounded query and looks truncated. +pub(crate) use drive::config::DEFAULT_QUERY_LIMIT; + +/// Parse a proved request's optional `count`/`limit`, applying Platform's default when omitted. +pub(crate) fn proved_request_limit(limit: Option) -> Result { + limit.map_or(Ok(DEFAULT_QUERY_LIMIT), |limit| { + u16::try_from(limit).map_err(|_| Error::RequestError { + error: "query limit exceeds u16::MAX".to_string(), + }) + }) +} + // Needed for #[derive(PlatformSerialize, PlatformDeserialize)] #[cfg(feature = "mocks")] use dpp::serialization; + +#[cfg(test)] +mod tests { + use super::{proved_request_limit, DEFAULT_QUERY_LIMIT}; + + #[test] + fn proved_request_limit_defaults_when_omitted() { + assert_eq!(proved_request_limit(None).unwrap(), DEFAULT_QUERY_LIMIT); + } + + #[test] + fn proved_request_limit_preserves_explicit_value() { + assert_eq!(proved_request_limit(Some(7)).unwrap(), 7); + } + + #[test] + fn proved_request_limit_rejects_u32_overflow() { + assert!(proved_request_limit(Some(u16::MAX as u32 + 1)).is_err()); + } +} diff --git a/packages/rs-drive-proof-verifier/src/proof.rs b/packages/rs-drive-proof-verifier/src/proof.rs index b32e5591d81..61add5e6149 100644 --- a/packages/rs-drive-proof-verifier/src/proof.rs +++ b/packages/rs-drive-proof-verifier/src/proof.rs @@ -10,7 +10,7 @@ pub mod token_total_supply; use crate::from_request::TryFromRequest; use crate::verify::verify_tenderdash_proof; -use crate::{types::*, ContextProvider, DataContractProvider, Error}; +use crate::{proved_request_limit, types::*, ContextProvider, DataContractProvider, Error}; use dapi_grpc::platform::v0::get_evonodes_proposed_epoch_blocks_by_range_request::get_evonodes_proposed_epoch_blocks_by_range_request_v0::Start; use dapi_grpc::platform::v0::get_identities_contract_keys_request::GetIdentitiesContractKeysRequestV0; use dapi_grpc::platform::v0::get_path_elements_request::GetPathElementsRequestV0; @@ -2362,7 +2362,7 @@ impl FromProof for Propo Some(index) => try_u32_to_u16(index)?, None => try_u32_to_u16(mtd.epoch)?, }; - let checked_limit = limit.map(try_u32_to_u16).transpose()?; + let checked_limit = Some(proved_request_limit(limit)?); let (root_hash, proposer_block_counts) = Drive::verify_epoch_proposers( &proof.grovedb_proof, diff --git a/packages/rs-drive-proof-verifier/src/proof/groups.rs b/packages/rs-drive-proof-verifier/src/proof/groups.rs index 392dccfdc30..4438ce4a680 100644 --- a/packages/rs-drive-proof-verifier/src/proof/groups.rs +++ b/packages/rs-drive-proof-verifier/src/proof/groups.rs @@ -1,7 +1,7 @@ use crate::error::MapGroveDbError; use crate::types::groups::{GroupActionSigners, GroupActions, Groups}; use crate::verify::verify_tenderdash_proof; -use crate::{ContextProvider, Error, FromProof}; +use crate::{proved_request_limit, ContextProvider, Error, FromProof}; use dapi_grpc::platform::v0::{ get_group_action_signers_request, get_group_actions_request, get_group_info_request, get_group_infos_request, GetGroupActionSignersRequest, GetGroupActionSignersResponse, @@ -109,7 +109,7 @@ impl FromProof for Groups { ) }); - let count = v0.count.map(|count| count as u16); + let count = Some(proved_request_limit(v0.count)?); (contract_id, start_group_contract_position, count) } @@ -195,7 +195,7 @@ impl FromProof for GroupActions { let group_contract_position = v0.group_contract_position as GroupContractPosition; - let count = v0.count.map(|count| count as u16); + let count = Some(proved_request_limit(v0.count)?); let status = GroupActionStatus::try_from(v0.status).map_err(|error| { Error::RequestError { diff --git a/packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs b/packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs index f88ed305014..05adb9bf5a2 100644 --- a/packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs +++ b/packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs @@ -1,6 +1,6 @@ use crate::error::MapGroveDbError; use crate::verify::verify_tenderdash_proof; -use crate::{types::TokenPreProgrammedDistributions, ContextProvider, Error}; +use crate::{proved_request_limit, types::TokenPreProgrammedDistributions, ContextProvider, Error}; use dapi_grpc::platform::v0::{ get_token_pre_programmed_distributions_request, GetTokenPreProgrammedDistributionsRequest, GetTokenPreProgrammedDistributionsResponse, Proof, ResponseMetadata, @@ -68,14 +68,7 @@ impl FromProof for TokenPreProgrammed None => None, }; - let limit = req_v0 - .limit - .map(|l| { - u16::try_from(l).map_err(|_| Error::RequestError { - error: "limit exceeds u16::MAX".into(), - }) - }) - .transpose()?; + let limit = Some(proved_request_limit(req_v0.limit)?); let metadata = response .metadata() diff --git a/packages/rs-drive/src/verify/document/verify_proof_keep_serialized/v0/mod.rs b/packages/rs-drive/src/verify/document/verify_proof_keep_serialized/v0/mod.rs index 97984c1ac0f..9d355b7c60f 100644 --- a/packages/rs-drive/src/verify/document/verify_proof_keep_serialized/v0/mod.rs +++ b/packages/rs-drive/src/verify/document/verify_proof_keep_serialized/v0/mod.rs @@ -142,6 +142,105 @@ mod tests { } } + /// Regression test exposing the same proof-mismatch class of bug this PR + /// fixes for contested resources, but on the documents path — which this + /// PR does NOT address. + /// + /// Server (`packages/rs-drive-abci/src/query/document_query/v0/mod.rs:124`) + /// applies `default_query_limit` when wire `limit == 0`, while the SDK's + /// `TryFrom<&DocumentQuery> for DriveDocumentQuery` + /// (`packages/rs-sdk/src/platform/documents/document_query.rs:325-329`) + /// maps wire `limit == 0` back to `limit: None`. The asymmetric + /// `SizedQuery` flowing through `DriveDocumentQuery::construct_path_query` + /// (`packages/rs-drive/src/query/mod.rs:1288, 1328, 2113`) then trips + /// GroveDB's `verify_query` with the same "Proof is missing data for + /// query range" failure that motivated this PR. + /// + /// `proved_request_limit` introduced in this PR is not applied to the + /// document path; that gap should be closed before merge or in a tracked + /// follow-up. Until then this test fails CI to keep the regression + /// visible. + #[test] + fn document_proof_with_limit_some_prove_vs_limit_none_verify() { + let drive = setup_drive_with_initial_state_structure(None); + let platform_version = PlatformVersion::latest(); + + let contract = load_system_data_contract(SystemDataContract::DPNS, platform_version) + .expect("expected to load DPNS contract"); + + drive + .apply_contract( + &contract, + BlockInfo::default(), + true, + None, + None, + platform_version, + ) + .expect("expected to apply contract"); + + let document_type = contract + .document_type_for_name("preorder") + .expect("expected preorder document type"); + + // Insert > DEFAULT_QUERY_LIMIT documents so the bounded vs unbounded + // queries cannot trivially produce identical proofs. + let document_count = 110u64; + for seed in 1..=document_count { + let document = document_type + .random_document(Some(seed), platform_version) + .expect("expected a random document"); + + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo((&document, None)), + owner_id: None, + }, + contract: &contract, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + platform_version, + None, + ) + .expect("expected to insert document"); + } + + // Prover applies `default_query_limit` (server side). + let prover_query = DriveDocumentQuery::all_items_query( + &contract, + document_type, + Some(crate::config::DEFAULT_QUERY_LIMIT), + ); + + let (proof, _cost) = prover_query + .execute_with_proof(&drive, None, None, platform_version) + .expect("expected to execute query with proof"); + + // Verifier reconstructs with `limit: None`, mirroring what the SDK + // does today for omitted document-query limits. + let verifier_query = DriveDocumentQuery::all_items_query(&contract, document_type, None); + + let (_root, docs) = verifier_query + .verify_proof_keep_serialized(proof.as_slice(), platform_version) + .expect( + "verify with limit=None should succeed against a limit=Some(DEFAULT_QUERY_LIMIT) \ + proof; failure here indicates the document path needs the same \ + `proved_request_limit` treatment the contested-resource paths receive in this PR", + ); + + assert_eq!( + docs.len(), + crate::config::DEFAULT_QUERY_LIMIT as usize, + "expected verifier to return DEFAULT_QUERY_LIMIT documents when prover bounded the result set" + ); + } + #[test] fn should_prove_and_verify_keep_serialized_empty_result() { let drive = setup_drive_with_initial_state_structure(None); diff --git a/packages/rs-sdk/tests/fetch/contested_resource.rs b/packages/rs-sdk/tests/fetch/contested_resource.rs index 0e606007c95..946059ba4d0 100644 --- a/packages/rs-sdk/tests/fetch/contested_resource.rs +++ b/packages/rs-sdk/tests/fetch/contested_resource.rs @@ -4,7 +4,12 @@ use crate::fetch::{ common::{setup_logs, setup_sdk_for_test_case, TEST_DPNS_NAME}, config::Config, }; -use dash_sdk::{platform::FetchMany, Error}; +use dapi_grpc::platform::v0::get_contested_resources_request; +use dapi_grpc::platform::v0::GetContestedResourcesRequest; +use dash_sdk::{ + platform::{FetchMany, Query}, + Error, +}; use dpp::{ platform_value::Value, voting::{ @@ -436,3 +441,84 @@ pub async fn check_mn_voting_prerequisites(cfg: &Config) -> Result<(), Vec| VotePollsByDocumentTypeQuery { + contract_id: cfg.existing_data_contract_id, + document_type_name: cfg.existing_document_type_name.clone(), + index_name: "parentNameAndLabel".to_string(), + start_at_value: None, + start_index_values: vec![Value::Text("dash".to_string())], + end_index_values: vec![], + limit, + order_ascending: true, + }; + + let request: GetContestedResourcesRequest = make_query(None) + .clone() + .query(true) + .expect("query should serialize"); + let get_contested_resources_request::Version::V0(v0) = + request.version.expect("request should contain version"); + assert_eq!( + v0.count, None, + "omitted query limit must stay omitted on the wire", + ); + + // 1. With limit → works fine. + let with_limit = + ContestedResource::fetch_many(&sdk, make_query(Some(drive::config::DEFAULT_QUERY_LIMIT))) + .await + .expect("query with start_index_values + limit should succeed"); + tracing::info!(count = with_limit.0.len(), "With limit: OK"); + assert!(!with_limit.0.is_empty(), "expected contested labels"); + + // 2. Without limit → should now verify successfully and match the explicit default limit. + let no_limit = ContestedResource::fetch_many(&sdk, make_query(None)) + .await + .expect("query with omitted limit should verify successfully"); + + tracing::info!(count = no_limit.0.len(), "No-limit query: OK"); + assert!( + !no_limit.0.is_empty(), + "expected contested labels under 'dash'" + ); + assert_eq!( + no_limit.0, with_limit.0, + "omitted-limit proof should match the explicit default-limit result set", + ); +} diff --git a/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/.gitkeep b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json new file mode 100644 index 00000000000..0f9a8ab8cc5 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/data_contract-e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155.json @@ -0,0 +1 @@ +{"$formatVersion":"1","id":"GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec","config":{"$formatVersion":"1","canBeDeleted":false,"readonly":false,"keepsHistory":false,"documentsKeepHistoryContractDefault":false,"documentsMutableContractDefault":true,"documentsCanBeDeletedContractDefault":true,"requiresIdentityEncryptionBoundedKey":null,"requiresIdentityDecryptionBoundedKey":null,"sizedIntegerTypes":false},"version":1,"ownerId":"11111111111111111111111111111111","schemaDefs":null,"documentSchemas":{"domain":{"documentsMutable":false,"canBeDeleted":true,"transferable":1,"tradeMode":1,"type":"object","indices":[{"name":"parentNameAndLabel","properties":[{"normalizedParentDomainName":"asc"},{"normalizedLabel":"asc"}],"unique":true,"contested":{"fieldMatches":[{"field":"normalizedLabel","regexPattern":"^[a-zA-Z01-]{3,19}$"}],"resolution":0,"description":"If the normalized label part of this index is less than 20 characters (all alphabet a-z, A-Z, 0, 1, and -) then a masternode vote contest takes place to give out the name"}},{"name":"identityId","nullSearchable":false,"properties":[{"records.identity":"asc"}]}],"properties":{"label":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$","minLength":3,"maxLength":63,"position":0,"description":"Domain label. e.g. 'Bob'."},"normalizedLabel":{"type":"string","pattern":"^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-]{0,61}[a-hj-km-np-z0-9]$","maxLength":63,"position":1,"description":"Domain label converted to lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'b0b'","$comment":"Must be equal to the label in lowercase. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\"."},"parentDomainName":{"type":"string","pattern":"^$|^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$","minLength":0,"maxLength":63,"position":2,"description":"A full parent domain name. e.g. 'dash'."},"normalizedParentDomainName":{"type":"string","pattern":"^$|^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-\\.]{0,61}[a-hj-km-np-z0-9]$","minLength":0,"maxLength":63,"position":3,"description":"A parent domain name in lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'dash'","$comment":"Must either be equal to an existing domain or empty to create a top level domain. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\". Only the data contract owner can create top level domains."},"preorderSalt":{"type":"array","byteArray":true,"minItems":32,"maxItems":32,"position":4,"description":"Salt used in the preorder document"},"records":{"type":"object","properties":{"identity":{"type":"array","byteArray":true,"minItems":32,"maxItems":32,"position":1,"contentMediaType":"application/x.dash.dpp.identifier","description":"Identifier name record that refers to an Identity"}},"minProperties":1,"position":5,"additionalProperties":false},"subdomainRules":{"type":"object","properties":{"allowSubdomains":{"type":"boolean","description":"This option defines who can create subdomains: true - anyone; false - only the domain owner","$comment":"Only the domain owner is allowed to create subdomains for non top-level domains","position":0}},"position":6,"description":"Subdomain rules allow domain owners to define rules for subdomains","additionalProperties":false,"required":["allowSubdomains"]}},"required":["$createdAt","$updatedAt","$transferredAt","label","normalizedLabel","normalizedParentDomainName","preorderSalt","records","subdomainRules"],"transient":["preorderSalt"],"additionalProperties":false,"$comment":"In order to register a domain you need to create a preorder. The preorder step is needed to prevent man-in-the-middle attacks. normalizedLabel + '.' + normalizedParentDomain must not be longer than 253 chars length as defined by RFC 1035. Domain documents are immutable: modification and deletion are restricted"},"preorder":{"documentsMutable":false,"canBeDeleted":true,"type":"object","indices":[{"name":"saltedHash","properties":[{"saltedDomainHash":"asc"}],"unique":true}],"properties":{"saltedDomainHash":{"type":"array","byteArray":true,"minItems":32,"maxItems":32,"position":0,"description":"Double sha-256 of the concatenation of a 32 byte random salt and a normalized domain name"}},"required":["saltedDomainHash"],"additionalProperties":false,"$comment":"Preorder documents are immutable: modification and deletion are restricted"}},"createdAt":null,"updatedAt":null,"createdAtBlockHeight":null,"updatedAtBlockHeight":null,"createdAtEpoch":null,"updatedAtEpoch":null,"groups":{},"tokens":{},"keywords":[],"description":null} \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/msg_GetContestedResourcesRequest_8f4daadf3e41747492cd381cbbd1cf33dd52735f597de4b4c804effd2600d135.json b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/msg_GetContestedResourcesRequest_8f4daadf3e41747492cd381cbbd1cf33dd52735f597de4b4c804effd2600d135.json new file mode 100644 index 00000000000..bc5dbb1fef9 Binary files /dev/null and b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/msg_GetContestedResourcesRequest_8f4daadf3e41747492cd381cbbd1cf33dd52735f597de4b4c804effd2600d135.json differ diff --git a/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/msg_GetContestedResourcesRequest_8f71462d5f438e1b12fedf94ad5799af81392b7b0cbb7e86412da37ab13aef4b.json b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/msg_GetContestedResourcesRequest_8f71462d5f438e1b12fedf94ad5799af81392b7b0cbb7e86412da37ab13aef4b.json new file mode 100644 index 00000000000..d52a58f76f7 Binary files /dev/null and b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/msg_GetContestedResourcesRequest_8f71462d5f438e1b12fedf94ad5799af81392b7b0cbb7e86412da37ab13aef4b.json differ diff --git a/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/quorum_pubkey-6-000000928ce4e3adf20561462b82d40e0804fdbde0e6115133f9a9348267cace.json b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/quorum_pubkey-6-000000928ce4e3adf20561462b82d40e0804fdbde0e6115133f9a9348267cace.json new file mode 100644 index 00000000000..41ce883b732 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/contested_resources_start_index_values_proof_bug/quorum_pubkey-6-000000928ce4e3adf20561462b82d40e0804fdbde0e6115133f9a9348267cace.json @@ -0,0 +1 @@ +b1f60096ca81649bb3ed1f2c326e9b6a602dd615ce9e116509e6f0307261601a7ecc524366368b98030cd2cd45c1c7d2 \ No newline at end of file