Troubleshooting

Common issues and solutions for Opacus Protocol.

Connection Issues

❌ Cannot Connect to Gateway

Error Message

Error: Failed to connect to gateway at wss://gateway.opacus.ai
WebSocket connection failed: ECONNREFUSED

Possible Causes:

Solutions:

// 1. Check gateway status
curl https://gateway.opacus.ai/health
// Expected: { "status": "ok", "version": "1.0.0" }

// 2. Verify WebSocket connectivity
wscat -c wss://gateway.opacus.ai
// Should connect without errors

// 3. Try alternative protocol
const client = new OpacusClient({
  gateway: 'gateway.opacus.ai',
  protocol: 'websocket'  // Force WebSocket instead of WebTransport
});

// 4. Check firewall rules
// Allow outbound connections on ports 443 (WSS) and 4433 (QUIC)

❌ Connection Drops Frequently

Solutions:

// TypeScript: Enable keepalive
const client = new OpacusClient({
  gateway: 'gateway.opacus.ai',
  keepalive: true,
  keepaliveInterval: 30000,  // 30 seconds
  reconnect: true,
  maxReconnectAttempts: 5
});

// Handle reconnection
client.on('reconnect', (attempt) => {
  console.log(`Reconnecting (attempt ${attempt})...`);
});

client.on('reconnected', () => {
  console.log('Connection restored');
});
// Rust: Configure keepalive
let config = OpacusConfig {
    gateway_url: "gateway.opacus.ai".to_string(),
    keep_alive_interval: Duration::from_secs(30),
    max_idle_timeout: Duration::from_secs(120),
    ..Default::default()
};

let client = OpacusClient::new(config).await?;

Authentication & Identity

❌ Agent Registration Fails

Error Message

Error: Transaction reverted: Agent already exists

Solution:

// Check if agent already registered
const agentId = keccak256(abi.encodePacked(edPublicKey, xPublicKey));
const agent = await agentRegistry.getAgent(agentId);

if (agent.active) {
  console.log('Agent already registered:', agentId);
  // Use existing agent
} else {
  // Register new agent
  await agentRegistry.registerAgent(metadata, edPublicKey, xPublicKey);
}

❌ Signature Verification Failed

Error Message

Error: Invalid signature: Ed25519 verification failed

Possible Causes:

Solution:

// TypeScript: Verify signature locally first
import { ed25519 } from '@noble/curves/ed25519';

const message = 'Hello, world!';
const messageBytes = new TextEncoder().encode(message);

// Sign
const signature = ed25519.sign(messageBytes, privateKey);

// Verify immediately
const valid = ed25519.verify(signature, messageBytes, publicKey);
console.log('Signature valid:', valid);

// Common mistake: signing different format
// ❌ Wrong:
const sig1 = ed25519.sign(message, privateKey);  // String

// ✅ Correct:
const sig2 = ed25519.sign(messageBytes, privateKey);  // Uint8Array

❌ Public Key Not Found On-Chain

Solution:

// Check agent registration status
const agentId = '0x...';

try {
  const agent = await agentRegistry.getAgent(agentId);
  
  if (!agent.active) {
    console.error('Agent is deactivated');
    // Reactivate if you own it
    await agentRegistry.reactivateAgent(agentId);
  }
  
  console.log('Public keys:', {
    ed25519: agent.edPublicKey,
    x25519: agent.xPublicKey
  });
} catch (err) {
  console.error('Agent not found:', agentId);
  // Register the agent first
}

Encryption & Decryption

❌ Decryption Failed

Error Message

Error: Decryption failed: authentication tag mismatch

Possible Causes:

Solution:

// TypeScript: Debug encryption/decryption
import { x25519 } from '@noble/curves/ed25519';

// Sender side
const alicePrivate = x25519.utils.randomPrivateKey();
const alicePublic = x25519.getPublicKey(alicePrivate);

const bobPublic = await getRecipientPublicKey(bobAgentId);

// Derive shared secret
const sharedSecret = x25519.scalarMult(alicePrivate, bobPublic);
console.log('Shared secret (sender):', Buffer.from(sharedSecret).toString('hex'));

// Recipient side
const bobPrivate = x25519.utils.randomPrivateKey();
const bobPublic2 = x25519.getPublicKey(bobPrivate);

// Derive same shared secret
const sharedSecret2 = x25519.scalarMult(bobPrivate, alicePublic);
console.log('Shared secret (recipient):', Buffer.from(sharedSecret2).toString('hex'));

// ✅ Both should match!
assert(Buffer.from(sharedSecret).equals(Buffer.from(sharedSecret2)));

❌ Key Derivation Mismatch

// Ensure consistent key derivation
import { hkdf } from '@noble/hashes/hkdf';
import { sha256 } from '@noble/hashes/sha256';

function deriveMessageKey(
  sharedSecret: Uint8Array,
  messageId: string,
  keyLength: number = 32
): Uint8Array {
  const salt = new Uint8Array(0);  // Empty salt
  const info = new TextEncoder().encode(`opacus-v1-${messageId}`);
  
  return hkdf(sha256, sharedSecret, salt, info, keyLength);
}

// ✅ Both sender and recipient must use same parameters
const key1 = deriveMessageKey(secret, 'msg-123');
const key2 = deriveMessageKey(secret, 'msg-123');
// key1 === key2 ✓

Smart Contract Issues

❌ Transaction Reverted

Error Message

Error: Transaction reverted without a reason string

Solutions:

// 1. Estimate gas before sending
const gasEstimate = await contract.estimateGas.registerAgent(
  metadata,
  edPublicKey,
  xPublicKey
);

console.log('Estimated gas:', gasEstimate.toString());

// Add 20% buffer
const gasLimit = gasEstimate * 120n / 100n;

// 2. Send with explicit gas limit
const tx = await contract.registerAgent(
  metadata,
  edPublicKey,
  xPublicKey,
  { gasLimit }
);

// 3. Debug with hardhat node
// Run local node: npx hardhat node
// Deploy to local: npx hardhat run scripts/deploy.ts --network localhost
// Transaction errors will show detailed stack traces

❌ Insufficient Gas

// Check wallet balance
const balance = await provider.getBalance(wallet.address);
console.log('Balance:', ethers.formatEther(balance), '0G');

if (balance < ethers.parseEther('0.01')) {
  console.error('Insufficient balance. Need at least 0.01 0G');
  // Get testnet tokens from faucet
  console.log('Faucet: https://faucet.0g.ai');
}

❌ Nonce Too Low

Error Message

Error: nonce has already been used

Solution:

// Get latest nonce
const nonce = await provider.getTransactionCount(wallet.address, 'latest');

// Send with explicit nonce
const tx = await contract.registerAgent(
  metadata,
  edPublicKey,
  xPublicKey,
  { nonce }
);

// Or reset local cache
// Hardhat: npx hardhat clean
// Ethers: create new provider instance

Message Delivery

❌ Messages Not Being Received

Debug Checklist:

// 1. Verify recipient is connected
const recipientOnline = await client.isAgentOnline(recipientId);
console.log('Recipient online:', recipientOnline);

// 2. Check message was sent
const messageId = await client.sendMessage({
  to: recipientId,
  content: 'Test'
});
console.log('Message sent:', messageId);

// 3. Verify delivery
const status = await client.getMessageStatus(messageId);
console.log('Message status:', status);
// Possible: 'pending', 'delivered', 'failed'

// 4. Check recipient's message handler
client.onMessage((msg) => {
  console.log('Received message:', msg);
  // Make sure this handler is registered!
});

❌ Message Order Not Preserved

Solution:

// Use message sequencing
let messageSequence = 0;

async function sendOrdered(to: string, content: string) {
  const seq = messageSequence++;
  
  await client.sendMessage({
    to,
    content,
    metadata: { sequence: seq }
  });
}

// Recipient side: reorder messages
const messageBuffer = new Map();
let expectedSeq = 0;

client.onMessage((msg) => {
  const seq = msg.metadata.sequence;
  
  if (seq === expectedSeq) {
    processMessage(msg);
    expectedSeq++;
    
    // Process buffered messages
    while (messageBuffer.has(expectedSeq)) {
      processMessage(messageBuffer.get(expectedSeq)!);
      messageBuffer.delete(expectedSeq);
      expectedSeq++;
    }
  } else {
    // Buffer out-of-order message
    messageBuffer.set(seq, msg);
  }
});

Performance Issues

❌ High Latency

Diagnose:

// Measure end-to-end latency
const start = performance.now();

const messageId = await client.sendMessage({
  to: recipientId,
  content: 'Ping'
});

// Wait for delivery confirmation
await client.waitForDelivery(messageId);

const latency = performance.now() - start;
console.log('Round-trip latency:', latency, 'ms');

// Expected: <10ms on good network
// If >100ms, check:
// - Network connectivity (ping gateway)
// - Gateway load (check /metrics endpoint)
// - Message size (large messages take longer)

Solutions:

❌ Low Throughput

// Measure throughput
const count = 1000;
const start = Date.now();

for (let i = 0; i < count; i++) {
  await client.sendMessage({
    to: recipientId,
    content: `Message ${i}`
  });
}

const duration = (Date.now() - start) / 1000;
const throughput = count / duration;

console.log('Throughput:', throughput.toFixed(0), 'msg/s');

// Expected: >1000 msg/s
// If lower, try:
// - Batch messages
// - Use parallel processing
// - Increase connection pool size

Development & Debugging

Enable Debug Logging

// TypeScript: Enable verbose logging
const client = new OpacusClient({
  gateway: 'gateway.opacus.ai',
  logLevel: 'debug'
});

// Logs will show:
// [DEBUG] Connecting to gateway...
// [DEBUG] WebSocket opened
// [DEBUG] Sending message: {...}
// [DEBUG] Received message: {...}
// Rust: Enable tracing
use tracing_subscriber;

tracing_subscriber::fmt()
    .with_max_level(tracing::Level::DEBUG)
    .init();

// Or set environment variable
// RUST_LOG=debug cargo run

Test in Local Environment

# Start local 0G node
git clone https://github.com/0glabs/0g-chain
cd 0g-chain
make install
0gchaind start

# Deploy contracts locally
npx hardhat run scripts/deploy.ts --network localhost

# Start local gateway
cd gateway
cargo run -- --config config.local.toml

# Run SDK tests
npm test  # TypeScript
cargo test  # Rust

Common Error Codes

Error Code Description Solution
ECONNREFUSED Connection refused Check gateway URL and firewall
ETIMEOUT Connection timeout Check network connectivity
INVALID_SIGNATURE Signature verification failed Verify keys and message format
AGENT_NOT_FOUND Agent not registered Register agent on-chain first
INSUFFICIENT_GAS Not enough gas for transaction Add more 0G tokens to wallet
NONCE_TOO_LOW Transaction nonce already used Get latest nonce or reset cache
DECRYPTION_FAILED Cannot decrypt message Check keys and encryption params
RATE_LIMITED Too many requests Implement backoff and retry

Getting Help

💬

Community Support

Get help from the community
GitHub Discussions

🐛

GitHub Issues

Report bugs
github.com/Opacus-xyz/Opacus

📚

Documentation

Read the docs
docs.opacus.ai

✉️

Email Support

Contact us
support@opacus.ai

FAQ

Q: Which SDK should I use?

A: Use TypeScript SDK for web/Node.js applications. Use Rust SDK for high-performance server applications or when you need QUIC protocol.

Q: How much do transactions cost?

A: Registration costs ~0.0001 0G, attestations ~0.00008 0G. Message routing is off-chain and free (only gateway bandwidth costs).

Q: Can I use testnet for production?

A: No, testnet is for development only. Testnet can be reset without notice. Use mainnet for production applications.

Q: How do I rotate keys?

A: Generate new keypair, update on-chain via agentRegistry.updateKeys(), revoke old attestations, create new ones.

Q: What happens if gateway goes down?

A: SDKs will automatically reconnect. Messages are queued during downtime. Use multiple gateway URLs for redundancy.

Q: How private are my messages?

A: Messages are end-to-end encrypted. Gateways cannot read content. Only sender and recipient can decrypt. See Security Model.

🎯 Still Stuck?

If you can't find a solution here:

Related Documentation