From 7c0ad7805a98a14014c3e5d41780039e99288fa7 Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Tue, 14 Mar 2023 18:27:40 -0700 Subject: [PATCH] [workloads] add adversarial workload A new workload type that tries to push against system limits. For starters, I added logic for generating as many objects of the max size as possible without going over a limit. Some other ideas for extensions to this workload: - MaxReads (by creating a bunch of shared objects in the module init for adversarial, then taking them all as input) - MaxDynamicFields (by reading a bunch of dynamic fields at runtime) - MaxEffects (by creating a bunch of small objects) - MaxEvents (max out VM's event size limit) Hopefully this will be useful for simtests/stress-testing. --- crates/sui-benchmark/src/in_memory_wallet.rs | 72 +++++- crates/sui-benchmark/src/lib.rs | 11 + crates/sui-benchmark/src/options.rs | 24 +- .../src/workloads/adversarial.rs | 216 ++++++++++++++++++ .../src/workloads/batch_payment.rs | 29 ++- .../src/workloads/data/adversarial/Move.toml | 10 + .../data/adversarial/sources/adversarial.move | 69 ++++++ crates/sui-benchmark/src/workloads/mod.rs | 1 + .../src/workloads/workload_configuration.rs | 15 +- crates/sui-benchmark/tests/simtest.rs | 2 + crates/sui-types/src/messages.rs | 42 ++++ 11 files changed, 460 insertions(+), 31 deletions(-) create mode 100644 crates/sui-benchmark/src/workloads/adversarial.rs create mode 100644 crates/sui-benchmark/src/workloads/data/adversarial/Move.toml create mode 100644 crates/sui-benchmark/src/workloads/data/adversarial/sources/adversarial.move diff --git a/crates/sui-benchmark/src/in_memory_wallet.rs b/crates/sui-benchmark/src/in_memory_wallet.rs index f4531f4beff8d..fa75b8b5ec33c 100644 --- a/crates/sui-benchmark/src/in_memory_wallet.rs +++ b/crates/sui-benchmark/src/in_memory_wallet.rs @@ -3,37 +3,49 @@ use std::{collections::BTreeMap, sync::Arc}; +use move_core_types::{identifier::Identifier, language_storage::TypeTag}; use sui_types::{ base_types::{ObjectID, ObjectRef, SuiAddress}, crypto::AccountKeyPair, - messages::{TransactionData, TransactionDataAPI, VerifiedTransaction}, + messages::{CallArg, TransactionData, TransactionDataAPI, VerifiedTransaction}, object::Owner, utils::to_sender_signed_transaction, }; -use crate::ExecutionEffects; +use crate::{workloads::Gas, ExecutionEffects}; /// A Sui account and all of the objects it owns #[derive(Debug)] pub struct SuiAccount { key: Arc, + /// object this account uses to pay for gas + pub gas: ObjectRef, + /// objects owned by this account. does not include `gas` owned: BTreeMap, // TODO: optional type info } impl SuiAccount { - pub fn new(key: Arc, objs: Vec) -> Self { + pub fn new(key: Arc, gas: ObjectRef, objs: Vec) -> Self { let owned = objs.into_iter().map(|obj| (obj.0, obj)).collect(); - SuiAccount { key, owned } + SuiAccount { key, gas, owned } } /// Update the state associated with `obj`, adding it if it doesn't exist pub fn add_or_update(&mut self, obj: ObjectRef) -> Option { - self.owned.insert(obj.0, obj) + if self.gas.0 == obj.0 { + let old_gas = self.gas; + self.gas = obj; + Some(old_gas) + } else { + self.owned.insert(obj.0, obj) + } } /// Delete `id` and return the old value pub fn delete(&mut self, id: &ObjectID) -> Option { + debug_assert!(self.gas.0 != *id, "Deleting gas object"); + self.owned.remove(id) } } @@ -45,13 +57,22 @@ pub struct InMemoryWallet { } impl InMemoryWallet { + pub fn new(gas: &Gas) -> Self { + let mut wallet = InMemoryWallet { + accounts: BTreeMap::new(), + }; + wallet.add_account(gas.1, gas.2.clone(), gas.0, Vec::new()); + wallet + } + pub fn add_account( &mut self, addr: SuiAddress, key: Arc, + gas: ObjectRef, objs: Vec, ) { - self.accounts.insert(addr, SuiAccount::new(key, objs)); + self.accounts.insert(addr, SuiAccount::new(key, gas, objs)); } /// Apply updates from `effects` to `self` @@ -76,6 +97,18 @@ impl InMemoryWallet { } // else, tx sender is not an account we can spend from, we don't care } + pub fn account_mut(&mut self, addr: &SuiAddress) -> Option<&mut SuiAccount> { + self.accounts.get_mut(addr) + } + + pub fn account(&self, addr: &SuiAddress) -> Option<&SuiAccount> { + self.accounts.get(addr) + } + + pub fn gas(&self, addr: &SuiAddress) -> Option<&ObjectRef> { + self.accounts.get(addr).map(|a| &a.gas) + } + pub fn owned_object(&self, addr: &SuiAddress, id: &ObjectID) -> Option<&ObjectRef> { self.accounts.get(addr).and_then(|a| a.owned.get(id)) } @@ -89,6 +122,33 @@ impl InMemoryWallet { to_sender_signed_transaction(data, self.accounts.get(&sender).unwrap().key.as_ref()) } + pub fn move_call( + &self, + sender: SuiAddress, + package: ObjectID, + module: &str, + function: &str, + type_arguments: Vec, + arguments: Vec, + gas_budget: u64, + gas_price: u64, + ) -> VerifiedTransaction { + let account = self.account(&sender).unwrap(); + let data = TransactionData::new_move_call( + sender, + package, + Identifier::new(module).unwrap(), + Identifier::new(function).unwrap(), + type_arguments, + account.gas, + arguments, + gas_budget, + gas_price, + ) + .unwrap(); + to_sender_signed_transaction(data, account.key.as_ref()) + } + pub fn keypair(&self, addr: &SuiAddress) -> Option> { self.accounts.get(addr).map(|a| a.key.clone()) } diff --git a/crates/sui-benchmark/src/lib.rs b/crates/sui-benchmark/src/lib.rs index d5d4a7156cf2e..ae5c703a866a3 100644 --- a/crates/sui-benchmark/src/lib.rs +++ b/crates/sui-benchmark/src/lib.rs @@ -139,6 +139,17 @@ impl ExecutionEffects { Owner::ObjectOwner(_) | Owner::Shared { .. } | Owner::Immutable => unreachable!(), // owner of gas object is always an address } } + + pub fn is_ok(&self) -> bool { + match self { + ExecutionEffects::CertifiedTransactionEffects(certified_effects, ..) => { + certified_effects.data().status().is_ok() + } + ExecutionEffects::SuiTransactionEffects(sui_tx_effects) => { + sui_tx_effects.status().is_ok() + } + } + } } #[async_trait] diff --git a/crates/sui-benchmark/src/options.rs b/crates/sui-benchmark/src/options.rs index ed0d8bc4d36cc..a2cbc569336d2 100644 --- a/crates/sui-benchmark/src/options.rs +++ b/crates/sui-benchmark/src/options.rs @@ -130,18 +130,11 @@ pub enum RunSpec { // will likely change in future to support // more representative workloads. Bench { + // ----- workloads ---- // relative weight of shared counter // transaction in the benchmark workload #[clap(long, default_value = "0")] shared_counter: u32, - // 100 for max hotness i.e all requests target - // just the same shared counter, 0 for no hotness - // i.e. all requests target a different shared - // counter. The way total number of counters to - // create is computed roughly as: - // total_shared_counters = max(1, qps * (1.0 - hotness/100.0)) - #[clap(long, default_value = "50")] - shared_counter_hotness_factor: u32, // relative weight of transfer object // transactions in the benchmark workload #[clap(long, default_value = "1")] @@ -152,9 +145,24 @@ pub enum RunSpec { // relative weight of batch payment transactions in the benchmark workload #[clap(long, default_value = "0")] batch_payment: u32, + // relative weight of adversarial transactions in the benchmark workload + #[clap(long, default_value = "0")] + adversarial: u32, + + // --- workload-specific options --- (TODO: use subcommands or similar) + // 100 for max hotness i.e all requests target + // just the same shared counter, 0 for no hotness + // i.e. all requests target a different shared + // counter. The way total number of counters to + // create is computed roughly as: + // total_shared_counters = max(1, qps * (1.0 - hotness/100.0)) + #[clap(long, default_value = "50")] + shared_counter_hotness_factor: u32, // batch size use for batch payment workload #[clap(long, default_value = "15")] batch_payment_size: u32, + + // --- generic options --- // Target qps #[clap(long, default_value = "1000", global = true)] target_qps: u64, diff --git a/crates/sui-benchmark/src/workloads/adversarial.rs b/crates/sui-benchmark/src/workloads/adversarial.rs new file mode 100644 index 0000000000000..8a0bd40cd8a00 --- /dev/null +++ b/crates/sui-benchmark/src/workloads/adversarial.rs @@ -0,0 +1,216 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use sui_types::{base_types::ObjectID, object::Owner}; +use test_utils::messages::create_publish_move_package_transaction; + +use std::path::PathBuf; +use std::sync::Arc; + +use sui_types::{base_types::SuiAddress, crypto::get_key_pair, messages::VerifiedTransaction}; + +use crate::in_memory_wallet::InMemoryWallet; +use crate::system_state_observer::SystemStateObserver; +use crate::workloads::payload::Payload; +use crate::workloads::{Gas, GasCoinConfig}; +use crate::{ExecutionEffects, ValidatorProxy}; + +use super::{ + workload::{Workload, WorkloadBuilder, MAX_GAS_FOR_TESTING}, + WorkloadBuilderInfo, WorkloadParams, +}; + +// TODO: copied from protocol_config, but maybe we can put this in SystemStateObserver +const MAX_TX_GAS: u64 = 10_000_000_000; + +// TODO: copied from protocol_config, but maybe we can put this in SystemStateObserver +/// Number of max size objects to create in the max object payload +const NUM_OBJECTS: u64 = 32; +// TODO: make this big once https://github.com/MystenLabs/sui/pull/9394 lands +//const NUM_OBJECTS: u64 = 2048; + +/*enum PayloadType { + /// create NUM_OBJECTS objects with the max object size. This will write out a lot of object data + MaxObjects, + // TODO: + // - MaxReads (by creating a bunch of shared objects in the module init for adversarial, then taking them all as input) + // - MaxDynamicFields (by reading a bunch of dynamic fields at runtime) + // - MaxEffects (by creating a bunch of small objects) + // - MaxEvents (max out VM's event size limit) +}*/ + +#[derive(Debug)] +pub struct AdversarialTestPayload { + /// ID of the Move package with adversarial utility functions + package_id: ObjectID, + /// address to send adversarial transactions from + sender: SuiAddress, + state: InMemoryWallet, + system_state_observer: Arc, +} + +impl std::fmt::Display for AdversarialTestPayload { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "adversarial") + } +} + +impl Payload for AdversarialTestPayload { + fn make_new_payload(&mut self, effects: &ExecutionEffects) { + // important to keep this as a sanity check that we don't hit protocol limits or run out of gas as things change elsewhere. + // adversarial tests aren't much use if they don't have effects :) + debug_assert!( + effects.is_ok(), + "Adversarial transactions should never abort" + ); + + self.state.update(effects); + } + + fn make_transaction(&mut self) -> VerifiedTransaction { + // TODO: default benchmarking gas coins are too small to use MAX_TX_GAS. But we will want to be able to use that much to hit some limits + let gas_budget = MAX_TX_GAS / 100; + // TODO: generate random number, convert it to a PayloadType, call the appropriate function + self.state.move_call( + self.sender, + self.package_id, + "adversarial", + "create_max_size_owned_objects", + vec![], + vec![NUM_OBJECTS.into()], + gas_budget, + *self.system_state_observer.reference_gas_price.borrow(), + ) + } +} + +#[derive(Debug)] +pub struct AdversarialWorkloadBuilder { + num_payloads: u64, +} + +#[async_trait] +impl WorkloadBuilder for AdversarialWorkloadBuilder { + async fn generate_coin_config_for_init(&self) -> Vec { + // Gas coin for publishing adversarial package + let (address, keypair) = get_key_pair(); + vec![GasCoinConfig { + amount: MAX_GAS_FOR_TESTING, + address, + keypair: Arc::new(keypair), + }] + } + + async fn generate_coin_config_for_payloads(&self) -> Vec { + let mut configs = vec![]; + // Gas coins for running workload + for _i in 0..self.num_payloads { + let (address, keypair) = get_key_pair(); + configs.push(GasCoinConfig { + amount: MAX_GAS_FOR_TESTING, + address, + keypair: Arc::new(keypair), + }); + } + configs + } + + async fn build( + &self, + mut init_gas: Vec, + payload_gas: Vec, + ) -> Box> { + Box::>::from(Box::new(AdversarialWorkload { + package_id: ObjectID::ZERO, + init_gas: init_gas.pop().unwrap(), + payload_gas, + })) + } +} + +impl AdversarialWorkloadBuilder { + pub fn from( + workload_weight: f32, + target_qps: u64, + num_workers: u64, + in_flight_ratio: u64, + ) -> Option { + let target_qps = (workload_weight * target_qps as f32) as u64; + let num_workers = (workload_weight * num_workers as f32).ceil() as u64; + let max_ops = target_qps * in_flight_ratio; + if max_ops == 0 || num_workers == 0 { + None + } else { + let workload_params = WorkloadParams { + target_qps, + num_workers, + max_ops, + }; + let workload_builder = Box::>::from(Box::new( + AdversarialWorkloadBuilder { + num_payloads: max_ops, + }, + )); + let builder_info = WorkloadBuilderInfo { + workload_params, + workload_builder, + }; + Some(builder_info) + } + } +} + +#[derive(Debug)] +pub struct AdversarialWorkload { + /// ID of the Move package with adversarial utility functions + package_id: ObjectID, + pub init_gas: Gas, + pub payload_gas: Vec, +} + +#[async_trait] +impl Workload for AdversarialWorkload { + async fn init( + &mut self, + proxy: Arc, + system_state_observer: Arc, + ) { + let gas = &self.init_gas; + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("src/workloads/data/adversarial"); + let gas_price = *system_state_observer.reference_gas_price.borrow(); + let transaction = + create_publish_move_package_transaction(gas.0, path, gas.1, &gas.2, Some(gas_price)); + let effects = proxy.execute_transaction(transaction.into()).await.unwrap(); + let created = effects.created(); + // should only create the package object + upgrade cap. otherwise, there are some object initializers running and we will need to disambiguate + assert_eq!(created.len(), 2); + let package_obj = created + .iter() + .find(|o| matches!(o.1, Owner::Immutable)) + .unwrap(); + self.package_id = package_obj.0 .0; + } + + async fn make_test_payloads( + &self, + _proxy: Arc, + system_state_observer: Arc, + ) -> Vec> { + let mut payloads = Vec::new(); + + for gas in &self.payload_gas { + payloads.push(AdversarialTestPayload { + package_id: self.package_id, + sender: gas.1, + state: InMemoryWallet::new(gas), + system_state_observer: system_state_observer.clone(), + }) + } + payloads + .into_iter() + .map(|b| Box::::from(Box::new(b))) + .collect() + } +} diff --git a/crates/sui-benchmark/src/workloads/batch_payment.rs b/crates/sui-benchmark/src/workloads/batch_payment.rs index 09587564801b7..67c50c8222f47 100644 --- a/crates/sui-benchmark/src/workloads/batch_payment.rs +++ b/crates/sui-benchmark/src/workloads/batch_payment.rs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use sui_types::base_types::ObjectID; +use sui_types::base_types::{ObjectID, SequenceNumber}; +use sui_types::digests::ObjectDigest; use sui_types::object::Owner; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::sync::Arc; use sui_types::{ @@ -32,11 +33,11 @@ const PRIMARY_COIN_VALUE: u64 = 10_000_000; /// Number of mist sent to each address on each batch transfer const TRANSFER_AMOUNT: u64 = 1; +const DUMMY_GAS: ObjectRef = (ObjectID::ZERO, SequenceNumber::MIN, ObjectDigest::MIN); + #[derive(Debug)] pub struct BatchPaymentTestPayload { state: InMemoryWallet, - // largest value coins owned by each address - primary_coins: BTreeMap, /// total number of payments made, to be used in reporting batch TPS num_payments: usize, /// address of the first sender. important because in the beginning, only one address has any coins. @@ -57,7 +58,7 @@ impl Payload for BatchPaymentTestPayload { if self.num_payments == 0 { for (coin_obj, owner) in effects.created().into_iter().chain(effects.mutated()) { if let Owner::AddressOwner(addr) = owner { - self.primary_coins.insert(addr, coin_obj.0); + self.state.account_mut(&addr).unwrap().gas = coin_obj; } else { unreachable!("Initial payment should only send to addresses") } @@ -77,10 +78,7 @@ impl Payload for BatchPaymentTestPayload { addrs[self.num_payments % num_recipients] }; // we're only using gas objects in this benchmark, so safe to assume everything owned by an address is a gas object - let gas_obj = self - .state - .owned_object(&sender, self.primary_coins.get(&sender).unwrap()) - .unwrap(); + let gas_obj = self.state.gas(&sender).unwrap(); let amount = if self.num_payments == 0 { PRIMARY_COIN_VALUE } else { @@ -208,21 +206,20 @@ impl Workload for BatchPaymentWorkload { for (addr, gas) in gas_by_address { let mut state = InMemoryWallet::default(); let key = gas[0].2.clone(); - let gas_objs: Vec = gas.into_iter().map(|g| g.0).collect(); - let primary_coin = gas_objs[0].0; - state.add_account(addr, key, gas_objs); - let mut primary_coins = BTreeMap::new(); - primary_coins.insert(addr, primary_coin); + let mut objs: Vec = gas.into_iter().map(|g| g.0).collect(); + let gas_coin = objs.pop().unwrap(); + state.add_account(addr, key, gas_coin, objs); // add empty accounts for `addr` to transfer to for _ in 0..self.batch_size - 1 { let (a, key) = get_key_pair(); - state.add_account(a, Arc::new(key), Vec::new()); + // we'll replace this after the first send + let gas = DUMMY_GAS; + state.add_account(a, Arc::new(key), gas, Vec::new()); } payloads.push(Box::new(BatchPaymentTestPayload { state, num_payments: 0, first_sender: addr, - primary_coins, system_state_observer: system_state_observer.clone(), })); } diff --git a/crates/sui-benchmark/src/workloads/data/adversarial/Move.toml b/crates/sui-benchmark/src/workloads/data/adversarial/Move.toml new file mode 100644 index 0000000000000..10b0e164c4367 --- /dev/null +++ b/crates/sui-benchmark/src/workloads/data/adversarial/Move.toml @@ -0,0 +1,10 @@ +[package] +name = "adversarial" +version = "0.0.1" + +[dependencies] +Sui = { local = "../../../../../sui-framework" } + +[addresses] +adversarial = "0x0" +sui = "0000000000000000000000000000000000000000000000000000000000000002" diff --git a/crates/sui-benchmark/src/workloads/data/adversarial/sources/adversarial.move b/crates/sui-benchmark/src/workloads/data/adversarial/sources/adversarial.move new file mode 100644 index 0000000000000..06030ef6199ab --- /dev/null +++ b/crates/sui-benchmark/src/workloads/data/adversarial/sources/adversarial.move @@ -0,0 +1,69 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module adversarial::adversarial { + use std::vector; + use sui::bcs; + use sui::object::{Self, UID}; + use sui::tx_context::{Self, TxContext}; + use sui::transfer; + + struct S has key, store { + id: UID, + contents: vector + } + + struct Wrapper has key { + id: UID, + s: S, + } + + const MAX_OBJ_SIZE: u64 = 25600; + + // create an object whose Move BCS representation is `n` bytes + public fun create_object_with_size(n: u64, ctx: &mut TxContext): S { + // minimum object size for S is 32 bytes for UID + 1 byte for vector length + assert!(n > std::address::length() + 1, 0); + let contents = vector[]; + let i = 0; + let bytes_to_add = n - (std::address::length() + 1); + while (i < bytes_to_add) { + vector::push_back(&mut contents, 9); + i = i + 1; + }; + let s = S { id: object::new(ctx), contents }; + let size = vector::length(&bcs::to_bytes(&s)); + // shrink by 1 byte until we match size. mismatch happens because of len(UID) + vector length byte + while (size > n) { + let _ = vector::pop_back(&mut s.contents); + // hack: assume this doesn't change the size of the BCS length byte + size = size - 1; + }; + // double-check that we got the size right + assert!(vector::length(&bcs::to_bytes(&s)) == n, 1); + s + } + + public fun create_max_size_object(ctx: &mut TxContext): S { + create_object_with_size(MAX_OBJ_SIZE, ctx) + } + + /// Create `n` max size objects and transfer them to the tx sender + public fun create_max_size_owned_objects(n: u64, ctx: &mut TxContext) { + let i = 0; + let sender = tx_context::sender(ctx); + while (i < n) { + transfer::transfer(create_max_size_object(ctx), sender); + i = i + 1 + } + } + + /// Create `n` max size objects and share them + public fun create_max_size_shared_objects(n: u64, ctx: &mut TxContext) { + let i = 0; + while (i < n) { + transfer::share_object(create_max_size_object(ctx)); + i = i + 1 + } + } +} diff --git a/crates/sui-benchmark/src/workloads/mod.rs b/crates/sui-benchmark/src/workloads/mod.rs index 595a22f13c002..f8323ea0bac50 100644 --- a/crates/sui-benchmark/src/workloads/mod.rs +++ b/crates/sui-benchmark/src/workloads/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +pub mod adversarial; pub mod batch_payment; pub mod delegation; pub mod payload; diff --git a/crates/sui-benchmark/src/workloads/workload_configuration.rs b/crates/sui-benchmark/src/workloads/workload_configuration.rs index 04f85a9737ec6..5569c4d01aae7 100644 --- a/crates/sui-benchmark/src/workloads/workload_configuration.rs +++ b/crates/sui-benchmark/src/workloads/workload_configuration.rs @@ -12,6 +12,8 @@ use crate::workloads::WorkloadInfo; use anyhow::Result; use std::sync::Arc; +use super::adversarial::AdversarialWorkloadBuilder; + pub struct WorkloadConfiguration; impl WorkloadConfiguration { @@ -29,6 +31,7 @@ impl WorkloadConfiguration { transfer_object, delegation, batch_payment, + adversarial, batch_payment_size, shared_counter_hotness_factor, .. @@ -40,6 +43,7 @@ impl WorkloadConfiguration { transfer_object, delegation, batch_payment, + adversarial, batch_payment_size, shared_counter_hotness_factor, target_qps, @@ -60,6 +64,7 @@ impl WorkloadConfiguration { transfer_object_weight: u32, delegation_weight: u32, batch_payment_weight: u32, + adversarial_weight: u32, batch_payment_size: u32, shared_counter_hotness_factor: u32, target_qps: u64, @@ -71,7 +76,8 @@ impl WorkloadConfiguration { let total_weight = shared_counter_weight + transfer_object_weight + delegation_weight - + batch_payment_weight; + + batch_payment_weight + + adversarial_weight; let mut workload_builders = vec![]; let shared_workload = SharedCounterWorkloadBuilder::from( shared_counter_weight as f32 / total_weight as f32, @@ -104,6 +110,13 @@ impl WorkloadConfiguration { batch_payment_size, ); workload_builders.push(batch_payment_workload); + let adversarial_workload = AdversarialWorkloadBuilder::from( + adversarial_weight as f32 / total_weight as f32, + target_qps, + num_workers, + in_flight_ratio, + ); + workload_builders.push(adversarial_workload); let (workload_params, workload_builders): (Vec<_>, Vec<_>) = workload_builders .into_iter() .flatten() diff --git a/crates/sui-benchmark/tests/simtest.rs b/crates/sui-benchmark/tests/simtest.rs index 5c0cbdecbb602..f952576f8a054 100644 --- a/crates/sui-benchmark/tests/simtest.rs +++ b/crates/sui-benchmark/tests/simtest.rs @@ -253,6 +253,7 @@ mod test { let num_transfer_accounts = 2; let delegation_weight = 1; let batch_payment_weight = 1; + let adversarial_weight = 1; let shared_counter_hotness_factor = 50; let workloads = WorkloadConfiguration::build_workloads( @@ -262,6 +263,7 @@ mod test { transfer_object_weight, delegation_weight, batch_payment_weight, + adversarial_weight, batch_payment_size, shared_counter_hotness_factor, target_qps, diff --git a/crates/sui-types/src/messages.rs b/crates/sui-types/src/messages.rs index 3008ca6d10f81..39b25ab7218e5 100644 --- a/crates/sui-types/src/messages.rs +++ b/crates/sui-types/src/messages.rs @@ -267,6 +267,48 @@ impl CallArg { } } +impl From for CallArg { + fn from(b: bool) -> Self { + // unwrap safe because every u8 value is BCS-serializable + CallArg::Pure(bcs::to_bytes(&b).unwrap()) + } +} + +impl From for CallArg { + fn from(n: u8) -> Self { + // unwrap safe because every u8 value is BCS-serializable + CallArg::Pure(bcs::to_bytes(&n).unwrap()) + } +} + +impl From for CallArg { + fn from(n: u16) -> Self { + // unwrap safe because every u16 value is BCS-serializable + CallArg::Pure(bcs::to_bytes(&n).unwrap()) + } +} + +impl From for CallArg { + fn from(n: u32) -> Self { + // unwrap safe because every u32 value is BCS-serializable + CallArg::Pure(bcs::to_bytes(&n).unwrap()) + } +} + +impl From for CallArg { + fn from(n: u64) -> Self { + // unwrap safe because every u64 value is BCS-serializable + CallArg::Pure(bcs::to_bytes(&n).unwrap()) + } +} + +impl From for CallArg { + fn from(n: u128) -> Self { + // unwrap safe because every u128 value is BCS-serializable + CallArg::Pure(bcs::to_bytes(&n).unwrap()) + } +} + impl ObjectArg { pub fn id(&self) -> ObjectID { match self {