From 5fdb468c286e8d82f4c633760dc3ec442a74c11b Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Tue, 6 Sep 2022 15:04:50 +0200 Subject: [PATCH] NEP-364 (#7165) Implementing the following host functions according to the design proposed in [NEP-364](https://github.com/near/NEPs/pull/364) ```rs pub fn ed25519_verify( &mut self, sig_len: u64, sig_ptr: u64, msg_len: u64, msg_ptr: u64, pub_key_len: u64, pub_key_ptr: u64, register_id: u64, ) -> Result<()>; ``` - [x] added unit tests - [x] added costs (this needs to be reviewed, since I'm not sure how to infer the right numbers) --- Cargo.lock | 1 + chain/jsonrpc/res/rpc_errors_schema.json | 10 ++- core/primitives-core/Cargo.toml | 2 + core/primitives-core/src/config.rs | 23 +++++ core/primitives-core/src/parameter.rs | 4 + core/primitives-core/src/profile.rs | 8 ++ core/primitives/Cargo.toml | 7 +- .../res/runtime_configs/parameters.txt | 3 + .../runtime_configs/parameters_testnet.txt | 3 + core/primitives/src/version.rs | 4 + .../estimator-contract/src/lib.rs | 44 ++++++++++ runtime/near-vm-errors/src/lib.rs | 4 + runtime/near-vm-logic/Cargo.toml | 5 ++ runtime/near-vm-logic/src/logic.rs | 52 ++++++++++++ runtime/near-vm-logic/src/tests/miscs.rs | 85 +++++++++++++++++++ runtime/near-vm-runner/Cargo.toml | 6 ++ runtime/near-vm-runner/src/imports.rs | 8 ++ runtime/runtime-params-estimator/Cargo.toml | 10 ++- runtime/runtime-params-estimator/costs.txt | 2 + runtime/runtime-params-estimator/src/cost.rs | 7 +- .../src/costs_to_runtime_config.rs | 4 + runtime/runtime-params-estimator/src/lib.rs | 16 ++++ 22 files changed, 304 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b97aecc4576..baf3d6cf49d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3477,6 +3477,7 @@ dependencies = [ "borsh", "bs58", "byteorder", + "ed25519-dalek", "hex", "near-account-id", "near-crypto", diff --git a/chain/jsonrpc/res/rpc_errors_schema.json b/chain/jsonrpc/res/rpc_errors_schema.json index a1ab234544a..2fed69a416d 100644 --- a/chain/jsonrpc/res/rpc_errors_schema.json +++ b/chain/jsonrpc/res/rpc_errors_schema.json @@ -81,6 +81,13 @@ "msg": "" } }, + "Ed25519VerifyInvalidInput": { + "name": "Ed25519VerifyInvalidInput", + "subtypes": [], + "props": { + "msg": "" + } + }, "EmptyMethodName": { "name": "EmptyMethodName", "subtypes": [], @@ -147,7 +154,8 @@ "ContractSizeExceeded", "Deprecated", "ECRecoverError", - "AltBn128InvalidInput" + "AltBn128InvalidInput", + "Ed25519VerifyInvalidInput" ], "props": {} }, diff --git a/core/primitives-core/Cargo.toml b/core/primitives-core/Cargo.toml index 6cb852ddc97..079c18f0909 100644 --- a/core/primitives-core/Cargo.toml +++ b/core/primitives-core/Cargo.toml @@ -31,6 +31,8 @@ serde_json = "1" [features] default = [] +protocol_feature_ed25519_verify = [] + deepsize_feature = [ "deepsize", "near-account-id/deepsize_feature", diff --git a/core/primitives-core/src/config.rs b/core/primitives-core/src/config.rs index e26e4150e19..e46894662ec 100644 --- a/core/primitives-core/src/config.rs +++ b/core/primitives-core/src/config.rs @@ -309,6 +309,13 @@ pub struct ExtCostsConfig { /// Cost of getting ripemd160 per message block pub ripemd160_block: Gas, + /// Cost of getting ed25519 base + #[cfg(feature = "protocol_feature_ed25519_verify")] + pub ed25519_verify_base: Gas, + /// Cost of getting ed25519 per byte + #[cfg(feature = "protocol_feature_ed25519_verify")] + pub ed25519_verify_byte: Gas, + /// Cost of calling ecrecover pub ecrecover_base: Gas, @@ -452,6 +459,10 @@ impl ExtCostsConfig { keccak512_base: SAFETY_MULTIPLIER * 1937129412, keccak512_byte: SAFETY_MULTIPLIER * 12216567, ripemd160_base: SAFETY_MULTIPLIER * 284558362, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_base: SAFETY_MULTIPLIER * 1513656750, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_byte: SAFETY_MULTIPLIER * 7157035, // Cost per byte is 3542227. There are 64 bytes in a block. ripemd160_block: SAFETY_MULTIPLIER * 226702528, ecrecover_base: SAFETY_MULTIPLIER * 1121789875000, @@ -520,6 +531,10 @@ impl ExtCostsConfig { keccak512_byte: 0, ripemd160_base: 0, ripemd160_block: 0, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_base: 0, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_byte: 0, ecrecover_base: 0, log_base: 0, log_byte: 0, @@ -589,6 +604,10 @@ pub enum ExtCosts { keccak512_byte, ripemd160_base, ripemd160_block, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_base, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_byte, ecrecover_base, log_base, log_byte, @@ -670,6 +689,10 @@ impl ExtCosts { keccak512_byte => config.keccak512_byte, ripemd160_base => config.ripemd160_base, ripemd160_block => config.ripemd160_block, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_base => config.ed25519_verify_base, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_byte => config.ed25519_verify_byte, ecrecover_base => config.ecrecover_base, log_base => config.log_base, log_byte => config.log_byte, diff --git a/core/primitives-core/src/parameter.rs b/core/primitives-core/src/parameter.rs index 3e8669d4ef9..4c3096cab25 100644 --- a/core/primitives-core/src/parameter.rs +++ b/core/primitives-core/src/parameter.rs @@ -108,6 +108,8 @@ pub enum Parameter { WasmRipemd160Base, WasmRipemd160Block, WasmEcrecoverBase, + WasmEd25519VerifyBase, + WasmEd25519VerifyByte, WasmLogBase, WasmLogByte, WasmStorageWriteBase, @@ -236,6 +238,8 @@ impl Parameter { Parameter::WasmRipemd160Base, Parameter::WasmRipemd160Block, Parameter::WasmEcrecoverBase, + Parameter::WasmEd25519VerifyBase, + Parameter::WasmEd25519VerifyByte, Parameter::WasmLogBase, Parameter::WasmLogByte, Parameter::WasmStorageWriteBase, diff --git a/core/primitives-core/src/profile.rs b/core/primitives-core/src/profile.rs index 004c07dc5d2..98285ecb7ec 100644 --- a/core/primitives-core/src/profile.rs +++ b/core/primitives-core/src/profile.rs @@ -252,6 +252,10 @@ impl Cost { Cost::ExtCost { ext_cost_kind: ExtCosts::alt_bn128_pairing_check_element }, Cost::ExtCost { ext_cost_kind: ExtCosts::alt_bn128_g1_sum_base }, Cost::ExtCost { ext_cost_kind: ExtCosts::alt_bn128_g1_sum_element }, + #[cfg(feature = "protocol_feature_ed25519_verify")] + Cost::ExtCost { ext_cost_kind: ExtCosts::ed25519_verify_base }, + #[cfg(feature = "protocol_feature_ed25519_verify")] + Cost::ExtCost { ext_cost_kind: ExtCosts::ed25519_verify_byte }, ]; pub fn index(self) -> usize { @@ -326,6 +330,10 @@ impl Cost { Cost::ExtCost { ext_cost_kind: ExtCosts::alt_bn128_pairing_check_element } => 67, Cost::ExtCost { ext_cost_kind: ExtCosts::alt_bn128_g1_sum_base } => 68, Cost::ExtCost { ext_cost_kind: ExtCosts::alt_bn128_g1_sum_element } => 69, + #[cfg(feature = "protocol_feature_ed25519_verify")] + Cost::ExtCost { ext_cost_kind: ExtCosts::ed25519_verify_base } => 70, + #[cfg(feature = "protocol_feature_ed25519_verify")] + Cost::ExtCost { ext_cost_kind: ExtCosts::ed25519_verify_byte } => 71, } } } diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index 5a06d22a442..8dd5fe92b09 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -46,13 +46,18 @@ protocol_feature_fix_staking_threshold = [] protocol_feature_fix_contract_loading_cost = [] protocol_feature_account_id_in_function_call_permission = [] protocol_feature_reject_blocks_with_outdated_protocol_version = [] +protocol_feature_ed25519_verify = [ + "near-primitives-core/protocol_feature_ed25519_verify" +] nightly = [ "nightly_protocol", "protocol_feature_fix_staking_threshold", "protocol_feature_fix_contract_loading_cost", "protocol_feature_account_id_in_function_call_permission", - "protocol_feature_reject_blocks_with_outdated_protocol_version" + "protocol_feature_reject_blocks_with_outdated_protocol_version", + "protocol_feature_ed25519_verify", ] + nightly_protocol = [] diff --git a/core/primitives/res/runtime_configs/parameters.txt b/core/primitives/res/runtime_configs/parameters.txt index 62e72921272..00bd1dfc6a2 100644 --- a/core/primitives/res/runtime_configs/parameters.txt +++ b/core/primitives/res/runtime_configs/parameters.txt @@ -94,6 +94,9 @@ wasm_keccak512_byte: 36_649_701 wasm_ripemd160_base: 853_675_086 wasm_ripemd160_block: 680_107_584 wasm_ecrecover_base: 3_365_369_625_000 +# both ed25519_verify_base and ed25519_verify_byte have NON-FINAL numbers (needs fine tuning) +wasm_ed25519_verify_base: 40_311_888_867 +wasm_ed25519_verify_byte: 423_978_605 wasm_log_base: 3_543_313_050 wasm_log_byte: 13_198_791 wasm_storage_write_base: 64_196_736_000 diff --git a/core/primitives/res/runtime_configs/parameters_testnet.txt b/core/primitives/res/runtime_configs/parameters_testnet.txt index dde330f73d4..d8223613ecf 100644 --- a/core/primitives/res/runtime_configs/parameters_testnet.txt +++ b/core/primitives/res/runtime_configs/parameters_testnet.txt @@ -90,6 +90,9 @@ wasm_keccak512_byte: 36_649_701 wasm_ripemd160_base: 853_675_086 wasm_ripemd160_block: 680_107_584 wasm_ecrecover_base: 3_365_369_625_000 +# both ed25519_verify_base and ed25519_verify_byte have NON-FINAL numbers (needs fine tuning) +wasm_ed25519_verify_base: 40_311_888_867 +wasm_ed25519_verify_byte: 423_978_605 wasm_log_base: 3_543_313_050 wasm_log_byte: 13_198_791 wasm_storage_write_base: 64_196_736_000 diff --git a/core/primitives/src/version.rs b/core/primitives/src/version.rs index 953fe8b2e43..71d1b4ac650 100644 --- a/core/primitives/src/version.rs +++ b/core/primitives/src/version.rs @@ -162,6 +162,8 @@ pub enum ProtocolFeature { /// Validate account id for function call access keys. #[cfg(feature = "protocol_feature_account_id_in_function_call_permission")] AccountIdInFunctionCallPermission, + #[cfg(feature = "protocol_feature_ed25519_verify")] + Ed25519Verify, #[cfg(feature = "protocol_feature_reject_blocks_with_outdated_protocol_version")] RejectBlocksWithOutdatedProtocolVersions, #[cfg(feature = "shardnet")] @@ -258,6 +260,8 @@ impl ProtocolFeature { ProtocolFeature::FixContractLoadingCost => 129, #[cfg(feature = "protocol_feature_account_id_in_function_call_permission")] ProtocolFeature::AccountIdInFunctionCallPermission => 130, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ProtocolFeature::Ed25519Verify => 131, #[cfg(feature = "protocol_feature_reject_blocks_with_outdated_protocol_version")] ProtocolFeature::RejectBlocksWithOutdatedProtocolVersions => { if cfg!(feature = "shardnet") { diff --git a/runtime/near-test-contracts/estimator-contract/src/lib.rs b/runtime/near-test-contracts/estimator-contract/src/lib.rs index b33ec512ba1..22f911ad5ee 100644 --- a/runtime/near-test-contracts/estimator-contract/src/lib.rs +++ b/runtime/near-test-contracts/estimator-contract/src/lib.rs @@ -54,6 +54,14 @@ extern "C" { malleability_flag: u64, register_id: u64, ) -> u64; + pub fn ed25519_verify( + sig_len: u64, + sig_ptr: u64, + msg_len: u64, + msg_ptr: u64, + pub_key_len: u64, + pub_key_ptr: u64, + ) -> u64; // ##################### // # Miscellaneous API # // ##################### @@ -467,6 +475,42 @@ pub unsafe fn ecrecover_10k() { } } +// Function to measure `ed25519_verify_base`. Also measures `base`, `write_register_base`, and +// `write_register_byte`. However `ed25519_verify_base` computation is more expensive than register writing +// so we are okay overcharging it. // TODO: validate this +// Compute ecrecover 10k times. +#[no_mangle] +pub unsafe fn ed25519_verify_10k() { + let signature: [u8; 64] = [ + 145, 193, 203, 18, 114, 227, 14, 117, 33, 213, 121, 66, 130, 14, 25, 4, 36, 120, 46, 142, + 226, 215, 7, 66, 122, 112, 97, 30, 249, 135, 61, 165, 221, 249, 252, 23, 105, 40, 56, 70, + 31, 152, 236, 141, 154, 122, 207, 20, 75, 118, 79, 90, 168, 6, 221, 122, 213, 29, 126, 196, + 216, 104, 191, 6, + ]; + + let public_key: [u8; 32] = [ + 32, 122, 6, 120, 146, 130, 30, 37, 215, 112, 241, 251, 160, 196, 124, 17, 255, 75, 129, 62, + 84, 22, 46, 206, 158, 184, 57, 224, 118, 35, 26, 182, + ]; + + // 32 bytes message + let message: [u8; 32] = [ + 107, 97, 106, 100, 108, 102, 107, 106, 97, 108, 107, 102, 106, 97, 107, 108, 102, 106, 100, + 107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107, + ]; + + for _ in 0..10_000 { + ed25519_verify( + signature.len() as _, + signature.as_ptr() as _, + message.len() as _, + message.as_ptr() as _, + public_key.len() as _, + public_key.as_ptr() as _, + ); + } +} + #[repr(C)] struct MultiexpElem([u8; 64], [u8; 32]); diff --git a/runtime/near-vm-errors/src/lib.rs b/runtime/near-vm-errors/src/lib.rs index dcb0036908c..76814b4c24f 100644 --- a/runtime/near-vm-errors/src/lib.rs +++ b/runtime/near-vm-errors/src/lib.rs @@ -224,6 +224,9 @@ pub enum HostError { /// Invalid input to alt_bn128 familiy of functions (e.g., point which isn't /// on the curve). AltBn128InvalidInput { msg: String }, + /// Invalid input to ed25519 signature verification function (e.g. signature cannot be + /// derived from bytes). + Ed25519VerifyInvalidInput { msg: String }, } #[derive(Debug, PartialEq)] @@ -421,6 +424,7 @@ impl std::fmt::Display for HostError { Deprecated {method_name}=> write!(f, "Attempted to call deprecated host function {}", method_name), AltBn128InvalidInput { msg } => write!(f, "AltBn128 invalid input: {}", msg), ECRecoverError { msg } => write!(f, "ECDSA recover error: {}", msg), + Ed25519VerifyInvalidInput { msg } => write!(f, "ED25519 signature verification error: {}", msg), } } } diff --git a/runtime/near-vm-logic/Cargo.toml b/runtime/near-vm-logic/Cargo.toml index f25ce8f1899..e75f190e623 100644 --- a/runtime/near-vm-logic/Cargo.toml +++ b/runtime/near-vm-logic/Cargo.toml @@ -22,6 +22,7 @@ ripemd = "0.1.1" serde = { version = "1", features = ["derive"] } sha2 = ">=0.8,<=0.10" sha3 = ">=0.8,<=0.10" +ed25519-dalek = "1" near-crypto = { path = "../../core/crypto" } near-account-id = { path = "../../core/account-id", features = [ "internal_unstable" ] } @@ -37,11 +38,15 @@ tracing = { version = "0.1.13", optional = true } hex = { version = "0.4", features = ["serde"] } serde_json = { version = "1", features = ["preserve_order"] } + [features] default = [] protocol_feature_fix_contract_loading_cost = [ "near-primitives/protocol_feature_fix_contract_loading_cost", ] +protocol_feature_ed25519_verify = [ + "near-primitives/protocol_feature_ed25519_verify" +] io_trace = ["tracing"] # Use this feature to enable counting of fees and costs applied. diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 3287b044a02..cd68548d9e4 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -1119,6 +1119,58 @@ impl<'a> VMLogic<'a> { Ok(false as u64) } + /// Verify an ED25519 signature given a message and a public key. + /// # Returns + /// - 1 meaning the boolean expression true to encode that the signature was properly verified + /// - 0 meaning the boolean expression false to encode that the signature failed to be verified + /// + /// # Cost + /// + /// Each input can either be in memory or in a register. Set the length of the input to `u64::MAX` + /// to declare that the input is a register number and not a pointer. + /// Each input has a gas cost input_cost(num_bytes) that depends on whether it is from memory + /// or from a register. It is either read_memory_base + num_bytes * read_memory_byte in the + /// former case or read_register_base + num_bytes * read_register_byte in the latter. This function + /// is labeled as `input_cost` below. + /// + /// `input_cost(num_bytes_signature) + input_cost(num_bytes_message) + input_cost(num_bytes_public_key) + + /// ed25519_verify_base + ed25519_verify_byte * num_bytes_message` + #[cfg(feature = "protocol_feature_ed25519_verify")] + pub fn ed25519_verify( + &mut self, + sig_len: u64, + sig_ptr: u64, + msg_len: u64, + msg_ptr: u64, + pub_key_len: u64, + pub_key_ptr: u64, + ) -> Result { + use ed25519_dalek::{PublicKey, Signature, Verifier, SIGNATURE_LENGTH}; + + self.gas_counter.pay_base(ed25519_verify_base)?; + if sig_len != SIGNATURE_LENGTH as u64 { + return Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { + msg: "invalid signature length".to_string(), + })); + } + let msg = self.get_vec_from_memory_or_register(msg_ptr, msg_len)?; + let signature_array = self.get_vec_from_memory_or_register(sig_ptr, sig_len)?; + let signature = Signature::from_bytes(&signature_array).map_err(|e| { + VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { msg: e.to_string() }) + })?; + let num_bytes = msg.len(); + self.gas_counter.pay_per(ed25519_verify_byte, num_bytes as _)?; + + let pub_key_array = self.get_vec_from_memory_or_register(pub_key_ptr, pub_key_len)?; + let pub_key = PublicKey::from_bytes(&pub_key_array).map_err(|e| { + VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { msg: e.to_string() }) + })?; + match pub_key.verify(&msg, &signature) { + Err(_) => Ok(0), + Ok(()) => Ok(1), + } + } + /// Called by gas metering injected into Wasm. Counts both towards `burnt_gas` and `used_gas`. /// /// # Errors diff --git a/runtime/near-vm-logic/src/tests/miscs.rs b/runtime/near-vm-logic/src/tests/miscs.rs index 547496cf3f5..207319f7d46 100644 --- a/runtime/near-vm-logic/src/tests/miscs.rs +++ b/runtime/near-vm-logic/src/tests/miscs.rs @@ -859,3 +859,88 @@ fn test_contract_size_limit() { .into()) ); } + +#[cfg(feature = "protocol_feature_ed25519_verify")] +#[test] +fn test_ed25519_verify() { + use near_vm_errors::VMLogicError; + + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(get_context(vec![], false)); + + let signature: [u8; 64] = [ + 145, 193, 203, 18, 114, 227, 14, 117, 33, 213, 121, 66, 130, 14, 25, 4, 36, 120, 46, 142, + 226, 215, 7, 66, 122, 112, 97, 30, 249, 135, 61, 165, 221, 249, 252, 23, 105, 40, 56, 70, + 31, 152, 236, 141, 154, 122, 207, 20, 75, 118, 79, 90, 168, 6, 221, 122, 213, 29, 126, 196, + 216, 104, 191, 6, + ]; + + let bad_signature: [u8; 64] = [1; 64]; + + let public_key: [u8; 32] = [ + 32, 122, 6, 120, 146, 130, 30, 37, 215, 112, 241, 251, 160, 196, 124, 17, 255, 75, 129, 62, + 84, 22, 46, 206, 158, 184, 57, 224, 118, 35, 26, 182, + ]; + + // 32 bytes message + let message: [u8; 32] = [ + 107, 97, 106, 100, 108, 102, 107, 106, 97, 108, 107, 102, 106, 97, 107, 108, 102, 106, 100, + 107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107, + ]; + + let result = logic + .ed25519_verify( + signature.len() as _, + signature.as_ptr() as _, + message.len() as _, + message.as_ptr() as _, + public_key.len() as _, + public_key.as_ptr() as _, + ) + .unwrap(); + + assert_eq!(result, 1); + + assert_costs(map! { + ExtCosts::read_memory_byte: 128, + ExtCosts::read_memory_base: 3, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }); + + let result = logic + .ed25519_verify( + bad_signature.len() as _, + bad_signature.as_ptr() as _, + message.len() as _, + message.as_ptr() as _, + public_key.len() as _, + public_key.as_ptr() as _, + ) + .unwrap(); + + assert_eq!(result, 0); + + assert_costs(map! { + ExtCosts::read_memory_byte: 128, + ExtCosts::read_memory_base: 3, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }); + + let result = logic.ed25519_verify( + (signature.len() - 1) as _, + signature.as_ptr() as _, + message.len() as _, + message.as_ptr() as _, + public_key.len() as _, + public_key.as_ptr() as _, + ); + + assert_eq!( + result, + Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { + msg: "invalid signature length".to_string() + })) + ); +} diff --git a/runtime/near-vm-runner/Cargo.toml b/runtime/near-vm-runner/Cargo.toml index 7de43456aa6..ee15cd09e6a 100644 --- a/runtime/near-vm-runner/Cargo.toml +++ b/runtime/near-vm-runner/Cargo.toml @@ -101,10 +101,16 @@ protocol_feature_fix_contract_loading_cost = [ nightly = [ "near-primitives/nightly", "protocol_feature_fix_contract_loading_cost", + "protocol_feature_ed25519_verify", ] sandbox = ["near-vm-logic/sandbox"] io_trace = ["near-vm-logic/io_trace"] +protocol_feature_ed25519_verify = [ + "near-primitives/protocol_feature_ed25519_verify", + "near-vm-logic/protocol_feature_ed25519_verify" +] + [package.metadata.cargo-udeps.ignore] # `no_cache` feature leads to an unused `cached` crate normal = ["cached"] diff --git a/runtime/near-vm-runner/src/imports.rs b/runtime/near-vm-runner/src/imports.rs index ce27f40d342..6cff8bb241f 100644 --- a/runtime/near-vm-runner/src/imports.rs +++ b/runtime/near-vm-runner/src/imports.rs @@ -103,6 +103,14 @@ imports! { sha256<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, keccak256<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, keccak512<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + #["protocol_feature_ed25519_verify", Ed25519Verify] ed25519_verify<[ + sig_len: u64, + sig_ptr: u64, + msg_len: u64, + msg_ptr: u64, + pub_key_len: u64, + pub_key_ptr: u64 + ] -> [u64]>, #[MathExtension] ripemd160<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, #[MathExtension] ecrecover<[hash_len: u64, hash_ptr: u64, sign_len: u64, sig_ptr: u64, v: u64, malleability_flag: u64, register_id: u64] -> [u64]>, // ##################### diff --git a/runtime/runtime-params-estimator/Cargo.toml b/runtime/runtime-params-estimator/Cargo.toml index f1b7621967c..ef1ceb8c583 100644 --- a/runtime/runtime-params-estimator/Cargo.toml +++ b/runtime/runtime-params-estimator/Cargo.toml @@ -64,6 +64,14 @@ wasmtime = ["near-vm-runner/force_wasmtime"] nightly = [ "nightly_protocol", ] -nightly_protocol = ["near-primitives/nightly_protocol", "near-test-contracts/nightly"] +nightly_protocol = [ + "near-primitives/nightly_protocol", + "near-test-contracts/nightly", + "protocol_feature_ed25519_verify" +] sandbox = ["node-runtime/sandbox"] io_trace = ["near-store/io_trace", "near-o11y/io_trace", "near-vm-logic/io_trace"] +protocol_feature_ed25519_verify = [ + "near-vm-logic/protocol_feature_ed25519_verify", + "near-vm-runner/protocol_feature_ed25519_verify" +] \ No newline at end of file diff --git a/runtime/runtime-params-estimator/costs.txt b/runtime/runtime-params-estimator/costs.txt index 4e88f1b50c2..1baed02df29 100644 --- a/runtime/runtime-params-estimator/costs.txt +++ b/runtime/runtime-params-estimator/costs.txt @@ -36,6 +36,8 @@ Keccak512Byte 12_216_567 Ripemd160Base 284_558_362 Ripemd160Block 226_702_528 EcrecoverBase 92_940_662_819 +Ed25519VerifyBase 40_311_888_867 +Ed25519VerifyByte 423_978_605 LogBase 1_181_104_350 LogByte 4_399_597 StorageWriteBase 21_398_912_000 diff --git a/runtime/runtime-params-estimator/src/cost.rs b/runtime/runtime-params-estimator/src/cost.rs index 8b68452db47..1e18b367d24 100644 --- a/runtime/runtime-params-estimator/src/cost.rs +++ b/runtime/runtime-params-estimator/src/cost.rs @@ -354,7 +354,12 @@ pub enum Cost { /// function `ecrecover` to verify an ECDSA signature and extract the /// signer. EcrecoverBase, - + /// Estimates `ed25519_verify_base`, which covers the base cost of the host + /// function `ed25519_verify` to verify an ED25519 signatures. + Ed25519VerifyBase, + /// Estimates `ed25519_verify_byte`, the cost charged per input byte in calls to the + /// ed25519_verify host function. + Ed25519VerifyByte, // `storage_write` records a single key-value pair, initially in the // prospective changes in-memory hash map, and then once a full block has // been processed, in the on-disk trie. If there was already a value diff --git a/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs b/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs index a4655421450..f6b65d0241d 100644 --- a/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs +++ b/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs @@ -114,6 +114,10 @@ fn ext_costs_config(cost_table: &CostTable) -> anyhow::Result { ripemd160_base: get(Cost::Ripemd160Base)?, ripemd160_block: get(Cost::Ripemd160Block)?, ecrecover_base: get(Cost::EcrecoverBase)?, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_base: get(Cost::Ed25519VerifyBase)?, + #[cfg(feature = "protocol_feature_ed25519_verify")] + ed25519_verify_byte: get(Cost::Ed25519VerifyByte)?, log_base: get(Cost::LogBase)?, log_byte: get(Cost::LogByte)?, storage_write_base: get(Cost::StorageWriteBase)?, diff --git a/runtime/runtime-params-estimator/src/lib.rs b/runtime/runtime-params-estimator/src/lib.rs index a39fb41787f..776d2cad437 100644 --- a/runtime/runtime-params-estimator/src/lib.rs +++ b/runtime/runtime-params-estimator/src/lib.rs @@ -168,6 +168,10 @@ static ALL_COSTS: &[(Cost, fn(&mut EstimatorContext) -> GasCost)] = &[ (Cost::Ripemd160Base, ripemd160_base), (Cost::Ripemd160Block, ripemd160_block), (Cost::EcrecoverBase, ecrecover_base), + #[cfg(feature = "protocol_feature_ed25519_verify")] + (Cost::Ed25519VerifyBase, ed25519_verify_base), + #[cfg(feature = "protocol_feature_ed25519_verify")] + (Cost::Ed25519VerifyByte, ed25519_verify_byte), (Cost::AltBn128G1MultiexpBase, alt_bn128g1_multiexp_base), (Cost::AltBn128G1MultiexpElement, alt_bn128g1_multiexp_element), (Cost::AltBn128G1SumBase, alt_bn128g1_sum_base), @@ -923,6 +927,18 @@ fn ecrecover_base(ctx: &mut EstimatorContext) -> GasCost { fn_cost(ctx, "ecrecover_10k", ExtCosts::ecrecover_base, 10_000) } +#[cfg(feature = "protocol_feature_ed25519_verify")] +// TODO: gas estimation will be calculated later -> setting a placeholder for now +fn ed25519_verify_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "ed25519_verify_10k", ExtCosts::ed25519_verify_base, 10_000) +} + +#[cfg(feature = "protocol_feature_ed25519_verify")] +// TODO: gas estimation will be calculated later -> setting a placeholder for now +fn ed25519_verify_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "ed25519_verify_10k", ExtCosts::ed25519_verify_byte, 960000) +} + fn alt_bn128g1_multiexp_base(ctx: &mut EstimatorContext) -> GasCost { fn_cost(ctx, "alt_bn128_g1_multiexp_1_10", ExtCosts::alt_bn128_g1_multiexp_base, 10) }