1use std::{
2 collections::HashMap,
3 fmt::Debug,
4 sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
5};
6
7use alloy::{
8 primitives::{Address, Bytes as AlloyBytes, StorageValue, B256, U256},
9 providers::{
10 fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller},
11 Provider, RootProvider,
12 },
13 transports::{RpcError, TransportErrorKind},
14};
15use revm::{
16 context::DBErrorMarker,
17 state::{AccountInfo, Bytecode},
18 DatabaseRef,
19};
20use thiserror::Error;
21use tracing::{debug, info};
22use tycho_client::feed::BlockHeader;
23
24use super::{
25 super::account_storage::{AccountStorage, StateUpdate},
26 engine_db_interface::EngineDatabaseInterface,
27};
28
29pub struct OverriddenSimulationDB<'a, DB: DatabaseRef> {
32 pub inner_db: &'a DB,
34 pub overrides: &'a HashMap<Address, HashMap<U256, U256>>,
37 pub native_balance_overrides: &'a HashMap<Address, U256>,
41}
42
43impl<'a, DB: DatabaseRef> OverriddenSimulationDB<'a, DB> {
44 pub fn new(
46 inner_db: &'a DB,
47 overrides: &'a HashMap<Address, HashMap<U256, U256>>,
48 native_balance_overrides: &'a HashMap<Address, U256>,
49 ) -> Self {
50 OverriddenSimulationDB { inner_db, overrides, native_balance_overrides }
51 }
52}
53
54impl<DB: DatabaseRef> DatabaseRef for OverriddenSimulationDB<'_, DB> {
55 type Error = DB::Error;
56
57 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
58 let Some(balance) = self
59 .native_balance_overrides
60 .get(&address)
61 else {
62 return self.inner_db.basic_ref(address);
63 };
64 let info = self
65 .inner_db
66 .basic_ref(address)
67 .ok()
68 .flatten();
69 Ok(Some(AccountInfo { balance: *balance, ..info.unwrap_or_default() }))
70 }
71
72 fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
73 self.inner_db
74 .code_by_hash_ref(code_hash)
75 }
76
77 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
78 match self.overrides.get(&address) {
79 None => self
80 .inner_db
81 .storage_ref(address, index),
82 Some(slot_overrides) => match slot_overrides.get(&index) {
83 Some(value) => {
84 debug!(%address, %index, %value, "Requested storage of account {:x?} slot {}", address, index);
85 Ok(*value)
86 }
87 None => self
88 .inner_db
89 .storage_ref(address, index),
90 },
91 }
92 }
93
94 fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
95 self.inner_db.block_hash_ref(number)
96 }
97}
98
99#[derive(Clone, Debug)]
101pub struct SimulationDB<P: Provider + Debug> {
102 client: Arc<P>,
104 account_storage: Arc<RwLock<AccountStorage>>,
106 block: Option<BlockHeader>,
108 pub runtime: Option<Arc<tokio::runtime::Runtime>>,
110}
111
112pub type EVMProvider = FillProvider<
113 JoinFill<
114 alloy::providers::Identity,
115 JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
116 >,
117 RootProvider,
118>;
119
120impl<P: Provider + Debug + 'static> SimulationDB<P> {
121 pub fn new(
122 client: Arc<P>,
123 runtime: Option<Arc<tokio::runtime::Runtime>>,
124 block: Option<BlockHeader>,
125 ) -> Self {
126 Self {
127 client,
128 account_storage: Arc::new(RwLock::new(AccountStorage::new())),
129 block,
130 runtime,
131 }
132 }
133
134 pub fn set_block(&mut self, block: Option<BlockHeader>) {
136 self.block = block;
137 }
138
139 pub fn update_state(
151 &mut self,
152 updates: &HashMap<Address, StateUpdate>,
153 block: BlockHeader,
154 ) -> Result<HashMap<Address, StateUpdate>, SimulationDBError> {
155 info!("Received account state update.");
156 let mut revert_updates = HashMap::new();
157 self.block = Some(block);
158 for (address, update_info) in updates.iter() {
159 let mut revert_entry = StateUpdate::default();
160 if let Some(current_account) = self
161 .read_account_storage()?
162 .get_account_info(address)
163 {
164 revert_entry.balance = Some(current_account.balance);
165 }
166 if let Some(storage_updates) = update_info.storage.as_ref() {
167 let mut revert_storage = HashMap::default();
168 for index in storage_updates.keys() {
169 if let Some(s) = self
170 .read_account_storage()?
171 .get_permanent_storage(address, index)
172 {
173 revert_storage.insert(*index, s);
174 }
175 }
176 revert_entry.storage = Some(revert_storage);
177 }
178 revert_updates.insert(*address, revert_entry);
179
180 self.write_account_storage()?
181 .update_account(address, update_info);
182 }
183 Ok(revert_updates)
184 }
185
186 fn query_account_info(
198 &self,
199 address: Address,
200 ) -> Result<AccountInfo, <SimulationDB<P> as DatabaseRef>::Error> {
201 debug!("Querying account info of {:x?} at block {:?}", address, self.block);
202
203 let (balance, nonce, code) = self.block_on(async {
204 let mut balance_request = self.client.get_balance(address);
205 let mut nonce_request = self
206 .client
207 .get_transaction_count(address);
208 let mut code_request = self.client.get_code_at(address);
209
210 if let Some(block) = &self.block {
211 balance_request = balance_request.number(block.number);
212 nonce_request = nonce_request.number(block.number);
213 code_request = code_request.number(block.number);
214 }
215
216 tokio::join!(balance_request, nonce_request, code_request,)
217 });
218 let code = Bytecode::new_raw(AlloyBytes::copy_from_slice(&code?));
219
220 Ok(AccountInfo::new(balance?, nonce?, code.hash_slow(), code))
221 }
222
223 pub fn query_storage(
235 &self,
236 address: Address,
237 index: U256,
238 ) -> Result<StorageValue, <SimulationDB<P> as DatabaseRef>::Error> {
239 let mut request = self
240 .client
241 .get_storage_at(address, index);
242 if let Some(block) = &self.block {
243 request = request.number(block.number);
244 }
245
246 let storage_future = async move {
247 request.await.map_err(|err| {
248 SimulationDBError::SimulationError(format!(
249 "Failed to fetch storage for {address:?} slot {index}: {err}"
250 ))
251 })
252 };
253
254 self.block_on(storage_future)
255 }
256
257 fn read_account_storage(
258 &self,
259 ) -> Result<RwLockReadGuard<'_, AccountStorage>, SimulationDBError> {
260 self.account_storage
261 .read()
262 .map_err(|_| SimulationDBError::Internal("Account storage read lock poisoned".into()))
263 }
264
265 fn write_account_storage(
266 &self,
267 ) -> Result<RwLockWriteGuard<'_, AccountStorage>, SimulationDBError> {
268 self.account_storage
269 .write()
270 .map_err(|_| SimulationDBError::Internal("Account storage write lock poisoned".into()))
271 }
272
273 fn block_on<F: core::future::Future>(&self, f: F) -> F::Output {
274 match &self.runtime {
278 Some(runtime) => runtime.block_on(f),
279 None => futures::executor::block_on(f),
280 }
281 }
282}
283
284impl<P: Provider + Debug> EngineDatabaseInterface for SimulationDB<P>
285where
286 P: Provider + Send + Sync + 'static,
287{
288 type Error = SimulationDBError;
289
290 fn init_account(
304 &self,
305 address: Address,
306 mut account: AccountInfo,
307 permanent_storage: Option<HashMap<U256, U256>>,
308 mocked: bool,
309 ) -> Result<(), <Self as EngineDatabaseInterface>::Error> {
310 if let Some(code) = account.code.clone() {
311 account.code = Some(code);
312 }
313
314 self.write_account_storage()?
315 .init_account(address, account, permanent_storage, mocked);
316
317 Ok(())
318 }
319
320 fn clear_temp_storage(&mut self) -> Result<(), <Self as EngineDatabaseInterface>::Error> {
325 self.write_account_storage()?
326 .clear_temp_storage();
327
328 Ok(())
329 }
330
331 fn get_current_block(&self) -> Option<BlockHeader> {
332 self.block.clone()
333 }
334}
335
336#[derive(Error, Debug)]
337pub enum SimulationDBError {
338 #[error("Simulation error: {0} ")]
339 SimulationError(String),
340 #[error("Not implemented error: {0}")]
341 NotImplementedError(String),
342 #[error("Simulation DB internal error: {0}")]
343 Internal(String),
344}
345
346impl DBErrorMarker for SimulationDBError {}
347
348impl From<RpcError<TransportErrorKind>> for SimulationDBError {
349 fn from(err: RpcError<TransportErrorKind>) -> Self {
350 SimulationDBError::SimulationError(err.to_string())
351 }
352}
353
354impl<P: Provider> DatabaseRef for SimulationDB<P>
355where
356 P: Provider + Debug + Send + Sync + 'static,
357{
358 type Error = SimulationDBError;
359
360 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
391 if let Some(account) = {
392 self.read_account_storage()?
393 .get_account_info(&address)
394 .cloned()
395 } {
396 return Ok(Some(account));
397 }
398 let account_info = self.query_account_info(address)?;
399 self.init_account(address, account_info.clone(), None, false)?;
400 Ok(Some(account_info))
401 }
402
403 fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
404 Err(SimulationDBError::NotImplementedError(
405 "Code by hash is not implemented in SimulationDB".to_string(),
406 ))
407 }
408
409 fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
444 debug!("Requested storage of account {:x?} slot {}", address, index);
445 let (is_mocked, local_value) = {
446 let account_storage = self.read_account_storage()?;
447 (
448 account_storage.is_mocked_account(&address),
449 account_storage.get_storage(&address, &index),
450 )
451 };
452
453 if let Some(storage_value) = local_value {
454 debug!(
455 "Got value locally. This is a {} account. Value: {}",
456 if is_mocked.unwrap_or(false) { "mocked" } else { "non-mocked" },
457 storage_value
458 );
459 return Ok(storage_value);
460 }
461
462 match is_mocked {
464 Some(true) => {
465 debug!("This is a mocked account for which we don't have data. Returning zero.");
466 Ok(U256::ZERO)
467 }
468 Some(false) => {
469 let storage_value = self.query_storage(address, index)?;
470 self.write_account_storage()?
471 .set_temp_storage(address, index, storage_value);
472 debug!(
473 "This is a non-mocked account for which we didn't have data. Fetched value: {}",
474 storage_value
475 );
476 Ok(storage_value)
477 }
478 None => {
479 let account_info = self.query_account_info(address)?;
480 let storage_value = self.query_storage(address, index)?;
481 self.init_account(address, account_info, None, false)?;
482 self.write_account_storage()?
483 .set_temp_storage(address, index, storage_value);
484 debug!("This is non-initialised account. Fetched value: {}", storage_value);
485 Ok(storage_value)
486 }
487 }
488 }
489
490 fn block_hash_ref(&self, _number: u64) -> Result<B256, Self::Error> {
493 match &self.block {
494 Some(header) => Ok(B256::from_slice(&header.hash)),
495 None => Ok(B256::ZERO),
496 }
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use std::{error::Error, str::FromStr};
503
504 use alloy::primitives::U160;
505 use rstest::rstest;
506 use tycho_common::Bytes;
507
508 use super::*;
509 use crate::evm::engine_db::utils::{get_client, get_runtime};
510
511 #[rstest]
512 fn test_query_storage_latest_block() -> Result<(), Box<dyn Error>> {
513 let db = SimulationDB::new(
514 get_client(None).expect("Failed to create test client"),
515 get_runtime().expect("Failed to create test runtime"),
516 None,
517 );
518 let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
519 let index = U256::from_limbs_slice(&[8]);
520 db.init_account(address, AccountInfo::default(), None, false)
521 .expect("Failed to init account");
522
523 db.query_storage(address, index)
524 .unwrap();
525
526 Ok(())
530 }
531
532 #[rstest]
533 fn test_query_account_info() {
534 let mut db = SimulationDB::new(
535 get_client(None).expect("Failed to create test client"),
536 get_runtime().expect("Failed to create test runtime"),
537 None,
538 );
539 let block = BlockHeader {
540 number: 20308186,
541 hash: Bytes::from_str(
542 "0x61c51e3640b02ae58a03201be0271e84e02dac8a4826501995cbe4da24174b52",
543 )
544 .unwrap(),
545 timestamp: 234,
546 ..Default::default()
547 };
548 db.set_block(Some(block));
549 let address = Address::from_str("0x168b93113fe5902c87afaecE348581A1481d0f93").unwrap();
550 db.init_account(address, AccountInfo::default(), None, false)
551 .expect("Failed to init account");
552
553 let account_info = db.query_account_info(address).unwrap();
554
555 assert_eq!(account_info.balance, U256::from_str("6246978663692389").unwrap());
556 assert_eq!(account_info.nonce, 17);
557 }
558
559 #[rstest]
560 fn test_mock_account_get_acc_info() {
561 let db = SimulationDB::new(
562 get_client(None).expect("Failed to create test client"),
563 get_runtime().expect("Failed to create test runtime"),
564 None,
565 );
566 let mock_acc_address =
567 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
568 db.init_account(mock_acc_address, AccountInfo::default(), None, true)
569 .expect("Failed to init account");
570
571 let acc_info = db
572 .basic_ref(mock_acc_address)
573 .unwrap()
574 .unwrap();
575
576 assert_eq!(
577 db.account_storage
578 .read()
579 .unwrap()
580 .get_account_info(&mock_acc_address)
581 .unwrap(),
582 &acc_info
583 );
584 }
585
586 #[rstest]
587 fn test_mock_account_get_storage() {
588 let db = SimulationDB::new(
589 get_client(None).expect("Failed to create test client"),
590 get_runtime().expect("Failed to create test runtime"),
591 None,
592 );
593 let mock_acc_address =
594 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
595 let storage_address = U256::ZERO;
596 db.init_account(mock_acc_address, AccountInfo::default(), None, true)
597 .expect("Failed to init account");
598
599 let storage = db
600 .storage_ref(mock_acc_address, storage_address)
601 .unwrap();
602
603 assert_eq!(storage, U256::ZERO);
604 }
605
606 #[rstest]
607 fn test_update_state() {
608 let mut db = SimulationDB::new(
609 get_client(None).expect("Failed to create test client"),
610 get_runtime().expect("Failed to create test runtime"),
611 None,
612 );
613 let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
614 db.init_account(address, AccountInfo::default(), None, false)
615 .expect("Failed to init account");
616
617 let mut new_storage = HashMap::default();
618 let new_storage_value_index = U256::from_limbs_slice(&[123]);
619 new_storage.insert(new_storage_value_index, new_storage_value_index);
620 let new_balance = U256::from_limbs_slice(&[500]);
621 let update = StateUpdate { storage: Some(new_storage), balance: Some(new_balance) };
622 let mut updates = HashMap::default();
623 updates.insert(address, update);
624 let new_block = BlockHeader { number: 1, timestamp: 234, ..Default::default() };
625
626 let reverse_update = db
627 .update_state(&updates, new_block)
628 .expect("State update should succeed");
629
630 assert_eq!(
631 db.account_storage
632 .read()
633 .expect("Storage entry should exist")
634 .get_storage(&address, &new_storage_value_index)
635 .unwrap(),
636 new_storage_value_index
637 );
638 assert_eq!(
639 db.account_storage
640 .read()
641 .unwrap()
642 .get_account_info(&address)
643 .unwrap()
644 .balance,
645 new_balance
646 );
647 assert_eq!(db.block.unwrap().number, 1);
648
649 assert_eq!(
650 reverse_update
651 .get(&address)
652 .unwrap()
653 .balance
654 .unwrap(),
655 AccountInfo::default().balance
656 );
657 assert_eq!(
658 reverse_update
659 .get(&address)
660 .unwrap()
661 .storage,
662 Some(HashMap::default())
663 );
664 }
665
666 #[rstest]
667 fn test_overridden_db() {
668 let db = SimulationDB::new(
669 get_client(None).expect("Failed to create test client"),
670 get_runtime().expect("Failed to create test runtime"),
671 None,
672 );
673 let slot1 = U256::from_limbs_slice(&[1]);
674 let slot2 = U256::from_limbs_slice(&[2]);
675 let orig_value1 = U256::from_limbs_slice(&[100]);
676 let orig_value2 = U256::from_limbs_slice(&[200]);
677 let original_storage: HashMap<U256, U256> = [(slot1, orig_value1), (slot2, orig_value2)]
678 .iter()
679 .cloned()
680 .collect();
681
682 let address1 = Address::from(U160::from(1));
683 let address2 = Address::from(U160::from(2));
684 let address3 = Address::from(U160::from(3));
685
686 db.init_account(address1, AccountInfo::default(), Some(original_storage.clone()), false)
689 .expect("Failed to init account");
690 db.init_account(address2, AccountInfo::default(), Some(original_storage), false)
691 .expect("Failed to init account");
692
693 let overridden_value1 = U256::from_limbs_slice(&[101]);
694 let mut overrides: HashMap<Address, HashMap<U256, U256>> = HashMap::new();
695 overrides.insert(
696 address2,
697 [(slot1, overridden_value1)]
698 .iter()
699 .cloned()
700 .collect(),
701 );
702 overrides.insert(
703 address3,
704 [(slot1, overridden_value1)]
705 .iter()
706 .cloned()
707 .collect(),
708 );
709
710 let native_balance_overrides = HashMap::new();
711 let overriden_db = OverriddenSimulationDB::new(&db, &overrides, &native_balance_overrides);
712
713 assert_eq!(
714 overriden_db
715 .storage_ref(address1, slot1)
716 .expect("Value should be available"),
717 orig_value1,
718 "Slots of non-overridden account should hold original values."
719 );
720
721 assert_eq!(
722 overriden_db
723 .storage_ref(address1, slot2)
724 .expect("Value should be available"),
725 orig_value2,
726 "Slots of non-overridden account should hold original values."
727 );
728
729 assert_eq!(
730 overriden_db
731 .storage_ref(address2, slot1)
732 .expect("Value should be available"),
733 overridden_value1,
734 "Overridden slot of overridden account should hold an overridden value."
735 );
736
737 assert_eq!(
738 overriden_db
739 .storage_ref(address2, slot2)
740 .expect("Value should be available"),
741 orig_value2,
742 "Non-overridden slot of an account with other slots overridden \
743 should hold an original value."
744 );
745
746 assert_eq!(
747 overriden_db
748 .storage_ref(address3, slot1)
749 .expect("Value should be available"),
750 overridden_value1,
751 "Overridden slot of an overridden non-existent account should hold an overriden value."
752 );
753 }
754
755 #[derive(Debug, Default)]
756 struct StaticDB {
757 accounts: HashMap<Address, AccountInfo>,
758 fail_basic_ref: bool,
759 }
760
761 impl DatabaseRef for StaticDB {
762 type Error = SimulationDBError;
763
764 fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
765 if self.fail_basic_ref {
766 return Err(SimulationDBError::Internal("account unavailable".to_string()));
767 }
768 Ok(self.accounts.get(&address).cloned())
769 }
770
771 fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
772 Ok(Bytecode::default())
773 }
774
775 fn storage_ref(&self, _address: Address, _index: U256) -> Result<U256, Self::Error> {
776 Ok(U256::ZERO)
777 }
778
779 fn block_hash_ref(&self, _number: u64) -> Result<B256, Self::Error> {
780 Ok(B256::ZERO)
781 }
782 }
783
784 #[rstest]
785 fn test_balance_override_known_account() {
786 let address = Address::from(U160::from(1));
787 let bytecode = Bytecode::new_raw(AlloyBytes::from_static(&[0x00]));
788 let account = AccountInfo::new(U256::from(100), 7, bytecode.hash_slow(), bytecode.clone());
789 let db = StaticDB { accounts: HashMap::from([(address, account)]), ..Default::default() };
790 let overrides = HashMap::new();
791 let native_balance_overrides = HashMap::from([(address, U256::from(999))]);
792 let overridden_db = OverriddenSimulationDB::new(&db, &overrides, &native_balance_overrides);
793
794 let info = overridden_db
795 .basic_ref(address)
796 .expect("basic_ref should succeed")
797 .expect("account should exist");
798
799 assert_eq!(info.balance, U256::from(999), "Overridden balance should be returned.");
800 assert_eq!(info.nonce, 7, "Other account fields should be preserved.");
801 assert_eq!(info.code, Some(bytecode), "Contract code should be preserved.");
802 }
803
804 #[rstest]
805 fn test_balance_override_unknown_account() {
806 let address = Address::from(U160::from(1));
807 let db = StaticDB::default();
808 let overrides = HashMap::new();
809 let native_balance_overrides = HashMap::from([(address, U256::from(999))]);
810 let overridden_db = OverriddenSimulationDB::new(&db, &overrides, &native_balance_overrides);
811
812 let info = overridden_db
813 .basic_ref(address)
814 .expect("basic_ref should succeed")
815 .expect("overridden unknown account should be synthesized");
816
817 assert_eq!(
818 info,
819 AccountInfo { balance: U256::from(999), ..Default::default() },
820 "Unknown account should be synthesized as an EOA with the overridden balance."
821 );
822 }
823
824 #[rstest]
825 fn test_balance_override_materializes_account_when_inner_db_errors() {
826 let address = Address::from(U160::from(1));
827 let db = StaticDB { fail_basic_ref: true, ..Default::default() };
828 let overrides = HashMap::new();
829 let native_balance_overrides = HashMap::from([(address, U256::from(999))]);
830 let overridden_db = OverriddenSimulationDB::new(&db, &overrides, &native_balance_overrides);
831
832 let info = overridden_db
833 .basic_ref(address)
834 .expect("balance override should shield an inner account miss")
835 .expect("overridden account should be synthesized");
836
837 assert_eq!(info, AccountInfo { balance: U256::from(999), ..Default::default() });
838 }
839
840 #[rstest]
841 fn test_missing_balance_override_propagates_inner_error() {
842 let address = Address::from(U160::from(1));
843 let db = StaticDB { fail_basic_ref: true, ..Default::default() };
844 let overrides = HashMap::new();
845 let native_balance_overrides = HashMap::new();
846 let overridden_db = OverriddenSimulationDB::new(&db, &overrides, &native_balance_overrides);
847
848 let result = overridden_db.basic_ref(address);
849
850 match result {
851 Err(SimulationDBError::Internal(message)) => {
852 assert_eq!(message, "account unavailable")
853 }
854 other => panic!("expected inner database error, got {other:?}"),
855 }
856 }
857
858 #[rstest]
859 fn test_balance_override_absent_passthrough() {
860 let address = Address::from(U160::from(1));
861 let unknown_address = Address::from(U160::from(2));
862 let account = AccountInfo { balance: U256::from(100), nonce: 7, ..Default::default() };
863 let db = StaticDB {
864 accounts: HashMap::from([(address, account.clone())]),
865 ..Default::default()
866 };
867 let overrides = HashMap::new();
868 let native_balance_overrides = HashMap::new();
869 let overridden_db = OverriddenSimulationDB::new(&db, &overrides, &native_balance_overrides);
870
871 assert_eq!(
872 overridden_db
873 .basic_ref(address)
874 .expect("basic_ref should succeed"),
875 Some(account),
876 "Known account without override should pass through unchanged."
877 );
878 assert_eq!(
879 overridden_db
880 .basic_ref(unknown_address)
881 .expect("basic_ref should succeed"),
882 None,
883 "Unknown account without override should stay absent."
884 );
885 }
886}