commit 6246a9ad2e8b5c62195a288eeeb89a6a91a0ea96
parent 705eecaabaa0a83e93de9569e3e1ba789792f424
Author: triesap <tyson@radroots.org>
Date: Sat, 4 Jul 2026 01:48:15 +0000
trade: surface validation trust decisions
- pass configured trusted RHI keys into SDK status
- expose trust decisions in status views and errors
- render terminal validation trust confidence
- align source guards with SDK trust policy use
Diffstat:
7 files changed, 114 insertions(+), 15 deletions(-)
diff --git a/src/ops/exec/trade.rs b/src/ops/exec/trade.rs
@@ -908,6 +908,7 @@ fn order_status_error_detail(view: &OrderStatusView) -> Value {
"revision": &view.revision,
"inventory": &view.inventory,
"lifecycle": &view.lifecycle,
+ "sdk_receipt": &view.sdk_receipt,
"reducer_issues": &view.reducer_issues,
"target_relays": &view.target_relays,
"connected_relays": &view.connected_relays,
diff --git a/src/out/terminal/renderers/trade.rs b/src/out/terminal/renderers/trade.rs
@@ -320,6 +320,24 @@ fn trade_status_document(envelope: &OutputEnvelope, result: &Value) -> TerminalD
result,
&["lifecycle", "terminal"],
);
+ common::push_path_field(
+ &mut document,
+ "Validation trust",
+ result,
+ &["sdk_receipt", "validation_trust", "state"],
+ );
+ common::push_bool_field(
+ &mut document,
+ "Production committed",
+ result,
+ &["sdk_receipt", "validation_trust", "production_committed"],
+ );
+ common::push_path_field(
+ &mut document,
+ "Trust reason",
+ result,
+ &["sdk_receipt", "validation_trust", "reason_code"],
+ );
common::push_path_field(&mut document, "Revision", result, &["revision", "state"]);
push_relay_field(&mut document, result);
push_inventory_section(&mut document, result);
diff --git a/src/runtime/order.rs b/src/runtime/order.rs
@@ -1295,7 +1295,9 @@ pub fn status(
config: &RuntimeConfig,
args: &TradeStatusArgs,
) -> Result<OrderStatusView, CliSdkAdapterError> {
- let request = TradeStatusRequest::parse(args.key.as_str())?;
+ let request = TradeStatusRequest::parse(args.key.as_str())?.try_with_trusted_rhi_pubkeys(
+ config.rhi.trusted_worker_pubkeys.iter().map(String::as_str),
+ )?;
let session = CliSdkSession::connect(config)?;
let receipt = session.block_on(session.sdk().trades().status(request))?;
Ok(sdk_order_status_view(receipt))
@@ -1309,7 +1311,7 @@ fn decide_trade_via_sdk(
let actor = sdk_trade_actor(seller, RadrootsActorRole::Seller, args.decision.command())?;
let session = connect_sdk_for_trade_actor(config, seller, "trade decision")?;
let locator = trade_locator_from_key(args.key.as_str())?;
- let status = trade_status_for_locator(&session, locator.clone())?;
+ let status = trade_status_for_locator(config, &session, locator.clone())?;
let status_view = sdk_order_status_view(status.clone());
let publish_mode = trade_publish_mode(config);
let ack_policy = trade_ack_policy(publish_mode)?;
@@ -1372,7 +1374,7 @@ fn propose_revision_via_sdk(
let actor = sdk_trade_actor(seller, RadrootsActorRole::Seller, "revision propose")?;
let session = connect_sdk_for_trade_actor(config, seller, "trade revision propose")?;
let locator = trade_locator_from_key(args.key.as_str())?;
- let status = trade_status_for_locator(&session, locator.clone())?;
+ let status = trade_status_for_locator(config, &session, locator.clone())?;
let status_view = sdk_order_status_view(status);
let revision = revision_request_parts_from_status(args, &status_view)?;
let publish_mode = trade_publish_mode(config);
@@ -1419,7 +1421,7 @@ fn decide_revision_via_sdk(
let actor = sdk_trade_actor(&account, RadrootsActorRole::Buyer, "revision decision")?;
let session = connect_sdk_for_trade_actor(config, &account, "trade revision decision")?;
let locator = trade_locator_from_key(args.key.as_str())?;
- let status = trade_status_for_locator(&session, locator.clone())?;
+ let status = trade_status_for_locator(config, &session, locator.clone())?;
let status_view = sdk_order_status_view(status);
let revision_id = protocol_revision_id(args.revision_id.trim(), "revision_id")?;
let decision = match args.decision {
@@ -1484,7 +1486,7 @@ fn cancel_trade_via_sdk(
let actor = sdk_trade_actor(&account, RadrootsActorRole::Buyer, "cancel")?;
let session = connect_sdk_for_trade_actor(config, &account, "trade cancel")?;
let locator = trade_locator_from_key(args.key.as_str())?;
- let status = trade_status_for_locator(&session, locator.clone())?;
+ let status = trade_status_for_locator(config, &session, locator.clone())?;
let status_view = sdk_order_status_view(status);
let publish_mode = trade_publish_mode(config);
let ack_policy = trade_ack_policy(publish_mode)?;
@@ -1511,12 +1513,16 @@ fn cancel_trade_via_sdk(
}
fn trade_status_for_locator(
+ config: &RuntimeConfig,
session: &CliSdkSession,
locator: RadrootsTradeLocator,
) -> Result<TradeStatusReceipt, CliSdkAdapterError> {
- Ok(session.block_on(session.sdk().trades().status(
- TradeStatusRequest::new(locator).with_source(SdkTradeStatusSource::ResyncThenLocal),
- ))?)
+ let request = TradeStatusRequest::new(locator)
+ .with_source(SdkTradeStatusSource::ResyncThenLocal)
+ .try_with_trusted_rhi_pubkeys(
+ config.rhi.trusted_worker_pubkeys.iter().map(String::as_str),
+ )?;
+ Ok(session.block_on(session.sdk().trades().status(request))?)
}
fn inventory_commitments_from_status(
diff --git a/src/runtime/order/sdk_status.rs b/src/runtime/order/sdk_status.rs
@@ -1,13 +1,13 @@
use radroots_events::ids::RadrootsEventId;
use radroots_sdk::{
SdkTradeStatusIssue, TradeStatusEligibility, TradeStatusEvidenceSummary, TradeStatusKind,
- TradeStatusNextActionKind, TradeStatusReceipt,
+ TradeStatusNextActionKind, TradeStatusReceipt, TradeValidationTrustDecision,
};
use crate::view::runtime::{
OrderIssueView, OrderStatusEligibilityView, OrderStatusEvidenceSummaryView,
OrderStatusLifecycleCancellationView, OrderStatusLifecycleView, OrderStatusSdkReceiptView,
- OrderStatusView, OrderTradeLocatorView,
+ OrderStatusValidationTrustView, OrderStatusView, OrderTradeLocatorView,
};
use super::{ORDER_ACTOR_CONTEXT_SDK_LOCAL, ORDER_STATUS_SDK_SOURCE};
@@ -85,6 +85,10 @@ fn sdk_order_status_receipt_view(receipt: &TradeStatusReceipt) -> OrderStatusSdk
next_action: sdk_status_next_action(receipt.next_action).to_owned(),
evidence: sdk_status_evidence_view(&receipt.evidence),
eligibility: sdk_status_eligibility_view(&receipt.eligibility),
+ validation_trust: receipt
+ .validation_trust
+ .as_ref()
+ .map(sdk_status_validation_trust_view),
}
}
@@ -97,12 +101,40 @@ fn sdk_status_evidence_view(
has_request: evidence.has_request,
has_decision: evidence.has_decision,
has_agreement: evidence.has_agreement,
+ has_validation_receipt: evidence.has_validation_receipt,
has_pending_revision: evidence.has_pending_revision,
has_cancellation: evidence.has_cancellation,
has_issues: evidence.has_issues,
}
}
+fn sdk_status_validation_trust_view(
+ decision: &TradeValidationTrustDecision,
+) -> OrderStatusValidationTrustView {
+ OrderStatusValidationTrustView {
+ state: decision.state.as_str().to_owned(),
+ trusted_rhi_pubkey_count: decision.trusted_rhi_pubkey_count,
+ allow_deterministic_none: decision.allow_deterministic_none,
+ require_cryptographic_proof: decision.require_cryptographic_proof,
+ receipt_event_id: decision.receipt_event_id.as_ref().map(ToString::to_string),
+ receipt_author: decision.receipt_author.as_ref().map(ToString::to_string),
+ result_event_id: decision.result_event_id.as_ref().map(ToString::to_string),
+ result_author: decision.result_author.as_ref().map(ToString::to_string),
+ proof_system: decision.proof_system.clone(),
+ validation_authority: decision
+ .validation_authority
+ .map(|authority| authority.as_str().to_owned()),
+ commitment_confidence: decision
+ .commitment_confidence
+ .map(|confidence| confidence.as_str().to_owned()),
+ cryptographic_proof_required: decision.cryptographic_proof_required,
+ cryptographic_proof_verified: decision.cryptographic_proof_verified,
+ production_committed: decision.production_committed,
+ reason_code: decision.reason_code.clone(),
+ reason: decision.reason.clone(),
+ }
+}
+
fn sdk_status_eligibility_view(eligibility: &TradeStatusEligibility) -> OrderStatusEligibilityView {
OrderStatusEligibilityView {
can_decide: eligibility.can_decide,
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -931,7 +931,12 @@ mod tests {
path: "src/runtime/order.rs",
start: "pub fn status(\n config: &RuntimeConfig",
end: "fn decide_trade_via_sdk(",
- required_tokens: &["TradeStatusRequest::parse", "session.sdk().trades().status"],
+ required_tokens: &[
+ "TradeStatusRequest::parse",
+ ".try_with_trusted_rhi_pubkeys(",
+ "config.rhi.trusted_worker_pubkeys",
+ "session.sdk().trades().status",
+ ],
},
MigratedCliPathGuard {
label: "order locator status helper",
@@ -945,6 +950,8 @@ mod tests {
".sdk()",
".trades()",
".with_source(SdkTradeStatusSource::ResyncThenLocal)",
+ ".try_with_trusted_rhi_pubkeys(",
+ "config.rhi.trusted_worker_pubkeys",
],
},
MigratedCliPathGuard {
@@ -957,6 +964,9 @@ mod tests {
"OrderStatusView",
"OrderStatusLifecycleView",
"OrderStatusSdkReceiptView",
+ "TradeValidationTrustDecision",
+ "validation_trust",
+ "has_validation_receipt",
],
},
MigratedCliPathGuard {
diff --git a/src/runtime/validation_receipt.rs b/src/runtime/validation_receipt.rs
@@ -854,7 +854,7 @@ fn none_proof_trust_summary(
trusted_worker_summary("pending_rhi_validation", authority, confidence, false)
}
("trusted_rhi_service_key", "committed_by_trusted_service") => {
- trusted_worker_summary("trusted_service_validated", authority, confidence, true)
+ trusted_worker_summary("trusted_service_validated", authority, confidence, false)
}
("cryptographic_proof_verified", "committed_by_cryptographic_proof") => {
trusted_worker_summary("cryptographic_proof_verified", authority, confidence, true)
@@ -1426,7 +1426,7 @@ mod tests {
}
#[test]
- fn none_receipts_surface_trusted_service_confidence_as_production_verification() {
+ fn none_receipts_surface_trusted_service_confidence_without_production_verification() {
let view = proof_verification_view_for_receipt(
&deterministic_receipt(),
ValidationReceiptWorkerEvidenceSelection {
@@ -1448,7 +1448,7 @@ mod tests {
view.commitment_confidence.as_deref(),
Some("committed_by_trusted_service")
);
- assert!(view.production_verification);
+ assert!(!view.production_verification);
assert!(proof_state_is_verification_success(view.state.as_str()));
}
@@ -1492,7 +1492,7 @@ mod tests {
}
#[test]
- fn production_verification_success_excludes_local_only_and_sp1_execute_checked() {
+ fn validation_success_labels_exclude_local_only_and_sp1_execute_checked() {
assert!(!proof_state_is_verification_success(
"local_only_deterministic_receipt"
));
diff --git a/src/view/runtime.rs b/src/view/runtime.rs
@@ -2189,6 +2189,37 @@ pub struct OrderStatusSdkReceiptView {
pub next_action: String,
pub evidence: OrderStatusEvidenceSummaryView,
pub eligibility: OrderStatusEligibilityView,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub validation_trust: Option<OrderStatusValidationTrustView>,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct OrderStatusValidationTrustView {
+ pub state: String,
+ pub trusted_rhi_pubkey_count: usize,
+ pub allow_deterministic_none: bool,
+ pub require_cryptographic_proof: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub receipt_event_id: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub receipt_author: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub result_event_id: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub result_author: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub proof_system: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub validation_authority: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub commitment_confidence: Option<String>,
+ pub cryptographic_proof_required: bool,
+ pub cryptographic_proof_verified: bool,
+ pub production_committed: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reason_code: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
@@ -2198,6 +2229,7 @@ pub struct OrderStatusEvidenceSummaryView {
pub has_request: bool,
pub has_decision: bool,
pub has_agreement: bool,
+ pub has_validation_receipt: bool,
pub has_pending_revision: bool,
pub has_cancellation: bool,
pub has_issues: bool,