Skip to main content

chio_settle/channel/
state.rs

1use chio_core::canonical::canonical_json_bytes;
2use chio_core::capability::scope::MonetaryAmount;
3use chio_core::crypto::{sha256_hex, PublicKey};
4use chio_core::receipt::body::ChioReceipt;
5use chio_core::receipt::economics::{
6    ChannelSettlementModeV1, SettlementStatus, CHIO_CHANNEL_RECEIPT_METADATA_SCHEMA,
7};
8use chio_credit::obligation::{
9    derive_obligation_payee_binding_digest, ObligationAtomV1, ObligationCreditElectionV1,
10};
11use serde::{Deserialize, Serialize};
12
13use super::validation::{
14    digest, parse_base_units, validate_currency, validate_digest, validate_text,
15    I_JSON_MAX_SAFE_INTEGER,
16};
17use super::{
18    ChannelError, ChannelLifecycleStatusV1, ChannelOpenTrustV1, ChannelSignatureV1,
19    VerifiedAdmittedChannelReservationV1, VerifiedChannelOpenConsentV1,
20};
21
22pub const CHANNEL_STATE_SCHEMA: &str = "chio.channel.state.v1";
23
24const CHANNEL_STATE_BODY_DIGEST_DOMAIN: &[u8] = b"chio.channel.state.body.digest.v1\0";
25const CHANNEL_STATE_DIGEST_DOMAIN: &[u8] = b"chio.channel.state.digest.v1\0";
26const CHANNEL_RECEIPT_ROOT_DOMAIN: &[u8] = b"chio.channel.receipt-root.v1\0";
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase", deny_unknown_fields)]
30pub struct ChannelStateBodyV1 {
31    pub schema: String,
32    pub channel_id: String,
33    pub seq: u64,
34    #[serde(
35        default,
36        skip_serializing_if = "Option::is_none",
37        deserialize_with = "super::validation::deserialize_present_option"
38    )]
39    pub prev_state_digest: Option<String>,
40    pub cumulative_owed: MonetaryAmount,
41    pub receipt_id_root: String,
42    pub receipt_count: u64,
43    #[serde(
44        default,
45        skip_serializing_if = "Option::is_none",
46        deserialize_with = "super::validation::deserialize_present_option"
47    )]
48    pub receipt_id: Option<String>,
49    #[serde(
50        default,
51        skip_serializing_if = "Option::is_none",
52        deserialize_with = "super::validation::deserialize_present_option"
53    )]
54    pub receipt_digest: Option<String>,
55    #[serde(
56        default,
57        skip_serializing_if = "Option::is_none",
58        deserialize_with = "super::validation::deserialize_present_option"
59    )]
60    pub receipt_authority_digest: Option<String>,
61    #[serde(
62        default,
63        skip_serializing_if = "Option::is_none",
64        deserialize_with = "super::validation::deserialize_present_option"
65    )]
66    pub obligation_atom_digest: Option<String>,
67    #[serde(
68        default,
69        skip_serializing_if = "Option::is_none",
70        deserialize_with = "super::validation::deserialize_present_option"
71    )]
72    pub reservation_digest: Option<String>,
73    #[serde(
74        default,
75        skip_serializing_if = "Option::is_none",
76        deserialize_with = "super::validation::deserialize_present_option"
77    )]
78    pub actual_charge: Option<MonetaryAmount>,
79    pub cumulative_token_base_units: String,
80    pub asset_binding_digest: String,
81}
82
83impl ChannelStateBodyV1 {
84    pub fn initial(
85        channel_id: String,
86        currency: String,
87        asset_binding_digest: String,
88    ) -> Result<Self, ChannelError> {
89        let state = Self {
90            schema: CHANNEL_STATE_SCHEMA.to_owned(),
91            channel_id,
92            seq: 0,
93            prev_state_digest: None,
94            cumulative_owed: MonetaryAmount { units: 0, currency },
95            receipt_id_root: empty_receipt_root()?,
96            receipt_count: 0,
97            receipt_id: None,
98            receipt_digest: None,
99            receipt_authority_digest: None,
100            obligation_atom_digest: None,
101            reservation_digest: None,
102            actual_charge: None,
103            cumulative_token_base_units: "0".to_owned(),
104            asset_binding_digest,
105        };
106        state.validate()?;
107        Ok(state)
108    }
109
110    pub fn validate(&self) -> Result<(), ChannelError> {
111        if self.schema != CHANNEL_STATE_SCHEMA {
112            return Err(ChannelError::InvalidField("channel_state_schema"));
113        }
114        validate_digest("state_channel_id", &self.channel_id)?;
115        validate_currency(&self.cumulative_owed.currency)?;
116        validate_digest("receipt_id_root", &self.receipt_id_root)?;
117        validate_digest("state_asset_binding_digest", &self.asset_binding_digest)?;
118        parse_base_units(&self.cumulative_token_base_units)?;
119        if self.seq > I_JSON_MAX_SAFE_INTEGER || self.receipt_count > I_JSON_MAX_SAFE_INTEGER {
120            return Err(ChannelError::InvalidField("channel_state_sequence"));
121        }
122        if self.cumulative_owed.units > I_JSON_MAX_SAFE_INTEGER {
123            return Err(ChannelError::InvalidField("channel_state_amount"));
124        }
125        for value in [
126            self.prev_state_digest.as_deref(),
127            self.receipt_digest.as_deref(),
128            self.receipt_authority_digest.as_deref(),
129            self.obligation_atom_digest.as_deref(),
130            self.reservation_digest.as_deref(),
131        ]
132        .into_iter()
133        .flatten()
134        {
135            validate_digest("channel_state_digest_binding", value)?;
136        }
137        if let Some(receipt_id) = self.receipt_id.as_deref() {
138            validate_text("channel_receipt_id", receipt_id)?;
139        }
140        if let Some(actual_charge) = &self.actual_charge {
141            validate_currency(&actual_charge.currency)?;
142            if actual_charge.currency != self.cumulative_owed.currency {
143                return Err(ChannelError::InvalidField("actual_charge_currency"));
144            }
145            if actual_charge.units > I_JSON_MAX_SAFE_INTEGER {
146                return Err(ChannelError::InvalidField("actual_charge_amount"));
147            }
148        }
149        let bindings = [
150            self.prev_state_digest.is_some(),
151            self.receipt_id.is_some(),
152            self.receipt_digest.is_some(),
153            self.receipt_authority_digest.is_some(),
154            self.obligation_atom_digest.is_some(),
155            self.reservation_digest.is_some(),
156            self.actual_charge.is_some(),
157        ];
158        if self.seq == 0 {
159            if self.receipt_count != 0
160                || self.receipt_id_root != empty_receipt_root()?
161                || self.cumulative_owed.units != 0
162                || self.cumulative_token_base_units != "0"
163                || bindings.into_iter().any(|present| present)
164            {
165                return Err(ChannelError::InvalidField("initial_channel_state"));
166            }
167        } else {
168            let required = [
169                self.prev_state_digest.is_some(),
170                self.receipt_id.is_some(),
171                self.receipt_digest.is_some(),
172                self.receipt_authority_digest.is_some(),
173                self.reservation_digest.is_some(),
174                self.actual_charge.is_some(),
175            ];
176            let obligation_matches_charge = self
177                .actual_charge
178                .as_ref()
179                .is_some_and(|charge| (charge.units == 0) == self.obligation_atom_digest.is_none());
180            if self.receipt_count != self.seq
181                || required.into_iter().any(|present| !present)
182                || !obligation_matches_charge
183            {
184                return Err(ChannelError::InvalidField("channel_state_binding"));
185            }
186        }
187        Ok(())
188    }
189
190    pub fn digest(&self) -> Result<String, ChannelError> {
191        self.validate()?;
192        digest(CHANNEL_STATE_BODY_DIGEST_DOMAIN, self)
193    }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase", deny_unknown_fields)]
198pub struct SignedChannelStateV1 {
199    pub body: ChannelStateBodyV1,
200    pub payee_signature: ChannelSignatureV1,
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
204pub struct VerifiedChannelStateV1 {
205    body: ChannelStateBodyV1,
206    payee_signature: Option<ChannelSignatureV1>,
207}
208
209impl VerifiedChannelStateV1 {
210    pub(super) fn initial(body: ChannelStateBodyV1) -> Self {
211        Self {
212            body,
213            payee_signature: None,
214        }
215    }
216
217    #[must_use]
218    pub const fn body(&self) -> &ChannelStateBodyV1 {
219        &self.body
220    }
221
222    #[must_use]
223    pub const fn payee_signature(&self) -> Option<&ChannelSignatureV1> {
224        self.payee_signature.as_ref()
225    }
226
227    pub fn digest(&self) -> Result<String, ChannelError> {
228        match &self.payee_signature {
229            Some(payee_signature) => SignedChannelStateV1 {
230                body: self.body.clone(),
231                payee_signature: payee_signature.clone(),
232            }
233            .digest(),
234            None => self.body.digest(),
235        }
236    }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct VerifiedChannelReceiptBindingV1 {
241    channel_id: String,
242    open_digest: String,
243    reservation_digest: String,
244    sequence: u64,
245    receipt_id: String,
246    receipt_digest: String,
247    receipt_authority_digest: String,
248    receipt_timestamp_unix_ms: u64,
249    obligation_atom_id: Option<String>,
250    obligation_atom_digest: Option<String>,
251    obligation_atom: Option<ObligationAtomV1>,
252    actual_charge: MonetaryAmount,
253}
254
255impl VerifiedChannelReceiptBindingV1 {
256    #[must_use]
257    pub fn channel_id(&self) -> &str {
258        &self.channel_id
259    }
260
261    #[must_use]
262    pub fn open_digest(&self) -> &str {
263        &self.open_digest
264    }
265
266    #[must_use]
267    pub fn reservation_digest(&self) -> &str {
268        &self.reservation_digest
269    }
270
271    #[must_use]
272    pub const fn sequence(&self) -> u64 {
273        self.sequence
274    }
275
276    #[must_use]
277    pub fn receipt_id(&self) -> &str {
278        &self.receipt_id
279    }
280
281    #[must_use]
282    pub fn receipt_digest(&self) -> &str {
283        &self.receipt_digest
284    }
285
286    #[must_use]
287    pub fn receipt_authority_digest(&self) -> &str {
288        &self.receipt_authority_digest
289    }
290
291    #[must_use]
292    pub const fn receipt_timestamp_unix_ms(&self) -> u64 {
293        self.receipt_timestamp_unix_ms
294    }
295
296    #[must_use]
297    pub fn obligation_atom_id(&self) -> Option<&str> {
298        self.obligation_atom_id.as_deref()
299    }
300
301    #[must_use]
302    pub fn obligation_atom_digest(&self) -> Option<&str> {
303        self.obligation_atom_digest.as_deref()
304    }
305
306    #[must_use]
307    pub const fn obligation_atom(&self) -> Option<&ObligationAtomV1> {
308        self.obligation_atom.as_ref()
309    }
310
311    #[must_use]
312    pub const fn actual_charge(&self) -> &MonetaryAmount {
313        &self.actual_charge
314    }
315}
316
317pub fn derive_channel_receipt_authority_digest(
318    kernel_key: &PublicKey,
319) -> Result<String, ChannelError> {
320    digest(b"chio.channel.receipt-authority.digest.v1\0", kernel_key)
321}
322
323pub fn derive_channel_payee_binding_digest(
324    payee_id: &str,
325    settlement_destination_ref: &str,
326) -> Result<String, ChannelError> {
327    validate_text("channel_payee_id", payee_id)?;
328    super::validation::validate_evm_address(
329        "channel_payee_destination",
330        settlement_destination_ref,
331    )?;
332    derive_obligation_payee_binding_digest(payee_id, settlement_destination_ref)
333        .map_err(|_| ChannelError::AuthorityVerification)
334}
335
336pub fn verify_channel_receipt_binding(
337    receipt: &ChioReceipt,
338    trusted_kernel_key: &PublicKey,
339    reservation: &VerifiedAdmittedChannelReservationV1,
340    open: &VerifiedChannelOpenConsentV1,
341    obligation: Option<&ObligationAtomV1>,
342) -> Result<VerifiedChannelReceiptBindingV1, ChannelError> {
343    let reservation = reservation.artifact();
344    let intent = open.intent();
345    let open_artifact = open.artifact();
346    let receipt_digest = sha256_hex(
347        &canonical_json_bytes(receipt)
348            .map_err(|error| ChannelError::Canonicalization(error.to_string()))?,
349    );
350    let receipt_authority_digest = derive_channel_receipt_authority_digest(trusted_kernel_key)?;
351    let receipt_timestamp_unix_ms = receipt
352        .timestamp
353        .checked_mul(1_000)
354        .ok_or(ChannelError::ArithmeticOverflow)?;
355    let metadata = receipt
356        .channel_metadata()
357        .ok_or(ChannelError::AuthorityVerification)?;
358    let financial = receipt
359        .financial_metadata()
360        .ok_or(ChannelError::AuthorityVerification)?;
361    let actual_charge = MonetaryAmount {
362        units: financial.cost_charged,
363        currency: financial.currency.clone(),
364    };
365    let terminal_settlement_matches = if actual_charge.units == 0 {
366        (receipt.is_allowed() || receipt.is_denied())
367            && financial.settlement_status == SettlementStatus::NotApplicable
368    } else {
369        receipt.is_allowed() && financial.settlement_status == SettlementStatus::Pending
370    };
371    validate_currency(&actual_charge.currency)?;
372    if !metadata.is_valid()
373        || actual_charge.units > I_JSON_MAX_SAFE_INTEGER
374        || &receipt.kernel_key != trusted_kernel_key
375        || !receipt
376            .verify_signature()
377            .map_err(|_| ChannelError::AuthorityVerification)?
378        || reservation.body.receipt_authority_digest != receipt_authority_digest
379        || reservation.body.channel_id != open_artifact.body.channel_id
380        || reservation.body.open_digest != open_artifact.digest()?
381        || metadata.schema != CHIO_CHANNEL_RECEIPT_METADATA_SCHEMA
382        || metadata.channel_id != reservation.body.channel_id
383        || metadata.open_digest != reservation.body.open_digest
384        || metadata.reservation_id != reservation.body.reservation_id
385        || metadata.reservation_digest != reservation.digest()?
386        || metadata.sequence != reservation.body.next_sequence
387        || metadata.settlement_mode != ChannelSettlementModeV1::Channelized
388        || !terminal_settlement_matches
389        || financial.payment_reference.is_some()
390        || actual_charge.currency != reservation.body.maximum_charge.currency
391        || actual_charge.units > reservation.body.maximum_charge.units
392    {
393        return Err(ChannelError::AuthorityVerification);
394    }
395    let (obligation_atom_id, obligation_atom_digest) = match (actual_charge.units, obligation) {
396        (0, None) => (None, None),
397        (0, Some(_)) | (_, None) => return Err(ChannelError::AuthorityVerification),
398        (_, Some(atom)) => {
399            atom.validate()
400                .map_err(|_| ChannelError::AuthorityVerification)?;
401            let reservation_digest = reservation.digest()?;
402            let reservation_proposal_digest = reservation.body.proposal_digest()?;
403            let payee_binding_digest = derive_channel_payee_binding_digest(
404                &intent.body.payee_id,
405                &intent.body.payee_beneficiary_address,
406            )?;
407            if atom.source_receipt_id() != receipt.id
408                || atom.economic_intent_digest() != reservation_proposal_digest
409                || atom.source_receipt_digest() != receipt_digest
410                || atom.amount() != &actual_charge
411                || atom.debtor_id() != intent.body.payer_id
412                || atom.original_creditor_id() != intent.body.payee_id
413                || atom.original_settlement_destination_ref()
414                    != intent.body.payee_beneficiary_address
415                || atom.payee_binding_digest() != payee_binding_digest
416                || atom.pre_action_authority_digest() != reservation_digest
417                || atom.credit_election() != &ObligationCreditElectionV1::NotCredit
418            {
419                return Err(ChannelError::AuthorityVerification);
420            }
421            (
422                Some(atom.obligation_id().to_owned()),
423                Some(
424                    atom.digest()
425                        .map_err(|_| ChannelError::AuthorityVerification)?,
426                ),
427            )
428        }
429    };
430    Ok(VerifiedChannelReceiptBindingV1 {
431        channel_id: metadata.channel_id,
432        open_digest: metadata.open_digest,
433        reservation_digest: metadata.reservation_digest,
434        sequence: metadata.sequence,
435        receipt_id: receipt.id.clone(),
436        receipt_digest,
437        receipt_authority_digest,
438        receipt_timestamp_unix_ms,
439        obligation_atom_id,
440        obligation_atom_digest,
441        obligation_atom: obligation.cloned(),
442        actual_charge,
443    })
444}
445
446pub fn build_channel_state_transition(
447    prior: &VerifiedChannelStateV1,
448    reservation: &VerifiedAdmittedChannelReservationV1,
449    receipt: &VerifiedChannelReceiptBindingV1,
450    open: &VerifiedChannelOpenConsentV1,
451) -> Result<ChannelStateBodyV1, ChannelError> {
452    let prior_digest = prior.digest()?;
453    let prior = prior.body();
454    let reservation = reservation.artifact();
455    let intent = open.intent();
456    prior.validate()?;
457    reservation.body.validate()?;
458    intent.body.validate()?;
459    let body = &reservation.body;
460    let sequence = next_sequence(prior.seq)?;
461    let reservation_digest = reservation.digest()?;
462    let open_digest = open.artifact().digest()?;
463    let cumulative_units = prior
464        .cumulative_owed
465        .units
466        .checked_add(receipt.actual_charge.units)
467        .filter(|units| *units <= I_JSON_MAX_SAFE_INTEGER)
468        .ok_or(ChannelError::ArithmeticOverflow)?;
469    let cumulative_owed = MonetaryAmount {
470        units: cumulative_units,
471        currency: intent.body.currency.clone(),
472    };
473    let cumulative_token_base_units = intent
474        .body
475        .asset_binding
476        .token_base_units(&cumulative_owed)?;
477    let actual_token_base_units = intent
478        .body
479        .asset_binding
480        .token_base_units(&receipt.actual_charge)?;
481    if receipt.channel_id != body.channel_id
482        || receipt.open_digest != open_digest
483        || receipt.reservation_digest != reservation_digest
484        || receipt.sequence != body.next_sequence
485        || body.channel_id != prior.channel_id
486        || body.prior_state_digest != prior_digest
487        || body.next_sequence != sequence
488        || body.receipt_authority_digest != receipt.receipt_authority_digest
489        || body.maximum_charge.currency != receipt.actual_charge.currency
490        || receipt.actual_charge.currency != intent.body.currency
491        || receipt.actual_charge.units > body.maximum_charge.units
492        || parse_base_units(&actual_token_base_units)?
493            > parse_base_units(&body.maximum_token_base_units)?
494        || cumulative_units > intent.body.bound.units
495        || parse_base_units(&cumulative_token_base_units)?
496            > parse_base_units(&intent.body.bound_token_base_units)?
497        || prior.asset_binding_digest != intent.body.asset_binding.digest()?
498    {
499        return Err(ChannelError::AuthorityVerification);
500    }
501    let state = ChannelStateBodyV1 {
502        schema: CHANNEL_STATE_SCHEMA.to_owned(),
503        channel_id: prior.channel_id.clone(),
504        seq: sequence,
505        prev_state_digest: Some(prior_digest),
506        cumulative_owed,
507        receipt_id_root: append_receipt_root(&prior.receipt_id_root, &receipt.receipt_id)?,
508        receipt_count: next_sequence(prior.receipt_count)?,
509        receipt_id: Some(receipt.receipt_id.clone()),
510        receipt_digest: Some(receipt.receipt_digest.clone()),
511        receipt_authority_digest: Some(receipt.receipt_authority_digest.clone()),
512        obligation_atom_digest: receipt.obligation_atom_digest.clone(),
513        reservation_digest: Some(reservation_digest),
514        actual_charge: Some(receipt.actual_charge.clone()),
515        cumulative_token_base_units,
516        asset_binding_digest: prior.asset_binding_digest.clone(),
517    };
518    state.validate()?;
519    Ok(state)
520}
521
522pub fn verify_channel_state_transition(
523    state: &SignedChannelStateV1,
524    prior: &VerifiedChannelStateV1,
525    reservation: &VerifiedAdmittedChannelReservationV1,
526    receipt: &VerifiedChannelReceiptBindingV1,
527    open: &VerifiedChannelOpenConsentV1,
528    trust: &ChannelOpenTrustV1,
529) -> Result<VerifiedChannelStateV1, ChannelError> {
530    let lifecycle = reservation.snapshot().lifecycle();
531    lifecycle.validate()?;
532    trust.validate()?;
533    if !trust.matches_intent(&open.intent().body) {
534        return Err(ChannelError::AuthorityVerification);
535    }
536    let prior_digest = prior.digest()?;
537    let expected_state_version = reservation
538        .artifact()
539        .body
540        .channel_state_expected_version
541        .checked_add(1)
542        .ok_or(ChannelError::ArithmeticOverflow)?;
543    let expected_fence = reservation
544        .artifact()
545        .body
546        .lifecycle_fence
547        .checked_add(1)
548        .ok_or(ChannelError::ArithmeticOverflow)?;
549    if lifecycle.status != ChannelLifecycleStatusV1::Open
550        || lifecycle.channel_id != open.artifact().body.channel_id
551        || lifecycle.latest_state_digest != prior_digest
552        || lifecycle.latest_sequence != prior.body().seq
553        || lifecycle.state_version != expected_state_version
554        || lifecycle.lifecycle_fence != expected_fence
555        || lifecycle.live_reservation_id.as_deref()
556            != Some(&reservation.artifact().body.reservation_id)
557        || lifecycle.operation_id.as_deref() != Some(&reservation.artifact().body.operation_id)
558    {
559        return Err(ChannelError::AuthorityVerification);
560    }
561    let expected = build_channel_state_transition(prior, reservation, receipt, open)?;
562    state.body.validate()?;
563    state.payee_signature.verify(
564        &state.body,
565        &trust.payee_id,
566        trust.payee_key_epoch,
567        &trust.payee_key,
568    )?;
569    if state.body != expected {
570        return Err(ChannelError::AuthorityVerification);
571    }
572    Ok(VerifiedChannelStateV1 {
573        body: state.body.clone(),
574        payee_signature: Some(state.payee_signature.clone()),
575    })
576}
577
578pub(super) fn verify_retained_signed_channel_state(
579    state: &SignedChannelStateV1,
580    open: &VerifiedChannelOpenConsentV1,
581    trust: &ChannelOpenTrustV1,
582) -> Result<VerifiedChannelStateV1, ChannelError> {
583    state.body.validate()?;
584    trust.validate()?;
585    state.payee_signature.verify(
586        &state.body,
587        &trust.payee_id,
588        trust.payee_key_epoch,
589        &trust.payee_key,
590    )?;
591    let intent = &open.intent().body;
592    if !trust.matches_intent(intent)
593        || state.body.seq == 0
594        || state.body.channel_id != open.artifact().body.channel_id
595        || state.body.cumulative_owed.currency != intent.currency
596        || state.body.cumulative_owed.units > intent.bound.units
597        || state.body.asset_binding_digest != intent.asset_binding.digest()?
598        || parse_base_units(&state.body.cumulative_token_base_units)?
599            > parse_base_units(&intent.bound_token_base_units)?
600    {
601        return Err(ChannelError::AuthorityVerification);
602    }
603    intent.asset_binding.verify_round_trip(
604        &state.body.cumulative_owed,
605        &state.body.cumulative_token_base_units,
606    )?;
607    Ok(VerifiedChannelStateV1 {
608        body: state.body.clone(),
609        payee_signature: Some(state.payee_signature.clone()),
610    })
611}
612
613impl SignedChannelStateV1 {
614    pub fn digest(&self) -> Result<String, ChannelError> {
615        self.body.validate()?;
616        digest(CHANNEL_STATE_DIGEST_DOMAIN, self)
617    }
618}
619
620pub fn empty_receipt_root() -> Result<String, ChannelError> {
621    digest(CHANNEL_RECEIPT_ROOT_DOMAIN, &Vec::<String>::new())
622}
623
624pub fn append_receipt_root(prior_root: &str, receipt_id: &str) -> Result<String, ChannelError> {
625    validate_digest("prior_receipt_root", prior_root)?;
626    validate_text("channel_receipt_id", receipt_id)?;
627    digest(CHANNEL_RECEIPT_ROOT_DOMAIN, &(prior_root, receipt_id))
628}
629
630pub(super) fn next_sequence(value: u64) -> Result<u64, ChannelError> {
631    value
632        .checked_add(1)
633        .filter(|value| *value <= I_JSON_MAX_SAFE_INTEGER)
634        .ok_or(ChannelError::ArithmeticOverflow)
635}