use miden_protocol::Word;
use miden_protocol::account::component::AccountComponentMetadata;
use miden_protocol::account::{
AccountBuilder,
AccountComponent,
StorageMap,
StorageMapKey,
StorageSlot,
StorageSlotName,
};
use miden_standards::code_builder::CodeBuilder;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use crate::{Auth, TransactionContextBuilder};
const TEST_ARRAY_SLOT: &str = "test::array::data";
const TEST_DOUBLE_WORD_ARRAY_SLOT: &str = "test::double_word_array::data";
#[tokio::test]
async fn test_array_get_and_set() -> anyhow::Result<()> {
let slot_name = StorageSlotName::new(TEST_ARRAY_SLOT).expect("slot name should be valid");
let wrapper_component_code = format!(
r#"
use miden::core::word
use miden::standards::data_structures::array
const ARRAY_SLOT_NAME = word("{slot_name}")
#! Wrapper for array::get that uses exec internally.
#! Inputs: [index, pad(15)]
#! Outputs: [VALUE, pad(12)]
pub proc test_get
push.ARRAY_SLOT_NAME[0..2]
exec.array::get
# => [VALUE, pad(15)]
swapw dropw
end
#! Wrapper for array::set that uses exec internally.
#! Inputs: [index, VALUE, pad(11)]
#! Outputs: [OLD_VALUE, pad(12)]
pub proc test_set
push.ARRAY_SLOT_NAME[0..2]
exec.array::set
# => [OLD_VALUE, pad(12)]
end
"#,
);
let wrapper_library = CodeBuilder::default()
.compile_component_code("wrapper::component", wrapper_component_code)?;
let initial_value = Word::from([42u32, 42, 42, 42]);
let wrapper_component = AccountComponent::new(
wrapper_library.clone(),
vec![StorageSlot::with_map(
slot_name.clone(),
StorageMap::with_entries([(StorageMapKey::empty(), initial_value)])?,
)],
AccountComponentMetadata::mock("wrapper::component"),
)?;
let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random())
.with_auth_component(Auth::IncrNonce)
.with_component(wrapper_component)
.build_existing()?;
assert!(
account.storage().get(&slot_name).is_some(),
"Array data slot should exist in account storage"
);
let tx_script_code = r#"
use wrapper::component->wrapper
begin
# Step 1: Get value at index 0 (should return [42, 42, 42, 42])
push.0
# => [index, pad(16)]
call.wrapper::test_get
# => [VALUE, pad(13)]
# Verify value is [42, 42, 42, 42]
push.42.42.42.42
assert_eqw.err="get(0) should return [42, 42, 42, 42] initially"
# => [pad(16)] (auto-padding)
# Step 2: Set value at index 0 to [43, 43, 43, 43]
push.43.43.43.43
push.0
# => [index, VALUE, pad(16)]
call.wrapper::test_set
# => [OLD_VALUE, pad(17)]
dropw
# Step 3: Get value at index 0 (should return [43, 43, 43, 43])
push.0
# => [index, pad(17)]
call.wrapper::test_get
# => [VALUE, pad(14)]
# Verify value is [43, 43, 43, 43]
push.43.43.43.43
assert_eqw.err="get(0) should return [43, 43, 43, 43] after set"
# => [pad(16)] (auto-padding)
end
"#;
let tx_script = CodeBuilder::default()
.with_dynamically_linked_library(&wrapper_library)?
.compile_tx_script(tx_script_code)?;
let tx_context = TransactionContextBuilder::new(account).tx_script(tx_script).build()?;
tx_context.execute().await?;
Ok(())
}
#[tokio::test]
async fn test_double_word_array_get_and_set() -> anyhow::Result<()> {
let slot_name =
StorageSlotName::new(TEST_DOUBLE_WORD_ARRAY_SLOT).expect("slot name should be valid");
let index = 7;
let wrapper_component_code = format!(
r#"
use miden::core::word
use miden::standards::data_structures::double_word_array
const ARRAY_SLOT_NAME = word("{slot_name}")
#! Wrapper for double_word_array::get that uses exec internally.
#! Inputs: [index, pad(15)]
#! Outputs: [VALUE_0, VALUE_1, pad(8)]
pub proc test_get
push.ARRAY_SLOT_NAME[0..2]
exec.double_word_array::get
# => [VALUE_0, VALUE_1, pad(15)]
swapdw dropw dropw
# => [VALUE_0, VALUE_1, pad(8)] auto-padding
end
#! Wrapper for double_word_array::set that uses exec internally.
#! Inputs: [index, VALUE_0, VALUE_1, pad(7)]
#! Outputs: [OLD_VALUE_0, OLD_VALUE_1, pad(8)]
pub proc test_set
push.ARRAY_SLOT_NAME[0..2]
exec.double_word_array::set
# => [OLD_VALUE_0, OLD_VALUE_1, pad(8)] auto-padding
end
"#,
);
let wrapper_library = CodeBuilder::default()
.compile_component_code("wrapper::component", wrapper_component_code)?;
let initial_value_0 = Word::from([1u32, 2, 3, 4]);
let initial_value_1 = Word::from([5u32, 6, 7, 8]);
let wrapper_component = AccountComponent::new(
wrapper_library.clone(),
vec![StorageSlot::with_map(
slot_name.clone(),
StorageMap::with_entries([
(StorageMapKey::from_array([0, 0, 0, index]), initial_value_0),
(StorageMapKey::from_array([0, 0, 1, index]), initial_value_1),
])?,
)],
AccountComponentMetadata::mock("wrapper::component"),
)?;
let account = AccountBuilder::new(ChaCha20Rng::from_os_rng().random())
.with_auth_component(Auth::IncrNonce)
.with_component(wrapper_component)
.build_existing()?;
assert!(
account.storage().get(&slot_name).is_some(),
"Double-word array data slot should exist in account storage"
);
let updated_value_0 = Word::from([9u32, 9, 9, 9]);
let updated_value_1 = Word::from([10u32, 10, 10, 10]);
let tx_script_code = format!(
r#"
use wrapper::component->wrapper
begin
# Step 1: Get value at index {index} (should return the initial double-word)
push.{index}
call.wrapper::test_get
push.{initial_value_0}
assert_eqw.err="get(index) should return initial word 0"
push.{initial_value_1}
assert_eqw.err="get(index) should return initial word 1"
# Step 2: Set the double-word at index {index} to the updated values
push.{updated_value_1}
push.{updated_value_0}
push.{index}
call.wrapper::test_set
push.{initial_value_0}
assert_eqw.err="set(index) should return the original double-word, left word"
push.{initial_value_1}
assert_eqw.err="set(index) should return the original double-word, right word"
# Step 3: Get value at index {index} (should return the updated double-word)
push.{index}
call.wrapper::test_get
push.{updated_value_0}
assert_eqw.err="get(index) should return the updated double-word, left word"
push.{updated_value_1}
assert_eqw.err="get(index) should return the updated double-word, right word"
repeat.8 drop end
end
"#,
);
let tx_script = CodeBuilder::default()
.with_dynamically_linked_library(&wrapper_library)?
.compile_tx_script(tx_script_code)?;
let tx_context = TransactionContextBuilder::new(account).tx_script(tx_script).build()?;
tx_context.execute().await?;
Ok(())
}