multiversx-crypto-verification

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MultiversX Crypto Verification —
self.crypto()
API Reference

MultiversX 加密验证 —
self.crypto()
API 参考文档

Complete reference for hashing and signature verification in MultiversX smart contracts (SDK v0.64+).
MultiversX智能合约中哈希与签名验证的完整参考(SDK v0.64+)。

Hashing Functions

哈希函数

All hashing functions take
&ManagedBuffer
(or anything that borrows as
ManagedBuffer
) and return a fixed-size
ManagedByteArray
.
MethodReturn TypeOutput SizeUse Case
.sha256(data)
ManagedByteArray<A, 32>
32 bytesGeneral purpose hashing, Merkle trees
.keccak256(data)
ManagedByteArray<A, 32>
32 bytesEthereum compatibility, EIP-712
.ripemd160(data)
ManagedByteArray<A, 20>
20 bytesBitcoin address derivation (rare)
rust
// Hash a message
let message = ManagedBuffer::from("data to hash");
let hash: ManagedByteArray<Self::Api, 32> = self.crypto().sha256(&message);

// Hash for Ethereum compatibility
let eth_hash = self.crypto().keccak256(&abi_encoded_data);
所有哈希函数接收
&ManagedBuffer
(或任何可借用为
ManagedBuffer
的类型)并返回固定大小的
ManagedByteArray
方法返回类型输出大小使用场景
.sha256(data)
ManagedByteArray<A, 32>
32字节通用哈希、默克尔树
.keccak256(data)
ManagedByteArray<A, 32>
32字节以太坊兼容性、EIP-712
.ripemd160(data)
ManagedByteArray<A, 20>
20字节比特币地址推导(少见)
rust
// 哈希消息
let message = ManagedBuffer::from("data to hash");
let hash: ManagedByteArray<Self::Api, 32> = self.crypto().sha256(&message);

// 以太坊兼容哈希
let eth_hash = self.crypto().keccak256(&abi_encoded_data);

Signature Verification

签名验证

Critical Distinction: Panic vs Bool

关键区别:Panic 与返回布尔值

Panic-based — Transaction fails immediately on invalid signature. No error handling possible. Use when invalid signature = unauthorized action.
Bool-based — Returns
true
/
false
. Contract continues execution. Use when you need graceful error handling or multiple verification attempts.
MethodReturnsOn Invalid Signature
verify_ed25519(key, message, signature)
()
Panics — tx fails with "invalid signature"
verify_bls(key, message, signature)
()
Panics
verify_secp256r1(key, message, signature)
()
Panics
verify_bls_signature_share(key, message, signature)
()
Panics
verify_bls_aggregated_signature(keys, message, signature)
()
Panics
verify_secp256k1(key, message, signature)
bool
Returns false
verify_custom_secp256k1(key, message, signature, hash_type)
bool
Returns false
Panic模式 — 签名无效时交易立即失败,无法进行错误处理。适用于签名无效即代表未授权操作的场景。
布尔返回模式 — 返回
true
/
false
,合约继续执行。适用于需要优雅错误处理或多次验证尝试的场景。
方法返回值签名无效时的行为
verify_ed25519(key, message, signature)
()
触发Panic — 交易因"无效签名"失败
verify_bls(key, message, signature)
()
触发Panic
verify_secp256r1(key, message, signature)
()
触发Panic
verify_bls_signature_share(key, message, signature)
()
触发Panic
verify_bls_aggregated_signature(keys, message, signature)
()
触发Panic
verify_secp256k1(key, message, signature)
bool
返回false
verify_custom_secp256k1(key, message, signature, hash_type)
bool
返回false

Method Signatures

方法签名

rust
// Panic-based (no return value)
fn verify_ed25519(
    &self,
    key: &ManagedBuffer<A>,       // 32-byte public key
    message: &ManagedBuffer<A>,   // arbitrary message
    signature: &ManagedBuffer<A>, // 64-byte signature
)

fn verify_bls(
    &self,
    key: &ManagedBuffer<A>,       // 96-byte BLS public key
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>, // 48-byte BLS signature
)

fn verify_secp256r1(
    &self,
    key: &ManagedBuffer<A>,       // 33 or 65-byte public key
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,
)

fn verify_bls_signature_share(
    &self,
    key: &ManagedBuffer<A>,
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,
)

fn verify_bls_aggregated_signature(
    &self,
    keys: &ManagedVec<A, ManagedBuffer<A>>,  // list of BLS public keys
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,             // aggregated signature
)

// Bool-based
fn verify_secp256k1(
    &self,
    key: &ManagedBuffer<A>,
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,  // DER-encoded or raw (min 2 bytes)
) -> bool

fn verify_custom_secp256k1(
    &self,
    key: &ManagedBuffer<A>,
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,
    hash_type: MessageHashType,    // how the message was hashed
) -> bool
rust
// Panic-based (no return value)
fn verify_ed25519(
    &self,
    key: &ManagedBuffer<A>,       // 32-byte public key
    message: &ManagedBuffer<A>,   // arbitrary message
    signature: &ManagedBuffer<A>, // 64-byte signature
)

fn verify_bls(
    &self,
    key: &ManagedBuffer<A>,       // 96-byte BLS public key
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>, // 48-byte BLS signature
)

fn verify_secp256r1(
    &self,
    key: &ManagedBuffer<A>,       // 33 or 65-byte public key
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,
)

fn verify_bls_signature_share(
    &self,
    key: &ManagedBuffer<A>,
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,
)

fn verify_bls_aggregated_signature(
    &self,
    keys: &ManagedVec<A, ManagedBuffer<A>>,  // list of BLS public keys
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,             // aggregated signature
)

// Bool-based
fn verify_secp256k1(
    &self,
    key: &ManagedBuffer<A>,
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,  // DER-encoded or raw (min 2 bytes)
) -> bool

fn verify_custom_secp256k1(
    &self,
    key: &ManagedBuffer<A>,
    message: &ManagedBuffer<A>,
    signature: &ManagedBuffer<A>,
    hash_type: MessageHashType,    // how the message was hashed
) -> bool

MessageHashType Enum

MessageHashType 枚举

Used with
verify_custom_secp256k1
to specify how the message was pre-hashed:
rust
pub enum MessageHashType {
    ECDSAPlainMsg,      // Message is not hashed (raw)
    ECDSASha256,        // Message was SHA-256 hashed
    ECDSADoubleSha256,  // Message was double SHA-256 hashed (Bitcoin)
    ECDSAKeccak256,     // Message was Keccak-256 hashed (Ethereum)
    ECDSARipemd160,     // Message was RIPEMD-160 hashed
    ECDSABlake2b,       // Message was Blake2b hashed
}
用于
verify_custom_secp256k1
,指定消息的预哈希方式:
rust
pub enum MessageHashType {
    ECDSAPlainMsg,      // Message is not hashed (raw)
    ECDSASha256,        // Message was SHA-256 hashed
    ECDSADoubleSha256,  // Message was double SHA-256 hashed (Bitcoin)
    ECDSAKeccak256,     // Message was Keccak-256 hashed (Ethereum)
    ECDSARipemd160,     // Message was RIPEMD-160 hashed
    ECDSABlake2b,       // Message was Blake2b hashed
}

DER Signature Encoding

DER签名编码

Convert raw (r, s) components to DER format for secp256k1:
rust
fn encode_secp256k1_der_signature(
    &self,
    r: &ManagedBuffer<A>,  // 32-byte r component
    s: &ManagedBuffer<A>,  // 32-byte s component
) -> ManagedBuffer<A>      // DER-encoded signature
将原始(r, s)组件转换为secp256k1的DER格式:
rust
fn encode_secp256k1_der_signature(
    &self,
    r: &ManagedBuffer<A>,  // 32-byte r component
    s: &ManagedBuffer<A>,  // 32-byte s component
) -> ManagedBuffer<A>      // DER-encoded signature

Algorithm Selection Guide

算法选择指南

AlgorithmWhen to Use
Ed25519MultiversX native signatures. Verify user/SC signatures from the chain. Default choice.
secp256k1Ethereum/Bitcoin compatibility. Bridge contracts, cross-chain verification.
secp256r1NIST P-256 / WebAuthn / Apple Secure Enclave. Passkey-based auth.
BLSValidator signatures, multi-sig aggregation, threshold schemes.
BLS aggregatedVerify a single aggregated signature from multiple validators.
算法使用场景
Ed25519MultiversX原生签名。验证链上用户/智能合约签名。默认选择。
secp256k1以太坊/比特币兼容性。桥接合约、跨链验证。
secp256r1NIST P-256 / WebAuthn / Apple安全隔区。基于Passkey的身份验证。
BLS验证者签名、多签聚合、门限方案。
BLS聚合签名验证来自多个验证者的单个聚合签名。

Common Patterns

常见模式

Ed25519 Signature Gate (MultiversX Native)

Ed25519签名校验门(MultiversX原生)

rust
#[endpoint(executeWithSignature)]
fn execute_with_signature(
    &self,
    data: ManagedBuffer,
    signature: ManagedBuffer,
) {
    let signer = self.trusted_signer().get();
    // Panics if invalid — tx reverts automatically
    self.crypto().verify_ed25519(
        &signer,
        &data,
        &signature,
    );
    // Only reached if signature is valid
    self.process_data(&data);
}
rust
#[endpoint(executeWithSignature)]
fn execute_with_signature(
    &self,
    data: ManagedBuffer,
    signature: ManagedBuffer,
) {
    let signer = self.trusted_signer().get();
    // Panics if invalid — tx reverts automatically
    self.crypto().verify_ed25519(
        &signer,
        &data,
        &signature,
    );
    // Only reached if signature is valid
    self.process_data(&data);
}

Ethereum Signature Verification (Graceful)

以太坊签名验证(优雅处理)

rust
#[endpoint(verifyEthSignature)]
fn verify_eth_signature(
    &self,
    key: ManagedBuffer,
    message: ManagedBuffer,
    signature: ManagedBuffer,
) -> bool {
    // Returns bool — handle failure gracefully
    let valid = self.crypto().verify_custom_secp256k1(
        &key,
        &message,
        &signature,
        MessageHashType::ECDSAKeccak256,
    );
    require!(valid, "Invalid Ethereum signature");
    valid
}
rust
#[endpoint(verifyEthSignature)]
fn verify_eth_signature(
    &self,
    key: ManagedBuffer,
    message: ManagedBuffer,
    signature: ManagedBuffer,
) -> bool {
    // Returns bool — handle failure gracefully
    let valid = self.crypto().verify_custom_secp256k1(
        &key,
        &message,
        &signature,
        MessageHashType::ECDSAKeccak256,
    );
    require!(valid, "Invalid Ethereum signature");
    valid
}

Multi-Validator BLS Aggregated Check

多验证者BLS聚合签名校验

rust
#[endpoint(verifyValidators)]
fn verify_validators(
    &self,
    validator_keys: ManagedVec<ManagedBuffer>,
    message: ManagedBuffer,
    aggregated_sig: ManagedBuffer,
) {
    // Panics if aggregated signature is invalid
    self.crypto().verify_bls_aggregated_signature(
        &validator_keys,
        &message,
        &aggregated_sig,
    );
}
rust
#[endpoint(verifyValidators)]
fn verify_validators(
    &self,
    validator_keys: ManagedVec<ManagedBuffer>,
    message: ManagedBuffer,
    aggregated_sig: ManagedBuffer,
) {
    // Panics if aggregated signature is invalid
    self.crypto().verify_bls_aggregated_signature(
        &validator_keys,
        &message,
        &aggregated_sig,
    );
}

Hash-Based Commit-Reveal

基于哈希的提交-揭示模式

rust
#[endpoint(commit)]
fn commit(&self, hash: ManagedByteArray<Self::Api, 32>) {
    let caller = self.blockchain().get_caller();
    self.commitments(&caller).set(hash);
}

#[endpoint(reveal)]
fn reveal(&self, value: ManagedBuffer) {
    let caller = self.blockchain().get_caller();
    let stored_hash = self.commitments(&caller).get();
    let computed_hash = self.crypto().sha256(&value);
    require!(stored_hash == computed_hash, "Hash mismatch");
    self.commitments(&caller).clear();
    self.process_reveal(&caller, &value);
}
rust
#[endpoint(commit)]
fn commit(&self, hash: ManagedByteArray<Self::Api, 32>) {
    let caller = self.blockchain().get_caller();
    self.commitments(&caller).set(hash);
}

#[endpoint(reveal)]
fn reveal(&self, value: ManagedBuffer) {
    let caller = self.blockchain().get_caller();
    let stored_hash = self.commitments(&caller).get();
    let computed_hash = self.crypto().sha256(&value);
    require!(stored_hash == computed_hash, "Hash mismatch");
    self.commitments(&caller).clear();
    self.process_reveal(&caller, &value);
}

DER Encoding for secp256k1

secp256k1的DER编码

rust
// When you have raw r,s components (e.g., from an oracle)
let r = ManagedBuffer::from(&r_bytes[..]);
let s = ManagedBuffer::from(&s_bytes[..]);
let der_sig = self.crypto().encode_secp256k1_der_signature(&r, &s);

let valid = self.crypto().verify_secp256k1(&pubkey, &message, &der_sig);
rust
// When you have raw r,s components (e.g., from an oracle)
let r = ManagedBuffer::from(&r_bytes[..]);
let s = ManagedBuffer::from(&s_bytes[..]);
let der_sig = self.crypto().encode_secp256k1_der_signature(&r, &s);

let valid = self.crypto().verify_secp256k1(&pubkey, &message, &der_sig);

Anti-Patterns

反模式

rust
// BAD: Trying to catch Ed25519 failure — it panics, there's nothing to catch
let result = self.crypto().verify_ed25519(&key, &msg, &sig);
// ← verify_ed25519 returns (), not Result. If invalid, tx is already dead.

// GOOD: Use Ed25519 as a gate (panic is the intended behavior)
self.crypto().verify_ed25519(&key, &msg, &sig);
// Execution continues only if valid

// BAD: Using verify_secp256k1 without checking the bool
self.crypto().verify_secp256k1(&key, &msg, &sig);
// ← Compiles fine but ignores the result! Signature not actually checked.

// GOOD: Always check the bool return
let valid = self.crypto().verify_secp256k1(&key, &msg, &sig);
require!(valid, "Invalid signature");

// BAD: Using wrong hash type for Ethereum signatures
self.crypto().verify_custom_secp256k1(&key, &msg, &sig, MessageHashType::ECDSASha256);
// ← Ethereum uses Keccak256, not SHA256

// GOOD: Match the hash type to the chain
self.crypto().verify_custom_secp256k1(&key, &msg, &sig, MessageHashType::ECDSAKeccak256);
rust
// BAD: Trying to catch Ed25519 failure — it panics, there's nothing to catch
let result = self.crypto().verify_ed25519(&key, &msg, &sig);
// ← verify_ed25519 returns (), not Result. If invalid, tx is already dead.

// GOOD: Use Ed25519 as a gate (panic is the intended behavior)
self.crypto().verify_ed25519(&key, &msg, &sig);
// Execution continues only if valid

// BAD: Using verify_secp256k1 without checking the bool
self.crypto().verify_secp256k1(&key, &msg, &sig);
// ← Compiles fine but ignores the result! Signature not actually checked.

// GOOD: Always check the bool return
let valid = self.crypto().verify_secp256k1(&key, &msg, &sig);
require!(valid, "Invalid signature");

// BAD: Using wrong hash type for Ethereum signatures
self.crypto().verify_custom_secp256k1(&key, &msg, &sig, MessageHashType::ECDSASha256);
// ← Ethereum uses Keccak256, not SHA256

// GOOD: Match the hash type to the chain
self.crypto().verify_custom_secp256k1(&key, &msg, &sig, MessageHashType::ECDSAKeccak256);