Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sr25519_verify() #1840

Merged
merged 36 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
57c27e3
sr25519 signature verification
kziemianek Apr 18, 2023
5d9cbee
offchain signature verification
kziemianek Apr 19, 2023
37fa479
fmt
kziemianek Apr 19, 2023
0a910e1
schnorrkel optional
kziemianek Apr 19, 2023
b2224d6
update dictionary
kziemianek Apr 19, 2023
d6a9f7c
fix
kziemianek Apr 19, 2023
9d00c92
update changelog
kziemianek Apr 19, 2023
da71ca2
add info about unstable function usage
kziemianek Apr 19, 2023
eb93905
missing docs
kziemianek Apr 20, 2023
9ca19c4
review suggestions
kziemianek Apr 20, 2023
9b074f1
added catch for invalid public key or signature in sr25519 verification
goastler Jul 5, 2023
b4a65d2
switched to simple signing context in sr25519_verify fn
goastler Jul 5, 2023
548fe27
Added docs for sr25519_verify()
goastler Jul 6, 2023
53c2442
Added docs to sr25519VerifyFailed error
goastler Jul 6, 2023
6ce47f4
Added docs link to substrate sr25519 signing context
goastler Jul 6, 2023
b9cfbf5
cargo fmt
goastler Jul 6, 2023
a33283e
Remove reference on message variable of sr25519_verify()
goastler Jul 6, 2023
35175bb
Clippy embed error in panic statement during metadata execution
goastler Jul 6, 2023
7fd25ba
sr25519 verification tests
goastler Jul 6, 2023
fda5ab1
Updated sr25519_verify PR number in CHANGELOG.md
goastler Jul 6, 2023
5ab85fc
Merge branch 'master' into goastler-add-sr25519_verify
goastler Jul 6, 2023
fd914df
Fix comment to adhere to spell check in sr25519 verification tests
goastler Jul 6, 2023
ae2c65c
Merge branch 'goastler-add-sr25519_verify' of github.com:prosopo/ink_…
goastler Jul 6, 2023
8866fdb
add parity as author of sr25519 module
goastler Jul 18, 2023
79bea8a
ignore semicolon correction in item module
goastler Jul 28, 2023
d0c91cf
Merge branch 'goastler-add-sr25519_verify' of github.com:prosopo/ink_…
goastler Jul 28, 2023
2aef6a8
add warning to docs for sr25519_verify() depending on unstable interf…
goastler Jul 28, 2023
77c6cea
Merge remote-tracking branch 'parity/master' into goastler-add-sr2551…
goastler Jul 28, 2023
97e2d23
remove space typo from sr25519 docs
goastler Jul 28, 2023
cf04d6d
remove #1741 from changelog
goastler Jul 31, 2023
c500a03
added documentation to the dummy method in sr25519_verification example
goastler Jul 31, 2023
4c3cbc8
typo in sr25519 example contract dummy method
goastler Aug 1, 2023
bba49ea
spell check fix in method doc for sr25519
goastler Aug 1, 2023
1810468
spell check fix in method doc for sr25519
goastler Aug 1, 2023
eeee6d4
move changelog entry for #1741
goastler Aug 2, 2023
77044d2
changelog typo
goastler Aug 2, 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
1 change: 1 addition & 0 deletions .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ reentrancy
refcount
scalability
scalable
sr25519
stdin
stdout
subber
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Schema generation - [#1765](https://github.com/paritytech/ink/pull/1765)
- Add `set_block_number` to off-chain test api `Engine` - [#1806](https://github.com/paritytech/ink/pull/1806)
- Added `sr25519_verify` function to `ink_env` [#1840](https://github.com/paritytech/ink/pull/1840)

### Changed
- E2E: improve call API, remove `build_message` + callback - [#1782](https://github.com/paritytech/ink/pull/1782)
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/macro/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl ContractManifests {
let cmd = cargo_metadata::MetadataCommand::new();
let metadata = cmd
.exec()
.unwrap_or_else(|err| panic!("Error invoking `cargo metadata`: {}", err));
.unwrap_or_else(|err| panic!("Error invoking `cargo metadata`: {err}"));

fn maybe_contract_package(package: &cargo_metadata::Package) -> Option<String> {
package
Expand Down
2 changes: 2 additions & 0 deletions crates/engine/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ define_error_codes! {
LoggingDisabled = 9,
/// ECDSA public key recovery failed. Most probably wrong recovery id or signature.
EcdsaRecoveryFailed = 11,
/// sr25519 signature verification failed. This may be because of an invalid public key, invalid message or invalid signature.
Sr25519VerifyFailed = 12,
}

/// The raw return code returned by the host side.
Expand Down
4 changes: 4 additions & 0 deletions crates/env/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ blake2 = { workspace = true, optional = true }
# ECDSA for the off-chain environment.
secp256k1 = { workspace = true, features = ["recovery", "global-context"], optional = true }

# schnorrkel for the off-chain environment.
schnorrkel = { version = "0.10.2", optional = true }

# Only used in the off-chain environment.
#
# Sadly couldn't be marked as dev-dependency.
Expand All @@ -67,6 +70,7 @@ std = [
"scale-encode",
"scale-info/std",
"secp256k1",
"schnorrkel",
"num-traits/std",
# Enables hashing crates for off-chain environment.
"sha2",
Expand Down
37 changes: 37 additions & 0 deletions crates/env/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,43 @@ pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result<
})
}

/// Verifies a sr25519 signature.
///
/// # Example
///
/// ```
/// 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,
/// ];
/// let message: &[u8; 11] = b"hello world";
/// let pub_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 result = ink::env::sr25519_verify(&signature, message.as_slice(), &pub_key);
/// assert!(result.is_ok())
/// ```
///
/// # Errors
///
/// - If sr25519 signature cannot be verified.
///
/// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces),
/// which is unsafe and normally is not available on production chains.
pub fn sr25519_verify(
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result<()> {
<EnvInstance as OnInstance>::on_instance(|instance| {
instance.sr25519_verify(signature, message, pub_key)
})
}

/// Checks whether the specified account is a contract.
///
/// # Errors
Expand Down
15 changes: 15 additions & 0 deletions crates/env/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,21 @@ pub trait EnvBackend {
output: &mut [u8; 20],
) -> Result<()>;

/// Verifies a sr25519 signature.
///
/// # Errors
///
/// - If the signature verification failed.
///
/// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces),
/// which is unsafe and normally is not available on production chains.
fn sr25519_verify(
&mut self,
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result<()>;

/// Low-level interface to call a chain extension method.
///
/// Returns the output of the chain extension of the specified type.
Expand Down
27 changes: 27 additions & 0 deletions crates/env/src/engine/off_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ use ink_engine::{
ext::Engine,
};
use ink_storage_traits::Storable;
use schnorrkel::{
PublicKey,
Signature,
};

/// The capacity of the static buffer.
/// This is the same size as the ink! on-chain environment. We chose to use the same size
Expand Down Expand Up @@ -115,6 +119,7 @@ impl From<ext::Error> for crate::Error {
ext::Error::NotCallable => Self::NotCallable,
ext::Error::LoggingDisabled => Self::LoggingDisabled,
ext::Error::EcdsaRecoveryFailed => Self::EcdsaRecoveryFailed,
ext::Error::Sr25519VerifyFailed => Self::Sr25519VerifyFailed,
}
}
}
Expand Down Expand Up @@ -329,6 +334,28 @@ impl EnvBackend for EnvInstance {
Ok(())
}

fn sr25519_verify(
&mut self,
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result<()> {
// the context associated with the signing (specific to the sr25519 algorithm)
// defaults to "substrate" in substrate, but could be different elsewhere
// https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60
let context = b"substrate";
// attempt to parse a signature from bytes
let signature: Signature =
Signature::from_bytes(signature).map_err(|_| Error::Sr25519VerifyFailed)?;
// attempt to parse a public key from bytes
let public_key: PublicKey =
PublicKey::from_bytes(pub_key).map_err(|_| Error::Sr25519VerifyFailed)?;
// verify the signature
public_key
.verify_simple(context, message, &signature)
.map_err(|_| Error::Sr25519VerifyFailed)
}

fn call_chain_extension<I, T, E, ErrorCode, F, D>(
&mut self,
func_id: u32,
Expand Down
17 changes: 17 additions & 0 deletions crates/env/src/engine/on_chain/ext/riscv32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,23 @@ pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result
ret_code.into()
}

/// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces),
/// which is unsafe and normally is not available on production chains.
pub fn sr25519_verify(
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result {
let ret_code = (
Ptr32::from_slice(signature),
Ptr32::from_slice(pub_key),
message.len() as u32,
Ptr32::from_slice(message),
)
.using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data)));
ret_code.into()
}

pub fn is_contract(account_id: &[u8]) -> bool {
let ret_val = sys::call(FUNC_ID, Ptr32::from_slice(account_id));
ret_val.into_bool()
Expand Down
25 changes: 25 additions & 0 deletions crates/env/src/engine/on_chain/ext/wasm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ mod sys {
output_ptr: Ptr32Mut<[u8]>,
) -> ReturnCode;

/// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces),
/// which is unsafe and normally is not available on production chains.
pub fn sr25519_verify(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@athei @cmichi What do you think about adding a new unstable feature to the ink_env crate?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep . I think we can feature-gate the function with the sr25519-verify verify until it gets stabilized

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Features are a last resort and should only be used when absolutely necessary (i.e not here). We don't need a feature here. A clear doc that this is an unstable functionality is enough. If someone ignores that hint nothing bad will happen. The contract will simply not deploy on a production chain.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goastler Could you propagate this warning to the public method then, please(available for users)? Because this comment is useful only for people who dive deep into the ink! codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

signature_ptr: Ptr32<[u8]>,
public_key_ptr: Ptr32<[u8]>,
message_len: u32,
message_ptr: Ptr32<[u8]>,
) -> ReturnCode;

pub fn take_storage(
key_ptr: Ptr32<[u8]>,
key_len: u32,
Expand Down Expand Up @@ -595,6 +604,22 @@ pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result
ret_code.into()
}

pub fn sr25519_verify(
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result {
let ret_code = unsafe {
sys::sr25519_verify(
Ptr32::from_slice(signature),
Ptr32::from_slice(pub_key),
message.len() as u32,
Ptr32::from_slice(message),
)
};
ret_code.into()
}

pub fn is_contract(account_id: &[u8]) -> bool {
let ret_val = unsafe { sys::is_contract(Ptr32::from_slice(account_id)) };
ret_val.into_bool()
Expand Down
9 changes: 9 additions & 0 deletions crates/env/src/engine/on_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,15 @@ impl EnvBackend for EnvInstance {
ext::ecdsa_to_eth_address(pubkey, output).map_err(Into::into)
}

fn sr25519_verify(
&mut self,
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result<()> {
ext::sr25519_verify(signature, message, pub_key).map_err(Into::into)
}

fn call_chain_extension<I, T, E, ErrorCode, F, D>(
&mut self,
func_id: u32,
Expand Down
2 changes: 2 additions & 0 deletions crates/env/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Error {
CallRuntimeFailed,
/// ECDSA pubkey recovery failed. Most probably wrong recovery id or signature.
EcdsaRecoveryFailed,
/// sr25519 signature verification failed.
Sr25519VerifyFailed,
}

/// A result of environmental operations.
Expand Down
60 changes: 60 additions & 0 deletions crates/ink/src/env_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,66 @@ where
.map_err(|_| Error::EcdsaRecoveryFailed)
}

/// Verifies a SR25519 signature against a message and a public key.
///
/// # Example
///
/// ```
/// # #[ink::contract]
/// # pub mod my_contract {
/// # #[ink(storage)]
/// # pub struct MyContract { }
/// #
/// # impl MyContract {
/// # #[ink(constructor)]
/// # pub fn new() -> Self {
/// # Self {}
/// # }
/// #
/// #[ink(message)]
/// pub fn sr25519_verify(&self) {
/// let mut signature: [u8; 64] = [
/// 10, 125, 162, 182, 49, 112, 76, 220, 254, 147, 199, 64, 228, 18, 23, 185,
/// 172, 102, 122, 12, 135, 85, 216, 218, 26, 130, 50, 219, 82, 127, 72, 124,
/// 135, 231, 128, 210, 237, 193, 137, 106, 235, 107, 27, 239, 11, 199, 195, 141,
/// 157, 242, 19, 91, 99, 62, 171, 139, 251, 23, 119, 232, 47, 173, 58, 143,
/// ];
/// let mut message: [u8; 49] = [
/// 60, 66, 121, 116, 101, 115, 62, 48, 120, 52, 54, 102, 98, 55, 52, 48, 56,
/// 100, 52, 102, 50, 56, 53, 50, 50, 56, 102, 52, 97, 102, 53, 49, 54, 101, 97,
/// 50, 53, 56, 53, 49, 98, 60, 47, 66, 121, 116, 101, 115, 62,
/// ];
/// let mut 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 result = ink::env::sr25519_verify(&signature, &message, &public_key);
/// assert_eq!(result, Ok(()));
/// }
/// #
/// # }
/// # }
/// ```
///
/// # Note
///
/// The context for sr25519 signing is hard-coded to "substrate" to match sr25519
/// signing in substrate.
///
/// For more details visit: [`ink_env::sr25519_verify`]
///
/// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces),
/// which is unsafe and normally is not available on production chains.
pub fn sr25519_verify(
self,
signature: &[u8; 64],
message: &[u8],
pub_key: &[u8; 32],
) -> Result<()> {
ink_env::sr25519_verify(signature, message, pub_key)
.map_err(|_| Error::Sr25519VerifyFailed)
}

/// Checks whether a specified account belongs to a contract.
///
/// # Example
Expand Down
9 changes: 9 additions & 0 deletions integration-tests/sr25519-verification/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
28 changes: 28 additions & 0 deletions integration-tests/sr25519-verification/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "sr25519_verification"
version = "4.1.0"
authors = ["Parity Technologies <[email protected]>", "George Oastler <[email protected]>"]
edition = "2021"
publish = false

[dependencies]
ink = { path = "../../crates/ink", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.5", default-features = false, features = ["derive"], optional = true }

[dev-dependencies]
ink_e2e = { path = "../../crates/e2e" }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
e2e-tests = []
Loading