I have a Pallet that should verify a signature for some input data and a specific public key.
It looks like this:
fn verify(_: OriginFor<T>, data: Vec<u8>, sig: ???, from: ???)
Is this possible? What kind of signature schemes can I use?
Substrate and Polkadot Stack Exchange is a question and answer site for developers building blockchains with the Substrate SDK. It only takes a minute to sign up.
Sign up to join this communitySubstrate provides a Verify and an IdentifyAccount trait.
They work with any signature scheme since they are just traits and can be implemented for different crypto schemata.
This is very helpful when implementing off-chain protocols like Channels, Plasmas, Rollups etc.
The Config
trait of your pallet needs to be extended to support it:
/// A Signature can be verified with a specific `PublicKey`.
/// The additional traits are boilerplate.
type Signature: Verify<Signer = Self::PublicKey> + Encode + Decode + Parameter;
/// A PublicKey can be converted into an `AccountId`. This is required by the
/// `Signature` type.
/// The additional traits are boilerplate.
type PublicKey: IdentifyAccount<AccountId = Self::PublicKey> + Encode + Decode + Parameter;
This makes it possible to verify signatures within the pallet.
Using it in an Extrinsic looks like this:
fn my_verify(_: OriginFor<T>, data: Vec<u8>, sig: T::Signature, from: T::PublicKey)
{
let ok = sig.verify(&data, &from);
// `ok` is a bool. Use in an `if` or `ensure!`.
}
On the runtime side you can then inject the concrete crypto implementation that you want:
type Signature = sp_core::ecdsa::Signature;
type PublicKey = sp_core::ecdsa::Public;
Available are ecdsa
, sr25519
and ed25519
all from sp_core.
The Merline signing context for sr25519
is b"substrate"
as defined here.
Developer -> Sign and Verify -> Verify Signature
.
Feb 18, 2022 at 11:01
SIG = sign(PrivateKey, DATA)
and verifying ok = verify(PublicKey, DATA, SIG)
.
Feb 18, 2022 at 11:32
You can also see fully integrated examples of signature verification in a Pallet with:
claims
pallet, which verifies Ethereum Signaturesimpl<T: Config> Pallet<T> {
// Constructs the message that Ethereum RPC's `personal_sign` and `eth_sign` would sign.
fn ethereum_signable_message(what: &[u8], extra: &[u8]) -> Vec<u8> {
let prefix = T::Prefix::get();
let mut l = prefix.len() + what.len() + extra.len();
let mut rev = Vec::new();
while l > 0 {
rev.push(b'0' + (l % 10) as u8);
l /= 10;
}
let mut v = b"\x19Ethereum Signed Message:\n".to_vec();
v.extend(rev.into_iter().rev());
v.extend_from_slice(&prefix[..]);
v.extend_from_slice(what);
v.extend_from_slice(extra);
v
}
// Attempts to recover the Ethereum address from a message signature signed by using
// the Ethereum RPC's `personal_sign` and `eth_sign`.
fn eth_recover(s: &EcdsaSignature, what: &[u8], extra: &[u8]) -> Option<EthereumAddress> {
let msg = keccak_256(&Self::ethereum_signable_message(what, extra));
let mut res = EthereumAddress::default();
res.0
.copy_from_slice(&keccak_256(&secp256k1_ecdsa_recover(&s.0, &msg).ok()?[..])[12..]);
Some(res)
}
...
crowdloan
pallet which verifies multiple different kinds of signatures using the Multisignature
type:if let Some(ref verifier) = fund.verifier {
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
let payload = (index, &who, old_balance, value);
let valid = payload.using_encoded(|encoded| {
signature.verify(encoded, &verifier.clone().into_account())
});
ensure!(valid, Error::<T>::InvalidSignature);
}
claims
pallet: polkadot's runtime/common/src/claims.rs#L532
Feb 14 at 21:54