radix_clis/resim/
mod.rs

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/// Build fast, reward everyone, and scale without friction
68#[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}