Skip to content

Commit

Permalink
Crypto natives pt 1 cost params: ed25519 and hash (MystenLabs#9383)
Browse files Browse the repository at this point in the history
## Description 

Adds the cost params logic for natives used in `ed25519.move` and
`hash.move`

## Test Plan 

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
oxade authored Mar 20, 2023
1 parent cf7646d commit 439750b
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 14 deletions.
47 changes: 43 additions & 4 deletions crates/sui-framework/src/natives/crypto/ed25519.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
use crate::legacy_empty_cost;
use crate::natives::NativesCostTable;
use fastcrypto::{
ed25519::{Ed25519PublicKey, Ed25519Signature},
traits::{ToFromBytes, VerifyingKey},
};
use move_binary_format::errors::PartialVMResult;
use move_vm_runtime::native_functions::NativeContext;
use move_core_types::gas_algebra::InternalGas;
use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
use move_vm_types::{
loaded_data::runtime_types::Type,
natives::function::NativeResult,
Expand All @@ -16,22 +17,60 @@ use move_vm_types::{
use smallvec::smallvec;
use std::collections::VecDeque;

const ED25519_BLOCK_SIZE: usize = 128;

#[derive(Clone)]
pub struct Ed25519VerifyCostParams {
/// Base cost for invoking the `ed25519_verify` function
pub ed25519_ed25519_verify_cost_base: InternalGas,
/// Cost per byte of `msg`
pub ed25519_ed25519_verify_msg_cost_per_byte: InternalGas,
/// Cost per block of `data`, where a block is 128 bytes
pub ed25519_ed25519_verify_msg_cost_per_block: InternalGas,
}
/***************************************************************************************************
* native fun ed25519_verify
* Implementation of the Move native function `ed25519::ed25519_verify(signature: &vector<u8>, public_key: &vector<u8>, msg: &vector<u8>): bool;`
* gas cost: ed25519_ed25519_verify_cost_base | base cost for function call and fixed opers
* + ed25519_ed25519_verify_msg_cost_per_byte * msg.len() | cost depends on length of message
* + ed25519_ed25519_verify_msg_cost_per_block * block_size | cost depends on number of blocks in message
**************************************************************************************************/
pub fn ed25519_verify(
_context: &mut NativeContext,
context: &mut NativeContext,
ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
debug_assert!(ty_args.is_empty());
debug_assert!(args.len() == 3);

// Load the cost paramaters from the protocol config
let ed25519_verify_cost_params = &context
.extensions()
.get::<NativesCostTable>()
.ed25519_verify_cost_params
.clone();
// Charge the base cost for this oper
native_charge_gas_early_exit!(
context,
ed25519_verify_cost_params.ed25519_ed25519_verify_cost_base
);

let msg = pop_arg!(args, VectorRef);
let msg_ref = msg.as_bytes_ref();
let public_key_bytes = pop_arg!(args, VectorRef);
let public_key_bytes_ref = public_key_bytes.as_bytes_ref();
let signature_bytes = pop_arg!(args, VectorRef);
let signature_bytes_ref = signature_bytes.as_bytes_ref();

let cost = legacy_empty_cost();
// Charge the arg size dependent costs
native_charge_gas_early_exit!(
context,
ed25519_verify_cost_params.ed25519_ed25519_verify_msg_cost_per_byte
* (msg_ref.len() as u64).into()
+ ed25519_verify_cost_params.ed25519_ed25519_verify_msg_cost_per_block
* (((msg_ref.len() + ED25519_BLOCK_SIZE - 1) / ED25519_BLOCK_SIZE) as u64).into()
);
let cost = context.gas_used();

let signature = match <Ed25519Signature as ToFromBytes>::from_bytes(&signature_bytes_ref) {
Ok(signature) => signature,
Expand Down
106 changes: 97 additions & 9 deletions crates/sui-framework/src/natives/crypto/hash.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,138 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
use crate::legacy_empty_cost;
use crate::natives::NativesCostTable;
use fastcrypto::hash::{Blake2b256, HashFunction, Keccak256};
use move_binary_format::errors::PartialVMResult;
use move_vm_runtime::native_functions::NativeContext;
use move_core_types::gas_algebra::InternalGas;
use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
use move_vm_types::{
loaded_data::runtime_types::Type,
natives::function::NativeResult,
pop_arg,
values::{Value, VectorRef},
};
use smallvec::smallvec;
use std::collections::VecDeque;
use std::{collections::VecDeque, ops::Mul};

const BLAKE_2B256_BLOCK_SIZE: u16 = 128;
const KECCAK_256_BLOCK_SIZE: u16 = 136;

fn hash<H: HashFunction<DIGEST_SIZE>, const DIGEST_SIZE: usize>(
_context: &mut NativeContext,
context: &mut NativeContext,
ty_args: Vec<Type>,
mut args: VecDeque<Value>,
// The caller provides the cost per byte
msg_cost_per_byte: InternalGas,
// The caller provides the cost per block
msg_cost_per_block: InternalGas,
// The caller specifies the block size
block_size: u16,
) -> PartialVMResult<NativeResult> {
debug_assert!(ty_args.is_empty());
debug_assert!(args.len() == 1);

// TODO: implement native gas cost estimation https://github.com/MystenLabs/sui/issues/3593
let cost = legacy_empty_cost();
let msg = pop_arg!(args, VectorRef);
let msg_ref = msg.as_bytes_ref();

let block_size = block_size as usize;

// Charge the msg dependent costs
native_charge_gas_early_exit!(
context,
msg_cost_per_byte.mul((msg_ref.len() as u64).into())
// Round up the blocks
+ msg_cost_per_block
.mul((((msg_ref.len() + block_size - 1) / block_size) as u64).into())
);

Ok(NativeResult::ok(
cost,
context.gas_used(),
smallvec![Value::vector_u8(
H::digest(msg.as_bytes_ref().as_slice()).digest
)],
))
}

#[derive(Clone)]
pub struct HashKeccak256CostParams {
/// Base cost for invoking the `blake2b256` function
pub hash_keccak256_cost_base: InternalGas,
/// Cost per byte of `data`
pub hash_keccak256_data_cost_per_byte: InternalGas,
/// Cost per block of `data`, where a block is 136 bytes
pub hash_keccak256_data_cost_per_block: InternalGas,
}

/***************************************************************************************************
* native fun keccak256
* Implementation of the Move native function `hash::keccak256(data: &vector<u8>): vector<u8>`
* gas cost: hash_keccak256_cost_base | base cost for function call and fixed opers
* + hash_keccak256_data_cost_per_byte * msg.len() | cost depends on length of message
* + hash_keccak256_data_cost_per_block * num_blocks | cost depends on number of blocks in message
**************************************************************************************************/
pub fn keccak256(
context: &mut NativeContext,
ty_args: Vec<Type>,
args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
hash::<Keccak256, 32>(context, ty_args, args)
// Load the cost paramaters from the protocol config
let hash_keccak256_cost_params = &context
.extensions()
.get::<NativesCostTable>()
.hash_keccak256_cost_params
.clone();
// Charge the base cost for this oper
native_charge_gas_early_exit!(context, hash_keccak256_cost_params.hash_keccak256_cost_base);

hash::<Keccak256, 32>(
context,
ty_args,
args,
hash_keccak256_cost_params.hash_keccak256_cost_base,
hash_keccak256_cost_params.hash_keccak256_data_cost_per_block,
KECCAK_256_BLOCK_SIZE,
)
}

#[derive(Clone)]
pub struct HashBlake2b256CostParams {
/// Base cost for invoking the `blake2b256` function
pub hash_blake2b256_cost_base: InternalGas,
/// Cost per byte of `data`
pub hash_blake2b256_data_cost_per_byte: InternalGas,
/// Cost per block of `data`, where a block is 128 bytes
pub hash_blake2b256_data_cost_per_block: InternalGas,
}
/***************************************************************************************************
* native fun blake2b256
* Implementation of the Move native function `hash::blake2b256(data: &vector<u8>): vector<u8>`
* gas cost: hash_blake2b256_cost_base | base cost for function call and fixed opers
* + hash_blake2b256_data_cost_per_byte * msg.len() | cost depends on length of message
* + hash_blake2b256_data_cost_per_block * num_blocks | cost depends on number of blocks in message
**************************************************************************************************/
pub fn blake2b256(
context: &mut NativeContext,
ty_args: Vec<Type>,
args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
hash::<Blake2b256, 32>(context, ty_args, args)
// Load the cost paramaters from the protocol config
let hash_blake2b256_cost_params = &context
.extensions()
.get::<NativesCostTable>()
.hash_blake2b256_cost_params
.clone();
// Charge the base cost for this oper
native_charge_gas_early_exit!(
context,
hash_blake2b256_cost_params.hash_blake2b256_cost_base
);

hash::<Blake2b256, 32>(
context,
ty_args,
args,
hash_blake2b256_cost_params.hash_blake2b256_data_cost_per_byte,
hash_blake2b256_cost_params.hash_blake2b256_data_cost_per_block,
BLAKE_2B256_BLOCK_SIZE,
)
}
44 changes: 43 additions & 1 deletion crates/sui-framework/src/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ use sui_protocol_config::ProtocolConfig;

use self::{
address::{AddressFromBytesCostParams, AddressFromU256CostParams, AddressToU256CostParams},
crypto::{bls12381, ecdsa_k1, ecdsa_r1, ecvrf, ed25519, groth16, hash, hmac},
crypto::{
bls12381, ecdsa_k1, ecdsa_r1, ecvrf,
ed25519::{self, Ed25519VerifyCostParams},
groth16,
hash::{self, HashBlake2b256CostParams, HashKeccak256CostParams},
hmac,
},
event::EventEmitCostParams,
};

Expand All @@ -39,6 +45,9 @@ pub struct NativesCostTable {
pub address_to_u256_cost_params: AddressToU256CostParams,
pub address_from_u256_cost_params: AddressFromU256CostParams,
pub event_emit_cost_params: EventEmitCostParams,
pub ed25519_verify_cost_params: Ed25519VerifyCostParams,
pub hash_blake2b256_cost_params: HashBlake2b256CostParams,
pub hash_keccak256_cost_params: HashKeccak256CostParams,
}

impl NativesCostTable {
Expand Down Expand Up @@ -78,6 +87,39 @@ impl NativesCostTable {
.into(),
event_emit_cost_per_byte: protocol_config.event_emit_cost_per_byte().into(),
},

// Crypto
// ed25519
ed25519_verify_cost_params: Ed25519VerifyCostParams {
ed25519_ed25519_verify_cost_base: protocol_config
.ed25519_ed25519_verify_cost_base()
.into(),
ed25519_ed25519_verify_msg_cost_per_byte: protocol_config
.ed25519_ed25519_verify_msg_cost_per_byte()
.into(),
ed25519_ed25519_verify_msg_cost_per_block: protocol_config
.ed25519_ed25519_verify_msg_cost_per_block()
.into(),
},
// hash
hash_blake2b256_cost_params: HashBlake2b256CostParams {
hash_blake2b256_cost_base: protocol_config.hash_blake2b256_cost_base().into(),
hash_blake2b256_data_cost_per_byte: protocol_config
.hash_blake2b256_data_cost_per_byte()
.into(),
hash_blake2b256_data_cost_per_block: protocol_config
.hash_blake2b256_data_cost_per_block()
.into(),
},
hash_keccak256_cost_params: HashKeccak256CostParams {
hash_keccak256_cost_base: protocol_config.hash_keccak256_cost_base().into(),
hash_keccak256_data_cost_per_byte: protocol_config
.hash_keccak256_data_cost_per_byte()
.into(),
hash_keccak256_data_cost_per_block: protocol_config
.hash_keccak256_data_cost_per_block()
.into(),
},
}
}
}
Expand Down
64 changes: 64 additions & 0 deletions crates/sui-protocol-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,23 @@ pub struct ProtocolConfig {
event_value_size_derivation_cost_per_byte: Option<u64>,
event_tag_size_derivation_cost_per_byte: Option<u64>,
event_emit_cost_per_byte: Option<u64>,

// Crypto natives

// ed25519
ed25519_ed25519_verify_cost_base: Option<u64>,
ed25519_ed25519_verify_msg_cost_per_byte: Option<u64>,
ed25519_ed25519_verify_msg_cost_per_block: Option<u64>,

// hash::blake2b256
hash_blake2b256_cost_base: Option<u64>,
hash_blake2b256_data_cost_per_byte: Option<u64>,
hash_blake2b256_data_cost_per_block: Option<u64>,

// hash::keccak256
hash_keccak256_cost_base: Option<u64>,
hash_keccak256_data_cost_per_byte: Option<u64>,
hash_keccak256_data_cost_per_block: Option<u64>,
}

const CONSTANT_ERR_MSG: &str = "protocol constant not present in current protocol version";
Expand Down Expand Up @@ -623,6 +640,40 @@ impl ProtocolConfig {
pub fn event_emit_cost_per_byte(&self) -> u64 {
self.event_emit_cost_per_byte.expect(CONSTANT_ERR_MSG)
}
pub fn ed25519_ed25519_verify_cost_base(&self) -> u64 {
self.ed25519_ed25519_verify_cost_base
.expect(CONSTANT_ERR_MSG)
}
pub fn ed25519_ed25519_verify_msg_cost_per_byte(&self) -> u64 {
self.ed25519_ed25519_verify_msg_cost_per_byte
.expect(CONSTANT_ERR_MSG)
}
pub fn ed25519_ed25519_verify_msg_cost_per_block(&self) -> u64 {
self.ed25519_ed25519_verify_msg_cost_per_block
.expect(CONSTANT_ERR_MSG)
}
pub fn hash_blake2b256_cost_base(&self) -> u64 {
self.hash_blake2b256_cost_base.expect(CONSTANT_ERR_MSG)
}
pub fn hash_blake2b256_data_cost_per_byte(&self) -> u64 {
self.hash_blake2b256_data_cost_per_byte
.expect(CONSTANT_ERR_MSG)
}
pub fn hash_blake2b256_data_cost_per_block(&self) -> u64 {
self.hash_blake2b256_data_cost_per_block
.expect(CONSTANT_ERR_MSG)
}
pub fn hash_keccak256_cost_base(&self) -> u64 {
self.hash_keccak256_cost_base.expect(CONSTANT_ERR_MSG)
}
pub fn hash_keccak256_data_cost_per_byte(&self) -> u64 {
self.hash_keccak256_data_cost_per_byte
.expect(CONSTANT_ERR_MSG)
}
pub fn hash_keccak256_data_cost_per_block(&self) -> u64 {
self.hash_keccak256_data_cost_per_block
.expect(CONSTANT_ERR_MSG)
}

// When adding a new constant, create a new getter for it as follows, so that the validator
// will crash if the constant is accessed before the protocol in which it is defined.
Expand Down Expand Up @@ -804,6 +855,19 @@ impl ProtocolConfig {
event_tag_size_derivation_cost_per_byte: Some(1_000),
// Emitting an event is cheap since its a vector push
event_emit_cost_per_byte: Some(1_000),
// Crypto
// ed25519
ed25519_ed25519_verify_cost_base: Some(52),
ed25519_ed25519_verify_msg_cost_per_byte: Some(0),
ed25519_ed25519_verify_msg_cost_per_block: Some(0),
// hash::blake2b256
hash_blake2b256_cost_base: Some(52),
hash_blake2b256_data_cost_per_byte: Some(0),
hash_blake2b256_data_cost_per_block: Some(0),
// hash::keccak256
hash_keccak256_cost_base: Some(52),
hash_keccak256_data_cost_per_byte: Some(0),
hash_keccak256_data_cost_per_block: Some(0)

// When adding a new constant, set it to None in the earliest version, like this:
// new_constant: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,13 @@ copy_convert_to_address_cost_per_byte: 10
event_value_size_derivation_cost_per_byte: 1000
event_tag_size_derivation_cost_per_byte: 1000
event_emit_cost_per_byte: 1000
ed25519_ed25519_verify_cost_base: 52
ed25519_ed25519_verify_msg_cost_per_byte: 0
ed25519_ed25519_verify_msg_cost_per_block: 0
hash_blake2b256_cost_base: 52
hash_blake2b256_data_cost_per_byte: 0
hash_blake2b256_data_cost_per_block: 0
hash_keccak256_cost_base: 52
hash_keccak256_data_cost_per_byte: 0
hash_keccak256_data_cost_per_block: 0

0 comments on commit 439750b

Please sign in to comment.