Parcourir la source

runtime/import/util: rework utility host function gas metering

Charge MIN_GAS at the start of each call, before the ACL check.

Apply per-byte pricing:
* get_tx/get_tx_location/get_blockchain_time/get_last_block_height:
  READ_GAS_PER_BYTE for on-chain data reads

* drk_log/set_return_data/get_object_bytes/get_tx_hash: per-byte
  charge for data copied

* Primitive returns (get_call_index, get_verifying_block_height,
  get_block_target, get_object_size): MIN_GAS only
darkfi il y a 1 mois
Parent
commit
c67aa09a10
1 fichiers modifiés avec 52 ajouts et 39 suppressions
  1. 52 39
      src/runtime/import/util.rs

+ 52 - 39
src/runtime/import/util.rs

@@ -26,6 +26,7 @@ use wasmer::{FunctionEnvMut, StoreMut, WasmPtr};
 use super::acl::acl_allow;
 use crate::{
     runtime::vm_runtime::{ContractSection, Env},
+    validator::fees::{MIN_GAS, READ_GAS_PER_BYTE},
     Result,
 };
 
@@ -33,8 +34,8 @@ use crate::{
 pub(crate) fn drk_log(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) {
     let (env, mut store) = ctx.data_and_store_mut();
 
-    // Subtract used gas. Here we count the length of the string.
-    env.subtract_gas(&mut store, len as u64);
+    // Subtract base gas plus a per-byte charge for the log string.
+    env.subtract_gas(&mut store, MIN_GAS.saturating_add(len as u64));
 
     let memory_view = env.memory_view(&store);
     match ptr.read_utf8_string(&memory_view, len) {
@@ -82,7 +83,10 @@ pub(crate) fn wasm_mem_read(
 /// Permissions: metadata, exec
 pub(crate) fn set_return_data(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
-    let cid = &env.contract_id;
+    let cid = env.contract_id;
+
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
 
     // Enforce function ACL
     if let Err(e) = acl_allow(env, &[ContractSection::Metadata, ContractSection::Exec]) {
@@ -93,7 +97,7 @@ pub(crate) fn set_return_data(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, le
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the length read from the memory slice.
+    // Subtract per-byte gas for the data copied to memory.
     env.subtract_gas(&mut store, len as u64);
 
     let memory_view = env.memory_view(&store);
@@ -120,6 +124,9 @@ pub(crate) fn get_object_bytes(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, i
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     // Enforce function ACL
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
@@ -181,6 +188,9 @@ pub(crate) fn get_object_size(mut ctx: FunctionEnvMut<Env>, idx: u32) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     // Enforce function ACL
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
@@ -210,10 +220,6 @@ pub(crate) fn get_object_size(mut ctx: FunctionEnvMut<Env>, idx: u32) -> i64 {
         return darkfi_sdk::error::DATA_TOO_LARGE
     }
 
-    // Subtract used gas. Here we count the size of the object.
-    // TODO: This could probably be fixed-cost
-    env.subtract_gas(&mut store, obj_len as u64);
-
     obj_len as i64
 }
 
@@ -224,6 +230,9 @@ pub(crate) fn get_verifying_block_height(mut ctx: FunctionEnvMut<Env>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
     {
@@ -234,10 +243,6 @@ pub(crate) fn get_verifying_block_height(mut ctx: FunctionEnvMut<Env>) -> i64 {
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the size of the object.
-    // u32 is 4 bytes.
-    env.subtract_gas(&mut store, 4);
-
     env.verifying_block_height as i64
 }
 
@@ -248,6 +253,9 @@ pub(crate) fn get_block_target(mut ctx: FunctionEnvMut<Env>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
     {
@@ -258,10 +266,6 @@ pub(crate) fn get_block_target(mut ctx: FunctionEnvMut<Env>) -> i64 {
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the size of the object.
-    // u32 is 4 bytes.
-    env.subtract_gas(&mut store, 4);
-
     env.block_target as i64
 }
 
@@ -272,6 +276,9 @@ pub(crate) fn get_tx_hash(mut ctx: FunctionEnvMut<Env>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas plus the fixed cost of a 32-byte tx hash.
+    env.subtract_gas(&mut store, MIN_GAS.saturating_add(32));
+
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
     {
@@ -282,9 +289,6 @@ pub(crate) fn get_tx_hash(mut ctx: FunctionEnvMut<Env>) -> i64 {
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the size of the object.
-    env.subtract_gas(&mut store, 32);
-
     // Return the length of the objects Vector.
     // This is the location of the data that was retrieved and pushed
     let mut objects = env.objects.borrow_mut();
@@ -299,6 +303,9 @@ pub(crate) fn get_call_index(mut ctx: FunctionEnvMut<Env>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
     {
@@ -309,10 +316,6 @@ pub(crate) fn get_call_index(mut ctx: FunctionEnvMut<Env>) -> i64 {
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the size of the object.
-    // u8 is 1 byte.
-    env.subtract_gas(&mut store, 1);
-
     env.call_idx as i64
 }
 
@@ -322,7 +325,10 @@ pub(crate) fn get_call_index(mut ctx: FunctionEnvMut<Env>) -> i64 {
 /// Permissions: deploy, metadata, exec
 pub(crate) fn get_blockchain_time(mut ctx: FunctionEnvMut<Env>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
-    let cid = &env.contract_id;
+    let cid = env.contract_id;
+
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
 
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
@@ -346,9 +352,8 @@ pub(crate) fn get_blockchain_time(mut ctx: FunctionEnvMut<Env>) -> i64 {
         }
     };
 
-    // Subtract used gas. Here we count the size of the object.
-    // u64 is 8 bytes.
-    env.subtract_gas(&mut store, 8);
+    // Charge for the on-chain read: an 8-byte timestamp value.
+    env.subtract_gas(&mut store, 8u64 * READ_GAS_PER_BYTE);
 
     // Create the return object
     let mut ret = Vec::with_capacity(8);
@@ -373,7 +378,10 @@ pub(crate) fn get_blockchain_time(mut ctx: FunctionEnvMut<Env>) -> i64 {
 /// Permissions: deploy, metadata, exec
 pub(crate) fn get_last_block_height(mut ctx: FunctionEnvMut<Env>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
-    let cid = &env.contract_id;
+    let cid = env.contract_id;
+
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
 
     // Enforce function ACL
     if let Err(e) =
@@ -398,9 +406,8 @@ pub(crate) fn get_last_block_height(mut ctx: FunctionEnvMut<Env>) -> i64 {
         }
     };
 
-    // Subtract used gas. Here we count the size of the object.
-    // u64 is 8 bytes.
-    env.subtract_gas(&mut store, 8);
+    // Charge for the on-chain read: an 8-byte block height value.
+    env.subtract_gas(&mut store, 8u64 * READ_GAS_PER_BYTE);
 
     // Create the return object
     let mut ret = Vec::with_capacity(8);
@@ -428,6 +435,9 @@ pub(crate) fn get_tx(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) -> i64 {
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
     {
@@ -438,8 +448,8 @@ pub(crate) fn get_tx(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) -> i64 {
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the length of the looked-up hash.
-    env.subtract_gas(&mut store, blake3::OUT_LEN as u64);
+    // Charge for the on-chain read of the lookup key (a blake3 hash).
+    env.subtract_gas(&mut store, blake3::OUT_LEN as u64 * READ_GAS_PER_BYTE);
 
     // Ensure that it is possible to read memory
     let memory_view = env.memory_view(&store);
@@ -509,8 +519,8 @@ pub(crate) fn get_tx(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) -> i64 {
         return darkfi_sdk::error::DATA_TOO_LARGE
     }
 
-    // Subtract used gas. Here we count the length of the data read from db.
-    env.subtract_gas(&mut store, return_data.len() as u64);
+    // Charge for the on-chain read of the returned value.
+    env.subtract_gas(&mut store, return_data.len() as u64 * READ_GAS_PER_BYTE);
 
     // Copy the data (Vec<u8>) to the VM by pushing it to the objects Vector.
     let mut objects = env.objects.borrow_mut();
@@ -536,6 +546,9 @@ pub(crate) fn get_tx_location(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) ->
     let (env, mut store) = ctx.data_and_store_mut();
     let cid = env.contract_id;
 
+    // Subtract base gas before the ACL check.
+    env.subtract_gas(&mut store, MIN_GAS);
+
     if let Err(e) =
         acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
     {
@@ -546,8 +559,8 @@ pub(crate) fn get_tx_location(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) ->
         return darkfi_sdk::error::CALLER_ACCESS_DENIED
     }
 
-    // Subtract used gas. Here we count the length of the looked-up hash.
-    env.subtract_gas(&mut store, blake3::OUT_LEN as u64);
+    // Charge for the on-chain read of the lookup key (a blake3 hash).
+    env.subtract_gas(&mut store, blake3::OUT_LEN as u64 * READ_GAS_PER_BYTE);
 
     // Ensure that it is possible to read memory
     let memory_view = env.memory_view(&store);
@@ -617,8 +630,8 @@ pub(crate) fn get_tx_location(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) ->
         return darkfi_sdk::error::DATA_TOO_LARGE
     }
 
-    // Subtract used gas. Here we count the length of the data read from db.
-    env.subtract_gas(&mut store, return_data.len() as u64);
+    // Charge for the on-chain read of the returned value.
+    env.subtract_gas(&mut store, return_data.len() as u64 * READ_GAS_PER_BYTE);
 
     // Copy the data (Vec<u8>) to the VM by pushing it to the objects Vector.
     let mut objects = env.objects.borrow_mut();