Docs

VerifySignature - Validate Transaction Signatures

Verify cryptographic signatures for Sui transactions via gRPC. Essential for security validation and multi-sig workflows with Dwellir.

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.v2.SignatureVerificationService Method: VerifySignature Type: Unary RPC

Use Cases

Pre-Execution Validation

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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

TypeScript
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.