1mod addressing;
2mod cmd_call_function;
3mod cmd_call_method;
4mod cmd_export_package_definition;
5mod cmd_generate_key_pair;
6mod cmd_mint;
7mod cmd_new_account;
8mod cmd_new_badge_fixed;
9mod cmd_new_badge_mutable;
10mod cmd_new_simple_badge;
11mod cmd_new_token_fixed;
12mod cmd_new_token_mutable;
13mod cmd_publish;
14mod cmd_reset;
15mod cmd_run;
16mod cmd_set_current_epoch;
17mod cmd_set_current_time;
18mod cmd_set_default_account;
19mod cmd_show;
20mod cmd_show_configs;
21mod cmd_show_ledger;
22mod cmd_transfer;
23mod config;
24mod dumper;
25mod error;
26
27pub use addressing::*;
28pub use cmd_call_function::CallFunction;
29pub use cmd_call_method::CallMethod;
30pub use cmd_export_package_definition::*;
31pub use cmd_generate_key_pair::*;
32pub use cmd_new_account::*;
33pub use cmd_new_badge_fixed::*;
34pub use cmd_new_badge_mutable::*;
35pub use cmd_new_simple_badge::*;
36pub use cmd_new_token_fixed::*;
37pub use cmd_new_token_mutable::*;
38pub use cmd_publish::*;
39pub use cmd_reset::*;
40pub use cmd_run::*;
41pub use cmd_set_current_epoch::*;
42pub use cmd_set_current_time::*;
43pub use cmd_set_default_account::*;
44pub use cmd_show::*;
45pub use cmd_show_configs::*;
46pub use cmd_show_ledger::*;
47pub use cmd_transfer::*;
48pub use config::*;
49pub use dumper::*;
50pub use error::*;
51
52pub const DEFAULT_SCRYPTO_DIR_UNDER_HOME: &str = ".scrypto";
53pub const ENV_DATA_DIR: &str = "DATA_DIR";
54pub const ENV_DISABLE_MANIFEST_OUTPUT: &str = "DISABLE_MANIFEST_OUTPUT";
55
56use crate::prelude::*;
57use clap::{Parser, Subcommand};
58use radix_engine::blueprints::consensus_manager::*;
59use radix_engine::blueprints::models::FieldPayload;
60use radix_engine::system::system_db_reader::*;
61use radix_engine::transaction::*;
62use radix_engine_interface::api::ModuleId;
63use radix_engine_interface::blueprints::package::*;
64use radix_substate_store_impls::rocks_db::RocksdbSubstateStore;
65use radix_transactions::validation::TransactionValidator;
66
67#[derive(Parser, Debug)]
69#[clap(author, version, about, long_about = None, name = "resim")]
70pub struct ResimCli {
71 #[clap(subcommand)]
72 pub(crate) command: Command,
73}
74
75impl ResimCli {
76 pub fn get_command(&self) -> &Command {
77 &self.command
78 }
79}
80
81#[derive(Subcommand, Debug)]
82pub enum Command {
83 CallFunction(CallFunction),
84 CallMethod(CallMethod),
85 ExportPackageDefinition(ExportPackageDefinition),
86 GenerateKeyPair(GenerateKeyPair),
87 Mint(crate::resim::cmd_mint::Mint),
88 NewAccount(NewAccount),
89 NewSimpleBadge(NewSimpleBadge),
90 NewBadgeFixed(NewBadgeFixed),
91 NewBadgeMutable(NewBadgeMutable),
92 NewTokenFixed(NewTokenFixed),
93 NewTokenMutable(NewTokenMutable),
94 Publish(Publish),
95 Reset(Reset),
96 Run(Run),
97 SetCurrentEpoch(SetCurrentEpoch),
98 SetCurrentTime(SetCurrentTime),
99 SetDefaultAccount(SetDefaultAccount),
100 ShowConfigs(ShowConfigs),
101 ShowLedger(ShowLedger),
102 Show(Show),
103 Transfer(Transfer),
104}
105
106pub fn run() -> Result<(), String> {
107 let cli = ResimCli::parse();
108
109 let mut out = std::io::stdout();
110
111 match cli.command {
112 Command::CallFunction(cmd) => cmd.run(&mut out),
113 Command::CallMethod(cmd) => cmd.run(&mut out),
114 Command::ExportPackageDefinition(cmd) => cmd.run(&mut out),
115 Command::GenerateKeyPair(cmd) => cmd.run(&mut out),
116 Command::Mint(cmd) => cmd.run(&mut out),
117 Command::NewAccount(cmd) => cmd.run(&mut out),
118 Command::NewSimpleBadge(cmd) => cmd.run(&mut out).map(|_| ()),
119 Command::NewBadgeFixed(cmd) => cmd.run(&mut out),
120 Command::NewBadgeMutable(cmd) => cmd.run(&mut out),
121 Command::NewTokenFixed(cmd) => cmd.run(&mut out),
122 Command::NewTokenMutable(cmd) => cmd.run(&mut out),
123 Command::Publish(cmd) => cmd.run(&mut out),
124 Command::Reset(cmd) => cmd.run(&mut out),
125 Command::Run(cmd) => cmd.run(&mut out),
126 Command::SetCurrentEpoch(cmd) => cmd.run(&mut out),
127 Command::SetCurrentTime(cmd) => cmd.run(&mut out),
128 Command::SetDefaultAccount(cmd) => cmd.run(&mut out),
129 Command::ShowConfigs(cmd) => cmd.run(&mut out),
130 Command::ShowLedger(cmd) => cmd.run(&mut out),
131 Command::Show(cmd) => cmd.run(&mut out),
132 Command::Transfer(cmd) => cmd.run(&mut out),
133 }
134}
135
136pub fn handle_system_transaction<O: std::io::Write>(
137 manifest: SystemTransactionManifestV1,
138 initial_proofs: BTreeSet<NonFungibleGlobalId>,
139 trace: bool,
140 print_receipt: bool,
141 out: &mut O,
142) -> Result<TransactionReceipt, Error> {
143 let SimulatorEnvironment {
144 mut db, vm_modules, ..
145 } = SimulatorEnvironment::new()?;
146
147 let nonce = get_nonce()?;
148 let unique_hash = hash(format!("Simulator system transaction: {}", nonce));
149 let transaction = manifest.into_transaction(unique_hash);
150 let validator = TransactionValidator::new(&db, &NetworkDefinition::simulator());
151
152 let receipt = execute_and_commit_transaction(
153 &mut db,
154 &vm_modules,
155 &ExecutionConfig::for_system_transaction(NetworkDefinition::simulator())
156 .with_kernel_trace(trace),
157 transaction
158 .with_proofs(initial_proofs)
159 .into_executable(&validator)
160 .map_err(Error::TransactionPrepareError)?,
161 );
162
163 if print_receipt {
164 let encoder = AddressBech32Encoder::for_simulator();
165 let display_context = TransactionReceiptDisplayContextBuilder::new()
166 .encoder(&encoder)
167 .schema_lookup_from_db(&db)
168 .build();
169 writeln!(out, "{}", receipt.display(display_context)).map_err(Error::IOError)?;
170 }
171 drop(db);
172
173 process_receipt(receipt)
174}
175
176pub fn handle_manifest<O: std::io::Write>(
177 manifest: AnyManifest,
178 signing_keys: &Option<String>,
179 network: &Option<String>,
180 write_manifest: &Option<PathBuf>,
181 trace: bool,
182 print_receipt: bool,
183 out: &mut O,
184) -> Result<Option<TransactionReceipt>, String> {
185 let network = match network {
186 Some(n) => NetworkDefinition::from_str(n).map_err(Error::ParseNetworkError)?,
187 None => NetworkDefinition::simulator(),
188 };
189
190 manifest
191 .validate(ValidationRuleset::all())
192 .map_err(|err| format!("{err:?}"))?;
193
194 match write_manifest {
195 Some(path) => {
196 if env::var(ENV_DISABLE_MANIFEST_OUTPUT).is_err() {
197 let manifest_str =
198 decompile_any(&manifest, &network).map_err(Error::DecompileError)?;
199 write_ensuring_folder_exists(path, manifest_str).map_err(Error::IOError)?;
200 for (blob_hash, blob) in manifest.get_blobs() {
201 let mut blob_path = path
202 .parent()
203 .expect("Manifest file parent not found")
204 .to_owned();
205 blob_path.push(format!("{}.blob", blob_hash));
206 write_ensuring_folder_exists(blob_path, blob).map_err(Error::IOError)?;
207 }
208 }
209 Ok(None)
210 }
211 None => {
212 let SimulatorEnvironment {
213 mut db, vm_modules, ..
214 } = SimulatorEnvironment::new()?;
215
216 let sks = get_signing_keys(signing_keys)?;
217 let initial_proofs = sks
218 .into_iter()
219 .map(|e| NonFungibleGlobalId::from_public_key(e.public_key()))
220 .collect::<BTreeSet<NonFungibleGlobalId>>();
221 let nonce = get_nonce()?;
222 let validator = TransactionValidator::new(&db, &NetworkDefinition::simulator());
223 let transaction =
224 TestTransaction::new_from_any_manifest(manifest, nonce, initial_proofs)?;
225
226 let receipt = execute_and_commit_transaction(
227 &mut db,
228 &vm_modules,
229 &ExecutionConfig::for_test_transaction().with_kernel_trace(trace),
230 transaction
231 .into_executable(&validator)
232 .map_err(Error::TransactionPrepareError)?,
233 );
234
235 if print_receipt {
236 let encoder = AddressBech32Encoder::for_simulator();
237 let display_context = TransactionReceiptDisplayContextBuilder::new()
238 .encoder(&encoder)
239 .schema_lookup_from_db(&db)
240 .build();
241 writeln!(out, "{}", receipt.display(display_context)).map_err(Error::IOError)?;
242 }
243 drop(db);
244
245 process_receipt(receipt)
246 .map(Option::Some)
247 .map_err(|err| err.into())
248 }
249 }
250}
251
252pub fn process_receipt(receipt: TransactionReceipt) -> Result<TransactionReceipt, Error> {
253 match &receipt.result {
254 TransactionResult::Commit(commit) => {
255 let mut configs = get_configs()?;
256 configs.nonce = get_nonce()? + 1;
257 set_configs(&configs)?;
258
259 match &commit.outcome {
260 TransactionOutcome::Failure(error) => Err(Error::TransactionFailed(error.clone())),
261 TransactionOutcome::Success(_) => Ok(receipt),
262 }
263 }
264 TransactionResult::Reject(rejection) => {
265 Err(Error::TransactionRejected(rejection.reason.clone()))
266 }
267 TransactionResult::Abort(result) => Err(Error::TransactionAborted(result.reason.clone())),
268 }
269}
270
271pub fn parse_private_key_from_bytes(slice: &[u8]) -> Result<Secp256k1PrivateKey, Error> {
272 Secp256k1PrivateKey::from_bytes(slice).map_err(|_| Error::InvalidPrivateKey)
273}
274
275pub fn parse_private_key_from_str(key: &str) -> Result<Secp256k1PrivateKey, Error> {
276 hex::decode(key)
277 .map_err(|_| Error::InvalidPrivateKey)
278 .and_then(|bytes| parse_private_key_from_bytes(&bytes))
279}
280
281pub fn get_signing_keys(signing_keys: &Option<String>) -> Result<Vec<Secp256k1PrivateKey>, Error> {
282 let private_keys = if let Some(keys) = signing_keys {
283 keys.split(",")
284 .map(str::trim)
285 .filter(|s: &&str| !s.is_empty())
286 .map(parse_private_key_from_str)
287 .collect::<Result<Vec<Secp256k1PrivateKey>, Error>>()?
288 } else {
289 vec![get_default_private_key()?]
290 };
291
292 Ok(private_keys)
293}
294
295pub fn export_package_schema(
296 package_address: PackageAddress,
297) -> Result<BTreeMap<BlueprintVersionKey, BlueprintDefinition>, Error> {
298 let SimulatorEnvironment { db, .. } = SimulatorEnvironment::new()?;
299
300 let system_reader = SystemDatabaseReader::new(&db);
301 let package_definition = system_reader.get_package_definition(package_address);
302 Ok(package_definition)
303}
304
305pub fn export_object_info(component_address: ComponentAddress) -> Result<ObjectInfo, Error> {
306 let SimulatorEnvironment { db, .. } = SimulatorEnvironment::new()?;
307
308 let system_reader = SystemDatabaseReader::new(&db);
309 system_reader
310 .get_object_info(component_address)
311 .map_err(|_| Error::ComponentNotFound(component_address))
312}
313
314pub fn export_schema(
315 node_id: &NodeId,
316 schema_hash: SchemaHash,
317) -> Result<VersionedScryptoSchema, Error> {
318 let SimulatorEnvironment { db, .. } = SimulatorEnvironment::new()?;
319
320 let system_reader = SystemDatabaseReader::new(&db);
321 let schema = system_reader
322 .get_schema(node_id, &schema_hash)
323 .map_err(|_| Error::SchemaNotFound(*node_id, schema_hash))?;
324
325 Ok(schema.as_ref().clone())
326}
327
328pub fn export_blueprint_interface(
329 package_address: PackageAddress,
330 blueprint_name: &str,
331) -> Result<BlueprintInterface, Error> {
332 let interface = export_package_schema(package_address)?
333 .get(&BlueprintVersionKey::new_default(blueprint_name))
334 .cloned()
335 .ok_or(Error::BlueprintNotFound(
336 package_address,
337 blueprint_name.to_string(),
338 ))?
339 .interface;
340 Ok(interface)
341}
342
343pub fn get_blueprint_id(component_address: ComponentAddress) -> Result<BlueprintId, Error> {
344 let SimulatorEnvironment { db, .. } = SimulatorEnvironment::new()?;
345
346 let system_reader = SystemDatabaseReader::new(&db);
347 let object_info = system_reader
348 .get_object_info(component_address)
349 .expect("Unexpected");
350 Ok(object_info.blueprint_info.blueprint_id)
351}
352
353pub fn db_upsert_timestamps(
354 milli_timestamp: ProposerMilliTimestampSubstate,
355 minute_timestamp: ProposerMinuteTimestampSubstate,
356) -> Result<(), Error> {
357 let SimulatorEnvironment { mut db, .. } = SimulatorEnvironment::new()?;
358
359 let mut writer = SystemDatabaseWriter::new(&mut db);
360
361 writer
362 .write_typed_object_field(
363 CONSENSUS_MANAGER.as_node_id(),
364 ModuleId::Main,
365 ConsensusManagerField::ProposerMilliTimestamp.field_index(),
366 ConsensusManagerProposerMilliTimestampFieldPayload::from_content_source(
367 milli_timestamp,
368 ),
369 )
370 .unwrap();
371
372 writer
373 .write_typed_object_field(
374 CONSENSUS_MANAGER.as_node_id(),
375 ModuleId::Main,
376 ConsensusManagerField::ProposerMinuteTimestamp.field_index(),
377 ConsensusManagerProposerMinuteTimestampFieldPayload::from_content_source(
378 minute_timestamp,
379 ),
380 )
381 .unwrap();
382
383 Ok(())
384}
385
386pub fn db_upsert_epoch(epoch: Epoch) -> Result<(), Error> {
387 let SimulatorEnvironment { mut db, .. } = SimulatorEnvironment::new()?;
388
389 let reader = SystemDatabaseReader::new(&db);
390
391 let mut consensus_mgr_state = reader
392 .read_typed_object_field::<ConsensusManagerStateFieldPayload>(
393 CONSENSUS_MANAGER.as_node_id(),
394 ModuleId::Main,
395 ConsensusManagerField::State.field_index(),
396 )
397 .unwrap_or_else(|_| {
398 ConsensusManagerStateFieldPayload::from_content_source(ConsensusManagerSubstate {
399 epoch: Epoch::zero(),
400 effective_epoch_start_milli: 0,
401 actual_epoch_start_milli: 0,
402 round: Round::zero(),
403 current_leader: Some(0),
404 started: true,
405 })
406 })
407 .fully_update_and_into_latest_version();
408
409 consensus_mgr_state.epoch = epoch;
410
411 let mut writer = SystemDatabaseWriter::new(&mut db);
412
413 writer
414 .write_typed_object_field(
415 CONSENSUS_MANAGER.as_node_id(),
416 ModuleId::Main,
417 ConsensusManagerField::State.field_index(),
418 ConsensusManagerStateFieldPayload::from_content_source(consensus_mgr_state),
419 )
420 .unwrap();
421
422 Ok(())
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428
429 fn test_no_value() {
430 let mut out = std::io::stdout();
431 let rtn = Reset {}.run(&mut out);
432 assert!(rtn.is_ok(), "Reset failed with: {:?}", rtn);
433 let new_account = NewAccount {
434 network: None,
435 manifest: None,
436 trace: false,
437 };
438 assert!(new_account.run(&mut out).is_ok());
439 let cmd = Show { address: None };
440 assert!(cmd.run(&mut out).is_ok());
441 }
442
443 fn test_pre_process_manifest() {
444 temp_env::with_vars(
445 vec![
446 (
447 "faucet",
448 Some("system_sim1qsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpql4sktx"),
449 ),
450 (
451 "xrd",
452 Some("resource_sim1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqu57yag"),
453 ),
454 ],
455 || {
456 let manifest = r#"CALL_METHOD ComponentAddress("${ faucet }") "free";\nTAKE_ALL_FROM_WORKTOP ResourceAddress("${xrd}") Bucket("bucket1");\n"#;
457 let after = r#"CALL_METHOD ComponentAddress("system_sim1qsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpql4sktx") "free";\nTAKE_ALL_FROM_WORKTOP ResourceAddress("resource_sim1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqu57yag") Bucket("bucket1");\n"#;
458 assert_eq!(Run::pre_process_manifest(manifest), after);
459 },
460 );
461 }
462
463 fn test_set_default_account_validation() {
464 let mut out = std::io::stdout();
465 let private_key = Secp256k1PrivateKey::from_hex(
466 "6847c11e2d602548dbf38789e0a1f4543c1e7719e4f591d4aa6e5684f5c13d9c",
467 )
468 .unwrap();
469 let public_key = private_key.public_key().to_string();
470
471 let make_cmd = |key_string: String| SetDefaultAccount {
472 component_address: SimulatorComponentAddress::from_str(
473 "account_sim1c9yeaya6pehau0fn7vgavuggeev64gahsh05dauae2uu25njk224xz",
474 )
475 .unwrap(),
476 private_key: key_string,
477 owner_badge: SimulatorNonFungibleGlobalId::from_str(
478 "resource_sim1ngvrads4uj3rgq2v9s78fzhvry05dw95wzf3p9r8skhqusf44dlvmr:#1#",
479 )
480 .unwrap(),
481 };
482
483 assert!(make_cmd(private_key.to_hex()).run(&mut out).is_ok());
484 assert!(make_cmd(public_key.to_string()).run(&mut out).is_err());
485 }
486
487 #[test]
488 fn serial_resim_command_tests() {
489 test_no_value();
490 test_pre_process_manifest();
491 test_set_default_account_validation();
492 }
493}