Skip to main content

Try it Live

Run RLP examples in the interactive playground

    BrandedRlp Type

    type BrandedRlp =
      | { type: "bytes"; value: Uint8Array }
      | { type: "list"; value: BrandedRlp[] }
    

    Bytes Comparison

    Compares byte-by-byte:
    import { Rlp } from 'tevm'
    
    const a = { type: 'bytes', value: new Uint8Array([1, 2, 3]) }
    const b = { type: 'bytes', value: new Uint8Array([1, 2, 3]) }
    Rlp.equals(a, b)
    // => true
    
    // Different length
    const c = { type: 'bytes', value: new Uint8Array([1, 2]) }
    Rlp.equals(a, c)
    // => false
    
    // Different values
    const d = { type: 'bytes', value: new Uint8Array([1, 2, 4]) }
    Rlp.equals(a, d)
    // => false
    
    // Empty bytes
    const empty1 = { type: 'bytes', value: Bytes() }
    const empty2 = { type: 'bytes', value: Bytes() }
    Rlp.equals(empty1, empty2)
    // => true
    

    List Comparison

    Compares recursively:
    import { Rlp } from 'tevm'
    
    const list1 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([1]) },
        { type: 'bytes', value: new Uint8Array([2]) }
      ]
    }
    
    const list2 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([1]) },
        { type: 'bytes', value: new Uint8Array([2]) }
      ]
    }
    
    Rlp.equals(list1, list2)
    // => true
    
    // Different length
    const list3 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([1]) }
      ]
    }
    Rlp.equals(list1, list3)
    // => false
    
    // Different order
    const list4 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([2]) },
        { type: 'bytes', value: new Uint8Array([1]) }
      ]
    }
    Rlp.equals(list1, list4)
    // => false
    

    Nested Comparison

    Deeply nested structures are compared recursively:
    import { Rlp } from 'tevm'
    
    const nested1 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([1]) },
        {
          type: 'list',
          value: [
            { type: 'bytes', value: new Uint8Array([2]) },
            { type: 'bytes', value: new Uint8Array([3]) }
          ]
        }
      ]
    }
    
    const nested2 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([1]) },
        {
          type: 'list',
          value: [
            { type: 'bytes', value: new Uint8Array([2]) },
            { type: 'bytes', value: new Uint8Array([3]) }
          ]
        }
      ]
    }
    
    Rlp.equals(nested1, nested2)
    // => true
    

    Usage Patterns

    Transaction Comparison

    Compare decoded transactions:
    import { Rlp } from 'tevm'
    
    const tx1Bytes = new Uint8Array([...])
    const tx2Bytes = new Uint8Array([...])
    
    const tx1 = Rlp.decode(tx1Bytes)
    const tx2 = Rlp.decode(tx2Bytes)
    
    if (Rlp.equals(tx1.data, tx2.data)) {
      console.log('Transactions are identical')
    }
    

    Deduplication

    Deduplicate RLP data structures:
    import { Rlp } from 'tevm'
    
    function deduplicate(items: BrandedRlp[]): BrandedRlp[] {
      const unique: BrandedRlp[] = []
    
      for (const item of items) {
        const isDuplicate = unique.some(existing =>
          Rlp.equals(existing, item)
        )
        if (!isDuplicate) {
          unique.push(item)
        }
      }
    
      return unique
    }
    
    const items = [
      { type: 'bytes', value: new Uint8Array([1]) },
      { type: 'bytes', value: new Uint8Array([2]) },
      { type: 'bytes', value: new Uint8Array([1]) },  // Duplicate
    ]
    
    const unique = deduplicate(items)
    // => [{ type: 'bytes', value: [1] }, { type: 'bytes', value: [2] }]
    

    Cache Key Generation

    Use equality for cache lookups:
    import { Rlp } from 'tevm'
    
    class RlpCache {
      private cache: Array<{ key: BrandedRlp; value: any }> = []
    
      get(key: BrandedRlp): any | undefined {
        const entry = this.cache.find(e => Rlp.equals(e.key, key))
        return entry?.value
      }
    
      set(key: BrandedRlp, value: any): void {
        // Remove existing entry if present
        this.cache = this.cache.filter(e => !Rlp.equals(e.key, key))
        this.cache.push({ key, value })
      }
    }
    

    Test Assertions

    Compare expected vs actual in tests:
    import { Rlp } from 'tevm'
    import { expect } from 'vitest'
    
    test('encodes transaction correctly', () => {
      const tx = [
        new Uint8Array([0x00]),
        new Uint8Array([0x01])
      ]
    
      const encoded = Rlp.encode(tx)
      const decoded = Rlp.decode(encoded)
    
      const expected = {
        type: 'list',
        value: [
          { type: 'bytes', value: new Uint8Array([0x00]) },
          { type: 'bytes', value: new Uint8Array([0x01]) }
        ]
      }
    
      expect(Rlp.equals(decoded.data, expected)).toBe(true)
    })
    

    Merkle Proof Verification

    Compare proof nodes:
    import { Rlp } from 'tevm'
    
    function verifyProof(
      leaf: BrandedRlp,
      proof: BrandedRlp[],
      root: BrandedRlp
    ): boolean {
      let current = leaf
    
      for (const node of proof) {
        // Hash current with proof node
        const combined = {
          type: 'list' as const,
          value: [current, node]
        }
        current = hashRlp(combined)
      }
    
      return Rlp.equals(current, root)
    }
    

    Performance

    Comparison Algorithm

    // Conceptual implementation
    function equals(a: BrandedRlp, b: BrandedRlp): boolean {
      // Different types
      if (a.type !== b.type) {
        return false
      }
    
      // Bytes comparison
      if (a.type === 'bytes' && b.type === 'bytes') {
        if (a.value.length !== b.value.length) return false
        for (let i = 0; i < a.value.length; i++) {
          if (a.value[i] !== b.value[i]) return false
        }
        return true
      }
    
      // List comparison (recursive)
      if (a.type === 'list' && b.type === 'list') {
        if (a.value.length !== b.value.length) return false
        for (let i = 0; i < a.value.length; i++) {
          if (!equals(a.value[i], b.value[i])) return false
        }
        return true
      }
    
      return false
    }
    

    Complexity

    • Time: O(n) where n is total number of bytes in all data structures
    • Space: O(d) where d is recursion depth (for nested lists)
    • Early exit: Returns false on first mismatch

    Short-circuit Optimization

    Comparison short-circuits on first difference:
    import { Rlp } from 'tevm'
    
    // Large lists with difference at start
    const list1 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([1]) },
        ...Array(1000).fill({ type: 'bytes', value: new Uint8Array([2]) })
      ]
    }
    
    const list2 = {
      type: 'list',
      value: [
        { type: 'bytes', value: new Uint8Array([99]) },  // Different
        ...Array(1000).fill({ type: 'bytes', value: new Uint8Array([2]) })
      ]
    }
    
    // Returns false after checking only first element
    Rlp.equals(list1, list2)
    // => false (fast - doesn't check all 1000 elements)
    

    Alternative: Encoded Comparison

    For faster comparison, compare encoded bytes:
    import { Rlp } from 'tevm'
    
    function fastEquals(a: BrandedRlp, b: BrandedRlp): boolean {
      const aEncoded = Rlp.encode(a)
      const bEncoded = Rlp.encode(b)
    
      if (aEncoded.length !== bEncoded.length) return false
    
      for (let i = 0; i < aEncoded.length; i++) {
        if (aEncoded[i] !== bEncoded[i]) return false
      }
    
      return true
    }
    
    // Faster for deeply nested structures
    // But requires encoding overhead
    

    See Also