Skip to content

Commit

Permalink
Add tests for the execution module & fix events
Browse files Browse the repository at this point in the history
Add a unit test for the event generation, build a local wasm project on
massa-execution build. Then execute the test.

Fixes: the events where not stored correctly in `final_events`
Changes: Flter slot, include last slot.
  • Loading branch information
adrien-zinger committed Feb 15, 2022
1 parent 2156f2b commit 1c09292
Show file tree
Hide file tree
Showing 15 changed files with 291 additions and 10 deletions.
1 change: 1 addition & 0 deletions massa-execution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name = "massa_execution"
version = "0.1.0"
authors = ["Massa Labs <info@massa.net>"]
edition = "2021"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
11 changes: 11 additions & 0 deletions massa-execution/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn main() {
// Build also the wasm files massa-execution if test
let _ = std::process::Command::new("yarn")
.current_dir("./src/tests/wasm_tests")
.args(&["install"])
.status();
let _ = std::process::Command::new("yarn")
.current_dir("./src/tests/wasm_tests")
.args(&["build"])
.status();
}
6 changes: 4 additions & 2 deletions massa-execution/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,13 @@ impl ExecutionCommandSender {
}

/// Get events optionnally filtered by:
/// * start slot
/// * end slot
/// * start slot (default Slot(0,0))
/// * end slot (default Slot(0,0))
/// * emitter address
/// * original caller address
/// * operation id
///
/// If None, take all
pub async fn get_filtered_sc_output_event(
&self,
start: Option<Slot>,
Expand Down
4 changes: 4 additions & 0 deletions massa-execution/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub enum ExecutionError {

/// File error
FileError(String),

#[cfg(test)]
/// MassaHashError (used in test only): {0}
MassaHashError(#[from] massa_hash::MassaHashError),
}

macro_rules! bootstrap_file_error {
Expand Down
123 changes: 119 additions & 4 deletions massa-execution/src/tests/scenarios_mandatories.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// Copyright (c) 2021 MASSA LABS <info@massa.net>

use crate::{
config::ExecutionConfigs, start_controller, ExecutionSettings, SCELedger, SCELedgerEntry,
config::ExecutionConfigs, start_controller, ExecutionError, ExecutionSettings, SCELedger,
SCELedgerEntry,
};
use massa_hash::hash::Hash;
use massa_models::{
prehash::Map, Block, BlockHeader, BlockHeaderContent, BlockId, Operation, OperationContent,
OperationType, SerializeCompact,
};
use massa_models::prehash::Map;
use massa_models::{Address, Amount, Slot};
use massa_signature::{derive_public_key, generate_random_private_key};
use massa_signature::{
derive_public_key, generate_random_private_key, sign, PrivateKey, PublicKey,
};
use massa_time::MassaTime;
use serial_test::serial;
use std::str::FromStr;
Expand All @@ -24,10 +31,17 @@ pub fn generate_ledger_initial_file(values: &Map<Address, Amount>) -> NamedTempF
file_named
}

/// Return a randomized address
pub fn get_random_address() -> Address {
get_random_address_full().0
}

/// Same as `get_random_address()` and return priv_key and pub_key associated
/// to the address.
pub fn get_random_address_full() -> (Address, PrivateKey, PublicKey) {
let priv_key = generate_random_private_key();
let pub_key = derive_public_key(&priv_key);
Address::from_public_key(&pub_key)
(Address::from_public_key(&pub_key), priv_key, pub_key)
}

fn get_sample_settings() -> (NamedTempFile, ExecutionConfigs) {
Expand Down Expand Up @@ -171,3 +185,104 @@ async fn test_execution_with_bootstrap() {
.expect("Failed to send command");
manager.stop().await.expect("Failed to stop execution.");
}

#[tokio::test]
#[serial]
async fn generate_events() {
// Compile the `./wasm_tests` and generate a block with `event_test.wasm`
// as data. Then we check if we get an event as expected.

let bootstrap_state = crate::BootstrapExecutionState {
final_slot: Slot::new(0, 0),
final_ledger: get_sample_ledger(),
};
let (_config_file_keepalive, settings) = get_sample_settings();
let (command_sender, _event_receiver, manager) =
start_controller(settings, Some(bootstrap_state))
.await
.expect("Failed to start execution.");

let (sender_address, sender_private_key, sender_public_key) = get_random_address_full();
let event_test_data = include_bytes!("./wasm_tests/build/event_test.wasm");
let (block_id, block) = create_block(vec![create_execute_sc_operation(
sender_private_key,
sender_public_key,
event_test_data,
)
.unwrap()])
.unwrap();

let mut finalized_blocks: Map<BlockId, Block> = Default::default();
let blockclique: Map<BlockId, Block> = Default::default();
let slot = block.header.content.slot;
finalized_blocks.insert(block_id, block);
command_sender
.update_blockclique(finalized_blocks, blockclique)
.await
.expect("Failed to send command");

let events = command_sender
.get_filtered_sc_output_event(Some(slot), Some(slot), Some(sender_address), None, None)
.await
.unwrap();
assert!(!events.is_empty(), "At least one event was expected");
manager.stop().await.expect("Failed to stop execution.");
}

/// Create an operation for the given sender with `data` as bytecode.
/// Return a result that should be unwraped in the root `#[test]` routine.
fn create_execute_sc_operation(
sender_private_key: PrivateKey,
sender_public_key: PublicKey,
data: &[u8],
) -> Result<Operation, ExecutionError> {
let signature = sign(&Hash::compute_from("dummy".as_bytes()), &sender_private_key)?;
let op = OperationType::ExecuteSC {
data: data.to_vec(),
max_gas: u64::MAX,
coins: Amount::from_raw(u64::MAX),
gas_price: Amount::from_str("1")?,
};
Ok(Operation {
content: OperationContent {
sender_public_key,
fee: Amount::from_raw(0),
expire_period: 10,
op,
},
signature,
})
}

/// Create an almost empty block with a vector `operations` and a random
/// creator.
///
/// Return a result that should be unwraped in the root `#[test]` routine.
fn create_block(operations: Vec<Operation>) -> Result<(BlockId, Block), ExecutionError> {
let creator = generate_random_private_key();
let public_key = derive_public_key(&creator);

let operation_merkle_root = Hash::compute_from(
&operations.iter().fold(Vec::new(), |acc, v| {
[acc, v.to_bytes_compact().unwrap()].concat()
})[..],
);

let (hash, header) = BlockHeader::new_signed(
&creator,
BlockHeaderContent {
creator: public_key,
slot: Slot {
period: 0,
thread: 1,
},
parents: vec![],
operation_merkle_root,
endorsements: vec![],
},
)?;

let block = Block { header, operations };

Ok((hash, block))
}
3 changes: 3 additions & 0 deletions massa-execution/src/tests/wasm_tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/
node_modules
ledger.json
20 changes: 20 additions & 0 deletions massa-execution/src/tests/wasm_tests/asconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"targets": {
"debug": {
"binaryFile": "build/untouched.wasm",
"textFile": "build/untouched.wat",
"sourceMap": true,
"debug": true
},
"release": {
"binaryFile": "build/optimized.wasm",
"textFile": "build/optimized.wat",
"sourceMap": true,
"optimizeLevel": 3,
"shrinkLevel": 0,
"converge": false,
"noAssert": false
}
},
"options": {}
}
12 changes: 12 additions & 0 deletions massa-execution/src/tests/wasm_tests/bin/include_base64.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const fs = require('fs');
const file = process.argv[2];
const match = require('./match_include_base64');
const lines = String(fs.readFileSync(file)).split('\n').map(line => {
let res = match(line);
if (res != null) {
const data = fs.readFileSync(res[1], 'base64');
line = line.replace(res[0], JSON.stringify(data));
}
return line;
});
fs.writeFileSync(file.replace('.ts', '.m.ts'), lines.join('\n'), { flag: 'w+' });
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const include_bytes_regex = /include_base64\(["']+([\.a-z_\-\/\\ ]*)["']+\)[;]+/i;
module.exports = (line) => line.match(include_bytes_regex);
13 changes: 13 additions & 0 deletions massa-execution/src/tests/wasm_tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "wasm_tests",
"version": "0.1.0",
"scripts": {
"build": "asc src/event_test.ts --target release --exportRuntime --binaryFile build/event_test.wasm"
},
"dependencies": {
"assemblyscript": "^0.19.20",
"json-as": "^0.2.6",
"massa-sc-std": "2.0.0",
"visitor-as": "^0.6.0"
}
}
14 changes: 14 additions & 0 deletions massa-execution/src/tests/wasm_tests/src/event_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** ***************************************************************************
* This file show you an example of how to call a smart contract
*
* Once you ran the command `yarn run-sc
**/

import { generate_event, print } from "massa-sc-std";

export function main(_args: string): string {
print("hehehe")

generate_event("hello world")
return "0"
}
6 changes: 6 additions & 0 deletions massa-execution/src/tests/wasm_tests/src/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "assemblyscript/std/assembly.json",
"include": [
"./**/*.ts"
]
}
75 changes: 75 additions & 0 deletions massa-execution/src/tests/wasm_tests/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


as-string-sink@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/as-string-sink/-/as-string-sink-0.4.2.tgz#e3d51fe8c3810d61b6ff2b5259fdd168b56d6bd9"
integrity sha512-eOuOYPUwwv4QumoJIa7XxyS0OjXI6CyZz2dtba/hTeG5hzhq58HzAPD2FbHhjfYUXQaSAacFnJ9g2Zlfw56LgQ==

assemblyscript@^0.19.20:
version "0.19.22"
resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.19.22.tgz#ef7eb8939864bd1b7603a9772e8f32e1fcfb8975"
integrity sha512-+Rclbx0+BI3qAe9fjc8XGbSUDaayTtjINnD19I4MmfpT2R43c9YTQERP36676shkPxb1fisDFZeSTL65Da8Q2g==
dependencies:
binaryen "102.0.0-nightly.20211028"
long "^5.2.0"
source-map-support "^0.5.20"

binaryen@102.0.0-nightly.20211028:
version "102.0.0-nightly.20211028"
resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-102.0.0-nightly.20211028.tgz#8f1efb0920afd34509e342e37f84313ec936afb2"
integrity sha512-GCJBVB5exbxzzvyt8MGDv/MeUjs6gkXDvf4xOIItRBptYl0Tz5sm1o/uG95YK0L0VeG5ajDu3hRtkBP2kzqC5w==

buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==

json-as@^0.2.6:
version "0.2.6"
resolved "https://registry.yarnpkg.com/json-as/-/json-as-0.2.6.tgz#4598b1b038a99fac2ebce3cfb0d126b7b346de29"
integrity sha512-adGnfJB57eQwVnw0yP3b0yUBhRWa9QUr/JDAgjYV2yvbl5ckV9P3ARAGwXywiZhOE854k1zL29vB7uGRZhM0cQ==
dependencies:
as-string-sink "^0.4.2"

lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=

long@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61"
integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==

massa-sc-std@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/massa-sc-std/-/massa-sc-std-2.0.0.tgz#7c75e3c04295deae4f80383c1d73c0ac45bf58f9"
integrity sha512-gpMX4iGWAzI6PTpb/wGYH3RN+q585o3ixFuaHnD7VuL2amqFlFW44RwZESPPVBcq+nBWypJxz2pt6hxOA/gjLQ==

source-map-support@^0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"

source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==

ts-mixer@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-5.4.1.tgz#b90db9ced48531aa17ce9184a2890d1e3c99b1e5"
integrity sha512-Zo9HgPCtNouDgJ+LGtrzVOjSg8+7WGQktIKLwAfaNrlOK1mWGlz1ejsAF/YqUEqAGjUTeB5fEg8gH9Aui6w9xA==

visitor-as@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/visitor-as/-/visitor-as-0.6.0.tgz#b0cca3c918bd9d396545faf08529d2b9ba968a40"
integrity sha512-4WcnwCLXWjhNkwJj9gSqh46sdIv9CyIvnSuwr61OOfrGCtN2mKcW5KE828OeEr1rYjEy0Z/CIdPBJKJRLsUgDA==
dependencies:
lodash.clonedeep "^4.5.0"
ts-mixer "^5.4.1"
2 changes: 1 addition & 1 deletion massa-execution/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ impl EventStore {
.iter()
// filter on slots
.filter_map(|(slot, ids)| {
if slot >= &start && slot < &end {
if slot >= &start && slot <= &end {
Some(ids)
} else {
None
Expand Down
9 changes: 6 additions & 3 deletions massa-execution/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl VM {
});
self.step_history
.iter()
.filter(|item| item.slot >= start && item.slot < end)
.filter(|item| item.slot >= start && item.slot <= end)
.flat_map(|item| {
item.events.get_filtered_sc_output_event(
start,
Expand Down Expand Up @@ -192,7 +192,7 @@ impl VM {
/// * max_final_events: max number of events kept in cache (todo should be removed when config become static)
pub(crate) fn run_final_step(&mut self, step: ExecutionStep, max_final_events: usize) {
// check if that step was already executed as the earliest active step
let history_item = if let Some(cached) = self.pop_cached_step(&step) {
let mut history_item = if let Some(cached) = self.pop_cached_step(&step) {
// if so, pop it
cached
} else {
Expand All @@ -210,7 +210,10 @@ impl VM {
.apply_changes(&history_item.ledger_changes);
ledger_step.final_ledger_slot.slot = step.slot;

self.final_events.extend(mem::take(&mut context.events));
// store the events and clear definitively from the item because the
// events should be weaks in memory
self.final_events
.extend(mem::take(&mut history_item.events));
self.final_events.prune(max_final_events)
}

Expand Down

0 comments on commit 1c09292

Please sign in to comment.