Skip to main content

daml_grpc/data/
commands.rs

1use std::convert::TryFrom;
2use std::time::Duration;
3
4use chrono::{DateTime, Utc};
5
6use crate::data::command::DamlCommand;
7use crate::data::identifier::DamlIdentifier;
8use crate::data::value::DamlValue;
9use crate::data::{DamlError, DamlResult};
10use crate::grpc_protobuf::com::daml::ledger::api::v2::commands::DeduplicationPeriod;
11use crate::grpc_protobuf::com::daml::ledger::api::v2::{Command, Commands, DisclosedContract, PrefetchContractKey};
12use crate::util;
13
14/// A composite Daml command: a set of [`DamlCommand`]s that the participant
15/// processes atomically (succeed-all-or-fail-all). Plus metadata about
16/// who submitted them, dedup period, and disclosed-contract overrides.
17#[derive(Debug, Eq, PartialEq, Clone)]
18pub struct DamlCommands {
19    /// On-ledger workflow identifier. Optional in v2 — leave empty when
20    /// not tracking workflows.
21    pub workflow_id: String,
22    /// The participant user that issued this submission. Required unless
23    /// the request is already authenticated with a user token (in which
24    /// case the token's `user_id` takes precedence and this field is
25    /// ignored). v2 renames v1's `application_id`.
26    pub user_id: String,
27    /// Uniquely identifies this command (together with `user_id` and
28    /// `act_as`). Required.
29    pub command_id: String,
30    /// Disambiguates retries of the same change-id. Typically a fresh
31    /// UUID per submission attempt. Optional — the participant fills in a
32    /// value if you leave it empty.
33    pub submission_id: String,
34    /// Set of parties this submission acts on behalf of. Must be
35    /// non-empty.
36    pub act_as: Vec<String>,
37    /// Additional parties whose contracts can be read but not signed for.
38    pub read_as: Vec<String>,
39    /// The atomic command list itself. Must be non-empty.
40    pub commands: Vec<DamlCommand>,
41    /// Deduplication period, by duration or completion-stream offset.
42    /// Defaults to the participant's configured maximum if unset.
43    pub deduplication_period: Option<DamlCommandsDeduplicationPeriod>,
44    /// Lower bound on the ledger time of the resulting transaction.
45    pub min_ledger_time: Option<DamlMinLedgerTime>,
46    /// Disclosed contracts allow the submitter to authoritatively present
47    /// contracts the participant might not otherwise know about, e.g.
48    /// from upstream events on other synchronizers.
49    pub disclosed_contracts: Vec<DamlDisclosedContract>,
50    /// Target synchronizer id. Leave empty to let the participant route.
51    pub synchronizer_id: String,
52    /// Package-name -> package-id pinning hints for command interpretation.
53    pub package_id_selection_preference: Vec<String>,
54    /// Keys to prefetch into participant caches before interpretation.
55    pub prefetch_contract_keys: Vec<DamlPrefetchContractKey>,
56    /// Caps the number of topology-aware package selection passes the
57    /// participant performs. `None` defers to participant config.
58    pub taps_max_passes: Option<u32>,
59}
60
61impl DamlCommands {
62    /// Minimal constructor for the common case: a set of commands acting
63    /// as a single party with no extra hints. All other fields default
64    /// to empty / unset; use struct update syntax to refine.
65    pub fn new(
66        user_id: impl Into<String>,
67        command_id: impl Into<String>,
68        act_as: impl Into<Vec<String>>,
69        commands: impl Into<Vec<DamlCommand>>,
70    ) -> Self {
71        Self {
72            workflow_id: String::new(),
73            user_id: user_id.into(),
74            command_id: command_id.into(),
75            submission_id: String::new(),
76            act_as: act_as.into(),
77            read_as: Vec::new(),
78            commands: commands.into(),
79            deduplication_period: None,
80            min_ledger_time: None,
81            disclosed_contracts: Vec::new(),
82            synchronizer_id: String::new(),
83            package_id_selection_preference: Vec::new(),
84            prefetch_contract_keys: Vec::new(),
85            taps_max_passes: None,
86        }
87    }
88}
89
90impl TryFrom<DamlCommands> for Commands {
91    type Error = DamlError;
92
93    fn try_from(d: DamlCommands) -> DamlResult<Commands> {
94        Ok(Commands {
95            workflow_id: d.workflow_id,
96            user_id: d.user_id,
97            command_id: d.command_id,
98            submission_id: d.submission_id,
99            act_as: d.act_as,
100            read_as: d.read_as,
101            commands: d.commands.into_iter().map(Command::from).collect(),
102            min_ledger_time_abs: match &d.min_ledger_time {
103                Some(DamlMinLedgerTime::Absolute(ts)) => Some(util::to_grpc_timestamp(*ts)?),
104                _ => None,
105            },
106            min_ledger_time_rel: match &d.min_ledger_time {
107                Some(DamlMinLedgerTime::Relative(dur)) => Some(util::to_grpc_duration(dur)?),
108                _ => None,
109            },
110            deduplication_period: d.deduplication_period.map(DeduplicationPeriod::try_from).transpose()?,
111            disclosed_contracts: d.disclosed_contracts.into_iter().map(DisclosedContract::from).collect(),
112            synchronizer_id: d.synchronizer_id,
113            package_id_selection_preference: d.package_id_selection_preference,
114            prefetch_contract_keys: d
115                .prefetch_contract_keys
116                .into_iter()
117                .map(PrefetchContractKey::try_from)
118                .collect::<DamlResult<Vec<_>>>()?,
119            taps_max_passes: d.taps_max_passes,
120        })
121    }
122}
123
124#[derive(Debug, Eq, PartialEq, Clone)]
125pub enum DamlMinLedgerTime {
126    Absolute(DateTime<Utc>),
127    Relative(Duration),
128}
129
130#[derive(Debug, Eq, PartialEq, Clone)]
131pub enum DamlCommandsDeduplicationPeriod {
132    /// Offset on the completion stream, exclusive. v2 makes this a real
133    /// integer offset rather than v1's stringified form.
134    DeduplicationOffset(i64),
135    DeduplicationDuration(Duration),
136}
137
138impl TryFrom<DamlCommandsDeduplicationPeriod> for DeduplicationPeriod {
139    type Error = DamlError;
140
141    fn try_from(period: DamlCommandsDeduplicationPeriod) -> DamlResult<Self> {
142        Ok(match period {
143            DamlCommandsDeduplicationPeriod::DeduplicationOffset(offset) => {
144                DeduplicationPeriod::DeduplicationOffset(offset)
145            },
146            DamlCommandsDeduplicationPeriod::DeduplicationDuration(dur) => {
147                DeduplicationPeriod::DeduplicationDuration(util::to_grpc_duration(&dur)?)
148            },
149        })
150    }
151}
152
153/// An out-of-band contract the submitter is asserting exists on the
154/// network, presented to the participant alongside a command so it can
155/// be referenced even when the participant hasn't otherwise observed it.
156///
157/// `created_event_blob` is the authoritative payload — the optional
158/// `template_id` and `contract_id` are validated against the blob when
159/// supplied.
160#[derive(Debug, Eq, PartialEq, Clone, Default)]
161pub struct DamlDisclosedContract {
162    pub template_id: Option<DamlIdentifier>,
163    pub contract_id: String,
164    pub created_event_blob: Vec<u8>,
165    pub synchronizer_id: String,
166}
167
168impl From<DamlDisclosedContract> for DisclosedContract {
169    fn from(d: DamlDisclosedContract) -> Self {
170        Self {
171            template_id: d.template_id.map(Into::into),
172            contract_id: d.contract_id,
173            created_event_blob: d.created_event_blob,
174            synchronizer_id: d.synchronizer_id,
175        }
176    }
177}
178
179/// Hints to the participant that it should warm its caches with contracts
180/// indexed by `(template_id, contract_key)` before interpreting the
181/// commands. `limit = None` means "fetch one"; `Some(0)` is forbidden by
182/// the protocol.
183#[derive(Debug, Eq, PartialEq, Clone)]
184pub struct DamlPrefetchContractKey {
185    pub template_id: DamlIdentifier,
186    pub contract_key: DamlValue,
187    pub limit: Option<u32>,
188}
189
190impl TryFrom<DamlPrefetchContractKey> for PrefetchContractKey {
191    type Error = DamlError;
192
193    fn try_from(p: DamlPrefetchContractKey) -> DamlResult<Self> {
194        Ok(Self {
195            template_id: Some(p.template_id.into()),
196            contract_key: Some(p.contract_key.into()),
197            limit: p.limit,
198        })
199    }
200}