use crate::Process;
use circuit::network::AleoV0;
use console::{
account::{Address, PrivateKey},
network::{MainnetV0, prelude::*},
program::{Identifier, Literal, Plaintext, ProgramID, Value},
types::U64,
};
use snarkvm_algorithms::snark::varuna::VarunaVersion;
use snarkvm_ledger_query::Query;
use snarkvm_ledger_store::{BlockStore, FinalizeStorage, FinalizeStore, helpers::memory::BlockMemory};
use snarkvm_synthesizer_program::{FinalizeGlobalState, FinalizeStoreTrait, Program};
use indexmap::IndexMap;
pub type CurrentNetwork = MainnetV0;
pub type CurrentAleo = AleoV0;
macro_rules! sample_finalize_store {
() => {{
#[cfg(feature = "rocks")]
let temp_dir = tempfile::tempdir().expect("Failed to open temporary directory");
#[cfg(not(feature = "rocks"))]
let temp_dir = ();
#[cfg(feature = "rocks")]
let store = FinalizeStore::<CurrentNetwork, snarkvm_ledger_store::helpers::rocksdb::FinalizeDB<_>>::open(
temp_dir.path().to_owned(),
)
.unwrap();
#[cfg(not(feature = "rocks"))]
let store = FinalizeStore::<CurrentNetwork, FinalizeMemory<_>>::open(0u16).unwrap();
(store, temp_dir)
}};
}
pub(super) use sample_finalize_store;
macro_rules! test_atomic_finalize {
($store:ident, $mode:expr, $test:block) => {{
let mut run = || -> Result<()> { atomic_finalize!($store, $mode, $test) };
run()
}};
}
pub(super) use test_atomic_finalize;
fn sample_finalize_state(block_height: u32) -> FinalizeGlobalState {
FinalizeGlobalState::from(block_height as u64, block_height, None, [0u8; 32])
}
fn get_mapping_value<N: Network, F: FinalizeStorage<N>>(
store: &FinalizeStore<N, F>,
program_id: &str,
mapping: &str,
key: Literal<N>,
) -> Result<Option<Value<N>>> {
let program_id = ProgramID::from_str(program_id)?;
let mapping = Identifier::from_str(mapping)?;
let key = Plaintext::from(key);
match store.get_value_speculative(program_id, mapping, &key) {
Ok(result) => Ok(result),
Err(err) => bail!("Error getting value for program_id: {program_id}, mapping: {mapping}, key: {key}: {err}"),
}
}
pub fn account_balance<N: Network, F: FinalizeStorage<N>>(
store: &FinalizeStore<N, F>,
address: &Address<N>,
) -> Result<u64> {
match get_mapping_value(store, "credits.aleo", "account", Literal::Address(*address))? {
Some(Value::Plaintext(Plaintext::Literal(Literal::U64(balance), _))) => Ok(*balance),
_ => bail!("Missing or malformed account balance for {address}"),
}
}
pub fn delegated_state<N: Network, F: FinalizeStorage<N>>(
store: &FinalizeStore<N, F>,
address: &Address<N>,
) -> Result<Option<u64>> {
let state = match get_mapping_value(store, "credits.aleo", "delegated", Literal::Address(*address))? {
Some(Value::Plaintext(Plaintext::Literal(Literal::U64(microcredits), _))) => microcredits,
None => return Ok(None),
_ => bail!("Malformed delegate state for {address}"),
};
Ok(Some(*state))
}
pub fn bond_state<N: Network, F: FinalizeStorage<N>>(
store: &FinalizeStore<N, F>,
address: &Address<N>,
) -> Result<Option<(Address<N>, u64)>> {
let state = match get_mapping_value(store, "credits.aleo", "bonded", Literal::Address(*address))? {
Some(Value::Plaintext(Plaintext::Struct(state, _))) => state,
None => return Ok(None),
_ => bail!("Malformed bond state for {address}"),
};
let validator = match state.get(&Identifier::from_str("validator")?) {
Some(Plaintext::Literal(Literal::Address(address), _)) => *address,
_ => bail!("`validator` not found for: {address}"),
};
let microcredits = match state.get(&Identifier::from_str("microcredits")?) {
Some(Plaintext::Literal(Literal::U64(microcredits), _)) => **microcredits,
_ => bail!("`microcredits` not found for: {address}"),
};
Ok(Some((validator, microcredits)))
}
pub fn unbond_state<N: Network, F: FinalizeStorage<N>>(
store: &FinalizeStore<N, F>,
address: &Address<N>,
) -> Result<Option<(u64, u32)>> {
let state = match get_mapping_value(store, "credits.aleo", "unbonding", Literal::Address(*address))? {
Some(Value::Plaintext(Plaintext::Struct(state, _))) => state,
None => return Ok(None),
_ => bail!("Malformed unbond state for {address}"),
};
let microcredits = match state.get(&Identifier::from_str("microcredits")?) {
Some(Plaintext::Literal(Literal::U64(microcredits), _)) => **microcredits,
_ => bail!("`microcredits` not found for: {address}"),
};
let height = match state.get(&Identifier::from_str("height")?) {
Some(Plaintext::Literal(Literal::U32(height), _)) => **height,
_ => bail!("`height` not found for: {address}"),
};
Ok(Some((microcredits, height)))
}
pub fn initialize_stakers<N: Network, F: FinalizeStorage<N>>(
finalize_store: &FinalizeStore<N, F>,
num_validators: u32,
num_delegators: u32,
rng: &mut TestRng,
) -> Result<(
IndexMap<PrivateKey<N>, (Address<N>, u64, PrivateKey<N>, Address<N>)>,
IndexMap<PrivateKey<N>, (Address<N>, u64)>,
)> {
let program = Program::<N>::credits()?;
for mapping in program.mappings().values() {
if !finalize_store.contains_mapping_confirmed(program.id(), mapping.name())? {
finalize_store.initialize_mapping(*program.id(), *mapping.name())?;
}
}
let mapping = Identifier::from_str("account")?;
let mut validators: IndexMap<_, _> = Default::default();
let mut delegators: IndexMap<_, _> = Default::default();
for i in 0..(num_validators + num_delegators) {
let private_key = PrivateKey::<N>::new(rng)?;
let address = Address::try_from(&private_key)?;
let balance = 100_000_000_000_000u64;
let key = Plaintext::from(Literal::Address(address));
let value = Value::from(Literal::U64(U64::new(balance)));
finalize_store.insert_key_value(*program.id(), mapping, key, value)?;
assert_eq!(balance, account_balance(finalize_store, &address).unwrap());
if i < num_validators {
let withdrawal_private_key = PrivateKey::<N>::new(rng)?;
let withdrawal_address = Address::try_from(&withdrawal_private_key)?;
validators.insert(private_key, (address, balance, withdrawal_private_key, withdrawal_address));
} else {
delegators.insert(private_key, (address, balance));
}
}
Ok((validators, delegators))
}
fn execute_function<F: FinalizeStorage<CurrentNetwork>>(
process: &Process<CurrentNetwork>,
finalize_store: &FinalizeStore<CurrentNetwork, F>,
caller_private_key: &PrivateKey<CurrentNetwork>,
function: &str,
inputs: &[String],
block_height: Option<u32>,
rng: &mut TestRng,
) -> Result<()> {
let authorization =
process.authorize::<CurrentAleo, _>(caller_private_key, "credits.aleo", function, inputs.iter(), rng)?;
let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng)?;
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(0u16)?;
trace.prepare(&Query::from(&block_store))?;
let execution = trace.prove_execution::<CurrentAleo, _>(function, VarunaVersion::V1, rng)?;
let block_height = block_height.unwrap_or(1);
process.finalize_execution(sample_finalize_state(block_height), finalize_store, &execution, None)?;
Ok(())
}
pub fn bond_validator<F: FinalizeStorage<CurrentNetwork>>(
process: &Process<CurrentNetwork>,
finalize_store: &FinalizeStore<CurrentNetwork, F>,
caller_private_key: &PrivateKey<CurrentNetwork>,
withdrawal_address: &Address<CurrentNetwork>,
amount: u64,
commission: u8,
rng: &mut TestRng,
) -> Result<()> {
execute_function(
process,
finalize_store,
caller_private_key,
"bond_validator",
&[withdrawal_address.to_string(), format!("{amount}_u64"), format!("{commission}_u8")],
None,
rng,
)
}
pub fn bond_public<F: FinalizeStorage<CurrentNetwork>>(
process: &Process<CurrentNetwork>,
finalize_store: &FinalizeStore<CurrentNetwork, F>,
caller_private_key: &PrivateKey<CurrentNetwork>,
validator_address: &Address<CurrentNetwork>,
withdrawal_address: &Address<CurrentNetwork>,
amount: u64,
rng: &mut TestRng,
) -> Result<()> {
execute_function(
process,
finalize_store,
caller_private_key,
"bond_public",
&[validator_address.to_string(), withdrawal_address.to_string(), format!("{amount}_u64")],
None,
rng,
)
}
pub fn unbond_public<F: FinalizeStorage<CurrentNetwork>>(
process: &Process<CurrentNetwork>,
finalize_store: &FinalizeStore<CurrentNetwork, F>,
caller_private_key: &PrivateKey<CurrentNetwork>,
address: &Address<CurrentNetwork>,
amount: u64,
block_height: u32,
rng: &mut TestRng,
) -> Result<()> {
execute_function(
process,
finalize_store,
caller_private_key,
"unbond_public",
&[address.to_string(), format!("{amount}_u64")],
Some(block_height),
rng,
)
}