Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Web Security TippsIntroducing the new Confluence integration with Google Chat(22.09.2026 um 19:40 Uhr)
Web Security TippsQuick notes in Take notes for me(22.09.2026 um 21:31 Uhr)
Sichere ProgrammierungSecurity improvements for SSH(22.09.2026 um 16:11 Uhr)
Sichere ProgrammierungKI-Akzeptanz: Wie Rewe digital einfach nur den Chatbot umbenannte(22.09.2026 um 18:00 Uhr)
Sichere ProgrammierungClaude Opus 5.5: Keeping safety ahead of capabilities(22.09.2026 um 20:59 Uhr)
Sichere ProgrammierungYour Terraform Monolith Isn't Too Big. It's Tightly Coupled.(22.09.2026 um 21:00 Uhr)
Sichere ProgrammierungMy PR got merged into Mike — OSS Legal AI Platform 🎉(22.09.2026 um 21:34 Uhr)
Sichere ProgrammierungStop Writing JavaScript To Fix `100vh` On Mobile(22.09.2026 um 21:35 Uhr)
Sichere ProgrammierungNext.js proxy.ts Explained (with Cheat Sheet)(22.09.2026 um 21:36 Uhr)
Web Security TippsIntroducing the new Confluence integration with Google Chat(22.09.2026 um 19:40 Uhr)
Web Security TippsQuick notes in Take notes for me(22.09.2026 um 21:31 Uhr)
Sichere ProgrammierungSecurity improvements for SSH(22.09.2026 um 16:11 Uhr)
Sichere ProgrammierungKI-Akzeptanz: Wie Rewe digital einfach nur den Chatbot umbenannte(22.09.2026 um 18:00 Uhr)
Sichere ProgrammierungClaude Opus 5.5: Keeping safety ahead of capabilities(22.09.2026 um 20:59 Uhr)
Sichere ProgrammierungYour Terraform Monolith Isn't Too Big. It's Tightly Coupled.(22.09.2026 um 21:00 Uhr)
Sichere ProgrammierungMy PR got merged into Mike — OSS Legal AI Platform 🎉(22.09.2026 um 21:34 Uhr)
Sichere ProgrammierungStop Writing JavaScript To Fix `100vh` On Mobile(22.09.2026 um 21:35 Uhr)
Sichere ProgrammierungNext.js proxy.ts Explained (with Cheat Sheet)(22.09.2026 um 21:36 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Zero-Knowledge Multi-Sig: How We Built Privacy Preserving Validator Voting

Built Privacy-Preserving Validator Voting When 2 of 3 consensus meets cryptographic privacy Here's a problem nobody talks about with multi-signature wallets: everyone can see who signed. When your DAO votes, when your multi-sig…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Built Privacy-Preserving Validator Voting



When 2 of 3 consensus meets cryptographic privacy






Here's a problem nobody talks about with multi-signature wallets: everyone can see who signed.



When your DAO votes, when your multi-sig approves a transaction, when validators reach consensus—it's all public. The blockchain records exactly which addresses participated.



For Trinity Protocol, that wasn't acceptable. We needed 2 of 3 consensus across three blockchains, but we didn't want to reveal which two validators agreed on any given operation.



So we built zero-knowledge multi-sig verification.






The Privacy Problem



Traditional multi-sig is transparent by design. If Alice, Bob, and Carol control a 2 of 3 wallet, every transaction shows exactly which two signed.



This creates problems:



1. Targeted Attacks

If attackers know Alice and Bob always sign together, they only need to compromise two specific people—not "any two of three."



2. Social Pressure

Validators can be pressured to sign or not sign based on public voting history.



3. Collusion Detection

Ironically, transparency makes secret collusion easier to hide. If two validators always agree, it looks normal. ZK proofs let us detect statistical anomalies without revealing individual votes.





The ZK-SNARK Solution



Zero-knowledge proofs let us prove something is true without revealing why it's true.



For multi-sig, this means:




  • ✅ Prove that 2 valid signatures exist

  • ❌ Reveal which 2 signers participated



The verifier learns only one bit of information: "Yes, the threshold was met."





Our Implementation: Trinity Protocol v3.5.24



We use Circom for circuit design and Groth16 for proof generation. Our circuits are specifically designed for Trinity's 2 of 3 multi-chain consensus.





Chain Identifiers



Each chain in Trinity Protocol has a unique identifier:




























Chain ID Blockchain Role
1 Arbitrum Primary security layer
2 Solana High-frequency monitoring
3 TON Emergency recovery + quantum-safe storage




Multi-Sig Verification Circuit



Here's our production circuit for Trinity consensus:




pragma circom 2.1.0;

include "node_modules/circomlib/circuits/poseidon.circom";
include "node_modules/circomlib/circuits/comparators.circom";
include "node_modules/circomlib/circuits/bitify.circom";

/**
* Trinity Protocol Multi-Signature Verification Circuit v3.5.24
*
* Proves 2-of-3 consensus without revealing which validators signed
* Supports cross-chain operations across Arbitrum, Solana, and TON
*
* Security: 128-bit (BN254 curve)
* Constraints: ~1,200
* Proof time: 10-20ms
*/
template TrinityMultiSigVerifier() {
// ===== PUBLIC INPUTS =====
signal input messageHash; // Hash of operation being authorized
signal input threshold; // Required signatures (2 for Trinity)
signal input operationId; // Unique operation identifier
signal input chainId; // Target chain (1=Arb, 2=Sol, 3=TON)
signal input validatorPubKeyHashes[3]; // Poseidon hashes of validator public keys

// ===== PRIVATE INPUTS =====
signal input validatorPrivateKeys[3]; // Private keys (NEVER revealed)
signal input signatureFlags[3]; // 1 if signed, 0 if not
signal input nonces[3]; // Per-signature nonces for replay protection

// ===== OUTPUT =====
signal output valid; // 1 if threshold met

// ===== CONSTRAINT: Flags must be binary =====
for (var i = 0; i < 3; i++) {
signatureFlags[i] * (signatureFlags[i] - 1) === 0;
}

// ===== VERIFY EACH SIGNATURE =====
component pubKeyHashers[3];
component sigVerifiers[3];
signal validSignatures[3];

for (var i = 0; i < 3; i++) {
// Hash private key to get public key hash
pubKeyHashers[i] = Poseidon(1);
pubKeyHashers[i].inputs[0] <== validatorPrivateKeys[i];

// Verify public key matches expected
sigVerifiers[i] = IsEqual();
sigVerifiers[i].in[0] <== pubKeyHashers[i].out;
sigVerifiers[i].in[1] <== validatorPubKeyHashes[i];

// Count only if flag is set AND signature is valid
validSignatures[i] <== signatureFlags[i] * sigVerifiers[i].out;
}

// ===== COUNT VALID SIGNATURES =====
signal sum01 <== validSignatures[0] + validSignatures[1];
signal totalValid <== sum01 + validSignatures[2];

// ===== THRESHOLD CHECK =====
component gte = GreaterEqThan(8);
gte.in[0] <== totalValid;
gte.in[1] <== threshold;

valid <== gte.out;

// ===== REPLAY PROTECTION =====
// Bind proof to specific operation via Poseidon hash
component replayProtection = Poseidon(3);
replayProtection.inputs[0] <== operationId;
replayProtection.inputs[1] <== chainId;
replayProtection.inputs[2] <== messageHash;

// This creates a unique proof fingerprint
signal proofBinding <== replayProtection.out;
}

component main {public [
messageHash,
threshold,
operationId,
chainId,
validatorPubKeyHashes
]} = TrinityMultiSigVerifier();









What Makes This Different




  1. Poseidon Hashing: We use Poseidon instead of MiMC—it's more efficient inside ZK circuits (fewer constraints)


  2. Chain ID Binding: Proofs are bound to specific chains, preventing cross-chain replay attacks


  3. Operation Binding: Each proof is tied to a unique operationId, preventing reuse


  4. ~1,200 Constraints: Optimized for fast proof generation (10-20ms)







Vault Ownership Circuit



We also use ZK proofs for vault ownership verification with Merkle tree support:




pragma circom 2.1.0;

include "node_modules/circomlib/circuits/poseidon.circom";
include "node_modules/circomlib/circuits/comparators.circom";

/**
* Vault Ownership Verification with Merkle Proof
*
* Proves ownership of a vault position without revealing:
* - Owner's private key
* - Exact vault contents
* - Position in the Merkle tree
*
* Supports ERC-4626 tokenized vaults
* Constraints: ~2,500
*/
template VaultOwnershipVerifier(merkleDepth) {
// ===== PUBLIC INPUTS =====
signal input ownerPublicKeyHash; // Poseidon hash of owner's public key
signal input vaultMerkleRoot; // Merkle root of all vaults
signal input challengeHash; // Challenge for replay protection
signal input vaultId; // Specific vault identifier

// ===== PRIVATE INPUTS =====
signal input ownerPrivateKey; // Owner's private key (NEVER revealed)
signal input vaultNonce; // Vault-specific nonce
signal input merklePathElements[merkleDepth]; // Merkle proof elements
signal input merklePathIndices[merkleDepth]; // Left/right path indicators
signal input vaultShares; // ERC-4626 share balance

// ===== OUTPUT =====
signal output valid;

// ===== VERIFY OWNERSHIP =====
component ownerHasher = Poseidon(1);
ownerHasher.inputs[0] <== ownerPrivateKey;

component ownerCheck = IsEqual();
ownerCheck.in[0] <== ownerHasher.out;
ownerCheck.in[1] <== ownerPublicKeyHash;

// ===== COMPUTE VAULT LEAF =====
component vaultLeaf = Poseidon(4);
vaultLeaf.inputs[0] <== ownerPublicKeyHash;
vaultLeaf.inputs[1] <== vaultId;
vaultLeaf.inputs[2] <== vaultShares;
vaultLeaf.inputs[3] <== vaultNonce;

// ===== VERIFY MERKLE PROOF =====
component merkleHashers[merkleDepth];
signal merkleNodes[merkleDepth + 1];
merkleNodes[0] <== vaultLeaf.out;

for (var i = 0; i < merkleDepth; i++) {
merkleHashers[i] = Poseidon(2);

// Select left/right based on path index
signal left <== merklePathIndices[i] * merklePathElements[i] +
(1 - merklePathIndices[i]) * merkleNodes[i];
signal right <== merklePathIndices[i] * merkleNodes[i] +
(1 - merklePathIndices[i]) * merklePathElements[i];

merkleHashers[i].inputs[0] <== left;
merkleHashers[i].inputs[1] <== right;
merkleNodes[i + 1] <== merkleHashers[i].out;
}

// ===== VERIFY ROOT MATCHES =====
component rootCheck = IsEqual();
rootCheck.in[0] <== merkleNodes[merkleDepth];
rootCheck.in[1] <== vaultMerkleRoot;

// ===== FINAL VALIDITY =====
valid <== ownerCheck.out * rootCheck.out;
}

component main {public [
ownerPublicKeyHash,
vaultMerkleRoot,
challengeHash,
vaultId
]} = VaultOwnershipVerifier(20);






This proves you own a vault without revealing your private key, exact balance, or position in the vault tree.






On-Chain Verification Contracts



Our ZK proofs are verified on-chain using production Solidity contracts:






Groth16Verifier.sol



Core verification logic for BN254 curve:




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Groth16Verifier {
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

function verifyMultisigProof(
uint256[8] calldata proof,
uint256[] calldata publicInputs
) external view returns (bool valid) {
// Validate all inputs are in field
for (uint256 i = 0; i < publicInputs.length; i++) {
if (publicInputs[i] >= SNARK_SCALAR_FIELD) revert ProofElementOutOfRange();
}

// BN254 pairing check
return _verifyProof(proof, publicInputs);
}
}









ZKConsensusVerifier.sol



Trinity Protocol integration with validator management:




// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ZKConsensusVerifier {
// Chain identifiers
uint8 public constant ARBITRUM_CHAIN_ID = 1;
uint8 public constant SOLANA_CHAIN_ID = 2;
uint8 public constant TON_CHAIN_ID = 3;

// Validator public key hashes (Poseidon)
mapping(uint8 => uint256) public validatorPubKeyHashes;

// Replay protection
mapping(bytes32 => bool) public usedProofHashes;

event ZKConsensusVerified(
bytes32 indexed operationId,
uint8 threshold,
uint8 chainId
);

function verifyTrinityConsensus(
uint256[8] calldata proof,
bytes32 operationId,
uint8 chainId,
bytes32 messageHash
) external returns (bool) {
// Build proof hash for replay protection
bytes32 proofHash = keccak256(abi.encode(proof, operationId));
require(!usedProofHashes[proofHash], "Proof already used");

// Verify the ZK proof
uint256[] memory publicInputs = _buildPublicInputs(
operationId, chainId, messageHash
);

bool valid = groth16Verifier.verifyMultisigProof(proof, publicInputs);

if (valid) {
usedProofHashes[proofHash] = true;
emit ZKConsensusVerified(operationId, 2, chainId);
}

return valid;
}
}









Performance Metrics



Our circuits are optimized for production:





























Circuit Constraints Proof Gen Verification Proof Size
Multi-Sig (2-of-3) ~1,200 10-20ms 2-5ms 128 bytes
Vault Ownership ~2,500 20-40ms 2-5ms 128 bytes


Key insight: Verification is constant time regardless of complexity. The on-chain verifier just checks one proof.






Multi-Chain Deployment



The same ZK proofs work across all three chains:




┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ Arbitrum │ │ Solana │ │ TON │
│ Validator │ │ Validator │ │ Validator │
│ (Chain 1) │ │ (Chain 2) │ │ (Chain 3) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────┬───────┴───────────────────┘


┌─────────────────┐
│ ZK Proof Gen │
│ (off-chain) │
│ 10-40ms │
└────────┬────────┘


┌─────────────────┐
│ On-Chain │
│ Verification │
│ 128 bytes │
│ ~200k gas │
└─────────────────┘































Chain Contract Verification Method
Arbitrum ZKConsensusVerifier.sol Solidity pairing precompile
Solana zk_consensus_verifier.rs Anchor + Groth16 lib
TON zk_verifier.fc FunC + BN254 ops





The Mathematical Guarantee



This is what we can formally prove in Lean 4:




-- From our 184 verified theorems
theorem zk_soundness :
(proof : ZKProof) (inputs : PublicInputs),
verified proof inputs
(witnesses : PrivateInputs),
valid_consensus witnesses inputs
threshold_met witnesses.signatures 2

theorem zk_zero_knowledge :
(proof : ZKProof),
verified proof
¬reveals_which_validators_signed proof






In plain English: if the proof verifies, there must exist valid signatures meeting the threshold but the proof reveals nothing about which validators signed.






Repository Structure



All code is open source:



Circuits:





Verification Contracts:





Formal Proofs:











What's Next



In the next post, we'll cover how we formally verified these circuits with Lean 4 proving not just that they work, but that they can't fail. 184 theorems. Zero sorry statements.



Trust Math, Not Humans. 🔐






Series: Trinity Protocol Security

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Zero-Knowledge Multi-Sig: How We Built Privacy Preserving Validator Voting

Thematisch verwandte Begriffe: ZeroKnowledge, MultiSig, Built, Privacy · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-77259 | MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian pro…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick