> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

GoQuorum: Selective Privacy on Ethereum Without Breaking Consensus

[ View on GitHub ]

GoQuorum: Selective Privacy on Ethereum Without Breaking Consensus

Hook

A blockchain network where nodes validate transactions they cannot read seems paradoxical—yet this is exactly how GoQuorum enables privacy without sacrificing consensus in enterprise Ethereum deployments.

Context

When enterprises first explored blockchain technology around 2015-2016, they hit an immediate wall: public Ethereum's transparency model was fundamentally incompatible with business requirements. Financial institutions couldn't broadcast trade details to competitors. Healthcare providers couldn't share patient data on a public ledger. Supply chain participants needed selective disclosure, not universal visibility.

The initial response was to abandon Ethereum entirely for purpose-built enterprise blockchains like Hyperledger Fabric. But this meant losing Ethereum's developer ecosystem, tooling, and smart contract capabilities. JP Morgan's Quorum project (later acquired by ConsenSys and renamed GoQuorum) took a different approach: fork go-ethereum and add privacy as a core architectural feature while maintaining compatibility with the broader Ethereum ecosystem. The result is a permissioned Ethereum implementation where transaction participants can be whitelisted, consensus happens without proof-of-work mining, and private transactions exist alongside public state on the same ledger.

Technical Insight

State Management

Submit Private Tx

Encrypt & Store Payload

P2P Encrypted Channel

Tx with Payload Hash

Validated Block

Public Contracts

Private Contracts

Decrypt Payload

Execute Private Tx

Identical Across Network

Unique Per Node

User/DApp

GoQuorum Node

Tessera Privacy Manager

Recipient Tessera Nodes

Consensus Layer

QBFT/IBFT/Raft

Public State Tree

Private State Tree

Recipient GoQuorum Nodes

All Network Nodes

System architecture — auto-generated

GoQuorum's architecture centers on a dual-state model that separates public and private states at the EVM level. Every node maintains a public state tree (identical across all network participants) and a private state tree (unique to each node based on which private transactions they're party to). This separation happens transparently—smart contracts can interact with both states, but private contract storage only exists on nodes authorized to see that data.

Here's how a private transaction flows through the system:

// Private transaction creation in GoQuorum
txArgs := map[string]interface{}{
    "from":       "0x1234...",
    "to":         "0x5678...",
    "data":       contractCode,
    "privateFor": []string{"ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="}, // Tessera public keys
    "privateFrom": "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=",
}

// This routes through Tessera privacy manager
// The actual transaction payload is encrypted and stored off-chain
// On-chain, only a hash of the private payload appears

When you send a private transaction, the client intercepts it before broadcasting. The transaction payload gets encrypted and sent to Tessera, the privacy manager that runs as a separate process. Tessera distributes the encrypted payload only to the intended recipients' privacy managers over a peer-to-peer encrypted channel. The transaction that actually goes on-chain contains only a hash of this private payload, not the content itself.

Non-participant nodes see the transaction execute but cannot access the payload. Their EVM processes an empty transaction for the private contract, leaving their private state unmodified. Participant nodes retrieve the decrypted payload from their local Tessera instance and execute the full transaction, updating their private state accordingly. This creates a fascinating property: nodes reach consensus on transactions they cannot fully see.

The consensus layer underwent equally significant changes. GoQuorum supports four alternatives to proof-of-work: QBFT (Quorum Byzantine Fault Tolerance), Istanbul BFT, Raft, and Clique PoA. QBFT represents the current recommended option—it's a PBFT-based algorithm that provides Byzantine fault tolerance (continuing to operate correctly even if up to ⅓ of nodes behave maliciously) while achieving block times under 10 seconds:

// QBFT configuration in genesis.json
{
  "config": {
    "chainId": 1337,
    "qbft": {
      "blockperiodseconds": 5,
      "epochlength": 30000,
      "requesttimeoutseconds": 10,
      "policy": 0,  // Round-robin proposer selection
      "ceil2Nby3Block": 0,
      "validatorcontractaddress": "0x0000000000000000000000000000000000007777"
    }
  },
  "alloc": {...},
  "extraData": "0x..."  // RLP encoded validator addresses
}

QBFT's validator contract approach allows dynamic validator sets without requiring a genesis file change—validators can be added or removed through smart contract calls, a crucial feature for long-running enterprise networks where consortium membership evolves.

The architecture maintains go-ethereum compatibility through careful abstraction. Private transactions use the V value in the transaction signature (set to 37 or 38) to signal privacy, while standard Ethereum transactions pass through unchanged. This means you can deploy a mix of public and private contracts on the same network, use standard Ethereum tooling like Truffle or Hardhat, and even run the same smart contract code on both GoQuorum and public Ethereum with minimal modifications.

Permissioning happens at multiple levels. Node-level permissioning restricts which Ethereum nodes can join the network and communicate with peers. Account-level permissioning controls which addresses can submit transactions or deploy contracts. This is implemented through a permissioning smart contract that the client queries before accepting transactions:

// Example account permissioning contract
contract AccountPermissions {
    mapping(address => bool) public authorizedAccounts;
    
    function transactPermitted(address sender) 
        external 
        view 
        returns (bool) 
    {
        return authorizedAccounts[sender];
    }
    
    function addAccount(address account) 
        external 
        onlyAdmin 
    {
        authorizedAccounts[account] = true;
    }
}

The GoQuorum client checks this contract before processing transactions, effectively creating a whitelist at the protocol level. This eliminates the need for application-layer access control in every smart contract, though you'll typically implement both for defense in depth.

Gotcha

The dual-state model creates subtle complexity that bites developers unfamiliar with privacy semantics. If a private contract calls a public contract, that interaction happens in public state and is visible to all nodes. If a public contract attempts to call a private contract, non-participant nodes see the call fail while participant nodes see it succeed—creating state divergence that can break assumptions about consensus. You need to carefully architect contract interactions to avoid leaking private information through public state changes or creating scenarios where different nodes have irreconcilable views of contract behavior.

Tessera represents both a single point of failure and a trust assumption. If your privacy manager goes down, you cannot send or receive private transactions until it's restored. If someone gains access to Tessera's database or private keys, they can decrypt all private transactions that node participated in—past, present, and future. The privacy model assumes honest-but-curious adversaries (nodes that follow protocol but try to learn private information) rather than actively malicious participants. A compromised validator node with access to Tessera could potentially violate privacy guarantees in ways that wouldn't be detectable until the damage is done. You're also locked into managing a fork of go-ethereum, which means security patches and new features from upstream require careful merging and testing. The ConsenSys team maintains synchronization with go-ethereum releases, but there's inherent lag and risk in this model compared to running vanilla geth or a purpose-built implementation like Hyperledger Besu.

Verdict

Use if: You need Ethereum smart contract compatibility in a consortium blockchain where multiple organizations must share a ledger but keep specific transaction details private—financial settlement networks, insurance claim processing, or supply chain tracking with competitive sensitive data. The dual-state architecture shines when you have mixed workloads (some public coordination logic, some private business logic) and need the Ethereum developer ecosystem without public blockchain economics. QBFT's Besu compatibility matters if you're building multi-client enterprise networks for resilience. Skip if: You're building on public Ethereum mainnet (obviously), need true zero-knowledge privacy without trusted privacy managers, lack the operational capability to run both GoQuorum nodes and Tessera infrastructure, or cannot accept the governance and maintenance overhead of running a forked client. For new projects without existing Ethereum dependencies, Hyperledger Besu offers similar features with better long-term maintenance characteristics, while Hyperledger Fabric provides stronger privacy guarantees through channels if you don't need EVM compatibility.