commit c115c20527d94d86c529136516877574b08b815c
parent 5ee94e3024f54d684f34e826119b842feda2cde9
Author: triesap <tyson@radroots.org>
Date: Wed, 15 Jul 2026 17:34:58 +0000
trade: remove revision commands for validator receipts
Replace CLI RHI trust config with validator-set policy and bind status trust output to validator-set receipts.\n\nRemove active order revision command/runtime/renderer/view surfaces and rename validation receipt worker trust output to validator evidence.
Diffstat:
26 files changed, 557 insertions(+), 2123 deletions(-)
diff --git a/src/cli/global.rs b/src/cli/global.rs
@@ -290,53 +290,6 @@ pub struct TradeCancelArgs {
}
#[derive(Debug, Clone)]
-pub struct TradeRevisionProposeArgs {
- pub key: String,
- pub reason: String,
- pub confirm_public_note: bool,
- pub bin_id: Option<String>,
- pub bin_count: Option<u32>,
- pub adjustment_id: Option<String>,
- pub adjustment_effect: Option<String>,
- pub adjustment_amount: Option<String>,
- pub adjustment_currency: Option<String>,
- pub adjustment_reason: Option<String>,
- pub idempotency_key: Option<String>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum TradeRevisionDecisionArg {
- Accept,
- Decline,
-}
-
-impl TradeRevisionDecisionArg {
- pub fn as_str(self) -> &'static str {
- match self {
- Self::Accept => "accepted",
- Self::Decline => "declined",
- }
- }
-
- pub fn command(self) -> &'static str {
- match self {
- Self::Accept => "accept",
- Self::Decline => "decline",
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct TradeRevisionDecisionArgs {
- pub key: String,
- pub revision_id: String,
- pub decision: TradeRevisionDecisionArg,
- pub reason: Option<String>,
- pub idempotency_key: Option<String>,
- pub confirm_public_note: bool,
-}
-
-#[derive(Debug, Clone)]
pub struct TradeStatusArgs {
pub key: String,
}
diff --git a/src/cli/input.rs b/src/cli/input.rs
@@ -45,8 +45,8 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
FarmLocationCommand, FarmProfileCommand, ListingAppCommand, ListingCommand, MarketCommand,
MarketListingCommand, MarketProductCommand, MeshCommand, MeshScopeCommand,
StoreBackupCommand, StoreCommand, TradeAppCommand, TradeCommand, TradeEventCommand,
- TradeRevisionCommand, TradeStatusCommand, TransportCommand, TransportOutboxCommand,
- TransportProfileCommand, ValidationCommand, ValidationReceiptCommand,
+ TradeStatusCommand, TransportCommand, TransportOutboxCommand, TransportProfileCommand,
+ ValidationCommand, ValidationReceiptCommand,
};
let mut input = OperationData::new();
@@ -272,35 +272,6 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
insert_string(&mut input, "reason", &args.reason);
insert_bool(&mut input, "confirm_public_note", args.confirm_public_note);
}
- TradeCommand::Revision(revision) => match &revision.command {
- TradeRevisionCommand::Propose(args) => {
- insert_string(&mut input, "trade_id", &args.trade_id);
- insert_string(&mut input, "reason", &args.reason);
- insert_bool(&mut input, "confirm_public_note", args.confirm_public_note);
- insert_string(&mut input, "bin_id", &args.bin_id);
- if let Some(bin_count) = args.bin_count {
- input.insert(
- "bin_count".to_owned(),
- Value::Number(serde_json::Number::from(bin_count)),
- );
- }
- insert_string(&mut input, "adjustment_id", &args.adjustment_id);
- insert_string(&mut input, "adjustment_effect", &args.adjustment_effect);
- insert_string(&mut input, "adjustment_amount", &args.adjustment_amount);
- insert_string(&mut input, "adjustment_currency", &args.adjustment_currency);
- insert_string(&mut input, "adjustment_reason", &args.adjustment_reason);
- }
- TradeRevisionCommand::Accept(args) => {
- insert_string(&mut input, "trade_id", &args.trade_id);
- insert_string(&mut input, "revision_id", &args.revision_id);
- }
- TradeRevisionCommand::Decline(args) => {
- insert_string(&mut input, "trade_id", &args.trade_id);
- insert_string(&mut input, "revision_id", &args.revision_id);
- insert_string(&mut input, "reason", &args.reason);
- insert_bool(&mut input, "confirm_public_note", args.confirm_public_note);
- }
- },
TradeCommand::Status(status) => match &status.command {
TradeStatusCommand::Get(args) => {
insert_string(&mut input, "trade_id", &args.trade_id)
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
@@ -261,11 +261,6 @@ impl TargetCommand {
TradeCommand::Accept(_) => "trade.accept",
TradeCommand::Decline(_) => "trade.decline",
TradeCommand::Cancel(_) => "trade.cancel",
- TradeCommand::Revision(revision) => match &revision.command {
- TradeRevisionCommand::Propose(_) => "trade.revision.propose",
- TradeRevisionCommand::Accept(_) => "trade.revision.accept",
- TradeRevisionCommand::Decline(_) => "trade.revision.decline",
- },
TradeCommand::Status(status) => match &status.command {
TradeStatusCommand::Get(_) => "trade.status.get",
},
@@ -292,8 +287,7 @@ mod tests {
use super::{
AccountCommand, FarmCommand, FarmLocationCommand, ListingCommand, TargetCliArgs,
- TargetOutputFormat, TradeCommand, TradeRevisionCommand, ValidationCommand,
- ValidationReceiptCommand,
+ TargetOutputFormat, TradeCommand, ValidationCommand, ValidationReceiptCommand,
};
use crate::registry::OPERATION_REGISTRY;
@@ -658,109 +652,6 @@ mod tests {
}
#[test]
- fn target_parser_accepts_order_revision_propose_inputs() {
- let parsed = TargetCliArgs::try_parse_from([
- "radroots",
- "trade",
- "revision",
- "propose",
- "ord_test",
- "--reason",
- "update count",
- "--bin-id",
- "bin-1",
- "--bin-count",
- "3",
- "--adjustment-id",
- "adj_revision",
- "--adjustment-effect",
- "increase",
- "--adjustment-amount",
- "2",
- "--adjustment-currency",
- "USD",
- "--adjustment-reason",
- "packing change",
- "--confirm-public-note",
- ])
- .expect("target args parse");
-
- assert_eq!(parsed.command.operation_id(), "trade.revision.propose");
- let crate::cli::TargetCommand::Trade(trade) = parsed.command else {
- panic!("expected trade command")
- };
- let TradeCommand::Revision(revision) = trade.command else {
- panic!("expected trade revision command")
- };
- let TradeRevisionCommand::Propose(args) = revision.command else {
- panic!("expected trade revision propose command")
- };
- assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
- assert_eq!(args.reason.as_deref(), Some("update count"));
- assert_eq!(args.bin_id.as_deref(), Some("bin-1"));
- assert_eq!(args.bin_count, Some(3));
- assert_eq!(args.adjustment_id.as_deref(), Some("adj_revision"));
- assert_eq!(args.adjustment_effect.as_deref(), Some("increase"));
- assert!(args.confirm_public_note);
- }
-
- #[test]
- fn target_parser_accepts_order_revision_decision_inputs() {
- let accepted = TargetCliArgs::try_parse_from([
- "radroots",
- "trade",
- "revision",
- "accept",
- "ord_test",
- "--revision-id",
- "rev_test",
- ])
- .expect("target args parse");
-
- assert_eq!(accepted.command.operation_id(), "trade.revision.accept");
- let crate::cli::TargetCommand::Trade(trade) = accepted.command else {
- panic!("expected trade command")
- };
- let TradeCommand::Revision(revision) = trade.command else {
- panic!("expected trade revision command")
- };
- let TradeRevisionCommand::Accept(args) = revision.command else {
- panic!("expected trade revision accept command")
- };
- assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
- assert_eq!(args.revision_id.as_deref(), Some("rev_test"));
-
- let declined = TargetCliArgs::try_parse_from([
- "radroots",
- "trade",
- "revision",
- "decline",
- "ord_test",
- "--revision-id",
- "rev_test",
- "--reason",
- "keep original trade",
- "--confirm-public-note",
- ])
- .expect("target args parse");
-
- assert_eq!(declined.command.operation_id(), "trade.revision.decline");
- let crate::cli::TargetCommand::Trade(trade) = declined.command else {
- panic!("expected trade command")
- };
- let TradeCommand::Revision(revision) = trade.command else {
- panic!("expected trade revision command")
- };
- let TradeRevisionCommand::Decline(args) = revision.command else {
- panic!("expected trade revision decline command")
- };
- assert_eq!(args.trade_id.as_deref(), Some("ord_test"));
- assert_eq!(args.revision_id.as_deref(), Some("rev_test"));
- assert_eq!(args.reason.as_deref(), Some("keep original trade"));
- assert!(args.confirm_public_note);
- }
-
- #[test]
fn target_parser_accepts_validation_receipt_commands() {
let get = TargetCliArgs::try_parse_from([
"radroots",
diff --git a/src/cli/trade.rs b/src/cli/trade.rs
@@ -18,7 +18,6 @@ pub enum TradeCommand {
Accept(TradeKeyArgs),
Decline(TradeDeclineArgs),
Cancel(TradeCancelArgs),
- Revision(TradeRevisionArgs),
Status(TradeStatusArgs),
Event(TradeEventArgs),
}
@@ -79,60 +78,6 @@ pub struct TradeCancelArgs {
}
#[derive(Debug, Clone, Args)]
-pub struct TradeRevisionArgs {
- #[command(subcommand)]
- pub command: TradeRevisionCommand,
-}
-
-#[derive(Debug, Clone, Subcommand)]
-pub enum TradeRevisionCommand {
- Propose(TradeRevisionProposeArgs),
- Accept(TradeRevisionDecisionArgs),
- Decline(TradeRevisionDeclineArgs),
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct TradeRevisionProposeArgs {
- pub trade_id: Option<String>,
- #[arg(long)]
- pub reason: Option<String>,
- #[arg(long)]
- pub confirm_public_note: bool,
- #[arg(long)]
- pub bin_id: Option<String>,
- #[arg(long)]
- pub bin_count: Option<u32>,
- #[arg(long)]
- pub adjustment_id: Option<String>,
- #[arg(long)]
- pub adjustment_effect: Option<String>,
- #[arg(long)]
- pub adjustment_amount: Option<String>,
- #[arg(long)]
- pub adjustment_currency: Option<String>,
- #[arg(long)]
- pub adjustment_reason: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct TradeRevisionDecisionArgs {
- pub trade_id: Option<String>,
- #[arg(long)]
- pub revision_id: Option<String>,
-}
-
-#[derive(Debug, Clone, Args)]
-pub struct TradeRevisionDeclineArgs {
- pub trade_id: Option<String>,
- #[arg(long)]
- pub revision_id: Option<String>,
- #[arg(long)]
- pub reason: Option<String>,
- #[arg(long)]
- pub confirm_public_note: bool,
-}
-
-#[derive(Debug, Clone, Args)]
pub struct TradeStatusArgs {
#[command(subcommand)]
pub command: TradeStatusCommand,
diff --git a/src/main.rs b/src/main.rs
@@ -316,15 +316,6 @@ fn execute_request(
TargetOperationRequest::TradeCancel(request) => {
execute_with(TradeOperationService::new(config), request)
}
- TargetOperationRequest::TradeRevisionPropose(request) => {
- execute_with(TradeOperationService::new(config), request)
- }
- TargetOperationRequest::TradeRevisionAccept(request) => {
- execute_with(TradeOperationService::new(config), request)
- }
- TargetOperationRequest::TradeRevisionDecline(request) => {
- execute_with(TradeOperationService::new(config), request)
- }
TargetOperationRequest::TradeStatusGet(request) => {
execute_with(TradeOperationService::new(config), request)
}
diff --git a/src/ops/exec/basket.rs b/src/ops/exec/basket.rs
@@ -1929,7 +1929,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/exec/core.rs b/src/ops/exec/core.rs
@@ -1452,7 +1452,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/exec/farm.rs b/src/ops/exec/farm.rs
@@ -900,7 +900,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/exec/listing.rs b/src/ops/exec/listing.rs
@@ -625,7 +625,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/exec/market.rs b/src/ops/exec/market.rs
@@ -689,7 +689,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/exec/runtime.rs b/src/ops/exec/runtime.rs
@@ -493,7 +493,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/exec/trade.rs b/src/ops/exec/trade.rs
@@ -5,8 +5,7 @@ use serde_json::{Value, json};
use crate::cli::global::{
RecordLookupArgs, TradeAppRecordExportArgs, TradeCancelArgs, TradeDecisionArg,
- TradeDecisionArgs, TradeRebindArgs, TradeRevisionDecisionArg, TradeRevisionDecisionArgs,
- TradeRevisionProposeArgs, TradeStatusArgs, TradeSubmitArgs,
+ TradeDecisionArgs, TradeRebindArgs, TradeStatusArgs, TradeSubmitArgs,
};
use crate::ops::{
OperationAdapterError, OperationRequest, OperationRequestData, OperationRequestPayload,
@@ -15,17 +14,14 @@ use crate::ops::{
TradeCancelRequest, TradeCancelResult, TradeDeclineRequest, TradeDeclineResult,
TradeEventListRequest, TradeEventListResult, TradeEventWatchRequest, TradeEventWatchResult,
TradeGetRequest, TradeGetResult, TradeListRequest, TradeListResult, TradeRebindRequest,
- TradeRebindResult, TradeRevisionAcceptRequest, TradeRevisionAcceptResult,
- TradeRevisionDeclineRequest, TradeRevisionDeclineResult, TradeRevisionProposeRequest,
- TradeRevisionProposeResult, TradeStatusGetRequest, TradeStatusGetResult, TradeSubmitRequest,
+ TradeRebindResult, TradeStatusGetRequest, TradeStatusGetResult, TradeSubmitRequest,
TradeSubmitResult,
};
use crate::runtime::RuntimeError;
use crate::runtime::config::RuntimeConfig;
use crate::view::runtime::{
CommandDisposition, OrderAppRecordExportView, OrderCancellationView, OrderDecisionView,
- OrderRebindView, OrderRevisionDecisionView, OrderRevisionProposalView, OrderStatusView,
- OrderSubmitView,
+ OrderRebindView, OrderStatusView, OrderSubmitView,
};
const TRADE_EVENT_WATCH_DEFERRED_REASON: &str = "Nostr-backed trade event watch is not implemented";
@@ -288,137 +284,6 @@ impl OperationService<TradeCancelRequest> for TradeOperationService<'_> {
}
}
-impl OperationService<TradeRevisionProposeRequest> for TradeOperationService<'_> {
- type Result = TradeRevisionProposeResult;
-
- fn execute(
- &self,
- request: OperationRequest<TradeRevisionProposeRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let reason = string_input(&request, "reason")
- .map(|reason| reason.trim().to_owned())
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `reason` input".to_owned(),
- )
- })?;
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let args = TradeRevisionProposeArgs {
- key: required_trade_key(&request)?,
- reason,
- confirm_public_note: bool_input(&request, "confirm_public_note").unwrap_or(false),
- bin_id: string_input(&request, "bin_id"),
- bin_count: u32_input(&request, "bin_count"),
- adjustment_id: string_input(&request, "adjustment_id"),
- adjustment_effect: string_input(&request, "adjustment_effect"),
- adjustment_amount: string_input(&request, "adjustment_amount"),
- adjustment_currency: string_input(&request, "adjustment_currency"),
- adjustment_reason: string_input(&request, "adjustment_reason"),
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- };
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::revision_propose(&config, &args).map_err(|error| {
- OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
- })?;
- revision_proposal_result::<TradeRevisionProposeResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<TradeRevisionAcceptRequest> for TradeOperationService<'_> {
- type Result = TradeRevisionAcceptResult;
-
- fn execute(
- &self,
- request: OperationRequest<TradeRevisionAcceptRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let args = TradeRevisionDecisionArgs {
- key: required_trade_key(&request)?,
- revision_id: required_string_input(&request, "revision_id")?,
- decision: TradeRevisionDecisionArg::Accept,
- reason: None,
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- confirm_public_note: false,
- };
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::revision_decide(&config, &args).map_err(|error| {
- OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
- })?;
- revision_decision_result::<TradeRevisionAcceptResult>(request.operation_id(), &view)
- }
-}
-
-impl OperationService<TradeRevisionDeclineRequest> for TradeOperationService<'_> {
- type Result = TradeRevisionDeclineResult;
-
- fn execute(
- &self,
- request: OperationRequest<TradeRevisionDeclineRequest>,
- ) -> Result<OperationResult<Self::Result>, OperationAdapterError> {
- let reason = string_input(&request, "reason")
- .map(|reason| reason.trim().to_owned())
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- invalid_input(
- request.operation_id(),
- "missing required `reason` input".to_owned(),
- )
- })?;
- let args = TradeRevisionDecisionArgs {
- key: required_trade_key(&request)?,
- revision_id: required_string_input(&request, "revision_id")?,
- decision: TradeRevisionDecisionArg::Decline,
- reason: Some(reason),
- idempotency_key: request
- .context
- .idempotency_key
- .clone()
- .or_else(|| string_input(&request, "idempotency_key")),
- confirm_public_note: bool_input(&request, "confirm_public_note").unwrap_or(false),
- };
- if request.context.requires_approval_token() {
- return Err(OperationAdapterError::approval_required(
- request.operation_id(),
- ));
- }
-
- let mut config = self.config.clone();
- if request.context.dry_run {
- config.output.dry_run = true;
- }
- let view = crate::runtime::order::revision_decide(&config, &args).map_err(|error| {
- OperationAdapterError::sdk_adapter_failure(request.operation_id(), error)
- })?;
- revision_decision_result::<TradeRevisionDeclineResult>(request.operation_id(), &view)
- }
-}
-
impl OperationService<TradeStatusGetRequest> for TradeOperationService<'_> {
type Result = TradeStatusGetResult;
@@ -659,201 +524,6 @@ fn order_cancellation_error_detail(view: &OrderCancellationView) -> Value {
})
}
-fn revision_proposal_result<R>(
- operation_id: &str,
- view: &OrderRevisionProposalView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::ValidationFailed => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision propose failed validation with state `{}`",
- view.state
- )
- });
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- order_revision_proposal_error_detail(view),
- ))
- }
- disposition => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision propose finished with state `{}`",
- view.state
- )
- });
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_revision_proposal_error_detail(view);
- if !view.failed_transport_targets.is_empty()
- && view.attempted_transport_endpoints.is_empty()
- {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_revision_proposal_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_revision_proposal_error_detail(view: &OrderRevisionProposalView) -> Value {
- json!({
- "state": &view.state,
- "trade_id": &view.order_id,
- "locator": &view.locator,
- "revision_id": &view.revision_id,
- "listing_addr": &view.listing_addr,
- "request_event_id": &view.request_event_id,
- "decision_event_id": &view.decision_event_id,
- "root_event_id": &view.root_event_id,
- "prev_event_id": &view.prev_event_id,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "items": &view.items,
- "economics": &view.economics,
- "inventory": &view.inventory,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "dry_run": view.dry_run,
- "target_transport_endpoints": &view.target_transport_endpoints,
- "attempted_transport_endpoints": &view.attempted_transport_endpoints,
- "accepted_transport_endpoints": &view.accepted_transport_endpoints,
- "failed_transport_targets": &view.failed_transport_targets,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
-fn revision_decision_result<R>(
- operation_id: &str,
- view: &OrderRevisionDecisionView,
-) -> Result<OperationResult<R>, OperationAdapterError>
-where
- R: OperationResultData,
-{
- match view.disposition() {
- CommandDisposition::Success => serialized_target_result::<R, _>(view),
- CommandDisposition::ValidationFailed => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision {} failed validation with state `{}`",
- view.decision.as_deref().unwrap_or("decision"),
- view.state
- )
- });
- Err(OperationAdapterError::validation_failed_with_detail(
- operation_id,
- message,
- order_revision_decision_error_detail(view),
- ))
- }
- disposition => {
- let message = view.reason.clone().unwrap_or_else(|| {
- format!(
- "order revision {} finished with state `{}`",
- view.decision.as_deref().unwrap_or("decision"),
- view.state
- )
- });
- if disposition == CommandDisposition::ExternalUnavailable {
- let detail = order_revision_decision_error_detail(view);
- if !view.failed_transport_targets.is_empty()
- && view.attempted_transport_endpoints.is_empty()
- {
- Err(OperationAdapterError::network_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- } else {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- detail,
- ))
- }
- } else if disposition == CommandDisposition::Unconfigured {
- Err(OperationAdapterError::operation_unavailable_with_detail(
- operation_id,
- message,
- order_revision_decision_error_detail(view),
- ))
- } else {
- Err(OperationAdapterError::from_command_disposition(
- operation_id,
- disposition,
- message,
- ))
- }
- }
- }
-}
-
-fn order_revision_decision_error_detail(view: &OrderRevisionDecisionView) -> Value {
- json!({
- "state": &view.state,
- "trade_id": &view.order_id,
- "locator": &view.locator,
- "revision_id": &view.revision_id,
- "decision": &view.decision,
- "listing_addr": &view.listing_addr,
- "request_event_id": &view.request_event_id,
- "decision_event_id": &view.decision_event_id,
- "agreement_event_id": &view.agreement_event_id,
- "root_event_id": &view.root_event_id,
- "prev_event_id": &view.prev_event_id,
- "event_id": &view.event_id,
- "event_kind": view.event_kind,
- "economics": &view.economics,
- "inventory": &view.inventory,
- "buyer_pubkey": &view.buyer_pubkey,
- "seller_pubkey": &view.seller_pubkey,
- "dry_run": view.dry_run,
- "target_transport_endpoints": &view.target_transport_endpoints,
- "attempted_transport_endpoints": &view.attempted_transport_endpoints,
- "accepted_transport_endpoints": &view.accepted_transport_endpoints,
- "failed_transport_targets": &view.failed_transport_targets,
- "fetched_count": view.fetched_count,
- "decoded_count": view.decoded_count,
- "skipped_count": view.skipped_count,
- "idempotency_key": &view.idempotency_key,
- "signer_mode": &view.signer_mode,
- "issues": &view.issues,
- "actions": &view.actions,
- })
-}
-
fn status_result<R>(
operation_id: &str,
view: &OrderStatusView,
@@ -915,7 +585,6 @@ fn order_status_error_detail(view: &OrderStatusView) -> Value {
"buyer_pubkey": &view.buyer_pubkey,
"seller_pubkey": &view.seller_pubkey,
"last_event_id": &view.last_event_id,
- "revision": &view.revision,
"inventory": &view.inventory,
"lifecycle": &view.lifecycle,
"sdk_receipt": &view.sdk_receipt,
@@ -1232,18 +901,6 @@ where
string_input(request, key).map(PathBuf::from)
}
-fn u32_input<P>(request: &OperationRequest<P>, key: &str) -> Option<u32>
-where
- P: OperationRequestPayload + OperationRequestData,
-{
- request
- .payload
- .input()
- .get(key)
- .and_then(Value::as_u64)
- .and_then(|value| u32::try_from(value).ok())
-}
-
fn bool_input<P>(request: &OperationRequest<P>, key: &str) -> Option<bool>
where
P: OperationRequestPayload + OperationRequestData,
@@ -1274,7 +931,6 @@ mod tests {
OperationAdapter, OperationContext, OperationData, OperationRequest, TradeAcceptRequest,
TradeAcceptResult, TradeCancelRequest, TradeDeclineRequest, TradeDeclineResult,
TradeEventListRequest, TradeEventWatchRequest, TradeGetRequest, TradeListRequest,
- TradeRevisionAcceptRequest, TradeRevisionDeclineRequest, TradeRevisionProposeRequest,
TradeStatusGetRequest, TradeSubmitRequest,
};
use crate::runtime::config::{
@@ -1534,101 +1190,6 @@ mod tests {
}
#[test]
- fn order_revision_propose_requires_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(TradeOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- TradeRevisionProposeRequest::from_data(data(&[("trade_id", "ord_pending")])),
- )
- .expect("order revision request");
- let error = service.execute(revision).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_revision_propose_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(TradeOperationService::new(&config));
- let mut input = data(&[
- ("trade_id", "ord_pending"),
- ("reason", "update count"),
- ("bin_id", "bin-1"),
- ]);
- input.insert("bin_count".to_owned(), Value::from(3));
- let revision = OperationRequest::new(
- OperationContext::default(),
- TradeRevisionProposeRequest::from_data(input),
- )
- .expect("order revision request");
- let error = service.execute(revision).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_revision_accept_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(TradeOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- TradeRevisionAcceptRequest::from_data(data(&[
- ("trade_id", "ord_pending"),
- ("revision_id", "rev_pending"),
- ])),
- )
- .expect("order revision accept request");
- let error = service.execute(revision).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
- fn order_revision_decline_requires_reason_before_approval() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(TradeOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- TradeRevisionDeclineRequest::from_data(data(&[
- ("trade_id", "ord_pending"),
- ("revision_id", "rev_pending"),
- ])),
- )
- .expect("order revision decline request");
- let error = service.execute(revision).expect_err("reason required");
- let output_error = error.to_output_error();
-
- assert_eq!(output_error.code, "invalid_input");
- assert!(output_error.message.contains("reason"));
- }
-
- #[test]
- fn order_revision_decline_requires_approval_token() {
- let dir = tempdir().expect("tempdir");
- let config = sample_config(dir.path());
- let service = OperationAdapter::new(TradeOperationService::new(&config));
- let revision = OperationRequest::new(
- OperationContext::default(),
- TradeRevisionDeclineRequest::from_data(data(&[
- ("trade_id", "ord_pending"),
- ("revision_id", "rev_pending"),
- ("reason", "keep original order"),
- ])),
- )
- .expect("order revision decline request");
- let error = service.execute(revision).expect_err("approval required");
-
- assert_eq!(error.to_output_error().code, "approval_required");
- }
-
- #[test]
fn order_status_get_uses_local_sdk_projection_without_relay() {
let dir = tempdir().expect("tempdir");
let config = sample_config(dir.path());
@@ -1848,7 +1409,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/ops/mod.rs b/src/ops/mod.rs
@@ -277,191 +277,6 @@ mod tests {
#[test]
fn adapter_maps_order_lifecycle_inputs() {
- let revision = TargetCliArgs::try_parse_from([
- "radroots",
- "trade",
- "revision",
- "propose",
- "ord_test",
- "--reason",
- "update count",
- "--bin-id",
- "bin-1",
- "--bin-count",
- "3",
- "--adjustment-id",
- "adj-weather",
- "--adjustment-effect",
- "increase",
- "--adjustment-amount",
- "1.25",
- "--adjustment-currency",
- "USD",
- "--adjustment-reason",
- "weather delay",
- "--confirm-public-note",
- ])
- .expect("target args parse");
- let request =
- TargetOperationRequest::from_target_args(&revision).expect("operation request");
- let TargetOperationRequest::TradeRevisionPropose(request) = request else {
- panic!("expected order revision propose request")
- };
- assert_eq!(request.operation_id(), "trade.revision.propose");
- assert_eq!(
- request
- .payload
- .input
- .get("trade_id")
- .and_then(Value::as_str),
- Some("ord_test")
- );
- assert_eq!(
- request.payload.input.get("reason").and_then(Value::as_str),
- Some("update count")
- );
- assert_eq!(
- request.payload.input.get("bin_id").and_then(Value::as_str),
- Some("bin-1")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("bin_count")
- .and_then(Value::as_u64),
- Some(3)
- );
- assert_eq!(
- request
- .payload
- .input
- .get("adjustment_id")
- .and_then(Value::as_str),
- Some("adj-weather")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("adjustment_effect")
- .and_then(Value::as_str),
- Some("increase")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("adjustment_amount")
- .and_then(Value::as_str),
- Some("1.25")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("adjustment_currency")
- .and_then(Value::as_str),
- Some("USD")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("adjustment_reason")
- .and_then(Value::as_str),
- Some("weather delay")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("confirm_public_note")
- .and_then(Value::as_bool),
- Some(true)
- );
-
- let revision_accept = TargetCliArgs::try_parse_from([
- "radroots",
- "trade",
- "revision",
- "accept",
- "ord_test",
- "--revision-id",
- "rev_test",
- ])
- .expect("target args parse");
- let request =
- TargetOperationRequest::from_target_args(&revision_accept).expect("operation request");
- let TargetOperationRequest::TradeRevisionAccept(request) = request else {
- panic!("expected order revision accept request")
- };
- assert_eq!(request.operation_id(), "trade.revision.accept");
- assert_eq!(
- request
- .payload
- .input
- .get("trade_id")
- .and_then(Value::as_str),
- Some("ord_test")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("revision_id")
- .and_then(Value::as_str),
- Some("rev_test")
- );
-
- let revision_decline = TargetCliArgs::try_parse_from([
- "radroots",
- "trade",
- "revision",
- "decline",
- "ord_test",
- "--revision-id",
- "rev_test",
- "--reason",
- "keep original order",
- "--confirm-public-note",
- ])
- .expect("target args parse");
- let request =
- TargetOperationRequest::from_target_args(&revision_decline).expect("operation request");
- let TargetOperationRequest::TradeRevisionDecline(request) = request else {
- panic!("expected order revision decline request")
- };
- assert_eq!(request.operation_id(), "trade.revision.decline");
- assert_eq!(
- request
- .payload
- .input
- .get("trade_id")
- .and_then(Value::as_str),
- Some("ord_test")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("revision_id")
- .and_then(Value::as_str),
- Some("rev_test")
- );
- assert_eq!(
- request.payload.input.get("reason").and_then(Value::as_str),
- Some("keep original order")
- );
- assert_eq!(
- request
- .payload
- .input
- .get("confirm_public_note")
- .and_then(Value::as_bool),
- Some(true)
- );
-
let cancel = TargetCliArgs::try_parse_from([
"radroots",
"trade",
diff --git a/src/ops/target.rs b/src/ops/target.rs
@@ -249,9 +249,6 @@ target_operation_contracts! {
TradeAccept => (TradeAcceptRequest, TradeAcceptResult, "trade.accept"),
TradeDecline => (TradeDeclineRequest, TradeDeclineResult, "trade.decline"),
TradeCancel => (TradeCancelRequest, TradeCancelResult, "trade.cancel"),
- TradeRevisionPropose => (TradeRevisionProposeRequest, TradeRevisionProposeResult, "trade.revision.propose"),
- TradeRevisionAccept => (TradeRevisionAcceptRequest, TradeRevisionAcceptResult, "trade.revision.accept"),
- TradeRevisionDecline => (TradeRevisionDeclineRequest, TradeRevisionDeclineResult, "trade.revision.decline"),
TradeStatusGet => (TradeStatusGetRequest, TradeStatusGetResult, "trade.status.get"),
TradeEventList => (TradeEventListRequest, TradeEventListResult, "trade.event.list"),
TradeEventWatch => (TradeEventWatchRequest, TradeEventWatchResult, "trade.event.watch"),
diff --git a/src/out/terminal/renderers/trade.rs b/src/out/terminal/renderers/trade.rs
@@ -20,9 +20,6 @@ pub fn register(registry: TerminalRendererRegistry) -> TerminalRendererRegistry
.register("trade.accept", &TRADE_RENDERER)
.register("trade.decline", &TRADE_RENDERER)
.register("trade.cancel", &TRADE_RENDERER)
- .register("trade.revision.propose", &TRADE_RENDERER)
- .register("trade.revision.accept", &TRADE_RENDERER)
- .register("trade.revision.decline", &TRADE_RENDERER)
.register("trade.status.get", &TRADE_RENDERER)
.register("trade.event.list", &TRADE_RENDERER)
.register("trade.event.watch", &TRADE_RENDERER)
@@ -50,10 +47,6 @@ impl TerminalOperationRenderer for TradeRenderer {
"trade.submit" => trade_publish_document(envelope, result),
"trade.accept" | "trade.decline" => trade_decision_document(envelope, result),
"trade.cancel" => trade_cancel_document(envelope, result),
- "trade.revision.propose" => trade_revision_propose_document(envelope, result),
- "trade.revision.accept" | "trade.revision.decline" => {
- trade_revision_decision_document(envelope, result)
- }
_ => trade_detail_document(envelope, result),
}
}
@@ -111,41 +104,6 @@ fn trade_cancel_document(envelope: &OutputEnvelope, result: &Value) -> TerminalD
document
}
-fn trade_revision_propose_document(envelope: &OutputEnvelope, result: &Value) -> TerminalDocument {
- let mut document = common::document_with_status_title(envelope, "Trade revision propose");
- push_trade_identity_fields(&mut document, result);
- common::push_path_field(&mut document, "Revision", result, &["revision_id"]);
- common::push_bool_field(&mut document, "Dry run", result, &["dry_run"]);
- common::push_path_field(&mut document, "Request", result, &["request_event_id"]);
- common::push_path_field(&mut document, "Decision", result, &["decision_event_id"]);
- common::push_path_field(&mut document, "Event", result, &["event_id"]);
- common::push_path_field(&mut document, "Signer", result, &["signer_mode"]);
- push_relay_field(&mut document, result);
- push_trade_items_section(&mut document, result);
- push_inventory_section(&mut document, result);
- push_issue_sections(&mut document, result);
- document
-}
-
-fn trade_revision_decision_document(envelope: &OutputEnvelope, result: &Value) -> TerminalDocument {
- let mut document = common::document_with_status_title(
- envelope,
- trade_revision_title(envelope.operation_id.as_str()),
- );
- push_trade_identity_fields(&mut document, result);
- common::push_path_field(&mut document, "Revision", result, &["revision_id"]);
- common::push_path_field(&mut document, "Decision", result, &["decision"]);
- common::push_bool_field(&mut document, "Dry run", result, &["dry_run"]);
- common::push_path_field(&mut document, "Request", result, &["request_event_id"]);
- common::push_path_field(&mut document, "Agreement", result, &["agreement_event_id"]);
- common::push_path_field(&mut document, "Event", result, &["event_id"]);
- common::push_path_field(&mut document, "Signer", result, &["signer_mode"]);
- push_relay_field(&mut document, result);
- push_inventory_section(&mut document, result);
- push_issue_sections(&mut document, result);
- document
-}
-
fn trade_detail_document(envelope: &OutputEnvelope, result: &Value) -> TerminalDocument {
let mut document = common::document_with_title(envelope, "Trade");
push_trade_identity_fields(&mut document, result);
@@ -338,7 +296,6 @@ fn trade_status_document(envelope: &OutputEnvelope, result: &Value) -> TerminalD
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);
push_ambiguity_candidates_section(&mut document, result);
@@ -632,14 +589,6 @@ fn trade_decision_title(operation_id: &str) -> &'static str {
}
}
-fn trade_revision_title(operation_id: &str) -> &'static str {
- match operation_id {
- "trade.revision.accept" => "Trade revision accept",
- "trade.revision.decline" => "Trade revision decline",
- _ => "Trade revision decision",
- }
-}
-
#[cfg(test)]
mod tests {
use serde_json::json;
diff --git a/src/out/terminal/renderers/validation.rs b/src/out/terminal/renderers/validation.rs
@@ -174,21 +174,23 @@ fn push_relay_field(document: &mut TerminalDocument, result: &Value) {
fn proof_state_label(proof: &Value) -> String {
match common::string(proof, &["state"]).as_deref() {
- Some("local_only_deterministic_receipt") => return "local-only deterministic".to_owned(),
- Some("pending_rhi_validation") => return "pending RHI".to_owned(),
- Some("trusted_service_validated") => return "trusted service".to_owned(),
- Some("trusted_service_and_proof_verified") => return "trusted service + proof".to_owned(),
+ Some("validator_set_deterministic_receipt") => {
+ return "validator-set deterministic".to_owned();
+ }
+ Some("pending_validator_validation") => return "pending validator".to_owned(),
+ Some("validator_set_validated") => return "validator set".to_owned(),
+ Some("validator_set_and_proof_verified") => return "validator set + proof".to_owned(),
Some("sp1_inline_proof_verified") => return "cryptographic proof".to_owned(),
Some("cryptographic_proof_verified") => return "cryptographic proof".to_owned(),
Some("sp1_execute_checked") => return "SP1 execute checked".to_owned(),
- Some("worker_evidence_trust_metadata_missing") => {
+ Some("validator_evidence_trust_metadata_missing") => {
return "trust metadata missing".to_owned();
}
- Some("worker_evidence_trust_metadata_mismatch") => {
+ Some("validator_evidence_trust_metadata_mismatch") => {
return "trust metadata mismatch".to_owned();
}
- Some("validation_receipt_worker_evidence_invalid") => {
- return "worker evidence invalid".to_owned();
+ Some("validation_receipt_validator_evidence_invalid") => {
+ return "validator evidence invalid".to_owned();
}
_ => {}
}
@@ -203,16 +205,16 @@ fn proof_state_label(proof: &Value) -> String {
fn proof_summary_from_summary(receipt: &Value) -> String {
match common::string(receipt, &["proof_verification_state"]).as_deref() {
- Some("local_only_deterministic_receipt") => "local-only".to_owned(),
- Some("pending_rhi_validation") => "pending RHI".to_owned(),
- Some("trusted_service_validated") => "trusted service".to_owned(),
- Some("trusted_service_and_proof_verified") => "trusted+proof".to_owned(),
+ Some("validator_set_deterministic_receipt") => "validator-set".to_owned(),
+ Some("pending_validator_validation") => "pending validator".to_owned(),
+ Some("validator_set_validated") => "validator set".to_owned(),
+ Some("validator_set_and_proof_verified") => "validator+proof".to_owned(),
Some("sp1_inline_proof_verified") => "proof verified".to_owned(),
Some("cryptographic_proof_verified") => "proof verified".to_owned(),
Some("sp1_execute_checked") => "execute checked".to_owned(),
- Some("worker_evidence_trust_metadata_missing") => "trust missing".to_owned(),
- Some("worker_evidence_trust_metadata_mismatch") => "trust mismatch".to_owned(),
- Some("validation_receipt_worker_evidence_invalid") => "invalid".to_owned(),
+ Some("validator_evidence_trust_metadata_missing") => "trust missing".to_owned(),
+ Some("validator_evidence_trust_metadata_mismatch") => "trust mismatch".to_owned(),
+ Some("validation_receipt_validator_evidence_invalid") => "invalid".to_owned(),
Some(_) => "available".to_owned(),
None => String::new(),
}
@@ -300,10 +302,10 @@ mod tests {
"order_id": "trade_test",
"validation_state": "invalid",
"proof_verification": {
- "state": "local_only_deterministic_receipt",
+ "state": "validator_set_deterministic_receipt",
"proof_system": "none",
- "validation_authority": "dev_deterministic_only",
- "commitment_confidence": "local_only",
+ "validation_authority": "validator_set_deterministic",
+ "commitment_confidence": "committed_by_validator_set",
"production_verification": false,
"cryptographic_proof_required": false,
"cryptographic_proof_verified": false,
@@ -315,8 +317,8 @@ mod tests {
let document = VALIDATION_RENDERER.render(&envelope, &TerminalRenderContext::default());
let rendered = render_terminal_document(&document, &TerminalRenderContext::default());
- assert!(rendered.contains("Proof local-only deterministic"));
- assert!(rendered.contains("Authority dev_deterministic_only"));
+ assert!(rendered.contains("Proof validator-set deterministic"));
+ assert!(rendered.contains("Authority validator_set_deterministic"));
assert!(rendered.contains("Confidence local_only"));
assert!(!rendered.contains("Proof verified"));
}
@@ -335,13 +337,13 @@ mod tests {
{
"receipt_event_id": "receipt_local",
"result": "valid",
- "proof_verification_state": "local_only_deterministic_receipt",
+ "proof_verification_state": "validator_set_deterministic_receipt",
"receipt_type": "trade_transition"
},
{
"receipt_event_id": "receipt_trusted",
"result": "valid",
- "proof_verification_state": "trusted_service_validated",
+ "proof_verification_state": "validator_set_validated",
"receipt_type": "trade_transition"
}
],
@@ -352,8 +354,8 @@ mod tests {
let document = VALIDATION_RENDERER.render(&envelope, &TerminalRenderContext::default());
let rendered = render_terminal_document(&document, &TerminalRenderContext::default());
- assert!(rendered.contains("local-only"));
- assert!(rendered.contains("trusted service"));
+ assert!(rendered.contains("validator-set"));
+ assert!(rendered.contains("validator set"));
assert!(!rendered.contains("not required"));
}
}
diff --git a/src/registry/mod.rs b/src/registry/mod.rs
@@ -173,9 +173,6 @@ pub const OPERATION_REGISTRY: &[OperationSpec] = &[
trade::TRADE_ACCEPT,
trade::TRADE_DECLINE,
trade::TRADE_CANCEL,
- trade::TRADE_REVISION_PROPOSE,
- trade::TRADE_REVISION_ACCEPT,
- trade::TRADE_REVISION_DECLINE,
trade::TRADE_STATUS_GET,
trade::TRADE_EVENT_LIST,
trade::TRADE_EVENT_WATCH,
@@ -208,12 +205,7 @@ pub fn network_requirement(operation_id: &str) -> NetworkRequirement {
| "validation.receipt.verify" => NetworkRequirement::External {
dry_run_requires_network: false,
},
- "trade.accept"
- | "trade.decline"
- | "trade.cancel"
- | "trade.revision.propose"
- | "trade.revision.accept"
- | "trade.revision.decline" => NetworkRequirement::External {
+ "trade.accept" | "trade.decline" | "trade.cancel" => NetworkRequirement::External {
dry_run_requires_network: true,
},
_ => NetworkRequirement::Local,
@@ -229,9 +221,6 @@ pub fn requires_local_signer_mode(operation_id: &str) -> bool {
| "trade.accept"
| "trade.decline"
| "trade.cancel"
- | "trade.revision.propose"
- | "trade.revision.accept"
- | "trade.revision.decline"
)
}
@@ -341,9 +330,6 @@ mod tests {
"trade.accept",
"trade.decline",
"trade.cancel",
- "trade.revision.propose",
- "trade.revision.accept",
- "trade.revision.decline",
"trade.status.get",
"trade.event.list",
"trade.event.watch",
@@ -395,9 +381,6 @@ mod tests {
"trade.accept",
"trade.decline",
"trade.cancel",
- "trade.revision.propose",
- "trade.revision.accept",
- "trade.revision.decline",
];
const INTENTIONALLY_UNSUPPORTED_MUTATING_DRY_RUN_OPERATION_IDS: &[&str] = &[];
@@ -470,9 +453,6 @@ mod tests {
"trade.accept",
"trade.decline",
"trade.cancel",
- "trade.revision.propose",
- "trade.revision.accept",
- "trade.revision.decline",
]
.into_iter()
.collect::<BTreeSet<_>>();
@@ -574,9 +554,6 @@ mod tests {
"trade.accept",
"trade.decline",
"trade.cancel",
- "trade.revision.propose",
- "trade.revision.accept",
- "trade.revision.decline",
"transport.outbox.push",
"trade.event.list",
"validation.receipt.get",
@@ -602,9 +579,6 @@ mod tests {
"trade.accept",
"trade.decline",
"trade.cancel",
- "trade.revision.propose",
- "trade.revision.accept",
- "trade.revision.decline",
"transport.outbox.push",
]
.into_iter()
diff --git a/src/registry/trade.rs b/src/registry/trade.rs
@@ -144,54 +144,6 @@ pub const TRADE_CANCEL: OperationSpec = operation!(
true
);
-pub const TRADE_REVISION_PROPOSE: OperationSpec = operation!(
- "trade.revision.propose",
- "radroots trade revision propose",
- "trade",
- "trade_revision_propose",
- "TradeRevisionProposeRequest",
- "TradeRevisionProposeResult",
- "Propose seller-authored trade revision.",
- Seller,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const TRADE_REVISION_ACCEPT: OperationSpec = operation!(
- "trade.revision.accept",
- "radroots trade revision accept",
- "trade",
- "trade_revision_accept",
- "TradeRevisionAcceptRequest",
- "TradeRevisionAcceptResult",
- "Accept a seller-authored trade revision.",
- Buyer,
- true,
- Required,
- High,
- false,
- true
-);
-
-pub const TRADE_REVISION_DECLINE: OperationSpec = operation!(
- "trade.revision.decline",
- "radroots trade revision decline",
- "trade",
- "trade_revision_decline",
- "TradeRevisionDeclineRequest",
- "TradeRevisionDeclineResult",
- "Decline a seller-authored trade revision.",
- Buyer,
- true,
- Required,
- High,
- false,
- true
-);
-
pub const TRADE_STATUS_GET: OperationSpec = operation!(
"trade.status.get",
"radroots trade status get",
diff --git a/src/runtime/config.rs b/src/runtime/config.rs
@@ -56,7 +56,20 @@ const ENV_CLI_MYC_EXECUTABLE: &str = "RADROOTS_CLI_MYC_EXECUTABLE";
const ENV_CLI_MYC_STATUS_TIMEOUT_MS: &str = "RADROOTS_CLI_MYC_STATUS_TIMEOUT_MS";
const ENV_CLI_HYF_ENABLED: &str = "RADROOTS_CLI_HYF_ENABLED";
const ENV_CLI_HYF_EXECUTABLE: &str = "RADROOTS_CLI_HYF_EXECUTABLE";
-const ENV_CLI_RHI_TRUSTED_WORKER_PUBKEYS: &str = "RADROOTS_CLI_RHI_TRUSTED_WORKER_PUBKEYS";
+const ENV_CLI_RHI_VALIDATOR_SET_ID: &str = "RADROOTS_CLI_RHI_VALIDATOR_SET_ID";
+const ENV_CLI_RHI_VALIDATOR_PUBKEY: &str = "RADROOTS_CLI_RHI_VALIDATOR_PUBKEY";
+const ENV_CLI_RHI_VALIDATOR_SET_ADDR: &str = "RADROOTS_CLI_RHI_VALIDATOR_SET_ADDR";
+const ENV_CLI_RHI_VALIDATOR_SET_EVENT_ID: &str = "RADROOTS_CLI_RHI_VALIDATOR_SET_EVENT_ID";
+const ENV_CLI_RHI_VALIDATOR_SET_VALID_FROM: &str = "RADROOTS_CLI_RHI_VALIDATOR_SET_VALID_FROM";
+const ENV_CLI_RHI_VALIDATOR_SET_VALID_UNTIL: &str = "RADROOTS_CLI_RHI_VALIDATOR_SET_VALID_UNTIL";
+const ENV_CLI_RHI_VALIDATOR_SET_PROTOCOL_CONTRACT_HASH: &str =
+ "RADROOTS_CLI_RHI_VALIDATOR_SET_PROTOCOL_CONTRACT_HASH";
+const ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_NAME: &str =
+ "RADROOTS_CLI_RHI_VALIDATOR_SET_OPERATOR_NAME";
+const ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_CONTACT: &str =
+ "RADROOTS_CLI_RHI_VALIDATOR_SET_OPERATOR_CONTACT";
+const ENV_CLI_RHI_REQUIRE_CRYPTOGRAPHIC_PROOF: &str =
+ "RADROOTS_CLI_RHI_REQUIRE_CRYPTOGRAPHIC_PROOF";
const ENV_CLI_MESH_SCOPE: &str = "RADROOTS_CLI_MESH_SCOPE";
const SUPPORTED_ENV_FILE_KEYS: &[&str] = &[
ENV_CLI_OUTPUT_FORMAT,
@@ -81,7 +94,16 @@ const SUPPORTED_ENV_FILE_KEYS: &[&str] = &[
ENV_CLI_MYC_STATUS_TIMEOUT_MS,
ENV_CLI_HYF_ENABLED,
ENV_CLI_HYF_EXECUTABLE,
- ENV_CLI_RHI_TRUSTED_WORKER_PUBKEYS,
+ ENV_CLI_RHI_VALIDATOR_SET_ID,
+ ENV_CLI_RHI_VALIDATOR_PUBKEY,
+ ENV_CLI_RHI_VALIDATOR_SET_ADDR,
+ ENV_CLI_RHI_VALIDATOR_SET_EVENT_ID,
+ ENV_CLI_RHI_VALIDATOR_SET_VALID_FROM,
+ ENV_CLI_RHI_VALIDATOR_SET_VALID_UNTIL,
+ ENV_CLI_RHI_VALIDATOR_SET_PROTOCOL_CONTRACT_HASH,
+ ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_NAME,
+ ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_CONTACT,
+ ENV_CLI_RHI_REQUIRE_CRYPTOGRAPHIC_PROOF,
ENV_CLI_MESH_SCOPE,
];
@@ -445,7 +467,21 @@ pub struct RpcConfig {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RhiConfig {
- pub trusted_worker_pubkeys: Vec<String>,
+ pub validator_set: Option<RhiValidatorSetConfig>,
+ pub require_cryptographic_proof: bool,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct RhiValidatorSetConfig {
+ pub set_id: String,
+ pub validator_pubkey: String,
+ pub validator_set_addr: String,
+ pub validator_set_event_id: String,
+ pub valid_from: u64,
+ pub valid_until: u64,
+ pub protocol_contract_hash: String,
+ pub operator_name: String,
+ pub operator_contact: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -567,7 +603,22 @@ struct RpcFileConfig {
#[derive(Debug, Default, Deserialize)]
#[serde(default, deny_unknown_fields)]
struct RhiFileConfig {
- trusted_worker_pubkeys: Option<Vec<String>>,
+ validator_set: Option<RhiValidatorSetFileConfig>,
+ require_cryptographic_proof: Option<bool>,
+}
+
+#[derive(Debug, Default, Deserialize)]
+#[serde(default, deny_unknown_fields)]
+struct RhiValidatorSetFileConfig {
+ set_id: Option<String>,
+ validator_pubkey: Option<String>,
+ validator_set_addr: Option<String>,
+ validator_set_event_id: Option<String>,
+ valid_from: Option<u64>,
+ valid_until: Option<u64>,
+ protocol_contract_hash: Option<String>,
+ operator_name: Option<String>,
+ operator_contact: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
@@ -1202,47 +1253,224 @@ fn resolve_rhi_config(
user_config: Option<&CliConfigFile>,
workspace_config: Option<&CliConfigFile>,
) -> Result<RhiConfig, RuntimeError> {
- let trusted_worker_pubkeys =
- if let Some(value) = env_value(env, env_file, &[ENV_CLI_RHI_TRUSTED_WORKER_PUBKEYS]) {
- parse_pubkey_env_value(value.as_str(), ENV_CLI_RHI_TRUSTED_WORKER_PUBKEYS)?
- } else if let Some(values) = user_config
+ let validator_set =
+ resolve_rhi_validator_set_config(env, env_file, user_config, workspace_config)?;
+ let require_cryptographic_proof =
+ if let Some(value) = env_value(env, env_file, &[ENV_CLI_RHI_REQUIRE_CRYPTOGRAPHIC_PROOF]) {
+ parse_bool_env(ENV_CLI_RHI_REQUIRE_CRYPTOGRAPHIC_PROOF, value.as_str())?
+ } else if let Some(value) = user_config
.and_then(|config| config.rhi.as_ref())
- .and_then(|rhi| rhi.trusted_worker_pubkeys.clone())
+ .and_then(|rhi| rhi.require_cryptographic_proof)
{
- normalize_pubkeys(values, "user config [rhi].trusted_worker_pubkeys")?
- } else if let Some(values) = workspace_config
+ value
+ } else if let Some(value) = workspace_config
.and_then(|config| config.rhi.as_ref())
- .and_then(|rhi| rhi.trusted_worker_pubkeys.clone())
+ .and_then(|rhi| rhi.require_cryptographic_proof)
{
- normalize_pubkeys(values, "workspace config [rhi].trusted_worker_pubkeys")?
+ value
} else {
- Vec::new()
+ false
};
Ok(RhiConfig {
- trusted_worker_pubkeys,
+ validator_set,
+ require_cryptographic_proof,
})
}
-fn parse_pubkey_env_value(value: &str, key: &str) -> Result<Vec<String>, RuntimeError> {
- let entries = value
- .split(',')
- .map(str::trim)
- .filter(|entry| !entry.is_empty())
- .map(ToOwned::to_owned)
- .collect::<Vec<_>>();
- normalize_pubkeys(entries, key)
+fn resolve_rhi_validator_set_config(
+ env: &dyn Environment,
+ env_file: &EnvFileValues,
+ user_config: Option<&CliConfigFile>,
+ workspace_config: Option<&CliConfigFile>,
+) -> Result<Option<RhiValidatorSetConfig>, RuntimeError> {
+ let env_keys = [
+ ENV_CLI_RHI_VALIDATOR_SET_ID,
+ ENV_CLI_RHI_VALIDATOR_PUBKEY,
+ ENV_CLI_RHI_VALIDATOR_SET_ADDR,
+ ENV_CLI_RHI_VALIDATOR_SET_EVENT_ID,
+ ENV_CLI_RHI_VALIDATOR_SET_VALID_FROM,
+ ENV_CLI_RHI_VALIDATOR_SET_VALID_UNTIL,
+ ENV_CLI_RHI_VALIDATOR_SET_PROTOCOL_CONTRACT_HASH,
+ ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_NAME,
+ ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_CONTACT,
+ ];
+ if env_keys
+ .iter()
+ .any(|key| env_value(env, env_file, &[*key]).is_some())
+ {
+ return Ok(Some(validate_rhi_validator_set_config(
+ RhiValidatorSetConfig {
+ set_id: required_env_value(env, env_file, ENV_CLI_RHI_VALIDATOR_SET_ID)?,
+ validator_pubkey: required_env_value(env, env_file, ENV_CLI_RHI_VALIDATOR_PUBKEY)?,
+ validator_set_addr: required_env_value(
+ env,
+ env_file,
+ ENV_CLI_RHI_VALIDATOR_SET_ADDR,
+ )?,
+ validator_set_event_id: required_env_value(
+ env,
+ env_file,
+ ENV_CLI_RHI_VALIDATOR_SET_EVENT_ID,
+ )?,
+ valid_from: parse_u64_value(
+ ENV_CLI_RHI_VALIDATOR_SET_VALID_FROM,
+ required_env_value(env, env_file, ENV_CLI_RHI_VALIDATOR_SET_VALID_FROM)?
+ .as_str(),
+ )
+ .map_err(|err| RuntimeError::Config(err.to_string()))?,
+ valid_until: parse_u64_value(
+ ENV_CLI_RHI_VALIDATOR_SET_VALID_UNTIL,
+ required_env_value(env, env_file, ENV_CLI_RHI_VALIDATOR_SET_VALID_UNTIL)?
+ .as_str(),
+ )
+ .map_err(|err| RuntimeError::Config(err.to_string()))?,
+ protocol_contract_hash: required_env_value(
+ env,
+ env_file,
+ ENV_CLI_RHI_VALIDATOR_SET_PROTOCOL_CONTRACT_HASH,
+ )?,
+ operator_name: required_env_value(
+ env,
+ env_file,
+ ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_NAME,
+ )?,
+ operator_contact: env_value(
+ env,
+ env_file,
+ &[ENV_CLI_RHI_VALIDATOR_SET_OPERATOR_CONTACT],
+ ),
+ },
+ "environment RHI validator_set",
+ )?));
+ }
+ if let Some(config) = user_config
+ .and_then(|config| config.rhi.as_ref())
+ .and_then(|rhi| rhi.validator_set.as_ref())
+ {
+ return Ok(Some(rhi_validator_set_from_file_config(
+ config,
+ "user config [rhi.validator_set]",
+ )?));
+ }
+ if let Some(config) = workspace_config
+ .and_then(|config| config.rhi.as_ref())
+ .and_then(|rhi| rhi.validator_set.as_ref())
+ {
+ return Ok(Some(rhi_validator_set_from_file_config(
+ config,
+ "workspace config [rhi.validator_set]",
+ )?));
+ }
+ Ok(None)
}
-fn normalize_pubkeys(values: Vec<String>, source: &str) -> Result<Vec<String>, RuntimeError> {
- let mut normalized = Vec::new();
- for value in values {
- let pubkey = validate_pubkey(value.as_str(), source)?;
- if !normalized.iter().any(|existing| existing == &pubkey) {
- normalized.push(pubkey);
- }
+fn required_env_value(
+ env: &dyn Environment,
+ env_file: &EnvFileValues,
+ key: &'static str,
+) -> Result<String, RuntimeError> {
+ env_value(env, env_file, &[key]).ok_or_else(|| {
+ RuntimeError::Config(format!(
+ "{key} is required when any RHI validator set environment key is set"
+ ))
+ })
+}
+
+fn rhi_validator_set_from_file_config(
+ config: &RhiValidatorSetFileConfig,
+ source: &'static str,
+) -> Result<RhiValidatorSetConfig, RuntimeError> {
+ validate_rhi_validator_set_config(
+ RhiValidatorSetConfig {
+ set_id: required_file_value(config.set_id.clone(), source, "set_id")?,
+ validator_pubkey: required_file_value(
+ config.validator_pubkey.clone(),
+ source,
+ "validator_pubkey",
+ )?,
+ validator_set_addr: required_file_value(
+ config.validator_set_addr.clone(),
+ source,
+ "validator_set_addr",
+ )?,
+ validator_set_event_id: required_file_value(
+ config.validator_set_event_id.clone(),
+ source,
+ "validator_set_event_id",
+ )?,
+ valid_from: config
+ .valid_from
+ .ok_or_else(|| missing_file_value(source, "valid_from"))?,
+ valid_until: config
+ .valid_until
+ .ok_or_else(|| missing_file_value(source, "valid_until"))?,
+ protocol_contract_hash: required_file_value(
+ config.protocol_contract_hash.clone(),
+ source,
+ "protocol_contract_hash",
+ )?,
+ operator_name: required_file_value(
+ config.operator_name.clone(),
+ source,
+ "operator_name",
+ )?,
+ operator_contact: config.operator_contact.clone(),
+ },
+ source,
+ )
+}
+
+fn required_file_value(
+ value: Option<String>,
+ source: &'static str,
+ field: &'static str,
+) -> Result<String, RuntimeError> {
+ value.ok_or_else(|| missing_file_value(source, field))
+}
+
+fn missing_file_value(source: &'static str, field: &'static str) -> RuntimeError {
+ RuntimeError::Config(format!("{source}.{field} is required"))
+}
+
+fn validate_rhi_validator_set_config(
+ mut config: RhiValidatorSetConfig,
+ source: &'static str,
+) -> Result<RhiValidatorSetConfig, RuntimeError> {
+ config.set_id = validate_uuidv7(config.set_id.as_str(), source, "set_id")?;
+ config.validator_pubkey = validate_pubkey(
+ config.validator_pubkey.as_str(),
+ format!("{source}.validator_pubkey").as_str(),
+ )?;
+ config.validator_set_event_id = validate_event_id(
+ config.validator_set_event_id.as_str(),
+ format!("{source}.validator_set_event_id").as_str(),
+ )?;
+ config.protocol_contract_hash = validate_hash32(
+ config.protocol_contract_hash.as_str(),
+ format!("{source}.protocol_contract_hash").as_str(),
+ )?;
+ config.validator_set_addr = validate_validator_set_address(
+ config.validator_set_addr.as_str(),
+ config.set_id.as_str(),
+ format!("{source}.validator_set_addr").as_str(),
+ )?;
+ if config.valid_from >= config.valid_until {
+ return Err(RuntimeError::Config(format!(
+ "{source}.valid_from must be less than {source}.valid_until"
+ )));
}
- Ok(normalized)
+ config.operator_name = validate_required_text(
+ config.operator_name.as_str(),
+ format!("{source}.operator_name").as_str(),
+ )?;
+ if let Some(operator_contact) = config.operator_contact.as_ref() {
+ validate_required_text(
+ operator_contact.as_str(),
+ format!("{source}.operator_contact").as_str(),
+ )?;
+ }
+ Ok(config)
}
fn validate_pubkey(value: &str, source: &str) -> Result<String, RuntimeError> {
@@ -1255,6 +1483,92 @@ fn validate_pubkey(value: &str, source: &str) -> Result<String, RuntimeError> {
Ok(trimmed.to_ascii_lowercase())
}
+fn validate_event_id(value: &str, source: &str) -> Result<String, RuntimeError> {
+ validate_pubkey(value, source)
+}
+
+fn validate_hash32(value: &str, source: &str) -> Result<String, RuntimeError> {
+ let trimmed = value.trim();
+ let Some(hex) = trimmed.strip_prefix("0x") else {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a 0x-prefixed 32-byte lowercase hex digest"
+ )));
+ };
+ if hex.len() != 64 || !hex.chars().all(|char| char.is_ascii_hexdigit()) {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a 0x-prefixed 32-byte lowercase hex digest"
+ )));
+ }
+ Ok(format!("0x{}", hex.to_ascii_lowercase()))
+}
+
+fn validate_uuidv7(value: &str, source: &str, field: &str) -> Result<String, RuntimeError> {
+ let trimmed = value.trim().to_ascii_lowercase();
+ let bytes = trimmed.as_bytes();
+ if bytes.len() != 36
+ || bytes[8] != b'-'
+ || bytes[13] != b'-'
+ || bytes[18] != b'-'
+ || bytes[23] != b'-'
+ || bytes[14] != b'7'
+ || !matches!(bytes[19], b'8'..=b'9' | b'a'..=b'b')
+ {
+ return Err(RuntimeError::Config(format!(
+ "{source}.{field} must be a UUIDv7"
+ )));
+ }
+ for (index, byte) in bytes.iter().enumerate() {
+ if matches!(index, 8 | 13 | 18 | 23) {
+ continue;
+ }
+ if !byte.is_ascii_digit() && !(b'a'..=b'f').contains(byte) {
+ return Err(RuntimeError::Config(format!(
+ "{source}.{field} must be a UUIDv7"
+ )));
+ }
+ }
+ Ok(trimmed)
+}
+
+fn validate_validator_set_address(
+ value: &str,
+ expected_set_id: &str,
+ source: &str,
+) -> Result<String, RuntimeError> {
+ let trimmed = value.trim();
+ let mut parts = trimmed.split(':');
+ let Some(kind) = parts.next() else {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a 30381 address"
+ )));
+ };
+ let Some(pubkey) = parts.next() else {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a 30381 address"
+ )));
+ };
+ let Some(set_id) = parts.next() else {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a 30381 address"
+ )));
+ };
+ if parts.next().is_some() || kind != "30381" || set_id != expected_set_id {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a 30381 address"
+ )));
+ }
+ let pubkey = validate_pubkey(pubkey, source)?;
+ Ok(format!("30381:{pubkey}:{set_id}"))
+}
+
+fn validate_required_text(value: &str, source: &str) -> Result<String, RuntimeError> {
+ let trimmed = value.trim();
+ if trimmed.is_empty() {
+ return Err(RuntimeError::Config(format!("{source} must not be empty")));
+ }
+ Ok(trimmed.to_owned())
+}
+
fn resolve_capability_bindings(
user_config: Option<&CliConfigFile>,
workspace_config: Option<&CliConfigFile>,
@@ -2456,7 +2770,8 @@ mod tests {
PathBuf::from(DEFAULT_HYF_EXECUTABLE)
);
assert_eq!(resolved.rpc.url, DEFAULT_RPC_URL);
- assert_eq!(resolved.rhi.trusted_worker_pubkeys, Vec::<String>::new());
+ assert_eq!(resolved.rhi.validator_set, None);
+ assert!(!resolved.rhi.require_cryptographic_proof);
}
#[test]
diff --git a/src/runtime/order.rs b/src/runtime/order.rs
@@ -49,16 +49,17 @@ use radroots_runtime_store::{
};
use radroots_sdk::{
PrivacyPreflightConfirmation, ProductSensitivityField, PublishMode, PushOutboxEventReceipt,
- PushOutboxEventState, PushOutboxReceipt, PushOutboxTargetOutcomeKind, SatisfactionPolicy,
- SdkMutationState, SdkTradeStatusSource, TargetPolicy, TradeAcceptRequest, TradeCancelRequest,
- TradeCancellationPlan, TradeCancellationReceipt, TradeDecisionPlan, TradeDecisionReceipt,
- TradeDeclineRequest, TradeEvidenceMode, TradeMutationOutcome, TradeProposeRequest,
- TradeStatusReceipt, TradeStatusRequest, TradeSubmitPlan, TradeSubmitReceipt,
- TradeWorkflowEnqueueReceipt,
+ PushOutboxEventState, PushOutboxReceipt, PushOutboxTargetOutcomeKind,
+ RadrootsTradeValidationTrustPolicy, SatisfactionPolicy, SdkMutationState, SdkTradeStatusSource,
+ TargetPolicy, TradeAcceptRequest, TradeCancelRequest, TradeCancellationPlan,
+ TradeCancellationReceipt, TradeDecisionPlan, TradeDecisionReceipt, TradeDeclineRequest,
+ TradeEvidenceMode, TradeMutationOutcome, TradeProposeRequest, TradeStatusReceipt,
+ TradeStatusRequest, TradeSubmitPlan, TradeSubmitReceipt, TradeWorkflowEnqueueReceipt,
};
use radroots_sql_core::SqlxSqliteExecutor;
use radroots_trade::identity::RadrootsTradeLocator;
use radroots_trade::order::canonicalize_order_request_for_signer;
+use radroots_trade::validation_receipt::{RadrootsValidatorSetV1, validator_set_address_from_str};
use radroots_transport_nostr::{
RadrootsRelayFetchFailure, RadrootsRelayFetchedEventsReceipt, RadrootsRelayTransportError,
RadrootsRelayUrl, RadrootsRelayUrlPolicy,
@@ -68,8 +69,7 @@ use serde_json::{Value, json};
use crate::cli::global::{
OrderDraftCreateArgs, RecordLookupArgs, TradeAppRecordExportArgs, TradeCancelArgs,
- TradeDecisionArg, TradeDecisionArgs, TradeRebindArgs, TradeRevisionDecisionArg,
- TradeRevisionDecisionArgs, TradeRevisionProposeArgs, TradeStatusArgs, TradeSubmitArgs,
+ TradeDecisionArg, TradeDecisionArgs, TradeRebindArgs, TradeStatusArgs, TradeSubmitArgs,
};
use crate::runtime::RuntimeError;
use crate::runtime::account;
@@ -87,8 +87,8 @@ use crate::view::runtime::{
OrderAppRecordExportView, OrderAppRecordListView, OrderAppRecordSummaryView,
OrderCancellationView, OrderDecisionView, OrderDraftItemView, OrderEventListEntryView,
OrderEventListView, OrderGetView, OrderIssueView, OrderListView, OrderNewView, OrderRebindView,
- OrderRevisionDecisionView, OrderRevisionProposalView, OrderStatusView, OrderSubmitView,
- OrderSummaryView, OrderTradeLocatorView, TransportTargetFailureView,
+ OrderStatusView, OrderSubmitView, OrderSummaryView, OrderTradeLocatorView,
+ TransportTargetFailureView,
};
use self::sdk_status::sdk_order_status_view;
@@ -98,8 +98,6 @@ const ORDER_SOURCE: &str = "local trade drafts · local first";
const ORDER_APP_RECORD_SOURCE: &str = "app-authored shared local trade records";
const ORDER_SUBMIT_SOURCE: &str = "SDK trade submit · local key";
const ORDER_DECISION_SOURCE: &str = "SDK trade decision · local key";
-const ORDER_REVISION_PROPOSAL_SOURCE: &str = "SDK trade revision proposal · local key";
-const ORDER_REVISION_DECISION_SOURCE: &str = "SDK trade revision decision · local key";
const ORDER_CANCELLATION_SOURCE: &str = "SDK trade cancellation · local key";
const ORDER_EVENT_LIST_SOURCE: &str = "shared Nostr transport fetch · selected seller identity";
const ORDER_STATUS_SDK_SOURCE: &str = "SDK local trade projection";
@@ -223,13 +221,6 @@ fn protocol_listing_addr(value: &str, field: &str) -> Result<RadrootsListingAddr
})
}
-#[cfg(any())]
-fn protocol_revision_id(value: &str, field: &str) -> Result<RadrootsOrderRevisionId, RuntimeError> {
- value.parse().map_err(|error| {
- RuntimeError::Config(format!("{field} is not a valid trade revision id: {error}"))
- })
-}
-
fn protocol_quote_id(value: &str, field: &str) -> Result<RadrootsOrderQuoteId, RuntimeError> {
value.parse().map_err(|error| {
RuntimeError::Config(format!("{field} is not a valid order quote id: {error}"))
@@ -1232,37 +1223,6 @@ pub fn decide(
decide_trade_via_sdk(config, args, &seller)
}
-pub fn revision_propose(
- config: &RuntimeConfig,
- args: &TradeRevisionProposeArgs,
-) -> Result<OrderRevisionProposalView, CliSdkAdapterError> {
- if let Some(view) = order_revision_args_preflight_view(config, args) {
- return Ok(view);
- }
- let mut view = order_revision_base_view(config, args, "unavailable", config.output.dry_run);
- view.reason = Some(
- "trade revision proposal is not supported by the current SDK trade runtime".to_owned(),
- );
- view.actions = vec![format!("radroots trade status get {}", args.key)];
- Ok(view)
-}
-
-pub fn revision_decide(
- config: &RuntimeConfig,
- args: &TradeRevisionDecisionArgs,
-) -> Result<OrderRevisionDecisionView, CliSdkAdapterError> {
- if let Some(view) = order_revision_decision_args_preflight_view(config, args) {
- return Ok(view);
- }
- let mut view =
- order_revision_decision_base_view(config, args, "unavailable", config.output.dry_run);
- view.reason = Some(
- "trade revision decision is not supported by the current SDK trade runtime".to_owned(),
- );
- view.actions = vec![format!("radroots trade status get {}", args.key)];
- Ok(view)
-}
-
pub fn cancel(
config: &RuntimeConfig,
args: &TradeCancelArgs,
@@ -1284,9 +1244,8 @@ pub fn status(
config: &RuntimeConfig,
args: &TradeStatusArgs,
) -> Result<OrderStatusView, CliSdkAdapterError> {
- let request = TradeStatusRequest::parse(args.key.as_str())?.try_with_trusted_rhi_pubkeys(
- config.rhi.trusted_worker_pubkeys.iter().map(String::as_str),
- )?;
+ let request = TradeStatusRequest::parse(args.key.as_str())?
+ .with_validation_trust_policy(trade_validation_trust_policy(config)?);
let session = CliSdkSession::connect(config)?;
let receipt = session.block_on(session.sdk().trades().status(request))?;
Ok(sdk_order_status_view(receipt))
@@ -1355,114 +1314,6 @@ fn decide_trade_via_sdk(
))
}
-#[cfg(any())]
-fn propose_revision_via_sdk(
- config: &RuntimeConfig,
- args: &TradeRevisionProposeArgs,
- seller: &account::AccountRecordView,
-) -> Result<OrderRevisionProposalView, CliSdkAdapterError> {
- 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(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);
- let ack_policy = trade_satisfaction_policy(publish_mode)?;
- let mut request = TradeRevisionProposalRequest::new(
- actor,
- locator,
- revision.revision_id.clone(),
- revision.items.clone(),
- revision.economics.clone(),
- args.reason.trim(),
- trade_target_policy(),
- publish_mode,
- ack_policy,
- TradeEvidenceMode::ResyncBeforeMutation,
- )
- .with_privacy_confirmation(trade_privacy_confirmation(args.confirm_public_note));
- if let Some(idempotency_key) = args.idempotency_key.as_deref() {
- request = request.try_with_idempotency_key(idempotency_key)?;
- }
- let outcome = session.block_on(session.sdk().trades().seller().propose_revision(request))?;
- Ok(sdk_trade_revision_outcome_view(
- config,
- args,
- &status_view,
- revision,
- outcome,
- ))
-}
-
-#[cfg(any())]
-fn decide_revision_via_sdk(
- config: &RuntimeConfig,
- args: &TradeRevisionDecisionArgs,
- actor_context: OrderBuyerWriteActorContext,
-) -> Result<OrderRevisionDecisionView, CliSdkAdapterError> {
- let account = match actor_context.bound {
- Some(bound) => bound.account,
- None => account::resolve_account(config)?.ok_or_else(|| {
- RuntimeError::Config(
- "trade revision decision requires a selected buyer account".to_owned(),
- )
- })?,
- };
- 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(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 {
- TradeRevisionDecisionArg::Accept => RadrootsOrderRevisionOutcome::Accepted,
- TradeRevisionDecisionArg::Decline => RadrootsOrderRevisionOutcome::Declined {
- reason: args
- .reason
- .as_deref()
- .map(str::trim)
- .filter(|reason| !reason.is_empty())
- .ok_or_else(|| {
- RuntimeError::Config(
- "trade revision decline requires a non-empty reason".to_owned(),
- )
- })?
- .to_owned(),
- },
- };
- let publish_mode = trade_publish_mode(config);
- let ack_policy = trade_satisfaction_policy(publish_mode)?;
- let mut request = TradeRevisionDecisionRequest::new(
- actor,
- locator,
- revision_id,
- decision,
- trade_target_policy(),
- publish_mode,
- ack_policy,
- TradeEvidenceMode::ResyncBeforeMutation,
- )
- .with_privacy_confirmation(trade_privacy_confirmation(args.confirm_public_note));
- if let Some(idempotency_key) = args.idempotency_key.as_deref() {
- request = request.try_with_idempotency_key(idempotency_key)?;
- }
- let outcome = match args.decision {
- TradeRevisionDecisionArg::Accept => {
- session.block_on(session.sdk().trades().buyer().accept_revision(request))?
- }
- TradeRevisionDecisionArg::Decline => {
- session.block_on(session.sdk().trades().buyer().decline_revision(request))?
- }
- };
- Ok(sdk_trade_revision_decision_outcome_view(
- config,
- args,
- &status_view,
- outcome,
- ))
-}
-
fn cancel_trade_via_sdk(
config: &RuntimeConfig,
args: &TradeCancelArgs,
@@ -1510,12 +1361,41 @@ fn trade_status_for_locator(
) -> Result<TradeStatusReceipt, CliSdkAdapterError> {
let request = TradeStatusRequest::new(locator)
.with_source(SdkTradeStatusSource::ResyncThenLocal)
- .try_with_trusted_rhi_pubkeys(
- config.rhi.trusted_worker_pubkeys.iter().map(String::as_str),
- )?;
+ .with_validation_trust_policy(trade_validation_trust_policy(config)?);
Ok(session.block_on(session.sdk().trades().status(request))?)
}
+fn trade_validation_trust_policy(
+ config: &RuntimeConfig,
+) -> Result<RadrootsTradeValidationTrustPolicy, CliSdkAdapterError> {
+ let policy = RadrootsTradeValidationTrustPolicy::production()
+ .with_require_cryptographic_proof(config.rhi.require_cryptographic_proof);
+ let Some(validator_set_config) = config.rhi.validator_set.as_ref() else {
+ return Ok(policy);
+ };
+ let validator_set = RadrootsValidatorSetV1 {
+ set_id: validator_set_config.set_id.clone(),
+ validator_pubkey: RadrootsPublicKey::parse(validator_set_config.validator_pubkey.as_str())
+ .map_err(|error| {
+ RuntimeError::Config(format!("invalid RHI validator pubkey: {error}"))
+ })?,
+ threshold: 1,
+ valid_from: validator_set_config.valid_from,
+ valid_until: validator_set_config.valid_until,
+ protocol_contract_hash: validator_set_config.protocol_contract_hash.clone(),
+ operator_name: validator_set_config.operator_name.clone(),
+ operator_contact: validator_set_config.operator_contact.clone(),
+ };
+ let validator_set_addr =
+ validator_set_address_from_str(validator_set_config.validator_set_addr.as_str())
+ .map_err(|error| RuntimeError::Config(error.to_string()))?;
+ Ok(policy.with_validator_set(
+ validator_set,
+ validator_set_addr,
+ validator_set_config.validator_set_event_id.clone(),
+ ))
+}
+
fn inventory_commitments_from_status(
status: &TradeStatusReceipt,
) -> Result<Vec<RadrootsOrderInventoryCommitment>, RuntimeError> {
@@ -1532,39 +1412,6 @@ fn inventory_commitments_from_status(
.collect())
}
-#[cfg(any())]
-#[derive(Debug, Clone)]
-struct SdkRevisionRequestParts {
- revision_id: RadrootsOrderRevisionId,
- items: Vec<RadrootsOrderItem>,
- economics: RadrootsOrderEconomics,
-}
-
-#[cfg(any())]
-fn revision_request_parts_from_status(
- args: &TradeRevisionProposeArgs,
- status: &OrderStatusView,
-) -> Result<SdkRevisionRequestParts, RuntimeError> {
- let revision_id = protocol_revision_id(next_revision_id().as_str(), "revision_id")?;
- let economics = status.economics.clone().ok_or_else(|| {
- RuntimeError::Config("accepted trade is missing current agreement economics".to_owned())
- })?;
- let economics = revised_order_economics(args, revision_id.as_str(), &economics)?;
- let items = economics
- .items
- .iter()
- .map(|item| RadrootsOrderItem {
- bin_id: item.bin_id.clone(),
- bin_count: item.bin_count,
- })
- .collect::<Vec<_>>();
- Ok(SdkRevisionRequestParts {
- revision_id,
- items,
- economics,
- })
-}
-
fn sdk_trade_decision_outcome_view(
config: &RuntimeConfig,
args: &TradeDecisionArgs,
@@ -1597,85 +1444,6 @@ fn sdk_trade_decision_outcome_view(
}
}
-#[cfg(any())]
-fn sdk_trade_revision_outcome_view(
- config: &RuntimeConfig,
- args: &TradeRevisionProposeArgs,
- status: &OrderStatusView,
- revision: SdkRevisionRequestParts,
- outcome: TradeMutationOutcome<TradeRevisionProposalPlan, TradeRevisionProposalReceipt>,
-) -> OrderRevisionProposalView {
- match outcome {
- TradeMutationOutcome::DryRun { plan } => {
- let mut view = order_revision_base_view(config, args, "dry_run", true);
- apply_order_revision_status(&mut view, status);
- view.revision_id = Some(revision.revision_id.to_string());
- view.root_event_id = Some(plan.root_event_id.to_string());
- view.prev_event_id = Some(plan.previous_event_id.to_string());
- view.event_id = Some(plan.expected_event_id.to_string());
- view.event_kind = Some(KIND_ORDER_REVISION_PROPOSAL);
- view.items = revision
- .items
- .iter()
- .map(|item| OrderDraftItemView {
- bin_id: item.bin_id.to_string(),
- bin_count: item.bin_count,
- })
- .collect();
- view.economics = Some(revision.economics);
- view.target_transport_endpoints = config.transport.nostr_relay_urls.clone();
- view.reason =
- Some("dry run requested; seller revision proposal publication skipped".to_owned());
- view.actions = vec![format!("radroots trade status get {}", status.order_id)];
- view
- }
- TradeMutationOutcome::Enqueued { receipt } => {
- sdk_enqueued_order_revision_view(config, args, status, revision, receipt, None)
- }
- TradeMutationOutcome::Published { receipt, publish } => sdk_enqueued_order_revision_view(
- config,
- args,
- status,
- revision,
- receipt,
- Some(&publish),
- ),
- }
-}
-
-#[cfg(any())]
-fn sdk_trade_revision_decision_outcome_view(
- config: &RuntimeConfig,
- args: &TradeRevisionDecisionArgs,
- status: &OrderStatusView,
- outcome: TradeMutationOutcome<TradeRevisionDecisionPlan, TradeRevisionDecisionReceipt>,
-) -> OrderRevisionDecisionView {
- match outcome {
- TradeMutationOutcome::DryRun { plan } => {
- let mut view = order_revision_decision_base_view(config, args, "dry_run", true);
- apply_order_revision_decision_status(&mut view, status);
- view.revision_id = Some(args.revision_id.trim().to_owned());
- view.root_event_id = Some(plan.root_event_id.to_string());
- view.prev_event_id = Some(plan.previous_event_id.to_string());
- view.event_id = Some(plan.expected_event_id.to_string());
- view.event_kind = Some(KIND_ORDER_REVISION_DECISION);
- view.target_transport_endpoints = config.transport.nostr_relay_urls.clone();
- view.reason = Some(format!(
- "dry run requested; buyer revision {} publication skipped",
- args.decision.command()
- ));
- view.actions = vec![format!("radroots trade status get {}", status.order_id)];
- view
- }
- TradeMutationOutcome::Enqueued { receipt } => {
- sdk_enqueued_order_revision_decision_view(config, args, status, receipt, None)
- }
- TradeMutationOutcome::Published { receipt, publish } => {
- sdk_enqueued_order_revision_decision_view(config, args, status, receipt, Some(&publish))
- }
- }
-}
-
fn sdk_trade_cancellation_outcome_view(
config: &RuntimeConfig,
args: &TradeCancelArgs,
@@ -1930,87 +1698,6 @@ fn order_decision_base_view(
}
}
-fn order_revision_base_view(
- config: &RuntimeConfig,
- args: &TradeRevisionProposeArgs,
- state: &str,
- dry_run: bool,
-) -> OrderRevisionProposalView {
- OrderRevisionProposalView {
- state: state.to_owned(),
- source: ORDER_REVISION_PROPOSAL_SOURCE.to_owned(),
- order_id: args.key.clone(),
- locator: order_locator_view_from_key(args.key.as_str()),
- revision_id: None,
- listing_addr: None,
- buyer_pubkey: None,
- seller_pubkey: None,
- request_event_id: None,
- decision_event_id: None,
- root_event_id: None,
- prev_event_id: None,
- event_id: None,
- event_kind: None,
- items: Vec::new(),
- economics: None,
- inventory: None,
- dry_run,
- target_transport_endpoints: config.transport.nostr_relay_urls.clone(),
- attempted_transport_endpoints: Vec::new(),
- accepted_transport_endpoints: Vec::new(),
- failed_transport_targets: Vec::new(),
- fetched_count: 0,
- decoded_count: 0,
- skipped_count: 0,
- idempotency_key: args.idempotency_key.clone(),
- signer_mode: Some(config.signer.backend.as_str().to_owned()),
- reason: None,
- issues: Vec::new(),
- actions: Vec::new(),
- }
-}
-
-fn order_revision_decision_base_view(
- config: &RuntimeConfig,
- args: &TradeRevisionDecisionArgs,
- state: &str,
- dry_run: bool,
-) -> OrderRevisionDecisionView {
- OrderRevisionDecisionView {
- state: state.to_owned(),
- source: ORDER_REVISION_DECISION_SOURCE.to_owned(),
- order_id: args.key.clone(),
- locator: order_locator_view_from_key(args.key.as_str()),
- revision_id: Some(args.revision_id.trim().to_owned()).filter(|value| !value.is_empty()),
- decision: Some(args.decision.as_str().to_owned()),
- listing_addr: None,
- buyer_pubkey: None,
- seller_pubkey: None,
- request_event_id: None,
- decision_event_id: None,
- agreement_event_id: None,
- root_event_id: None,
- prev_event_id: None,
- event_id: None,
- event_kind: None,
- economics: None,
- inventory: None,
- dry_run,
- target_transport_endpoints: config.transport.nostr_relay_urls.clone(),
- attempted_transport_endpoints: Vec::new(),
- accepted_transport_endpoints: Vec::new(),
- failed_transport_targets: Vec::new(),
- fetched_count: 0,
- decoded_count: 0,
- skipped_count: 0,
- idempotency_key: args.idempotency_key.clone(),
- signer_mode: Some(config.signer.backend.as_str().to_owned()),
- reason: args.reason.as_ref().map(|reason| reason.trim().to_owned()),
- issues: Vec::new(),
- actions: Vec::new(),
- }
-}
-
fn order_cancellation_base_view(
config: &RuntimeConfig,
args: &TradeCancelArgs,
@@ -2098,290 +1785,6 @@ fn apply_order_decision_status(view: &mut OrderDecisionView, status: &OrderStatu
view.inventory = status.inventory.clone();
}
-#[cfg(any())]
-fn apply_order_revision_status(view: &mut OrderRevisionProposalView, status: &OrderStatusView) {
- view.order_id = status.order_id.clone();
- view.locator = order_locator_view_from_status(status);
- view.listing_addr = status.listing_addr.clone();
- view.buyer_pubkey = status.buyer_pubkey.clone();
- view.seller_pubkey = status.seller_pubkey.clone();
- view.request_event_id = status.request_event_id.clone();
- view.decision_event_id = status.decision_event_id.clone();
- view.root_event_id = status.request_event_id.clone();
- view.prev_event_id = status.last_event_id.clone();
- view.economics = status.economics.clone();
- view.inventory = status.inventory.clone();
- view.target_transport_endpoints = status.target_transport_endpoints.clone();
- view.attempted_transport_endpoints = status.attempted_transport_endpoints.clone();
- view.failed_transport_targets = status.failed_transport_targets.clone();
- view.fetched_count = status.fetched_count;
- view.decoded_count = status.decoded_count;
- view.skipped_count = status.skipped_count;
- view.issues = status.reducer_issues.clone();
-}
-
-#[cfg(any())]
-fn apply_order_revision_decision_status(
- view: &mut OrderRevisionDecisionView,
- status: &OrderStatusView,
-) {
- view.order_id = status.order_id.clone();
- view.locator = order_locator_view_from_status(status);
- view.listing_addr = status.listing_addr.clone();
- view.buyer_pubkey = status.buyer_pubkey.clone();
- view.seller_pubkey = status.seller_pubkey.clone();
- view.request_event_id = status.request_event_id.clone();
- view.decision_event_id = status.decision_event_id.clone();
- view.agreement_event_id = status.agreement_event_id.clone();
- view.root_event_id = status.request_event_id.clone();
- view.prev_event_id = status.last_event_id.clone();
- view.economics = status.economics.clone();
- view.inventory = status.inventory.clone();
- view.target_transport_endpoints = status.target_transport_endpoints.clone();
- view.attempted_transport_endpoints = status.attempted_transport_endpoints.clone();
- view.failed_transport_targets = status.failed_transport_targets.clone();
- view.fetched_count = status.fetched_count;
- view.decoded_count = status.decoded_count;
- view.skipped_count = status.skipped_count;
- view.issues = status.reducer_issues.clone();
-}
-
-fn order_revision_args_preflight_view(
- config: &RuntimeConfig,
- args: &TradeRevisionProposeArgs,
-) -> Option<OrderRevisionProposalView> {
- let mut issues = Vec::new();
- let has_bin_id = args.bin_id.as_deref().and_then(non_empty_ref).is_some();
- let has_bin_count = args.bin_count.is_some();
- if has_bin_id != has_bin_count {
- issues.push(issue_with_code(
- "revision_item_change_incomplete",
- "bin_id",
- "`bin_id` and `bin_count` must be supplied together",
- ));
- }
- if args.bin_count == Some(0) {
- issues.push(issue_with_code(
- "revision_bin_count_invalid",
- "bin_count",
- "bin_count must be greater than zero",
- ));
- }
-
- let adjustment_inputs = [
- args.adjustment_id.as_deref(),
- args.adjustment_effect.as_deref(),
- args.adjustment_amount.as_deref(),
- args.adjustment_currency.as_deref(),
- args.adjustment_reason.as_deref(),
- ];
- let adjustment_supplied = adjustment_inputs
- .iter()
- .any(|value| value.and_then(non_empty_ref).is_some());
- let adjustment_complete = adjustment_inputs
- .iter()
- .all(|value| value.and_then(non_empty_ref).is_some());
- if adjustment_supplied && !adjustment_complete {
- issues.push(issue_with_code(
- "revision_adjustment_incomplete",
- "adjustment",
- "all revision adjustment fields must be supplied together",
- ));
- }
-
- if !has_bin_id && !adjustment_supplied {
- issues.push(issue_with_code(
- "revision_no_changes",
- "revision",
- "trade revision propose requires a bin-count change or revision adjustment",
- ));
- }
-
- if issues.is_empty() {
- return None;
- }
- let mut view = order_revision_base_view(config, args, "invalid", config.output.dry_run);
- view.reason = Some(format!(
- "trade revision propose inputs for `{}` failed validation",
- args.key
- ));
- view.issues = issues;
- Some(view)
-}
-
-fn order_revision_decision_args_preflight_view(
- config: &RuntimeConfig,
- args: &TradeRevisionDecisionArgs,
-) -> Option<OrderRevisionDecisionView> {
- let mut issues = Vec::new();
- if args.revision_id.trim().is_empty() {
- issues.push(issue_with_code(
- "revision_id_required",
- "revision_id",
- "trade revision decision requires --revision-id",
- ));
- }
- if args.decision == TradeRevisionDecisionArg::Decline
- && args
- .reason
- .as_deref()
- .map(str::trim)
- .filter(|reason| !reason.is_empty())
- .is_none()
- {
- issues.push(issue_with_code(
- "revision_decline_reason_required",
- "reason",
- "trade revision decline requires a non-empty reason",
- ));
- }
-
- if issues.is_empty() {
- return None;
- }
- let mut view =
- order_revision_decision_base_view(config, args, "invalid", config.output.dry_run);
- view.reason = Some(format!(
- "trade revision {} inputs for `{}` failed validation",
- args.decision.command(),
- args.key
- ));
- view.issues = issues;
- Some(view)
-}
-
-#[cfg(any())]
-fn revised_order_economics(
- args: &TradeRevisionProposeArgs,
- revision_id: &str,
- current: &RadrootsOrderEconomics,
-) -> Result<RadrootsOrderEconomics, RuntimeError> {
- let mut current_canonical = current.clone();
- current_canonical.canonicalize();
- let mut economics = current_canonical.clone();
- let mut changed = false;
- economics.quote_id = protocol_quote_id(format!("revision_{revision_id}").as_str(), "quote_id")?;
- economics.quote_version = economics
- .quote_version
- .checked_add(1)
- .ok_or_else(|| RuntimeError::Config("revision quote_version overflowed".to_owned()))?;
-
- if let Some(bin_id) = args.bin_id.as_deref().and_then(non_empty_ref) {
- let bin_id = protocol_inventory_bin_id(bin_id, "revision bin_id")?;
- let bin_count = args.bin_count.ok_or_else(|| {
- RuntimeError::Config("revision bin_count is required with bin_id".to_owned())
- })?;
- let Some(item) = economics
- .items
- .iter_mut()
- .find(|item| item.bin_id == bin_id)
- else {
- return Err(RuntimeError::Config(format!(
- "revision bin `{bin_id}` is not part of the current agreement"
- )));
- };
- if item.bin_count != bin_count {
- changed = true;
- }
- item.bin_count = bin_count;
- item.line_subtotal = RadrootsCoreMoney::new(
- item.unit_price_amount * item.quantity_amount * RadrootsCoreDecimal::from(bin_count),
- item.unit_price_currency,
- );
- }
-
- if let Some(line) = revision_adjustment_line(args, economics.currency)? {
- changed = true;
- if economics
- .adjustments
- .iter()
- .any(|existing| existing.id == line.id)
- {
- return Err(RuntimeError::Config(format!(
- "revision adjustment id `{}` already exists in current agreement economics",
- line.id
- )));
- }
- economics.adjustments.push(line);
- }
-
- economics.canonicalize();
- economics
- .validate()
- .map_err(|error| RuntimeError::Config(format!("build revision economics: {error}")))?;
- if !changed {
- return Err(RuntimeError::Config(
- "trade revision propose requires a changed item count or adjustment".to_owned(),
- ));
- }
- Ok(economics)
-}
-
-#[cfg(any())]
-fn revision_adjustment_line(
- args: &TradeRevisionProposeArgs,
- expected_currency: RadrootsCoreCurrency,
-) -> Result<Option<RadrootsOrderEconomicLine>, RuntimeError> {
- let Some(id) = args.adjustment_id.as_deref().and_then(non_empty_ref) else {
- return Ok(None);
- };
- let effect = match args
- .adjustment_effect
- .as_deref()
- .and_then(non_empty_ref)
- .ok_or_else(|| RuntimeError::Config("revision adjustment effect is required".to_owned()))?
- {
- "increase" => RadrootsOrderEconomicEffect::Increase,
- "decrease" => RadrootsOrderEconomicEffect::Decrease,
- other => {
- return Err(RuntimeError::Config(format!(
- "revision adjustment effect `{other}` is invalid"
- )));
- }
- };
- let currency = parse_economics_currency(
- args.adjustment_currency
- .as_deref()
- .and_then(non_empty_ref)
- .ok_or_else(|| {
- RuntimeError::Config("revision adjustment currency is required".to_owned())
- })?,
- "revision_adjustment_currency",
- )?;
- if currency != expected_currency {
- return Err(RuntimeError::Config(
- "revision adjustment currency must match current agreement currency".to_owned(),
- ));
- }
- let amount = decimal_from_adjustment(
- args.adjustment_amount
- .as_deref()
- .and_then(non_empty_ref)
- .ok_or_else(|| {
- RuntimeError::Config("revision adjustment amount is required".to_owned())
- })?,
- "revision_adjustment_amount",
- )?;
- if amount.is_zero() {
- return Err(RuntimeError::Config(
- "revision adjustment amount must be greater than zero".to_owned(),
- ));
- }
- let reason = args
- .adjustment_reason
- .as_deref()
- .and_then(non_empty_ref)
- .ok_or_else(|| RuntimeError::Config("revision adjustment reason is required".to_owned()))?;
- Ok(Some(RadrootsOrderEconomicLine {
- id: id.to_owned(),
- kind: RadrootsOrderEconomicLineKind::RevisionAdjustment,
- actor: RadrootsOrderEconomicActor::Seller,
- effect,
- amount: RadrootsCoreMoney::new(amount, currency),
- reason: reason.to_owned(),
- }))
-}
-
fn sdk_enqueued_order_decision_view(
config: &RuntimeConfig,
args: &TradeDecisionArgs,
@@ -2482,103 +1885,6 @@ fn sdk_order_decision_actions(push_event: Option<&PushOutboxEventReceipt>) -> Ve
Vec::new()
}
-#[cfg(any())]
-fn sdk_enqueued_order_revision_view(
- config: &RuntimeConfig,
- args: &TradeRevisionProposeArgs,
- status: &OrderStatusView,
- revision: SdkRevisionRequestParts,
- enqueue: TradeRevisionProposalReceipt,
- push: Option<&PushOutboxReceipt>,
-) -> OrderRevisionProposalView {
- let push_event =
- push.and_then(|push| sdk_push_event_for_event_id(&enqueue.signed_event_id, push));
- let mut view = order_revision_base_view(
- config,
- args,
- sdk_order_lifecycle_state("proposed", push_event).as_str(),
- false,
- );
- apply_order_revision_status(&mut view, status);
- view.locator = order_locator_view_from_locator(&enqueue.locator);
- view.revision_id = Some(revision.revision_id.to_string());
- view.root_event_id = Some(enqueue.root_event_id.to_string());
- view.prev_event_id = Some(enqueue.previous_event_id.to_string());
- view.items = revision
- .items
- .iter()
- .map(|item| OrderDraftItemView {
- bin_id: item.bin_id.to_string(),
- bin_count: item.bin_count,
- })
- .collect();
- view.economics = Some(revision.economics);
- view.event_id = Some(enqueue.signed_event_id.as_str().to_owned());
- view.event_kind = Some(KIND_ORDER_REVISION_PROPOSAL);
- view.target_transport_endpoints = push_event
- .map(sdk_push_target_transport_endpoints)
- .unwrap_or_else(|| config.transport.nostr_relay_urls.clone());
- view.attempted_transport_endpoints = push_event
- .map(sdk_push_attempted_transport_endpoints)
- .unwrap_or_default();
- view.accepted_transport_endpoints = push_event
- .map(sdk_push_accepted_transport_endpoints)
- .unwrap_or_default();
- view.failed_transport_targets = push_event
- .map(sdk_push_failed_transport_targets)
- .unwrap_or_default();
- view.reason =
- sdk_order_lifecycle_reason("trade revision proposal", &enqueue.workflow, push_event);
- view.actions = sdk_order_lifecycle_actions(push_event);
- view
-}
-
-#[cfg(any())]
-fn sdk_enqueued_order_revision_decision_view(
- config: &RuntimeConfig,
- args: &TradeRevisionDecisionArgs,
- status: &OrderStatusView,
- enqueue: TradeRevisionDecisionReceipt,
- push: Option<&PushOutboxReceipt>,
-) -> OrderRevisionDecisionView {
- let push_event =
- push.and_then(|push| sdk_push_event_for_event_id(&enqueue.signed_event_id, push));
- let success_state = args.decision.as_str();
- let mut view = order_revision_decision_base_view(
- config,
- args,
- sdk_order_lifecycle_state(success_state, push_event).as_str(),
- false,
- );
- apply_order_revision_decision_status(&mut view, status);
- view.locator = order_locator_view_from_locator(&enqueue.locator);
- view.revision_id = Some(args.revision_id.trim().to_owned());
- view.root_event_id = Some(enqueue.root_event_id.to_string());
- view.prev_event_id = Some(enqueue.previous_event_id.to_string());
- view.decision = Some(args.decision.as_str().to_owned());
- view.event_id = Some(enqueue.signed_event_id.as_str().to_owned());
- view.event_kind = Some(KIND_ORDER_REVISION_DECISION);
- if args.decision == TradeRevisionDecisionArg::Accept {
- view.agreement_event_id = Some(enqueue.signed_event_id.as_str().to_owned());
- }
- view.target_transport_endpoints = push_event
- .map(sdk_push_target_transport_endpoints)
- .unwrap_or_else(|| config.transport.nostr_relay_urls.clone());
- view.attempted_transport_endpoints = push_event
- .map(sdk_push_attempted_transport_endpoints)
- .unwrap_or_default();
- view.accepted_transport_endpoints = push_event
- .map(sdk_push_accepted_transport_endpoints)
- .unwrap_or_default();
- view.failed_transport_targets = push_event
- .map(sdk_push_failed_transport_targets)
- .unwrap_or_default();
- view.reason =
- sdk_order_lifecycle_reason("trade revision decision", &enqueue.workflow, push_event);
- view.actions = sdk_order_lifecycle_actions(push_event);
- view
-}
-
fn sdk_enqueued_order_cancellation_view(
config: &RuntimeConfig,
args: &TradeCancelArgs,
@@ -5459,19 +4765,6 @@ fn next_order_id() -> String {
)
}
-#[cfg(any())]
-fn next_revision_id() -> String {
- let nanos = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .map(|duration| duration.as_nanos())
- .unwrap_or_default();
- let counter = ORDER_COUNTER.fetch_add(1, Ordering::Relaxed) as u128;
- format!(
- "rev_{}",
- encode_base64url_no_pad((nanos ^ counter).to_be_bytes())
- )
-}
-
fn is_valid_order_id(value: &str) -> bool {
if let Some(encoded) = value.strip_prefix("ord_") {
return encoded.len() == 22 && is_d_tag_base64url(encoded);
diff --git a/src/runtime/order/sdk_status.rs b/src/runtime/order/sdk_status.rs
@@ -52,7 +52,6 @@ pub(super) fn sdk_order_status_view(receipt: TradeStatusReceipt) -> OrderStatusV
seller_pubkey: receipt.seller_pubkey.as_ref().map(ToString::to_string),
economics: receipt.economics.clone(),
last_event_id: sdk_event_id_string(receipt.last_event_id.as_ref()),
- revision: None,
inventory: None,
lifecycle: Some(lifecycle),
sdk_receipt,
@@ -112,7 +111,6 @@ fn sdk_status_evidence_view(
has_decision: evidence.has_decision,
has_agreement: evidence.has_agreement,
has_validation_receipt: evidence.has_validation_receipt,
- has_pending_revision: false,
has_cancellation: evidence.has_cancellation,
has_issues: evidence.has_issues,
}
@@ -123,8 +121,9 @@ fn sdk_status_validation_trust_view(
) -> 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,
+ validator_count: decision.validator_count,
+ validator_set_addr: decision.validator_set_addr.clone(),
+ validator_set_event_id: decision.validator_set_event_id.clone(),
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),
@@ -148,8 +147,6 @@ fn sdk_status_validation_trust_view(
fn sdk_status_eligibility_view(eligibility: &TradeStatusEligibility) -> OrderStatusEligibilityView {
OrderStatusEligibilityView {
can_decide: eligibility.can_decide,
- can_propose_revision: false,
- can_decide_revision: false,
can_cancel: eligibility.can_cancel,
}
}
diff --git a/src/runtime/provider.rs b/src/runtime/provider.rs
@@ -350,7 +350,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: bindings,
}
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -1117,8 +1117,8 @@ mod tests {
end: "fn decide_trade_via_sdk(",
required_tokens: &[
"TradeStatusRequest::parse",
- ".try_with_trusted_rhi_pubkeys(",
- "config.rhi.trusted_worker_pubkeys",
+ ".with_validation_trust_policy(",
+ "trade_validation_trust_policy(config)?",
"session.sdk().trades().status",
],
},
@@ -1134,8 +1134,8 @@ mod tests {
".sdk()",
".trades()",
".with_source(SdkTradeStatusSource::ResyncThenLocal)",
- ".try_with_trusted_rhi_pubkeys(",
- "config.rhi.trusted_worker_pubkeys",
+ ".with_validation_trust_policy(",
+ "trade_validation_trust_policy(config)?",
],
},
MigratedCliPathGuard {
@@ -1200,7 +1200,7 @@ mod tests {
label: "order decision",
path: "src/runtime/order.rs",
start: "fn decide_trade_via_sdk(",
- end: "fn propose_revision_via_sdk(",
+ end: "fn cancel_trade_via_sdk(",
required_tokens: &[
"decide_trade_via_sdk",
"TradeAcceptRequest::new",
@@ -1213,19 +1213,12 @@ mod tests {
MigratedCliPathGuard {
label: "order lifecycle",
path: "src/runtime/order.rs",
- start: "fn propose_revision_via_sdk(",
+ start: "fn cancel_trade_via_sdk(",
end: "fn trade_status_for_locator(",
required_tokens: &[
- "propose_revision_via_sdk",
- "decide_revision_via_sdk",
"cancel_trade_via_sdk",
- "TradeRevisionProposalRequest::new",
- "TradeRevisionDecisionRequest::new",
"TradeCancelRequest::new",
"TradeEvidenceMode::ResyncBeforeMutation",
- "session.sdk().trades().seller().propose_revision",
- "session.sdk().trades().buyer().accept_revision",
- "session.sdk().trades().buyer().decline_revision",
"session.sdk().trades().buyer().cancel_trade",
],
},
@@ -1286,8 +1279,6 @@ mod tests {
"radroots_sdk::protocol::order",
"build_order_request_draft",
"build_order_decision_draft",
- "build_order_revision_proposal_draft",
- "build_order_revision_decision_draft",
"build_order_cancellation_draft",
"parse_order_root_tag",
"parse_order_prev_tag",
@@ -2844,7 +2835,8 @@ struct FixtureConfig;\n\
url: "http://127.0.0.1:7070".to_owned(),
},
rhi: RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/runtime/sync.rs b/src/runtime/sync.rs
@@ -3217,7 +3217,8 @@ mod tests {
url: "http://127.0.0.1:7070".into(),
},
rhi: crate::runtime::config::RhiConfig {
- trusted_worker_pubkeys: Vec::new(),
+ validator_set: None,
+ require_cryptographic_proof: false,
},
capability_bindings: Vec::new(),
}
diff --git a/src/runtime/validation_receipt.rs b/src/runtime/validation_receipt.rs
@@ -142,14 +142,14 @@ pub struct ValidationReceiptProofVerificationView {
pub verifying_key_hash: Option<String>,
pub proof_reference: Option<String>,
pub inline_proof_present: bool,
- pub worker_evidence: Option<ValidationReceiptWorkerEvidenceView>,
- pub untrusted_worker_evidence: Option<ValidationReceiptWorkerEvidenceView>,
+ pub validator_evidence: Option<ValidationReceiptValidatorEvidenceView>,
+ pub untrusted_validator_evidence: Option<ValidationReceiptValidatorEvidenceView>,
pub reason_code: Option<String>,
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
-pub struct ValidationReceiptWorkerEvidenceView {
+pub struct ValidationReceiptValidatorEvidenceView {
pub result_event_id: String,
pub author: String,
pub validation_authority: Option<String>,
@@ -166,9 +166,9 @@ pub struct ValidationReceiptWorkerEvidenceView {
}
#[derive(Clone, Debug, Default)]
-struct ValidationReceiptWorkerEvidenceSelection {
- trusted: Option<ValidationReceiptWorkerEvidenceView>,
- untrusted: Option<ValidationReceiptWorkerEvidenceView>,
+struct ValidationReceiptValidatorEvidenceSelection {
+ trusted: Option<ValidationReceiptValidatorEvidenceView>,
+ untrusted: Option<ValidationReceiptValidatorEvidenceView>,
}
#[derive(Debug, Clone, Serialize)]
@@ -387,7 +387,7 @@ fn inspected_event_view(
let order_id = sdk_receipt.tags.order_id.clone();
let proof_verification = proof_verification_view_for_receipt(
&sdk_receipt.receipt,
- ValidationReceiptWorkerEvidenceSelection::default(),
+ ValidationReceiptValidatorEvidenceSelection::default(),
);
let accepted = match intent {
ValidationReceiptCommandIntent::Inspect => {
@@ -481,7 +481,7 @@ fn list_from_sdk_receipt(
for sdk_event in sdk_receipt.receipts {
let proof_verification = proof_verification_view_for_receipt(
&sdk_event.receipt,
- ValidationReceiptWorkerEvidenceSelection::default(),
+ ValidationReceiptValidatorEvidenceSelection::default(),
);
if proof_state_is_invalid(proof_verification.state.as_str()) {
invalid_receipts.push(ValidationReceiptInvalidCandidateView {
@@ -741,14 +741,14 @@ struct ValidationReceiptTrustSummary {
fn local_only_trust_summary() -> ValidationReceiptTrustSummary {
ValidationReceiptTrustSummary {
- state: "local_only_deterministic_receipt",
+ state: "validator_set_deterministic_receipt",
validation_authority: Some(
- RadrootsTradeValidationAuthority::DevDeterministicOnly
+ RadrootsTradeValidationAuthority::ValidatorSetDeterministic
.as_str()
.to_owned(),
),
commitment_confidence: Some(
- RadrootsTradeCommitmentConfidence::LocalOnly
+ RadrootsTradeCommitmentConfidence::CommittedByValidatorSet
.as_str()
.to_owned(),
),
@@ -766,38 +766,38 @@ fn sp1_execute_checked_trust_summary() -> ValidationReceiptTrustSummary {
production_verification: false,
reason_code: Some("validation_receipt_trust_metadata_missing"),
reason: Some(
- "trusted worker evidence reports SP1 execution but omits validation authority or commitment confidence",
+ "validator evidence reports SP1 execution but omits validation authority or commitment confidence",
),
}
}
fn missing_trust_metadata_summary() -> ValidationReceiptTrustSummary {
ValidationReceiptTrustSummary {
- state: "worker_evidence_trust_metadata_missing",
+ state: "validator_evidence_trust_metadata_missing",
validation_authority: None,
commitment_confidence: None,
production_verification: false,
reason_code: Some("validation_receipt_trust_metadata_missing"),
- reason: Some("trusted worker evidence omits validation authority or commitment confidence"),
+ reason: Some("validator evidence omits validation authority or commitment confidence"),
}
}
fn mismatched_trust_metadata_summary() -> ValidationReceiptTrustSummary {
ValidationReceiptTrustSummary {
- state: "worker_evidence_trust_metadata_mismatch",
+ state: "validator_evidence_trust_metadata_mismatch",
validation_authority: None,
commitment_confidence: None,
production_verification: false,
reason_code: Some("validation_receipt_trust_metadata_mismatch"),
reason: Some(
- "trusted worker evidence validation authority does not match commitment confidence",
+ "validator evidence validation authority does not match commitment confidence",
),
}
}
-fn invalid_worker_evidence_summary() -> ValidationReceiptTrustSummary {
+fn invalid_validator_evidence_summary() -> ValidationReceiptTrustSummary {
ValidationReceiptTrustSummary {
- state: "validation_receipt_worker_evidence_invalid",
+ state: "validation_receipt_validator_evidence_invalid",
validation_authority: None,
commitment_confidence: Some(
RadrootsTradeCommitmentConfidence::Invalid
@@ -805,12 +805,12 @@ fn invalid_worker_evidence_summary() -> ValidationReceiptTrustSummary {
.to_owned(),
),
production_verification: false,
- reason_code: Some("validation_receipt_worker_evidence_invalid"),
- reason: Some("trusted worker evidence marks the validation receipt invalid"),
+ reason_code: Some("validation_receipt_validator_evidence_invalid"),
+ reason: Some("validator evidence marks the validation receipt invalid"),
}
}
-fn trusted_worker_summary(
+fn trusted_validator_summary(
state: &'static str,
authority: &str,
confidence: &str,
@@ -827,9 +827,9 @@ fn trusted_worker_summary(
}
fn none_proof_trust_summary(
- worker_evidence: &ValidationReceiptWorkerEvidenceSelection,
+ validator_evidence: &ValidationReceiptValidatorEvidenceSelection,
) -> ValidationReceiptTrustSummary {
- let Some(evidence) = worker_evidence.trusted.as_ref() else {
+ let Some(evidence) = validator_evidence.trusted.as_ref() else {
return local_only_trust_summary();
};
let authority = evidence.validation_authority.as_deref();
@@ -845,45 +845,43 @@ fn none_proof_trust_summary(
return missing_trust_metadata_summary();
};
match (authority, confidence) {
- ("dev_deterministic_only", "local_only") => local_only_trust_summary(),
- ("trusted_rhi_service_key", "pending_rhi") => {
- trusted_worker_summary("pending_rhi_validation", authority, confidence, false)
+ ("validator_set_deterministic", "awaiting_validation") => {
+ trusted_validator_summary("pending_validator_validation", authority, confidence, false)
}
- ("trusted_rhi_service_key", "committed_by_trusted_service") => {
- trusted_worker_summary("trusted_service_validated", authority, confidence, false)
+ ("validator_set_deterministic", "committed_by_validator_set") => {
+ trusted_validator_summary("validator_set_validated", authority, confidence, false)
}
("cryptographic_proof_verified", "committed_by_cryptographic_proof") => {
- trusted_worker_summary("cryptographic_proof_verified", authority, confidence, true)
+ trusted_validator_summary("cryptographic_proof_verified", authority, confidence, true)
}
- ("trusted_service_and_proof_verified", "committed_by_trusted_service_and_proof") => {
- trusted_worker_summary(
- "trusted_service_and_proof_verified",
+ ("validator_set_and_proof_verified", "committed_by_validator_set_and_proof") => {
+ trusted_validator_summary(
+ "validator_set_and_proof_verified",
authority,
confidence,
true,
)
}
- (_, "invalid") => invalid_worker_evidence_summary(),
+ (_, "invalid") => invalid_validator_evidence_summary(),
_ => mismatched_trust_metadata_summary(),
}
}
fn verified_sp1_trust_summary(
- worker_evidence: &ValidationReceiptWorkerEvidenceSelection,
+ validator_evidence: &ValidationReceiptValidatorEvidenceSelection,
) -> ValidationReceiptTrustSummary {
- if let Some(evidence) = worker_evidence.trusted.as_ref()
- && evidence.validation_authority.as_deref() == Some("trusted_service_and_proof_verified")
- && evidence.commitment_confidence.as_deref()
- == Some("committed_by_trusted_service_and_proof")
+ if let Some(evidence) = validator_evidence.trusted.as_ref()
+ && evidence.validation_authority.as_deref() == Some("validator_set_and_proof_verified")
+ && evidence.commitment_confidence.as_deref() == Some("committed_by_validator_set_and_proof")
{
- return trusted_worker_summary(
- "trusted_service_and_proof_verified",
- "trusted_service_and_proof_verified",
- "committed_by_trusted_service_and_proof",
+ return trusted_validator_summary(
+ "validator_set_and_proof_verified",
+ "validator_set_and_proof_verified",
+ "committed_by_validator_set_and_proof",
true,
);
}
- trusted_worker_summary(
+ trusted_validator_summary(
"sp1_inline_proof_verified",
"cryptographic_proof_verified",
"committed_by_cryptographic_proof",
@@ -893,12 +891,12 @@ fn verified_sp1_trust_summary(
fn proof_verification_view_for_receipt(
receipt: &RadrootsTradeValidationReceipt,
- worker_evidence: ValidationReceiptWorkerEvidenceSelection,
+ validator_evidence: ValidationReceiptValidatorEvidenceSelection,
) -> ValidationReceiptProofVerificationView {
let proof = &receipt.proof;
let cryptographic_proof_required = proof.system != RadrootsValidationReceiptProofSystem::None;
if proof.system == RadrootsValidationReceiptProofSystem::None {
- let trust = none_proof_trust_summary(&worker_evidence);
+ let trust = none_proof_trust_summary(&validator_evidence);
return ValidationReceiptProofVerificationView {
state: trust.state.to_owned(),
verifier: "radroots_cli_validation_receipt_v1".to_owned(),
@@ -915,8 +913,8 @@ fn proof_verification_view_for_receipt(
verifying_key_hash: proof.verifying_key_hash.clone(),
proof_reference: proof.proof_reference.clone(),
inline_proof_present: proof.inline_proof_base64.is_some(),
- worker_evidence: worker_evidence.trusted,
- untrusted_worker_evidence: worker_evidence.untrusted,
+ validator_evidence: validator_evidence.trusted,
+ untrusted_validator_evidence: validator_evidence.untrusted,
reason_code: trust.reason_code.map(str::to_owned),
reason: trust.reason.map(str::to_owned),
};
@@ -924,7 +922,7 @@ fn proof_verification_view_for_receipt(
if proof.proof_reference.is_some() {
return sp1_unverified_proof_view(
receipt,
- worker_evidence,
+ validator_evidence,
"sp1_reference_unresolved",
"unverified",
"reference_unresolved",
@@ -935,7 +933,7 @@ fn proof_verification_view_for_receipt(
if proof.inline_proof_base64.is_none() {
return sp1_unverified_proof_view(
receipt,
- worker_evidence,
+ validator_evidence,
"sp1_proof_material_missing",
"unverified",
"missing_proof_material",
@@ -946,7 +944,7 @@ fn proof_verification_view_for_receipt(
if proof.system != RadrootsValidationReceiptProofSystem::Sp1Core {
return sp1_unverified_proof_view(
receipt,
- worker_evidence,
+ validator_evidence,
"sp1_metadata_consistent_unverified",
"unverified",
"metadata_consistent_unverified",
@@ -957,7 +955,7 @@ fn proof_verification_view_for_receipt(
match verify_inline_sp1_receipt(receipt) {
Ok(()) => {
- let trust = verified_sp1_trust_summary(&worker_evidence);
+ let trust = verified_sp1_trust_summary(&validator_evidence);
ValidationReceiptProofVerificationView {
state: trust.state.to_owned(),
verifier: "radroots_cli_validation_receipt_v1".to_owned(),
@@ -974,8 +972,8 @@ fn proof_verification_view_for_receipt(
verifying_key_hash: proof.verifying_key_hash.clone(),
proof_reference: proof.proof_reference.clone(),
inline_proof_present: proof.inline_proof_base64.is_some(),
- worker_evidence: worker_evidence.trusted,
- untrusted_worker_evidence: worker_evidence.untrusted,
+ validator_evidence: validator_evidence.trusted,
+ untrusted_validator_evidence: validator_evidence.untrusted,
reason_code: trust.reason_code.map(str::to_owned),
reason: trust.reason.map(str::to_owned),
}
@@ -985,7 +983,7 @@ fn proof_verification_view_for_receipt(
let reason = error.to_string();
sp1_unverified_proof_view(
receipt,
- worker_evidence,
+ validator_evidence,
mapped.state,
mapped.public_values_hash_binding,
mapped.proof_metadata_binding,
@@ -998,7 +996,7 @@ fn proof_verification_view_for_receipt(
fn sp1_unverified_proof_view(
receipt: &RadrootsTradeValidationReceipt,
- worker_evidence: ValidationReceiptWorkerEvidenceSelection,
+ validator_evidence: ValidationReceiptValidatorEvidenceSelection,
state: &str,
public_values_hash_binding: &str,
proof_metadata_binding: &str,
@@ -1022,8 +1020,8 @@ fn sp1_unverified_proof_view(
verifying_key_hash: proof.verifying_key_hash.clone(),
proof_reference: proof.proof_reference.clone(),
inline_proof_present: proof.inline_proof_base64.is_some(),
- worker_evidence: worker_evidence.trusted,
- untrusted_worker_evidence: worker_evidence.untrusted,
+ validator_evidence: validator_evidence.trusted,
+ untrusted_validator_evidence: validator_evidence.untrusted,
reason_code: reason_code.map(str::to_owned),
reason: reason.map(str::to_owned),
}
@@ -1158,17 +1156,17 @@ fn proof_state_is_invalid(state: &str) -> bool {
| "sp1_program_hash_mismatch"
| "sp1_verifying_key_hash_mismatch"
| "sp1_proof_invalid"
- | "validation_receipt_worker_evidence_invalid"
- | "worker_evidence_trust_metadata_mismatch"
+ | "validation_receipt_validator_evidence_invalid"
+ | "validator_evidence_trust_metadata_mismatch"
)
}
fn proof_state_is_verification_success(state: &str) -> bool {
matches!(
state,
- "trusted_service_validated"
+ "validator_set_validated"
| "cryptographic_proof_verified"
- | "trusted_service_and_proof_verified"
+ | "validator_set_and_proof_verified"
| "sp1_inline_proof_verified"
)
}
@@ -1256,7 +1254,7 @@ fn receipt_result_label(value: RadrootsValidationReceiptResult) -> &'static str
#[cfg(test)]
mod tests {
use super::{
- ValidationReceiptWorkerEvidenceSelection, ValidationReceiptWorkerEvidenceView,
+ ValidationReceiptValidatorEvidenceSelection, ValidationReceiptValidatorEvidenceView,
proof_state_from_sp1_error, proof_state_is_invalid, proof_state_is_verification_success,
proof_verification_view_for_receipt, validation_receipt_invalid_reason_code,
};
@@ -1324,12 +1322,12 @@ mod tests {
})
}
- fn worker_evidence(
+ fn validator_evidence(
validation_authority: Option<&str>,
commitment_confidence: Option<&str>,
sp1_execute_checked: bool,
- ) -> ValidationReceiptWorkerEvidenceView {
- ValidationReceiptWorkerEvidenceView {
+ ) -> ValidationReceiptValidatorEvidenceView {
+ ValidationReceiptValidatorEvidenceView {
result_event_id: "result-1".to_owned(),
author: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_owned(),
validation_authority: validation_authority.map(str::to_owned),
@@ -1353,15 +1351,18 @@ mod tests {
fn none_receipts_report_local_only_without_crypto_claim() {
let view = proof_verification_view_for_receipt(
&deterministic_receipt(),
- ValidationReceiptWorkerEvidenceSelection::default(),
+ ValidationReceiptValidatorEvidenceSelection::default(),
);
- assert_eq!(view.state, "local_only_deterministic_receipt");
+ assert_eq!(view.state, "validator_set_deterministic_receipt");
assert_eq!(
view.validation_authority.as_deref(),
- Some("dev_deterministic_only")
+ Some("validator_set_deterministic")
+ );
+ assert_eq!(
+ view.commitment_confidence.as_deref(),
+ Some("committed_by_validator_set")
);
- assert_eq!(view.commitment_confidence.as_deref(), Some("local_only"));
assert!(!view.production_verification);
assert!(!view.cryptographic_proof_required);
assert!(!view.cryptographic_proof_verified);
@@ -1371,8 +1372,8 @@ mod tests {
fn none_receipts_surface_advisory_sp1_execute_evidence() {
let view = proof_verification_view_for_receipt(
&deterministic_receipt(),
- ValidationReceiptWorkerEvidenceSelection {
- trusted: Some(worker_evidence(None, None, true)),
+ ValidationReceiptValidatorEvidenceSelection {
+ trusted: Some(validator_evidence(None, None, true)),
untrusted: None,
},
);
@@ -1388,39 +1389,39 @@ mod tests {
}
#[test]
- fn none_receipts_surface_trusted_service_confidence_without_production_verification() {
+ fn none_receipts_surface_validator_set_confidence_without_production_verification() {
let view = proof_verification_view_for_receipt(
&deterministic_receipt(),
- ValidationReceiptWorkerEvidenceSelection {
- trusted: Some(worker_evidence(
- Some("trusted_rhi_service_key"),
- Some("committed_by_trusted_service"),
+ ValidationReceiptValidatorEvidenceSelection {
+ trusted: Some(validator_evidence(
+ Some("validator_set_deterministic"),
+ Some("committed_by_validator_set"),
false,
)),
untrusted: None,
},
);
- assert_eq!(view.state, "trusted_service_validated");
+ assert_eq!(view.state, "validator_set_validated");
assert_eq!(
view.validation_authority.as_deref(),
- Some("trusted_rhi_service_key")
+ Some("validator_set_deterministic")
);
assert_eq!(
view.commitment_confidence.as_deref(),
- Some("committed_by_trusted_service")
+ Some("committed_by_validator_set")
);
assert!(!view.production_verification);
assert!(proof_state_is_verification_success(view.state.as_str()));
}
#[test]
- fn invalid_worker_evidence_marks_receipt_invalid() {
+ fn invalid_validator_evidence_marks_receipt_invalid() {
let view = proof_verification_view_for_receipt(
&deterministic_receipt(),
- ValidationReceiptWorkerEvidenceSelection {
- trusted: Some(worker_evidence(
- Some("trusted_rhi_service_key"),
+ ValidationReceiptValidatorEvidenceSelection {
+ trusted: Some(validator_evidence(
+ Some("validator_set_deterministic"),
Some("invalid"),
false,
)),
@@ -1428,45 +1429,45 @@ mod tests {
},
);
- assert_eq!(view.state, "validation_receipt_worker_evidence_invalid");
+ assert_eq!(view.state, "validation_receipt_validator_evidence_invalid");
assert!(proof_state_is_invalid(view.state.as_str()));
assert!(!view.production_verification);
}
#[test]
- fn untrusted_worker_evidence_does_not_upgrade_deterministic_receipts() {
+ fn untrusted_validator_evidence_does_not_upgrade_deterministic_receipts() {
let view = proof_verification_view_for_receipt(
&deterministic_receipt(),
- ValidationReceiptWorkerEvidenceSelection {
+ ValidationReceiptValidatorEvidenceSelection {
trusted: None,
- untrusted: Some(worker_evidence(
- Some("trusted_rhi_service_key"),
- Some("committed_by_trusted_service"),
+ untrusted: Some(validator_evidence(
+ Some("validator_set_deterministic"),
+ Some("committed_by_validator_set"),
true,
)),
},
);
- assert_eq!(view.state, "local_only_deterministic_receipt");
+ assert_eq!(view.state, "validator_set_deterministic_receipt");
assert!(!view.production_verification);
- assert!(view.worker_evidence.is_none());
- assert!(view.untrusted_worker_evidence.is_some());
+ assert!(view.validator_evidence.is_none());
+ assert!(view.untrusted_validator_evidence.is_some());
}
#[test]
fn validation_success_labels_exclude_local_only_and_sp1_execute_checked() {
assert!(!proof_state_is_verification_success(
- "local_only_deterministic_receipt"
+ "validator_set_deterministic_receipt"
));
assert!(!proof_state_is_verification_success("sp1_execute_checked"));
assert!(proof_state_is_verification_success(
- "trusted_service_validated"
+ "validator_set_validated"
));
assert!(proof_state_is_verification_success(
"sp1_inline_proof_verified"
));
assert!(proof_state_is_verification_success(
- "trusted_service_and_proof_verified"
+ "validator_set_and_proof_verified"
));
}
@@ -1478,7 +1479,7 @@ mod tests {
let view = proof_verification_view_for_receipt(
&receipt,
- ValidationReceiptWorkerEvidenceSelection::default(),
+ ValidationReceiptValidatorEvidenceSelection::default(),
);
assert_eq!(view.state, "sp1_reference_unresolved");
@@ -1492,7 +1493,7 @@ mod tests {
fn invalid_inline_sp1_material_reports_invalid_proof_state() {
let view = proof_verification_view_for_receipt(
&receipt_with_proof(sp1_proof_with_material()),
- ValidationReceiptWorkerEvidenceSelection::default(),
+ ValidationReceiptValidatorEvidenceSelection::default(),
);
assert_eq!(view.state, "sp1_proof_invalid");
@@ -1506,7 +1507,7 @@ mod tests {
fn inline_sp1_material_reports_unavailable_verifier_without_sp1_verify_feature() {
let view = proof_verification_view_for_receipt(
&receipt_with_proof(sp1_proof_with_material()),
- ValidationReceiptWorkerEvidenceSelection::default(),
+ ValidationReceiptValidatorEvidenceSelection::default(),
);
assert_eq!(view.state, "sp1_verifier_unavailable");
diff --git a/src/view/runtime.rs b/src/view/runtime.rs
@@ -1981,159 +1981,6 @@ impl OrderCancellationView {
}
#[derive(Debug, Clone, Serialize)]
-pub struct OrderRevisionProposalView {
- pub state: String,
- pub source: String,
- #[serde(rename = "trade_id")]
- pub order_id: String,
- pub locator: OrderTradeLocatorView,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub revision_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub listing_addr: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub buyer_pubkey: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub seller_pubkey: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub request_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub decision_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub root_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub prev_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub event_kind: Option<u32>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub items: Vec<OrderDraftItemView>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub economics: Option<RadrootsOrderEconomics>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub inventory: Option<OrderInventoryView>,
- #[serde(default)]
- pub dry_run: bool,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub target_transport_endpoints: Vec<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub attempted_transport_endpoints: Vec<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub accepted_transport_endpoints: Vec<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub failed_transport_targets: Vec<TransportTargetFailureView>,
- #[serde(default)]
- pub fetched_count: usize,
- #[serde(default)]
- pub decoded_count: usize,
- #[serde(default)]
- pub skipped_count: usize,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub idempotency_key: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub signer_mode: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub reason: Option<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub issues: Vec<OrderIssueView>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub actions: Vec<String>,
-}
-
-impl OrderRevisionProposalView {
- pub fn disposition(&self) -> CommandDisposition {
- match self.state.as_str() {
- "missing" => CommandDisposition::NotFound,
- "invalid" | "requested" | "declined" | "order_declined" | "fulfilled" | "terminal"
- | "forked" => CommandDisposition::ValidationFailed,
- "unconfigured" => CommandDisposition::Unconfigured,
- "unavailable" => CommandDisposition::ExternalUnavailable,
- "error" => CommandDisposition::InternalError,
- _ => CommandDisposition::Success,
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize)]
-pub struct OrderRevisionDecisionView {
- pub state: String,
- pub source: String,
- #[serde(rename = "trade_id")]
- pub order_id: String,
- pub locator: OrderTradeLocatorView,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub revision_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub decision: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub listing_addr: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub buyer_pubkey: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub seller_pubkey: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub request_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub decision_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub agreement_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub root_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub prev_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub event_kind: Option<u32>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub economics: Option<RadrootsOrderEconomics>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub inventory: Option<OrderInventoryView>,
- #[serde(default)]
- pub dry_run: bool,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub target_transport_endpoints: Vec<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub attempted_transport_endpoints: Vec<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub accepted_transport_endpoints: Vec<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub failed_transport_targets: Vec<TransportTargetFailureView>,
- #[serde(default)]
- pub fetched_count: usize,
- #[serde(default)]
- pub decoded_count: usize,
- #[serde(default)]
- pub skipped_count: usize,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub idempotency_key: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub signer_mode: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub reason: Option<String>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub issues: Vec<OrderIssueView>,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub actions: Vec<String>,
-}
-
-impl OrderRevisionDecisionView {
- pub fn disposition(&self) -> CommandDisposition {
- match self.state.as_str() {
- "missing" => CommandDisposition::NotFound,
- "invalid" | "requested" | "order_declined" | "fulfilled" | "terminal" | "forked" => {
- CommandDisposition::ValidationFailed
- }
- "unconfigured" => CommandDisposition::Unconfigured,
- "unavailable" => CommandDisposition::ExternalUnavailable,
- "error" => CommandDisposition::InternalError,
- _ => CommandDisposition::Success,
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize)]
pub struct OrderStatusView {
pub state: String,
pub source: String,
@@ -2160,8 +2007,6 @@ pub struct OrderStatusView {
#[serde(skip_serializing_if = "Option::is_none")]
pub last_event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
- pub revision: Option<OrderStatusRevisionView>,
- #[serde(skip_serializing_if = "Option::is_none")]
pub inventory: Option<OrderInventoryView>,
pub lifecycle: Option<OrderStatusLifecycleView>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -2200,8 +2045,11 @@ pub struct OrderStatusSdkReceiptView {
#[derive(Debug, Clone, Serialize)]
pub struct OrderStatusValidationTrustView {
pub state: String,
- pub trusted_rhi_pubkey_count: usize,
- pub allow_deterministic_none: bool,
+ pub validator_count: usize,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub validator_set_addr: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub validator_set_event_id: Option<String>,
pub require_cryptographic_proof: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub receipt_event_id: Option<String>,
@@ -2234,7 +2082,6 @@ pub struct OrderStatusEvidenceSummaryView {
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,
}
@@ -2242,31 +2089,10 @@ pub struct OrderStatusEvidenceSummaryView {
#[derive(Debug, Clone, Serialize)]
pub struct OrderStatusEligibilityView {
pub can_decide: bool,
- pub can_propose_revision: bool,
- pub can_decide_revision: bool,
pub can_cancel: bool,
}
#[derive(Debug, Clone, Serialize)]
-pub struct OrderStatusRevisionView {
- pub state: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub revision_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub proposal_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub decision_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub root_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub prev_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub agreement_event_id: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub reason: Option<String>,
-}
-
-#[derive(Debug, Clone, Serialize)]
pub struct OrderStatusLifecycleView {
pub phase: String,
#[serde(default)]