Skip to main content

VerifySignature

Validate Transaction Signatures#

The VerifySignature method verifies that a cryptographic signature is valid for given transaction data and public key. This is essential for security validation, multi-signature workflows, and ensuring transaction authenticity before execution.

Method Signature#

Service: sui.rpc.v2beta2.SignatureVerificationService Method: VerifySignature Type: Unary RPC

Parameters#

ParameterTypeRequiredDescription
transactionbytesYesBCS-encoded transaction bytes
signaturebytesYesSignature bytes
public_keybytesYesPublic key bytes
schemeSignatureSchemeYesSignature scheme (ED25519, SECP256K1, etc.)

Signature Schemes#

SchemeValueDescription
ED255190Ed25519 signature scheme
SECP256K11Secp256k1 (Ethereum-compatible)
SECP256R12Secp256r1 (P-256)
MULTISIG3Multi-signature

Response Structure#

message VerifySignatureResponse {
bool is_valid = 1;
string error_message = 2;
}

Code Examples#

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';

const ENDPOINT = 'api-sui-mainnet-full.n.dwellir.com';
const API_TOKEN = 'your_api_token_here';

const packageDefinition = protoLoader.loadSync('./protos/signature.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ['./protos']
});

const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const credentials = grpc.credentials.createSsl();
const client = new protoDescriptor.sui.rpc.v2beta2.SignatureVerificationService(
ENDPOINT,
credentials
);

const metadata = new grpc.Metadata();
metadata.add('x-api-key', API_TOKEN);

enum SignatureScheme {
ED25519 = 0,
SECP256K1 = 1,
SECP256R1 = 2,
MULTISIG = 3
}

async function verifySignature(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
scheme: SignatureScheme
): Promise<{ isValid: boolean; error?: string }> {
return new Promise((resolve, reject) => {
const request = {
transaction: transactionBytes,
signature: signature,
public_key: publicKey,
scheme: scheme
};

client.VerifySignature(request, metadata, (error: any, response: any) => {
if (error) {
console.error('VerifySignature error:', error.message);
reject(error);
return;
}

resolve({
isValid: response.is_valid,
error: response.error_message
});
});
});
}

// Usage
const result = await verifySignature(
txBytes,
signatureBytes,
publicKeyBytes,
SignatureScheme.ED25519
);

if (result.isValid) {
console.log('Signature is valid');
} else {
console.error('Signature verification failed:', result.error);
}

Use Cases#

Pre-Execution Validation#

async function validateBeforeExecution(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
scheme: SignatureScheme
): Promise<boolean> {
const verification = await verifySignature(
transactionBytes,
signature,
publicKey,
scheme
);

if (!verification.isValid) {
console.error('Cannot execute: Invalid signature');
console.error('Reason:', verification.error);
return false;
}

console.log('Signature verified, safe to execute');
return true;
}

// Usage
const canExecute = await validateBeforeExecution(
txBytes,
signatureBytes,
publicKeyBytes,
SignatureScheme.ED25519
);

if (canExecute) {
await executeTransaction(txBytes, signatureBytes, publicKeyBytes);
}

Multi-Signature Verification#

interface SignerInfo {
publicKey: Uint8Array;
signature: Uint8Array;
scheme: SignatureScheme;
}

async function verifyMultiSig(
transactionBytes: Uint8Array,
signers: SignerInfo[],
threshold: number
): Promise<{
isValid: boolean;
validSignatures: number;
invalidSigners: number[];
}> {
const verificationPromises = signers.map((signer, index) =>
verifySignature(
transactionBytes,
signer.signature,
signer.publicKey,
signer.scheme
)
.then(result => ({ index, ...result }))
.catch(() => ({ index, isValid: false, error: 'Verification failed' }))
);

const results = await Promise.all(verificationPromises);

const validSignatures = results.filter(r => r.isValid).length;
const invalidSigners = results.filter(r => !r.isValid).map(r => r.index);

return {
isValid: validSignatures >= threshold,
validSignatures,
invalidSigners
};
}

// Usage - 2 of 3 multisig
const signers: SignerInfo[] = [
{ publicKey: pk1, signature: sig1, scheme: SignatureScheme.ED25519 },
{ publicKey: pk2, signature: sig2, scheme: SignatureScheme.ED25519 },
{ publicKey: pk3, signature: sig3, scheme: SignatureScheme.ED25519 }
];

const multiSigResult = await verifyMultiSig(txBytes, signers, 2);

if (multiSigResult.isValid) {
console.log(`Valid: ${multiSigResult.validSignatures}/3 signatures`);
} else {
console.error('Insufficient valid signatures');
console.error('Invalid signers:', multiSigResult.invalidSigners);
}

Signature Scheme Detection#

async function detectAndVerifySignature(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array
): Promise<{ scheme: SignatureScheme; isValid: boolean } | null> {
const schemes = [
SignatureScheme.ED25519,
SignatureScheme.SECP256K1,
SignatureScheme.SECP256R1
];

for (const scheme of schemes) {
try {
const result = await verifySignature(
transactionBytes,
signature,
publicKey,
scheme
);

if (result.isValid) {
return { scheme, isValid: true };
}
} catch (error) {
// Try next scheme
continue;
}
}

return null;
}

// Usage
const detection = await detectAndVerifySignature(txBytes, sig, pubKey);

if (detection) {
console.log(`Valid signature using ${SignatureScheme[detection.scheme]}`);
} else {
console.error('No valid signature scheme found');
}

Transaction Replay Protection#

class SignatureValidator {
private verifiedSignatures = new Set<string>();

async verifyOnce(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
scheme: SignatureScheme
): Promise<boolean> {
// Create unique key for this transaction+signature
const key = this.createSignatureKey(transactionBytes, signature);

if (this.verifiedSignatures.has(key)) {
console.warn('Signature already used (replay attempt detected)');
return false;
}

const result = await verifySignature(
transactionBytes,
signature,
publicKey,
scheme
);

if (result.isValid) {
this.verifiedSignatures.add(key);
return true;
}

return false;
}

private createSignatureKey(tx: Uint8Array, sig: Uint8Array): string {
// Simple concatenation - use proper hash in production
return `${Array.from(tx).join(',')}_${Array.from(sig).join(',')}`;
}

clear(): void {
this.verifiedSignatures.clear();
}
}

// Usage
const validator = new SignatureValidator();

const isValid = await validator.verifyOnce(txBytes, sig, pubKey, scheme);
const isDuplicate = await validator.verifyOnce(txBytes, sig, pubKey, scheme);

console.log('First verification:', isValid);
console.log('Second verification (replay):', isDuplicate); // false

Batch Signature Verification#

interface BatchVerificationItem {
transactionBytes: Uint8Array;
signature: Uint8Array;
publicKey: Uint8Array;
scheme: SignatureScheme;
id: string;
}

async function batchVerifySignatures(
items: BatchVerificationItem[]
): Promise<Map<string, boolean>> {
const results = new Map<string, boolean>();

const verifications = items.map(async item => {
try {
const result = await verifySignature(
item.transactionBytes,
item.signature,
item.publicKey,
item.scheme
);

results.set(item.id, result.isValid);
} catch (error) {
results.set(item.id, false);
}
});

await Promise.all(verifications);

return results;
}

// Usage
const batch: BatchVerificationItem[] = [
{ id: 'tx1', transactionBytes: tx1, signature: sig1, publicKey: pk1, scheme: SignatureScheme.ED25519 },
{ id: 'tx2', transactionBytes: tx2, signature: sig2, publicKey: pk2, scheme: SignatureScheme.ED25519 },
{ id: 'tx3', transactionBytes: tx3, signature: sig3, publicKey: pk3, scheme: SignatureScheme.SECP256K1 }
];

const results = await batchVerifySignatures(batch);

for (const [id, isValid] of results) {
console.log(`${id}: ${isValid ? 'Valid' : 'Invalid'}`);
}

Best Practices#

Always Verify Before Execute#

async function safeExecuteTransaction(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
scheme: SignatureScheme
): Promise<any> {
// Step 1: Verify signature
const verification = await verifySignature(
transactionBytes,
signature,
publicKey,
scheme
);

if (!verification.isValid) {
throw new Error(`Invalid signature: ${verification.error}`);
}

// Step 2: Execute only if valid
return await executeTransaction(transactionBytes, signature, publicKey);
}

Handle Verification Errors Gracefully#

async function verifySignatureSafe(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
scheme: SignatureScheme
): Promise<{ isValid: boolean; error?: string }> {
try {
return await verifySignature(transactionBytes, signature, publicKey, scheme);
} catch (error: any) {
console.error('Signature verification failed:', error.message);

return {
isValid: false,
error: error.message
};
}
}

Cache Verification Results#

class VerificationCache {
private cache = new Map<string, { isValid: boolean; timestamp: number }>();
private ttl = 60000; // 1 minute

async verify(
transactionBytes: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
scheme: SignatureScheme
): Promise<boolean> {
const key = this.createKey(transactionBytes, signature, publicKey);
const cached = this.cache.get(key);

if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.isValid;
}

const result = await verifySignature(
transactionBytes,
signature,
publicKey,
scheme
);

this.cache.set(key, {
isValid: result.isValid,
timestamp: Date.now()
});

return result.isValid;
}

private createKey(tx: Uint8Array, sig: Uint8Array, pk: Uint8Array): string {
return `${tx.length}_${sig.length}_${pk.length}`;
}
}

Performance Characteristics#

MetricValue
Typical Latency10-30ms
Response SizeLess than 100 bytes
Cache RecommendedYes (short TTL)
Rate Limit ImpactLow

Signature Scheme Comparison#

SchemeSecurityPerformanceUse Case
ED25519HighFastGeneral purpose, recommended
SECP256K1HighModerateEthereum compatibility
SECP256R1HighModerateWebAuthn, mobile devices
MULTISIGHighSlowerMulti-party approval

Common Errors#

Error CodeScenarioSolution
INVALID_ARGUMENTMalformed signature or keyVerify byte encoding
FAILED_PRECONDITIONInvalid scheme for key typeMatch scheme to key type
UNAUTHENTICATEDMissing/invalid tokenVerify x-api-key header

Need help? Contact support@dwellir.com or check the gRPC overview.