Skip to main content

daml_grpc/data/
state.rs

1use std::convert::TryFrom;
2
3use crate::data::event::DamlCreatedEvent;
4use crate::data::offset::DamlLedgerOffset;
5use crate::data::reassignment::{DamlAssignedEvent, DamlUnassignedEvent};
6use crate::data::topology::DamlParticipantPermission;
7use crate::data::{DamlError, DamlResult};
8use crate::grpc_protobuf::com::daml::ledger::api::v2::get_active_contracts_response::ContractEntry;
9use crate::grpc_protobuf::com::daml::ledger::api::v2::get_connected_synchronizers_response::ConnectedSynchronizer;
10use crate::grpc_protobuf::com::daml::ledger::api::v2::{
11    ActiveContract, GetActiveContractsResponse, IncompleteAssigned, IncompleteUnassigned, ParticipantPermission,
12};
13use crate::util::Required;
14
15/// A snapshot of one (contract, synchronizer) pair at a specific
16/// ledger offset.
17///
18/// Activeness is a per-synchronizer concept: a contract can be active
19/// on one synchronizer while archived on another. The same contract
20/// can therefore appear multiple times in a snapshot, once per
21/// synchronizer it is active on.
22#[derive(Debug, Eq, PartialEq, Clone)]
23pub struct DamlActiveContract {
24    /// The most recent create-or-assign event for this contract on
25    /// `synchronizer_id`. The event's offset may point at an already-
26    /// pruned update; do not assume it's lookup-able.
27    pub created_event: DamlCreatedEvent,
28    pub synchronizer_id: String,
29    /// `0` when the contract has never been reassigned; strictly
30    /// increases with each unassign.
31    pub reassignment_counter: u64,
32}
33
34impl TryFrom<ActiveContract> for DamlActiveContract {
35    type Error = DamlError;
36
37    fn try_from(c: ActiveContract) -> DamlResult<Self> {
38        Ok(Self {
39            created_event: DamlCreatedEvent::try_from(c.created_event.req()?)?,
40            synchronizer_id: c.synchronizer_id,
41            reassignment_counter: c.reassignment_counter,
42        })
43    }
44}
45
46/// A contract that was unassigned at or before the snapshot offset
47/// but whose matching `Assigned` hasn't been observed yet.
48///
49/// The contract is in an in-between state: visible on neither the
50/// source synchronizer (because it was unassigned) nor the target
51/// (because the assign hasn't landed). The `CreatedEvent` represents
52/// its prior state on the source.
53#[derive(Debug, Eq, PartialEq, Clone)]
54pub struct DamlIncompleteUnassigned {
55    pub created_event: DamlCreatedEvent,
56    pub unassigned_event: DamlUnassignedEvent,
57}
58
59impl TryFrom<IncompleteUnassigned> for DamlIncompleteUnassigned {
60    type Error = DamlError;
61
62    fn try_from(u: IncompleteUnassigned) -> DamlResult<Self> {
63        Ok(Self {
64            created_event: DamlCreatedEvent::try_from(u.created_event.req()?)?,
65            unassigned_event: DamlUnassignedEvent::try_from(u.unassigned_event.req()?)?,
66        })
67    }
68}
69
70/// A contract that was assigned at or before the snapshot offset but
71/// whose matching `Unassigned` hasn't been observed yet.
72///
73/// Note: per the proto, this **does not** mean the contract is active
74/// on the target — only that the participant has seen the assign half
75/// without the matching unassign half yet.
76#[derive(Debug, Eq, PartialEq, Clone)]
77pub struct DamlIncompleteAssigned {
78    pub assigned_event: DamlAssignedEvent,
79}
80
81impl TryFrom<IncompleteAssigned> for DamlIncompleteAssigned {
82    type Error = DamlError;
83
84    fn try_from(a: IncompleteAssigned) -> DamlResult<Self> {
85        Ok(Self {
86            assigned_event: DamlAssignedEvent::try_from(a.assigned_event.req()?)?,
87        })
88    }
89}
90
91/// One entry in the active-contracts snapshot — either a regular
92/// active contract or one of the two "incomplete" reassignment
93/// states. The variant tells you which.
94#[derive(Debug, Eq, PartialEq, Clone)]
95pub enum DamlContractEntry {
96    Active(DamlActiveContract),
97    IncompleteUnassigned(Box<DamlIncompleteUnassigned>),
98    IncompleteAssigned(Box<DamlIncompleteAssigned>),
99}
100
101impl TryFrom<ContractEntry> for DamlContractEntry {
102    type Error = DamlError;
103
104    fn try_from(e: ContractEntry) -> DamlResult<Self> {
105        Ok(match e {
106            ContractEntry::ActiveContract(c) => Self::Active(DamlActiveContract::try_from(c)?),
107            ContractEntry::IncompleteUnassigned(u) => {
108                Self::IncompleteUnassigned(Box::new(DamlIncompleteUnassigned::try_from(u)?))
109            },
110            ContractEntry::IncompleteAssigned(a) => {
111                Self::IncompleteAssigned(Box::new(DamlIncompleteAssigned::try_from(a)?))
112            },
113        })
114    }
115}
116
117/// One message in the `GetActiveContracts` stream — carries one
118/// contract entry plus, on the streaming RPC only, the continuation
119/// token to resume from after this message.
120#[derive(Debug, Eq, PartialEq, Clone)]
121pub struct DamlActiveContractsResponse {
122    pub workflow_id: String,
123    pub contract_entry: DamlContractEntry,
124    /// Opaque resume-cursor. Empty when not applicable (point-fetched
125    /// pages use a separate `next_page_token`).
126    pub stream_continuation_token: Vec<u8>,
127}
128
129impl TryFrom<GetActiveContractsResponse> for DamlActiveContractsResponse {
130    type Error = DamlError;
131
132    fn try_from(r: GetActiveContractsResponse) -> DamlResult<Self> {
133        Ok(Self {
134            workflow_id: r.workflow_id,
135            contract_entry: DamlContractEntry::try_from(r.contract_entry.req()?)?,
136            stream_continuation_token: r.stream_continuation_token,
137        })
138    }
139}
140
141/// A single page of `GetActiveContractsPage` results. The
142/// `active_at_offset` echoes the request (or the participant's
143/// chosen current offset when the request didn't specify one).
144/// `next_page_token = None` marks the last page.
145#[derive(Debug, Eq, PartialEq, Clone)]
146pub struct DamlActiveContractsPage {
147    pub active_contracts: Vec<DamlActiveContractsResponse>,
148    pub active_at_offset: DamlLedgerOffset,
149    pub next_page_token: Option<Vec<u8>>,
150}
151
152/// One synchronizer the participant is connected to. The optional
153/// `permission` is only populated when the query was scoped to a
154/// specific party.
155#[derive(Debug, Eq, PartialEq, Clone)]
156pub struct DamlConnectedSynchronizer {
157    pub synchronizer_alias: String,
158    pub synchronizer_id: String,
159    pub permission: Option<DamlParticipantPermission>,
160}
161
162impl TryFrom<ConnectedSynchronizer> for DamlConnectedSynchronizer {
163    type Error = DamlError;
164
165    fn try_from(c: ConnectedSynchronizer) -> DamlResult<Self> {
166        // `permission` is wire-default `Unspecified` when "not set"
167        // (proto doesn't model it as a separate Option). Treat
168        // Unspecified as None so callers can distinguish "no party
169        // scoped — no permission to report" from a real permission.
170        let permission = match ParticipantPermission::try_from(c.permission).ok().req()? {
171            ParticipantPermission::Unspecified => None,
172            other => Some(DamlParticipantPermission::from(other)),
173        };
174        Ok(Self {
175            synchronizer_alias: c.synchronizer_alias,
176            synchronizer_id: c.synchronizer_id,
177            permission,
178        })
179    }
180}
181
182/// The participant's currently-known prune offsets. Both fields are
183/// `0` when nothing has been pruned yet on that axis.
184#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
185pub struct DamlLatestPrunedOffsets {
186    /// Offset up to which the participant's main store has been
187    /// pruned (inclusive). Does not factor in divulged-contracts
188    /// pruning.
189    pub participant_pruned_up_to_inclusive: DamlLedgerOffset,
190    /// Offset up to which divulged events have been pruned.
191    /// Always at or before `participant_pruned_up_to_inclusive`.
192    pub all_divulged_contracts_pruned_up_to_inclusive: DamlLedgerOffset,
193}