/ CAFF_NODE_INTEGRATION_PLAN.md
CAFF_NODE_INTEGRATION_PLAN.md
   1  # Caff Node Integration Plan for ComposableScan
   2  
   3  ## ๐ŸŽฏ Goal
   4  
   5  Integrate **Caff Node** functionality into ComposableScan to enable:
   6  1. **Bi-directional Transaction Mapping**: 0x (Ethereum) โ†” TX~ (Espresso)
   7  2. **Block State Correlation**: L2 blocks โ†” Espresso blocks
   8  3. **Transaction Confirmation**: Espresso confirmation status for L2 transactions
   9  4. **Enhanced Search**: Search by 0x transaction hash
  10  
  11  **Key Principle**: **NO breaking changes, minimal new files, leverage existing structure**
  12  
  13  ---
  14  
  15  ## ๐Ÿ“‹ Understanding Caff Node
  16  
  17  ### What is a Caff Node?
  18  
  19  A **Caff Node** (Caffeinated Node) is a full node of an L2 chain (e.g., Rari) that reads confirmations from the Espresso Network.
  20  
  21  ```
  22  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  23  โ”‚                  Caff Node Architecture                     โ”‚
  24  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
  25  โ”‚                                                             โ”‚
  26  โ”‚  L2 Transaction (Rari)                                      โ”‚
  27  โ”‚         โ”‚                                                   โ”‚
  28  โ”‚         โ”‚ 1. Submitted to L2 Sequencer                      โ”‚
  29  โ”‚         โ–ผ                                                   โ”‚
  30  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                      โ”‚
  31  โ”‚  โ”‚ L2 Sequencer     โ”‚                                      โ”‚
  32  โ”‚  โ”‚ โ€ข Collects txs   โ”‚                                      โ”‚
  33  โ”‚  โ”‚ โ€ข Orders txs     โ”‚                                      โ”‚
  34  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                      โ”‚
  35  โ”‚           โ”‚ 2. Submits to Espresso                         โ”‚
  36  โ”‚           โ–ผ                                                โ”‚
  37  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                      โ”‚
  38  โ”‚  โ”‚ Espresso Network โ”‚                                      โ”‚
  39  โ”‚  โ”‚ โ€ข HotShot        โ”‚                                      โ”‚
  40  โ”‚  โ”‚ โ€ข Confirmation   โ”‚  (6-8 seconds)                       โ”‚
  41  โ”‚  โ”‚ โ€ข DA Layer       โ”‚                                      โ”‚
  42  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                      โ”‚
  43  โ”‚           โ”‚ 3. Espresso confirmation                       โ”‚
  44  โ”‚           โ–ผ                                                โ”‚
  45  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                      โ”‚
  46  โ”‚  โ”‚ Caff Node        โ”‚                                      โ”‚
  47  โ”‚  โ”‚ โ€ข Full L2 node   โ”‚                                      โ”‚
  48  โ”‚  โ”‚ โ€ข Reads Espresso โ”‚                                      โ”‚
  49  โ”‚  โ”‚ โ€ข Derives state  โ”‚                                      โ”‚
  50  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                      โ”‚
  51  โ”‚           โ”‚ 4. RPC interface (standard Ethereum JSON-RPC) โ”‚
  52  โ”‚           โ–ผ                                                โ”‚
  53  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                      โ”‚
  54  โ”‚  โ”‚ Application      โ”‚                                      โ”‚
  55  โ”‚  โ”‚ โ€ข eth_getBlock   โ”‚                                      โ”‚
  56  โ”‚  โ”‚ โ€ข eth_getTx      โ”‚                                      โ”‚
  57  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                      โ”‚
  58  โ”‚                                                             โ”‚
  59  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  60  ```
  61  
  62  ### Caff Node API
  63  
  64  **Endpoint**: `https://rari.caff.testnet.espresso.network`
  65  
  66  **Interface**: Standard Ethereum JSON-RPC
  67  
  68  **Key Methods**:
  69  ```json
  70  {
  71    "jsonrpc": "2.0",
  72    "method": "eth_getBlockByNumber",
  73    "params": ["0x32c9dc", true],
  74    "id": 1
  75  }
  76  
  77  {
  78    "jsonrpc": "2.0",
  79    "method": "eth_getTransactionByHash",
  80    "params": ["0xaa6594b9083eea574c350021c592c3dd6c93e83d..."],
  81    "id": 1
  82  }
  83  
  84  {
  85    "jsonrpc": "2.0",
  86    "method": "eth_blockNumber",
  87    "params": [],
  88    "id": 1
  89  }
  90  ```
  91  
  92  **Response Format**:
  93  ```json
  94  {
  95    "jsonrpc": "2.0",
  96    "id": 1,
  97    "result": {
  98      "number": "0x32c9dc",
  99      "hash": "0xe5c35783a15739fd6f7a21aec9c69c78758a1512...",
 100      "parentHash": "0xfdd541a33db9a46f4133b23669cdede69011cd27...",
 101      "timestamp": "0x66c5d38d",
 102      "transactions": [
 103        {
 104          "hash": "0xaa6594b9083eea574c350021c592c3dd6c93e83d...",
 105          "from": "0x...",
 106          "to": "0x...",
 107          "value": "0x...",
 108          "blockNumber": "0x32c9dc",
 109          "blockHash": "0xe5c35783a15739fd6f7a21aec9c69c78758a1512...",
 110          "transactionIndex": "0x0"
 111        }
 112      ]
 113    }
 114  }
 115  ```
 116  
 117  ---
 118  
 119  ## ๐Ÿ” Correlation Strategy: 0x โ†” TX~
 120  
 121  ### Challenge
 122  
 123  - **Espresso Network** uses: `TX~<base64>` format (44 chars)
 124  - **L2 (Rari)** uses: `0x<hex>` format (66 chars)
 125  - **No direct hash mapping** between the two formats
 126  
 127  ### Correlation Approaches
 128  
 129  #### Approach 1: **Timestamp + Block Height Correlation** (Primary)
 130  
 131  **Logic**:
 132  ```
 133  Given: L2 transaction with timestamp T and block B
 134  1. Query Espresso blocks around timestamp T (ยฑ30 minutes)
 135  2. For each Espresso block:
 136     - Get namespace transactions (e.g., RARI = 1380012617)
 137     - Check transaction count, size, offset
 138  3. Match by:
 139     - Timestamp proximity (< 5 min diff)
 140     - Transaction count in block
 141     - Transaction size
 142     - Transaction index/offset
 143  ```
 144  
 145  **Confidence Scoring**:
 146  ```typescript
 147  interface CorrelationMatch {
 148    espresso_tx: string;      // TX~...
 149    l2_tx: string;            // 0x...
 150    confidence: number;       // 0.0 - 1.0
 151    matching_factors: {
 152      timestamp_diff: number;  // seconds
 153      block_size_match: boolean;
 154      index_match: boolean;
 155      namespace_match: boolean;
 156    }
 157  }
 158  
 159  // Confidence calculation
 160  confidence = 1.0
 161  if (timestamp_diff > 60) confidence -= 0.2    // > 1 min
 162  if (timestamp_diff > 180) confidence -= 0.3   // > 3 min
 163  if (!block_size_match) confidence -= 0.2
 164  if (!index_match) confidence -= 0.1
 165  if (!namespace_match) confidence -= 0.3
 166  ```
 167  
 168  #### Approach 2: **Block Number Mapping** (Secondary)
 169  
 170  **Logic**:
 171  ```
 172  Given: L2 block number N
 173  1. Query Rari block N โ†’ get timestamp T
 174  2. Query Espresso blocks with timestamp โ‰ˆ T
 175  3. Find Espresso block with RARI namespace transactions
 176  4. Match all transactions in sequence
 177  ```
 178  
 179  **Mapping Structure**:
 180  ```typescript
 181  interface BlockMapping {
 182    l2_block: number;
 183    l2_timestamp: number;
 184    espresso_block: number;
 185    espresso_timestamp: number;
 186    namespace: number;
 187    tx_count: number;
 188    correlation_quality: 'high' | 'medium' | 'low';
 189  }
 190  ```
 191  
 192  #### Approach 3: **Transaction Metadata Matching** (Tertiary)
 193  
 194  **Logic**:
 195  ```
 196  Match by transaction characteristics:
 197  - Transaction size (bytes)
 198  - Gas used
 199  - Value transferred
 200  - Contract interaction
 201  ```
 202  
 203  **Limitations**:
 204  - Requires detailed transaction data
 205  - Multiple transactions may have similar characteristics
 206  - Only usable as supporting evidence
 207  
 208  ---
 209  
 210  ## ๐Ÿ—๏ธ Implementation Plan
 211  
 212  ### Phase 1: Add Caff Node Client (No Breaking Changes)
 213  
 214  **New File**: `src/services/caff/client.ts`
 215  
 216  ```typescript
 217  // src/services/caff/client.ts
 218  
 219  interface CaffNodeConfig {
 220    rpcUrl: string;
 221    timeout: number;
 222  }
 223  
 224  interface EthereumTransaction {
 225    hash: string;                // 0x...
 226    blockNumber: string;         // 0x... (hex)
 227    blockHash: string;           // 0x...
 228    from: string;
 229    to: string;
 230    value: string;
 231    gas: string;
 232    gasPrice: string;
 233    input: string;
 234    nonce: string;
 235    transactionIndex: string;
 236    timestamp?: number;          // Added from block
 237  }
 238  
 239  interface EthereumBlock {
 240    number: string;              // 0x... (hex)
 241    hash: string;                // 0x...
 242    parentHash: string;
 243    timestamp: string;           // 0x... (hex)
 244    transactions: EthereumTransaction[];
 245    gasUsed: string;
 246    gasLimit: string;
 247    size: string;
 248  }
 249  
 250  class CaffNodeClient {
 251    private config: CaffNodeConfig;
 252    private requestId: number = 1;
 253  
 254    constructor(config: CaffNodeConfig) {
 255      this.config = config;
 256    }
 257  
 258    private async jsonRpcCall(method: string, params: any[]): Promise<any> {
 259      const response = await fetch(this.config.rpcUrl, {
 260        method: 'POST',
 261        headers: { 'Content-Type': 'application/json' },
 262        body: JSON.stringify({
 263          jsonrpc: '2.0',
 264          method,
 265          params,
 266          id: this.requestId++
 267        })
 268      });
 269  
 270      if (!response.ok) {
 271        throw new Error(`Caff Node request failed: ${response.statusText}`);
 272      }
 273  
 274      const data = await response.json();
 275      if (data.error) {
 276        throw new Error(`Caff Node error: ${data.error.message}`);
 277      }
 278  
 279      return data.result;
 280    }
 281  
 282    async getBlockNumber(): Promise<number> {
 283      const result = await this.jsonRpcCall('eth_blockNumber', []);
 284      return parseInt(result, 16);
 285    }
 286  
 287    async getBlockByNumber(blockNumber: number | 'latest', fullTx: boolean = true): Promise<EthereumBlock> {
 288      const blockParam = blockNumber === 'latest' ? 'latest' : `0x${blockNumber.toString(16)}`;
 289      return await this.jsonRpcCall('eth_getBlockByNumber', [blockParam, fullTx]);
 290    }
 291  
 292    async getBlockByHash(blockHash: string, fullTx: boolean = true): Promise<EthereumBlock> {
 293      return await this.jsonRpcCall('eth_getBlockByHash', [blockHash, fullTx]);
 294    }
 295  
 296    async getTransactionByHash(txHash: string): Promise<EthereumTransaction> {
 297      return await this.jsonRpcCall('eth_getTransactionByHash', [txHash]);
 298    }
 299  
 300    async getTransactionReceipt(txHash: string): Promise<any> {
 301      return await this.jsonRpcCall('eth_getTransactionReceipt', [txHash]);
 302    }
 303  }
 304  
 305  // Singleton instance
 306  let caffNodeClient: CaffNodeClient | null = null;
 307  
 308  export function getCaffNodeClient(network: 'mainnet' | 'testnet' = 'testnet'): CaffNodeClient {
 309    if (!caffNodeClient) {
 310      const rpcUrl = network === 'testnet'
 311        ? 'https://rari.caff.testnet.espresso.network'
 312        : 'https://rari.caff.mainnet.espresso.network'; // Update when available
 313  
 314      caffNodeClient = new CaffNodeClient({
 315        rpcUrl,
 316        timeout: 30000
 317      });
 318    }
 319    return caffNodeClient;
 320  }
 321  
 322  export { CaffNodeClient, type EthereumTransaction, type EthereumBlock };
 323  ```
 324  
 325  ---
 326  
 327  ### Phase 2: Add Correlation Service (Minimal New Code)
 328  
 329  **New File**: `src/services/correlation/mapper.ts`
 330  
 331  ```typescript
 332  // src/services/correlation/mapper.ts
 333  
 334  import { getCaffNodeClient, type EthereumTransaction } from '../caff/client';
 335  import { getBlock, getNamespaceTransactions } from '../api/main';
 336  import { getLatestBlockHeight } from '../api/discovery';
 337  import type { EspressoTransaction } from '@/types/espresso';
 338  
 339  interface CorrelationMatch {
 340    espresso_tx: string;
 341    espresso_block: number;
 342    l2_tx: string;
 343    l2_block: number;
 344    confidence: number;
 345    timestamp_diff: number;
 346    factors: {
 347      timestamp_match: boolean;
 348      index_match: boolean;
 349      size_match: boolean;
 350    };
 351  }
 352  
 353  interface CorrelationResult {
 354    found: boolean;
 355    matches: CorrelationMatch[];
 356    search_range: {
 357      from_block: number;
 358      to_block: number;
 359      blocks_searched: number;
 360    };
 361  }
 362  
 363  /**
 364   * Find Espresso transaction(s) corresponding to L2 transaction
 365   */
 366  export async function correlateL2ToEspresso(
 367    l2TxHash: string,
 368    namespace: number = 1380012617 // Default: RARI
 369  ): Promise<CorrelationResult> {
 370    const caffClient = getCaffNodeClient();
 371    
 372    // 1. Get L2 transaction details
 373    const l2Tx = await caffClient.getTransactionByHash(l2TxHash);
 374    if (!l2Tx) {
 375      return { found: false, matches: [], search_range: { from_block: 0, to_block: 0, blocks_searched: 0 } };
 376    }
 377    
 378    const l2BlockNum = parseInt(l2Tx.blockNumber, 16);
 379    const l2Block = await caffClient.getBlockByNumber(l2BlockNum);
 380    const l2Timestamp = parseInt(l2Block.timestamp, 16);
 381    
 382    // 2. Calculate Espresso search range (ยฑ30 minutes)
 383    const timeWindow = 30 * 60; // 30 minutes in seconds
 384    const searchStart = l2Timestamp - timeWindow;
 385    const searchEnd = l2Timestamp + timeWindow;
 386    
 387    // 3. Get current Espresso block height
 388    const currentHeight = await getLatestBlockHeight();
 389    
 390    // 4. Binary search for Espresso blocks in time range
 391    const espressoBlocks = await findBlocksInTimeRange(searchStart, searchEnd, currentHeight);
 392    
 393    // 5. Search each block for namespace transactions
 394    const matches: CorrelationMatch[] = [];
 395    
 396    for (const espressoBlockHeight of espressoBlocks) {
 397      const namespaceData = await getNamespaceTransactions(espressoBlockHeight, namespace);
 398      if (!namespaceData || !namespaceData.transactions) continue;
 399      
 400      // 6. Match transactions by characteristics
 401      for (let i = 0; i < namespaceData.transactions.length; i++) {
 402        const espressoTx = namespaceData.transactions[i];
 403        
 404        const timeDiff = Math.abs(l2Timestamp - (espressoTx.timestamp || 0));
 405        const indexMatch = parseInt(l2Tx.transactionIndex, 16) === i;
 406        
 407        // Calculate confidence
 408        let confidence = 1.0;
 409        if (timeDiff > 60) confidence -= 0.2;
 410        if (timeDiff > 180) confidence -= 0.3;
 411        if (!indexMatch) confidence -= 0.1;
 412        
 413        if (confidence > 0.5) {
 414          matches.push({
 415            espresso_tx: espressoTx.hash,
 416            espresso_block: espressoBlockHeight,
 417            l2_tx: l2TxHash,
 418            l2_block: l2BlockNum,
 419            confidence,
 420            timestamp_diff: timeDiff,
 421            factors: {
 422              timestamp_match: timeDiff < 60,
 423              index_match: indexMatch,
 424              size_match: true // Placeholder
 425            }
 426          });
 427        }
 428      }
 429    }
 430    
 431    // Sort by confidence
 432    matches.sort((a, b) => b.confidence - a.confidence);
 433    
 434    return {
 435      found: matches.length > 0,
 436      matches,
 437      search_range: {
 438        from_block: espressoBlocks[0] || 0,
 439        to_block: espressoBlocks[espressoBlocks.length - 1] || 0,
 440        blocks_searched: espressoBlocks.length
 441      }
 442    };
 443  }
 444  
 445  /**
 446   * Find Espresso blocks within a timestamp range
 447   * Uses binary search for efficiency
 448   */
 449  async function findBlocksInTimeRange(
 450    startTime: number,
 451    endTime: number,
 452    maxHeight: number
 453  ): Promise<number[]> {
 454    const blocks: number[] = [];
 455    
 456    // Binary search for start block
 457    let low = Math.max(1, maxHeight - 10000); // Search last 10k blocks
 458    let high = maxHeight;
 459    
 460    while (low <= high) {
 461      const mid = Math.floor((low + high) / 2);
 462      const block = await getBlock(mid);
 463      
 464      if (!block) break;
 465      
 466      if (block.timestamp < startTime) {
 467        low = mid + 1;
 468      } else if (block.timestamp > endTime) {
 469        high = mid - 1;
 470      } else {
 471        // Found block in range, expand search
 472        blocks.push(mid);
 473        
 474        // Search backward
 475        for (let i = mid - 1; i >= low; i--) {
 476          const prevBlock = await getBlock(i);
 477          if (!prevBlock || prevBlock.timestamp < startTime) break;
 478          blocks.unshift(i);
 479        }
 480        
 481        // Search forward
 482        for (let i = mid + 1; i <= high; i++) {
 483          const nextBlock = await getBlock(i);
 484          if (!nextBlock || nextBlock.timestamp > endTime) break;
 485          blocks.push(i);
 486        }
 487        
 488        break;
 489      }
 490    }
 491    
 492    return blocks;
 493  }
 494  
 495  /**
 496   * Reverse lookup: Find L2 transaction from Espresso transaction
 497   */
 498  export async function correlateEspressoToL2(
 499    espressoTxHash: string,
 500    namespace: number = 1380012617
 501  ): Promise<CorrelationResult> {
 502    // Get Espresso transaction
 503    const espressoTx = await getTransactionByHash(espressoTxHash);
 504    if (!espressoTx) {
 505      return { found: false, matches: [], search_range: { from_block: 0, to_block: 0, blocks_searched: 0 } };
 506    }
 507    
 508    const espressoBlockHeight = espressoTx.block_height;
 509    const espressoTimestamp = espressoTx.timestamp || 0;
 510    
 511    // Get Espresso block
 512    const espressoBlock = await getBlock(espressoBlockHeight);
 513    if (!espressoBlock) {
 514      return { found: false, matches: [], search_range: { from_block: 0, to_block: 0, blocks_searched: 0 } };
 515    }
 516    
 517    // Calculate L2 search range
 518    const timeWindow = 30 * 60;
 519    const caffClient = getCaffNodeClient();
 520    const currentL2Block = await caffClient.getBlockNumber();
 521    
 522    // Search L2 blocks around the timestamp
 523    const matches: CorrelationMatch[] = [];
 524    const searchRange = 500; // Search ยฑ500 blocks
 525    
 526    for (let i = Math.max(1, currentL2Block - searchRange); i <= currentL2Block + searchRange; i++) {
 527      const l2Block = await caffClient.getBlockByNumber(i);
 528      if (!l2Block) continue;
 529      
 530      const l2Timestamp = parseInt(l2Block.timestamp, 16);
 531      const timeDiff = Math.abs(espressoTimestamp - l2Timestamp);
 532      
 533      if (timeDiff > timeWindow) continue;
 534      
 535      // Check transactions
 536      for (const l2Tx of l2Block.transactions) {
 537        const txIndex = parseInt(l2Tx.transactionIndex, 16);
 538        const indexMatch = txIndex === espressoTx.index;
 539        
 540        let confidence = 1.0;
 541        if (timeDiff > 60) confidence -= 0.2;
 542        if (timeDiff > 180) confidence -= 0.3;
 543        if (!indexMatch) confidence -= 0.1;
 544        
 545        if (confidence > 0.5) {
 546          matches.push({
 547            espresso_tx: espressoTxHash,
 548            espresso_block: espressoBlockHeight,
 549            l2_tx: l2Tx.hash,
 550            l2_block: i,
 551            confidence,
 552            timestamp_diff: timeDiff,
 553            factors: {
 554              timestamp_match: timeDiff < 60,
 555              index_match: indexMatch,
 556              size_match: true
 557            }
 558          });
 559        }
 560      }
 561    }
 562    
 563    matches.sort((a, b) => b.confidence - a.confidence);
 564    
 565    return {
 566      found: matches.length > 0,
 567      matches,
 568      search_range: {
 569        from_block: Math.max(1, currentL2Block - searchRange),
 570        to_block: currentL2Block + searchRange,
 571        blocks_searched: searchRange * 2
 572      }
 573    };
 574  }
 575  ```
 576  
 577  ---
 578  
 579  ### Phase 3: Extend Existing Types (No New Files)
 580  
 581  **Modify**: `src/types/espresso.ts`
 582  
 583  ```typescript
 584  // Add to existing file
 585  
 586  export interface EthereumTransaction {
 587    hash: string;                // 0x...
 588    blockNumber: string;         // hex
 589    blockHash: string;
 590    from: string;
 591    to: string;
 592    value: string;
 593    gas: string;
 594    gasPrice: string;
 595    timestamp?: number;
 596  }
 597  
 598  export interface CorrelationMatch {
 599    espresso_tx: string;
 600    espresso_block: number;
 601    l2_tx: string;
 602    l2_block: number;
 603    confidence: number;
 604    timestamp_diff: number;
 605    factors: {
 606      timestamp_match: boolean;
 607      index_match: boolean;
 608      size_match: boolean;
 609    };
 610  }
 611  
 612  export interface CorrelationResult {
 613    found: boolean;
 614    matches: CorrelationMatch[];
 615    search_range: {
 616      from_block: number;
 617      to_block: number;
 618      blocks_searched: number;
 619    };
 620  }
 621  
 622  export type CaffNodeNetwork = 'mainnet' | 'testnet';
 623  ```
 624  
 625  ---
 626  
 627  ### Phase 4: Extend Configuration (Minimal Changes)
 628  
 629  **Modify**: `src/lib/config.ts`
 630  
 631  ```typescript
 632  // Add to existing config
 633  
 634  const config = {
 635    // ... existing config ...
 636    
 637    // Caff Node endpoints
 638    CAFF_NODE_TESTNET_URL: process.env.NEXT_PUBLIC_CAFF_NODE_TESTNET_URL || 'https://rari.caff.testnet.espresso.network',
 639    CAFF_NODE_MAINNET_URL: process.env.NEXT_PUBLIC_CAFF_NODE_MAINNET_URL || 'https://rari.caff.mainnet.espresso.network',
 640    
 641    // Default namespace for correlation
 642    DEFAULT_CORRELATION_NAMESPACE: parseInt(process.env.NEXT_PUBLIC_DEFAULT_CORRELATION_NAMESPACE || '1380012617')
 643  };
 644  
 645  export const getCaffNodeUrl = (network: 'mainnet' | 'testnet' = 'testnet'): string => {
 646    return network === 'testnet' ? config.CAFF_NODE_TESTNET_URL : config.CAFF_NODE_MAINNET_URL;
 647  };
 648  
 649  export const getDefaultCorrelationNamespace = (): number => {
 650    return config.DEFAULT_CORRELATION_NAMESPACE;
 651  };
 652  ```
 653  
 654  **Add to `env.example`**:
 655  ```bash
 656  # Caff Node Configuration
 657  NEXT_PUBLIC_CAFF_NODE_TESTNET_URL=https://rari.caff.testnet.espresso.network
 658  NEXT_PUBLIC_CAFF_NODE_MAINNET_URL=https://rari.caff.mainnet.espresso.network
 659  
 660  # Default namespace for transaction correlation (RARI)
 661  NEXT_PUBLIC_DEFAULT_CORRELATION_NAMESPACE=1380012617
 662  ```
 663  
 664  ---
 665  
 666  ### Phase 5: Extend Search (Modify Existing)
 667  
 668  **Modify**: `src/services/api/main.ts`
 669  
 670  ```typescript
 671  // Add to existing file
 672  
 673  import { correlateL2ToEspresso, correlateEspressoToL2 } from '../correlation/mapper';
 674  
 675  /**
 676   * Enhanced search that supports both Espresso and L2 transaction hashes
 677   */
 678  export async function searchWithCorrelation(
 679    query: string,
 680    type: 'block' | 'transaction' | 'namespace' | 'rollup_name'
 681  ): Promise<any | null> {
 682    // Try standard search first
 683    const standardResult = await search(query, type);
 684    
 685    // If transaction search and starts with 0x, try correlation
 686    if (type === 'transaction' && query.startsWith('0x') && !standardResult) {
 687      const correlationResult = await correlateL2ToEspresso(query);
 688      
 689      if (correlationResult.found && correlationResult.matches.length > 0) {
 690        // Return best match with correlation data
 691        const bestMatch = correlationResult.matches[0];
 692        const espressoTx = await getTransactionByHash(bestMatch.espresso_tx);
 693        
 694        return {
 695          type: 'transaction',
 696          data: espressoTx,
 697          correlation: bestMatch
 698        };
 699      }
 700    }
 701    
 702    return standardResult;
 703  }
 704  
 705  /**
 706   * Get correlation info for an Espresso transaction
 707   */
 708  export async function getTransactionCorrelation(espressoTxHash: string): Promise<CorrelationResult | null> {
 709    try {
 710      return await correlateEspressoToL2(espressoTxHash);
 711    } catch (error) {
 712      console.error('Correlation failed:', error);
 713      return null;
 714    }
 715  }
 716  
 717  /**
 718   * Get L2 transaction details from 0x hash
 719   */
 720  export async function getL2Transaction(l2TxHash: string): Promise<EthereumTransaction | null> {
 721    try {
 722      const caffClient = getCaffNodeClient();
 723      return await caffClient.getTransactionByHash(l2TxHash);
 724    } catch (error) {
 725      console.error('L2 transaction fetch failed:', error);
 726      return null;
 727    }
 728  }
 729  ```
 730  
 731  ---
 732  
 733  ### Phase 6: UI Enhancement (Extend Components)
 734  
 735  **Option A: Add Correlation Badge to Transaction View**
 736  
 737  ```typescript
 738  // In transaction display component
 739  
 740  {transaction && (
 741    <div className="transaction-details">
 742      <h3>Transaction {transaction.hash}</h3>
 743      
 744      {/* Existing fields */}
 745      <div>Block: {transaction.block_height}</div>
 746      <div>Namespace: {transaction.namespace}</div>
 747      
 748      {/* NEW: Correlation info */}
 749      {correlation && (
 750        <div className="correlation-info">
 751          <h4>L2 Correlation</h4>
 752          <div>L2 Transaction: {correlation.l2_tx}</div>
 753          <div>L2 Block: {correlation.l2_block}</div>
 754          <div>Confidence: {(correlation.confidence * 100).toFixed(0)}%</div>
 755          <div>Time Diff: {correlation.timestamp_diff}s</div>
 756          <a href={`https://mainnet.explorer.rarichain.org/tx/${correlation.l2_tx}`} target="_blank">
 757            View on Rari Explorer
 758          </a>
 759        </div>
 760      )}
 761    </div>
 762  )}
 763  ```
 764  
 765  **Option B: Add Toggle Button**
 766  
 767  ```typescript
 768  // In search interface
 769  
 770  <button onClick={async () => {
 771    const result = await getTransactionCorrelation(transaction.hash);
 772    setCorrelation(result);
 773  }}>
 774    ๐Ÿ”— Find L2 Transaction
 775  </button>
 776  ```
 777  
 778  ---
 779  
 780  ## ๐Ÿ“Š Testing Strategy
 781  
 782  ### Unit Tests
 783  
 784  ```typescript
 785  // src/services/caff/__tests__/client.test.ts
 786  
 787  describe('CaffNodeClient', () => {
 788    test('should fetch block by number', async () => {
 789      const client = getCaffNodeClient('testnet');
 790      const block = await client.getBlockByNumber(3327456);
 791      expect(block).toBeDefined();
 792      expect(block.number).toBe('0x32c9dc');
 793    });
 794    
 795    test('should fetch transaction by hash', async () => {
 796      const client = getCaffNodeClient('testnet');
 797      const tx = await client.getTransactionByHash('0xaa6594b9...');
 798      expect(tx).toBeDefined();
 799      expect(tx.hash).toBe('0xaa6594b9...');
 800    });
 801  });
 802  ```
 803  
 804  ### Integration Tests
 805  
 806  ```typescript
 807  // src/services/correlation/__tests__/mapper.test.ts
 808  
 809  describe('Transaction Correlation', () => {
 810    test('should correlate L2 to Espresso', async () => {
 811      const result = await correlateL2ToEspresso('0xaa6594b9...');
 812      expect(result.found).toBe(true);
 813      expect(result.matches.length).toBeGreaterThan(0);
 814      expect(result.matches[0].confidence).toBeGreaterThan(0.5);
 815    });
 816    
 817    test('should handle non-existent L2 transaction', async () => {
 818      const result = await correlateL2ToEspresso('0xinvalid...');
 819      expect(result.found).toBe(false);
 820    });
 821  });
 822  ```
 823  
 824  ### E2E Tests
 825  
 826  ```typescript
 827  // src/components/__tests__/search.e2e.test.ts
 828  
 829  describe('Search with Correlation', () => {
 830    test('should find Espresso tx from 0x hash', async () => {
 831      const { getByPlaceholderText, getByText } = render(<SearchInterface />);
 832      
 833      const searchInput = getByPlaceholderText('Search...');
 834      fireEvent.change(searchInput, { target: { value: '0xaa6594b9...' } });
 835      
 836      await waitFor(() => {
 837        expect(getByText(/TX~/)).toBeInTheDocument();
 838        expect(getByText(/L2 Correlation/)).toBeInTheDocument();
 839      });
 840    });
 841  });
 842  ```
 843  
 844  ---
 845  
 846  ## ๐Ÿš€ Deployment Plan
 847  
 848  ### Phase 1: Development (Week 1-2)
 849  - โœ… Implement CaffNodeClient
 850  - โœ… Implement correlation service
 851  - โœ… Add unit tests
 852  - โœ… Update types and config
 853  
 854  ### Phase 2: Integration (Week 3)
 855  - โœ… Extend search functionality
 856  - โœ… Add correlation UI components
 857  - โœ… Integration testing
 858  - โœ… Update documentation
 859  
 860  ### Phase 3: Testing (Week 4)
 861  - โœ… E2E testing
 862  - โœ… Performance testing
 863  - โœ… Edge case handling
 864  - โœ… User acceptance testing
 865  
 866  ### Phase 4: Production (Week 5)
 867  - โœ… Deploy to staging
 868  - โœ… Monitor performance
 869  - โœ… Deploy to production
 870  - โœ… User feedback
 871  
 872  ---
 873  
 874  ## ๐Ÿ“ˆ Performance Considerations
 875  
 876  ### Optimization Strategies
 877  
 878  1. **Caching**: Cache L2 โ†” Espresso mappings
 879     ```typescript
 880     const correlationCache = new Map<string, CorrelationResult>();
 881     ```
 882  
 883  2. **Parallel Searches**: Search multiple blocks concurrently
 884     ```typescript
 885     await Promise.all(blocks.map(b => searchBlock(b)));
 886     ```
 887  
 888  3. **Time Window Optimization**: Start with narrow window, expand if needed
 889     ```typescript
 890     let window = 5 * 60; // Start with 5 minutes
 891     while (matches.length === 0 && window < 30 * 60) {
 892       window *= 2;
 893       // Retry with larger window
 894     }
 895     ```
 896  
 897  4. **Block Range Limits**: Limit search to reasonable block range
 898     ```typescript
 899     const MAX_BLOCKS_TO_SEARCH = 1000;
 900     ```
 901  
 902  ---
 903  
 904  ## ๐Ÿ” Security Considerations
 905  
 906  1. **Input Validation**: Validate all 0x hashes
 907     ```typescript
 908     if (!/^0x[0-9a-fA-F]{64}$/.test(hash)) {
 909       throw new Error('Invalid transaction hash');
 910     }
 911     ```
 912  
 913  2. **Rate Limiting**: Implement client-side rate limiting
 914     ```typescript
 915     const rateLimiter = new RateLimiter(10, 60000); // 10 req/min
 916     ```
 917  
 918  3. **Error Handling**: Graceful degradation
 919     ```typescript
 920     try {
 921       return await correlate();
 922     } catch (error) {
 923       console.error(error);
 924       return { found: false, matches: [], search_range: { ... } };
 925     }
 926     ```
 927  
 928  4. **Timeout Protection**: Add request timeouts
 929     ```typescript
 930     const controller = new AbortController();
 931     setTimeout(() => controller.abort(), 30000);
 932     ```
 933  
 934  ---
 935  
 936  ## ๐Ÿ“ Documentation Updates
 937  
 938  ### README.md
 939  
 940  ```markdown
 941  ## Features
 942  
 943  - Live network stats: transactions, payload size, success rate, block height
 944  - Search blocks, transactions, namespaces
 945  - **NEW**: L2 โ†” Espresso transaction correlation
 946  - **NEW**: Search by 0x (Ethereum) transaction hash
 947  - WebSocket updates; responsive UI (TypeScript/Next.js/Tailwind)
 948  
 949  ## Search Examples
 950  
 951  - Block height: `4603571`
 952  - Block hash: `BLOCK~<hash>`
 953  - Transaction hash: `TX~<hash>` or raw hash
 954  - **NEW**: L2 transaction hash: `0x<hash>` (66 chars)
 955  - Large namespace ID: long number
 956  ```
 957  
 958  ### API Documentation
 959  
 960  ```markdown
 961  ## Correlation API
 962  
 963  ### correlateL2ToEspresso(l2TxHash: string, namespace?: number)
 964  
 965  Finds Espresso transaction(s) corresponding to an L2 transaction.
 966  
 967  **Parameters**:
 968  - `l2TxHash`: L2 transaction hash (0x format)
 969  - `namespace`: Namespace ID (default: 1380012617 for RARI)
 970  
 971  **Returns**: `CorrelationResult`
 972  - `found`: Whether matches were found
 973  - `matches`: Array of correlation matches
 974  - `search_range`: Range of blocks searched
 975  
 976  **Example**:
 977  ```typescript
 978  const result = await correlateL2ToEspresso('0xaa6594b9...');
 979  if (result.found) {
 980    console.log('Espresso TX:', result.matches[0].espresso_tx);
 981  }
 982  ```
 983  ```
 984  
 985  ---
 986  
 987  ## ๐ŸŽฏ Success Metrics
 988  
 989  ### Technical Metrics
 990  - โœ… Correlation accuracy > 90%
 991  - โœ… Search response time < 5s
 992  - โœ… Cache hit rate > 70%
 993  - โœ… Test coverage > 80%
 994  
 995  ### User Metrics
 996  - โœ… Successful 0x searches > 80%
 997  - โœ… User engagement with correlation feature > 30%
 998  - โœ… Positive feedback > 90%
 999  
1000  ---
1001  
1002  ## ๐Ÿ”ฎ Future Enhancements
1003  
1004  ### Phase 2 Features
1005  1. **Bi-directional Real-time Sync**: WebSocket for L2 events
1006  2. **Confidence Score Display**: Visual confidence indicators
1007  3. **Multiple Chain Support**: Expand beyond Rari
1008  4. **Historical Correlation**: Pre-compute common mappings
1009  5. **Advanced Filtering**: Filter by confidence, time range
1010  
1011  ### Phase 3 Features
1012  1. **Transaction Lifecycle View**: Complete L2 โ†’ Espresso โ†’ L1 flow
1013  2. **Proof Verification**: Verify Espresso confirmations
1014  3. **Analytics Dashboard**: Correlation statistics
1015  4. **API Endpoints**: Expose correlation as REST API
1016  5. **SDK**: JavaScript/TypeScript SDK for developers
1017  
1018  ---
1019  
1020  ## ๐Ÿ“‹ Summary
1021  
1022  **Integration Strategy**: 
1023  - โœ… **2 new files**: `caff/client.ts`, `correlation/mapper.ts`
1024  - โœ… **3 modified files**: `types/espresso.ts`, `lib/config.ts`, `api/main.ts`
1025  - โœ… **No breaking changes**: All existing functionality preserved
1026  - โœ… **Minimal UI changes**: Optional correlation display
1027  
1028  **Key Benefits**:
1029  - ๐Ÿ”— Bi-directional transaction mapping
1030  - ๐ŸŽฏ High correlation accuracy (timestamp + metadata)
1031  - โšก Efficient binary search algorithm
1032  - ๐Ÿงช Comprehensive testing strategy
1033  - ๐Ÿ“ˆ Performance optimizations (caching, parallel)
1034  - ๐Ÿ” Security considerations (validation, timeouts)
1035  
1036  **Timeline**: 4-5 weeks from development to production
1037  
1038  ---
1039  
1040  *Integration Plan v1.0*
1041  *Ready for implementation*