1use std::iter::FromIterator;
4use std::time::Duration;
5
6use crate::prelude::*;
7use holo_hash::ActionHash;
8use holo_hash::AgentPubKey;
9use holo_hash::EntryHash;
10use holochain_serialized_bytes::SerializedBytesError;
11use holochain_timestamp::Timestamp;
12
13pub const SESSION_ACTION_TIME_OFFSET: Duration = Duration::from_millis(1000);
16
17pub const SESSION_TIME_FUTURE_MAX: Duration =
20 Duration::from_millis(5000 + SESSION_ACTION_TIME_OFFSET.as_millis() as u64);
21
22pub const MIN_COUNTERSIGNING_AGENTS: usize = 2;
24pub const MAX_COUNTERSIGNING_AGENTS: usize = 8;
26
27pub use error::CounterSigningError;
28mod error;
29
30#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
32pub struct CounterSigningSessionTimes {
33 pub start: Timestamp,
35 pub end: Timestamp,
37}
38
39impl CounterSigningSessionTimes {
40 pub fn try_new(start: Timestamp, end: Timestamp) -> Result<Self, CounterSigningError> {
42 let session_times = Self { start, end };
43 session_times.check_integrity()?;
44 Ok(session_times)
45 }
46
47 pub fn check_integrity(&self) -> Result<(), CounterSigningError> {
49 let times_are_valid = &Timestamp::from_micros(0) < self.start()
50 && self.start()
51 <= &(self.end() - SESSION_ACTION_TIME_OFFSET).map_err(|_| {
52 CounterSigningError::CounterSigningSessionTimes((*self).clone())
53 })?;
54 if times_are_valid {
55 Ok(())
56 } else {
57 Err(CounterSigningError::CounterSigningSessionTimes(
58 (*self).clone(),
59 ))
60 }
61 }
62
63 pub fn start(&self) -> &Timestamp {
65 &self.start
66 }
67
68 #[cfg(feature = "test_utils")]
70 pub fn start_mut(&mut self) -> &mut Timestamp {
71 &mut self.start
72 }
73
74 pub fn end(&self) -> &Timestamp {
76 &self.end
77 }
78
79 #[cfg(feature = "test_utils")]
81 pub fn end_mut(&mut self) -> &mut Timestamp {
82 &mut self.end
83 }
84}
85
86#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Hash)]
88pub struct PreflightBytes(#[serde(with = "serde_bytes")] pub Vec<u8>);
89
90#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
93pub struct Role(pub u8);
94
95impl Role {
96 pub fn new(role: u8) -> Self {
98 Self(role)
99 }
100}
101
102pub type CounterSigningAgents = Vec<(AgentPubKey, Vec<Role>)>;
104
105#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
109pub struct PreflightRequest {
110 pub app_entry_hash: EntryHash,
113 pub signing_agents: CounterSigningAgents,
115 pub optional_signing_agents: CounterSigningAgents,
120 pub minimum_optional_signing_agents: u8,
123 pub enzymatic: bool,
127 pub session_times: CounterSigningSessionTimes,
130 pub action_base: ActionBase,
133 pub preflight_bytes: PreflightBytes,
135}
136
137impl PreflightRequest {
138 #[allow(clippy::too_many_arguments)]
140 pub fn try_new(
141 app_entry_hash: EntryHash,
142 signing_agents: CounterSigningAgents,
143 optional_signing_agents: CounterSigningAgents,
144 minimum_optional_signing_agents: u8,
145 enzymatic: bool,
146 session_times: CounterSigningSessionTimes,
147 action_base: ActionBase,
148 preflight_bytes: PreflightBytes,
149 ) -> Result<Self, CounterSigningError> {
150 let preflight_request = Self {
151 app_entry_hash,
152 signing_agents,
153 optional_signing_agents,
154 minimum_optional_signing_agents,
155 enzymatic,
156 session_times,
157 action_base,
158 preflight_bytes,
159 };
160 preflight_request.check_integrity()?;
161 Ok(preflight_request)
162 }
163 pub fn check_integrity(&self) -> Result<(), CounterSigningError> {
165 self.check_enzyme()?;
166 self.session_times.check_integrity()?;
167 self.check_agents()?;
168 Ok(())
169 }
170
171 pub fn check_agents_dupes(&self) -> Result<(), CounterSigningError> {
173 let v: Vec<AgentPubKey> = self
174 .signing_agents
175 .iter()
176 .map(|(agent, _roles)| agent.clone())
177 .collect();
178 if std::collections::HashSet::<AgentPubKey>::from_iter(v.clone()).len()
179 == self.signing_agents.len()
180 {
181 Ok(())
182 } else {
183 Err(CounterSigningError::AgentsDupes(v))
184 }
185 }
186
187 pub fn check_agents_len(&self) -> Result<(), CounterSigningError> {
189 if MIN_COUNTERSIGNING_AGENTS <= self.signing_agents.len()
190 && self.signing_agents.len() <= MAX_COUNTERSIGNING_AGENTS
191 {
192 Ok(())
193 } else {
194 Err(CounterSigningError::AgentsLength(self.signing_agents.len()))
195 }
196 }
197
198 pub fn check_agents_optional(&self) -> Result<(), CounterSigningError> {
200 if self.minimum_optional_signing_agents as usize > self.optional_signing_agents.len() {
201 return Err(CounterSigningError::OptionalAgentsLength(
202 self.minimum_optional_signing_agents,
203 self.optional_signing_agents.len(),
204 ));
205 }
206 if ((self.minimum_optional_signing_agents * 2) as usize)
208 < self.optional_signing_agents.len()
209 && !self.optional_signing_agents.is_empty()
210 {
211 return Err(CounterSigningError::MinOptionalAgents(
212 self.minimum_optional_signing_agents,
213 self.optional_signing_agents.len(),
214 ));
215 }
216 Ok(())
217 }
218
219 pub fn check_agents(&self) -> Result<(), CounterSigningError> {
221 self.check_agents_dupes()?;
222 self.check_agents_len()?;
223 self.check_agents_optional()?;
224 Ok(())
225 }
226
227 pub fn check_enzyme(&self) -> Result<(), CounterSigningError> {
229 if self.enzymatic
232 && !self.optional_signing_agents.is_empty()
233 && self.signing_agents.first() != self.optional_signing_agents.first()
234 {
235 return Err(CounterSigningError::EnzymeMismatch(
236 self.signing_agents.first().cloned(),
237 self.optional_signing_agents.first().cloned(),
238 ));
239 }
240 if !self.enzymatic && !self.optional_signing_agents.is_empty() {
241 return Err(CounterSigningError::NonEnzymaticOptionalSigners);
242 }
243 Ok(())
244 }
245
246 pub fn fingerprint(&self) -> Result<Vec<u8>, SerializedBytesError> {
248 Ok(holo_hash::encode::blake2b_256(
249 &holochain_serialized_bytes::encode(self)?,
250 ))
251 }
252}
253
254#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
257pub struct PreflightResponse {
258 pub request: PreflightRequest,
260 pub agent_state: CounterSigningAgentState,
262 pub signature: Signature,
264}
265
266impl PreflightResponse {
267 pub fn try_new(
269 request: PreflightRequest,
270 agent_state: CounterSigningAgentState,
271 signature: Signature,
272 ) -> Result<Self, CounterSigningError> {
273 let preflight_response = Self {
274 request,
275 agent_state,
276 signature,
277 };
278 preflight_response.check_integrity()?;
279 Ok(preflight_response)
280 }
281
282 pub fn check_integrity(&self) -> Result<(), CounterSigningError> {
284 self.request().check_integrity()
285 }
286
287 pub fn encode_fields_for_signature(
289 request: &PreflightRequest,
290 agent_state: &CounterSigningAgentState,
291 ) -> Result<Vec<u8>, SerializedBytesError> {
292 holochain_serialized_bytes::encode(&(request, agent_state))
293 }
294
295 pub fn encode_for_signature(&self) -> Result<Vec<u8>, SerializedBytesError> {
297 Self::encode_fields_for_signature(&self.request, &self.agent_state)
298 }
299
300 pub fn request(&self) -> &PreflightRequest {
302 &self.request
303 }
304
305 #[cfg(feature = "test_utils")]
307 pub fn request_mut(&mut self) -> &mut PreflightRequest {
308 &mut self.request
309 }
310
311 pub fn agent_state(&self) -> &CounterSigningAgentState {
313 &self.agent_state
314 }
315
316 #[cfg(feature = "test_utils")]
318 pub fn agent_state_mut(&mut self) -> &mut CounterSigningAgentState {
319 &mut self.agent_state
320 }
321
322 pub fn signature(&self) -> &Signature {
324 &self.signature
325 }
326
327 #[cfg(feature = "test_utils")]
329 pub fn signature_mut(&mut self) -> &mut Signature {
330 &mut self.signature
331 }
332}
333
334#[derive(Debug, serde::Serialize, serde::Deserialize)]
336#[allow(clippy::large_enum_variant)]
337pub enum PreflightRequestAcceptance {
338 Accepted(PreflightResponse),
340 UnacceptableFutureStart,
342 UnacceptableAgentNotFound,
344 AnotherSessionIsInProgress,
346 Invalid(String),
348}
349
350#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
353pub struct CounterSigningAgentState {
354 agent_index: u8,
356 chain_top: ActionHash,
358 action_seq: u32,
360}
361
362impl CounterSigningAgentState {
363 pub fn new(agent_index: u8, chain_top: ActionHash, action_seq: u32) -> Self {
365 Self {
366 agent_index,
367 chain_top,
368 action_seq,
369 }
370 }
371
372 pub fn agent_index(&self) -> &u8 {
374 &self.agent_index
375 }
376
377 #[cfg(feature = "test_utils")]
379 pub fn agent_index_mut(&mut self) -> &mut u8 {
380 &mut self.agent_index
381 }
382
383 pub fn chain_top(&self) -> &ActionHash {
385 &self.chain_top
386 }
387
388 #[cfg(feature = "test_utils")]
390 pub fn chain_top_mut(&mut self) -> &mut ActionHash {
391 &mut self.chain_top
392 }
393
394 pub fn action_seq(&self) -> &u32 {
396 &self.action_seq
397 }
398
399 #[cfg(feature = "test_utils")]
401 pub fn action_seq_mut(&mut self) -> &mut u32 {
402 &mut self.action_seq
403 }
404}
405
406#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
409pub enum ActionBase {
410 Create(CreateBase),
412 Update(UpdateBase),
414 }
419
420#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
422pub struct CreateBase {
423 entry_type: EntryType,
424}
425
426impl CreateBase {
427 pub fn new(entry_type: EntryType) -> Self {
429 Self { entry_type }
430 }
431}
432
433#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
435pub struct UpdateBase {
436 pub original_action_address: ActionHash,
438 pub original_entry_address: EntryHash,
440 pub entry_type: EntryType,
442}
443
444impl Action {
445 pub fn from_countersigning_data(
447 entry_hash: EntryHash,
448 session_data: &CounterSigningSessionData,
449 author: AgentPubKey,
450 weight: EntryRateWeight,
451 ) -> Result<Self, CounterSigningError> {
452 let agent_state = session_data.agent_state_for_agent(&author)?;
453 Ok(match &session_data.preflight_request().action_base {
454 ActionBase::Create(base) => Action::Create(Create {
455 author,
456 timestamp: session_data.to_timestamp(),
457 action_seq: agent_state.action_seq + 1,
458 prev_action: agent_state.chain_top.clone(),
459 entry_type: base.entry_type.clone(),
460 weight,
461 entry_hash,
462 }),
463 ActionBase::Update(base) => Action::Update(Update {
464 author,
465 timestamp: session_data.to_timestamp(),
466 action_seq: agent_state.action_seq + 1,
467 prev_action: agent_state.chain_top.clone(),
468 original_action_address: base.original_action_address.clone(),
469 original_entry_address: base.original_entry_address.clone(),
470 entry_type: base.entry_type.clone(),
471 weight,
472 entry_hash,
473 }),
474 })
475 }
476}
477
478#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
480pub struct CounterSigningSessionData {
481 pub preflight_request: PreflightRequest,
483 pub responses: Vec<(CounterSigningAgentState, Signature)>,
485 pub optional_responses: Vec<(CounterSigningAgentState, Signature)>,
487}
488
489impl CounterSigningSessionData {
490 pub fn try_from_responses(
492 responses: Vec<PreflightResponse>,
493 optional_responses: Vec<PreflightResponse>,
494 ) -> Result<Self, CounterSigningError> {
495 let preflight_request = responses
496 .first()
497 .ok_or(CounterSigningError::MissingResponse)?
498 .to_owned()
499 .request;
500 let convert_responses =
501 |rs: Vec<PreflightResponse>| -> Vec<(CounterSigningAgentState, Signature)> {
502 rs.into_iter()
503 .map(|response| (response.agent_state.clone(), response.signature))
504 .collect()
505 };
506 let responses = convert_responses(responses);
507 let optional_responses = convert_responses(optional_responses);
508 Ok(Self {
509 preflight_request,
510 responses,
511 optional_responses,
512 })
513 }
514
515 pub fn agent_state_for_agent(
517 &self,
518 agent: &AgentPubKey,
519 ) -> Result<&CounterSigningAgentState, CounterSigningError> {
520 match self
521 .preflight_request
522 .signing_agents
523 .iter()
524 .position(|(pubkey, _)| pubkey == agent)
525 {
526 Some(agent_index) => match self.responses.get(agent_index) {
527 Some((agent_state, _)) => Ok(agent_state),
528 None => Err(CounterSigningError::AgentIndexOutOfBounds),
529 },
530 None => Err(CounterSigningError::AgentIndexOutOfBounds),
531 }
532 }
533
534 pub fn build_action_set(
538 &self,
539 entry_hash: EntryHash,
540 weight: EntryRateWeight,
541 ) -> Result<Vec<Action>, CounterSigningError> {
542 let mut actions = vec![];
543 let mut build_actions = |countersigning_agents: &CounterSigningAgents| -> Result<(), _> {
544 for (agent, _role) in countersigning_agents.iter() {
545 actions.push(Action::from_countersigning_data(
546 entry_hash.clone(),
547 self,
548 agent.clone(),
549 weight.clone(),
550 )?);
551 }
552 Ok(())
553 };
554 build_actions(&self.preflight_request.signing_agents)?;
555 build_actions(&self.preflight_request.optional_signing_agents)?;
556 Ok(actions)
557 }
558
559 pub fn try_new(
561 preflight_request: PreflightRequest,
562 responses: Vec<(CounterSigningAgentState, Signature)>,
563 optional_responses: Vec<(CounterSigningAgentState, Signature)>,
564 ) -> Result<Self, CounterSigningError> {
565 let session_data = Self {
566 preflight_request,
567 responses,
568 optional_responses,
569 };
570 session_data.check_integrity()?;
571 Ok(session_data)
572 }
573
574 pub fn check_integrity(&self) -> Result<(), CounterSigningError> {
576 self.check_responses_indexes()
577 }
578
579 pub fn check_responses_indexes(&self) -> Result<(), CounterSigningError> {
582 if self.preflight_request().signing_agents.len() != self.responses().len() {
583 Err(CounterSigningError::CounterSigningSessionResponsesLength(
584 self.responses().len(),
585 self.preflight_request().signing_agents.len(),
586 ))
587 } else {
588 for (i, (response, _response_signature)) in self.responses().iter().enumerate() {
589 if *response.agent_index() as usize != i {
590 return Err(CounterSigningError::CounterSigningSessionResponsesOrder(
591 *response.agent_index(),
592 i,
593 ));
594 }
595 }
596 Ok(())
597 }
598 }
599
600 pub fn to_timestamp(&self) -> Timestamp {
603 (self.preflight_request().session_times.start() + SESSION_ACTION_TIME_OFFSET)
604 .unwrap_or(Timestamp::MAX)
605 }
606
607 pub fn preflight_request(&self) -> &PreflightRequest {
609 &self.preflight_request
610 }
611
612 #[cfg(feature = "test_utils")]
614 pub fn preflight_request_mut(&mut self) -> &mut PreflightRequest {
615 &mut self.preflight_request
616 }
617
618 pub fn signing_agents(&self) -> impl Iterator<Item = &AgentPubKey> {
620 self.preflight_request.signing_agents.iter().map(|(a, _)| a)
621 }
622
623 pub fn responses(&self) -> &Vec<(CounterSigningAgentState, Signature)> {
625 &self.responses
626 }
627
628 #[cfg(feature = "test_utils")]
630 pub fn responses_mut(&mut self) -> &mut Vec<(CounterSigningAgentState, Signature)> {
631 &mut self.responses
632 }
633}
634
635#[cfg(test)]
636mod test {
637 use super::CounterSigningError;
638 use super::CounterSigningSessionTimes;
639 use super::PreflightRequest;
640 use super::SESSION_ACTION_TIME_OFFSET;
641 use crate::CounterSigningSessionData;
642 use crate::Role;
643 use crate::Signature;
644 use crate::{
645 ActionBase, AppEntryDef, CounterSigningAgentState, CreateBase, EntryType, EntryVisibility,
646 PreflightBytes,
647 };
648 use fixt::*;
649 use holo_hash::fixt::ActionHashFixturator;
650 use holo_hash::fixt::AgentPubKeyFixturator;
651 use holo_hash::fixt::EntryHashFixturator;
652 use holochain_timestamp::Timestamp;
653 use std::time::Duration;
654
655 fn test_preflight_request() -> PreflightRequest {
656 let mut request = PreflightRequest::try_new(
657 fixt!(EntryHash),
658 vec![(fixt!(AgentPubKey), vec![]), (fixt!(AgentPubKey), vec![])],
659 vec![],
660 0,
661 false,
662 CounterSigningSessionTimes::try_new(
663 Timestamp::now(),
664 (Timestamp::now() + Duration::from_secs(30)).unwrap(),
665 )
666 .unwrap(),
667 ActionBase::Create(CreateBase::new(EntryType::App(AppEntryDef {
668 entry_index: 0.into(),
669 zome_index: 0.into(),
670 visibility: EntryVisibility::Public,
671 }))),
672 PreflightBytes(vec![]),
673 )
674 .unwrap();
675 request.signing_agents.clear();
676
677 request
678 }
679
680 #[test]
681 fn test_check_countersigning_session_times() {
682 let mut session_times = CounterSigningSessionTimes {
683 start: Timestamp(0),
684 end: Timestamp(0),
685 };
686
687 assert!(matches!(
689 session_times.check_integrity(),
690 Err(CounterSigningError::CounterSigningSessionTimes(_))
691 ));
692
693 *session_times.end_mut() =
695 (session_times.end() + core::time::Duration::from_millis(1)).unwrap();
696 assert!(matches!(
697 session_times.check_integrity(),
698 Err(CounterSigningError::CounterSigningSessionTimes(_))
699 ));
700
701 *session_times.end_mut() = (session_times.end() + SESSION_ACTION_TIME_OFFSET).unwrap();
703 assert!(matches!(
704 session_times.check_integrity(),
705 Err(CounterSigningError::CounterSigningSessionTimes(_))
706 ));
707
708 *session_times.start_mut() =
710 (session_times.start() + core::time::Duration::from_millis(1)).unwrap();
711 session_times.check_integrity().unwrap();
712
713 *session_times.start_mut() =
715 (session_times.start() + core::time::Duration::from_millis(1)).unwrap();
716 assert!(matches!(
717 session_times.check_integrity(),
718 Err(CounterSigningError::CounterSigningSessionTimes(_))
719 ));
720 }
721
722 #[test]
723 fn test_check_countersigning_preflight_request_optional_agents() {
724 let mut preflight_request = test_preflight_request();
725
726 preflight_request.check_agents_optional().unwrap();
728
729 let alice = fixt!(AgentPubKey);
731
732 preflight_request
733 .optional_signing_agents
734 .push((alice.clone(), vec![]));
735
736 assert!(matches!(
737 preflight_request.check_agents_optional(),
738 Err(CounterSigningError::MinOptionalAgents(0, 1))
739 ));
740
741 preflight_request.minimum_optional_signing_agents = 1;
744
745 preflight_request.check_agents_optional().unwrap();
746
747 preflight_request
749 .optional_signing_agents
750 .push((alice.clone(), vec![]));
751
752 preflight_request.check_agents_optional().unwrap();
753
754 preflight_request
756 .optional_signing_agents
757 .push((alice.clone(), vec![]));
758
759 assert!(matches!(
760 preflight_request.check_agents_optional(),
761 Err(CounterSigningError::MinOptionalAgents(1, 3))
762 ));
763
764 preflight_request.minimum_optional_signing_agents = 2;
766
767 preflight_request.check_agents_optional().unwrap();
768
769 preflight_request.minimum_optional_signing_agents = 4;
771
772 assert!(matches!(
773 preflight_request.check_agents_optional(),
774 Err(CounterSigningError::OptionalAgentsLength(4, 3))
775 ));
776 }
777
778 #[test]
779 fn test_check_countersigning_preflight_request_enzyme() {
780 let mut preflight_request = test_preflight_request();
781
782 preflight_request.check_enzyme().unwrap();
784
785 let alice = fixt!(AgentPubKey);
786 let bob = fixt!(AgentPubKey);
787
788 preflight_request
790 .signing_agents
791 .push((alice.clone(), vec![]));
792
793 preflight_request.check_enzyme().unwrap();
794
795 preflight_request
797 .optional_signing_agents
798 .push((alice.clone(), vec![]));
799
800 assert!(matches!(
801 preflight_request.check_enzyme(),
802 Err(CounterSigningError::NonEnzymaticOptionalSigners),
803 ));
804
805 preflight_request.optional_signing_agents = vec![];
807 preflight_request.enzymatic = true;
808
809 preflight_request.check_enzyme().unwrap();
810
811 preflight_request.optional_signing_agents = vec![(alice.clone(), vec![])];
813
814 preflight_request.check_enzyme().unwrap();
815
816 preflight_request.optional_signing_agents = vec![(bob.clone(), vec![])];
818
819 assert!(matches!(
820 preflight_request.check_enzyme(),
821 Err(CounterSigningError::EnzymeMismatch(_, _)),
822 ));
823 }
824
825 #[test]
826 fn test_check_countersigning_preflight_request_agents_len() {
827 let mut preflight_request = test_preflight_request();
828
829 assert!(matches!(
831 preflight_request.check_agents_len(),
832 Err(CounterSigningError::AgentsLength(_))
833 ));
834
835 let alice = fixt!(AgentPubKey);
837 preflight_request
838 .signing_agents
839 .push((alice.clone(), vec![]));
840
841 assert!(matches!(
842 preflight_request.check_agents_len(),
843 Err(CounterSigningError::AgentsLength(_))
844 ));
845
846 let bob = fixt!(AgentPubKey);
848 preflight_request.signing_agents.push((bob.clone(), vec![]));
849
850 preflight_request.check_agents_len().unwrap();
851 }
852
853 #[test]
854 fn test_check_countersigning_preflight_request_agents_dupes() {
855 let mut preflight_request = test_preflight_request();
856
857 let alice = fixt!(AgentPubKey);
858 let bob = fixt!(AgentPubKey);
859
860 preflight_request.check_agents_dupes().unwrap();
861
862 preflight_request
863 .signing_agents
864 .push((alice.clone(), vec![]));
865 preflight_request.check_agents_dupes().unwrap();
866
867 preflight_request.signing_agents.push((bob.clone(), vec![]));
868 preflight_request.check_agents_dupes().unwrap();
869
870 preflight_request
872 .signing_agents
873 .push((alice.clone(), vec![Role::new(0_u8)]));
874 assert!(matches!(
875 preflight_request.check_agents_dupes(),
876 Err(CounterSigningError::AgentsDupes(_))
877 ));
878 }
879
880 #[test]
881 pub fn test_check_countersigning_session_data_responses_indexes() {
882 let mut session_data =
883 CounterSigningSessionData::try_new(test_preflight_request(), vec![], vec![]).unwrap();
884
885 let alice = fixt!(AgentPubKey);
886 let bob = fixt!(AgentPubKey);
887
888 session_data.check_responses_indexes().unwrap();
890
891 session_data
893 .preflight_request_mut()
894 .signing_agents
895 .push((alice.clone(), vec![]));
896 assert!(matches!(
897 session_data.check_responses_indexes(),
898 Err(CounterSigningError::CounterSigningSessionResponsesLength(
899 _,
900 _
901 ))
902 ));
903
904 session_data
906 .preflight_request_mut()
907 .signing_agents
908 .push((bob.clone(), vec![]));
909
910 let alice_state = CounterSigningAgentState::new(0, fixt!(ActionHash), 0);
911 let alice_signature = Signature(vec![0; 64].try_into().unwrap());
912 let mut bob_state = CounterSigningAgentState::new(0, fixt!(ActionHash), 0);
913 let bob_signature = Signature(vec![1; 64].try_into().unwrap());
914
915 (*session_data.responses_mut()).push((alice_state, alice_signature));
916 (*session_data.responses_mut()).push((bob_state.clone(), bob_signature.clone()));
917
918 assert!(
919 matches!(
920 session_data.check_responses_indexes(),
921 Err(CounterSigningError::CounterSigningSessionResponsesOrder(
922 _,
923 _
924 ))
925 ),
926 "But got: {:?}",
927 session_data.check_responses_indexes()
928 );
929
930 *bob_state.agent_index_mut() = 1;
931 (*session_data.responses_mut()).pop();
932 (*session_data.responses_mut()).push((bob_state, bob_signature));
933 session_data.check_responses_indexes().unwrap()
934 }
935}