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

Benchmark Wasm via argon2 and change gas price to 150_000 per op #1120

Merged
merged 12 commits into from
Sep 30, 2021
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ workflows:
- load-wasm-speed
- cache-analyze
- fix-benches
- benchmark_argon2
deploy:
jobs:
- build_and_upload_devcontracts:
Expand Down
42 changes: 42 additions & 0 deletions contracts/hackatom/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 contracts/hackatom/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ backtraces = ["cosmwasm-std/backtraces", "cosmwasm-vm/backtraces"]

[dependencies]
cosmwasm-std = { path = "../../packages/std", default-features = false }
rust-argon2 = "0.8"
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
sha2 = "0.9.1"
Expand Down
31 changes: 31 additions & 0 deletions contracts/hackatom/schema/execute_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,37 @@
},
"additionalProperties": false
},
{
"description": "Hashes some data. Uses CPU and memory, but no external calls.",
"type": "object",
"required": [
"argon2"
],
"properties": {
"argon2": {
"type": "object",
"required": [
"mem_cost",
"time_cost"
],
"properties": {
"mem_cost": {
"description": "The amount of memory requested (KB).",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"time_cost": {
"description": "The number of passes.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
}
},
"additionalProperties": false
},
{
"description": "Infinite loop to burn cpu cycles (only run when metering is enabled)",
"type": "object",
Expand Down
26 changes: 26 additions & 0 deletions contracts/hackatom/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ pub fn execute(
) -> Result<Response, HackError> {
match msg {
ExecuteMsg::Release {} => do_release(deps, env, info),
ExecuteMsg::Argon2 {
mem_cost,
time_cost,
} => do_argon2(mem_cost, time_cost),
ExecuteMsg::CpuLoop {} => do_cpu_loop(),
ExecuteMsg::StorageLoop {} => do_storage_loop(deps),
ExecuteMsg::MemoryLoop {} => do_memory_loop(),
Expand Down Expand Up @@ -101,6 +105,28 @@ fn do_release(deps: DepsMut, env: Env, info: MessageInfo) -> Result<Response, Ha
}
}

fn do_argon2(mem_cost: u32, time_cost: u32) -> Result<Response, HackError> {
let password = b"password";
let salt = b"othersalt";
let config = argon2::Config {
variant: argon2::Variant::Argon2i,
version: argon2::Version::Version13,
mem_cost,
time_cost,
lanes: 4,
thread_mode: argon2::ThreadMode::Sequential,
secret: &[],
ad: &[],
hash_length: 32,
};
let hash = argon2::hash_encoded(password, salt, &config)
.map_err(|e| StdError::generic_err(format!("hash_encoded errored: {}", e)))?;
// let matches = argon2::verify_encoded(&hash, password).unwrap();
// assert!(matches);
Ok(Response::new().set_data(hash.into_bytes()))
//Ok(Response::new())
}

fn do_cpu_loop() -> Result<Response, HackError> {
let mut counter = 0u64;
loop {
Expand Down
7 changes: 7 additions & 0 deletions contracts/hackatom/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ pub enum SudoMsg {
pub enum ExecuteMsg {
/// Releasing all funds in the contract to the beneficiary. This is the only "proper" action of this demo contract.
Release {},
/// Hashes some data. Uses CPU and memory, but no external calls.
Argon2 {
/// The amount of memory requested (KB).
mem_cost: u32,
/// The number of passes.
time_cost: u32,
},
/// Infinite loop to burn cpu cycles (only run when metering is enabled)
CpuLoop {},
/// Infinite loop making storage calls (to test when their limit hits)
Expand Down
40 changes: 35 additions & 5 deletions contracts/hackatom/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use cosmwasm_vm::{
call_execute, from_slice,
testing::{
execute, instantiate, migrate, mock_env, mock_info, mock_instance,
mock_instance_with_balances, query, sudo, test_io, MOCK_CONTRACT_ADDR,
mock_instance_with_balances, mock_instance_with_gas_limit, query, sudo, test_io,
MOCK_CONTRACT_ADDR,
},
Storage, VmError,
};
Expand Down Expand Up @@ -306,6 +307,35 @@ fn execute_release_fails_for_wrong_sender() {
);
}

#[test]
fn execute_argon2() {
let mut deps = mock_instance_with_gas_limit(WASM, 1_000_000_000);

let (instantiate_msg, creator) = make_init_msg();
let init_info = mock_info(creator.as_str(), &[]);
let init_res: Response =
instantiate(&mut deps, mock_env(), init_info, instantiate_msg).unwrap();
assert_eq!(0, init_res.messages.len());

let gas_before = deps.get_gas_left();
let _execute_res: Response = execute(
&mut deps,
mock_env(),
mock_info(creator.as_str(), &[]),
ExecuteMsg::Argon2 {
mem_cost: 256,
time_cost: 5,
},
)
.unwrap();
let gas_used = gas_before - deps.get_gas_left();
// Note: the exact gas usage depends on the Rust version used to compile Wasm,
// which we only fix when using rust-optimizer, not integration tests.
let expected = 157407464; // +/- 20%
assert!(gas_used > expected * 80 / 100, "Gas used: {}", gas_used);
assert!(gas_used < expected * 120 / 100, "Gas used: {}", gas_used);
}

#[test]
fn execute_cpu_loop() {
let mut deps = mock_instance(WASM, &[]);
Expand Down Expand Up @@ -403,8 +433,8 @@ fn execute_allocate_large_memory() {
);
let gas_used = gas_before - deps.get_gas_left();
// Gas consumption is relatively small
// Note: the exact gas usage depends on the Rust version used to compile WASM,
// which we only fix when using cosmwasm-opt, not integration tests.
// Note: the exact gas usage depends on the Rust version used to compile Wasm,
// which we only fix when using rust-optimizer, not integration tests.
let expected = 27880; // +/- 20%
assert!(gas_used > expected * 80 / 100, "Gas used: {}", gas_used);
assert!(gas_used < expected * 120 / 100, "Gas used: {}", gas_used);
Expand All @@ -424,8 +454,8 @@ fn execute_allocate_large_memory() {
assert_eq!(result.unwrap_err(), "Generic error: memory.grow failed");
let gas_used = gas_before - deps.get_gas_left();
// Gas consumption is relatively small
// Note: the exact gas usage depends on the Rust version used to compile WASM,
// which we only fix when using cosmwasm-opt, not integration tests.
// Note: the exact gas usage depends on the Rust version used to compile Wasm,
// which we only fix when using rust-optimizer, not integration tests.
let expected = 31076; // +/- 20%
assert!(gas_used > expected * 80 / 100, "Gas used: {}", gas_used);
assert!(gas_used < expected * 120 / 100, "Gas used: {}", gas_used);
Expand Down
30 changes: 29 additions & 1 deletion packages/vm/benches/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn bench_instance(c: &mut Criterion) {
});
});

group.bench_function("execute execute", |b| {
group.bench_function("execute execute (release)", |b| {
let backend = mock_backend(&[]);
let much_gas: InstanceOptions = InstanceOptions {
gas_limit: 500_000_000_000,
Expand All @@ -77,6 +77,34 @@ fn bench_instance(c: &mut Criterion) {
});
});

group.bench_function("execute execute (argon2)", |b| {
let backend = mock_backend(&[]);
let much_gas: InstanceOptions = InstanceOptions {
gas_limit: 500_000_000_000,
..DEFAULT_INSTANCE_OPTIONS
};
let mut instance =
Instance::from_code(CONTRACT, backend, much_gas, Some(DEFAULT_MEMORY_LIMIT)).unwrap();

let info = mock_info("creator", &coins(1000, "earth"));
let msg = br#"{"verifier": "verifies", "beneficiary": "benefits"}"#;
let contract_result =
call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg).unwrap();
assert!(contract_result.into_result().is_ok());

let mut gas_used = 0;
b.iter(|| {
let gas_before = instance.get_gas_left();
let info = mock_info("hasher", &[]);
let msg = br#"{"argon2":{"mem_cost":256,"time_cost":3}}"#;
let contract_result =
call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg).unwrap();
assert!(contract_result.into_result().is_ok());
gas_used = gas_before - instance.get_gas_left();
});
println!("Gas used: {}", gas_used);
});

group.finish();
}

Expand Down
4 changes: 2 additions & 2 deletions packages/vm/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ mod singlepass_tests {
.unwrap();

let execute_used = gas_before_execute - instance.get_gas_left();
assert_eq!(execute_used, 162233);
assert_eq!(execute_used, 162308);
}

#[test]
Expand Down Expand Up @@ -947,6 +947,6 @@ mod singlepass_tests {
assert_eq!(answer.as_slice(), b"{\"verifier\":\"verifies\"}");

let query_used = gas_before_query - instance.get_gas_left();
assert_eq!(query_used, 30646);
assert_eq!(query_used, 30663);
}
}
Binary file modified packages/vm/testdata/hackatom_1.0.wasm
Binary file not shown.