Skip to content

Commit

Permalink
correctly solving string slices usign script offset
Browse files Browse the repository at this point in the history
  • Loading branch information
xunilrj committed Aug 30, 2023
1 parent 0e099d2 commit 0068573
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 117 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fuel-vm = "0.35.3"
# Dependencies from the `fuels-rs` repository:
fuels-core = "0.45"
fuels-accounts = "0.45"
fuels-programs = "0.45"

# Dependencies from the `forc-wallet` repository:
forc-wallet = "0.2.5"
Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fuel-tx = { workspace = true, features = ["builder"] }
fuel-vm = { workspace = true }
fuels-accounts = { workspace = true }
fuels-core = { workspace = true }
fuels-programs = { workspace = true }
futures = "0.3"
hex = "0.4.3"
rand = "0.8"
Expand Down
2 changes: 1 addition & 1 deletion forc-plugins/forc-client/src/op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ mod run;
mod submit;

pub use deploy::deploy;
pub use run::run;
pub use run::{encode_script_data, run, script_offset};
pub use submit::submit;
40 changes: 24 additions & 16 deletions forc-plugins/forc-client/src/op/run/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,31 @@ mod tests {

#[test]
fn test_main_encoding_success() {
let test_json_abi = r#"{"types":[{"typeId":0,"type":"()","components":[],"typeParameters":null},
{"typeId":1,"type":"bool","components":null,"typeParameters":null},{"typeId":2,"type":"u8","components":null,
"typeParameters":null}],"functions":[{"inputs":[{"name":"test_u8","type":2,"typeArguments":null},{"name":"test_bool",
"type":1,"typeArguments":null}],"name":"main","output":{"name":"","type":0,"typeArguments":null},"attributes":null}],
"loggedTypes":[],"messagesTypes":[],"configurables":[]}"#;
let test_json_abi = r#"{"types":[
{"typeId":0,"type":"()","components":[],"typeParameters":null},
{"typeId":1,"type":"bool","components":null,"typeParameters":null},
{"typeId":2,"type":"u8","components":null,"typeParameters":null},
{"typeId":3,"type":"str","components":null,"typeParameters":null}
], "functions":[{"inputs":[
{"name":"test_u8","type":2,"typeArguments":null},
{"name":"test_bool","type":1,"typeArguments":null},
{"name":"test_str","type":3,"typeArguments":null}
],"name":"main","output":{"name":"","type":0,"typeArguments":null},"attributes":null}],"loggedTypes":[],"messagesTypes":[],"configurables":[]}"#;
let call_handler = ScriptCallHandler::from_json_abi_str(test_json_abi).unwrap();
let values = ["2", "true"];

let test_data_offset = 0;
let encoded_bytes = call_handler
.encode_arguments(&values)
.unwrap()
.resolve(test_data_offset);
let expected_bytes = vec![
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8,
];
assert_eq!(encoded_bytes, expected_bytes);

// Encoded from strings
let values = ["2", "true", "abcd"];
let encoded_bytes = call_handler.encode_arguments(&values).unwrap().resolve(0);
assert_eq!(
encoded_bytes,
vec![
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, // 2
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, // true
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 32u8, // "abcd".ptr
0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 4u8, // "abcd".len
97u8, 98u8, 99u8, 100u8 // "abcd"
]
);
}

#[test]
Expand Down
41 changes: 30 additions & 11 deletions forc-plugins/forc-client/src/op/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use forc_pkg::{self as pkg, fuel_core_not_running, PackageManifestFile};
use forc_tracing::println_warning;
use forc_util::tx_utils::format_log_receipts;
use fuel_core_client::client::FuelClient;
use fuel_tx::{ContractId, Transaction, TransactionBuilder};
use fuel_tx::{
field::ScriptData, ConsensusParameters, ContractId, Transaction, TransactionBuilder,
};
use pkg::BuiltPackage;
use std::time::Duration;
use std::{path::PathBuf, str::FromStr};
Expand Down Expand Up @@ -72,16 +74,7 @@ pub async fn run_pkg(
let client = FuelClient::new(node_url.clone())?;

let script_data = match (&command.data, &command.args) {
(None, Some(args)) => {
let minify_json_abi = true;
let package_json_abi = compiled
.json_abi_string(minify_json_abi)?
.ok_or_else(|| anyhow::anyhow!("Missing json abi string"))?;
let main_arg_handler = ScriptCallHandler::from_json_abi_str(&package_json_abi)?;
let args = args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>();
let unresolved_bytes = main_arg_handler.encode_arguments(args.as_slice())?;
unresolved_bytes.resolve(0)
}
(None, Some(args)) => encode_script_data(compiled, args)?,
(Some(_), Some(_)) => {
bail!("Both --args and --data provided, must choose one.")
}
Expand Down Expand Up @@ -135,6 +128,32 @@ pub async fn run_pkg(
}
}

/// See https://specs.fuel.network/master/fuel-vm/index.html#vm-initialization
pub fn script_offset(parameters: &ConsensusParameters, script: &fuel_tx::Script) -> usize {
let tx_offset = fuel_tx::Bytes32::LEN // Tx ID
+ fuels_core::constants::WORD_SIZE // Tx size
+ parameters.max_inputs as usize * (fuel_tx::AssetId::LEN + fuels_core::constants::WORD_SIZE); // Asset ID/Balance coin input pairs

tx_offset + script.script_data_offset()
}

pub fn encode_script_data(
compiled: &BuiltPackage,
args: &[String],
) -> Result<Vec<u8>, anyhow::Error> {
let minify_json_abi = true;
let package_json_abi = compiled
.json_abi_string(minify_json_abi)?
.ok_or_else(|| anyhow::anyhow!("Missing json abi string"))?;

let main_arg_handler = ScriptCallHandler::from_json_abi_str(&package_json_abi)?;

let args = args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>();
let unresolved_bytes = main_arg_handler.encode_arguments(args.as_slice())?;

Ok(unresolved_bytes.resolve(0))
}

async fn try_send_tx(
node_url: &str,
tx: &Transaction,
Expand Down
1 change: 0 additions & 1 deletion sway-core/src/ir_generation/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ pub(super) fn compile_script(
messages_types_map: &HashMap<TypeId, MessageId>,
test_fns: &[(ty::TyFunctionDecl, DeclRefFunction)],
) -> Result<Module, Vec<CompileError>> {
println!("1");
let module = Module::new(context, Kind::Script);
let mut md_mgr = MetadataManager::default();

Expand Down
4 changes: 2 additions & 2 deletions sway-lib-core/src/ops.sw
Original file line number Diff line number Diff line change
Expand Up @@ -1299,8 +1299,8 @@ impl Eq for str {
} else {
let self_ptr = self.as_ptr();
let other_ptr = other.as_ptr();
asm(r1: self_ptr, r2: other_ptr, r3, r4) {
addi r3 zero i32;
let l = self.len();
asm(r1: self_ptr, r2: other_ptr, r3: l, r4) {
meq r4 r1 r2 r3;
r4: bool
}
Expand Down
1 change: 0 additions & 1 deletion sway-lib-core/src/str.sw
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ impl str {
len
}
}

53 changes: 35 additions & 18 deletions test/src/e2e_vm_tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,41 +137,58 @@ pub(crate) enum VMExecutionResult {
pub(crate) fn runs_in_vm(
script: BuiltPackage,
script_data: Option<Vec<u8>>,
verbose: bool,
) -> Result<VMExecutionResult> {
match script.descriptor.target {
BuildTarget::Fuel => {
let storage = MemoryStorage::default();

let script_data = script_data.unwrap_or_default();

let rng = &mut StdRng::seed_from_u64(2322u64);
let maturity = 1.into();
let script_data = script_data.unwrap_or_default();
let block_height = (u32::MAX >> 1).into();
let params = ConsensusParameters {
// The default max length is 1MB which isn't enough for the bigger tests.
max_script_length: 64 * 1024 * 1024,
..ConsensusParameters::DEFAULT
};

let tx = TransactionBuilder::script(script.bytecode.bytes, script_data)
.with_params(params)
.add_unsigned_coin_input(
rng.gen(),
rng.gen(),
1,
Default::default(),
rng.gen(),
0u32.into(),
)
.gas_limit(fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx)
.maturity(maturity)
.finalize_checked(block_height, &GasCosts::default());
let tx: fuel_vm::checked_transaction::Checked<Script> =
TransactionBuilder::script(script.bytecode.bytes, script_data)
.with_params(params)
.add_unsigned_coin_input(
rng.gen(),
rng.gen(),
1,
Default::default(),
rng.gen(),
0u32.into(),
)
.gas_limit(fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx)
.maturity(maturity)
.finalize_checked(block_height, &GasCosts::default());

if verbose {
let offset = forc_client::op::script_offset(
&ConsensusParameters::default(),
tx.transaction(),
);
println!("\nScript Data Ofsset: {offset}");
}

let mut i = Interpreter::with_storage(storage, Default::default(), GasCosts::default());
let transition = i.transact(tx)?;
Ok(VMExecutionResult::Fuel(
*transition.state(),
transition.receipts().to_vec(),
))
let state = *transition.state();
let receipts = transition.receipts().to_vec();

if verbose {
for r in receipts.iter() {
println!("{r:?}");
}
}

Ok(VMExecutionResult::Fuel(state, receipts))
}
BuildTarget::EVM => {
let mut evm = revm::new();
Expand Down
46 changes: 41 additions & 5 deletions test/src/e2e_vm_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ impl TestContext {
},
)
}
async fn run(&self, test: TestDescription, output: &mut String) -> Result<()> {

async fn run(&self, test: TestDescription, output: &mut String, verbose: bool) -> Result<()> {
let context = self;
let TestDescription {
name,
Expand Down Expand Up @@ -140,7 +141,7 @@ impl TestContext {
)));
}

let result = harness::runs_in_vm(compiled.clone(), script_data)?;
let result = harness::runs_in_vm(compiled.clone(), script_data, verbose)?;
let result = match result {
harness::VMExecutionResult::Fuel(state, receipts) => {
match state {
Expand Down Expand Up @@ -423,11 +424,11 @@ pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result

let result = if !filter_config.first_only {
context
.run(test, &mut output)
.run(test, &mut output, run_config.verbose)
.instrument(tracing::trace_span!("E2E", i))
.await
} else {
context.run(test, &mut output).await
context.run(test, &mut output, run_config.verbose).await
};

if let Err(err) = result {
Expand Down Expand Up @@ -602,11 +603,46 @@ fn parse_test_toml(path: &Path) -> Result<TestDescription> {
bail!("'fail' tests must contain some FileCheck verification directives.");
}

let parse_value = |operand: &str| -> u64 {
if let Some(name) = operand.strip_prefix('$') {
toml_content
.get(name)
.map(|x| x.to_string())
.and_then(|x| x.parse::<u64>().ok())
.unwrap()
} else {
u64::from_str_radix(operand, 16).unwrap()
}
};

// allow variables and simple expressions to script_data
let script_data = match &category {
TestCategory::Runs | TestCategory::RunsWithContract => {
match toml_content.get("script_data") {
Some(toml::Value::String(v)) => {
let decoded = hex::decode(v)
// quick parser and evaluator for script data
let mut data = String::new();
let tokens = v.split_whitespace();
for t in tokens {
if t.starts_with('(') && t.ends_with(')') {
let t = &t[1..(t.len() - 1)];
let mut operands = t.split('+');
let l = operands.next().unwrap();
let r = operands.next().unwrap();

let l_value = parse_value(l);
let r_value = parse_value(r);

let result = l_value + r_value;
let result = format!("{:0>16x}", result);
let result = &result[(result.len() - r.len())..result.len()];
data.push_str(result);
} else {
data.push_str(t);
}
}

let decoded = hex::decode(data)
.map_err(|e| anyhow!("Invalid hex value for 'script_data': {}", e))?;
Some(decoded)
}
Expand Down
Loading

0 comments on commit 0068573

Please sign in to comment.