1use core::mem::size_of;
25use core::ops::{Deref, DerefMut};
26
27use arrayref::{array_refs, mut_array_refs};
28use solana_account_view::{AccountView, Ref};
29use solana_program_error::ProgramError;
30use solana_pubkey::Pubkey;
31
32use crate::{Role, SettlementAccount, SettlementError};
33
34pub const DISCRIMINATOR: u8 = SettlementAccount::SettlementState.discriminator();
36
37const WIDTH_DISCRIMINATOR: usize = size_of::<u8>();
39
40pub const WIDTH_PUBKEY: usize = size_of::<Pubkey>();
43
44pub const WIDTH_HEADER: usize = WIDTH_DISCRIMINATOR + 4 * WIDTH_PUBKEY;
47
48struct HeaderSlots<'a> {
52 discriminator: &'a [u8; WIDTH_DISCRIMINATOR],
53 manager: &'a [u8; WIDTH_PUBKEY],
54 solver_authority: &'a [u8; WIDTH_PUBKEY],
55 reclaim_authority: &'a [u8; WIDTH_PUBKEY],
56 settlement_owned_order_authority: &'a [u8; WIDTH_PUBKEY],
57}
58
59struct HeaderSlotsMut<'a> {
61 discriminator: &'a mut [u8; WIDTH_DISCRIMINATOR],
62 manager: &'a mut [u8; WIDTH_PUBKEY],
63 solver_authority: &'a mut [u8; WIDTH_PUBKEY],
64 reclaim_authority: &'a mut [u8; WIDTH_PUBKEY],
65 settlement_owned_order_authority: &'a mut [u8; WIDTH_PUBKEY],
66}
67
68#[inline]
70fn header_slots(header: &[u8; WIDTH_HEADER]) -> HeaderSlots<'_> {
71 let (
72 discriminator,
73 manager,
74 solver_authority,
75 reclaim_authority,
76 settlement_owned_order_authority,
77 ) = array_refs![
78 header,
79 WIDTH_DISCRIMINATOR,
80 WIDTH_PUBKEY,
81 WIDTH_PUBKEY,
82 WIDTH_PUBKEY,
83 WIDTH_PUBKEY
84 ];
85 HeaderSlots {
86 discriminator,
87 manager,
88 solver_authority,
89 reclaim_authority,
90 settlement_owned_order_authority,
91 }
92}
93
94#[inline]
96fn header_slots_mut(header: &mut [u8; WIDTH_HEADER]) -> HeaderSlotsMut<'_> {
97 let (
98 discriminator,
99 manager,
100 solver_authority,
101 reclaim_authority,
102 settlement_owned_order_authority,
103 ) = mut_array_refs![
104 header,
105 WIDTH_DISCRIMINATOR,
106 WIDTH_PUBKEY,
107 WIDTH_PUBKEY,
108 WIDTH_PUBKEY,
109 WIDTH_PUBKEY
110 ];
111 HeaderSlotsMut {
112 discriminator,
113 manager,
114 solver_authority,
115 reclaim_authority,
116 settlement_owned_order_authority,
117 }
118}
119
120#[derive(Clone, Debug, Eq, PartialEq)]
122pub struct StateInitArgs {
123 pub manager: Pubkey,
125 pub solver_authority: Pubkey,
127 pub reclaim_authority: Pubkey,
129 pub settlement_owned_order_authority: Pubkey,
131}
132
133pub struct StateAccount<T>(T);
140
141impl<T: Deref<Target = [u8]>> StateAccount<T> {
142 pub fn attach(bytes: T) -> Result<Self, ProgramError> {
146 let header = bytes
147 .first_chunk::<WIDTH_HEADER>()
148 .ok_or(ProgramError::InvalidAccountData)?;
149 if header_slots(header).discriminator != &[DISCRIMINATOR] {
150 return Err(ProgramError::InvalidAccountData);
151 }
152 Ok(Self(bytes))
153 }
154
155 fn header(&self) -> &[u8; WIDTH_HEADER] {
156 self.0
157 .first_chunk::<WIDTH_HEADER>()
158 .expect("header length is guaranteed by any constructor of `StateAccount`")
159 }
160
161 pub fn authority(&self, role: Role) -> Pubkey {
163 let slots = header_slots(self.header());
164 let holder = match role {
165 Role::Manager => slots.manager,
166 Role::SolverAuthority => slots.solver_authority,
167 Role::ReclaimAuthority => slots.reclaim_authority,
168 Role::SettlementOwnedOrderAuthority => slots.settlement_owned_order_authority,
169 };
170 Pubkey::new_from_array(*holder)
171 }
172
173 fn solver_region(&self) -> &[[u8; WIDTH_PUBKEY]] {
177 let region = self.0.get(WIDTH_HEADER..).unwrap_or_default();
178 let (solvers, _partial) = region.as_chunks::<WIDTH_PUBKEY>();
179 solvers
180 }
181
182 pub fn solver_search(&self, solver: &Pubkey) -> Result<usize, usize> {
186 let seek = solver.to_bytes();
187 self.solver_region().binary_search(&seek)
188 }
189
190 pub fn is_solver(&self, solver: &Pubkey) -> bool {
192 self.solver_search(solver).is_ok()
193 }
194
195 pub fn solvers(&self) -> impl Iterator<Item = Pubkey> + '_ {
197 self.solver_region()
198 .iter()
199 .map(|raw| Pubkey::new_from_array(*raw))
200 }
201
202 pub fn grown_len(&self) -> Result<usize, ProgramError> {
211 self.0
212 .len()
213 .checked_add(WIDTH_PUBKEY)
214 .ok_or(ProgramError::ArithmeticOverflow)
215 }
216}
217
218impl<'a> StateAccount<Ref<'a, [u8]>> {
219 pub fn from_account(account: &'a AccountView) -> Result<Self, ProgramError> {
220 Self::attach(account.try_borrow()?)
221 }
222}
223
224impl<T: DerefMut<Target = [u8]>> StateAccount<T> {
225 pub fn initialize(mut bytes: T, args: &StateInitArgs) -> Result<Self, ProgramError> {
231 {
232 let slots = header_slots_mut(
233 bytes
234 .first_chunk_mut::<WIDTH_HEADER>()
235 .ok_or(ProgramError::AccountDataTooSmall)?,
236 );
237 *slots.discriminator = [DISCRIMINATOR];
238 *slots.manager = args.manager.to_bytes();
239 *slots.solver_authority = args.solver_authority.to_bytes();
240 *slots.reclaim_authority = args.reclaim_authority.to_bytes();
241 *slots.settlement_owned_order_authority =
242 args.settlement_owned_order_authority.to_bytes();
243 }
244 Ok(Self(bytes))
245 }
246
247 fn header_mut(&mut self) -> &mut [u8; WIDTH_HEADER] {
248 let bytes: &mut [u8] = &mut self.0;
249 bytes
250 .first_chunk_mut::<WIDTH_HEADER>()
251 .expect("header length is guaranteed by `new`")
252 }
253
254 pub fn set_authority(&mut self, role: Role, new: &Pubkey) {
256 let slots = header_slots_mut(self.header_mut());
257 let holder = match role {
258 Role::Manager => slots.manager,
259 Role::SolverAuthority => slots.solver_authority,
260 Role::ReclaimAuthority => slots.reclaim_authority,
261 Role::SettlementOwnedOrderAuthority => slots.settlement_owned_order_authority,
262 };
263 *holder = new.to_bytes();
264 }
265
266 pub fn insert_solver(&mut self, solver: &Pubkey) -> Result<(), ProgramError> {
276 let (_spare, occupied) = self
277 .solver_region()
278 .split_last()
279 .expect("account grown by one solver slot before insertion");
280 let index = match occupied.binary_search(&solver.to_bytes()) {
281 Ok(_) => return Err(SettlementError::SolverAlreadyExists.into()),
282 Err(index) => index,
283 };
284
285 let data: &mut [u8] = &mut self.0;
288 let occupied_end = data
289 .len()
290 .checked_sub(WIDTH_PUBKEY)
291 .expect("account grown by one solver slot before insertion");
292 let gap = WIDTH_HEADER
293 .checked_add(
294 index
295 .checked_mul(WIDTH_PUBKEY)
296 .expect("insertion index bound by account length"),
297 )
298 .expect("insertion offset bound by account length");
299 let gap_end = gap
300 .checked_add(WIDTH_PUBKEY)
301 .expect("insertion slot bound by account length");
302
303 data.copy_within(gap..occupied_end, gap_end);
304 data[gap..gap_end].copy_from_slice(&solver.to_bytes());
305 Ok(())
306 }
307
308 pub fn remove_solver(&mut self, solver: &Pubkey) -> Result<usize, ProgramError> {
316 let index = match self.solver_region().binary_search(&solver.to_bytes()) {
317 Ok(index) => index,
318 Err(_) => return Err(SettlementError::SolverNotFound.into()),
319 };
320
321 let data: &mut [u8] = &mut self.0;
324 let len = data.len();
325 let offset = WIDTH_HEADER
326 .checked_add(
327 index
328 .checked_mul(WIDTH_PUBKEY)
329 .expect("removal index bound by data length"),
330 )
331 .expect("removal offset bound by data length");
332 let slot_end = offset
333 .checked_add(WIDTH_PUBKEY)
334 .expect("removal slot bound by data length");
335
336 data.copy_within(slot_end..len, offset);
337
338 let updated_length = len
339 .checked_sub(WIDTH_PUBKEY)
340 .expect("a solver was removed, so the length doesn't underflow");
341 Ok(updated_length)
342 }
343}
344
345#[cfg(any(test, feature = "test-fixtures"))]
348pub mod fixtures {
349 use proptest::prelude::*;
350 use solana_pubkey::Pubkey;
351
352 use super::{StateAccount, StateInitArgs, WIDTH_HEADER};
353
354 pub fn state_account_bytes(header: &StateInitArgs, solvers: &[Pubkey]) -> Vec<u8> {
358 let mut sorted = solvers.to_vec();
359 sorted.sort();
360 let mut bytes = vec![0u8; WIDTH_HEADER];
361 StateAccount::initialize(&mut bytes[..], header).expect("header fits");
362 for solver in &sorted {
363 bytes.extend_from_slice(&solver.to_bytes());
364 }
365 bytes
366 }
367
368 pub fn arb_init_params() -> impl Strategy<Value = StateInitArgs> {
370 any::<[[u8; 32]; 4]>().prop_map(
371 |[manager, solver_authority, reclaim_authority, settlement_owned_order_authority]| {
372 StateInitArgs {
373 manager: Pubkey::new_from_array(manager),
374 solver_authority: Pubkey::new_from_array(solver_authority),
375 reclaim_authority: Pubkey::new_from_array(reclaim_authority),
376 settlement_owned_order_authority: Pubkey::new_from_array(
377 settlement_owned_order_authority,
378 ),
379 }
380 },
381 )
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use std::sync::LazyLock;
388
389 use super::*;
390 use crate::fixtures::pubkey_from_seed;
391
392 const DISCRIMINATOR_OFFSET: usize = 0;
394
395 static SAMPLE_INIT_ARGS: LazyLock<StateInitArgs> = LazyLock::new(|| StateInitArgs {
396 manager: pubkey_from_seed("SAMPLE_INIT_ARGS's sample manager"),
397 solver_authority: pubkey_from_seed("SAMPLE_INIT_ARGS's sample solver authority"),
398 reclaim_authority: pubkey_from_seed("SAMPLE_INIT_ARGS's sample reclaim authority"),
399 settlement_owned_order_authority: pubkey_from_seed(
400 "SAMPLE_INIT_ARGS's sample settlement-owned-order authority",
401 ),
402 });
403
404 fn header_bytes() -> [u8; WIDTH_HEADER] {
406 let mut bytes = [0u8; WIDTH_HEADER];
407 StateAccount::initialize(&mut bytes[..], &SAMPLE_INIT_ARGS).expect("header fits");
408 bytes
409 }
410
411 fn state_bytes(solvers: &[Pubkey]) -> Vec<u8> {
414 super::fixtures::state_account_bytes(&SAMPLE_INIT_ARGS, solvers)
415 }
416
417 #[test]
418 fn header_has_the_canonical_wire_layout() {
419 assert_eq!(WIDTH_HEADER, 129);
420
421 let bytes = header_bytes();
422 assert_eq!(bytes[0], SettlementAccount::SettlementState.discriminator());
423 assert_eq!(&bytes[1..33], &SAMPLE_INIT_ARGS.manager.to_bytes()[..]);
424 assert_eq!(
425 &bytes[33..65],
426 &SAMPLE_INIT_ARGS.solver_authority.to_bytes()[..]
427 );
428 assert_eq!(
429 &bytes[65..97],
430 &SAMPLE_INIT_ARGS.reclaim_authority.to_bytes()[..]
431 );
432 assert_eq!(
433 &bytes[97..129],
434 &SAMPLE_INIT_ARGS.settlement_owned_order_authority.to_bytes()[..]
435 );
436 }
437
438 #[test]
439 fn reads_role_holders_from_the_header() {
440 let bytes = header_bytes();
441 let StateInitArgs {
442 manager,
443 solver_authority,
444 reclaim_authority,
445 settlement_owned_order_authority,
446 } = *SAMPLE_INIT_ARGS;
447
448 let state = StateAccount::attach(&bytes[..]).expect("valid header");
449 assert_eq!(state.authority(Role::Manager), manager);
450 assert_eq!(state.authority(Role::SolverAuthority), solver_authority);
451 assert_eq!(state.authority(Role::ReclaimAuthority), reclaim_authority);
452 assert_eq!(
453 state.authority(Role::SettlementOwnedOrderAuthority),
454 settlement_owned_order_authority
455 );
456 }
457
458 #[test]
459 fn new_rejects_wrong_discriminator() {
460 let mut bytes = header_bytes();
461 bytes[DISCRIMINATOR_OFFSET] = 0xff;
462 assert_eq!(
463 StateAccount::attach(&bytes[..]).err(),
464 Some(ProgramError::InvalidAccountData),
465 );
466 }
467
468 #[test]
469 fn new_rejects_too_short_buffer() {
470 let bytes = header_bytes();
471 assert_eq!(
472 StateAccount::attach(&bytes[..WIDTH_HEADER - 1]).err(),
473 Some(ProgramError::InvalidAccountData),
474 );
475 }
476
477 fn assert_set_authority_updates_only(target: Role) {
480 let new_holder = pubkey_from_seed("set_authority's new holder");
481
482 let mut bytes = header_bytes();
483 let before = Role::ALL.map(|role| {
484 StateAccount::attach(&bytes[..])
485 .expect("valid header")
486 .authority(role)
487 });
488
489 StateAccount::attach(&mut bytes[..])
490 .expect("valid header")
491 .set_authority(target, &new_holder);
492
493 let state = StateAccount::attach(&bytes[..]).expect("valid header");
494 for (role, prior) in Role::ALL.into_iter().zip(before) {
495 let expected = if role == target { new_holder } else { prior };
496 assert_eq!(state.authority(role), expected);
497 }
498 }
499
500 macro_rules! set_authority_tests {
502 ($($role:ident),+ $(,)?) => {
503 pastey::paste! {
504 $(
505 #[test]
506 fn [< set_authority_updates_only_ $role:snake >]() {
507 assert_set_authority_updates_only(Role::$role);
508 }
509 )+
510 }
511
512 const _: () = match Role::Manager {
515 $( Role::$role => {} ),+
516 };
517 };
518 }
519
520 set_authority_tests!(
521 Manager,
522 SolverAuthority,
523 ReclaimAuthority,
524 SettlementOwnedOrderAuthority
525 );
526
527 #[test]
528 fn new_accepts_a_longer_account_and_reads_the_header() {
529 let mut bytes = header_bytes().to_vec();
530 bytes.push(0x42);
531
532 let StateInitArgs {
533 manager,
534 solver_authority,
535 reclaim_authority,
536 settlement_owned_order_authority,
537 } = *SAMPLE_INIT_ARGS;
538
539 let state = StateAccount::attach(&bytes[..]).expect("header with trailing bytes is valid");
540 assert_eq!(state.authority(Role::Manager), manager);
541 assert_eq!(state.authority(Role::SolverAuthority), solver_authority);
542 assert_eq!(state.authority(Role::ReclaimAuthority), reclaim_authority);
543 assert_eq!(
544 state.authority(Role::SettlementOwnedOrderAuthority),
545 settlement_owned_order_authority
546 );
547 }
548
549 fn sample_solvers() -> (Vec<u8>, [Pubkey; 3]) {
553 let solvers = [
554 pubkey_from_seed("sample_solver's solver a"),
555 pubkey_from_seed("sample_solver's solver b"),
556 pubkey_from_seed("sample_solver's solver c"),
557 ];
558 let bytes = state_bytes(&solvers);
559 let mut sorted = solvers;
560 sorted.sort();
561 (bytes, sorted)
562 }
563
564 #[test]
565 fn solvers_is_empty_without_any_stored() {
566 let bytes = state_bytes(&[]);
567 let state = StateAccount::attach(&bytes[..]).expect("valid header");
568 assert_eq!(state.solvers().count(), 0);
569 }
570
571 #[test]
572 fn solvers_lists_stored_solvers_sorted() {
573 let (bytes, sorted) = sample_solvers();
574 let state = StateAccount::attach(&bytes[..]).expect("valid header");
575 assert_eq!(state.solvers().collect::<Vec<_>>(), sorted);
576 }
577
578 #[test]
579 fn solver_search_on_empty_list() {
580 let bytes = state_bytes(&[]);
581 let state = StateAccount::attach(&bytes[..]).expect("valid header");
582 let absent = pubkey_from_seed("absent solver");
583 assert_eq!(state.solver_search(&absent), Err(0));
584 }
585
586 #[test]
587 fn solver_search_finds_present() {
588 let (bytes, sorted) = sample_solvers();
589 let state = StateAccount::attach(&bytes[..]).expect("valid header");
590 for (index, solver) in sorted.iter().enumerate() {
591 assert_eq!(state.solver_search(solver), Ok(index));
592 }
593 }
594
595 #[test]
596 fn solver_search_locates_absent() {
597 let (bytes, sorted) = sample_solvers();
598 let state = StateAccount::attach(&bytes[..]).expect("valid header");
599 let absent = pubkey_from_seed("absent solver");
602 let slot = state.solver_search(&absent).expect_err("absent solver");
603 assert_eq!(slot, sorted.partition_point(|s| s < &absent));
604 }
605
606 #[test]
607 fn initialize_rejects_too_small_buffer() {
608 let mut bytes = [0u8; WIDTH_HEADER - 1];
609 assert_eq!(
610 StateAccount::initialize(&mut bytes[..], &SAMPLE_INIT_ARGS).err(),
611 Some(ProgramError::AccountDataTooSmall),
612 );
613 }
614
615 mod proptest {
616 use ::proptest::prelude::*;
617
618 use super::*;
619
620 proptest! {
621 #[test]
624 fn is_solver_reflects_membership(
625 header in fixtures::arb_init_params(),
626 raw_solvers in prop::collection::btree_set(any::<[u8; 32]>(), 0..50),
628 raw_absent in any::<[u8; 32]>(),
629 ) {
630 prop_assume!(!raw_solvers.contains(&raw_absent));
631 let stored: Vec<Pubkey> =
632 raw_solvers.into_iter().map(Pubkey::new_from_array).collect();
633 let absent = Pubkey::new_from_array(raw_absent);
634
635 let bytes = fixtures::state_account_bytes(&header, &stored);
636 let state = StateAccount::attach(&bytes[..]).expect("valid header");
637
638 for solver in &stored {
639 prop_assert!(state.is_solver(solver));
640 }
641 prop_assert!(!state.is_solver(&absent));
642 }
643
644 #[test]
647 fn account_encode_roundtrip(header in fixtures::arb_init_params()) {
648 let mut bytes = [0u8; WIDTH_HEADER];
649 StateAccount::initialize(&mut bytes[..], &header).expect("header fits");
650
651 let state = StateAccount::attach(&bytes[..]).expect("valid header");
652 let StateInitArgs { manager, solver_authority, reclaim_authority, settlement_owned_order_authority } = header;
653 prop_assert_eq!(state.authority(Role::Manager), manager);
654 prop_assert_eq!(state.authority(Role::SolverAuthority), solver_authority);
655 prop_assert_eq!(state.authority(Role::ReclaimAuthority), reclaim_authority);
656 prop_assert_eq!(
657 state.authority(Role::SettlementOwnedOrderAuthority),
658 settlement_owned_order_authority
659 );
660 }
661
662 #[test]
663 fn insert_solver_inserts_an_absent_solver(
664 header in fixtures::arb_init_params(),
665 raw_solvers in prop::collection::btree_set(any::<[u8; 32]>(), 0..50),
667 raw_new in any::<[u8; 32]>(),
668 ) {
669 prop_assume!(!raw_solvers.contains(&raw_new));
670 let stored: Vec<Pubkey> =
671 raw_solvers.into_iter().map(Pubkey::new_from_array).collect();
672 let new = Pubkey::new_from_array(raw_new);
673
674 let mut bytes = fixtures::state_account_bytes(&header, &stored);
677 let grown_len = StateAccount::attach(&bytes[..])
678 .expect("valid header")
679 .grown_len()
680 .expect("grown length fits");
681 prop_assert_eq!(grown_len, bytes.len().strict_add(WIDTH_PUBKEY));
682 bytes.resize(grown_len, 0);
683 StateAccount::attach(&mut bytes[..])
684 .expect("valid header")
685 .insert_solver(&new)
686 .expect("absent solver inserts");
687
688 let mut expected = stored;
689 expected.push(new);
690 expected.sort();
691 let state = StateAccount::attach(&bytes[..]).expect("valid header");
692 prop_assert_eq!(state.solvers().collect::<Vec<_>>(), expected);
693 }
694
695 #[test]
696 fn insert_solver_rejects_an_existing_solver(
697 header in fixtures::arb_init_params(),
698 raw_solvers in prop::collection::btree_set(any::<[u8; 32]>(), 1..50),
700 pick in any::<prop::sample::Index>(),
701 ) {
702 let stored: Vec<Pubkey> =
703 raw_solvers.into_iter().map(Pubkey::new_from_array).collect();
704 let existing = stored[pick.index(stored.len())];
705
706 let mut bytes = fixtures::state_account_bytes(&header, &stored);
708 bytes.resize(bytes.len().strict_add(WIDTH_PUBKEY), 0);
709 prop_assert_eq!(
710 StateAccount::attach(&mut bytes[..])
711 .expect("valid header")
712 .insert_solver(&existing),
713 Err(SettlementError::SolverAlreadyExists.into()),
714 );
715
716 let state = StateAccount::attach(&bytes[..]).expect("valid header");
719 prop_assert_eq!(state.solvers().take(stored.len()).collect::<Vec<_>>(), stored);
720 }
721
722 #[test]
723 fn remove_solver_drops_a_present_solver(
724 header in fixtures::arb_init_params(),
725 raw_solvers in prop::collection::btree_set(any::<[u8; 32]>(), 1..50),
727 pick in any::<prop::sample::Index>(),
728 ) {
729 let stored: Vec<Pubkey> =
730 raw_solvers.into_iter().map(Pubkey::new_from_array).collect();
731 let index = pick.index(stored.len());
732 let removed = stored[index];
733
734 let mut bytes = fixtures::state_account_bytes(&header, &stored);
737 let shrunk_len = StateAccount::attach(&mut bytes[..])
738 .expect("valid header")
739 .remove_solver(&removed)
740 .expect("a present solver is removed");
741 prop_assert_eq!(shrunk_len, bytes.len().strict_sub(WIDTH_PUBKEY));
742 bytes.truncate(shrunk_len);
743
744 let mut expected = stored;
745 expected.remove(index);
746 let state = StateAccount::attach(&bytes[..]).expect("valid header");
747 prop_assert_eq!(state.solvers().collect::<Vec<_>>(), expected);
748 prop_assert_eq!(state.solver_search(&removed), Err(index));
749 }
750
751 #[test]
752 fn remove_solver_rejects_an_absent_solver(
753 header in fixtures::arb_init_params(),
754 raw_solvers in prop::collection::btree_set(any::<[u8; 32]>(), 0..50),
756 raw_absent in any::<[u8; 32]>(),
757 ) {
758 prop_assume!(!raw_solvers.contains(&raw_absent));
759 let stored: Vec<Pubkey> =
760 raw_solvers.into_iter().map(Pubkey::new_from_array).collect();
761 let absent = Pubkey::new_from_array(raw_absent);
762
763 let mut bytes = fixtures::state_account_bytes(&header, &stored);
764 prop_assert_eq!(
765 StateAccount::attach(&mut bytes[..])
766 .expect("valid header")
767 .remove_solver(&absent),
768 Err(SettlementError::SolverNotFound.into()),
769 );
770
771 let state = StateAccount::attach(&bytes[..]).expect("valid header");
773 prop_assert_eq!(state.solvers().collect::<Vec<_>>(), stored);
774 }
775 }
776 }
777}