mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 16:57:58 +00:00
* Implement ext_ hashes for contracts (issue #5258) * load cryto hash .wat from raw string literal instead of file * update .wat contents for testing crypto hashes * remove unnecessary 'static * fix bug in input (call_indirect required 1+ at least it seems) * no longer use scratch buffer for crypto hash functions * improve doc comments of ext_ hash functions * remove unnecessary comment in .wat test file * add return value (const 0) to contract test to hopefully enable result buffer * fix bug in contract assertion * implement proper output_len in contract * implement proper test for crypto hashes * bump spec_version 238 -> 239 * fix COMPLEXITY description * remove final invalid instances of scratch buffer from docs
This commit is contained in:
@@ -2736,3 +2736,149 @@ fn get_runtime_storage() {
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
const CODE_CRYPTO_HASHES: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_write" (func $ext_scratch_write (param i32 i32)))
|
||||
|
||||
(import "env" "ext_hash_sha2_256" (func $ext_hash_sha2_256 (param i32 i32 i32)))
|
||||
(import "env" "ext_hash_keccak_256" (func $ext_hash_keccak_256 (param i32 i32 i32)))
|
||||
(import "env" "ext_hash_blake2_256" (func $ext_hash_blake2_256 (param i32 i32 i32)))
|
||||
(import "env" "ext_hash_blake2_128" (func $ext_hash_blake2_128 (param i32 i32 i32)))
|
||||
(import "env" "ext_hash_twox_256" (func $ext_hash_twox_256 (param i32 i32 i32)))
|
||||
(import "env" "ext_hash_twox_128" (func $ext_hash_twox_128 (param i32 i32 i32)))
|
||||
(import "env" "ext_hash_twox_64" (func $ext_hash_twox_64 (param i32 i32 i32)))
|
||||
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(type $hash_fn_sig (func (param i32 i32 i32)))
|
||||
(table 8 funcref)
|
||||
(elem (i32.const 1)
|
||||
$ext_hash_sha2_256
|
||||
$ext_hash_keccak_256
|
||||
$ext_hash_blake2_256
|
||||
$ext_hash_blake2_128
|
||||
$ext_hash_twox_256
|
||||
$ext_hash_twox_128
|
||||
$ext_hash_twox_64
|
||||
)
|
||||
(data (i32.const 1) "20202010201008") ;; Output sizes of the hashes in order in hex.
|
||||
|
||||
;; Not in use by the tests besides instantiating the contract.
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Called by the tests.
|
||||
;;
|
||||
;; The `call` function expects data in a certain format in the scratch
|
||||
;; buffer.
|
||||
;;
|
||||
;; 1. The first byte encodes an identifier for the crypto hash function
|
||||
;; under test. (*)
|
||||
;; 2. The rest encodes the input data that is directly fed into the
|
||||
;; crypto hash function chosen in 1.
|
||||
;;
|
||||
;; The `deploy` function then computes the chosen crypto hash function
|
||||
;; given the input and puts the result back into the scratch buffer.
|
||||
;; After contract execution the test driver then asserts that the returned
|
||||
;; values are equal to the expected bytes for the input and chosen hash
|
||||
;; function.
|
||||
;;
|
||||
;; (*) The possible value for the crypto hash identifiers can be found below:
|
||||
;;
|
||||
;; | value | Algorithm | Bit Width |
|
||||
;; |-------|-----------|-----------|
|
||||
;; | 0 | SHA2 | 256 |
|
||||
;; | 1 | KECCAK | 256 |
|
||||
;; | 2 | BLAKE2 | 256 |
|
||||
;; | 3 | BLAKE2 | 128 |
|
||||
;; | 4 | TWOX | 256 |
|
||||
;; | 5 | TWOX | 128 |
|
||||
;; | 6 | TWOX | 64 |
|
||||
;; ---------------------------------
|
||||
(func (export "call") (result i32)
|
||||
(local $chosen_hash_fn i32)
|
||||
(local $input_ptr i32)
|
||||
(local $input_len i32)
|
||||
(local $output_ptr i32)
|
||||
(local $output_len i32)
|
||||
(local.set $input_ptr (i32.const 10))
|
||||
(call $ext_scratch_read (local.get $input_ptr) (i32.const 0) (call $ext_scratch_size))
|
||||
(local.set $chosen_hash_fn (i32.load8_u (local.get $input_ptr)))
|
||||
(if (i32.gt_u (local.get $chosen_hash_fn) (i32.const 7))
|
||||
;; We check that the chosen hash fn identifier is within bounds: [0,7]
|
||||
(unreachable)
|
||||
)
|
||||
(local.set $input_ptr (i32.add (local.get $input_ptr) (i32.const 1)))
|
||||
(local.set $input_len (i32.sub (call $ext_scratch_size) (i32.const 1)))
|
||||
(local.set $output_ptr (i32.const 100))
|
||||
(local.set $output_len (i32.load8_u (local.get $chosen_hash_fn)))
|
||||
(call_indirect (type $hash_fn_sig)
|
||||
(local.get $input_ptr)
|
||||
(local.get $input_len)
|
||||
(local.get $output_ptr)
|
||||
(local.get $chosen_hash_fn) ;; Which crypto hash function to execute.
|
||||
)
|
||||
(call $ext_scratch_write
|
||||
(local.get $output_ptr) ;; Linear memory location of the output buffer.
|
||||
(local.get $output_len) ;; Number of output buffer bytes.
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn crypto_hashes() {
|
||||
let (wasm, code_hash) = compile_module::<Test>(&CODE_CRYPTO_HASHES).unwrap();
|
||||
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Instantiate the CRYPTO_HASHES contract.
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100_000,
|
||||
100_000,
|
||||
code_hash.into(),
|
||||
vec![],
|
||||
));
|
||||
// Perform the call.
|
||||
let input = b"_DEAD_BEEF";
|
||||
use sp_io::hashing::*;
|
||||
// Wraps a hash function into a more dynamic form usable for testing.
|
||||
macro_rules! dyn_hash_fn {
|
||||
($name:ident) => {
|
||||
Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice())
|
||||
};
|
||||
}
|
||||
// All hash functions and their associated output byte lengths.
|
||||
let test_cases: &[(Box<dyn Fn(&[u8]) -> Box<[u8]>>, usize)] = &[
|
||||
(dyn_hash_fn!(sha2_256), 32),
|
||||
(dyn_hash_fn!(keccak_256), 32),
|
||||
(dyn_hash_fn!(blake2_256), 32),
|
||||
(dyn_hash_fn!(blake2_128), 16),
|
||||
(dyn_hash_fn!(twox_256), 32),
|
||||
(dyn_hash_fn!(twox_128), 16),
|
||||
(dyn_hash_fn!(twox_64), 8),
|
||||
];
|
||||
// Test the given hash functions for the input: "_DEAD_BEEF"
|
||||
for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() {
|
||||
// We offset data in the contract tables by 1.
|
||||
let mut params = vec![(n + 1) as u8];
|
||||
params.extend_from_slice(input);
|
||||
let result = <Module<Test>>::bare_call(
|
||||
ALICE,
|
||||
BOB,
|
||||
0,
|
||||
100_000,
|
||||
params,
|
||||
).unwrap();
|
||||
assert_eq!(result.status, 0);
|
||||
let expected = hash_fn(input.as_ref());
|
||||
assert_eq!(&result.data[..*expected_size], &*expected);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user