1use std::borrow::Cow;
12use std::fmt;
13
14use bitcoin::{Amount, Transaction};
15use bitcoin::secp256k1::{Keypair, PublicKey};
16use lightning_invoice::Bolt11Invoice;
17
18use ark::{Vtxo, VtxoId, VtxoPolicy, VtxoRequest};
19use ark::vtxo::Full;
20use ark::mailbox::MailboxIdentifier;
21use ark::tree::signed::{UnlockHash, VtxoTreeSpec};
22use ark::lightning::{PaymentHash, Preimage};
23use ark::rounds::RoundSeq;
24
25use crate::WalletVtxo;
26use crate::exit::{ExitState, ExitTxOrigin, ExitVtxo};
27use crate::movement::MovementId;
28use crate::lock_manager::LockGuard;
29use crate::round::{AttemptState, RoundFlowState, RoundParticipation, RoundState};
30use crate::vtxo::VtxoState;
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct SerdeVtxo {
42 #[serde(with = "ark::encode::serde")]
43 pub vtxo: Vtxo<Full>,
44 pub states: Vec<VtxoState>,
46 #[serde(default)]
49 pub registered: bool,
50}
51
52#[derive(Debug, thiserror::Error)]
53#[error("vtxo has no state")]
54pub struct MissingStateError;
55
56impl SerdeVtxo {
57 pub fn current_state(&self) -> Option<&VtxoState> {
58 self.states.last()
59 }
60
61 pub fn to_wallet_vtxo(&self) -> Result<WalletVtxo, MissingStateError> {
62 let state = self.current_state().cloned().ok_or(MissingStateError)?;
63 Ok(wallet_vtxo_from_full(&self.vtxo, state, self.registered))
64 }
65}
66
67pub(crate) fn wallet_vtxo_from_full(
77 vtxo: &Vtxo<Full>,
78 state: VtxoState,
79 registered: bool,
80) -> WalletVtxo {
81 WalletVtxo {
82 vtxo: vtxo.to_bare(),
83 state,
84 exit_depth: vtxo.exit_depth(),
85 exit_tx_weight: vtxo.transactions().map(|t| t.tx.weight()).sum(),
86 registered,
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct SerdeVtxoKey {
93 pub index: u32,
94 pub public_key: PublicKey,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99pub struct RoundStateId(pub u32);
100
101impl RoundStateId {
102 pub fn to_bytes(&self) -> [u8; 4] {
103 self.0.to_be_bytes()
104 }
105}
106
107impl fmt::Display for RoundStateId {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 fmt::Display::fmt(&self.0, f)
110 }
111}
112
113#[allow(unused)]
114pub struct Locked(Box<dyn LockGuard>);
115
116pub struct Unlocked;
117
118pub struct StoredRoundState<G = Locked> {
119 id: RoundStateId,
120 state: RoundState,
121 _guard: G
122}
123
124impl<G> StoredRoundState<G> {
125 pub fn id(&self) -> RoundStateId {
126 self.id
127 }
128
129 pub fn state(&self) -> &RoundState {
130 &self.state
131 }
132}
133
134impl StoredRoundState<Unlocked> {
135 pub fn new(id: RoundStateId, state: RoundState) -> Self {
136 Self { id, state, _guard: Unlocked }
137 }
138
139 pub fn lock(self, guard: Box<dyn LockGuard>) -> StoredRoundState {
140 StoredRoundState { id: self.id, state: self.state, _guard: Locked(guard) }
141 }
142}
143
144impl StoredRoundState<Locked> {
145 pub fn state_mut(&mut self) -> &mut RoundState {
146 &mut self.state
147 }
148
149 pub fn unlock(self) -> StoredRoundState<Unlocked> {
150 StoredRoundState { id: self.id, state: self.state, _guard: Unlocked }
151 }
152}
153
154#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156pub struct PendingBoard {
157 #[serde(with = "bitcoin_ext::serde::encodable")]
160 pub funding_tx: Transaction,
161 pub vtxos: Vec<VtxoId>,
165 #[serde(with = "bitcoin::amount::serde::as_sat")]
167 pub amount: Amount,
168 pub movement_id: MovementId,
170}
171
172#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
178pub struct PaidInvoice {
179 pub payment_hash: PaymentHash,
180 pub preimage: Preimage,
181 pub paid_at: chrono::DateTime<chrono::Local>,
182}
183
184#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
189pub struct SettledLightningReceive {
190 pub payment_hash: PaymentHash,
191 pub preimage: Preimage,
192 pub invoice: Bolt11Invoice,
193 pub amount: Amount,
194 pub settled_at: chrono::DateTime<chrono::Local>,
195}
196
197#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
203pub struct StoredExit {
204 pub vtxo_id: VtxoId,
206 pub state: ExitState,
208 pub history: Vec<ExitState>,
210 pub movement_id: Option<MovementId>,
213}
214
215impl StoredExit {
216 pub fn new(exit: &ExitVtxo) -> Self {
218 Self {
219 vtxo_id: exit.id(),
220 state: exit.state().clone(),
221 history: exit.history().clone(),
222 movement_id: exit.movement_id(),
223 }
224 }
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct SerdeExitChildTx {
230 #[serde(with = "bitcoin_ext::serde::encodable")]
231 pub child_tx: Transaction,
232 pub origin: ExitTxOrigin,
233}
234
235#[derive(Debug, Clone, Deserialize, Serialize)]
236struct SerdeVtxoRequest<'a> {
237 #[serde(with = "bitcoin::amount::serde::as_sat")]
238 amount: Amount,
239 #[serde(with = "ark::encode::serde")]
240 policy: Cow<'a, VtxoPolicy>,
241}
242
243impl<'a> From<&'a VtxoRequest> for SerdeVtxoRequest<'a> {
244 fn from(v: &'a VtxoRequest) -> Self {
245 Self {
246 amount: v.amount,
247 policy: Cow::Borrowed(&v.policy),
248 }
249 }
250}
251
252impl<'a> From<SerdeVtxoRequest<'a>> for VtxoRequest {
253 fn from(v: SerdeVtxoRequest<'a>) -> Self {
254 VtxoRequest {
255 amount: v.amount,
256 policy: v.policy.into_owned(),
257 }
258 }
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
263struct SerdeRoundParticipation<'a> {
264 #[serde(with = "ark::encode::serde::cow::vec")]
265 inputs: Cow<'a, [Vtxo<Full>]>,
266 outputs: Vec<SerdeVtxoRequest<'a>>,
267 #[serde(default, skip_serializing_if = "Option::is_none", with = "ark::encode::serde::opt")]
268 unblinded_mailbox_id: Option<MailboxIdentifier>,
269}
270
271impl<'a> From<&'a RoundParticipation> for SerdeRoundParticipation<'a> {
272 fn from(v: &'a RoundParticipation) -> Self {
273 Self {
274 inputs: Cow::Borrowed(&v.inputs),
275 outputs: v.outputs.iter().map(|v| v.into()).collect(),
276 unblinded_mailbox_id: v.unblinded_mailbox_id,
277 }
278 }
279}
280
281impl<'a> From<SerdeRoundParticipation<'a>> for RoundParticipation {
282 fn from(v: SerdeRoundParticipation<'a>) -> Self {
283 Self {
284 inputs: v.inputs.into_owned(),
285 outputs: v.outputs.into_iter().map(|v| v.into()).collect(),
286 unblinded_mailbox_id: v.unblinded_mailbox_id,
287 }
288 }
289}
290
291#[derive(Debug, Default)]
294struct PersistedNoncesPlaceholder;
295
296impl ::serde::Serialize for PersistedNoncesPlaceholder {
297 fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
298 s.collect_seq(std::iter::empty::<()>())
299 }
300}
301
302impl<'de> ::serde::Deserialize<'de> for PersistedNoncesPlaceholder {
303 fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
304 ::serde::de::IgnoredAny::deserialize(d)?;
305 Ok(PersistedNoncesPlaceholder)
306 }
307}
308
309#[derive(Debug, Serialize, Deserialize)]
311enum SerdeAttemptState<'a> {
312 AwaitingAttempt,
313 AwaitingUnsignedVtxoTree {
314 cosign_keys: Cow<'a, [Keypair]>,
315 #[serde(rename = "secret_nonces", default)]
318 _legacy_secret_nonces: PersistedNoncesPlaceholder,
319 unlock_hash: UnlockHash,
320 },
321 AwaitingFinishedRound {
322 #[serde(with = "bitcoin_ext::serde::encodable::cow")]
323 unsigned_round_tx: Cow<'a, Transaction>,
324 #[serde(with = "ark::encode::serde")]
325 vtxos_spec: Cow<'a, VtxoTreeSpec>,
326 unlock_hash: UnlockHash,
327 },
328}
329
330impl<'a> From<&'a AttemptState> for SerdeAttemptState<'a> {
331 fn from(state: &'a AttemptState) -> Self {
332 match state {
333 AttemptState::AwaitingAttempt => SerdeAttemptState::AwaitingAttempt,
334 AttemptState::AwaitingUnsignedVtxoTree { cosign_keys, unlock_hash } => {
335 SerdeAttemptState::AwaitingUnsignedVtxoTree {
336 cosign_keys: Cow::Borrowed(cosign_keys),
337 _legacy_secret_nonces: PersistedNoncesPlaceholder,
338 unlock_hash: *unlock_hash,
339 }
340 },
341 AttemptState::AwaitingFinishedRound { unsigned_round_tx, vtxos_spec, unlock_hash } => {
342 SerdeAttemptState::AwaitingFinishedRound {
343 unsigned_round_tx: Cow::Borrowed(unsigned_round_tx),
344 vtxos_spec: Cow::Borrowed(vtxos_spec),
345 unlock_hash: *unlock_hash,
346 }
347 },
348 }
349 }
350}
351
352impl<'a> From<SerdeAttemptState<'a>> for AttemptState {
353 fn from(state: SerdeAttemptState<'a>) -> Self {
354 match state {
355 SerdeAttemptState::AwaitingAttempt => AttemptState::AwaitingAttempt,
356 SerdeAttemptState::AwaitingUnsignedVtxoTree { cosign_keys, _legacy_secret_nonces: _, unlock_hash } => {
357 AttemptState::AwaitingUnsignedVtxoTree {
358 cosign_keys: cosign_keys.into_owned(),
359 unlock_hash: unlock_hash,
360 }
361 },
362 SerdeAttemptState::AwaitingFinishedRound { unsigned_round_tx, vtxos_spec, unlock_hash } => {
363 AttemptState::AwaitingFinishedRound {
364 unsigned_round_tx: unsigned_round_tx.into_owned(),
365 vtxos_spec: vtxos_spec.into_owned(),
366 unlock_hash: unlock_hash,
367 }
368 },
369 }
370 }
371}
372
373#[derive(Debug, Serialize, Deserialize)]
375enum SerdeRoundFlowState<'a> {
376 NonInteractivePending {
378 unlock_hash: UnlockHash,
379 },
380
381 InteractivePending,
383 InteractiveOngoing {
385 round_seq: RoundSeq,
386 attempt_seq: usize,
387 state: SerdeAttemptState<'a>,
388 },
389
390 Finished {
392 funding_tx: Cow<'a, Transaction>,
393 unlock_hash: UnlockHash,
394 },
395
396 Failed {
398 error: Cow<'a, str>,
399 },
400
401 Canceled,
403}
404
405impl<'a> From<&'a RoundFlowState> for SerdeRoundFlowState<'a> {
406 fn from(state: &'a RoundFlowState) -> Self {
407 match state {
408 RoundFlowState::NonInteractivePending { unlock_hash } => {
409 SerdeRoundFlowState::NonInteractivePending {
410 unlock_hash: *unlock_hash,
411 }
412 },
413 RoundFlowState::InteractivePending => SerdeRoundFlowState::InteractivePending,
414 RoundFlowState::InteractiveOngoing { round_seq, attempt_seq, state } => {
415 SerdeRoundFlowState::InteractiveOngoing {
416 round_seq: *round_seq,
417 attempt_seq: *attempt_seq,
418 state: state.into(),
419 }
420 },
421 RoundFlowState::Finished { funding_tx, unlock_hash } => {
422 SerdeRoundFlowState::Finished {
423 funding_tx: Cow::Borrowed(funding_tx),
424 unlock_hash: *unlock_hash,
425 }
426 },
427 RoundFlowState::Failed { error } => {
428 SerdeRoundFlowState::Failed {
429 error: Cow::Borrowed(error),
430 }
431 },
432 RoundFlowState::Canceled => SerdeRoundFlowState::Canceled,
433 }
434 }
435}
436
437impl<'a> From<SerdeRoundFlowState<'a>> for RoundFlowState {
438 fn from(state: SerdeRoundFlowState<'a>) -> Self {
439 match state {
440 SerdeRoundFlowState::NonInteractivePending { unlock_hash } => {
441 RoundFlowState::NonInteractivePending { unlock_hash }
442 },
443 SerdeRoundFlowState::InteractivePending => RoundFlowState::InteractivePending,
444 SerdeRoundFlowState::InteractiveOngoing { round_seq, attempt_seq, state } => {
445 RoundFlowState::InteractiveOngoing {
446 round_seq: round_seq,
447 attempt_seq: attempt_seq,
448 state: state.into(),
449 }
450 },
451 SerdeRoundFlowState::Finished { funding_tx, unlock_hash } => {
452 RoundFlowState::Finished {
453 funding_tx: funding_tx.into_owned(),
454 unlock_hash,
455 }
456 },
457 SerdeRoundFlowState::Failed { error } => {
458 RoundFlowState::Failed {
459 error: error.into_owned(),
460 }
461 },
462 SerdeRoundFlowState::Canceled => RoundFlowState::Canceled,
463 }
464 }
465}
466
467#[derive(Debug, Serialize, Deserialize)]
469pub struct SerdeRoundState<'a> {
470 done: bool,
471 participation: SerdeRoundParticipation<'a>,
472 movement_id: Option<MovementId>,
473 flow: SerdeRoundFlowState<'a>,
474 #[serde(with = "ark::encode::serde::cow::vec")]
475 new_vtxos: Cow<'a, [Vtxo<Full>]>,
476 sent_forfeit_sigs: bool,
477}
478
479impl<'a> From<&'a RoundState> for SerdeRoundState<'a> {
480 fn from(state: &'a RoundState) -> Self {
481 Self {
482 done: state.done,
483 participation: (&state.participation).into(),
484 movement_id: state.movement_id,
485 flow: (&state.flow).into(),
486 new_vtxos: Cow::Borrowed(&state.new_vtxos),
487 sent_forfeit_sigs: state.sent_forfeit_sigs,
488 }
489 }
490}
491
492impl<'a> From<SerdeRoundState<'a>> for RoundState {
493 fn from(state: SerdeRoundState<'a>) -> Self {
494 Self {
495 done: state.done,
496 participation: state.participation.into(),
497 movement_id: state.movement_id,
498 flow: state.flow.into(),
499 new_vtxos: state.new_vtxos.into_owned(),
500 sent_forfeit_sigs: state.sent_forfeit_sigs,
501 }
502 }
503}
504
505#[cfg(test)]
506mod test {
507 use crate::exit::{ExitState, ExitTxOrigin};
508 use crate::vtxo::VtxoState;
509 use super::SerdeAttemptState;
510
511 #[test]
512 fn test_serialized_structs() {
515 let serialised = r#"{"type":"start","tip_height":119}"#;
517 serde_json::from_str::<ExitState>(serialised).unwrap();
518 let serialised = r#"{"type":"awaiting-delta","tip_height":122,"confirmed_block":"122:3cdd30fc942301a74666c481beb82050ccd182050aee3c92d2197e8cad427b8f","claimable_height":134}"#;
519 serde_json::from_str::<ExitState>(serialised).unwrap();
520 let serialised = r#"{"type":"claimable","tip_height":134,"claimable_since": "134:71fe28f4c803a4c46a3a93d0a9937507d7c20b4bd9586ba317d1109e1aebaac9","last_scanned_block":null}"#;
521 serde_json::from_str::<ExitState>(serialised).unwrap();
522 let serialised = r#"{"type":"claimable","tip_height":140,"claimable_since": "134:71fe28f4c803a4c46a3a93d0a9937507d7c20b4bd9586ba317d1109e1aebaac9","last_scanned_block": "139:c6e9eb8c8b4d9620bbe87b94d7fb0fbb8eef1c4a8c1e60f7b3a5d80fe26b0d3e"}"#;
523 serde_json::from_str::<ExitState>(serialised).unwrap();
524 let serialised = r#"{"type":"claim-in-progress","tip_height":134, "claimable_since": "134:6585896bdda6f08d924bf45cc2b16418af56703b3c50930e4dccbc1728d3800a","claim_txid":"599347c35870bd36f7acb22b81f9ffa8b911d9b5e94834858aebd3ec09339f4c"}"#;
525 serde_json::from_str::<ExitState>(serialised).unwrap();
526 let serialised = r#"{"type":"claimed","tip_height":134,"txid":"599347c35870bd36f7acb22b81f9ffa8b911d9b5e94834858aebd3ec09339f4c","block": "122:3cdd30fc942301a74666c481beb82050ccd182050aee3c92d2197e8cad427b8f"}"#;
527 serde_json::from_str::<ExitState>(serialised).unwrap();
528 let serialised = r#"{"type":"vtxo-already-spent","tip_height":135}"#;
529 serde_json::from_str::<ExitState>(serialised).unwrap();
530 let serialised = r#"{"type":"canceled","tip_height":135}"#;
531 serde_json::from_str::<ExitState>(serialised).unwrap();
532
533 let serialised = r#"{"type":"processing","tip_height":119,"transactions":[{"txid":"9fd34b8c556dd9954bda80ba2cf3474a372702ebc31a366639483e78417c6812","status":{"type":"verify-inputs"}}]}"#;
537 serde_json::from_str::<ExitState>(serialised).unwrap();
538 let serialised = r#"{"type":"processing","tip_height":119,"transactions":[{"txid":"9fd34b8c556dd9954bda80ba2cf3474a372702ebc31a366639483e78417c6812","status":{"type":"awaiting-input-confirmation","txids":["ddfe11920358d1a1fae970dc80459c60675bf1392896f69b103fc638313751de"]}}]}"#;
539 serde_json::from_str::<ExitState>(serialised).unwrap();
540 let serialised = r#"{"type":"processing","tip_height":119,"transactions":[{"txid":"9fd34b8c556dd9954bda80ba2cf3474a372702ebc31a366639483e78417c6812","status":{"type":"awaiting-cpfp-broadcast"}}]}"#;
541 serde_json::from_str::<ExitState>(serialised).unwrap();
542 let serialised = r#"{"type":"processing","tip_height":119,"transactions":[{"txid":"9fd34b8c556dd9954bda80ba2cf3474a372702ebc31a366639483e78417c6812","status":{"type":"awaiting-confirmation","child_txid":"ddfe11920358d1a1fae970dc80459c60675bf1392896f69b103fc638313751de","origin":{"type":"wallet","confirmed_in":null}}}]}"#;
543 serde_json::from_str::<ExitState>(serialised).unwrap();
544 let serialised = r#"{"type":"processing","tip_height":119,"transactions":[{"txid":"9fd34b8c556dd9954bda80ba2cf3474a372702ebc31a366639483e78417c6812","status":{"type":"awaiting-confirmation","child_txid":"ddfe11920358d1a1fae970dc80459c60675bf1392896f69b103fc638313751de","origin":{"type":"mempool","fee_rate_kwu":25000,"total_fee":27625}}}]}"#;
545 serde_json::from_str::<ExitState>(serialised).unwrap();
546 let serialised = r#"{"type":"processing","tip_height":134,"transactions":[{"txid":"9fd34b8c556dd9954bda80ba2cf3474a372702ebc31a366639483e78417c6812","status":{"type":"confirmed","child_txid":"ddfe11920358d1a1fae970dc80459c60675bf1392896f69b103fc638313751de","block":"122:3cdd30fc942301a74666c481beb82050ccd182050aee3c92d2197e8cad427b8f","origin":{"type":"block","confirmed_in":"122:3cdd30fc942301a74666c481beb82050ccd182050aee3c92d2197e8cad427b8f"}}}]}"#;
547 serde_json::from_str::<ExitState>(serialised).unwrap();
548
549 let serialized = r#"{"type":"wallet","confirmed_in":null}"#;
551 serde_json::from_str::<ExitTxOrigin>(serialized).unwrap();
552 let serialized = r#"{"type":"wallet","confirmed_in": "134:71fe28f4c803a4c46a3a93d0a9937507d7c20b4bd9586ba317d1109e1aebaac9"}"#;
553 serde_json::from_str::<ExitTxOrigin>(serialized).unwrap();
554 let serialized = r#"{"type":"mempool"}"#;
556 serde_json::from_str::<ExitTxOrigin>(serialized).unwrap();
557 let serialized = r#"{"type":"mempool","fee_rate_kwu":25000,"total_fee":27625}"#;
559 serde_json::from_str::<ExitTxOrigin>(serialized).unwrap();
560 let serialized = r#"{"type":"block","confirmed_in": "134:71fe28f4c803a4c46a3a93d0a9937507d7c20b4bd9586ba317d1109e1aebaac9"}"#;
561 serde_json::from_str::<ExitTxOrigin>(serialized).unwrap();
562
563 let serialised = r#"{"type": "spendable"}"#;
565 serde_json::from_str::<VtxoState>(serialised).unwrap();
566 let serialised = r#"{"type": "spent"}"#;
567 serde_json::from_str::<VtxoState>(serialised).unwrap();
568 let serialised = r#"{"type": "exited"}"#;
569 serde_json::from_str::<VtxoState>(serialised).unwrap();
570 let serialised = r#"{"type": "locked", "movement_id": null}"#;
572 serde_json::from_str::<VtxoState>(serialised).unwrap();
573 let serialised = r#"{"type": "locked", "movement_id": 42}"#;
574 serde_json::from_str::<VtxoState>(serialised).unwrap();
575 let serialised = r#"{"type": "locked", "holder": null}"#;
577 serde_json::from_str::<VtxoState>(serialised).unwrap();
578 let serialised = r#"{"type": "locked", "holder": {"type": "movement", "id": 42}}"#;
579 serde_json::from_str::<VtxoState>(serialised).unwrap();
580 let serialised = r#"{"type": "locked", "holder": {"type": "action", "id": "test-action-id"}}"#;
581 serde_json::from_str::<VtxoState>(serialised).unwrap();
582
583 let serialised = r#"{"AwaitingUnsignedVtxoTree":{"cosign_keys":[],"secret_nonces":[[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]],"unlock_hash":"0000000000000000000000000000000000000000000000000000000000000000"}}"#;
586 serde_json::from_str::<SerdeAttemptState>(serialised).unwrap();
587 let serialised = r#"{"AwaitingUnsignedVtxoTree":{"cosign_keys":[],"unlock_hash":"0000000000000000000000000000000000000000000000000000000000000000"}}"#;
588 serde_json::from_str::<SerdeAttemptState>(serialised).unwrap();
589 }
590
591 #[test]
595 fn test_serialized_round_state_msgpack() {
596 use bitcoin::hex::FromHex;
597
598 let serialised = "81b84177616974696e67556e7369676e65645674786f5472656593909191dc0084000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4200000000000000000000000000000000000000000000000000000000000000000";
600 rmp_serde::from_slice::<SerdeAttemptState>(
601 &Vec::<u8>::from_hex(serialised).unwrap(),
602 ).unwrap();
603 let serialised = "81b84177616974696e67556e7369676e65645674786f54726565939090c4200000000000000000000000000000000000000000000000000000000000000000";
605 rmp_serde::from_slice::<SerdeAttemptState>(
606 &Vec::<u8>::from_hex(serialised).unwrap(),
607 ).unwrap();
608 }
609}