JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Bridge trust models — and the billion-dollar hacks

A bridge is only as safe as whatever decides 'yes, those funds really were locked.' Meet the trust spectrum — from a 5-of-9 multisig to a zk light client — through the three hacks that lost over a billion dollars.

A bridge is a $625 million honeypot

On 23 March 2022, the Ronin bridge — the rails that carried money in and out of the Axie Infinity game — paid 173,600 ETH and 25.5 million USDC to an attacker. About $625 million. No cryptography was broken: no hash was reversed, no signature forged from thin air. The attacker simply gathered five signing keys, and the bridge did exactly what it was built to do. Nobody noticed for six days.

Recall the lock-and-mint mechanism from the last guide: a bridge locks collateral on chain A and mints a wrapped IOU on chain B. That wrapped token is worth something only if you trust whatever decides *'the collateral really was locked.'* That decision-maker is the bridge's trust assumption — and it, not the maths, is where bridges live or die. This guide is about who or what makes that decision, and the three times it failed catastrophically.

The trust spectrum: who actually verifies the message?

Every cross-chain message faces the same hard question. Chain B cannot see chain A's state, so when a bridge contract on B is told *'100 ETH was locked on A — mint 100 wETH,'* something has to attest that this is true. Who attests, and how you'd catch them lying, defines exactly where a bridge sits on the trust spectrum. There are three families.

  1. Externally verified (trusted). An outside group — a multisig, an MPC/threshold-signature committee, or a validator set — watches chain A and signs an attestation. Chain B simply trusts their signatures. Security equals the honesty (and key hygiene) of that group. This covers most bridges in production — and most of the hacks.
  2. Optimistically verified. Messages are posted and assumed valid, but a challenge window opens during which any honest watcher can submit a fraud proof to cancel a lie. Security equals at least one honest, awake watcher plus a challenge that actually works. The price is latency: you wait out the window.
  3. Natively / locally verified (trust-minimized). Chain B verifies chain A itself — by running a light client of A's consensus inside a contract, or by checking a validity proof of it. No new trusted party is introduced; security collapses to chain A's own consensus plus correct code. This is the hardest to build, and the rarest.

There is a reason almost everyone reaches for the first option. The interoperability trilemma (named by Arjun Bhuptani) observes that a bridge struggles to be all of: trust-minimized, generalizable (able to carry arbitrary data, not just token transfers), and extensible (cheap to deploy on any new chain). External verification buys generality and easy deployment — by quietly adding a trusted party. The three hacks below are three different ways that trade-off detonates.

Ronin: when five keys are the whole security model

Ronin is an Ethereum sidechain built for Axie Infinity. The bridge connecting it to Ethereum was secured by a validator set of nine nodes; releasing funds required a 5-of-9 multisig of their signatures. Sky Mavis, the studio behind Axie, operated four of those nine validators directly.

To absorb a traffic spike in November 2021, the Axie DAO whitelisted Sky Mavis to sign transactions on the DAO's behalf — effectively handing Sky Mavis control of a fifth validator. The arrangement was meant to be temporary. The allowlist was never revoked. So four keys plus one delegated key equalled five — the exact threshold — and all five were reachable through Sky Mavis's own infrastructure. The '9 independent parties' on paper had quietly become 'one company's servers.'

The Lazarus Group (attributed to North Korea by the FBI) phished a Sky Mavis engineer with a fake job offer carrying a malware PDF, then pivoted through the company's systems into the validator nodes. With those five signing keys, the attacker forged two withdrawals draining 173,600 ETH and 25.5M USDC. Because the bridge only ever checks signatures, the withdrawals looked perfectly valid. No alarm fired — the theft surfaced six days later only when a user couldn't withdraw 5,000 ETH.

// Ronin-style bridge withdrawal (simplified)
function withdrawERC20(WithdrawalReceipt receipt, Signature[] sigs) external {
    bytes32 h = hashReceipt(receipt);
    uint256 weight = 0;
    for (uint256 i = 0; i < sigs.length; i++) {
        address signer = ecrecover(h, sigs[i]);
        require(isValidator[signer], "not a validator");
        weight += validatorWeight[signer];
    }
    // The ENTIRE security of the bridge is this one line:
    require(weight >= threshold, "insufficient signatures"); // 5 of 9
    release(receipt.token, receipt.to, receipt.amount);
}
If an attacker controls validator keys whose weight reaches `threshold`, every check passes and the funds leave. No bug was needed — the model worked exactly as designed.

Wormhole: the validator set was fine, the code wasn't

Five weeks earlier, Wormhole — a Solana ↔ Ethereum bridge — lost 120,000 wETH (~$325M) without a single key leaking. Wormhole is guarded by 19 'guardian' nodes; a valid cross-chain message (a VAA, Verified Action Approval) needs 13-of-19 guardian signatures. The validator set was honest and fully intact. The bug was in the code that was supposed to check their signatures.

On Solana, signature checks run in a separate instruction earlier in the same transaction; the bridge program then inspects the transaction's instruction list (via the 'instructions sysvar') to confirm the real `secp256k1` program had verified the guardians' signatures. Wormhole used a deprecated helper, `load_instruction_at`, which did not verify that the sysvar account it read was the genuine one.

So the attacker supplied a fake 'instructions' account claiming a `secp256k1` verification had succeeded — when none had run. The program believed thirteen guardians had signed, accepted a forged VAA, and minted 120,000 wETH on Solana backed by nothing. They bridged most of it to Ethereum before the collateral gap was spotted. Jump Crypto, Wormhole's backer, refilled the 120,000 ETH within days to keep wETH solvent — a bailout, not a recovery.

// VULNERABLE: never checks that `sysvar_account` is the real instructions sysvar
let current_ix = load_instruction_at(index, &sysvar_account.data);
verify_secp256k1_ran(current_ix);   // attacker forged sysvar_account -> check bypassed

// FIX: the *_checked variant validates the sysvar account's address first
let current_ix = load_instruction_at_checked(index, &sysvar_account)?;
verify_secp256k1_ran(current_ix);   // a spoofed account now reverts
A 13-of-19 guardian set is irrelevant if the on-chain code can be tricked into skipping the check entirely. The fix was, almost literally, one function name.

Nomad: a single zero that opened the vault

Nomad tried to be different — an optimistically verified bridge. Instead of a quorum signing every message, Nomad committed Merkle roots of pending messages on-chain and opened a ~30-minute window in which watchers could submit a fraud proof against a fraudulent root. In principle, that's a real step down the spectrum toward trust-minimization.

In August 2022 a routine upgrade re-initialized the contract's trusted root to `0x0000…0000`. The proving logic checked `acceptableRoot[root]` to decide whether a message was confirmed — but because of how the new root was committed, the zero value was treated as an acceptable root. Every message whose Merkle proof 'validated' against zero — that is, effectively every message — was now considered proven and ready to deliver.

// Nomad-style message processing (simplified)
function process(bytes memory message) public {
    bytes32 root = computeRootFromProof(message);  // attacker fully controls `message`
    require(acceptableRoot[root], "not proven");    // BUG: acceptableRoot[0x00] == true
    // ...deliver the message, e.g. "release 100 WBTC to <attacker>"
    handle(message);
}
After the bad upgrade, any crafted message proved against the zero root, so the require passed for everyone. There was no key to steal — the gate was simply left wide open.

What followed was unprecedented: the first crowd-sourced hack. The original exploiter's transaction sat in the public mempool for all to see; hundreds of onlookers copied it, swapped in their own address, and replayed it. Roughly $190M drained in thousands of copycat transactions over a few chaotic hours — a leaderless, free-for-all looting that nobody needed special skill to join.

The trust-minimized end: optimistic, light-client, and zk bridges

So what does the trust-minimized end look like when it is built correctly? Three constructions, in increasing rigour.

Optimistic bridges, done right. Post each message with a bond and an challenge window; any watcher may slash that bond with a fraud proof if the message lies. Security rests on a '1-of-N honest watcher' assumption — far weaker, and so far stronger, than 'trust this committee.' The costs are real: latency (minutes to hours) and the assumption that a watcher is genuinely awake and able to land its challenge transaction. Nomad's idea was exactly this; its bug lived elsewhere.

Light-client (natively verified) bridges. The destination chain runs a light client of the source chain as a smart contract: it ingests source block headers, verifies the source validators' consensus signatures (for example a BLS aggregate of their attestations), then checks a Merkle proof that the event sat inside a verified block. Trust collapses to the source chain's own consensus plus correct code — no extra committee at all. Cosmos's IBC works this way (the next guide goes deep). The cost: verifying another chain's consensus on-chain is expensive, must be re-implemented per chain pair, and chains without fast finality are painful to prove.

zk bridges, the frontier. Verifying an entire consensus on-chain is costly, so zk bridges replace it with a succinct validity proof — a zk-SNARK attesting that *'a valid source block containing this event exists, and the source validators signed it.'* The destination verifies a tiny proof cheaply: no challenge window, no committee. Trust reduces to source-chain security + the soundness of the proof system + a correct circuit. Designs like zkBridge, Polyhedra, and Succinct push this way; the hard parts are proving cost and getting the circuit right.

How to read a bridge's trust model before you cross

Before you let a bridge hold your money, read its trust model the way an auditor would. The cryptography is rarely the weak point; keys, code, and upgrade powers are. Work through this checklist for any bridge you depend on.

  1. Count the quorum. Externally verified? Find the N-of-M and who holds the M keys. 'Five of nine, all run by one company' is one phishing email away from Ronin. Prefer large, independent, DKG-generated threshold sets over a hot multisig on a few servers.
  2. Find the admin key. Is there an upgrade or pause key? Is it a single key, a multisig, or sitting behind a timelock? An instant, no-delay upgrade key is a backdoor with good intentions — and it can drain everything regardless of how elegant the verification below it is.
  3. Identify the verification mechanism. External committee, optimistic + watchers, or native/light-client/zk? Each pushes the risk somewhere different — onto people, onto watcher liveness, or onto code — and you should know which one you're betting on.
  4. Weigh value-at-risk against review. Hundreds of millions locked should mean multiple deep audits, a serious bug bounty, and real time in production. Read the post-mortems of similar designs first — the same mistakes recur.
  5. Ask what fails if a relayer disappears. In a good design a relayer can stall you (a liveness problem) but cannot steal from you (a safety problem). If a single relayer can do both, that asymmetry is a red flag.

Every bridge is only as strong as its weakest link, and history is blunt about where that link sits. Ronin fell to key management, Wormhole to a verifier bug, Nomad to an initialization mistake — none to broken mathematics. When you cross a bridge, you are not trusting Ethereum or Bitcoin; you are trusting whatever this particular bridge decided would speak for them. Know exactly what that is before you send the funds.