mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +00:00
Implemented seal_ecdsa_recovery function in the contract pallet (#9686)
* Implemented `seal_ecdsa_recovery` function in the contract pallet. Added benchmark and unit test. * Run `cargo fmt` * Skip fmt for slices * Changes according comments in pull request. * Fix build without `unstable-interface` feature * Applied suggestion from the review * Apply suggestions from code review Co-authored-by: Alexander Theißen <alex.theissen@me.com> * Apply suggestions from code review Co-authored-by: Alexander Theißen <alex.theissen@me.com> * Changed RecoveryFailed to EcdsaRecoverFailed * Manually updated weights.rs * Apply suggestions from code review Co-authored-by: Michael Müller <mich@elmueller.net> Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: Michael Müller <mich@elmueller.net>
This commit is contained in:
committed by
GitHub
parent
110ba540ec
commit
a36e881783
Generated
+32
-13
@@ -2518,6 +2518,17 @@ dependencies = [
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac-drbg"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b"
|
||||
dependencies = [
|
||||
"digest 0.8.1",
|
||||
"generic-array 0.12.4",
|
||||
"hmac 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac-drbg"
|
||||
version = "0.3.0"
|
||||
@@ -3659,6 +3670,22 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsecp256k1"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"crunchy",
|
||||
"digest 0.8.1",
|
||||
"hmac-drbg 0.2.0",
|
||||
"rand 0.7.3",
|
||||
"sha2 0.8.2",
|
||||
"subtle 2.4.0",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsecp256k1"
|
||||
version = "0.5.0"
|
||||
@@ -3668,7 +3695,7 @@ dependencies = [
|
||||
"arrayref",
|
||||
"base64 0.12.3",
|
||||
"digest 0.9.0",
|
||||
"hmac-drbg",
|
||||
"hmac-drbg 0.3.0",
|
||||
"libsecp256k1-core",
|
||||
"libsecp256k1-gen-ecmult",
|
||||
"libsecp256k1-gen-genmult",
|
||||
@@ -3687,7 +3714,7 @@ dependencies = [
|
||||
"arrayref",
|
||||
"base64 0.12.3",
|
||||
"digest 0.9.0",
|
||||
"hmac-drbg",
|
||||
"hmac-drbg 0.3.0",
|
||||
"libsecp256k1-core",
|
||||
"libsecp256k1-gen-ecmult",
|
||||
"libsecp256k1-gen-genmult",
|
||||
@@ -5022,6 +5049,7 @@ dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"hex-literal",
|
||||
"libsecp256k1 0.3.5",
|
||||
"log 0.4.14",
|
||||
"pallet-balances",
|
||||
"pallet-contracts-primitives",
|
||||
@@ -5032,8 +5060,8 @@ dependencies = [
|
||||
"parity-scale-codec",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pwasm-utils",
|
||||
"rand 0.8.4",
|
||||
"rand_pcg 0.3.0",
|
||||
"rand 0.7.3",
|
||||
"rand_pcg 0.2.1",
|
||||
"serde",
|
||||
"smallvec 1.6.1",
|
||||
"sp-core",
|
||||
@@ -6816,15 +6844,6 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de198537002b913568a3847e53535ace266f93526caf5c360ec41d72c5787f0"
|
||||
dependencies = [
|
||||
"rand_core 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -468,3 +468,20 @@ algorithms have different inherent complexity so users must expect the above
|
||||
mentioned crypto hashes to have varying gas costs.
|
||||
The complexity of each cryptographic hash function highly depends on the underlying
|
||||
implementation.
|
||||
|
||||
### seal_ecdsa_recover
|
||||
|
||||
This function receives the following arguments:
|
||||
|
||||
- `signature` is 65 bytes buffer,
|
||||
- `message_hash` is 32 bytes buffer,
|
||||
- `output` is 33 bytes buffer to return compressed public key,
|
||||
|
||||
It consists of the following steps:
|
||||
|
||||
1. Loading `signature` buffer from the sandbox memory (see sandboxing memory get).
|
||||
2. Loading `message_hash` buffer from the sandbox memory.
|
||||
3. Invoking the executive function `secp256k1_ecdsa_recover_compressed`.
|
||||
4. Copy the bytes of compressed public key into the contract side output buffer.
|
||||
|
||||
**complexity**: Complexity is partially constant(it doesn't depend on input) but still depends on points of ECDSA and calculation.
|
||||
@@ -27,8 +27,9 @@ smallvec = { version = "1", default-features = false, features = [
|
||||
wasmi-validation = { version = "0.4", default-features = false }
|
||||
|
||||
# Only used in benchmarking to generate random contract code
|
||||
rand = { version = "0.8", optional = true, default-features = false }
|
||||
rand_pcg = { version = "0.3", optional = true }
|
||||
libsecp256k1 = { version = "0.3.5", optional = true, default-features = false, features = ["hmac"] }
|
||||
rand = { version = "0.7.3", optional = true, default-features = false }
|
||||
rand_pcg = { version = "0.2", optional = true }
|
||||
|
||||
# Substrate Dependencies
|
||||
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true }
|
||||
@@ -73,9 +74,11 @@ std = [
|
||||
"pallet-contracts-proc-macro/full",
|
||||
"log/std",
|
||||
"rand/std",
|
||||
"libsecp256k1/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking",
|
||||
"libsecp256k1",
|
||||
"rand",
|
||||
"rand_pcg",
|
||||
"unstable-interface",
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
;; This contract:
|
||||
;; 1) Reads signature and message hash from the input
|
||||
;; 2) Calls ecdsa_recover
|
||||
;; 3) Validates that result is Success
|
||||
;; 4) Returns recovered compressed public key
|
||||
(module
|
||||
(import "__unstable__" "seal_ecdsa_recover" (func $seal_ecdsa_recover (param i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
;; [4, 8) len of signature + message hash - 65 bytes + 32 byte = 97 bytes
|
||||
(data (i32.const 4) "\61")
|
||||
|
||||
;; Memory layout during `call`
|
||||
;; [10, 75) signature
|
||||
;; [75, 107) message hash
|
||||
(func (export "call")
|
||||
(local $signature_ptr i32)
|
||||
(local $message_hash_ptr i32)
|
||||
(local $result i32)
|
||||
(local.set $signature_ptr (i32.const 10))
|
||||
(local.set $message_hash_ptr (i32.const 75))
|
||||
;; Read signature and message hash - 97 bytes
|
||||
(call $seal_input (local.get $signature_ptr) (i32.const 4))
|
||||
(local.set
|
||||
$result
|
||||
(call $seal_ecdsa_recover
|
||||
(local.get $signature_ptr)
|
||||
(local.get $message_hash_ptr)
|
||||
(local.get $signature_ptr) ;; Store output into message signature ptr, because we don't need it anymore
|
||||
)
|
||||
)
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(local.get $result) ;; The result of recovery execution
|
||||
(i32.const 0x0) ;; 0x0 - Success result
|
||||
)
|
||||
)
|
||||
|
||||
;; exit with success and return recovered public key
|
||||
(call $seal_return (i32.const 0) (local.get $signature_ptr) (i32.const 33))
|
||||
)
|
||||
)
|
||||
@@ -492,11 +492,11 @@ pub mod body {
|
||||
vec![Instruction::I32Const(current as i32)]
|
||||
},
|
||||
DynInstr::RandomUnaligned(low, high) => {
|
||||
let unaligned = rng.gen_range(*low..*high) | 1;
|
||||
let unaligned = rng.gen_range(*low, *high) | 1;
|
||||
vec![Instruction::I32Const(unaligned as i32)]
|
||||
},
|
||||
DynInstr::RandomI32(low, high) => {
|
||||
vec![Instruction::I32Const(rng.gen_range(*low..*high))]
|
||||
vec![Instruction::I32Const(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomI32Repeated(num) => (&mut rng)
|
||||
.sample_iter(Standard)
|
||||
@@ -509,19 +509,19 @@ pub mod body {
|
||||
.map(|val| Instruction::I64Const(val))
|
||||
.collect(),
|
||||
DynInstr::RandomGetLocal(low, high) => {
|
||||
vec![Instruction::GetLocal(rng.gen_range(*low..*high))]
|
||||
vec![Instruction::GetLocal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomSetLocal(low, high) => {
|
||||
vec![Instruction::SetLocal(rng.gen_range(*low..*high))]
|
||||
vec![Instruction::SetLocal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomTeeLocal(low, high) => {
|
||||
vec![Instruction::TeeLocal(rng.gen_range(*low..*high))]
|
||||
vec![Instruction::TeeLocal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomGetGlobal(low, high) => {
|
||||
vec![Instruction::GetGlobal(rng.gen_range(*low..*high))]
|
||||
vec![Instruction::GetGlobal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
DynInstr::RandomSetGlobal(low, high) => {
|
||||
vec![Instruction::SetGlobal(rng.gen_range(*low..*high))]
|
||||
vec![Instruction::SetGlobal(rng.gen_range(*low, *high))]
|
||||
},
|
||||
})
|
||||
.chain(sp_std::iter::once(Instruction::End))
|
||||
|
||||
@@ -1415,6 +1415,60 @@ benchmarks! {
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||
|
||||
// Only calling the function itself with valid arguments.
|
||||
// It generates different private keys and signatures for the message "Hello world".
|
||||
seal_ecdsa_recover {
|
||||
let r in 0 .. API_BENCHMARK_BATCHES;
|
||||
use rand::SeedableRng;
|
||||
let mut rng = rand_pcg::Pcg32::seed_from_u64(123456);
|
||||
|
||||
let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes());
|
||||
let signatures = (0..r * API_BENCHMARK_BATCH_SIZE)
|
||||
.map(|i| {
|
||||
use secp256k1::{SecretKey, Message, sign};
|
||||
|
||||
let private_key = SecretKey::random(&mut rng);
|
||||
let (signature, recovery_id) = sign(&Message::parse(&message_hash), &private_key);
|
||||
let mut full_signature = [0; 65];
|
||||
full_signature[..64].copy_from_slice(&signature.serialize());
|
||||
full_signature[64] = recovery_id.serialize();
|
||||
full_signature
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let signatures = signatures.iter().flatten().cloned().collect::<Vec<_>>();
|
||||
let signatures_bytes_len = signatures.len() as i32;
|
||||
|
||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
imported_functions: vec![ImportedFunction {
|
||||
module: "__unstable__",
|
||||
name: "seal_ecdsa_recover",
|
||||
params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
|
||||
return_type: Some(ValueType::I32),
|
||||
}],
|
||||
data_segments: vec![
|
||||
DataSegment {
|
||||
offset: 0,
|
||||
value: message_hash[..].to_vec(),
|
||||
},
|
||||
DataSegment {
|
||||
offset: 32,
|
||||
value: signatures,
|
||||
},
|
||||
],
|
||||
call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
|
||||
Counter(32, 65), // signature_ptr
|
||||
Regular(Instruction::I32Const(0)), // message_hash_ptr
|
||||
Regular(Instruction::I32Const(signatures_bytes_len + 32)), // output_len_ptr
|
||||
Regular(Instruction::Call(0)),
|
||||
Regular(Instruction::Drop),
|
||||
])),
|
||||
.. Default::default()
|
||||
});
|
||||
let instance = Contract::<T>::new(code, vec![])?;
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||
|
||||
// We make the assumption that pushing a constant and dropping a value takes roughly
|
||||
// the same amount of time. We follow that `t.load` and `drop` both have the weight
|
||||
// of this benchmark / 2. We need to make this assumption because there is no way
|
||||
|
||||
@@ -30,6 +30,7 @@ use frame_system::RawOrigin;
|
||||
use pallet_contracts_primitives::ExecReturnValue;
|
||||
use smallvec::{Array, SmallVec};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_io::crypto::secp256k1_ecdsa_recover_compressed;
|
||||
use sp_runtime::traits::{Convert, Saturating};
|
||||
use sp_std::{marker::PhantomData, mem, prelude::*};
|
||||
|
||||
@@ -205,6 +206,9 @@ pub trait Ext: sealing::Sealed {
|
||||
|
||||
/// Call some dispatchable and return the result.
|
||||
fn call_runtime(&self, call: <Self::T as Config>::Call) -> DispatchResultWithPostInfo;
|
||||
|
||||
/// 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], ()>;
|
||||
}
|
||||
|
||||
/// Describes the different functions that can be exported by an [`Executable`].
|
||||
@@ -1033,6 +1037,10 @@ where
|
||||
origin.add_filter(T::CallFilter::contains);
|
||||
call.dispatch(origin)
|
||||
}
|
||||
|
||||
fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> {
|
||||
secp256k1_ecdsa_recover_compressed(&signature, &message_hash).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
fn deposit_event<T: Config>(topics: Vec<T::Hash>, event: Event<T>) {
|
||||
|
||||
@@ -378,6 +378,9 @@ pub struct HostFnWeights<T: Config> {
|
||||
/// Weight per byte hashed by `seal_hash_blake2_128`.
|
||||
pub hash_blake2_128_per_byte: Weight,
|
||||
|
||||
/// Weight of calling `seal_ecdsa_recover`.
|
||||
pub ecdsa_recover: Weight,
|
||||
|
||||
/// The type parameter is used in the default implementation.
|
||||
#[codec(skip)]
|
||||
pub _phantom: PhantomData<T>,
|
||||
@@ -625,6 +628,7 @@ impl<T: Config> Default for HostFnWeights<T> {
|
||||
hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb),
|
||||
hash_blake2_128: cost_batched!(seal_hash_blake2_128),
|
||||
hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb),
|
||||
ecdsa_recover: cost_batched!(seal_ecdsa_recover),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1795,3 +1795,53 @@ fn gas_estimation_call_runtime() {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn ecdsa_recover() {
|
||||
let (wasm, code_hash) = compile_module::<Test>("ecdsa_recover").unwrap();
|
||||
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
// Instantiate the ecdsa_recover contract.
|
||||
assert_ok!(Contracts::instantiate_with_code(
|
||||
Origin::signed(ALICE),
|
||||
100_000,
|
||||
GAS_LIMIT,
|
||||
wasm,
|
||||
vec![],
|
||||
vec![],
|
||||
));
|
||||
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let signature: [u8; 65] = [
|
||||
161, 234, 203, 74, 147, 96, 51, 212, 5, 174, 231, 9, 142, 48, 137, 201,
|
||||
162, 118, 192, 67, 239, 16, 71, 216, 125, 86, 167, 139, 70, 7, 86, 241,
|
||||
33, 87, 154, 251, 81, 29, 160, 4, 176, 239, 88, 211, 244, 232, 232, 52,
|
||||
211, 234, 100, 115, 230, 47, 80, 44, 152, 166, 62, 50, 8, 13, 86, 175,
|
||||
28,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
let message_hash: [u8; 32] = [
|
||||
162, 28, 244, 179, 96, 76, 244, 178, 188, 83, 230, 248, 143, 106, 77, 117,
|
||||
239, 95, 244, 171, 65, 95, 62, 153, 174, 166, 182, 28, 130, 73, 196, 208
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [
|
||||
2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11,
|
||||
7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23,
|
||||
152,
|
||||
];
|
||||
let mut params = vec![];
|
||||
params.extend_from_slice(&signature);
|
||||
params.extend_from_slice(&message_hash);
|
||||
assert!(params.len() == 65 + 32);
|
||||
let result = <Pallet<Test>>::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, params, false)
|
||||
.result
|
||||
.unwrap();
|
||||
assert!(result.is_success());
|
||||
assert_eq!(result.data.as_ref(), &EXPECTED_COMPRESSED_PUBLIC_KEY);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@ mod tests {
|
||||
schedule: Schedule<Test>,
|
||||
gas_meter: GasMeter<Test>,
|
||||
debug_buffer: Vec<u8>,
|
||||
ecdsa_recover: RefCell<Vec<([u8; 65], [u8; 32])>>,
|
||||
}
|
||||
|
||||
/// The call is mocked and just returns this hardcoded value.
|
||||
@@ -315,6 +316,7 @@ mod tests {
|
||||
schedule: Default::default(),
|
||||
gas_meter: GasMeter::new(10_000_000_000),
|
||||
debug_buffer: Default::default(),
|
||||
ecdsa_recover: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,6 +420,15 @@ mod tests {
|
||||
self.runtime_calls.borrow_mut().push(call);
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
fn ecdsa_recover(
|
||||
&self,
|
||||
signature: &[u8; 65],
|
||||
message_hash: &[u8; 32],
|
||||
) -> Result<[u8; 33], ()> {
|
||||
self.ecdsa_recover.borrow_mut().push((signature.clone(), message_hash.clone()));
|
||||
Ok([3; 33])
|
||||
}
|
||||
}
|
||||
|
||||
fn execute<E: BorrowMut<MockExt>>(wat: &str, input_data: Vec<u8>, mut ext: E) -> ExecResult {
|
||||
@@ -850,6 +861,51 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
const CODE_ECDSA_RECOVER: &str = r#"
|
||||
(module
|
||||
;; seal_ecdsa_recover(
|
||||
;; signature_ptr: u32,
|
||||
;; message_hash_ptr: u32,
|
||||
;; output_ptr: u32
|
||||
;; ) -> u32
|
||||
(import "__unstable__" "seal_ecdsa_recover" (func $seal_ecdsa_recover (param i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
(drop
|
||||
(call $seal_ecdsa_recover
|
||||
(i32.const 36) ;; Pointer to signature.
|
||||
(i32.const 4) ;; Pointer to message hash.
|
||||
(i32.const 36) ;; Pointer for output - public key.
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Hash of message.
|
||||
(data (i32.const 4)
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
)
|
||||
;; Signature
|
||||
(data (i32.const 36)
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\01"
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn contract_ecdsa_recover() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
assert_ok!(execute(&CODE_ECDSA_RECOVER, vec![], &mut mock_ext));
|
||||
assert_eq!(mock_ext.ecdsa_recover.into_inner(), [([1; 65], [1; 32])]);
|
||||
}
|
||||
|
||||
const CODE_GET_STORAGE: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_get_storage" (func $seal_get_storage (param i32 i32 i32) (result i32)))
|
||||
|
||||
@@ -73,6 +73,9 @@ pub enum ReturnCode {
|
||||
/// The call dispatched by `seal_call_runtime` was executed but returned an error.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CallRuntimeReturnedError = 10,
|
||||
/// ECDSA pubkey recovery failed. Most probably wrong recovery id or signature.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
EcdsaRecoverFailed = 11,
|
||||
}
|
||||
|
||||
impl ConvertibleToWasm for ReturnCode {
|
||||
@@ -199,6 +202,9 @@ pub enum RuntimeCosts {
|
||||
HashBlake256(u32),
|
||||
/// Weight of calling `seal_hash_blake2_128` for the given input size.
|
||||
HashBlake128(u32),
|
||||
/// Weight of calling `seal_ecdsa_recover`.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
EcdsaRecovery,
|
||||
/// Weight charged by a chain extension through `seal_call_chain_extension`.
|
||||
ChainExtension(u64),
|
||||
/// Weight charged for copying data from the sandbox.
|
||||
@@ -265,6 +271,8 @@ impl RuntimeCosts {
|
||||
HashBlake128(len) => s
|
||||
.hash_blake2_128
|
||||
.saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())),
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
EcdsaRecovery => s.ecdsa_recover,
|
||||
ChainExtension(amount) => amount,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
|
||||
@@ -1712,4 +1720,44 @@ define_env!(Env, <E: Ext>,
|
||||
Err(_) => Ok(ReturnCode::CallRuntimeReturnedError),
|
||||
}
|
||||
},
|
||||
|
||||
// Recovers the ECDSA public key from the given message hash and signature.
|
||||
//
|
||||
// Writes the public key into the given output buffer.
|
||||
// Assumes the secp256k1 curve.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// - `signature_ptr`: the pointer into the linear memory where the signature
|
||||
// is placed. Should be decodable as a 65 bytes. Traps otherwise.
|
||||
// - `message_hash_ptr`: the pointer into the linear memory where the message
|
||||
// hash is placed. Should be decodable as a 32 bytes. Traps otherwise.
|
||||
// - `output_ptr`: the pointer into the linear memory where the output
|
||||
// data is placed. The buffer should be 33 bytes. Traps otherwise.
|
||||
// The function will write the result directly into this buffer.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// `ReturnCode::EcdsaRecoverFailed`
|
||||
[__unstable__] seal_ecdsa_recover(ctx, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?;
|
||||
|
||||
let mut signature: [u8; 65] = [0; 65];
|
||||
ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?;
|
||||
let mut message_hash: [u8; 32] = [0; 32];
|
||||
ctx.read_sandbox_memory_into_buf(message_hash_ptr, &mut message_hash)?;
|
||||
|
||||
let result = ctx.ext.ecdsa_recover(&signature, &message_hash);
|
||||
|
||||
match result {
|
||||
Ok(pub_key) => {
|
||||
// Write the recovered compressed ecdsa public key back into the sandboxed output
|
||||
// buffer.
|
||||
ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?;
|
||||
|
||||
Ok(ReturnCode::Success)
|
||||
},
|
||||
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ use crate::crypto::KeyTypeId;
|
||||
pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25");
|
||||
/// Key type for generic Sr 25519 key.
|
||||
pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25");
|
||||
/// Key type for generic Sr 25519 key.
|
||||
/// Key type for generic ECDSA key.
|
||||
pub const ECDSA: KeyTypeId = KeyTypeId(*b"ecds");
|
||||
|
||||
/// Macro for exporting functions from wasm in with the expected signature for using it with the
|
||||
|
||||
Reference in New Issue
Block a user