use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec;
use std::collections::BTreeMap;
use std::vec::Vec;
use crate::account::{
AccountCode,
AccountHeader,
AccountId,
AccountStorageHeader,
PartialAccount,
PartialStorage,
StorageSlotHeader,
StorageSlotName,
StorageSlotType,
};
use crate::asset::PartialVault;
use crate::block::account_tree::AccountIdKey;
use crate::errors::TransactionInputsExtractionError;
use crate::testing::account_id::{
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2,
};
use crate::transaction::TransactionInputs;
use crate::utils::serde::{Deserializable, Serializable};
use crate::{Felt, Word};
#[test]
fn test_read_foreign_account_inputs_missing_data() {
let native_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
let foreign_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap();
let code = AccountCode::mock();
let storage_header = AccountStorageHeader::new(vec![]).unwrap();
let partial_storage = PartialStorage::new(storage_header, []).unwrap();
let partial_vault = PartialVault::new(Word::default());
let partial_account = PartialAccount::new(
native_account_id,
Felt::new(10),
code,
partial_storage,
partial_vault,
None,
)
.unwrap();
let tx_inputs = TransactionInputs {
account: partial_account,
block_header: crate::block::BlockHeader::mock(0, None, None, &[], Word::default()),
blockchain: crate::transaction::PartialBlockchain::default(),
input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(),
tx_args: crate::transaction::TransactionArgs::default(),
advice_inputs: crate::vm::AdviceInputs::default(),
foreign_account_code: Vec::new(),
foreign_account_slot_names: BTreeMap::new(),
};
let result = tx_inputs.read_foreign_account_inputs(foreign_account_id);
assert!(
matches!(result, Err(TransactionInputsExtractionError::ForeignAccountNotFound(id)) if id == foreign_account_id)
);
}
#[test]
fn test_read_foreign_account_inputs_with_storage_data() {
use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2;
let native_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
let foreign_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap();
let code = AccountCode::mock();
let storage_header = AccountStorageHeader::new(vec![]).unwrap();
let partial_storage = PartialStorage::new(storage_header, []).unwrap();
let partial_vault = PartialVault::new(Word::default());
let partial_account = PartialAccount::new(
native_account_id,
Felt::new(10),
code.clone(),
partial_storage,
partial_vault,
None,
)
.unwrap();
let foreign_header = AccountHeader::new(
foreign_account_id,
Felt::new(5),
Word::default(),
Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
code.commitment(),
);
let slot_name1 = StorageSlotName::new("test::slot1::value".to_string()).unwrap();
let slot_name2 = StorageSlotName::new("test::slot2::value".to_string()).unwrap();
let slot1 = StorageSlotHeader::new(
slot_name1,
StorageSlotType::Value,
Word::new([Felt::new(10), Felt::new(20), Felt::new(30), Felt::new(40)]),
);
let slot2 = StorageSlotHeader::new(
slot_name2,
StorageSlotType::Map,
Word::new([Felt::new(50), Felt::new(60), Felt::new(70), Felt::new(80)]),
);
let mut slots = vec![slot1, slot2];
slots.sort_by_key(|slot| slot.id());
let foreign_storage_header = AccountStorageHeader::new(slots.clone()).unwrap();
let mut advice_inputs = crate::vm::AdviceInputs::default();
let account_id_key = AccountIdKey::from(foreign_account_id);
advice_inputs
.map
.insert(account_id_key.as_word(), foreign_header.to_elements().to_vec());
advice_inputs
.map
.insert(foreign_header.storage_commitment(), foreign_storage_header.to_elements());
let foreign_account_slot_names = BTreeMap::from([
(slots[0].id(), slots[0].name().clone()),
(slots[1].id(), slots[1].name().clone()),
]);
let tx_inputs = TransactionInputs {
account: partial_account,
block_header: crate::block::BlockHeader::mock(0, None, None, &[], Word::default()),
blockchain: crate::transaction::PartialBlockchain::default(),
input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(),
tx_args: crate::transaction::TransactionArgs::default(),
advice_inputs,
foreign_account_code: vec![code],
foreign_account_slot_names,
};
let account_inputs = tx_inputs.read_foreign_account_inputs(foreign_account_id).unwrap();
assert_eq!(account_inputs.id(), foreign_account_id);
assert_eq!(account_inputs.account().nonce(), Felt::new(5));
let storage = account_inputs.account().storage();
assert_eq!(storage.header().slots().count(), 2);
let witness = account_inputs.witness();
assert_eq!(witness.id(), foreign_account_id);
let computed_root = account_inputs.compute_account_root();
assert!(
computed_root.is_ok(),
"Failed to compute account root from witness: {:?}",
computed_root.err()
);
assert_eq!(witness.path().depth(), 64, "Witness path should have SMT depth of 64");
}
#[test]
fn test_read_foreign_account_inputs_with_proper_witness() {
use crate::block::account_tree::AccountTree;
use crate::crypto::merkle::smt::Smt;
use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2;
let native_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
let foreign_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap();
let code = AccountCode::mock();
let storage_header = AccountStorageHeader::new(vec![]).unwrap();
let partial_storage = PartialStorage::new(storage_header, []).unwrap();
let partial_vault = PartialVault::new(Word::default());
let native_account = PartialAccount::new(
native_account_id,
Felt::new(10),
code.clone(),
partial_storage,
partial_vault,
None,
)
.unwrap();
let foreign_header = AccountHeader::new(
foreign_account_id,
Felt::new(5),
Word::default(),
Word::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
code.commitment(),
);
let foreign_storage_header = AccountStorageHeader::new(vec![]).unwrap();
let mut account_tree = AccountTree::<Smt>::default();
let native_commitment = AccountHeader::from(&native_account).to_commitment();
account_tree.insert(native_account_id, native_commitment).unwrap();
let _foreign_partial_account = PartialAccount::new(
foreign_account_id,
Felt::new(5),
code.clone(),
PartialStorage::new(foreign_storage_header.clone(), []).unwrap(),
PartialVault::new(Word::default()),
None,
)
.unwrap();
account_tree.insert(foreign_account_id, foreign_header.to_commitment()).unwrap();
let account_tree_root = account_tree.root();
let foreign_witness = account_tree.open(foreign_account_id);
let mut advice_inputs = crate::vm::AdviceInputs::default();
let account_id_key = AccountIdKey::from(foreign_account_id);
advice_inputs
.map
.insert(account_id_key.as_word(), foreign_header.to_elements().to_vec());
advice_inputs
.map
.insert(foreign_header.storage_commitment(), foreign_storage_header.to_elements());
advice_inputs.store.extend(foreign_witness.authenticated_nodes());
let leaf = foreign_witness.leaf();
advice_inputs
.map
.insert(leaf.hash(), leaf.to_elements().collect::<Arc<[Felt]>>());
let block_header = crate::block::BlockHeader::mock(0, None, None, &[], account_tree_root);
let tx_inputs = TransactionInputs {
account: native_account,
block_header,
blockchain: crate::transaction::PartialBlockchain::default(),
input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(),
tx_args: crate::transaction::TransactionArgs::default(),
advice_inputs,
foreign_account_code: vec![code],
foreign_account_slot_names: BTreeMap::new(),
};
let account_inputs = tx_inputs.read_foreign_account_inputs(foreign_account_id).unwrap();
assert_eq!(account_inputs.id(), foreign_account_id);
assert_eq!(account_inputs.account().nonce(), Felt::new(5));
let witness = account_inputs.witness();
assert_eq!(witness.id(), foreign_account_id);
let computed_root = account_inputs.compute_account_root();
assert!(
computed_root.is_ok(),
"Failed to compute account root from witness: {:?}",
computed_root.err()
);
let _computed_root_value = computed_root.unwrap();
assert_eq!(witness.path().depth(), 64, "Witness path should have SMT depth of 64");
}
#[test]
fn test_transaction_inputs_serialization_with_foreign_slot_names() {
use miden_core::Felt;
use crate::account::{
AccountCode,
AccountId,
AccountStorageHeader,
PartialAccount,
PartialStorage,
StorageSlotName,
};
use crate::asset::PartialVault;
use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE;
let native_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
let slot_name_1 = StorageSlotName::new("test::slot1::value".to_string()).unwrap();
let slot_name_2 = StorageSlotName::new("test::slot2::map".to_string()).unwrap();
let slot_name_3 = StorageSlotName::new("another::slot::value".to_string()).unwrap();
let slot_id_1 = slot_name_1.id();
let slot_id_2 = slot_name_2.id();
let slot_id_3 = slot_name_3.id();
let mut foreign_account_slot_names = BTreeMap::new();
foreign_account_slot_names.insert(slot_id_1, slot_name_1.clone());
foreign_account_slot_names.insert(slot_id_2, slot_name_2.clone());
foreign_account_slot_names.insert(slot_id_3, slot_name_3.clone());
let code = AccountCode::mock();
let storage_header = AccountStorageHeader::new(vec![]).unwrap();
let partial_storage = PartialStorage::new(storage_header, []).unwrap();
let partial_vault = PartialVault::new(Word::default());
let partial_account = PartialAccount::new(
native_account_id,
Felt::new(10),
code,
partial_storage,
partial_vault,
None,
)
.unwrap();
let original_tx_inputs = TransactionInputs {
account: partial_account,
block_header: crate::block::BlockHeader::mock(0, None, None, &[], Word::default()),
blockchain: crate::transaction::PartialBlockchain::default(),
input_notes: crate::transaction::InputNotes::new(vec![]).unwrap(),
tx_args: crate::transaction::TransactionArgs::default(),
advice_inputs: crate::vm::AdviceInputs::default(),
foreign_account_code: Vec::new(),
foreign_account_slot_names,
};
let serialized = original_tx_inputs.to_bytes();
let deserialized = TransactionInputs::read_from_bytes(&serialized).unwrap();
assert_eq!(
original_tx_inputs.foreign_account_slot_names(),
deserialized.foreign_account_slot_names()
);
let deserialized_slots = deserialized.foreign_account_slot_names();
assert_eq!(deserialized_slots.get(&slot_id_1).unwrap(), &slot_name_1);
assert_eq!(deserialized_slots.get(&slot_id_2).unwrap(), &slot_name_2);
assert_eq!(deserialized_slots.get(&slot_id_3).unwrap(), &slot_name_3);
assert_eq!(original_tx_inputs, deserialized);
}