Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

contracts: add sr25519_verify #13724

Merged
merged 45 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dab8913
wip
pgherveou Mar 29, 2023
d2da027
fix
pgherveou Mar 29, 2023
dd14d7f
wip
pgherveou Mar 29, 2023
d57ffcb
fix lint
pgherveou Mar 29, 2023
1a0f97d
rm fixture fix
pgherveou Mar 29, 2023
feba43d
missing comment
pgherveou Mar 29, 2023
d560208
fix lint
pgherveou Mar 29, 2023
c7e5430
add comment to the wsm file
pgherveou Mar 30, 2023
505919c
fix comment
pgherveou Mar 30, 2023
c5fba3a
Apply suggestions from code review
pgherveou Apr 3, 2023
a2ed618
wip
pgherveou Apr 4, 2023
1c305a4
wip weights
pgherveou Apr 4, 2023
5aa4bf1
wip weights
pgherveou Apr 4, 2023
57120be
Merge branch 'master' into pg/contracts-add-sr25519_recover
pgherveou Apr 4, 2023
2e7b44d
PR comment: test with return code
pgherveou Apr 4, 2023
f93e934
wip
pgherveou Apr 4, 2023
5084dbb
PR review add mock test
pgherveou Apr 4, 2023
90fd7ca
remove
pgherveou Apr 4, 2023
24fcbd4
lint
pgherveou Apr 4, 2023
66a9322
Update frame/contracts/fixtures/sr25519_verify.wat
pgherveou Apr 4, 2023
03ad784
fix comments
pgherveou Apr 5, 2023
dcd849d
Update frame/contracts/src/benchmarking/mod.rs
pgherveou Apr 5, 2023
b896c4c
Update frame/contracts/src/wasm/runtime.rs
pgherveou Apr 5, 2023
8a30502
Update frame/contracts/fixtures/sr25519_verify.wat
pgherveou Apr 5, 2023
d49c633
Update frame/contracts/src/benchmarking/mod.rs
pgherveou Apr 5, 2023
851111a
fix lint
pgherveou Apr 5, 2023
f7dda14
Merge branch 'master' of https://github.com/paritytech/substrate into…
Apr 5, 2023
a7ab12b
".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts
Apr 5, 2023
d11b430
Update frame/contracts/src/wasm/runtime.rs
pgherveou Apr 5, 2023
9534b05
PR: review use unstable + remove arbitrary index 4
pgherveou Apr 5, 2023
c0a436a
Add benchmark for calculating overhead of calling sr25519_verify
pgherveou Apr 6, 2023
a39c784
fix message length encoding
pgherveou Apr 6, 2023
4f2b8e9
fix weights
pgherveou Apr 6, 2023
6fdb544
Merge branch 'master' of https://github.com/paritytech/substrate into…
Apr 6, 2023
3fbe20f
".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts
Apr 6, 2023
44c0dca
Apply suggestions from code review
pgherveou Apr 7, 2023
5907075
Update frame/contracts/src/wasm/runtime.rs
pgherveou Apr 7, 2023
6c1bbdb
Update frame/contracts/src/wasm/runtime.rs
pgherveou Apr 7, 2023
4c1cee8
Update frame/contracts/src/benchmarking/mod.rs
pgherveou Apr 7, 2023
e2d01de
Update frame/contracts/src/benchmarking/mod.rs
pgherveou Apr 7, 2023
7d206cf
Update frame/contracts/src/schedule.rs
pgherveou Apr 12, 2023
5f1eb72
Update frame/contracts/src/schedule.rs
pgherveou Apr 12, 2023
9529c0a
Update frame/contracts/src/wasm/runtime.rs
pgherveou Apr 12, 2023
3b9b849
Update frame/contracts/src/wasm/runtime.rs
pgherveou Apr 12, 2023
e5c9723
PR review
pgherveou Apr 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions frame/contracts/fixtures/sr25519_verify.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
;; This contract:
;; 1) Reads signature, message and public key from the input
;; 2) Calls sr25519_verify
;; 3) Traps if the signature is invalid
pgherveou marked this conversation as resolved.
Show resolved Hide resolved

(module
(import "seal0" "seal_sr25519_verify" (func $seal_sr25519_verify (param i32 i32 i32 i32) (result i32)))
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "env" "memory" (memory 1 1))

;; [4, 8) len of signature + message + public key - 64 + 12 + 32 = 108 bytes
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
(data (i32.const 4) "\6c")

(func $assert (param i32)
(block $ok
(br_if $ok
(get_local 0)
)
(unreachable)
)
)

;; Memory layout during `call`
;; [10, 75) signature
;; [75, 86) message
;; [86, 118) public key


(func (export "deploy"))

(func (export "call")
(local $signature_ptr i32)
(local $message_ptr i32)
(local $pub_key_ptr i32)
(local $result i32)

(local.set $signature_ptr (i32.const 10))
(local.set $message_ptr (i32.const 74))
(local.set $pub_key_ptr (i32.const 86))

;; Read signature, message and public key - 107 bytes
(call $seal_input (local.get $signature_ptr) (i32.const 4))
(local.set
$result
(call $seal_sr25519_verify
(local.get $signature_ptr)
(local.get $message_ptr)
(i32.const 12)
(local.get $pub_key_ptr)
)
)

(call $assert
(i32.eq
(local.get $result) ;; The result
(i32.const 0x0) ;; 0x0 - Success result
)
)
)
)

17 changes: 15 additions & 2 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ use frame_support::{
use frame_system::RawOrigin;
use pallet_contracts_primitives::ExecReturnValue;
use smallvec::{Array, SmallVec};
use sp_core::ecdsa::Public as ECDSAPublic;
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
use sp_core::{
ecdsa::Public as ECDSAPublic,
sr25519::{Public as SR25519Public, Signature as SR25519Signature},
};
use sp_io::{
crypto::{secp256k1_ecdsa_recover_compressed, sr25519_verify as crypto_sr25519_verify},
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
hashing::blake2_256,
};
use sp_runtime::traits::{Convert, Hash};
use sp_std::{marker::PhantomData, mem, prelude::*, vec::Vec};

Expand Down Expand Up @@ -272,6 +278,9 @@ pub trait Ext: sealing::Sealed {
/// Recovers ECDSA compressed public key based on signature and message hash.
fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>;

/// Recovers sr25519 compressed public key based on signature and message hash.
fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool;

/// Returns Ethereum address from the ECDSA compressed public key.
fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()>;

Expand Down Expand Up @@ -1347,6 +1356,10 @@ where
secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ())
}

fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool {
crypto_sr25519_verify(&SR25519Signature(*signature), message, &SR25519Public(*pub_key))
}

fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> {
ECDSAPublic(*pk).to_eth_address()
}
Expand Down
4 changes: 4 additions & 0 deletions frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ pub struct HostFnWeights<T: Config> {
/// Weight of calling `seal_ecdsa_to_eth_address`.
pub ecdsa_to_eth_address: Weight,

/// Weight of calling `seal_sr25519_verify`.
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
pub sr25519_verify: Weight,

/// Weight of calling `reentrance_count`.
pub reentrance_count: Weight,

Expand Down Expand Up @@ -616,6 +619,7 @@ impl<T: Config> Default for HostFnWeights<T> {
hash_blake2_128: cost!(seal_hash_blake2_128),
hash_blake2_128_per_byte: cost!(seal_hash_blake2_128_per_byte),
ecdsa_recover: cost!(seal_ecdsa_recover),
sr25519_verify: cost!(seal_sr25519_verify),
ecdsa_to_eth_address: cost!(seal_ecdsa_to_eth_address),
reentrance_count: cost!(seal_reentrance_count),
account_reentrance_count: cost!(seal_account_reentrance_count),
Expand Down
62 changes: 62 additions & 0 deletions frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2906,6 +2906,68 @@ fn ecdsa_recover() {
})
}

#[test]
fn sr25519_verify() {
let (wasm, _code_hash) = compile_module::<Test>("sr25519_verify").unwrap();

ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);

// Instantiate the sr25519_verify contract.
let addr = Contracts::bare_instantiate(
ALICE,
100_000,
GAS_LIMIT,
None,
Code::Upload(wasm),
vec![],
vec![],
false,
)
.result
.unwrap()
.account_id;

let call_with_message = |message: &[u8; 11]| {
// Alice's signature for "hello world"
#[rustfmt::skip]
let signature: [u8; 64] = [
184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247,
99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83,
85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255,
228, 54, 115, 63, 30, 207, 205, 131,
];

// Alice's public key
#[rustfmt::skip]
let public_key: [u8; 32] = [
212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44,
133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125,
];

let mut params = vec![];
params.extend_from_slice(&signature);
params.extend_from_slice(&message.to_vec().encode());
params.extend_from_slice(&public_key);

<Pallet<Test>>::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
params,
false,
Determinism::Enforced,
)
.result
};

assert!(call_with_message(&b"hello world").is_ok());
assert!(call_with_message(&b"hello worlD").is_err());
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
})
}

#[test]
fn upload_code_works() {
let (wasm, code_hash) = compile_module::<Test>("dummy").unwrap();
Expand Down
6 changes: 6 additions & 0 deletions frame/contracts/src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ mod tests {
gas_meter: GasMeter<Test>,
debug_buffer: Vec<u8>,
ecdsa_recover: RefCell<Vec<([u8; 65], [u8; 32])>>,
sr25519_verify: RefCell<Vec<([u8; 64], Vec<u8>, [u8; 32])>>,
code_hashes: Vec<CodeHash<Test>>,
}

Expand All @@ -460,6 +461,7 @@ mod tests {
gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)),
debug_buffer: Default::default(),
ecdsa_recover: Default::default(),
sr25519_verify: Default::default(),
}
}
}
Expand Down Expand Up @@ -614,6 +616,10 @@ mod tests {
self.ecdsa_recover.borrow_mut().push((*signature, *message_hash));
Ok([3; 33])
}
fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool {
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
self.sr25519_verify.borrow_mut().push((*signature, message.into(), *pub_key));
true
}
fn contract_info(&mut self) -> &mut crate::ContractInfo<Self::T> {
unimplemented!()
}
Expand Down
42 changes: 42 additions & 0 deletions frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub enum ReturnCode {
/// ECDSA compressed pubkey conversion into Ethereum address failed (most probably
/// wrong pubkey provided).
EcdsaRecoverFailed = 11,
/// sr25519 signature verification failed.
Sr25519VerifyFailed = 12,
}

impl From<ExecReturnValue> for ReturnCode {
Expand Down Expand Up @@ -250,6 +252,8 @@ pub enum RuntimeCosts {
HashBlake128(u32),
/// Weight of calling `seal_ecdsa_recover`.
EcdsaRecovery,
/// Weight of calling `seal_sr25519_verify`.
Sr25519Verify,
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
/// Weight charged by a chain extension through `seal_call_chain_extension`.
ChainExtension(Weight),
/// Weight charged for calling into the runtime.
Expand Down Expand Up @@ -335,6 +339,7 @@ impl RuntimeCosts {
.hash_blake2_128
.saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())),
EcdsaRecovery => s.ecdsa_recover,
Sr25519Verify => s.sr25519_verify,
ChainExtension(weight) => weight,
CallRuntime(weight) => weight,
SetCodeHash => s.set_code_hash,
Expand Down Expand Up @@ -2465,6 +2470,43 @@ pub mod env {
}
}

/// Verify the sr25519 signature given the message and public key
///
/// # Parameters
///
/// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should
/// be decodable as a 64 bytes. Traps otherwise.
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
/// - `message_ptr`: the pointer into the linear memory where the message is placed.
/// - `message_len`: the length of the message payload. Should be decodable as a Scale encoded
/// Vec<u8>. Traps otherwise.
/// - `pub_key_ptr`: the pointer into the linear memory where the public key is placed. Should
/// be decodable as a 32 bytes. Traps otherwise.
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
/// # Errors
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
///
/// - `ReturnCode::Sr25519VerifyFailed
#[prefixed_alias]
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
fn sr25519_verify(
ctx: _,
memory: _,
signature_ptr: u32,
message_ptr: u32,
message_len: u32,
pub_key_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::Sr25519Verify)?;
let mut signature: [u8; 64] = [0; 64];
ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?;
let mut pub_key: [u8; 32] = [0; 32];
ctx.read_sandbox_memory_into_buf(memory, pub_key_ptr, &mut pub_key)?;
let message: Vec<u8> =
ctx.read_sandbox_memory_as_unbounded(memory, message_ptr, message_len)?;
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
if ctx.ext.sr25519_verify(&signature, &message, &pub_key) {
Ok(ReturnCode::Success)
} else {
Ok(ReturnCode::Sr25519VerifyFailed)
}
}

/// Replace the contract code at the specified address with new code.
///
/// # Note
Expand Down
7 changes: 7 additions & 0 deletions frame/contracts/src/weights.rs
pgherveou marked this conversation as resolved.
Show resolved Hide resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.