1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use crate::internal_prelude::*;
use crate::system::system_modules::costing::SystemLoanFeeReserve;
use crate::vm::wasm::*;
use crate::vm::wasm_runtime::NoOpWasmRuntime;
use crate::vm::ScryptoVmVersion;
use crate::{errors::InvokeError, transaction::CostingParameters};
use radix_engine_interface::blueprints::package::*;
use radix_transactions::prelude::TransactionCostingParameters;
use sbor::rust::iter;

#[derive(Debug)]
pub enum ExtractSchemaError {
    InvalidWasm(PrepareError),
    RunSchemaGenError(InvokeError<WasmRuntimeError>),
    SchemaDecodeError(DecodeError),
}

impl From<PrepareError> for ExtractSchemaError {
    fn from(value: PrepareError) -> Self {
        ExtractSchemaError::InvalidWasm(value)
    }
}

pub fn extract_definition(code: &[u8]) -> Result<PackageDefinition, ExtractSchemaError> {
    let function_exports = WasmModule::init(code)
        .and_then(WasmModule::to_bytes)?
        .1
        .into_iter()
        .filter(|s| s.ends_with("_schema"));

    // Validate WASM
    let validator = ScryptoV1WasmValidator::new(ScryptoVmVersion::latest());
    let code_hash = CodeHash(Hash([0u8; 32]));
    let instrumented_code = validator
        .validate(&code, iter::empty())
        .map_err(|e| ExtractSchemaError::InvalidWasm(e))?
        .0;

    // Execute with empty state (with default cost unit limit)
    let wasm_engine = DefaultWasmEngine::default();
    let fee_reserve = SystemLoanFeeReserve::new(
        &CostingParameters::babylon_genesis(),
        &TransactionCostingParameters {
            tip_percentage: 0,
            free_credit_in_xrd: Decimal::try_from(PREVIEW_CREDIT_IN_XRD).unwrap(),
            abort_when_loan_repaid: false,
        },
    );
    let mut wasm_execution_units_consumed = 0;
    let mut runtime: Box<dyn WasmRuntime> = Box::new(NoOpWasmRuntime::new(
        fee_reserve,
        &mut wasm_execution_units_consumed,
    ));
    let mut instance = wasm_engine.instantiate(code_hash, &instrumented_code);
    let mut blueprints = index_map_new();
    for function_export in function_exports {
        let rtn = instance
            .invoke_export(&function_export, vec![], &mut runtime)
            .map_err(ExtractSchemaError::RunSchemaGenError)?;

        let name = function_export.replace("_schema", "").to_string();
        let blueprint_setup: BlueprintDefinitionInit =
            scrypto_decode(rtn.as_slice()).map_err(ExtractSchemaError::SchemaDecodeError)?;
        blueprints.insert(name.clone(), blueprint_setup);
    }

    Ok(PackageDefinition { blueprints })
}