From 968d12b53db6269c7a0ead0250ecc010c22c2957 Mon Sep 17 00:00:00 2001 From: pasta Date: Fri, 7 Aug 2026 18:27:29 -0500 Subject: [PATCH] test(sdk): add proof-vector regression corpus for drive-proof-verifier drive-proof-verifier had no integration tests: its verification pipeline (grovedb proof replay + tenderdash quorum-signature check) was exercised only indirectly through rs-sdk's mock replay. Add a corpus of 16 fixture directories generated from a real Drive state (platform v4.0.0 fixtures, protocol version 12) driving 19 test cases through the crate's real public FromProof entry points, with a per-case manifest.json plus proof/signature/quorum-key blobs and a loader that synthesizes the DAPI response protobuf from components. Positive cases assert decoded values against the manifest (balances, nonces, key bytes and ordering, contender tallies, winners, proof-of- absence), and every quorum-sig positive runs a genuine BLS check with real fixture key material: all drive fixtures commit to one root hash, which is exactly the app hash the fixture quorum signature signs. Negative cases each change exactly one ingredient so none can pass for the wrong reason: a bit-flipped proof fails at the grovedb layer, a tampered signature at point decompression, a wrong quorum key and a wrong block-id hash at signature verification, and tampering any signed ResponseMetadata field (height, time_ms, core_chain_locked_height) breaks the signature - pinning that the StateId digest really covers the metadata. Never a panic. The documents-family cases pin the DriveDocumentQuery shape (root hash and serialized payloads byte-for-byte) and clean decode failure on the fixture's placeholder payloads; the coverage matrix in tests/vectors/ README.md states exactly what each family reaches. The same fixtures are replayed byte-exact by Dash Core's platform GUI implementation, so drift between what Drive proves and what any client verifies fails loudly here. --- Cargo.lock | 1 + packages/rs-drive-proof-verifier/Cargo.toml | 11 + .../tests/common/mod.rs | 282 ++++++++++++++++++ .../tests/vectors/README.md | 35 +++ .../contested-vote-state-absent/manifest.json | 37 +++ .../contested-vote-state-absent/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../contested-vote-state-absent/signature.hex | 1 + .../contested-vote-state-active/manifest.json | 51 ++++ .../contested-vote-state-active/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../contested-vote-state-active/signature.hex | 1 + .../manifest.json | 51 ++++ .../contested-vote-state-finished/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../signature.hex | 1 + .../dashpay-contacts-incoming/manifest.json | 35 +++ .../dashpay-contacts-incoming/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../dashpay-contacts-incoming/signature.hex | 1 + .../vectors/dashpay-profile/manifest.json | 33 ++ .../tests/vectors/dashpay-profile/proof.hex | 1 + .../vectors/dashpay-profile/quorum_pubkey.hex | 1 + .../vectors/dashpay-profile/signature.hex | 1 + .../vectors/dpns-domain-exact/manifest.json | 34 +++ .../tests/vectors/dpns-domain-exact/proof.hex | 1 + .../dpns-domain-exact/quorum_pubkey.hex | 1 + .../vectors/dpns-domain-exact/signature.hex | 1 + .../vectors/dpns-domain-prefix/manifest.json | 34 +++ .../vectors/dpns-domain-prefix/proof.hex | 1 + .../dpns-domain-prefix/quorum_pubkey.hex | 1 + .../vectors/dpns-domain-prefix/signature.hex | 1 + .../manifest.json | 30 ++ .../proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../signature.hex | 1 + .../vectors/identity-balance/manifest.json | 31 ++ .../tests/vectors/identity-balance/proof.hex | 1 + .../identity-balance/quorum_pubkey.hex | 1 + .../vectors/identity-balance/signature.hex | 1 + .../identity-contract-nonce/manifest.json | 32 ++ .../vectors/identity-contract-nonce/proof.hex | 1 + .../identity-contract-nonce/quorum_pubkey.hex | 1 + .../identity-contract-nonce/signature.hex | 1 + .../tests/vectors/identity-keys/manifest.json | 35 +++ .../tests/vectors/identity-keys/proof.hex | 1 + .../vectors/identity-keys/quorum_pubkey.hex | 1 + .../tests/vectors/identity-keys/signature.hex | 1 + .../vectors/identity-nonce/manifest.json | 31 ++ .../tests/vectors/identity-nonce/proof.hex | 1 + .../vectors/identity-nonce/quorum_pubkey.hex | 1 + .../vectors/identity-nonce/signature.hex | 1 + .../manifest.json | 30 ++ .../quorum-sig-tampered-signature/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../signature.hex | 1 + .../vectors/quorum-sig-valid/manifest.json | 31 ++ .../tests/vectors/quorum-sig-valid/proof.hex | 1 + .../quorum-sig-valid/quorum_pubkey.hex | 1 + .../vectors/quorum-sig-valid/signature.hex | 1 + .../manifest.json | 30 ++ .../quorum-sig-wrong-block-id-hash/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../signature.hex | 1 + .../quorum-sig-wrong-quorum-key/manifest.json | 30 ++ .../quorum-sig-wrong-quorum-key/proof.hex | 1 + .../quorum_pubkey.hex | 1 + .../quorum-sig-wrong-quorum-key/signature.hex | 1 + .../tests/vectors_contested.rs | 177 +++++++++++ .../tests/vectors_documents.rs | 251 ++++++++++++++++ .../tests/vectors_identity.rs | 259 ++++++++++++++++ .../tests/vectors_quorum_sig.rs | 148 +++++++++ 72 files changed, 1767 insertions(+) create mode 100644 packages/rs-drive-proof-verifier/tests/common/mod.rs create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/README.md create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-balance/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-keys/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-keys/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-keys/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-keys/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/manifest.json create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/proof.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/quorum_pubkey.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/signature.hex create mode 100644 packages/rs-drive-proof-verifier/tests/vectors_contested.rs create mode 100644 packages/rs-drive-proof-verifier/tests/vectors_documents.rs create mode 100644 packages/rs-drive-proof-verifier/tests/vectors_identity.rs create mode 100644 packages/rs-drive-proof-verifier/tests/vectors_quorum_sig.rs diff --git a/Cargo.lock b/Cargo.lock index d448b3594d9..f2c92755680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2274,6 +2274,7 @@ dependencies = [ "platform-serialization", "platform-serialization-derive", "serde", + "serde_json", "tenderdash-abci", "thiserror 2.0.18", "tracing", diff --git a/packages/rs-drive-proof-verifier/Cargo.toml b/packages/rs-drive-proof-verifier/Cargo.toml index 5ef2372aee3..d0246c9a8eb 100644 --- a/packages/rs-drive-proof-verifier/Cargo.toml +++ b/packages/rs-drive-proof-verifier/Cargo.toml @@ -47,3 +47,14 @@ derive_more = { version = "1.0", features = ["from"] } dpp = { path = "../rs-dpp", features = [ "fixtures-and-mocks", ], default-features = false } +dapi-grpc = { path = "../dapi-grpc", default-features = false, features = [ + "platform", + "client", +] } +drive = { path = "../rs-drive", default-features = false, features = [ + "verify", +] } +hex = { version = "0.4.3" } +indexmap = { version = "2.6.0" } +serde = { version = "1.0.219", features = ["derive"] } +serde_json = { version = "1.0" } diff --git a/packages/rs-drive-proof-verifier/tests/common/mod.rs b/packages/rs-drive-proof-verifier/tests/common/mod.rs new file mode 100644 index 00000000000..ba341f6c3b5 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/common/mod.rs @@ -0,0 +1,282 @@ +//! Loader for the proof-vector regression corpus in `tests/vectors/`. +//! +//! Each case directory carries a `manifest.json` (request parameters, block +//! metadata, expected outcome) plus the raw blobs (`proof.hex`, +//! `signature.hex`, `quorum_pubkey.hex`). The corpus was generated from the +//! Dash Core fixture set (`drive_query_vectors.json` / +//! `quorum_sig_vectors.json`, platform v4.0.0 state, protocol version 12); +//! every grovedb proof commits to the same root hash, which the fixture +//! quorum signed, so positive cases run the full grovedb + tenderdash +//! verification pipeline with real key material. + +// Each integration-test binary compiles its own copy of this module and uses +// a different subset of it. +#![allow(dead_code)] + +use std::path::PathBuf; +use std::sync::Arc; + +use dapi_grpc::platform::v0::{Proof, ResponseMetadata}; +use dpp::dashcore::Network; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::TokenConfiguration; +use dpp::prelude::{CoreBlockHeight, DataContract, Identifier}; +use dpp::system_data_contracts::{load_system_data_contract, SystemDataContract}; +use dpp::version::PlatformVersion; +use drive_proof_verifier::{ContextProvider, ContextProviderError}; +use serde::Deserialize; + +/// The network the fixture chain id (`dash-testnet-51`) belongs to. +pub const NETWORK: Network = Network::Testnet; + +#[derive(Deserialize)] +pub struct Manifest { + pub description: String, + pub request: RequestSpec, + pub block: BlockMeta, + pub proof_meta: ProofMeta, + pub expected: Expected, + #[serde(default)] + pub expected_root_hash_hex: Option, +} + +#[derive(Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum RequestSpec { + IdentityBalance { + identity_id: String, + }, + IdentityNonce { + identity_id: String, + }, + IdentityContractNonce { + identity_id: String, + contract_id: String, + }, + IdentityKeys { + identity_id: String, + }, + DocumentsDpnsExact { + normalized_label: String, + limit: u16, + }, + DocumentsDpnsPrefix { + normalized_prefix: String, + limit: u16, + }, + DocumentsDashpayProfile { + owner_id: String, + }, + DocumentsDashpayContacts { + identity_id: String, + to_identity: bool, + limit: u16, + }, + ContestedVoteState { + contract_id: String, + document_type_name: String, + index_name: String, + index_values: Vec, + count: u16, + }, +} + +#[derive(Deserialize)] +pub struct BlockMeta { + pub height: u64, + pub core_chain_locked_height: u32, + pub epoch: u32, + pub time_ms: u64, + pub protocol_version: u32, + pub chain_id: String, +} + +#[derive(Deserialize)] +pub struct ProofMeta { + pub round: u32, + pub quorum_type: u32, + pub quorum_hash_hex: String, + pub block_id_hash_hex: String, +} + +#[derive(Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum Expected { + IdentityBalance { + balance: u64, + }, + IdentityNonce { + nonce: u64, + }, + IdentityContractNonce { + nonce: u64, + }, + IdentityKeys { + serialized_keys: Vec, + }, + /// The fixture grovedb state stores placeholder payloads at document + /// positions; the grovedb layer must verify and yield exactly these + /// bytes, and decoding them as DPP documents must fail cleanly. + DocumentsPlaceholder { + serialized_documents: Vec, + }, + Contested { + contenders: Vec, + abstain_votes: Option, + lock_votes: Option, + finished: bool, + winner_identity_id: Option, + }, + ContestedAbsent, + Error { + class: ErrorClass, + }, +} + +#[derive(Deserialize)] +pub struct ExpectedContender { + pub identity_id: String, + pub votes: Option, +} + +#[derive(Deserialize, Clone, Copy, PartialEq, Eq, Debug)] +#[serde(rename_all = "snake_case")] +pub enum ErrorClass { + InvalidSignature, + ProofInvalid, +} + +pub struct Case { + pub name: String, + pub manifest: Manifest, + pub grovedb_proof: Vec, + pub signature: Vec, + pub quorum_pubkey: [u8; 48], +} + +pub fn hex_vec(s: &str) -> Vec { + hex::decode(s.trim()).expect("corpus hex blob must decode") +} + +pub fn hex32(s: &str) -> [u8; 32] { + hex_vec(s).try_into().expect("expected 32 bytes of hex") +} + +pub fn identifier(s: &str) -> Identifier { + Identifier::from_bytes(&hex_vec(s)).expect("corpus identifier must be 32 bytes") +} + +pub fn load_case(name: &str) -> Case { + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/vectors") + .join(name); + let read = |file: &str| { + std::fs::read_to_string(dir.join(file)) + .unwrap_or_else(|e| panic!("read corpus file {name}/{file}: {e}")) + }; + let manifest: Manifest = + serde_json::from_str(&read("manifest.json")).expect("parse corpus manifest"); + Case { + name: name.to_string(), + grovedb_proof: hex_vec(&read("proof.hex")), + signature: hex_vec(&read("signature.hex")), + quorum_pubkey: hex_vec(&read("quorum_pubkey.hex")) + .try_into() + .expect("quorum public key must be 48 bytes"), + manifest, + } +} + +impl Case { + /// The tenderdash proof envelope for the DAPI response. + pub fn grpc_proof(&self) -> Proof { + Proof { + grovedb_proof: self.grovedb_proof.clone(), + quorum_hash: hex_vec(&self.manifest.proof_meta.quorum_hash_hex), + signature: self.signature.clone(), + round: self.manifest.proof_meta.round, + block_id_hash: hex_vec(&self.manifest.proof_meta.block_id_hash_hex), + quorum_type: self.manifest.proof_meta.quorum_type, + } + } + + /// The response metadata (block context the quorum signed over). + pub fn metadata(&self) -> ResponseMetadata { + ResponseMetadata { + height: self.manifest.block.height, + core_chain_locked_height: self.manifest.block.core_chain_locked_height, + epoch: self.manifest.block.epoch, + time_ms: self.manifest.block.time_ms, + protocol_version: self.manifest.block.protocol_version, + chain_id: self.manifest.block.chain_id.clone(), + } + } + + /// The platform version the vectors were generated with. The proofs are + /// self-contained, so they must keep verifying under this version even + /// as the crate's latest version moves on. + pub fn platform_version(&self) -> &'static PlatformVersion { + PlatformVersion::get(self.manifest.block.protocol_version) + .expect("corpus protocol version must be known") + } + + /// A [ContextProvider] serving this case's quorum public key and the + /// system data contracts referenced by the fixture proofs. + pub fn provider(&self) -> VectorContextProvider { + VectorContextProvider { + quorum_type: self.manifest.proof_meta.quorum_type, + quorum_hash: hex32(&self.manifest.proof_meta.quorum_hash_hex), + quorum_pubkey: self.quorum_pubkey, + } + } +} + +/// [ContextProvider] backed by the per-case corpus quorum key material. +pub struct VectorContextProvider { + quorum_type: u32, + quorum_hash: [u8; 32], + quorum_pubkey: [u8; 48], +} + +impl ContextProvider for VectorContextProvider { + fn get_data_contract( + &self, + id: &Identifier, + platform_version: &PlatformVersion, + ) -> Result>, ContextProviderError> { + for system_contract in [SystemDataContract::DPNS, SystemDataContract::Dashpay] { + let contract = load_system_data_contract(system_contract, platform_version) + .map_err(|e| ContextProviderError::DataContractFailure(e.to_string()))?; + if contract.id() == *id { + return Ok(Some(Arc::new(contract))); + } + } + Ok(None) + } + + fn get_token_configuration( + &self, + _token_id: &Identifier, + ) -> Result, ContextProviderError> { + Ok(None) + } + + fn get_quorum_public_key( + &self, + quorum_type: u32, + quorum_hash: [u8; 32], + _core_chain_locked_height: u32, + ) -> Result<[u8; 48], ContextProviderError> { + if quorum_type != self.quorum_type || quorum_hash != self.quorum_hash { + return Err(ContextProviderError::InvalidQuorum(format!( + "unexpected quorum requested: type {quorum_type}, hash {}", + hex::encode(quorum_hash) + ))); + } + Ok(self.quorum_pubkey) + } + + fn get_platform_activation_height(&self) -> Result { + Ok(1) + } +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/README.md b/packages/rs-drive-proof-verifier/tests/vectors/README.md new file mode 100644 index 00000000000..e47fc391923 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/README.md @@ -0,0 +1,35 @@ +# Proof-vector regression corpus + +Fixture cases generated from a real Drive state (platform v4.0.0 fixtures, +protocol version 12, grovedb 5.0.0), replayed through the crate's public +`FromProof` entry points. The same fixtures are replayed byte-exact by Dash +Core's platform GUI implementation, so drift between what Drive proves and +what any client verifies fails loudly here. + +Each case directory contains `manifest.json` (request parameters, block +metadata, expected outcome, pinned root hash) plus `proof.hex`, +`signature.hex`, and `quorum_pubkey.hex`. Loaders live in +`../common/mod.rs`; the suite is gated behind the `mocks` feature. + +## Coverage matrix — what each family actually exercises + +| family | grovedb proof replay | tenderdash BLS check | notes | +|---|---|---|---| +| identity (4 cases) | ✅ | ✅ | full pipeline through `FromProof` | +| contested vote state (3) | ✅ | ✅ | incl. `Ok(None)` proof-of-absence | +| quorum-sig (4 fixtures / 7 cases) | ✅ | ✅ | 1 positive + 3 negatives on-disk (tampered sig, wrong key, wrong block-id hash) + 3 in-test negatives that tamper the `quorum-sig-valid` response's `ResponseMetadata` (height, time_ms, core_chain_locked_height) to prove those fields are inside the signed `StateId` too | +| documents / DPNS / DashPay (4) | ✅ | ❌ (not reached) | fixture state stores placeholder payloads at document positions, so `FromProof` fails at document decode *before* the signature check; these cases pin the `DriveDocumentQuery` shape (root hash + serialized payloads byte-for-byte via `verify_proof_keep_serialized`) and the clean-`Err` decode failure | +| identity-balance corrupted proof (1) | ✅ (rejects) | ❌ (not reached) | negative: bit-flipped proof fails as a GroveDB error | + +The `quorum-sig-valid` case shares its proof bytes with `identity-balance` +(the corpus has 15 distinct fixtures across 16 on-disk cases): all drive +fixtures commit to the same root hash, which is exactly the app hash the +quorum signature signs — that is what lets the positive cases run a genuine +BLS verification with real fixture key material. The 3 metadata-tamper +negatives in `vectors_quorum_sig.rs` reuse `quorum-sig-valid`'s on-disk bytes +and mutate the `ResponseMetadata` in-test rather than adding new fixture +directories, bringing the total to 19 `#[test]` functions over 16 fixture +directories. + +Regenerate only deliberately (fixture-generation lives with the Dash Core +platform GUI's vector tooling); a regeneration should be its own commit. diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/manifest.json new file mode 100644 index 00000000000..b34245cddd1 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/manifest.json @@ -0,0 +1,37 @@ +{ + "description": "Absent DPNS name contest ('carol'): proof of non-existence, FromProof returns Ok(None).", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "contested_vote_state", + "contract_id": "e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155", + "document_type_name": "domain", + "index_name": "parentNameAndLabel", + "index_values": [ + "dash", + "carol" + ], + "count": 100 + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "contested_absent" + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/proof.hex new file mode 100644 index 00000000000..48db66e0a38 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/proof.hex @@ -0,0 +1 @@ +0100b201039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a02dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1004017000050201016300ade908a28152707d22ed66d575d6f3ce38c27447c295a245272dc100dd0c4a621111010170002a040163000502010170001e5c60ed1b4311b6baf76a42f41550bcbba0f9f87d71b48e76d800c95f22cccc01016300490401700024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155005401be78056200f8a616eddb15fbd41fbc4204330111709350c20535528492b5010170004e0420e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155000a020106646f6d61696e004412968f90b3c569b0e5b9760130f021137946563c0d05c6cb64233e4a35a57f0120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155002f0406646f6d61696e00050201010100bba90275bf4b39ce726f641378ede6c4fcd17162611f992a34fea2ad303f4eeb0106646f6d61696e002d0401010008020104646173680060b4e46b6e0fca7c9c1634f80d1d3a18a9188165d64e81782800392ba3f08c5901010100310404646173680009020105616c6963650078f7ad26be337eb5f110a07a8d21736265aef4cb370c25b3aaa3f8d354ee191101046461736800470246f1e3eb733288ba8ee508b3b972872149b7ee87c6b0c05bbd4bb5982d93e7140503626f62818e82b602d0fff0c045f136090f3816039b90af1ac6f33f3ec84deba88908d61100 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-absent/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/manifest.json new file mode 100644 index 00000000000..01ea67f5d3b --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/manifest.json @@ -0,0 +1,51 @@ +{ + "description": "Active DPNS name contest ('alice'): two contenders plus abstain/lock tallies, no winner yet.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "contested_vote_state", + "contract_id": "e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155", + "document_type_name": "domain", + "index_name": "parentNameAndLabel", + "index_values": [ + "dash", + "alice" + ], + "count": 100 + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "contested", + "contenders": [ + { + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777", + "votes": 5 + }, + { + "identity_id": "8888888888888888888888888888888888888888888888888888888888888888", + "votes": 2 + } + ], + "abstain_votes": 2, + "lock_votes": 3, + "finished": false, + "winner_identity_id": null + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/proof.hex new file mode 100644 index 00000000000..e88ff11ea0a --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/proof.hex @@ -0,0 +1 @@ +0100b201039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a02dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1004017000050201016300ade908a28152707d22ed66d575d6f3ce38c27447c295a245272dc100dd0c4a621111010170002a040163000502010170001e5c60ed1b4311b6baf76a42f41550bcbba0f9f87d71b48e76d800c95f22cccc01016300490401700024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155005401be78056200f8a616eddb15fbd41fbc4204330111709350c20535528492b5010170004e0420e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155000a020106646f6d61696e004412968f90b3c569b0e5b9760130f021137946563c0d05c6cb64233e4a35a57f0120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155002f0406646f6d61696e00050201010100bba90275bf4b39ce726f641378ede6c4fcd17162611f992a34fea2ad303f4eeb0106646f6d61696e002d0401010008020104646173680060b4e46b6e0fca7c9c1634f80d1d3a18a9188165d64e81782800392ba3f08c5901010100310404646173680009020105616c6963650078f7ad26be337eb5f110a07a8d21736265aef4cb370c25b3aaa3f8d354ee1911010464617368006f0405616c6963650024020120777777777777777777777777777777777777777777777777777777777777777700ba2fead8aaa37d1be007db47f2e9779b773387336b1fcb738551a2a8ca295ef201a5ddbf84df65edf5876f0141adfa01d1988f011c18020496c3e93599f57dd5d5110105616c69636500fb016703200000000000000000000000000000000000000000000000000000000000000000001b0018000003fd0000018bcfe56800fc0001e078fc001e84760400000420000000000000000000000000000000000000000000000000000000000000000100050201010100ac8e80c26488285c9ef7ce4167825d9e9bef26e737bb7f7f1340d69af3a8eb5910042000000000000000000000000000000000000000000000000000000000000000020005020101010012ec6171d927bc5d9f132f407acd3ab0e75049f021389f6430dd8d4ba87ce26d1104207777777777777777777777777777777777777777777777777777777777777777000502010100008bbee91750be1729b067ae752c218d46345cee65af3996e02cc4aa079d9b26fd10042088888888888888888888888888888888888888888888888888888888888888880005020101000092fab297d83548fabdbefbad2d5e3c26a4005332274f31ff9cd51c9436d401971104200000000000000000000000000000000000000000000000000000000000000001006b1c01010025040120c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c10400c1d2a7d2d0355e6a6e662719e9e8475444c47022e4f1eddddfb5b45a8686acea006a29c24ef5eb604e398952790112a98973d5485490ab94ec88844dd50ac7525100200000000000000000000000000000000000000000000000000000000000000002006b1c01010025040120c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2060035878fa5d0c12a20364a12f71fc2124b8ddc22d9d81368bd79b20400da6eee8800efe92f879d4c44f631bb52241d7606b85bbbab020cb55fd4395a887bd97101db00207777777777777777777777777777777777777777777777777777777777777777008d025ef5cd4abe01c5cc27c851dca48ca596628dfc45b454ee847bcfc824f17312b61c01010025040120a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a10a00f5b15a04f171a7a3fe3cf9dc367b0bfe597fea84cd7239384715570ce4551fe800a706a5fc391f817996f9e274f0b37ff0b8a61b579bf079a8da92846c9252d5511100208888888888888888888888888888888888888888888888888888888888888888008d02e6ee602462484dae8b0c314ddc157efc77b293ea2350cb615962612be3d768bf1c01010025040120b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b104002bb7188bd669db6e98f44ee502c9742d85da29c4e7824b4fbd27f9587ad2758a009b9c598e017cb597ea60903480c868289dd951ce282f67f97459c79366a9d0d31100 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-active/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/manifest.json new file mode 100644 index 00000000000..5cff9b4d2e4 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/manifest.json @@ -0,0 +1,51 @@ +{ + "description": "Finished DPNS name contest ('bob'): winner recorded with finalization block info.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "contested_vote_state", + "contract_id": "e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155", + "document_type_name": "domain", + "index_name": "parentNameAndLabel", + "index_values": [ + "dash", + "bob" + ], + "count": 100 + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "contested", + "contenders": [ + { + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777", + "votes": 5 + }, + { + "identity_id": "8888888888888888888888888888888888888888888888888888888888888888", + "votes": 2 + } + ], + "abstain_votes": 2, + "lock_votes": 1, + "finished": true, + "winner_identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/proof.hex new file mode 100644 index 00000000000..300633f010e --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/proof.hex @@ -0,0 +1 @@ +0100b201039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a02dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1004017000050201016300ade908a28152707d22ed66d575d6f3ce38c27447c295a245272dc100dd0c4a621111010170002a040163000502010170001e5c60ed1b4311b6baf76a42f41550bcbba0f9f87d71b48e76d800c95f22cccc01016300490401700024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155005401be78056200f8a616eddb15fbd41fbc4204330111709350c20535528492b5010170004e0420e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155000a020106646f6d61696e004412968f90b3c569b0e5b9760130f021137946563c0d05c6cb64233e4a35a57f0120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155002f0406646f6d61696e00050201010100bba90275bf4b39ce726f641378ede6c4fcd17162611f992a34fea2ad303f4eeb0106646f6d61696e002d0401010008020104646173680060b4e46b6e0fca7c9c1634f80d1d3a18a9188165d64e81782800392ba3f08c5901010100310404646173680009020105616c6963650078f7ad26be337eb5f110a07a8d21736265aef4cb370c25b3aaa3f8d354ee1911010464617368006d0246f1e3eb733288ba8ee508b3b972872149b7ee87c6b0c05bbd4bb5982d93e7140403626f620024020120000000000000000000000000000000000000000000000000000000000000000000818e82b602d0fff0c045f136090f3816039b90af1ac6f33f3ec84deba88908d6110103626f6200fb018403200000000000000000000000000000000000000000000000000000000000000000016000fb015b00010400777777777777777777777777777777777777777777777777777777777777777702a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a104a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a20100888888888888888888888888888888888888888888888888888888888888888801b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1020101c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1020201c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c201fd0000018bcfe56800fc0001e078fc001e847604fd0000018bcfed0920fc0001e208fc001e847f04017777777777777777777777777777777777777777777777777777777777777777017777777777777777777777777777777777777777777777777777777777777777000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/contested-vote-state-finished/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/manifest.json new file mode 100644 index 00000000000..024981b8176 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/manifest.json @@ -0,0 +1,35 @@ +{ + "description": "Dashpay incoming contact requests query proof (toUserId); grovedb layer verifies, placeholder document payload must fail decoding cleanly.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "documents_dashpay_contacts", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777", + "to_identity": true, + "limit": 100 + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "documents_placeholder", + "serialized_documents": [ + "70726f7665642d636f6e746163742d646f63756d656e74" + ] + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/proof.hex new file mode 100644 index 00000000000..e24661821a4 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/proof.hex @@ -0,0 +1 @@ +0100d101039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f237100401400024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315500fbda163cb3ac4d5ca76ccab587620f53bdcbe46a96dbcb1e0ed411ace9395b2202dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010140006b0420a2a1b4ac6fef22ea2a1a68e8123644b357875f6b412c18109281c146e7b271bc000502010101005b6467dac53627bd556c06b7de5e97bd2d50688788cc29c251144d866715c4bf02b8d9d2e8f03bb4b317f5fc24e2741a89ea9f5bb6cb43a44ebbf99bf056fc6105100120a2a1b4ac6fef22ea2a1a68e8123644b357875f6b412c18109281c146e7b271bc0030040101000b02010770726f66696c650084e1535a92501db14221531aacd7009467348130c1807153e9b3af041b75f6140101010060040e636f6e7461637452657175657374000c020108246f776e6572496400c59531534d16dc4ec238c5c8a30fb6414b28cdbb977c82c77bfacf55ab8d3dcd028434a8d562d84df39d1971af05bd8fdd748581b35202e723eda1d4f737d7e93710010e636f6e74616374526571756573740094014b4c08659a34e5bccbbc817edffcec379dc3300ad974a50d539307d25bd505c202241ddcbc852479eec397f2fd32bcaf69ebadd79675ddbf92504d7e42436c1774100408746f5573657249640024020120777777777777777777777777777777777777777777777777777777777777777700902c3fed2d96edf795511e59795d4fa6fd6d36220a32a9aa13450e9562c5018c110108746f557365724964005204207777777777777777777777777777777777777777777777777777777777777777000e02010a2463726561746564417400c75709cd8917bb5116f06f15c1b88c22917b42e472c4f4ea2fecfe993291976b01207777777777777777777777777777777777777777777777777777777777777777003a040a24637265617465644174000c0201080000018bcfe56800005d8532c133405f01f9a65173c587d6a363a865b0c1e6fdc6a6b8b52d13f68575010a24637265617465644174003104080000018bcfe56800000502010100000dedbe3b16265a48243462cef6c2fbcdfcad43aed206f5310dbc84ad1a39d98901080000018bcfe5680000490401000024020120d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d300a2ab6c494b389894e8a4efe5a4e0673566880fd6a1d376962621108ced3d6120010100005e0620d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3001a001770726f7665642d636f6e746163742d646f63756d656e740007547e597229008c8eed5a86719458be289d9d088f8943866923c9c3cd230da400 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-contacts-incoming/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/manifest.json new file mode 100644 index 00000000000..45fbfbbb511 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/manifest.json @@ -0,0 +1,33 @@ +{ + "description": "Dashpay profile-by-owner query proof; grovedb layer verifies, placeholder document payload must fail decoding cleanly.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "documents_dashpay_profile", + "owner_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "documents_placeholder", + "serialized_documents": [ + "70726f7665642d70726f66696c652d646f63756d656e74" + ] + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/proof.hex new file mode 100644 index 00000000000..5a262525560 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/proof.hex @@ -0,0 +1 @@ +0100d101039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f237100401400024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315500fbda163cb3ac4d5ca76ccab587620f53bdcbe46a96dbcb1e0ed411ace9395b2202dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010140006b0420a2a1b4ac6fef22ea2a1a68e8123644b357875f6b412c18109281c146e7b271bc000502010101005b6467dac53627bd556c06b7de5e97bd2d50688788cc29c251144d866715c4bf02b8d9d2e8f03bb4b317f5fc24e2741a89ea9f5bb6cb43a44ebbf99bf056fc6105100120a2a1b4ac6fef22ea2a1a68e8123644b357875f6b412c18109281c146e7b271bc0030040101000b02010770726f66696c650084e1535a92501db14221531aacd7009467348130c1807153e9b3af041b75f61401010100520181acb67debb4c2c3aeaf6481bba22c23e59118b587c1adffcbe3c42e4754a6d5040770726f66696c650005020101000064064ce41f142aa0218b83251b9511d2d4affbb8012187cb1348f2062e61008710010770726f66696c65007202b077d63006fc15cfb2db05ddbee6d6dd7cf56291575215cee5216f7f1552a1250408246f776e657249640024020120777777777777777777777777777777777777777777777777777777777777777700282c1c78c773ac1bbcd84b55ad6bc8aa39653642a01744998b92045ce37949c7110108246f776e657249640049042077777777777777777777777777777777777777777777777777777777777777770005020101000071ea7f7cb98ab3eb3eae2895ce3a94c2714e806bb98c0e8c1177dde712022e8a01207777777777777777777777777777777777777777777777777777777777777777003f060100001a001770726f7665642d70726f66696c652d646f63756d656e7400da1581c51512f64b6e931eff13bd4fb05003f6befbbbc3b2962d208a582958df00 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dashpay-profile/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/manifest.json new file mode 100644 index 00000000000..6dcd1bb8c90 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/manifest.json @@ -0,0 +1,34 @@ +{ + "description": "DPNS domain-by-exact-label query proof (label 'alice'); grovedb layer verifies, placeholder document payload must fail decoding cleanly.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "documents_dpns_exact", + "normalized_label": "alice", + "limit": 1 + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "documents_placeholder", + "serialized_documents": [ + "70726f7665642d64706e732d646f63756d656e74" + ] + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/proof.hex new file mode 100644 index 00000000000..c857a730a41 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/proof.hex @@ -0,0 +1 @@ +0100d101039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f237100401400024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315500fbda163cb3ac4d5ca76ccab587620f53bdcbe46a96dbcb1e0ed411ace9395b2202dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010140006b013af9b5c483d29ba0cb144b3c8d0b00acb6894c2ba54526ee525ecba262f25bbe0420e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155000502010101005dfffca61718377c8e28755c8c4c41cd3281e43700cc17ef2fb2ce858f8d9e08100120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155002f040101000a020106646f6d61696e00aef97d6723e7dfdd1a37d4051a65d1851a7183ee391f51b154add1042479806001010100480406646f6d61696e001e02011a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6500c0db47fafbb7b914aca5112d697ebb013ac3f9de326d8135d68029a191e89cb20106646f6d61696e008a01bb6f7c477bd80200888c4db39eee8829d38f1ab953cc25c13f82c05fc5b25f32041a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6500080201046461736800aeb4ddee9352a25186598ca471577e45325bd9cf1b02c80bb31d03ab58ba336810014fd3d5c7b045c62f2ac81c9f51f067a4450901d41999b0f82a3f15b103df4a6a11011a6e6f726d616c697a6564506172656e74446f6d61696e4e616d65003b040464617368001302010f6e6f726d616c697a65644c6162656c000127fd05259b354ce1f58158158364d18a5a6c9f07227e9170a8ebda0e5e7fa3010464617368003c040f6e6f726d616c697a65644c6162656c0009020105616c696365001e7beb5cde43e295777634dc595f1536f62e2885343dca251fd9c3551285b8f6010f6e6f726d616c697a65644c6162656c002e0405616c69636500050201010000ecb69a79bd697e495ef181171cb50b0443bdc72b78e3c0446e75ae9f68ffeb2c0105616c696365003c0601000017001470726f7665642d64706e732d646f63756d656e7400a711904e44b1b6aec95a008065047732ea534162d3e3e0934617a6faa218739500 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-exact/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/manifest.json new file mode 100644 index 00000000000..f84480e2ed7 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/manifest.json @@ -0,0 +1,34 @@ +{ + "description": "DPNS domain-by-prefix query proof (prefix 'ali', limit 25); grovedb layer verifies, placeholder document payload must fail decoding cleanly.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "documents_dpns_prefix", + "normalized_prefix": "ali", + "limit": 25 + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "documents_placeholder", + "serialized_documents": [ + "70726f7665642d64706e732d646f63756d656e74" + ] + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/proof.hex new file mode 100644 index 00000000000..c857a730a41 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/proof.hex @@ -0,0 +1 @@ +0100d101039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f237100401400024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315500fbda163cb3ac4d5ca76ccab587620f53bdcbe46a96dbcb1e0ed411ace9395b2202dae5ae1465037089786f368eee27aa0437d77fbaf34d14dd299aae3d6a54168b1001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010140006b013af9b5c483d29ba0cb144b3c8d0b00acb6894c2ba54526ee525ecba262f25bbe0420e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155000502010101005dfffca61718377c8e28755c8c4c41cd3281e43700cc17ef2fb2ce858f8d9e08100120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155002f040101000a020106646f6d61696e00aef97d6723e7dfdd1a37d4051a65d1851a7183ee391f51b154add1042479806001010100480406646f6d61696e001e02011a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6500c0db47fafbb7b914aca5112d697ebb013ac3f9de326d8135d68029a191e89cb20106646f6d61696e008a01bb6f7c477bd80200888c4db39eee8829d38f1ab953cc25c13f82c05fc5b25f32041a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6500080201046461736800aeb4ddee9352a25186598ca471577e45325bd9cf1b02c80bb31d03ab58ba336810014fd3d5c7b045c62f2ac81c9f51f067a4450901d41999b0f82a3f15b103df4a6a11011a6e6f726d616c697a6564506172656e74446f6d61696e4e616d65003b040464617368001302010f6e6f726d616c697a65644c6162656c000127fd05259b354ce1f58158158364d18a5a6c9f07227e9170a8ebda0e5e7fa3010464617368003c040f6e6f726d616c697a65644c6162656c0009020105616c696365001e7beb5cde43e295777634dc595f1536f62e2885343dca251fd9c3551285b8f6010f6e6f726d616c697a65644c6162656c002e0405616c69636500050201010000ecb69a79bd697e495ef181171cb50b0443bdc72b78e3c0446e75ae9f68ffeb2c0105616c696365003c0601000017001470726f7665642d64706e732d646f63756d656e7400a711904e44b1b6aec95a008065047732ea534162d3e3e0934617a6faa218739500 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/dpns-domain-prefix/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/manifest.json new file mode 100644 index 00000000000..0cbd1fb8d0f --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/manifest.json @@ -0,0 +1,30 @@ +{ + "description": "Identity balance proof with one bit flipped mid-stream; grovedb verification must fail with a clean error (no panic), and any surviving root hash cannot match the signed app hash.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_balance", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "error", + "class": "proof_invalid" + } +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/proof.hex new file mode 100644 index 00000000000..34afee48bc6 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/proof.hex @@ -0,0 +1 @@ +0100da01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a040160002d0401207777777777777777777777777777777777777777777777777777767777777777fd00000002540be400006ab14e39df7d262e55bae17e60bab5d996cb66d6ded567f4d445ba536eef3b931001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010160002f03207777777777777777777777777777777777777777777777777777777777777777000b03fd00000002540be4000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance-corrupted-proof/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/manifest.json new file mode 100644 index 00000000000..3daf42e7b0e --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/manifest.json @@ -0,0 +1,31 @@ +{ + "description": "Balance proof for the fixture identity; positive end-to-end verification.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_balance", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "identity_balance", + "balance": 5000000000 + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/proof.hex new file mode 100644 index 00000000000..255f940a0c5 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/proof.hex @@ -0,0 +1 @@ +0100da01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a040160002d0401207777777777777777777777777777777777777777777777777777777777777777fd00000002540be400006ab14e39df7d262e55bae17e60bab5d996cb66d6ded567f4d445ba536eef3b931001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010160002f03207777777777777777777777777777777777777777777777777777777777777777000b03fd00000002540be4000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-balance/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/manifest.json new file mode 100644 index 00000000000..ee623fa84ed --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/manifest.json @@ -0,0 +1,32 @@ +{ + "description": "Identity contract nonce proof against the DPNS contract; positive.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_contract_nonce", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777", + "contract_id": "e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "identity_contract_nonce", + "nonce": 11 + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/proof.hex new file mode 100644 index 00000000000..8a8943d0c2e --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/proof.hex @@ -0,0 +1 @@ +01008d01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a0401200024020120777777777777777777777777777777777777777777777777777777777777777700a2f8eb7d55888b059806841f7a2e92d211d65049ed09fe2bab4d1cd1f72aa0e41001eb804ecd6886810d1d7c79a0f1b6b35791037224de02c64c11ce42338455528d1101012000490420777777777777777777777777777777777777777777777777777777777777777700050201018000aee59dc5acac5fe8d4b452c1fd0f22601b6c1d876e28d0e3512a2ff39d18bcee0120777777777777777777777777777777777777777777777777777777777777777700af0401200024020120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155008b708962e5e42245c8220f3a06dbcc9ba3a7e655df4fed3dba6e8cff9426f43d024e6a60642818a48d5f2e7c3647a08b69f48d05941c95aaa24536b810b3c49b681002cbeddd5e01bcb90c5002f6a710bc7957af406d2a6a9df600dbfa36951abf3534100185d286a688ac17d96cdb2880a8f7174e151dfb90742370effeeaae391ee359df1101012000490420e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315500050201010000411ce8bb44ce6a4f4169880f3111dbad167ccd51ab08199b006247e83b1fd1510120e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c531550010030100000b0008000000000000000b0000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-contract-nonce/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/manifest.json new file mode 100644 index 00000000000..f3d066a6de2 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/manifest.json @@ -0,0 +1,35 @@ +{ + "description": "All-keys proof for the fixture identity; keys compared byte-for-byte.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_keys", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "identity_keys", + "serialized_keys": [ + "0000000000000021034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa00", + "000100020000002102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f2700", + "0002010300000021035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c3001fd0000018bcfe5687b" + ] + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/proof.hex new file mode 100644 index 00000000000..8a623ed819b --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/proof.hex @@ -0,0 +1 @@ +01008d01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a0401200024020120777777777777777777777777777777777777777777777777777777777777777700a2f8eb7d55888b059806841f7a2e92d211d65049ed09fe2bab4d1cd1f72aa0e41001eb804ecd6886810d1d7c79a0f1b6b35791037224de02c64c11ce42338455528d1101012000490420777777777777777777777777777777777777777777777777777777777777777700050201018000aee59dc5acac5fe8d4b452c1fd0f22601b6c1d876e28d0e3512a2ff39d18bcee01207777777777777777777777777777777777777777777777777777777777777777006e017b204423f6e41b597890ac921d8f72453128cad7e268750f6fc87d648bcc72aa04018000050201010100d8e4a96433656c05d1cfef00c047f123db4140e64fd3e509993825149a64a5d0100185d286a688ac17d96cdb2880a8f7174e151dfb90742370effeeaae391ee359df1101018000a1030100002d002a0000000000000021034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0000030101002d002a000100020000002102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27000010030102003600330002010300000021035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c3001fd0000018bcfe5687b001100 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-keys/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/manifest.json new file mode 100644 index 00000000000..4930c4ee341 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/manifest.json @@ -0,0 +1,31 @@ +{ + "description": "Identity nonce proof; positive end-to-end verification.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_nonce", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "identity_nonce", + "nonce": 7 + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/proof.hex new file mode 100644 index 00000000000..78436a05fef --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/proof.hex @@ -0,0 +1 @@ +01008d01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a0401200024020120777777777777777777777777777777777777777777777777777777777777777700a2f8eb7d55888b059806841f7a2e92d211d65049ed09fe2bab4d1cd1f72aa0e41001eb804ecd6886810d1d7c79a0f1b6b35791037224de02c64c11ce42338455528d1101012000490420777777777777777777777777777777777777777777777777777777777777777700050201018000aee59dc5acac5fe8d4b452c1fd0f22601b6c1d876e28d0e3512a2ff39d18bcee01207777777777777777777777777777777777777777777777777777777777777777007601b44b3a90d97444a14074c2bf69ff971d88fd27e3ba2da8b11cf3910f087dedf6030140000b00080000000000000007001002cbeddd5e01bcb90c5002f6a710bc7957af406d2a6a9df600dbfa36951abf3534100185d286a688ac17d96cdb2880a8f7174e151dfb90742370effeeaae391ee359df1100 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/identity-nonce/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/manifest.json new file mode 100644 index 00000000000..2198d5ccc35 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/manifest.json @@ -0,0 +1,30 @@ +{ + "description": "One bit flipped in the BLS signature; verification must fail with an invalid-signature error.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_balance", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "error", + "class": "invalid_signature" + } +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/proof.hex new file mode 100644 index 00000000000..255f940a0c5 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/proof.hex @@ -0,0 +1 @@ +0100da01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a040160002d0401207777777777777777777777777777777777777777777777777777777777777777fd00000002540be400006ab14e39df7d262e55bae17e60bab5d996cb66d6ded567f4d445ba536eef3b931001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010160002f03207777777777777777777777777777777777777777777777777777777777777777000b03fd00000002540be4000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/signature.hex new file mode 100644 index 00000000000..e22b25531cb --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-tampered-signature/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da83d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/manifest.json new file mode 100644 index 00000000000..54af7a37e8b --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/manifest.json @@ -0,0 +1,31 @@ +{ + "description": "Envelope grovedb proof with a valid quorum signature; full tenderdash verification must pass.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_balance", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "identity_balance", + "balance": 5000000000 + }, + "expected_root_hash_hex": "dad905d8fddd7a31089ed57521ff006ec5946b5648d48056bce493357675ab72" +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/proof.hex new file mode 100644 index 00000000000..255f940a0c5 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/proof.hex @@ -0,0 +1 @@ +0100da01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a040160002d0401207777777777777777777777777777777777777777777777777777777777777777fd00000002540be400006ab14e39df7d262e55bae17e60bab5d996cb66d6ded567f4d445ba536eef3b931001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010160002f03207777777777777777777777777777777777777777777777777777777777777777000b03fd00000002540be4000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-valid/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/manifest.json new file mode 100644 index 00000000000..3b49b649f4b --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/manifest.json @@ -0,0 +1,30 @@ +{ + "description": "Proof metadata carries a block id hash the quorum never signed; must fail with an invalid-signature error.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_balance", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215171b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "error", + "class": "invalid_signature" + } +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/proof.hex new file mode 100644 index 00000000000..255f940a0c5 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/proof.hex @@ -0,0 +1 @@ +0100da01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a040160002d0401207777777777777777777777777777777777777777777777777777777777777777fd00000002540be400006ab14e39df7d262e55bae17e60bab5d996cb66d6ded567f4d445ba536eef3b931001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010160002f03207777777777777777777777777777777777777777777777777777777777777777000b03fd00000002540be4000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/quorum_pubkey.hex new file mode 100644 index 00000000000..a1912bdde28 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/quorum_pubkey.hex @@ -0,0 +1 @@ +b6087e2054b847ec64a1e18af3930d39fb040b73eb8a10620f9adcb1be85bf7d5ea961dc2458d601ba9003b1e39ead15 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-block-id-hash/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/manifest.json b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/manifest.json new file mode 100644 index 00000000000..af9c549bde3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/manifest.json @@ -0,0 +1,30 @@ +{ + "description": "Valid signature checked against a different quorum's public key; must fail with an invalid-signature error.", + "source": { + "platform_repo_tag": "v4.0.0", + "grovedb_repo_tag": "v5.0.0", + "generated_protocol_version": 12 + }, + "request": { + "type": "identity_balance", + "identity_id": "7777777777777777777777777777777777777777777777777777777777777777" + }, + "block": { + "height": 123456, + "core_chain_locked_height": 2000000, + "epoch": 0, + "time_ms": 1700000000000, + "protocol_version": 12, + "chain_id": "dash-testnet-51" + }, + "proof_meta": { + "round": 0, + "quorum_type": 106, + "quorum_hash_hex": "01080f161d242b323940474e555c636a71787f868d949ba2a9b0b7bec5ccd3da", + "block_id_hash_hex": "090c0f1215181b1e2124272a2d303336393c3f4245484b4e5154575a5d606366" + }, + "expected": { + "kind": "error", + "class": "invalid_signature" + } +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/proof.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/proof.hex new file mode 100644 index 00000000000..255f940a0c5 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/proof.hex @@ -0,0 +1 @@ +0100da01039118e921c386a7a9c166dbad8a7df9b87fa7f3dc8e3bd4fb42e124cffaa65a02cc9a023abb3efe685ac357116032ee5eb34884d6c3e5861ea7ac662ab511f2371001282192d9f288b3dd8ceeba34fda695ec21d080458ed5d9503eb5916a63494a7a040160002d0401207777777777777777777777777777777777777777777777777777777777777777fd00000002540be400006ab14e39df7d262e55bae17e60bab5d996cb66d6ded567f4d445ba536eef3b931001f61ef78f4632c6b8cb48fd3087644cf1775a597afa62b67660943fccfabd887b1111010160002f03207777777777777777777777777777777777777777777777777777777777777777000b03fd00000002540be4000000 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/quorum_pubkey.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/quorum_pubkey.hex new file mode 100644 index 00000000000..42440c3cdbf --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/quorum_pubkey.hex @@ -0,0 +1 @@ +99d85ad48c7ca9ffbe49f170e444a77ca98035a446c0fe9919180860757ef0d73e8a04abb18f5b67558baed45e748100 diff --git a/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/signature.hex b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/signature.hex new file mode 100644 index 00000000000..0ca209f79f3 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors/quorum-sig-wrong-quorum-key/signature.hex @@ -0,0 +1 @@ +a4fca3d223938deeb9da82d0021a2cfb8cd7463412f7613c8943cd86a4f26b9aa81daf9d218c310ed3bb9e9c6a80b38118d16d27ff981dcc7ca46a65c95be36036eb0e1c4bc31801d2aa5699fc3d9932295f375bec3073e03c4279668429d319 diff --git a/packages/rs-drive-proof-verifier/tests/vectors_contested.rs b/packages/rs-drive-proof-verifier/tests/vectors_contested.rs new file mode 100644 index 00000000000..d812d4dc183 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors_contested.rs @@ -0,0 +1,177 @@ +//! Contested-resource (DPNS name contest) proof-vector regression tests: +//! an active contest, a finished contest with a winner, and proof of +//! absence. Requests are built through [`TryFromRequest::try_to_request`] +//! so the gRPC round-trip the SDK relies on is exercised too. + +#![cfg(feature = "mocks")] + +mod common; + +use common::{identifier, load_case, Case, Expected, RequestSpec, NETWORK}; +use dapi_grpc::platform::v0::{self as platform, get_contested_resource_vote_state_response}; +use dpp::platform_value::Value; +use dpp::voting::vote_info_storage::contested_document_vote_poll_winner_info::ContestedDocumentVotePollWinnerInfo; +use dpp::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll; +use drive::query::vote_poll_vote_state_query::{ + ContestedDocumentVotePollDriveQuery, ContestedDocumentVotePollDriveQueryResultType, +}; +use drive_proof_verifier::from_request::TryFromRequest; +use drive_proof_verifier::types::Contenders; +use drive_proof_verifier::FromProof; + +fn contested_pair( + case: &Case, +) -> ( + platform::GetContestedResourceVoteStateRequest, + platform::GetContestedResourceVoteStateResponse, +) { + let RequestSpec::ContestedVoteState { + contract_id, + document_type_name, + index_name, + index_values, + count, + } = &case.manifest.request + else { + panic!("{}: expected a contested_vote_state request", case.name); + }; + let query = ContestedDocumentVotePollDriveQuery { + vote_poll: ContestedDocumentResourceVotePoll { + contract_id: identifier(contract_id), + document_type_name: document_type_name.clone(), + index_name: index_name.clone(), + index_values: index_values + .iter() + .map(|value| Value::Text(value.clone())) + .collect(), + }, + result_type: ContestedDocumentVotePollDriveQueryResultType::VoteTally, + offset: None, + limit: Some(*count), + start_at: None, + allow_include_locked_and_abstaining_vote_tally: true, + }; + let request = query + .try_to_request() + .expect("contested query must convert to a gRPC request"); + let response = platform::GetContestedResourceVoteStateResponse { + version: Some(get_contested_resource_vote_state_response::Version::V0( + get_contested_resource_vote_state_response::GetContestedResourceVoteStateResponseV0 { + metadata: Some(case.metadata()), + result: Some(get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::Result::Proof( + case.grpc_proof(), + )), + }, + )), + }; + (request, response) +} + +fn verify_contested(case: &Case) -> Option { + let (request, response) = contested_pair(case); + Contenders::maybe_from_proof( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) + .unwrap_or_else(|e| panic!("{}: contested proof must verify: {e}", case.name)) +} + +fn assert_contenders(case: &Case, contenders: &Contenders) { + let Expected::Contested { + contenders: expected_contenders, + abstain_votes, + lock_votes, + .. + } = &case.manifest.expected + else { + panic!("{}: manifest expectation mismatch", case.name); + }; + assert_eq!( + contenders.contenders.len(), + expected_contenders.len(), + "{}: contender count", + case.name + ); + for expected in expected_contenders { + let id = identifier(&expected.identity_id); + let contender = contenders + .contenders + .get(&id) + .unwrap_or_else(|| panic!("{}: contender {} missing", case.name, expected.identity_id)); + assert_eq!( + contender.vote_tally(), + expected.votes, + "{}: votes for {}", + case.name, + expected.identity_id + ); + } + assert_eq!( + contenders.abstain_vote_tally, *abstain_votes, + "{}: abstain votes", + case.name + ); + assert_eq!( + contenders.lock_vote_tally, *lock_votes, + "{}: lock votes", + case.name + ); +} + +#[test] +fn contested_vote_state_active() { + let case = load_case("contested-vote-state-active"); + let contenders = verify_contested(&case).expect("active contest must be found"); + assert_contenders(&case, &contenders); + assert!( + contenders.winner.is_none(), + "active contest must have no winner yet" + ); +} + +#[test] +fn contested_vote_state_finished() { + let case = load_case("contested-vote-state-finished"); + let contenders = verify_contested(&case).expect("finished contest must be found"); + assert_contenders(&case, &contenders); + let Expected::Contested { + finished, + winner_identity_id, + .. + } = &case.manifest.expected + else { + panic!("manifest expectation mismatch"); + }; + assert!(*finished); + let (winner_info, finalization_block) = contenders + .winner + .as_ref() + .expect("finished contest must carry winner info"); + let expected_winner = winner_identity_id + .as_deref() + .map(identifier) + .expect("finished fixture names a winner"); + assert_eq!( + *winner_info, + ContestedDocumentVotePollWinnerInfo::WonByIdentity(expected_winner) + ); + assert!( + finalization_block.time_ms > 0, + "finalization block info must be present" + ); +} + +#[test] +fn contested_vote_state_absent() { + let case = load_case("contested-vote-state-absent"); + let Expected::ContestedAbsent = case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + assert!( + verify_contested(&case).is_none(), + "absent contest must verify as proof of non-existence (Ok(None))" + ); +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors_documents.rs b/packages/rs-drive-proof-verifier/tests/vectors_documents.rs new file mode 100644 index 00000000000..e6eb7753f3d --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors_documents.rs @@ -0,0 +1,251 @@ +//! Documents-family (DPNS / Dashpay) proof-vector regression tests. +//! +//! The fixture grovedb state stores placeholder ASCII payloads at the +//! document positions (real documents were not re-fixtured for this corpus), +//! so neither half of these vectors can run a full end-to-end positive +//! verification the way the identity/contested families do. Instead they +//! pin two narrower, still-real things: +//! +//! 1. the [`DriveDocumentQuery`] shape each DAPI query maps to still matches +//! the proof the server generated. This is checked with `drive`'s own +//! `verify_proof_keep_serialized` -- a lower-level call on the `drive` +//! dev-dependency, *not* this crate's public [`FromProof`] entry point -- +//! comparing the root hash and the recovered serialized payloads +//! byte-for-byte. A DAPI query-shape drift (wrong field name, missing +//! clause, wrong ordering) changes the query's grovedb path and fails +//! this root-hash pin. +//! 2. that this crate's real public entry point, +//! `>::maybe_from_proof_with_metadata`, +//! itself replays the same grovedb proof internally (via +//! `DriveDocumentQuery::verify_proof`) and then fails cleanly (an `Err`, +//! never a panic) once it tries to decode the placeholder payload as a +//! DPP document -- before the quorum signature is ever consulted. +//! +//! Neither half reaches the tenderdash BLS check: the placeholder payloads +//! make that unreachable here. Coverage of the signature layer itself lives +//! in `vectors_quorum_sig.rs`. + +#![cfg(feature = "mocks")] + +mod common; + +use std::collections::BTreeMap; + +use common::{hex_vec, identifier, load_case, Case, Expected, RequestSpec, NETWORK}; +use dapi_grpc::platform::v0::{self as platform, get_documents_response}; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::platform_value::Value; +use dpp::prelude::DataContract; +use dpp::system_data_contracts::{load_system_data_contract, SystemDataContract}; +use drive::query::{DriveDocumentQuery, InternalClauses, OrderClause, WhereClause, WhereOperator}; +use drive_proof_verifier::types::Documents; +use drive_proof_verifier::{Error, FromProof}; +use indexmap::IndexMap; + +fn equal_clause(field: &str, value: Value) -> (String, WhereClause) { + ( + field.to_string(), + WhereClause { + field: field.to_string(), + operator: WhereOperator::Equal, + value, + }, + ) +} + +fn asc_order(field: &str) -> IndexMap { + let mut order_by = IndexMap::new(); + order_by.insert( + field.to_string(), + OrderClause { + field: field.to_string(), + ascending: true, + }, + ); + order_by +} + +/// Builds the [`DriveDocumentQuery`] for the case, mirroring the query +/// shapes the Dash Core Platform GUI transport sends (pinned by the fixture +/// vectors). +fn document_query<'a>(case: &Case, contract: &'a DataContract) -> DriveDocumentQuery<'a> { + let (document_type_name, internal_clauses, order_by, limit) = match &case.manifest.request { + RequestSpec::DocumentsDpnsExact { + normalized_label, + limit, + } => ( + "domain", + InternalClauses { + equal_clauses: BTreeMap::from([ + equal_clause( + "normalizedParentDomainName", + Value::Text("dash".to_string()), + ), + equal_clause("normalizedLabel", Value::Text(normalized_label.to_string())), + ]), + ..Default::default() + }, + IndexMap::new(), + *limit, + ), + RequestSpec::DocumentsDpnsPrefix { + normalized_prefix, + limit, + } => ( + "domain", + InternalClauses { + equal_clauses: BTreeMap::from([equal_clause( + "normalizedParentDomainName", + Value::Text("dash".to_string()), + )]), + range_clause: Some(WhereClause { + field: "normalizedLabel".to_string(), + operator: WhereOperator::StartsWith, + value: Value::Text(normalized_prefix.to_string()), + }), + ..Default::default() + }, + asc_order("normalizedLabel"), + *limit, + ), + RequestSpec::DocumentsDashpayProfile { owner_id } => ( + "profile", + InternalClauses { + equal_clauses: BTreeMap::from([equal_clause( + "$ownerId", + Value::Identifier(identifier(owner_id).into_buffer()), + )]), + ..Default::default() + }, + IndexMap::new(), + 1, + ), + RequestSpec::DocumentsDashpayContacts { + identity_id, + to_identity, + limit, + } => ( + "contactRequest", + InternalClauses { + equal_clauses: BTreeMap::from([equal_clause( + if *to_identity { "toUserId" } else { "$ownerId" }, + Value::Identifier(identifier(identity_id).into_buffer()), + )]), + ..Default::default() + }, + asc_order("$createdAt"), + *limit, + ), + _ => panic!("{}: not a documents request", case.name), + }; + DriveDocumentQuery { + contract, + document_type: contract + .document_type_for_name(document_type_name) + .expect("system contract document type"), + internal_clauses, + offset: None, + limit: Some(limit), + order_by, + start_at: None, + start_at_included: false, + block_time_ms: None, + } +} + +fn contract_for(case: &Case) -> SystemDataContract { + match &case.manifest.request { + RequestSpec::DocumentsDpnsExact { .. } | RequestSpec::DocumentsDpnsPrefix { .. } => { + SystemDataContract::DPNS + } + RequestSpec::DocumentsDashpayProfile { .. } + | RequestSpec::DocumentsDashpayContacts { .. } => SystemDataContract::Dashpay, + _ => panic!("{}: not a documents request", case.name), + } +} + +fn run_documents_case(name: &str) { + let case = load_case(name); + let Expected::DocumentsPlaceholder { + serialized_documents, + } = &case.manifest.expected + else { + panic!("{name}: manifest expectation mismatch"); + }; + let platform_version = case.platform_version(); + let contract = load_system_data_contract(contract_for(&case), platform_version) + .expect("load system data contract"); + let query = document_query(&case, &contract); + + // The grovedb layer must verify: same root hash the fixture quorum + // signed, and exactly the recorded placeholder payloads. + let (root_hash, serialized) = query + .verify_proof_keep_serialized(&case.grovedb_proof, platform_version) + .unwrap_or_else(|e| panic!("{name}: grovedb layer must verify: {e}")); + assert_eq!( + hex::encode(root_hash), + case.manifest + .expected_root_hash_hex + .as_deref() + .expect("documents cases pin a root hash"), + "{name}: root hash" + ); + assert_eq!( + serialized, + serialized_documents + .iter() + .map(|hex| hex_vec(hex)) + .collect::>(), + "{name}: proven serialized payloads" + ); + + // Through the crate's public API the placeholder payload cannot decode + // into a DPP document: FromProof must surface a clean error. + let response = platform::GetDocumentsResponse { + version: Some(get_documents_response::Version::V0( + get_documents_response::GetDocumentsResponseV0 { + metadata: Some(case.metadata()), + result: Some( + get_documents_response::get_documents_response_v0::Result::Proof( + case.grpc_proof(), + ), + ), + }, + )), + }; + let error = >::maybe_from_proof_with_metadata( + query, + response, + NETWORK, + platform_version, + &case.provider(), + ) + .expect_err("placeholder document payload must fail to decode"); + assert!( + matches!( + error, + Error::DriveError { .. } | Error::ProtocolError { .. } + ), + "{name}: expected a document decode error, got: {error:?}" + ); +} + +#[test] +fn dpns_domain_exact() { + run_documents_case("dpns-domain-exact"); +} + +#[test] +fn dpns_domain_prefix() { + run_documents_case("dpns-domain-prefix"); +} + +#[test] +fn dashpay_profile() { + run_documents_case("dashpay-profile"); +} + +#[test] +fn dashpay_contacts_incoming() { + run_documents_case("dashpay-contacts-incoming"); +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors_identity.rs b/packages/rs-drive-proof-verifier/tests/vectors_identity.rs new file mode 100644 index 00000000000..7201bf48b26 --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors_identity.rs @@ -0,0 +1,259 @@ +//! Identity-family proof-vector regression tests: balance, nonce, contract +//! nonce, and public keys, plus the corrupted-proof negative case. Every +//! case replays a recorded Dash Core fixture proof through +//! [`FromProof::maybe_from_proof_with_metadata`] with the real fixture +//! quorum signature, so both the grovedb and tenderdash layers are checked. + +#![cfg(feature = "mocks")] + +mod common; + +use common::{identifier, load_case, Case, Expected, RequestSpec, NETWORK}; +use dapi_grpc::platform::v0::get_identity_contract_nonce_response::get_identity_contract_nonce_response_v0::Result as ContractNonceResult; +use dapi_grpc::platform::v0::{ + self as platform, get_identity_balance_request, get_identity_balance_response, + get_identity_contract_nonce_request, get_identity_contract_nonce_response, + get_identity_keys_request, get_identity_keys_response, get_identity_nonce_request, + get_identity_nonce_response, key_request_type, AllKeys, KeyRequestType, +}; +use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dpp::identity::{IdentityPublicKey, KeyID}; +use dpp::serialization::PlatformDeserializable; +use drive_proof_verifier::types::{ + IdentityBalance, IdentityContractNonceFetcher, IdentityNonceFetcher, IdentityPublicKeys, +}; +use drive_proof_verifier::{Error, FromProof}; + +fn balance_pair( + case: &Case, +) -> ( + platform::GetIdentityBalanceRequest, + platform::GetIdentityBalanceResponse, +) { + let RequestSpec::IdentityBalance { identity_id } = &case.manifest.request else { + panic!("{}: expected an identity_balance request", case.name); + }; + let request = platform::GetIdentityBalanceRequest { + version: Some(get_identity_balance_request::Version::V0( + get_identity_balance_request::GetIdentityBalanceRequestV0 { + id: identifier(identity_id).to_vec(), + prove: true, + }, + )), + }; + let response = platform::GetIdentityBalanceResponse { + version: Some(get_identity_balance_response::Version::V0( + get_identity_balance_response::GetIdentityBalanceResponseV0 { + metadata: Some(case.metadata()), + result: Some( + get_identity_balance_response::get_identity_balance_response_v0::Result::Proof( + case.grpc_proof(), + ), + ), + }, + )), + }; + (request, response) +} + +// The crate's own Error type is what the public API returns; its size is +// not this test's concern. +#[allow(clippy::result_large_err)] +fn verify_balance_case(case: &Case) -> Result, Error> { + let (request, response) = balance_pair(case); + IdentityBalance::maybe_from_proof( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) +} + +#[test] +fn identity_balance() { + let case = load_case("identity-balance"); + let Expected::IdentityBalance { balance: expected } = case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + let (request, response) = balance_pair(&case); + let (balance, metadata, proof) = IdentityBalance::maybe_from_proof_with_metadata( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) + .expect("identity balance proof must verify"); + assert_eq!(balance, Some(expected)); + assert_eq!(metadata.height, case.manifest.block.height); + assert_eq!(metadata.chain_id, case.manifest.block.chain_id); + assert_eq!(proof.grovedb_proof, case.grovedb_proof); +} + +#[test] +fn identity_nonce() { + let case = load_case("identity-nonce"); + let RequestSpec::IdentityNonce { identity_id } = &case.manifest.request else { + panic!("manifest request mismatch"); + }; + let Expected::IdentityNonce { nonce: expected } = case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + let request = platform::GetIdentityNonceRequest { + version: Some(get_identity_nonce_request::Version::V0( + get_identity_nonce_request::GetIdentityNonceRequestV0 { + identity_id: identifier(identity_id).to_vec(), + prove: true, + }, + )), + }; + let response = platform::GetIdentityNonceResponse { + version: Some(get_identity_nonce_response::Version::V0( + get_identity_nonce_response::GetIdentityNonceResponseV0 { + metadata: Some(case.metadata()), + result: Some( + get_identity_nonce_response::get_identity_nonce_response_v0::Result::Proof( + case.grpc_proof(), + ), + ), + }, + )), + }; + let fetcher = IdentityNonceFetcher::maybe_from_proof( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) + .expect("identity nonce proof must verify") + .expect("nonce must be proven present"); + assert_eq!(fetcher.0, expected); +} + +#[test] +fn identity_contract_nonce() { + let case = load_case("identity-contract-nonce"); + let RequestSpec::IdentityContractNonce { + identity_id, + contract_id, + } = &case.manifest.request + else { + panic!("manifest request mismatch"); + }; + let Expected::IdentityContractNonce { nonce: expected } = case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + let request = platform::GetIdentityContractNonceRequest { + version: Some(get_identity_contract_nonce_request::Version::V0( + get_identity_contract_nonce_request::GetIdentityContractNonceRequestV0 { + identity_id: identifier(identity_id).to_vec(), + contract_id: identifier(contract_id).to_vec(), + prove: true, + }, + )), + }; + let response = platform::GetIdentityContractNonceResponse { + version: Some(get_identity_contract_nonce_response::Version::V0( + get_identity_contract_nonce_response::GetIdentityContractNonceResponseV0 { + metadata: Some(case.metadata()), + result: Some(ContractNonceResult::Proof(case.grpc_proof())), + }, + )), + }; + let fetcher = IdentityContractNonceFetcher::maybe_from_proof( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) + .expect("identity contract nonce proof must verify") + .expect("contract nonce must be proven present"); + assert_eq!(fetcher.0, expected); +} + +#[test] +fn identity_keys() { + let case = load_case("identity-keys"); + let RequestSpec::IdentityKeys { identity_id } = &case.manifest.request else { + panic!("manifest request mismatch"); + }; + let Expected::IdentityKeys { serialized_keys } = &case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + let request = platform::GetIdentityKeysRequest { + version: Some(get_identity_keys_request::Version::V0( + get_identity_keys_request::GetIdentityKeysRequestV0 { + identity_id: identifier(identity_id).to_vec(), + request_type: Some(KeyRequestType { + request: Some(key_request_type::Request::AllKeys(AllKeys {})), + }), + limit: None, + offset: None, + prove: true, + }, + )), + }; + let response = platform::GetIdentityKeysResponse { + version: Some(get_identity_keys_response::Version::V0( + get_identity_keys_response::GetIdentityKeysResponseV0 { + metadata: Some(case.metadata()), + result: Some( + get_identity_keys_response::get_identity_keys_response_v0::Result::Proof( + case.grpc_proof(), + ), + ), + }, + )), + }; + let keys = IdentityPublicKeys::maybe_from_proof( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) + .expect("identity keys proof must verify") + .expect("keys must be proven present"); + + let expected: Vec = serialized_keys + .iter() + .map(|serialized| { + IdentityPublicKey::deserialize_from_bytes(&common::hex_vec(serialized)) + .expect("expected fixture key must deserialize") + }) + .collect(); + assert_eq!(keys.len(), expected.len()); + for expected_key in &expected { + let key = keys + .get(&expected_key.id()) + .unwrap_or_else(|| panic!("key id {} missing from proof", expected_key.id())) + .as_ref() + .unwrap_or_else(|| panic!("key id {} proven absent", expected_key.id())); + assert_eq!(key, expected_key); + } + assert_eq!( + keys.keys().copied().collect::>(), + vec![0, 1, 2], + "keys must arrive ordered by id" + ); +} + +// A flipped bit inside the grovedb proof must surface as a clean proof +// error before the quorum signature is ever consulted -- never a panic and +// never an Ok result. +#[test] +fn corrupted_grovedb_proof_is_rejected() { + let case = load_case("identity-balance-corrupted-proof"); + let Expected::Error { class } = case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + assert_eq!(class, common::ErrorClass::ProofInvalid); + let error = verify_balance_case(&case).expect_err("corrupted proof must not verify"); + assert!( + matches!(error, Error::GroveDBError { .. } | Error::DriveError { .. }), + "expected a grovedb/drive proof error, got: {error:?}" + ); +} diff --git a/packages/rs-drive-proof-verifier/tests/vectors_quorum_sig.rs b/packages/rs-drive-proof-verifier/tests/vectors_quorum_sig.rs new file mode 100644 index 00000000000..8b099eb98ad --- /dev/null +++ b/packages/rs-drive-proof-verifier/tests/vectors_quorum_sig.rs @@ -0,0 +1,148 @@ +//! Tenderdash quorum-signature regression tests, seeded from the Dash Core +//! `quorum_sig_vectors.json` fixture (tenderdash v1.5.1, basic BLS scheme). +//! The positive case runs a full identity-balance verification whose +//! envelope carries a real quorum signature over the state; the negatives +//! flip exactly one ingredient each (signature bit, quorum key, block id +//! hash, or a `ResponseMetadata` field folded into the signed `StateId`) +//! and must fail with an invalid-signature error -- proving the signature +//! actually binds those inputs. + +#![cfg(feature = "mocks")] + +mod common; + +use common::{identifier, load_case, Case, ErrorClass, Expected, RequestSpec, NETWORK}; +use dapi_grpc::platform::v0::{ + self as platform, get_identity_balance_request, get_identity_balance_response, +}; +use drive_proof_verifier::types::IdentityBalance; +use drive_proof_verifier::{Error, FromProof}; + +// The crate's own Error type is what the public API returns; its size is +// not this test's concern. +#[allow(clippy::result_large_err)] +fn verify_balance(case: &Case) -> Result, Error> { + verify_balance_with_metadata(case, case.metadata()) +} + +/// Like [`verify_balance`], but lets the caller substitute the response +/// metadata that gets carried alongside the case's untouched proof envelope +/// -- used to prove the metadata itself is inside the signed `StateId`. +#[allow(clippy::result_large_err)] +fn verify_balance_with_metadata( + case: &Case, + metadata: platform::ResponseMetadata, +) -> Result, Error> { + let RequestSpec::IdentityBalance { identity_id } = &case.manifest.request else { + panic!("{}: expected an identity_balance request", case.name); + }; + let request = platform::GetIdentityBalanceRequest { + version: Some(get_identity_balance_request::Version::V0( + get_identity_balance_request::GetIdentityBalanceRequestV0 { + id: identifier(identity_id).to_vec(), + prove: true, + }, + )), + }; + let response = platform::GetIdentityBalanceResponse { + version: Some(get_identity_balance_response::Version::V0( + get_identity_balance_response::GetIdentityBalanceResponseV0 { + metadata: Some(metadata), + result: Some( + get_identity_balance_response::get_identity_balance_response_v0::Result::Proof( + case.grpc_proof(), + ), + ), + }, + )), + }; + IdentityBalance::maybe_from_proof( + request, + response, + NETWORK, + case.platform_version(), + &case.provider(), + ) +} + +fn expect_invalid_signature(name: &str) { + let case = load_case(name); + let Expected::Error { class } = case.manifest.expected else { + panic!("{name}: manifest expectation mismatch"); + }; + assert_eq!(class, ErrorClass::InvalidSignature); + let error = verify_balance(&case).expect_err("quorum signature check must fail"); + // A wrong key or wrong signed payload fails the pairing check + // (InvalidSignature); a bit-flipped signature may already fail G2 point + // decompression (SignatureVerificationError). Both are proper + // rejections of a bad quorum signature. + assert!( + matches!( + error, + Error::InvalidSignature { .. } | Error::SignatureVerificationError { .. } + ), + "{name}: expected a signature rejection, got: {error:?}" + ); +} + +#[test] +fn valid_quorum_signature_verifies() { + let case = load_case("quorum-sig-valid"); + let Expected::IdentityBalance { balance: expected } = case.manifest.expected else { + panic!("manifest expectation mismatch"); + }; + let balance = verify_balance(&case) + .expect("envelope with a valid quorum signature must verify end to end"); + assert_eq!(balance, Some(expected)); +} + +#[test] +fn tampered_signature_is_rejected() { + expect_invalid_signature("quorum-sig-tampered-signature"); +} + +#[test] +fn wrong_quorum_key_is_rejected() { + expect_invalid_signature("quorum-sig-wrong-quorum-key"); +} + +#[test] +fn wrong_block_id_hash_is_rejected() { + expect_invalid_signature("quorum-sig-wrong-block-id-hash"); +} + +// The quorum signs a `StateId` built from `height`, `core_chain_locked_height`, +// `time_ms`, and the app hash (see `verify_tenderdash_proof` in `src/verify.rs`). +// The proof envelope and signature bytes are untouched here -- only the +// out-of-band `ResponseMetadata` a server could independently lie about is +// mutated -- so a pass here would mean the metadata is never actually bound +// by the signature. +fn expect_metadata_tamper_breaks_signature(mutate: impl Fn(&mut platform::ResponseMetadata)) { + let case = load_case("quorum-sig-valid"); + let mut metadata = case.metadata(); + mutate(&mut metadata); + let error = verify_balance_with_metadata(&case, metadata) + .expect_err("tampered metadata must break the quorum signature check"); + assert!( + matches!( + error, + Error::InvalidSignature { .. } | Error::SignatureVerificationError { .. } + ), + "expected a signature rejection, got: {error:?}" + ); +} + +#[test] +fn tampered_height_breaks_signature() { + expect_metadata_tamper_breaks_signature(|metadata| metadata.height += 1); +} + +#[test] +fn tampered_time_ms_breaks_signature() { + expect_metadata_tamper_breaks_signature(|metadata| metadata.time_ms += 1); +} + +#[test] +fn tampered_core_chain_locked_height_breaks_signature() { + expect_metadata_tamper_breaks_signature(|metadata| metadata.core_chain_locked_height += 1); +}