Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit event msg size & event number per operation #4768

Merged
merged 8 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 51 additions & 38 deletions massa-execution-exports/src/event_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,48 +55,61 @@ impl EventStore {
/// * operation id
/// * is final
pub fn get_filtered_sc_output_events(&self, filter: &EventFilter) -> VecDeque<SCOutputEvent> {
self.0
.iter()
.filter(|x| {
if let Some(start) = filter.start {
if x.context.slot < start {
return false;
}
}
if let Some(end) = filter.end {
if x.context.slot >= end {
return false;
}
}
if let Some(is_final) = filter.is_final {
if x.context.is_final != is_final {
return false;
}
}
if let Some(is_error) = filter.is_error {
if x.context.is_error != is_error {
return false;
}
self.get_filtered_sc_output_events_iter(filter)
.cloned()
.collect()
}

/// Get events iterator optionally filtered by given EventFilter
pub fn get_filtered_sc_output_events_iter<'b, 'a: 'b>(
&'a self,
filter: &'b EventFilter,
) -> impl Iterator<Item = &'a SCOutputEvent> + 'b {
// Note on lifetimes:
// 'a -> is the lifetime for self -> because the iterator returns items from self
// 'b -> is the lifetime for filter -> because the returning iterator captures filter
// , and we have lifetime 'a > 'b because filter can live less than self

self.0.iter().filter(|x| {
if let Some(start) = filter.start {
if x.context.slot < start {
return false;
}
match (filter.original_caller_address, x.context.call_stack.front()) {
(Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
(Some(_), None) => return false,
_ => (),
}
if let Some(end) = filter.end {
if x.context.slot >= end {
return false;
}
match (filter.emitter_address, x.context.call_stack.back()) {
(Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
(Some(_), None) => return false,
_ => (),
}
if let Some(is_final) = filter.is_final {
if x.context.is_final != is_final {
return false;
}
match (filter.original_operation_id, x.context.origin_operation_id) {
(Some(addr1), Some(addr2)) if addr1 != addr2 => return false,
(Some(_), None) => return false,
_ => (),
}
if let Some(is_error) = filter.is_error {
if x.context.is_error != is_error {
return false;
}
true
})
.cloned()
.collect()
}

match (filter.original_caller_address, x.context.call_stack.front()) {
(Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
(Some(_), None) => return false,
_ => (),
}
match (filter.emitter_address, x.context.call_stack.back()) {
(Some(addr1), Some(addr2)) if addr1 != *addr2 => return false,
(Some(_), None) => return false,
_ => (),
}
match (filter.original_operation_id, x.context.origin_operation_id) {
(Some(addr1), Some(addr2)) if addr1 != addr2 => return false,
(Some(_), None) => return false,
_ => (),
}

true
})
}
}

Expand Down
2 changes: 2 additions & 0 deletions massa-execution-exports/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,6 @@ pub struct ExecutionConfig {
pub max_recursive_calls_depth: u16,
/// Runtime condom middleware limits
pub condom_limits: CondomLimits,
/// Maximum number of event that an operation can emit
pub max_event_per_operation: usize,
}
3 changes: 2 additions & 1 deletion massa-execution-exports/src/test_exports/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ impl Default for ExecutionConfig {
denunciation_expire_periods: DENUNCIATION_EXPIRE_PERIODS,
broadcast_enabled: true,
broadcast_slot_execution_output_channel_capacity: 5000,
max_event_size: 50_000,
max_event_size: 512,
max_event_per_operation: 25,
max_function_length: 1000,
max_parameter_length: 1000,
chain_id: *CHAINID,
Expand Down
32 changes: 26 additions & 6 deletions massa-execution-worker/src/interface_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ use tracing::warn;
test
))]
use massa_models::datastore::Datastore;
use massa_models::execution::EventFilter;

/// helper for locking the context mutex
macro_rules! context_guard {
Expand Down Expand Up @@ -87,6 +88,7 @@ impl InterfaceImpl {
pub fn new_default(
sender_addr: Address,
operation_datastore: Option<Datastore>,
config: Option<ExecutionConfig>,
) -> InterfaceImpl {
use massa_db_exports::{MassaDBConfig, MassaDBController};
use massa_db_worker::MassaDB;
Expand All @@ -100,7 +102,7 @@ impl InterfaceImpl {
use parking_lot::RwLock;
use tempfile::TempDir;

let config = ExecutionConfig::default();
let config = config.unwrap_or_default();
let mip_stats_config = MipStatsConfig {
block_count_considered: MIP_STORE_STATS_BLOCK_CONSIDERED,
warn_announced_version_ratio: Ratio::new_raw(30, 100),
Expand Down Expand Up @@ -1149,7 +1151,25 @@ impl Interface for InterfaceImpl {
};

let mut context = context_guard!(self);

let event = context.event_create(data, false);
let event_filter = EventFilter {
start: None,
end: None,
emitter_address: None,
original_caller_address: None,
original_operation_id: event.context.origin_operation_id,
is_final: None,
is_error: None,
};
let event_per_op = context
.events
.get_filtered_sc_output_events_iter(&event_filter)
.count();
if event_per_op >= self.config.max_event_per_operation {
bail!("Too many event for this operation");
}

context.event_emit(event);
Ok(())
}
Expand Down Expand Up @@ -1783,7 +1803,7 @@ mod tests {
#[test]
fn test_get_keys() {
let sender_addr = Address::from_public_key(&KeyPair::generate(0).unwrap().get_public_key());
let interface = InterfaceImpl::new_default(sender_addr, None);
let interface = InterfaceImpl::new_default(sender_addr, None, None);

interface
.set_ds_value_wasmv1(b"k1", b"v1", Some(sender_addr.to_string()))
Expand Down Expand Up @@ -1812,7 +1832,7 @@ mod tests {
operation_datastore.insert(b"k2".to_vec(), b"v2".to_vec());
operation_datastore.insert(b"l3".to_vec(), b"v3".to_vec());

let interface = InterfaceImpl::new_default(sender_addr, Some(operation_datastore));
let interface = InterfaceImpl::new_default(sender_addr, Some(operation_datastore), None);

let op_keys = interface.get_op_keys_wasmv1(b"k").unwrap();

Expand All @@ -1824,7 +1844,7 @@ mod tests {
#[test]
fn test_native_amount() {
let sender_addr = Address::from_public_key(&KeyPair::generate(0).unwrap().get_public_key());
let interface = InterfaceImpl::new_default(sender_addr, None);
let interface = InterfaceImpl::new_default(sender_addr, None, None);

let amount1 = interface.native_amount_from_str_wasmv1("100").unwrap();
let amount2 = interface.native_amount_from_str_wasmv1("100").unwrap();
Expand Down Expand Up @@ -1902,7 +1922,7 @@ mod tests {
#[test]
fn test_base58_check_to_form() {
let sender_addr = Address::from_public_key(&KeyPair::generate(0).unwrap().get_public_key());
let interface = InterfaceImpl::new_default(sender_addr, None);
let interface = InterfaceImpl::new_default(sender_addr, None, None);

let data = "helloworld";
let encoded = interface.bytes_to_base58_check_wasmv1(data.as_bytes());
Expand All @@ -1913,7 +1933,7 @@ mod tests {
#[test]
fn test_comparison_function() {
let sender_addr = Address::from_public_key(&KeyPair::generate(0).unwrap().get_public_key());
let interface = InterfaceImpl::new_default(sender_addr, None);
let interface = InterfaceImpl::new_default(sender_addr, None, None);

// address
let addr1 =
Expand Down
57 changes: 55 additions & 2 deletions massa-execution-worker/src/tests/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ use std::str::FromStr;
use hex_literal::hex;
use sha2::Digest;

use crate::interface_impl::InterfaceImpl;
use massa_execution_exports::ExecutionConfig;
use massa_models::address::Address;
use massa_sc_runtime::Interface;

use crate::interface_impl::InterfaceImpl;

#[test]
fn test_hash_sha256() {
let interface = InterfaceImpl::new_default(
Address::from_str("AU12cMW9zRKFDS43Z2W88VCmdQFxmHjAo54XvuVV34UzJeXRLXW9M").unwrap(),
None,
None,
);
let actual_hash = interface.hash_sha256(b"something").unwrap();
let expected_hash =
Expand All @@ -27,6 +28,7 @@ fn test_evm_signature_verify() {
let interface = InterfaceImpl::new_default(
Address::from_str("AU12cMW9zRKFDS43Z2W88VCmdQFxmHjAo54XvuVV34UzJeXRLXW9M").unwrap(),
None,
None,
);

let _address = hex!("807a7bb5193edf9898b9092c1597bb966fe52514");
Expand Down Expand Up @@ -56,6 +58,7 @@ fn test_evm_get_pubkey_from_signature() {
let interface = InterfaceImpl::new_default(
Address::from_str("AU12cMW9zRKFDS43Z2W88VCmdQFxmHjAo54XvuVV34UzJeXRLXW9M").unwrap(),
None,
None,
);

// let _address = hex!("807a7bb5193edf9898b9092c1597bb966fe52514");
Expand Down Expand Up @@ -94,3 +97,53 @@ fn test_evm_get_pubkey_from_signature() {
assert!(result.is_err());
}
}

#[test]
fn test_emit_event() {
// emit 2 events and check that the 2nd event is rejected (because the limit is reached)

let config = ExecutionConfig {
max_event_per_operation: 1,
..Default::default()
};

let interface = InterfaceImpl::new_default(
Address::from_str("AU12cMW9zRKFDS43Z2W88VCmdQFxmHjAo54XvuVV34UzJeXRLXW9M").unwrap(),
None,
Some(config),
);

let res = interface.generate_event("foo".to_string());
assert!(res.is_ok());
let res_2 = interface.generate_event("foo".to_string());
assert!(res_2.is_err());
println!("res_2: {:?}", res_2);
if let Err(e) = res_2 {
assert!(e.to_string().contains("Too many event for this operation"));
}
}

#[test]
fn test_emit_event_too_large() {
// emit 2 events and check that the 2nd event is rejected (because the msg is too large)

let config = ExecutionConfig {
max_event_size: 10,
..Default::default()
};

let interface = InterfaceImpl::new_default(
Address::from_str("AU12cMW9zRKFDS43Z2W88VCmdQFxmHjAo54XvuVV34UzJeXRLXW9M").unwrap(),
None,
Some(config.clone()),
);

let res = interface.generate_event("a".repeat(config.max_event_size).to_string());
assert!(res.is_ok());
let res_2 = interface.generate_event("b".repeat(config.max_event_size + 1).to_string());
assert!(res_2.is_err());
println!("res_2: {:?}", res_2);
if let Err(e) = res_2 {
assert!(e.to_string().contains("Event data size is too large"));
}
}
4 changes: 3 additions & 1 deletion massa-models/src/config/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@ pub const ASYNC_MSG_CST_GAS_COST: u64 = 750_000;
/// Gas used by a base operation (transaction, roll buy, roll sell)
pub const BASE_OPERATION_GAS_COST: u64 = 800_000; // approx MAX_GAS_PER_BLOCK / MAX_OPERATIONS_PER_BLOCK
/// Maximum event size in bytes
pub const MAX_EVENT_DATA_SIZE: usize = 50_000;
pub const MAX_EVENT_DATA_SIZE: usize = 512;
/// Maximum event number that can be emitted for an operation
pub const MAX_EVENT_PER_OPERATION: usize = 25;
/// Maximum number of recursion for calls
pub const MAX_RECURSIVE_CALLS_DEPTH: u16 = 25;

Expand Down
3 changes: 2 additions & 1 deletion massa-node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ use massa_models::config::constants::{
use massa_models::config::{
BASE_OPERATION_GAS_COST, CHAINID, KEEP_EXECUTED_HISTORY_EXTRA_PERIODS,
MAX_BOOTSTRAP_FINAL_STATE_PARTS_SIZE, MAX_BOOTSTRAP_VERSIONING_ELEMENTS_SIZE,
MAX_EVENT_DATA_SIZE, MAX_MESSAGE_SIZE, MAX_RECURSIVE_CALLS_DEPTH,
MAX_EVENT_DATA_SIZE, MAX_EVENT_PER_OPERATION, MAX_MESSAGE_SIZE, MAX_RECURSIVE_CALLS_DEPTH,
MAX_RUNTIME_MODULE_CUSTOM_SECTION_DATA_LEN, MAX_RUNTIME_MODULE_CUSTOM_SECTION_LEN,
MAX_RUNTIME_MODULE_EXPORTS, MAX_RUNTIME_MODULE_FUNCTIONS, MAX_RUNTIME_MODULE_FUNCTION_NAME_LEN,
MAX_RUNTIME_MODULE_GLOBAL_INITIALIZER, MAX_RUNTIME_MODULE_IMPORTS, MAX_RUNTIME_MODULE_MEMORIES,
Expand Down Expand Up @@ -543,6 +543,7 @@ async fn launch(
block_dump_folder_path,
max_recursive_calls_depth: MAX_RECURSIVE_CALLS_DEPTH,
condom_limits,
max_event_per_operation: MAX_EVENT_PER_OPERATION,
};

let execution_channels = ExecutionChannels {
Expand Down
Loading