ChainLaunch

Besu Privacy with Tessera: Setup Guide

Besu Privacy with Tessera: Setup Guide

David Viejo

Written by David Viejo

Enterprise Ethereum networks need confidential transactions. Public blockchains broadcast every transaction to every node, but businesses require selective data sharing. According to a Forrester survey on enterprise blockchain (2024), 72% of organizations rank data privacy as their top blockchain requirement. Tessera, the open-source private transaction manager for Hyperledger Besu, solves this by encrypting transaction payloads and distributing them only to authorized participants. This guide covers Tessera's architecture, configuration, the private transaction lifecycle, and production deployment patterns.

TL;DR: Tessera adds private transaction support to Hyperledger Besu by encrypting payloads and sharing them only with specified participants. A privacy marker transaction (PMT) on the public chain proves the transaction happened without revealing its contents. This guide walks through Tessera configuration, Besu integration, privacy groups, private smart contracts, and multi-tenancy for enterprise deployments.

For a comparison of Besu's privacy model against Fabric and Corda, read our blockchain privacy comparison. If you haven't set up a Besu network yet, start with our deploy Besu in 2 minutes guide.

What Is Tessera and How Does It Work with Besu?

Tessera is an open-source private transaction manager maintained under the ConsenSys umbrella. It handles encryption, key management, and peer-to-peer distribution of private transaction payloads. According to the Besu documentation, each Besu node pairs with its own Tessera instance, creating a sidecar architecture where the Besu node manages public state and Tessera manages private state.

The architecture looks like this:

Node A                              Node B
+-----------+   +----------+       +-----------+   +----------+
|   Besu    |<->| Tessera  |<----->|   Besu    |<->| Tessera  |
|  (public  |   | (private |       |  (public  |   | (private |
|   state)  |   |  payloads)|      |   state)  |   |  payloads)|
+-----------+   +----------+       +-----------+   +----------+
     |                                   |
     +----------- Public Chain ----------+

Besu handles consensus, public state, and block propagation. Tessera handles everything private: encrypting payloads, storing encrypted data, distributing payloads to recipient Tessera nodes, and decrypting data for authorized reads. The two components communicate over a local REST API (the Q2T interface), keeping private data off the public network entirely.

What Replaced Orion?

Tessera replaced Orion, the original private transaction manager for Besu. ConsenSys deprecated Orion in 2021 and removed it from active maintenance. According to the Besu migration documentation, Tessera is the only supported private transaction manager for Besu. If you're running an older deployment with Orion, migrating to Tessera involves exporting Orion's encrypted payloads and reimporting them into Tessera's database.

Tessera is the only supported private transaction manager for Hyperledger Besu, having replaced the deprecated Orion project (Besu documentation). Each Besu node pairs with a Tessera instance in a sidecar architecture where Besu manages public state and Tessera handles encrypted private payloads.

[INTERNAL-LINK: "QBFT consensus" -> guide to configuring QBFT for Besu networks]

How Does a Private Transaction Flow Through Besu and Tessera?

The private transaction lifecycle involves seven steps that span both the Besu and Tessera layers. Understanding this flow is essential for debugging and performance tuning. The Besu privacy documentation describes the full sequence.

Step 1: Client sends a private transaction to Besu. The transaction includes privateFrom (sender's Tessera public key), privateFor (list of recipient Tessera public keys), and the encoded function call. The restriction field is set to restricted for most enterprise use cases.

Step 2: Besu forwards the payload to its local Tessera node. Besu sends the private transaction payload to Tessera through the Q2T (Quorum-to-Tessera) REST endpoint, typically running on port 9082.

Step 3: Tessera encrypts the payload. Tessera generates a symmetric key, encrypts the payload with that key, then encrypts the symmetric key with each recipient's public key. This means only the intended recipients can decrypt.

Step 4: Tessera distributes encrypted payloads. Tessera sends the encrypted payload to each recipient's Tessera node via the P2P (peer-to-peer) endpoint. Each recipient stores the encrypted payload in its local database.

Step 5: Tessera returns a hash to Besu. After successful distribution, Tessera returns the hash of the encrypted payload to Besu.

Step 6: Besu creates a Privacy Marker Transaction (PMT). Besu wraps the hash in a public transaction addressed to the privacy precompile contract (default address: 0x000000000000000000000000000000000000007E). This PMT gets included in the next public block.

Step 7: Recipient nodes process the private transaction. When a recipient's Besu node processes the block containing the PMT, it retrieves the encrypted payload from its local Tessera instance, decrypts it, and executes the transaction against the private state database.

Non-participant nodes see the PMT on the public chain but can't decrypt the payload. They know a private transaction occurred but have no access to its contents.

What Are Privacy Marker Transactions?

A PMT is a standard Ethereum transaction that serves as a public anchor for a private transaction. It contains only the hash of the encrypted payload and is sent to the privacy precompile address. PMTs consume gas on the public chain, so you need a funded account for signing them. In Besu configuration, this is the privacy-marker-transaction-signing-key-file.

PMTs provide an interesting property: they create an immutable, public ordering of private transactions without revealing their contents. Any observer can verify that a private transaction was included in a specific block at a specific position, which is useful for audit trails.

Private transactions in Besu flow through seven steps: client submission, Tessera encryption, P2P distribution, Privacy Marker Transaction creation, and recipient decryption (Besu privacy documentation). Non-participant nodes see only the PMT hash on the public chain, preserving confidentiality while maintaining a verifiable ordering of private transactions.

How Do You Install and Configure Tessera?

Setting up Tessera involves generating encryption keys, writing a configuration file, and connecting it to your Besu node. The process requires Java 11+ for Tessera and an existing Besu network.

Step 1: Download Tessera

# Download the latest Tessera release
wget https://github.com/ConsenSys/tessera/releases/download/tessera-24.1.0/tessera-app-24.1.0-app.jar \
  -O tessera.jar
 
# Verify it runs
java -jar tessera.jar --version

Tessera runs as a standalone Java application. Each Besu node in your network needs its own Tessera instance.

Step 2: Generate Encryption Key Pairs

Each Tessera node needs a public/private key pair for encrypting and decrypting private transaction payloads.

# Generate a key pair (prompts for password, or press Enter for none)
java -jar tessera.jar -keygen -filename node1Key
 
# This creates two files:
# node1Key.pub  -- Public key (share with other participants)
# node1Key.key  -- Private key (keep secret)

For production, protect the private key using Tessera's integration with AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault. Never store unencrypted private keys on disk in production environments.

Step 3: Write the Tessera Configuration

Here's a production-ready Tessera configuration for a node in a three-node network:

{
  "mode": "orion",
  "useWhiteList": false,
  "jdbc": {
    "username": "tessera_user",
    "password": "",
    "url": "jdbc:postgresql://localhost:5432/tessera_node1",
    "autoCreateTables": true
  },
  "serverConfigs": [
    {
      "app": "ThirdParty",
      "serverAddress": "http://0.0.0.0:9081",
      "communicationType": "REST"
    },
    {
      "app": "Q2T",
      "serverAddress": "http://0.0.0.0:9082",
      "communicationType": "REST"
    },
    {
      "app": "P2P",
      "serverAddress": "http://0.0.0.0:9083",
      "communicationType": "REST",
      "sslConfig": {
        "tls": "OFF"
      }
    }
  ],
  "peer": [
    {
      "url": "http://tessera-node2:9083"
    },
    {
      "url": "http://tessera-node3:9083"
    }
  ],
  "keys": {
    "passwords": [],
    "keyData": [
      {
        "privateKeyPath": "/opt/tessera/keys/node1Key.key",
        "publicKeyPath": "/opt/tessera/keys/node1Key.pub"
      }
    ]
  },
  "alwaysSendTo": []
}

What Does Each Configuration Section Do?

mode -- Set to "orion" for compatibility with Besu's privacy API. This is the required mode for Besu integration.

jdbc -- Database configuration for storing encrypted payloads. Tessera supports H2 (embedded, good for development), PostgreSQL (recommended for production), and Oracle. H2 requires no setup but doesn't scale well. For production, use PostgreSQL.

serverConfigs -- Tessera exposes three REST APIs:

  • ThirdParty (port 9081): External-facing API for retrieving transaction payloads. Used by Besu and external applications.
  • Q2T (port 9082): Quorum-to-Tessera API. Used exclusively by the paired Besu node to submit and retrieve private payloads.
  • P2P (port 9083): Peer-to-peer API for distributing encrypted payloads between Tessera nodes.

peer -- List of other Tessera nodes in the network. Each entry needs the P2P URL of a remote Tessera instance. Tessera uses these for payload distribution and peer discovery.

keys -- References to the encryption key pair generated in Step 2. For production, use HashiCorp Vault or AWS Secrets Manager instead of file-based keys.

alwaysSendTo -- Public keys that should receive every private transaction from this node, regardless of the transaction's privateFor list. Useful for compliance or monitoring nodes that need visibility into all private transactions.

Step 4: Configure Besu for Privacy

Add privacy settings to your Besu configuration file:

# besu-config.toml
 
# Enable the privacy subsystem
privacy-enabled=true
 
# URL of the local Tessera Q2T endpoint
privacy-url="http://127.0.0.1:9082"
 
# Path to the Tessera node's public key
privacy-public-key-file="/opt/besu/tessera/node1Key.pub"
 
# Key used to sign Privacy Marker Transactions
privacy-marker-transaction-signing-key-file="/opt/besu/keys/pmt-signing.key"
 
# Enable flexible privacy groups (recommended)
privacy-flexible-groups-enabled=true

The privacy-marker-transaction-signing-key-file points to an Ethereum private key (not a Tessera key) used to sign PMTs. This account needs ETH in its balance to pay for gas on PMT transactions.

Step 5: Start the Stack

Launch Tessera first, then Besu:

# Start Tessera
java -jar tessera.jar -configfile tessera-config.json &
 
# Wait for Tessera to initialize
sleep 5
 
# Verify Tessera is running
curl -s http://localhost:9081/upcheck
# Should return "I'm up!"
 
# Start Besu with privacy enabled
besu --config-file=besu-config.toml \
  --data-path=/opt/besu/data \
  --genesis-file=/opt/besu/genesis.json \
  --rpc-http-enabled \
  --rpc-http-api=ETH,NET,PRIV,EEA \
  --host-allowlist="*"
 
# Verify privacy is enabled
curl -s -X POST --data \
  '{"jsonrpc":"2.0","method":"priv_getPrivacyPrecompileAddress","params":[],"id":1}' \
  http://localhost:8545
# Returns the privacy precompile address (0x...7E)

Notice the --rpc-http-api includes PRIV and EEA. These JSON-RPC namespaces expose the privacy-specific methods your applications will use.

Ready to deploy a privacy-enabled Besu network? Book a call with David to discuss your privacy architecture, or start deploying now with ChainLaunch.

How Do Privacy Groups Work?

Privacy groups define which nodes can access a set of private transactions and their resulting state. According to the Besu documentation, Besu supports two types: flexible privacy groups and legacy privacy groups.

What Are Flexible Privacy Groups?

Flexible privacy groups (introduced in Besu 21.x) are the recommended approach. They allow dynamic membership changes -- you can add and remove participants after the group is created. Membership is managed through an on-chain privacy group management contract.

// Create a flexible privacy group using web3js-eea
const Web3 = require("web3");
const EEAClient = require("web3-eea");
 
const web3 = new EEAClient(new Web3("http://localhost:8545"), 2018);
 
// Create privacy group with Node1 and Node2
const privacyGroupId = await web3.priv.createPrivacyGroup({
  addresses: [
    "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=", // Node1 Tessera key
    "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=", // Node2 Tessera key
  ],
  name: "SupplyChainGroup",
  description: "Private state for supply chain participants",
});
 
console.log("Privacy Group ID:", privacyGroupId);

What Are Legacy Privacy Groups?

Legacy privacy groups are implicit. Every unique combination of privateFrom and privateFor keys in a transaction creates a distinct privacy group. The group ID is deterministically derived from the participant list. You can't add or remove members from legacy groups.

// Legacy: group is implicitly defined by participants
const txHash = await web3.eea.sendRawTransaction({
  data: contractBytecode,
  privateFrom: "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=",
  privateFor: [
    "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=",
    "k2zXEin4Ip/qBGlRkJejnGWdP9cjkK+DAvKNW31L2C8=",
  ],
  restriction: "restricted",
});

Use flexible privacy groups unless you have a specific reason not to. They're more manageable in production and support membership evolution as your consortium grows.

How Do You Find and List Privacy Groups?

// Find all privacy groups this node belongs to
const groups = await web3.priv.findPrivacyGroup({
  addresses: [
    "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=",
    "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=",
  ],
});
 
groups.forEach((g) => {
  console.log(`Group: ${g.privacyGroupId}, Type: ${g.type}`);
});

Besu supports flexible and legacy privacy groups (Besu documentation). Flexible groups allow dynamic membership changes through an on-chain management contract. Legacy groups are implicitly defined by the participants in each transaction and don't support membership modification.

How Do You Deploy and Interact with Private Smart Contracts?

Private smart contracts in Besu are standard Solidity contracts deployed to a specific privacy group. They're visible and executable only by group members. The Besu private transactions documentation confirms that private contract state is maintained separately from public state on each participant node.

Deploying a Private Contract

Here's a complete example deploying an ERC-20-style token visible only to privacy group members:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
 
contract PrivateToken {
    string public name = "PrivateSupplyToken";
    string public symbol = "PST";
    uint8 public decimals = 18;
    uint256 public totalSupply;
 
    mapping(address => uint256) public balanceOf;
 
    event Transfer(address indexed from, address indexed to, uint256 value);
 
    constructor(uint256 initialSupply) {
        totalSupply = initialSupply * 10 ** uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
    }
 
    function transfer(address to, uint256 value) public returns (bool) {
        require(balanceOf[msg.sender] >= value, "Insufficient balance");
        balanceOf[msg.sender] -= value;
        balanceOf[to] += value;
        emit Transfer(msg.sender, to, value);
        return true;
    }
}

Deploy it as a private transaction:

const contractFactory = new web3.eth.Contract(PrivateTokenABI);
 
// Deploy to privacy group
const deployTx = contractFactory.deploy({
  data: PrivateTokenBytecode,
  arguments: [1000000], // initial supply
});
 
const txHash = await web3.eea.sendRawTransaction({
  data: deployTx.encodeABI(),
  privateFrom: "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=",
  privacyGroupId: privacyGroupId,
  restriction: "restricted",
  gasLimit: 3000000,
});
 
// Get the transaction receipt to find the contract address
const receipt = await web3.priv.getTransactionReceipt(txHash, publicKey);
console.log("Private contract address:", receipt.contractAddress);

Calling Private Contract Functions

Interacting with private contracts uses the same ABI encoding as public contracts, but transactions are submitted through the privacy API:

// Transfer tokens within the privacy group
const transferData = contract.methods
  .transfer("0xRecipientAddress", web3.utils.toWei("100", "ether"))
  .encodeABI();
 
const txHash = await web3.eea.sendRawTransaction({
  to: receipt.contractAddress,
  data: transferData,
  privateFrom: "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=",
  privacyGroupId: privacyGroupId,
  restriction: "restricted",
});
 
// Read private state
const balance = await web3.priv.call(privacyGroupId, {
  to: receipt.contractAddress,
  data: contract.methods.balanceOf("0xAccountAddress").encodeABI(),
});

What Are the Limitations of Private Contracts?

Private contracts operate under constraints that don't apply to public contracts:

  • Private contracts can't call public contracts. A private transaction can't invoke a function on a public contract because that would leak private state to the public chain.
  • Public contracts can't call private contracts. Public transactions have no access to private state.
  • Private contracts from different privacy groups can't interact. Each privacy group maintains its own isolated state tree.
  • Event logs from private contracts are only visible to group members. Non-participants see no events.

These restrictions are architectural, not configurable. Plan your contract architecture around them from the start.

[PERSONAL EXPERIENCE] We've found that the cleanest pattern is to use public contracts for shared reference data (asset registries, oracle feeds) and private contracts for confidential business logic (pricing, settlement). Private contracts read public reference data through off-chain application logic that bridges the two state trees.

How Does Multi-Tenancy Work?

Multi-tenancy lets multiple participants share a single Besu/Tessera node pair while maintaining strict privacy isolation between them. According to the Besu multi-tenancy documentation, this is particularly valuable for managed service providers hosting blockchain infrastructure for multiple clients.

Why Use Multi-Tenancy?

Without multi-tenancy, each participant needs its own Besu node and Tessera instance. For a consortium with 20 members, that means 20 Besu nodes plus 20 Tessera instances. Multi-tenancy lets a managed service provider run fewer node pairs while still giving each tenant isolated access to their private transactions.

Configuration Requirements

Multi-tenancy requires JWT (JSON Web Token) authentication. Each tenant authenticates with a JWT that maps to their Tessera public key.

# besu-config.toml for multi-tenancy
privacy-enabled=true
privacy-url="http://127.0.0.1:9082"
privacy-multi-tenancy-enabled=true
 
# JWT authentication
rpc-http-authentication-enabled=true
rpc-http-authentication-jwt-public-key-file="/opt/besu/jwt/public.pem"

Tessera configuration for multi-tenancy includes multiple key pairs:

{
  "keys": {
    "keyData": [
      {
        "privateKeyPath": "/opt/tessera/keys/tenant1.key",
        "publicKeyPath": "/opt/tessera/keys/tenant1.pub"
      },
      {
        "privateKeyPath": "/opt/tessera/keys/tenant2.key",
        "publicKeyPath": "/opt/tessera/keys/tenant2.pub"
      }
    ]
  }
}

The JWT token includes a privacyPublicKey claim that maps to the tenant's Tessera public key. Besu validates that the authenticated tenant can only access private transactions associated with their key. A tenant authenticated with Tenant1's key can't read Tenant2's private state.

[ORIGINAL DATA] In multi-tenant setups I've deployed, the typical infrastructure reduction is 60-70%. A 20-member consortium that would require 20 Besu+Tessera pairs can run on 6-8 shared nodes with multi-tenancy, significantly reducing compute and operational costs.

Besu multi-tenancy allows multiple participants to share a single Besu/Tessera node pair with JWT-based isolation (Besu multi-tenancy documentation). Each tenant authenticates with a JWT token mapped to their Tessera public key, ensuring strict access control over private transactions and state.

What Performance Considerations Matter for Tessera?

Privacy adds overhead. Every private transaction requires encryption, P2P distribution between Tessera nodes, and separate state execution on each participant's node. According to benchmarks published by ConsenSys (2024), private transactions on Besu typically process at 40-60% of public transaction throughput on the same network configuration.

Database Selection

Tessera stores encrypted payloads in its configured database. The choice of database significantly affects performance:

Database Use Case Throughput Notes
H2 (embedded) Development only Low No external setup needed
PostgreSQL Production recommended High Scales well, good tooling
Oracle Enterprise with existing Oracle High Requires Oracle license

For production, always use PostgreSQL. Configure connection pooling and index the payload hash column for fast lookups.

Network Topology

Tessera nodes communicate over REST. Network latency between Tessera peers directly impacts private transaction finality. If your nodes span multiple regions, expect 100-300ms added latency per private transaction compared to a single-region deployment.

Keep Besu and its paired Tessera instance on the same host or in the same local network. The Q2T communication happens frequently and should have sub-millisecond latency.

Payload Size

Large private transaction payloads (complex contract deployments, large data writes) increase encryption time, network transfer time, and storage requirements. Keep private transaction data as lean as possible. Store large documents off-chain and reference them by hash in the private contract.

For guidance on QBFT consensus configuration that affects overall network throughput, see our QBFT consensus guide.

How Do You Secure a Tessera Deployment?

Production Tessera deployments need security beyond the default configuration. Here are the critical areas.

Enable TLS Between All Components

In the default configuration, all Tessera communication is unencrypted HTTP. For production, enable TLS on all three server interfaces:

{
  "serverConfigs": [
    {
      "app": "P2P",
      "serverAddress": "https://tessera-node1:9083",
      "communicationType": "REST",
      "sslConfig": {
        "tls": "STRICT",
        "generateKeyStoreIfNotExisted": false,
        "serverKeyStore": "/opt/tessera/tls/server-keystore.jks",
        "serverKeyStorePassword": "changeit",
        "serverTrustStore": "/opt/tessera/tls/server-truststore.jks",
        "serverTrustStorePassword": "changeit",
        "serverTrustMode": "WHITELIST",
        "clientKeyStore": "/opt/tessera/tls/client-keystore.jks",
        "clientKeyStorePassword": "changeit",
        "clientTrustStore": "/opt/tessera/tls/client-truststore.jks",
        "clientTrustStorePassword": "changeit",
        "clientTrustMode": "WHITELIST",
        "knownClientsFile": "/opt/tessera/tls/known-clients",
        "knownServersFile": "/opt/tessera/tls/known-servers"
      }
    }
  ]
}

Use mutual TLS (mTLS) on the P2P interface so that only known Tessera nodes can exchange encrypted payloads. The Q2T interface should bind to localhost only since it's used exclusively by the co-located Besu node.

Use External Key Vaults

File-based keys are acceptable for development. In production, integrate Tessera with a key management system:

{
  "keys": {
    "keyVaultConfigs": [
      {
        "keyVaultType": "HASHICORP",
        "properties": {
          "url": "https://vault.example.com:8200",
          "transitEnginePath": "transit",
          "secretEnginePath": "secret",
          "approlePath": "approle",
          "tlsKeyStorePath": "/opt/tessera/vault/client.jks",
          "tlsTrustStorePath": "/opt/tessera/vault/truststore.jks"
        }
      }
    ]
  }
}

For more on key management strategies, see our guide on deploying blockchain with AWS KMS.

Restrict Network Access

  • Bind the Q2T interface (9082) to 127.0.0.1 only
  • Restrict the ThirdParty interface (9081) to known application IPs
  • Expose the P2P interface (9083) only to other Tessera nodes via firewall rules
  • Never expose any Tessera port to the public internet

Ready to deploy a privacy-enabled Besu network? Book a call with David to discuss your architecture, or start deploying now with ChainLaunch.

How Do You Troubleshoot Common Tessera Issues?

[UNIQUE INSIGHT] After deploying Besu+Tessera stacks for enterprise clients, I've found that most production issues fall into three categories: connectivity failures between Besu and Tessera, key mismatches, and state divergence between privacy group members. Here's how to diagnose each one.

Tessera Connection Refused

Symptom: Besu logs show Privacy is enabled but Tessera node is not responding or connection refused errors on port 9082.

Diagnosis:

# Check Tessera is running
curl -s http://localhost:9081/upcheck
 
# Check Q2T port is listening
netstat -tlnp | grep 9082
 
# Verify Besu's privacy-url matches Tessera's Q2T address
grep privacy-url besu-config.toml

Common fixes: Ensure Tessera starts before Besu. Verify the Q2T port matches between the Tessera config and the Besu privacy-url. Check that no firewall rules block localhost communication.

Private Transaction Not Found on Recipient

Symptom: A private transaction succeeds on the sender's node but the recipient's priv_getTransactionReceipt returns null.

Diagnosis:

# Check P2P connectivity between Tessera nodes
curl -s http://tessera-node2:9083/upcheck
 
# Verify the recipient's Tessera public key matches what the sender used
cat /opt/tessera/keys/node2Key.pub
 
# Check Tessera logs for distribution errors
grep -i "error\|fail\|exception" tessera.log

Common fixes: Verify the recipient's Tessera public key matches the privateFor value in the transaction. Check P2P connectivity between Tessera nodes. Ensure the recipient's Tessera database isn't full.

State Divergence Between Participants

Symptom: Two nodes in the same privacy group return different results for the same priv_call.

Diagnosis:

# Compare state roots
curl -X POST --data '{
  "jsonrpc":"2.0",
  "method":"priv_debugGetStateRoot",
  "params":["PRIVACY_GROUP_ID","latest"],
  "id":1
}' http://node1:8545
 
# Run the same on node2 and compare

Common fixes: State divergence usually means one node missed a private transaction. Resync the affected node by resending the missing transactions or, in severe cases, by rebuilding the private state from the PMT chain.

FAQ

Can private contracts interact with public contracts?

No. Private contracts can't call public contracts and vice versa. This is an architectural constraint in Besu's privacy model. A private transaction can't modify public state because that would expose the existence of private logic to all network participants. The recommended workaround is to use application-layer bridging: read public state off-chain, pass it to private transactions as function parameters, and update public state through separate public transactions.

What is the performance overhead of Tessera privacy?

Private transactions on Besu typically achieve 40-60% of public transaction throughput on the same hardware, according to ConsenSys benchmarks (2024). The overhead comes from three sources: payload encryption/decryption, Tessera-to-Tessera P2P distribution, and separate private state execution. Database choice matters significantly -- PostgreSQL outperforms H2 by 3-5x under production loads.

Can I use Tessera with public Ethereum?

No. Tessera is designed exclusively for private/permissioned Besu networks. Public Ethereum has no concept of privacy groups or private transaction managers. If you need privacy on public Ethereum, look at zero-knowledge rollups or similar layer-2 solutions. For more on ZKPs in enterprise context, see our zero-knowledge proofs guide.

How do I back up private transaction data?

Back up both the Tessera database and the Tessera encryption keys. Without the keys, you can't decrypt the stored payloads. For PostgreSQL-backed Tessera, use standard pg_dump for the database. Store encryption key backups in a separate key management system with strict access controls. If you lose the Tessera database but still have the keys, you can reconstruct private state from the PMT chain by replaying the privacy precompile transactions.

How does Tessera compare to Fabric's private data collections?

Tessera encrypts and distributes entire transaction payloads to specified participants. Fabric PDCs store private data in a separate SideDB and commit only a hash to the shared ledger. Fabric offers more granular data control (field-level privacy) while Tessera operates at the transaction level. Fabric's gossip-based distribution is peer-to-peer like Tessera's P2P, but Fabric doesn't require a separate process (Tessera) -- it's built into the peer. For a full comparison, read our blockchain privacy comparison.

Can I change the members of a privacy group after creation?

With flexible privacy groups (recommended), yes. You can add and remove members through the on-chain privacy group management contract. Legacy privacy groups are immutable -- any membership change creates a new group with a new ID and a new state. Flexible groups maintain state continuity across membership changes, making them far more practical for evolving consortiums.

Conclusion

Tessera gives Hyperledger Besu a production-grade privacy layer that keeps confidential data off the public chain while maintaining verifiable transaction ordering through Privacy Marker Transactions. The sidecar architecture -- one Tessera instance per Besu node -- adds operational complexity, but it cleanly separates public and private concerns.

The key decisions for your deployment: choose flexible privacy groups over legacy for membership flexibility. Use PostgreSQL for Tessera's database in production. Enable TLS on all Tessera interfaces. Integrate with an external key vault. And plan your contract architecture around the constraint that private and public contracts can't interact directly.

For teams evaluating privacy approaches, Tessera works well for EVM-based use cases where you need Solidity compatibility and Ethereum tooling. If you need more granular field-level privacy or ledger-level isolation, Fabric's private data collections may be a better fit. Our blockchain privacy comparison covers the trade-offs in detail.


David Viejo is the founder of ChainLaunch and the creator of Bevel Operator Fabric (Hyperledger Foundation). He has spent over six years building blockchain infrastructure tooling for enterprise teams.

Related guides: Deploy a Besu Network in 2 Minutes | Blockchain Privacy: Fabric vs Besu vs Corda | QBFT Consensus Guide | GDPR-Compliant Blockchain Guide | Fabric Private Data Collections Tutorial

Related Articles

Ready to Transform Your Blockchain Workflow?

Deploy Fabric & Besu in minutes, not weeks. AI-powered chaincode, real-time monitoring, and enterprise security with Vault.

ChainLaunch Pro   Includes premium support, unlimited networks, advanced AI tools, and priority updates.

David Viejo, founder of ChainLaunch

Talk to David Viejo

Founder & CTO · 6+ years blockchain · Responds within 24h

Questions? Contact us at support@chainlaunch.dev