Skip to content

Commit

Permalink
piecrust: price event bytes at the same rate as storage
Browse files Browse the repository at this point in the history
Each byte in an event, be it topic or data, is priced at the same rate
as each byte in a storage instruction. This ensures each event is priced
more than a simple host function, since they require a larger processing
window.

This should be the final commit in pricing imports and host functions,
apart from adding additional functionality such as external
configuration.

Resolves #359
  • Loading branch information
Eduardo Leegwater Simões committed Jul 1, 2024
1 parent cb080a1 commit b88c100
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 1 deletion.
4 changes: 4 additions & 0 deletions piecrust/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Make each event byte cost the same as a storage byte [#359]

### Fixed

- Fix incomplete removal of economic protocol functionality
Expand Down
8 changes: 8 additions & 0 deletions piecrust/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

// The gas cost for each byte
pub const BYTE_STORE_COST: i64 = 4;
11 changes: 11 additions & 0 deletions piecrust/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use piecrust_uplink::{
ContractError, ContractId, ARGBUF_LEN, CONTRACT_ID_BYTES,
};

use crate::config::BYTE_STORE_COST;
use crate::instance::{Env, WrappedInstance};
use crate::Error;

Expand Down Expand Up @@ -324,6 +325,16 @@ pub(crate) fn emit(
check_ptr(instance, topic_ofs, topic_len)?;
check_arg(instance, arg_len)?;

// charge for each byte emitted in an event
let gas_remaining = instance.get_remaining_gas();
let gas_cost = BYTE_STORE_COST as u64 * (topic_len as u64 + arg_len as u64);

if gas_cost > gas_remaining {
instance.set_remaining_gas(0);
Err(Error::OutOfGas)?;
}
instance.set_remaining_gas(gas_remaining - gas_cost);

let data = instance.with_arg_buf(|buf| {
let arg_len = arg_len as usize;
Vec::from(&buf[..arg_len])
Expand Down
1 change: 1 addition & 0 deletions piecrust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
#[macro_use]
mod bytecode_macro;
mod call_tree;
mod config;
mod contract;
mod error;
mod imports;
Expand Down
2 changes: 1 addition & 1 deletion piecrust/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use dusk_wasmtime::{
};
use tempfile::tempdir;

use crate::config::BYTE_STORE_COST;
use crate::session::{Session, SessionData};
use crate::store::ContractStore;
use crate::Error::{self, PersistenceError};
Expand Down Expand Up @@ -58,7 +59,6 @@ fn config() -> Config {
// Support 64-bit memories
config.wasm_memory64(true);

const BYTE_STORE_COST: i64 = 4;
const BYTE4_STORE_COST: i64 = 4 * BYTE_STORE_COST;
const BYTE8_STORE_COST: i64 = 8 * BYTE_STORE_COST;
const BYTE16_STORE_COST: i64 = 16 * BYTE_STORE_COST;
Expand Down
52 changes: 52 additions & 0 deletions piecrust/tests/eventer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,55 @@ pub fn vm_center_events() -> Result<(), Error> {

Ok(())
}

#[test]
pub fn event_costs() -> Result<(), Error> {
let vm = VM::ephemeral()?;

let mut session = vm.session(SessionData::builder())?;

let eventer_id = session.deploy(
contract_bytecode!("eventer"),
ContractData::builder().owner(OWNER),
LIMIT,
)?;

// This call is to "prime" the contract
let _ = session.call::<_, (u64, u64)>(
eventer_id,
"emit_input",
&vec![1u8; 100],
LIMIT,
)?;

let mut costs = vec![];

for size in (4..=40).step_by(4) {
let input = vec![1u8; size];
let (spent_before, spent_after) = session
.call::<_, (u64, u64)>(eventer_id, "emit_input", &input, LIMIT)?
.data;
let cost = spent_after - spent_before;
print!("{cost} ");
costs.push(cost);
}

// cost grows linearly with the amount of bytes processed, at a predictable
// rate.
//
// NOTE: it is not possible to directly test emission costs, unless this is
// externally configurable
let mut cost_diffs = Vec::with_capacity(costs.len() - 1);
for i in 0..costs.len() - 1 {
cost_diffs.push(costs[i + 1] - costs[i]);
}
let (ref_cost_diff, cost_diffs) = cost_diffs.split_first().unwrap();
for cost_diff in cost_diffs {
assert_eq!(
cost_diff, ref_cost_diff,
"cost should grow at a linear rate"
);
}

Ok(())
}

0 comments on commit b88c100

Please sign in to comment.