svm_bridge.rs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. use {
  2. agave_feature_set::FeatureSet,
  3. agave_syscalls::{
  4. SyscallAbort, SyscallGetClockSysvar, SyscallInvokeSignedRust, SyscallLog,
  5. SyscallLogBpfComputeUnits, SyscallLogPubkey, SyscallLogU64, SyscallMemcpy, SyscallMemset,
  6. SyscallSetReturnData,
  7. },
  8. log::*,
  9. solana_account::{Account, AccountSharedData, ReadableAccount},
  10. solana_clock::{Clock, Slot, UnixTimestamp},
  11. solana_compute_budget::compute_budget::ComputeBudget,
  12. solana_message::AccountKeys,
  13. solana_program_runtime::{
  14. invoke_context::InvokeContext,
  15. loaded_programs::{
  16. BlockRelation, ForkGraph, LoadProgramMetrics, ProgramCacheEntry,
  17. ProgramRuntimeEnvironments,
  18. },
  19. solana_sbpf::{
  20. program::{BuiltinProgram, SBPFVersion},
  21. vm::Config,
  22. },
  23. },
  24. solana_pubkey::Pubkey,
  25. solana_svm::{
  26. transaction_processing_result::TransactionProcessingResult,
  27. transaction_processor::TransactionBatchProcessor,
  28. },
  29. solana_svm_callback::{InvokeContextCallback, TransactionProcessingCallback},
  30. solana_sysvar_id::SysvarId,
  31. solana_transaction::sanitized::SanitizedTransaction,
  32. std::{
  33. collections::HashMap,
  34. sync::{Arc, RwLock},
  35. time::{SystemTime, UNIX_EPOCH},
  36. },
  37. };
  38. mod transaction {
  39. pub use solana_transaction_error::TransactionResult as Result;
  40. }
  41. const DEPLOYMENT_SLOT: u64 = 0;
  42. const DEPLOYMENT_EPOCH: u64 = 0;
  43. pub struct MockForkGraph {}
  44. impl ForkGraph for MockForkGraph {
  45. fn relationship(&self, a: Slot, b: Slot) -> BlockRelation {
  46. match a.cmp(&b) {
  47. std::cmp::Ordering::Less => BlockRelation::Ancestor,
  48. std::cmp::Ordering::Equal => BlockRelation::Equal,
  49. std::cmp::Ordering::Greater => BlockRelation::Descendant,
  50. }
  51. }
  52. }
  53. pub struct MockBankCallback {
  54. pub feature_set: Arc<FeatureSet>,
  55. pub account_shared_data: RwLock<HashMap<Pubkey, AccountSharedData>>,
  56. }
  57. impl InvokeContextCallback for MockBankCallback {}
  58. impl TransactionProcessingCallback for MockBankCallback {
  59. fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
  60. if let Some(data) = self.account_shared_data.read().unwrap().get(account) {
  61. if data.lamports() == 0 {
  62. None
  63. } else {
  64. owners.iter().position(|entry| data.owner() == entry)
  65. }
  66. } else {
  67. None
  68. }
  69. }
  70. fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
  71. debug!(
  72. "Get account {pubkey} shared data, thread {:?}",
  73. std::thread::current().name()
  74. );
  75. self.account_shared_data
  76. .read()
  77. .unwrap()
  78. .get(pubkey)
  79. .cloned()
  80. }
  81. fn add_builtin_account(&self, name: &str, program_id: &Pubkey) {
  82. let account_data = AccountSharedData::from(Account {
  83. lamports: 5000,
  84. data: name.as_bytes().to_vec(),
  85. owner: solana_sdk_ids::native_loader::id(),
  86. executable: true,
  87. rent_epoch: 0,
  88. });
  89. self.account_shared_data
  90. .write()
  91. .unwrap()
  92. .insert(*program_id, account_data);
  93. }
  94. }
  95. impl MockBankCallback {
  96. pub fn new(account_map: Vec<(Pubkey, AccountSharedData)>) -> Self {
  97. Self {
  98. feature_set: Arc::new(FeatureSet::default()),
  99. account_shared_data: RwLock::new(HashMap::from_iter(account_map)),
  100. }
  101. }
  102. #[allow(dead_code)]
  103. pub fn override_feature_set(&mut self, new_set: FeatureSet) {
  104. self.feature_set = Arc::new(new_set)
  105. }
  106. }
  107. pub struct LoadAndExecuteTransactionsOutput {
  108. // Vector of results indicating whether a transaction was executed or could not
  109. // be executed. Note executed transactions can still have failed!
  110. pub processing_results: Vec<TransactionProcessingResult>,
  111. }
  112. pub struct TransactionBatch<'a> {
  113. lock_results: Vec<transaction::Result<()>>,
  114. sanitized_txs: std::borrow::Cow<'a, [SanitizedTransaction]>,
  115. }
  116. impl<'a> TransactionBatch<'a> {
  117. pub fn new(
  118. lock_results: Vec<transaction::Result<()>>,
  119. sanitized_txs: std::borrow::Cow<'a, [SanitizedTransaction]>,
  120. ) -> Self {
  121. assert_eq!(lock_results.len(), sanitized_txs.len());
  122. Self {
  123. lock_results,
  124. sanitized_txs,
  125. }
  126. }
  127. pub fn lock_results(&self) -> &Vec<transaction::Result<()>> {
  128. &self.lock_results
  129. }
  130. pub fn sanitized_transactions(&self) -> &[SanitizedTransaction] {
  131. &self.sanitized_txs
  132. }
  133. }
  134. pub fn create_custom_environment<'a>() -> BuiltinProgram<InvokeContext<'a>> {
  135. let compute_budget = ComputeBudget::default();
  136. let vm_config = Config {
  137. max_call_depth: compute_budget.max_call_depth,
  138. stack_frame_size: compute_budget.stack_frame_size,
  139. enable_address_translation: true,
  140. enable_stack_frame_gaps: true,
  141. instruction_meter_checkpoint_distance: 10000,
  142. enable_instruction_meter: true,
  143. enable_instruction_tracing: true,
  144. enable_symbol_and_section_labels: true,
  145. reject_broken_elfs: true,
  146. noop_instruction_rate: 256,
  147. sanitize_user_provided_values: true,
  148. enabled_sbpf_versions: SBPFVersion::V0..=SBPFVersion::V3,
  149. optimize_rodata: false,
  150. aligned_memory_mapping: true,
  151. };
  152. // Register system calls that the compiled contract calls during execution.
  153. let mut loader = BuiltinProgram::new_loader(vm_config);
  154. loader
  155. .register_function("abort", SyscallAbort::vm)
  156. .expect("Registration failed");
  157. loader
  158. .register_function("sol_log_", SyscallLog::vm)
  159. .expect("Registration failed");
  160. loader
  161. .register_function("sol_log_64_", SyscallLogU64::vm)
  162. .expect("Registration failed");
  163. loader
  164. .register_function("sol_log_compute_units_", SyscallLogBpfComputeUnits::vm)
  165. .expect("Registration failed");
  166. loader
  167. .register_function("sol_log_pubkey", SyscallLogPubkey::vm)
  168. .expect("Registration failed");
  169. loader
  170. .register_function("sol_memcpy_", SyscallMemcpy::vm)
  171. .expect("Registration failed");
  172. loader
  173. .register_function("sol_memset_", SyscallMemset::vm)
  174. .expect("Registration failed");
  175. loader
  176. .register_function("sol_invoke_signed_rust", SyscallInvokeSignedRust::vm)
  177. .expect("Registration failed");
  178. loader
  179. .register_function("sol_set_return_data", SyscallSetReturnData::vm)
  180. .expect("Registration failed");
  181. loader
  182. .register_function("sol_get_clock_sysvar", SyscallGetClockSysvar::vm)
  183. .expect("Registration failed");
  184. loader
  185. }
  186. pub fn create_executable_environment(
  187. fork_graph: Arc<RwLock<MockForkGraph>>,
  188. account_keys: &AccountKeys,
  189. mock_bank: &mut MockBankCallback,
  190. transaction_processor: &TransactionBatchProcessor<MockForkGraph>,
  191. ) {
  192. let mut program_cache = transaction_processor.program_cache.write().unwrap();
  193. program_cache.environments = ProgramRuntimeEnvironments {
  194. program_runtime_v1: Arc::new(create_custom_environment()),
  195. // We are not using program runtime v2
  196. program_runtime_v2: Arc::new(BuiltinProgram::new_loader(Config::default())),
  197. };
  198. program_cache.fork_graph = Some(Arc::downgrade(&fork_graph));
  199. // add programs to cache
  200. for key in account_keys.iter() {
  201. if let Some(account) = mock_bank.get_account_shared_data(key) {
  202. if account.executable()
  203. && *account.owner() == solana_sdk_ids::bpf_loader_upgradeable::id()
  204. {
  205. let data = account.data();
  206. let program_data_account_key = Pubkey::try_from(data[4..].to_vec()).unwrap();
  207. let program_data_account = mock_bank
  208. .get_account_shared_data(&program_data_account_key)
  209. .unwrap();
  210. let program_data = program_data_account.data();
  211. let elf_bytes = program_data[45..].to_vec();
  212. let program_runtime_environment =
  213. program_cache.environments.program_runtime_v1.clone();
  214. program_cache.assign_program(
  215. *key,
  216. Arc::new(
  217. ProgramCacheEntry::new(
  218. &solana_sdk_ids::bpf_loader_upgradeable::id(),
  219. program_runtime_environment,
  220. 0,
  221. 0,
  222. &elf_bytes,
  223. elf_bytes.len(),
  224. &mut LoadProgramMetrics::default(),
  225. )
  226. .unwrap(),
  227. ),
  228. );
  229. }
  230. }
  231. }
  232. // We must fill in the sysvar cache entries
  233. let time_now = SystemTime::now()
  234. .duration_since(UNIX_EPOCH)
  235. .expect("Time went backwards")
  236. .as_secs() as i64;
  237. let clock = Clock {
  238. slot: DEPLOYMENT_SLOT,
  239. epoch_start_timestamp: time_now.saturating_sub(10) as UnixTimestamp,
  240. epoch: DEPLOYMENT_EPOCH,
  241. leader_schedule_epoch: DEPLOYMENT_EPOCH,
  242. unix_timestamp: time_now as UnixTimestamp,
  243. };
  244. let mut account_data = AccountSharedData::default();
  245. account_data.set_data_from_slice(bincode::serialize(&clock).unwrap().as_slice());
  246. mock_bank
  247. .account_shared_data
  248. .write()
  249. .unwrap()
  250. .insert(Clock::id(), account_data);
  251. }