use solana_hash::Hash;
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlockEnv<E = ()> {
pub latest_blockhash: Hash,
pub slot: u64,
pub ext: E,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct TxEnv<E = ()> {
pub ext: E,
}
#[cfg(test)]
mod tests {
use std::mem::{offset_of, size_of};
use agave_feature_set::FeatureSet;
use solana_address::Address;
use solana_hash::Hash;
use solana_keypair::Keypair;
use solana_message::Message;
use solana_native_token::LAMPORTS_PER_SOL;
use solana_signer::Signer;
use solana_system_interface::instruction::transfer;
use solana_transaction::{Transaction, versioned::VersionedTransaction};
use super::*;
use crate::{ClusterSpecId, Host, Svm, SvmTypesHost};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct L1SlotExt {
pub l1_slot: u64,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct L1Family;
impl SvmTypesHost for L1Family {
type SpecId = ClusterSpecId;
type Tx = VersionedTransaction;
type SvmExt = ();
type MessageExt = ();
type MessageResultExt = ();
type TxEnvExt = ();
type TxResultExt = ();
type BlockEnvExt = L1SlotExt;
type Host<'a> = Svm<'a, Self>;
}
const ORIGINAL_BALANCE: u64 = 1_000_000_000;
const TRANSFER_AMOUNT: u64 = 64;
const SIGNATURE_FEE: u64 = 5_000;
#[test]
fn l1_slot_ext_survives_a_full_transfer_through_host_block_env() {
let mut svm = Svm::<L1Family>::new_inner(false);
svm.set_feature_set(FeatureSet::all_enabled())
.expect("enabling every feature should succeed on a fresh VM");
svm.set_builtins();
svm.set_lamports(1_000_000 * LAMPORTS_PER_SOL);
svm.set_sysvars();
svm.set_feature_accounts();
svm.set_default_programs();
svm.set_sigverify(true);
svm.set_blockhash_check(true);
svm.block_env.ext = L1SlotExt { l1_slot: 42 };
let before = Host::<L1Family>::block_env(&svm);
assert_eq!(before.ext, L1SlotExt { l1_slot: 42 });
let payer = Keypair::new();
let recipient = Address::new_unique();
svm.airdrop(&payer.pubkey(), ORIGINAL_BALANCE).unwrap();
svm.airdrop(&recipient, ORIGINAL_BALANCE).unwrap();
let tx = Transaction::new(
&[&payer],
Message::new(
&[transfer(&payer.pubkey(), &recipient, TRANSFER_AMOUNT)],
Some(&payer.pubkey()),
),
svm.latest_blockhash(),
);
let metadata = svm.send_transaction(tx).expect("transfer should execute");
assert_eq!(metadata.fee, SIGNATURE_FEE);
assert_eq!(
svm.get_balance(&payer.pubkey()),
Some(ORIGINAL_BALANCE - SIGNATURE_FEE - TRANSFER_AMOUNT)
);
assert_eq!(svm.get_balance(&recipient), Some(ORIGINAL_BALANCE + TRANSFER_AMOUNT));
let after = Host::<L1Family>::block_env(&svm);
assert_eq!(after.ext.l1_slot, 42);
assert_eq!(after, before);
}
#[test]
fn base_block_env_is_layout_identical_to_the_previous_shape() {
assert_eq!(size_of::<BlockEnv<()>>(), size_of::<Hash>() + size_of::<u64>());
assert_eq!(size_of::<BlockEnv<()>>(), 40);
assert_eq!(size_of::<BlockEnv<L1SlotExt>>(), 48);
assert_eq!(offset_of!(BlockEnv<()>, latest_blockhash), 0);
assert_eq!(offset_of!(BlockEnv<()>, slot), 32);
assert_eq!(offset_of!(BlockEnv<()>, ext), 40);
}
#[test]
fn base_block_env_default_is_the_genesis_shape() {
let env = BlockEnv::<()>::default();
assert_eq!(env.latest_blockhash, Hash::default());
assert_eq!(env.slot, 0);
assert_eq!(env.ext, ());
}
#[test]
fn tx_env_carries_only_the_extension_slot() {
let env = TxEnv { ext: L1SlotExt { l1_slot: 7 } };
assert_eq!(env.ext.l1_slot, 7);
assert_eq!(size_of::<TxEnv<()>>(), 0);
assert_eq!(TxEnv::<()>::default().ext, ());
}
}