{"record":{"id":"2997dbc8ad2d384a","repo":"nautechsystems/nautilus_trader","slug":"verified-nonce-assignment-requires-exactly-three-p","errorCode":null,"errorMessage":"Verified nonce assignment requires exactly three provider and operator IDs","messagePattern":"Verified nonce assignment requires exactly three provider and operator IDs","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5896,"sourceCode":"        );\n        Ok(())\n    }\n\n    /// Assigns the canonical nonce and its authorizing verification evidence atomically.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the durable nonce ledger, intent ownership, manifest identity, or\n    /// evidence is inconsistent, or if persistence fails.\n    pub(crate) async fn assign_execution_intent_nonce_verified(\n        &self,\n        assignment: &ExecutionNonceAssignment<'_>,\n    ) -> anyhow::Result<()> {\n        anyhow::ensure!(\n            !assignment.decisions.is_empty(),\n            \"Verified nonce assignment requires decision evidence\"\n        );\n        anyhow::ensure!(\n            assignment.provider_ids.len() == 3 && assignment.operator_ids.len() == 3,\n            \"Verified nonce assignment requires exactly three provider and operator IDs\"\n        );\n        anyhow::ensure!(\n            assignment.failure_domain_ids.len() >= 3,\n            \"Verified nonce assignment requires the configured failure domains\"\n        );\n        let chain_id = i32::try_from(assignment.chain_id)\n            .context(\"Verification chain ID exceeds PostgreSQL INTEGER\")?;\n        let nonce =\n            i64::try_from(assignment.nonce).context(\"Execution nonce exceeds PostgreSQL BIGINT\")?;\n        let mut transaction = self\n            .pool\n            .begin()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to start verified nonce assignment: {e}\"))?;\n        let (manifest_version, manifest_digest, next_nonce, revision) =\n            sqlx::query_as::<_, (String, String, i64, i64)>(","sourceCodeStart":5878,"sourceCodeEnd":5914,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5878-L5914","documentation":"The verified nonce-assignment path enforces a BFT-style quorum: it requires exactly three provider IDs and exactly three operator IDs in the ExecutionNonceAssignment. The ensure! at database.rs:5896 rejects any other cardinality, because the verification logic is designed for a 3-of-N provider/operator configuration.","triggerScenarios":"Passing an ExecutionNonceAssignment where provider_ids.len() != 3 or operator_ids.len() != 3 — e.g. built from a config with fewer/more providers, partially collected evidence (2 providers responded), or duplicated/missing IDs when aggregating decisions.","commonSituations":"Misconfigured cluster running with only 2 operators available; evidence aggregation dropping duplicates (leaving <3) or including extras; changing cluster size without updating the verified-assignment quorum expectation; environment mismatch between staging (smaller cluster) and production (3 operators).","solutions":["Ensure exactly three provider IDs and three operator IDs are included in the assignment before calling the verified path.","Fix the cluster/provider configuration so the deployment matches the required 3-provider/3-operator topology.","If aggregation deduplicates or truncates, correct the aggregation logic to preserve exactly three distinct IDs.","If a different quorum size is genuinely required, that is a code change to the invariant, not a caller workaround — coordinate with maintainers."],"exampleFix":"// before: passing whatever providers responded\nlet assignment = ExecutionNonceAssignment { provider_ids: responded_providers, operator_ids: responded_operators, .. };\n// after\nanyhow::ensure!(responded_providers.len() == 3 && responded_operators.len() == 3,\n    \"need 3 providers and 3 operators, got {}/{}\", responded_providers.len(), responded_operators.len());\nlet assignment = ExecutionNonceAssignment { provider_ids: responded_providers, operator_ids: responded_operators, .. };\ndatabase.assign_execution_intent_nonce_verified(&assignment).await?;","handlingStrategy":"validation","validationCode":"// validate quorum cardinality before the verified call\nif assignment.provider_ids.len() != 3 || assignment.operator_ids.len() != 3 {\n    return Err(anyhow::anyhow!(\n        \"verified assignment needs exactly 3 providers/3 operators, got {}/{}\",\n        assignment.provider_ids.len(), assignment.operator_ids.len()));\n}","typeGuard":"fn has_full_quorum(a: &ExecutionNonceAssignment<'_>) -> bool {\n    a.provider_ids.len() == 3 && a.operator_ids.len() == 3\n}","tryCatchPattern":"match database.assign_execution_intent_nonce_verified(&assignment).await {\n    Err(e) if e.to_string().contains(\"exactly three provider and operator IDs\") => {\n        tracing::error!(providers = assignment.provider_ids.len(), operators = assignment.operator_ids.len(), \"quorum cardinality mismatch\");\n        return Err(e);\n    }\n    Ok(()) => {},\n    Err(e) => return Err(e),\n}","preventionTips":["Keep deployment topology at exactly 3 providers and 3 operators for the verified path.","Deduplicate IDs during aggregation and assert the count equals 3.","Fail fast when fewer than 3 providers respond instead of degrading the assignment.","Sync cluster-size config changes with the quorum expectation in code."],"tags":["validation","quorum","byzantine-fault-tolerance","configuration"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}