Skip to main content

DomainRecordSchema

Struct DomainRecordSchema 

Source
pub struct DomainRecordSchema {
    pub kind: DomainRecordKind,
    pub class: DomainRecordClass,
    pub holder_policy: KnowledgeHolderPolicy,
    pub mutation_policy: DomainRecordMutationPolicy,
    pub payload_schema: PayloadSchema,
    pub references: Vec<DomainReferenceSchema>,
}

Fields§

§kind: DomainRecordKind§class: DomainRecordClass§holder_policy: KnowledgeHolderPolicy§mutation_policy: DomainRecordMutationPolicy§payload_schema: PayloadSchema§references: Vec<DomainReferenceSchema>

Implementations§

Source§

impl DomainRecordSchema

Source

pub fn new( kind: DomainRecordKind, class: DomainRecordClass, ) -> DomainRecordSchema

Source

pub fn for_type<T>() -> DomainRecordSchema

Source

pub fn for_entity<T>() -> DomainRecordSchema

Examples found in repository?
examples/governance_transition.rs (line 125)
124fn treasury_state() -> StateKey {
125    DomainRecordSchema::for_entity::<TreasuryActionRecord>().state_key()
126}
127
128fn county_state() -> StateKey {
129    DomainRecordSchema::for_entity::<CountyActionRecord>().state_key()
130}
131
132fn action_payload(grain_units: u64) -> ReliefAction {
133    ReliefAction {
134        status: "committed".to_owned(),
135        grain_units,
136    }
137}
138
139fn action_hash(grain_units: u64) -> Result<String, CanwuError> {
140    let payload = serde_json::to_value(action_payload(grain_units)).map_err(|error| {
141        CanwuError::new(
142            ErrorCode::InvalidPayload,
143            format!("relief action payload cannot be encoded: {error}"),
144        )
145    })?;
146    canonical_hash(ACTION_HASH_DOMAIN, &payload)
147}
148
149fn issue_descriptor() -> PluginIngressDescriptor {
150    PluginIngressDescriptor {
151        name: ISSUE_INGRESS.to_owned(),
152        description: "Issue a central relief order".to_owned(),
153        class: IngressClass::ScheduledSystem,
154        payload_schema: object_schema(&[("order_id", PayloadValueType::String)]),
155    }
156}
157
158fn execute_descriptor() -> PluginIngressDescriptor {
159    PluginIngressDescriptor {
160        name: EXECUTE_INGRESS.to_owned(),
161        description: "Admit an owner execution of a relief order".to_owned(),
162        class: IngressClass::ScheduledSystem,
163        payload_schema: object_schema(&[("order_id", PayloadValueType::String)]),
164    }
165}
166
167fn owned_order_ingress(
168    view: &SimulationView<'_>,
169    context: &BoundaryContext,
170    owner: &str,
171    packet_type: &str,
172) -> Result<Option<String>, CanwuError> {
173    let mut order_id = None;
174    for ingress_id in &context.admitted_ingress {
175        let Some(record) = view.ingress(*ingress_id)? else {
176            continue;
177        };
178        let IngressPayload::Plugin {
179            plugin,
180            packet_type: admitted_packet_type,
181            payload,
182            ..
183        } = &record.payload
184        else {
185            continue;
186        };
187        if plugin != owner || admitted_packet_type != packet_type {
188            continue;
189        }
190        if order_id.is_some() {
191            return Err(CanwuError::new(
192                ErrorCode::InvalidBoundary,
193                format!("{owner} received duplicate {packet_type} ingress"),
194            ));
195        }
196        let value = payload
197            .get("order_id")
198            .and_then(Value::as_str)
199            .ok_or_else(|| {
200                CanwuError::new(
201                    ErrorCode::InvalidPayload,
202                    format!("{owner}.{packet_type} is missing order_id"),
203                )
204            })?;
205        order_id = Some(value.to_owned());
206    }
207    Ok(order_id)
208}
209
210fn order_payload(view: &SimulationView<'_>) -> Result<ReliefOrder, CanwuError> {
211    view.typed_domain_record(&order_reference())?
212        .ok_or_else(|| CanwuError::new(ErrorCode::InvalidBoundary, "relief order is missing"))?
213        .decode_payload::<ReliefOrderRecord>()
214}
215
216fn validate_order(
217    order: &ReliefOrder,
218    expected_system: &str,
219    expected_plugin: &str,
220    expected_hash: &str,
221) -> Result<(), CanwuError> {
222    let valid = order.order_id == ORDER_ID
223        && order.issued_by == CENTRAL_PLUGIN
224        && order.treasury_system == "prepare-treasury"
225        && order.treasury_version == 1
226        && order.treasury_disposition == "committed"
227        && order.county_system == "prepare-county"
228        && order.county_version == 1
229        && order.county_disposition == "committed"
230        && ((expected_plugin == TREASURY_PLUGIN
231            && expected_system == "prepare-treasury"
232            && order.treasury_hash == expected_hash)
233            || (expected_plugin == COUNTY_PLUGIN
234                && expected_system == "prepare-county"
235                && order.county_hash == expected_hash));
236    if !valid {
237        return Err(CanwuError::new(
238            ErrorCode::InvalidBoundary,
239            format!("{expected_plugin} received an invalid relief manifest"),
240        ));
241    }
242    Ok(())
243}
244
245fn publish_order(
246    view: &SimulationView<'_>,
247    context: &BoundaryContext,
248) -> Result<BoundaryProposal, CanwuError> {
249    let Some(order_id) = owned_order_ingress(view, context, CENTRAL_PLUGIN, ISSUE_INGRESS)? else {
250        return Ok(BoundaryProposal::default());
251    };
252    if order_id != ORDER_ID {
253        return Err(CanwuError::new(
254            ErrorCode::InvalidBoundary,
255            "the issue ingress names an unexpected relief order",
256        ));
257    }
258    if view.typed_domain_record(&order_reference())?.is_some() {
259        return Err(CanwuError::new(
260            ErrorCode::InvalidBoundary,
261            "the relief order already exists; duplicate issue ingress is invalid",
262        ));
263    }
264    let treasury_hash = action_hash(600)?;
265    let county_hash = action_hash(600)?;
266    let manifest = DomainRecordDraft::from_typed(
267        order_reference(),
268        &ReliefOrder {
269            order_id: ORDER_ID.to_owned(),
270            issued_by: CENTRAL_PLUGIN.to_owned(),
271            treasury_system: "prepare-treasury".to_owned(),
272            treasury_version: 1,
273            treasury_disposition: "committed".to_owned(),
274            treasury_hash,
275            county_system: "prepare-county".to_owned(),
276            county_version: 1,
277            county_disposition: "committed".to_owned(),
278            county_hash,
279        },
280    )?;
281    Ok(BoundaryProposal {
282        directives: vec![
283            BoundaryDirective::MutateRecord {
284                mutation: DomainRecordMutation::Create { record: manifest },
285                summary: "Publish the central relief order manifest".to_owned(),
286            },
287            BoundaryDirective::SchedulePluginIngress {
288                target_plugin: TREASURY_PLUGIN.to_owned(),
289                after: SimDuration::ZERO,
290                packet_type: EXECUTE_INGRESS.to_owned(),
291                priority: 0,
292                payload: json!({"order_id": ORDER_ID}),
293                affected: Vec::new(),
294            },
295            BoundaryDirective::SchedulePluginIngress {
296                target_plugin: COUNTY_PLUGIN.to_owned(),
297                after: SimDuration::ZERO,
298                packet_type: EXECUTE_INGRESS.to_owned(),
299                priority: 0,
300                payload: json!({"order_id": ORDER_ID}),
301                affected: Vec::new(),
302            },
303        ],
304        ..BoundaryProposal::default()
305    })
306}
307
308fn prepare_treasury(
309    view: &SimulationView<'_>,
310    context: &BoundaryContext,
311) -> Result<BoundaryProposal, CanwuError> {
312    prepare_owner(
313        view,
314        context,
315        TREASURY_PLUGIN,
316        "prepare-treasury",
317        treasury_reference(),
318        &action_payload(600),
319    )
320}
321
322fn prepare_county(
323    view: &SimulationView<'_>,
324    context: &BoundaryContext,
325) -> Result<BoundaryProposal, CanwuError> {
326    prepare_owner(
327        view,
328        context,
329        COUNTY_PLUGIN,
330        "prepare-county",
331        county_reference(),
332        &action_payload(600),
333    )
334}
335
336fn prepare_owner<T: DomainRecordType<Payload = ReliefAction>>(
337    view: &SimulationView<'_>,
338    context: &BoundaryContext,
339    plugin: &str,
340    system: &str,
341    reference: TypedDomainRecordRef<T>,
342    payload: &ReliefAction,
343) -> Result<BoundaryProposal, CanwuError> {
344    let Some(order_id) = owned_order_ingress(view, context, plugin, EXECUTE_INGRESS)? else {
345        return Ok(BoundaryProposal::default());
346    };
347    if order_id != ORDER_ID {
348        return Err(CanwuError::new(
349            ErrorCode::InvalidBoundary,
350            format!("{plugin} received an ingress for the wrong order"),
351        ));
352    }
353    let order = order_payload(view)?;
354    let hash = action_hash(payload.grain_units)?;
355    validate_order(&order, system, plugin, &hash)?;
356    let record = DomainRecordDraft::from_typed(reference, payload)?;
357    Ok(BoundaryProposal {
358        directives: vec![BoundaryDirective::MutateRecord {
359            mutation: DomainRecordMutation::Create { record },
360            summary: format!("{plugin} prepares its guarded relief disposition"),
361        }],
362        ..BoundaryProposal::default()
363    })
364}
365
366fn audit_order(
367    view: &SimulationView<'_>,
368    context: &BoundaryContext,
369) -> Result<BoundaryProposal, CanwuError> {
370    if owned_order_ingress(view, context, CENTRAL_PLUGIN, ISSUE_INGRESS)?.is_some() {
371        return Ok(BoundaryProposal::default());
372    }
373    let order = order_payload(view)?;
374    for (label, reference, expected_plugin, expected_system) in [
375        (
376            "treasury",
377            treasury_reference().as_untyped(),
378            TREASURY_PLUGIN,
379            "prepare-treasury",
380        ),
381        (
382            "county",
383            county_reference().as_untyped(),
384            COUNTY_PLUGIN,
385            "prepare-county",
386        ),
387    ] {
388        let record = view.domain_record(reference)?.ok_or_else(|| {
389            CanwuError::new(
390                ErrorCode::InvalidBoundary,
391                format!("central audit is missing the {label} relief disposition"),
392            )
393        })?;
394        let expected_hash = action_hash(600)?;
395        validate_order(&order, expected_system, expected_plugin, &expected_hash)?;
396        let actual_hash = canonical_hash(ACTION_HASH_DOMAIN, &record.payload)?;
397        if record.version != 1
398            || !record.is_active()
399            || record.owner != expected_plugin
400            || actual_hash != expected_hash
401            || record.payload != json!({"status": "committed", "grain_units": 600})
402        {
403            return Err(CanwuError::new(
404                ErrorCode::InvalidBoundary,
405                format!("central audit found an invalid {label} disposition"),
406            ));
407        }
408    }
409    Ok(BoundaryProposal::default())
410}
411
412struct CentralPlugin;
413
414impl SimulationPlugin for CentralPlugin {
415    fn name(&self) -> &'static str {
416        CENTRAL_PLUGIN
417    }
418
419    fn version(&self) -> &'static str {
420        "1"
421    }
422
423    fn semantic_hash(&self) -> &'static str {
424        "0000000000000000000000000000000000000000000000000000000000000301"
425    }
426
427    fn register(&self, registrar: &mut PluginRegistrar<'_>) -> Result<(), CanwuError> {
428        let mut schema = DomainRecordSchema::for_record::<ReliefOrderRecord>();
429        schema.payload_schema = object_schema(&[
430            ("order_id", PayloadValueType::String),
431            ("issued_by", PayloadValueType::String),
432            ("treasury_system", PayloadValueType::String),
433            ("treasury_version", PayloadValueType::Integer),
434            ("treasury_disposition", PayloadValueType::String),
435            ("treasury_hash", PayloadValueType::String),
436            ("county_system", PayloadValueType::String),
437            ("county_version", PayloadValueType::Integer),
438            ("county_disposition", PayloadValueType::String),
439            ("county_hash", PayloadValueType::String),
440        ]);
441        registrar.register_record_schema(schema)?;
442        registrar.register_ingress(issue_descriptor())?;
443
444        let mut publish = BoundarySystemContract::new(
445            "publish-order",
446            BoundaryPhase::DomainDeltaProposal,
447            SystemCadence::EventDriven,
448        );
449        publish.reads = vec![order_state(), StateKey::core_ingress()];
450        publish.writes = vec![order_state()];
451        publish.plugin_ingress_targets = vec![
452            canwu_api::PluginIngressTarget {
453                target_plugin: TREASURY_PLUGIN.to_owned(),
454                packet_type: EXECUTE_INGRESS.to_owned(),
455            },
456            canwu_api::PluginIngressTarget {
457                target_plugin: COUNTY_PLUGIN.to_owned(),
458                packet_type: EXECUTE_INGRESS.to_owned(),
459            },
460        ];
461        publish.visibility = StateVisibility::SameBoundary;
462        registrar.register_boundary_system(publish, publish_order)?;
463
464        let mut audit = BoundarySystemContract::new(
465            "audit-order",
466            BoundaryPhase::StrategicAggregation,
467            SystemCadence::EventDriven,
468        );
469        audit.reads = vec![
470            order_state(),
471            treasury_state(),
472            county_state(),
473            StateKey::core_ingress(),
474        ];
475        registrar.register_boundary_system(audit, audit_order)
476    }
477}
478
479struct TreasuryPlugin;
480
481impl SimulationPlugin for TreasuryPlugin {
482    fn name(&self) -> &'static str {
483        TREASURY_PLUGIN
484    }
485
486    fn version(&self) -> &'static str {
487        "1"
488    }
489
490    fn semantic_hash(&self) -> &'static str {
491        "0000000000000000000000000000000000000000000000000000000000000302"
492    }
493
494    fn register(&self, registrar: &mut PluginRegistrar<'_>) -> Result<(), CanwuError> {
495        let mut schema = DomainRecordSchema::for_entity::<TreasuryActionRecord>();
496        schema.payload_schema = object_schema(&[
497            ("status", PayloadValueType::String),
498            ("grain_units", PayloadValueType::Integer),
499        ]);
500        registrar.register_record_schema(schema)?;
501        registrar.register_ingress(execute_descriptor())?;
502        let mut prepare = BoundarySystemContract::new(
503            "prepare-treasury",
504            BoundaryPhase::HistoricalCandidateEvaluation,
505            SystemCadence::EventDriven,
506        );
507        prepare.reads = vec![order_state(), StateKey::core_ingress()];
508        prepare.writes = vec![treasury_state()];
509        prepare.visibility = StateVisibility::SameBoundary;
510        registrar.register_boundary_system(prepare, prepare_treasury)
511    }
512}
513
514struct CountyPlugin;
515
516impl SimulationPlugin for CountyPlugin {
517    fn name(&self) -> &'static str {
518        COUNTY_PLUGIN
519    }
520
521    fn version(&self) -> &'static str {
522        "1"
523    }
524
525    fn semantic_hash(&self) -> &'static str {
526        "0000000000000000000000000000000000000000000000000000000000000303"
527    }
528
529    fn register(&self, registrar: &mut PluginRegistrar<'_>) -> Result<(), CanwuError> {
530        let mut schema = DomainRecordSchema::for_entity::<CountyActionRecord>();
531        schema.payload_schema = object_schema(&[
532            ("status", PayloadValueType::String),
533            ("grain_units", PayloadValueType::Integer),
534        ]);
535        registrar.register_record_schema(schema)?;
536        registrar.register_ingress(execute_descriptor())?;
537        let mut prepare = BoundarySystemContract::new(
538            "prepare-county",
539            BoundaryPhase::HistoricalCandidateEvaluation,
540            SystemCadence::EventDriven,
541        );
542        prepare.reads = vec![order_state(), StateKey::core_ingress()];
543        prepare.writes = vec![county_state()];
544        prepare.visibility = StateVisibility::SameBoundary;
545        registrar.register_boundary_system(prepare, prepare_county)
546    }
Source

pub fn for_record<T>() -> DomainRecordSchema
where T: DomainValueType,

Examples found in repository?
examples/governance_transition.rs (line 121)
120fn order_state() -> StateKey {
121    DomainRecordSchema::for_record::<ReliefOrderRecord>().state_key()
122}
123
124fn treasury_state() -> StateKey {
125    DomainRecordSchema::for_entity::<TreasuryActionRecord>().state_key()
126}
127
128fn county_state() -> StateKey {
129    DomainRecordSchema::for_entity::<CountyActionRecord>().state_key()
130}
131
132fn action_payload(grain_units: u64) -> ReliefAction {
133    ReliefAction {
134        status: "committed".to_owned(),
135        grain_units,
136    }
137}
138
139fn action_hash(grain_units: u64) -> Result<String, CanwuError> {
140    let payload = serde_json::to_value(action_payload(grain_units)).map_err(|error| {
141        CanwuError::new(
142            ErrorCode::InvalidPayload,
143            format!("relief action payload cannot be encoded: {error}"),
144        )
145    })?;
146    canonical_hash(ACTION_HASH_DOMAIN, &payload)
147}
148
149fn issue_descriptor() -> PluginIngressDescriptor {
150    PluginIngressDescriptor {
151        name: ISSUE_INGRESS.to_owned(),
152        description: "Issue a central relief order".to_owned(),
153        class: IngressClass::ScheduledSystem,
154        payload_schema: object_schema(&[("order_id", PayloadValueType::String)]),
155    }
156}
157
158fn execute_descriptor() -> PluginIngressDescriptor {
159    PluginIngressDescriptor {
160        name: EXECUTE_INGRESS.to_owned(),
161        description: "Admit an owner execution of a relief order".to_owned(),
162        class: IngressClass::ScheduledSystem,
163        payload_schema: object_schema(&[("order_id", PayloadValueType::String)]),
164    }
165}
166
167fn owned_order_ingress(
168    view: &SimulationView<'_>,
169    context: &BoundaryContext,
170    owner: &str,
171    packet_type: &str,
172) -> Result<Option<String>, CanwuError> {
173    let mut order_id = None;
174    for ingress_id in &context.admitted_ingress {
175        let Some(record) = view.ingress(*ingress_id)? else {
176            continue;
177        };
178        let IngressPayload::Plugin {
179            plugin,
180            packet_type: admitted_packet_type,
181            payload,
182            ..
183        } = &record.payload
184        else {
185            continue;
186        };
187        if plugin != owner || admitted_packet_type != packet_type {
188            continue;
189        }
190        if order_id.is_some() {
191            return Err(CanwuError::new(
192                ErrorCode::InvalidBoundary,
193                format!("{owner} received duplicate {packet_type} ingress"),
194            ));
195        }
196        let value = payload
197            .get("order_id")
198            .and_then(Value::as_str)
199            .ok_or_else(|| {
200                CanwuError::new(
201                    ErrorCode::InvalidPayload,
202                    format!("{owner}.{packet_type} is missing order_id"),
203                )
204            })?;
205        order_id = Some(value.to_owned());
206    }
207    Ok(order_id)
208}
209
210fn order_payload(view: &SimulationView<'_>) -> Result<ReliefOrder, CanwuError> {
211    view.typed_domain_record(&order_reference())?
212        .ok_or_else(|| CanwuError::new(ErrorCode::InvalidBoundary, "relief order is missing"))?
213        .decode_payload::<ReliefOrderRecord>()
214}
215
216fn validate_order(
217    order: &ReliefOrder,
218    expected_system: &str,
219    expected_plugin: &str,
220    expected_hash: &str,
221) -> Result<(), CanwuError> {
222    let valid = order.order_id == ORDER_ID
223        && order.issued_by == CENTRAL_PLUGIN
224        && order.treasury_system == "prepare-treasury"
225        && order.treasury_version == 1
226        && order.treasury_disposition == "committed"
227        && order.county_system == "prepare-county"
228        && order.county_version == 1
229        && order.county_disposition == "committed"
230        && ((expected_plugin == TREASURY_PLUGIN
231            && expected_system == "prepare-treasury"
232            && order.treasury_hash == expected_hash)
233            || (expected_plugin == COUNTY_PLUGIN
234                && expected_system == "prepare-county"
235                && order.county_hash == expected_hash));
236    if !valid {
237        return Err(CanwuError::new(
238            ErrorCode::InvalidBoundary,
239            format!("{expected_plugin} received an invalid relief manifest"),
240        ));
241    }
242    Ok(())
243}
244
245fn publish_order(
246    view: &SimulationView<'_>,
247    context: &BoundaryContext,
248) -> Result<BoundaryProposal, CanwuError> {
249    let Some(order_id) = owned_order_ingress(view, context, CENTRAL_PLUGIN, ISSUE_INGRESS)? else {
250        return Ok(BoundaryProposal::default());
251    };
252    if order_id != ORDER_ID {
253        return Err(CanwuError::new(
254            ErrorCode::InvalidBoundary,
255            "the issue ingress names an unexpected relief order",
256        ));
257    }
258    if view.typed_domain_record(&order_reference())?.is_some() {
259        return Err(CanwuError::new(
260            ErrorCode::InvalidBoundary,
261            "the relief order already exists; duplicate issue ingress is invalid",
262        ));
263    }
264    let treasury_hash = action_hash(600)?;
265    let county_hash = action_hash(600)?;
266    let manifest = DomainRecordDraft::from_typed(
267        order_reference(),
268        &ReliefOrder {
269            order_id: ORDER_ID.to_owned(),
270            issued_by: CENTRAL_PLUGIN.to_owned(),
271            treasury_system: "prepare-treasury".to_owned(),
272            treasury_version: 1,
273            treasury_disposition: "committed".to_owned(),
274            treasury_hash,
275            county_system: "prepare-county".to_owned(),
276            county_version: 1,
277            county_disposition: "committed".to_owned(),
278            county_hash,
279        },
280    )?;
281    Ok(BoundaryProposal {
282        directives: vec![
283            BoundaryDirective::MutateRecord {
284                mutation: DomainRecordMutation::Create { record: manifest },
285                summary: "Publish the central relief order manifest".to_owned(),
286            },
287            BoundaryDirective::SchedulePluginIngress {
288                target_plugin: TREASURY_PLUGIN.to_owned(),
289                after: SimDuration::ZERO,
290                packet_type: EXECUTE_INGRESS.to_owned(),
291                priority: 0,
292                payload: json!({"order_id": ORDER_ID}),
293                affected: Vec::new(),
294            },
295            BoundaryDirective::SchedulePluginIngress {
296                target_plugin: COUNTY_PLUGIN.to_owned(),
297                after: SimDuration::ZERO,
298                packet_type: EXECUTE_INGRESS.to_owned(),
299                priority: 0,
300                payload: json!({"order_id": ORDER_ID}),
301                affected: Vec::new(),
302            },
303        ],
304        ..BoundaryProposal::default()
305    })
306}
307
308fn prepare_treasury(
309    view: &SimulationView<'_>,
310    context: &BoundaryContext,
311) -> Result<BoundaryProposal, CanwuError> {
312    prepare_owner(
313        view,
314        context,
315        TREASURY_PLUGIN,
316        "prepare-treasury",
317        treasury_reference(),
318        &action_payload(600),
319    )
320}
321
322fn prepare_county(
323    view: &SimulationView<'_>,
324    context: &BoundaryContext,
325) -> Result<BoundaryProposal, CanwuError> {
326    prepare_owner(
327        view,
328        context,
329        COUNTY_PLUGIN,
330        "prepare-county",
331        county_reference(),
332        &action_payload(600),
333    )
334}
335
336fn prepare_owner<T: DomainRecordType<Payload = ReliefAction>>(
337    view: &SimulationView<'_>,
338    context: &BoundaryContext,
339    plugin: &str,
340    system: &str,
341    reference: TypedDomainRecordRef<T>,
342    payload: &ReliefAction,
343) -> Result<BoundaryProposal, CanwuError> {
344    let Some(order_id) = owned_order_ingress(view, context, plugin, EXECUTE_INGRESS)? else {
345        return Ok(BoundaryProposal::default());
346    };
347    if order_id != ORDER_ID {
348        return Err(CanwuError::new(
349            ErrorCode::InvalidBoundary,
350            format!("{plugin} received an ingress for the wrong order"),
351        ));
352    }
353    let order = order_payload(view)?;
354    let hash = action_hash(payload.grain_units)?;
355    validate_order(&order, system, plugin, &hash)?;
356    let record = DomainRecordDraft::from_typed(reference, payload)?;
357    Ok(BoundaryProposal {
358        directives: vec![BoundaryDirective::MutateRecord {
359            mutation: DomainRecordMutation::Create { record },
360            summary: format!("{plugin} prepares its guarded relief disposition"),
361        }],
362        ..BoundaryProposal::default()
363    })
364}
365
366fn audit_order(
367    view: &SimulationView<'_>,
368    context: &BoundaryContext,
369) -> Result<BoundaryProposal, CanwuError> {
370    if owned_order_ingress(view, context, CENTRAL_PLUGIN, ISSUE_INGRESS)?.is_some() {
371        return Ok(BoundaryProposal::default());
372    }
373    let order = order_payload(view)?;
374    for (label, reference, expected_plugin, expected_system) in [
375        (
376            "treasury",
377            treasury_reference().as_untyped(),
378            TREASURY_PLUGIN,
379            "prepare-treasury",
380        ),
381        (
382            "county",
383            county_reference().as_untyped(),
384            COUNTY_PLUGIN,
385            "prepare-county",
386        ),
387    ] {
388        let record = view.domain_record(reference)?.ok_or_else(|| {
389            CanwuError::new(
390                ErrorCode::InvalidBoundary,
391                format!("central audit is missing the {label} relief disposition"),
392            )
393        })?;
394        let expected_hash = action_hash(600)?;
395        validate_order(&order, expected_system, expected_plugin, &expected_hash)?;
396        let actual_hash = canonical_hash(ACTION_HASH_DOMAIN, &record.payload)?;
397        if record.version != 1
398            || !record.is_active()
399            || record.owner != expected_plugin
400            || actual_hash != expected_hash
401            || record.payload != json!({"status": "committed", "grain_units": 600})
402        {
403            return Err(CanwuError::new(
404                ErrorCode::InvalidBoundary,
405                format!("central audit found an invalid {label} disposition"),
406            ));
407        }
408    }
409    Ok(BoundaryProposal::default())
410}
411
412struct CentralPlugin;
413
414impl SimulationPlugin for CentralPlugin {
415    fn name(&self) -> &'static str {
416        CENTRAL_PLUGIN
417    }
418
419    fn version(&self) -> &'static str {
420        "1"
421    }
422
423    fn semantic_hash(&self) -> &'static str {
424        "0000000000000000000000000000000000000000000000000000000000000301"
425    }
426
427    fn register(&self, registrar: &mut PluginRegistrar<'_>) -> Result<(), CanwuError> {
428        let mut schema = DomainRecordSchema::for_record::<ReliefOrderRecord>();
429        schema.payload_schema = object_schema(&[
430            ("order_id", PayloadValueType::String),
431            ("issued_by", PayloadValueType::String),
432            ("treasury_system", PayloadValueType::String),
433            ("treasury_version", PayloadValueType::Integer),
434            ("treasury_disposition", PayloadValueType::String),
435            ("treasury_hash", PayloadValueType::String),
436            ("county_system", PayloadValueType::String),
437            ("county_version", PayloadValueType::Integer),
438            ("county_disposition", PayloadValueType::String),
439            ("county_hash", PayloadValueType::String),
440        ]);
441        registrar.register_record_schema(schema)?;
442        registrar.register_ingress(issue_descriptor())?;
443
444        let mut publish = BoundarySystemContract::new(
445            "publish-order",
446            BoundaryPhase::DomainDeltaProposal,
447            SystemCadence::EventDriven,
448        );
449        publish.reads = vec![order_state(), StateKey::core_ingress()];
450        publish.writes = vec![order_state()];
451        publish.plugin_ingress_targets = vec![
452            canwu_api::PluginIngressTarget {
453                target_plugin: TREASURY_PLUGIN.to_owned(),
454                packet_type: EXECUTE_INGRESS.to_owned(),
455            },
456            canwu_api::PluginIngressTarget {
457                target_plugin: COUNTY_PLUGIN.to_owned(),
458                packet_type: EXECUTE_INGRESS.to_owned(),
459            },
460        ];
461        publish.visibility = StateVisibility::SameBoundary;
462        registrar.register_boundary_system(publish, publish_order)?;
463
464        let mut audit = BoundarySystemContract::new(
465            "audit-order",
466            BoundaryPhase::StrategicAggregation,
467            SystemCadence::EventDriven,
468        );
469        audit.reads = vec![
470            order_state(),
471            treasury_state(),
472            county_state(),
473            StateKey::core_ingress(),
474        ];
475        registrar.register_boundary_system(audit, audit_order)
476    }
Source

pub fn state_key(&self) -> StateKey

Examples found in repository?
examples/governance_transition.rs (line 121)
120fn order_state() -> StateKey {
121    DomainRecordSchema::for_record::<ReliefOrderRecord>().state_key()
122}
123
124fn treasury_state() -> StateKey {
125    DomainRecordSchema::for_entity::<TreasuryActionRecord>().state_key()
126}
127
128fn county_state() -> StateKey {
129    DomainRecordSchema::for_entity::<CountyActionRecord>().state_key()
130}

Trait Implementations§

Source§

impl Clone for DomainRecordSchema

Source§

fn clone(&self) -> DomainRecordSchema

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DomainRecordSchema

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for DomainRecordSchema

Source§

fn deserialize<__D>( __deserializer: __D, ) -> Result<DomainRecordSchema, <__D as Deserializer<'de>>::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for DomainRecordSchema

Source§

impl PartialEq for DomainRecordSchema

Source§

fn eq(&self, other: &DomainRecordSchema) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for DomainRecordSchema

Source§

fn serialize<__S>( &self, __serializer: __S, ) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for DomainRecordSchema

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.