Learnings from Bigfield Audit

barretenberg

├── acir_formal_proofs
├── api
├── bb
│   └── deps
├── benchmark
│   ├── append_only_tree_bench
│   ├── basics_bench
│   ├── bb_cli_bench
│   ├── circuit_construction_bench
│   ├── client_ivc_bench
│   ├── decrypt_bench
│   ├── goblin_bench
│   ├── indexed_tree_bench
│   ├── ipa_bench
│   ├── mega_memory_bench
│   ├── merkle_tree_bench
│   ├── pippenger_bench
│   ├── poseidon2_bench
│   ├── protogalaxy_bench
│   ├── protogalaxy_rounds_bench
│   ├── relations_bench
│   └── ultra_bench
├── boomerang_value_detection
├── circuit_checker
├── client_ivc
├── commitment_schemes
│   ├── gemini
│   ├── ipa
│   ├── kzg
│   ├── shplonk
│   ├── small_subgroup_ipa
│   └── utils
├── commitment_schemes_recursion
├── common
├── crypto
│   ├── aes128
│   ├── blake2s
│   ├── blake3s
│   ├── blake3s_full
│   ├── ecdsa
│   ├── generators
│   ├── hashers
│   ├── hmac
│   ├── keccak
│   ├── merkle_tree
│   │   ├── append_only_tree
│   │   ├── indexed_tree
│   │   ├── lmdb_store
│   │   ├── node_store
│   │   └── nullifier_tree
│   ├── pedersen_commitment
│   ├── pedersen_hash
│   ├── poseidon2
│   │   └── sponge
│   ├── schnorr
│   └── sha256
├── dsl
│   ├── acir_format
│   │   └── serde
│   └── acir_proofs
├── ecc
│   ├── batched_affine_addition
│   ├── curves
│   │   ├── bn254
│   │   ├── grumpkin
│   │   ├── secp256k1
│   │   └── secp256r1
│   ├── fields
│   ├── groups
│   └── scalar_multiplication
├── eccvm
├── env
├── ext
│   └── starknet
│       ├── crypto
│       │   └── poseidon
│       ├── ecc
│       │   └── curves
│       │       └── stark252
│       ├── stdlib_circuit_builders
│       └── transcript
├── flavor
├── goblin
├── grumpkin_srs_gen
├── honk
│   ├── composer
│   ├── execution_trace
│   ├── library
│   ├── proof_system
│   │   └── types
│   ├── types
│   └── utils
├── lmdblib
├── messaging
├── nodejs_module
│   ├── lmdb_store
│   ├── node_modules
│   │   ├── node-addon-api
│   │   │   └── tools
│   │   └── node-api-headers
│   │       ├── def
│   │       └── include
│   ├── util
│   └── world_state
├── numeric
│   ├── bitop
│   ├── random
│   ├── uint128
│   ├── uint256
│   └── uintx
├── op_queue
├── polynomials
├── protogalaxy
├── relations
│   ├── ecc_vm
│   ├── generic_lookup
│   ├── generic_permutation
│   └── translator_vm
├── serialize
│   └── msgpack_impl
├── smt_verification
│   ├── circuit
│   ├── solver
│   ├── terms
│   └── util
├── solidity_helpers
│   ├── circuits
│   └── utils
├── srs
│   └── factories
├── stdlib
│   ├── client_ivc_verifier
│   ├── commitment
│   │   └── pedersen
│   ├── eccvm_verifier
│   ├── encryption
│   │   ├── aes128
│   │   ├── ecdsa
│   │   └── schnorr
│   ├── goblin_verifier
│   ├── hash
│   │   ├── blake2s
│   │   ├── blake3s
│   │   ├── keccak
│   │   ├── pedersen
│   │   ├── poseidon2
│   │   │   └── sponge
│   │   └── sha256
│   ├── honk_verifier
│   ├── merkle_tree
│   │   └── indexed_tree
│   ├── primitives
│   │   ├── address
│   │   ├── bigfield
│   │   ├── biggroup
│   │   ├── bit_array
│   │   ├── bool
│   │   ├── byte_array
│   │   ├── circuit_builders
│   │   ├── curves
│   │   ├── databus
│   │   ├── field
│   │   ├── group
│   │   ├── logic
│   │   ├── memory
│   │   ├── packed_byte_array
│   │   ├── padding_indicator_array
│   │   ├── plookup
│   │   ├── public_input_component
│   │   ├── safe_uint
│   │   ├── uint
│   │   │   └── plookup
│   │   └── witness
│   ├── protogalaxy_verifier
│   ├── test_utils
│   ├── transcript
│   └── translator_vm_verifier
├── stdlib_circuit_builders
│   └── plookup_tables
│       ├── fixed_base
│       └── keccak
├── sumcheck
├── trace_to_polynomials
├── transcript
├── translator_vm
├── ultra_honk
├── vm2
│   ├── common
│   ├── constraining
│   │   ├── benchmark
│   │   ├── recursion
│   │   ├── relations
│   │   └── testing
│   ├── generated
│   │   └── relations
│   ├── simulation
│   │   ├── events
│   │   ├── lib
│   │   └── testing
│   ├── testing
│   ├── tooling
│   └── tracegen
│       └── lib
├── wasi
└── world_state
  • 206 directories, 1523 files!

\(\rightarrow\) client_ivc

\(\rightarrow\) commitment_schemes

\(\rightarrow\) crypto

\(\rightarrow\) dsl

\(\rightarrow\) ecc

\(\rightarrow\) eccvm

\(\rightarrow\) flavor

\(\rightarrow\) goblin

\(\rightarrow\) honk

\(\rightarrow\) messaging

\(\rightarrow\) numeric

\(\rightarrow\) polynomials

\(\rightarrow\) protogalaxy

\(\rightarrow\) relations

\(\rightarrow\) stdlib

\(\rightarrow\) sumcheck

\(\rightarrow\) trace_to_poly

\(\rightarrow\) transcript

\(\rightarrow\) ultra_honk

\(\rightarrow\) translator_vm

\(\rightarrow\) vm2

Classification

\(\rightarrow\) client_ivc

\(\rightarrow\) commitment_schemes

\(\rightarrow\) crypto

\(\rightarrow\) dsl

\(\rightarrow\) ecc

\(\rightarrow\) eccvm

\(\rightarrow\) flavor

\(\rightarrow\) goblin

\(\rightarrow\) honk

\(\rightarrow\) messaging

\(\rightarrow\) numeric

\(\rightarrow\) polynomials

\(\rightarrow\) protogalaxy

\(\rightarrow\) relations

\(\rightarrow\) stdlib

\(\rightarrow\) sumcheck

\(\rightarrow\) trace_to_poly

\(\rightarrow\) transcript

\(\rightarrow\) ultra_honk

\(\rightarrow\) translator_vm

\(\rightarrow\) vm2

🔴
🔴
🔴
🔴
🔴
🔴
🔴
🔴
🔴
🔴
🔴
🔴
🔴

⚠️

⚠️

⚠️

⚠️

⚠️

⚠️

⚠️

🔴

Classification

\(\rightarrow\) client_ivc

\(\rightarrow\) commitment_schemes

\(\rightarrow\) crypto

\(\rightarrow\) dsl

\(\rightarrow\) ecc

\(\rightarrow\) eccvm

\(\rightarrow\) flavor

\(\rightarrow\) goblin

\(\rightarrow\) honk

\(\rightarrow\) messaging

\(\rightarrow\) numeric

\(\rightarrow\) polynomials

\(\rightarrow\) protogalaxy

\(\rightarrow\) relations

\(\rightarrow\) stdlib

\(\rightarrow\) sumcheck

\(\rightarrow\) trace_to_poly

\(\rightarrow\) transcript

\(\rightarrow\) ultra_honk

\(\rightarrow\) translator_vm

\(\rightarrow\) vm2

🔴
🔴
🔴
🔴
🔴
🔴
🔴

⚠️

🔴
🔴
🔴
🔴
🔴
🔴

⚠️

⚠️

⚠️

⚠️

⚠️

⚠️

⚠️

Non-circuit

Circuit

\(\rightarrow\) stdlib

├── stdlib
│   ├── client_ivc_verifier
│   ├── commitment
│   │   └── pedersen
│   ├── eccvm_verifier
│   ├── encryption
│   │   ├── aes128
│   │   ├── ecdsa
│   │   └── schnorr
│   ├── goblin_verifier
│   ├── hash
│   │   ├── blake2s
│   │   ├── blake3s
│   │   ├── keccak
│   │   ├── pedersen
│   │   ├── poseidon2
│   │   │   └── sponge
│   │   └── sha256
│   ├── honk_verifier
│   ├── merkle_tree
│   │   └── indexed_tree
│   ├── primitives
│   │   ├── address
│   │   ├── bigfield
│   │   ├── biggroup
│   │   ├── bit_array
│   │   ├── bool
│   │   ├── byte_array
│   │   ├── circuit_builders
│   │   ├── curves
│   │   ├── databus
│   │   ├── field
│   │   ├── group
│   │   ├── logic
│   │   ├── memory
│   │   ├── packed_byte_array
│   │   ├── padding_indicator_array
│   │   ├── plookup
│   │   ├── public_input_component
│   │   ├── safe_uint
│   │   ├── uint
│   │   │   └── plookup
│   │   └── witness
│   ├── protogalaxy_verifier
│   ├── test_utils
│   ├── transcript
│   └── translator_vm_verifier
├── stdlib_circuit_builders
│   └── plookup_tables
│       ├── fixed_base
│       └── keccak

stdlib

│   ├── client_ivc_verifier
│   ├── eccvm_verifier
│   ├── goblin_verifier
│   ├── honk_verifier
│   ├── protogalaxy_verifier
│   ├── transcript
│   └── translator_vm_verifier
├── stdlib_circuit_builders
│   └── plookup_tables
│       ├── fixed_base
│       └── keccak
│   ├── commitment
│   │   └── pedersen
│   ├── encryption
│   │   ├── aes128
│   │   ├── ecdsa
│   │   └── schnorr
│   ├── hash
│   │   ├── blake2s
│   │   ├── blake3s
│   │   ├── keccak
│   │   ├── pedersen
│   │   ├── poseidon2
│   │   │   └── sponge
│   │   └── sha256
│   ├── merkle_tree
│   │   └── indexed_tree
│   ├── primitives
│   │   ├── address
│   │   ├── bigfield
│   │   ├── biggroup
│   │   ├── bit_array
│   │   ├── bool
│   │   ├── byte_array
│   │   ├── circuit_builders
│   │   ├── curves
│   │   ├── databus
│   │   ├── field
│   │   ├── group
│   │   ├── logic
│   │   ├── memory
│   │   ├── packed_byte_array
│   │   ├── padding_indicator_array
│   │   ├── plookup
│   │   ├── public_input_component
│   │   ├── safe_uint
│   │   ├── uint
│   │   │   └── plookup
│   │   └── witness

Building blocks

Cryptography

Recursive verifiers

Lookup tables

Primitives

dsl

field

bool

uint

witness

byte_array

bigfield

databus

logic

memory

packed_byte_array

padding_indicator_array

plookup

public_input_component

safe_uint

biggroup

group

bit_array

ultra

circuit

builder

mega

circuit

builder

\iff
\iff

noir

field

bool

uint

witness

byte_array

bigfield

databus

logic

memory

packed_byte_array

padding_indicator_array

plookup

public_input_component

safe_uint

biggroup

group

bit_array

Audit Objectives

  • Not to lose money
    • Bug bounty 🫠
    • Hack or attack 💀
  • To build confidence in what we've built

June 2025

now

October 2025

ignition

2026?

alpha

20**?

beta

if no bugs are found

  • Likely we'll never reach the "no-bugs" stage: But are we confident enough?
  • Thorough understanding of what you audit
  • Help external auditors find as many bugs as possible \(\implies\) documentation

Methodology

  • Disclaimer: highly dependent on what you're auditing and how old it is.
  • List all the files, functions and classes in scope
  • List forward and backward critical dependencies
  • Start with the tests to understand the interfaces and usage
  • Order of things to do:
    • Functionality-wise: Identify the most critical component / function / equation and unroll from there
    • Function-wise: lexigraphic order?
  • Mid-point checkin: talk with the author(s) to:
    • Get your questions resolved
    • Confirm if the critical component is correct and course-correct  

My Takeaways

  • Audit work \(\neq\) usual work
    • There isn't a fixed set of tasks
    • Understanding others' code can get very daunting
    • Alternate between audit and other tasks?
  • Time-bound audit is necessary
    • No amount of time will ever be enough:
    • Reasonable time on each function \(>\) concentrated time on a few
    • Set practical timeline with a buffer period for PRs and issues 
    • Documentation can take time but don't try to perfect it
  • Checking critical inter-dependencies is important 
    • Example: bigfield multiplication \(\longleftrightarrow\) cached ultra gates 

My Takeaways

  • Refactoring and fixing issues, adding tests
    • Can resolve old TODOs and remove redundant/unused code
    • Fix long-standing issues if necessary and the timeline allows
    • Check if all edge-cases are being tested, add/modify tests if necessary
  • Didn't find interesting bugs? Don't worry!
    • Audits are stressful, focus on building confidence in the code
    • Being well-prepared for external audits is invaluable

Historical bugs: Integer Division

  • Claim circuit was supposed to check:
w = \left\lfloor W \cdot \frac{d}{D} \right\rfloor

user deposit

total deposit

user withdrawal

total withdrawal

Historical bugs: Integer Division

  • Claim circuit was supposed to check:
w = \left\lfloor W \cdot \frac{d}{D} \right\rfloor
\implies w \cdot D + \textcolor{red}{r} = W \cdot d
  • Assume each amount is \(252\)-bit integer
  • How do we check integer arithmetic in circuit?
\begin{array}{lccr} [ \ d_3 & d_2 & d_1 & d_0 \ ] \\[3pt] [ \ w_3 & w_2 & w_1 & w_0 \ ] \\[5pt] [ \ D_3 & D_2 & D_1 & D_0 \ ] \\[5pt] [ \ W_3 & W_2 & W_1 & W_0 \ ] \\[5pt] [ \ r_3 & r_2 & r_1 & r_0 \ ] \end{array}
\begin{array}{rrrr} d \ \equiv \\[3pt] w \ \equiv \\[5pt] D \ \equiv \\[5pt] W \ \equiv \\[5pt] r \ \equiv \end{array}
\begin{array}{lccr} [ \ \textcolor{#FD7F2C}{x_3} & \textcolor{#FD9346}{x_2} & \textcolor{#FDA766}{x_1} & \textcolor{#FDB777}{x_0} \ ] \\[2pt] [ \ \textcolor{#9FD4A3}{y_3} & \textcolor{#BEE3BA}{y_2} & \textcolor{#DDF2D1}{y_1} & \textcolor{#ECFADC}{y_0} \ ] \end{array}
\begin{array}{rrrr} x \ \equiv \\[2pt] y \ \equiv \end{array}
\begin{array}{cccccccc} & & & & \textcolor{#FD7F2C}{x_3} \textcolor{#9FD4A3}{y_0} & \textcolor{#FD9346}{x_2} \textcolor{#9FD4A3}{y_0} & \textcolor{#FDA766}{x_1} \textcolor{#9FD4A3}{y_0} & \textcolor{#FDB777}{x_0} \textcolor{#9FD4A3}{y_0} \\[5pt] & & & \textcolor{#FD7F2C}{x_3} \textcolor{#BEE3BA}{y_1} & \textcolor{#FD9346}{x_2} \textcolor{#BEE3BA}{y_1} & \textcolor{#FDA766}{x_1} \textcolor{#BEE3BA}{y_1} & \textcolor{#FDB777}{x_0} \textcolor{#BEE3BA}{y_1} & \\[5pt] & & \textcolor{#FD7F2C}{x_3} \textcolor{#DDF2D1}{y_2} & \textcolor{#FD9346}{x_2} \textcolor{#DDF2D1}{y_2} & \textcolor{#FDA766}{x_1} \textcolor{#DDF2D1}{y_2} & \textcolor{#FDB777}{x_0} \textcolor{#DDF2D1}{y_2} & & \\[5pt] & \textcolor{#FD7F2C}{x_3} \textcolor{#ECFADC}{y_3} & \textcolor{#FD9346}{x_2} \textcolor{#ECFADC}{y_3} & \textcolor{#FDA766}{x_1} \textcolor{#ECFADC}{y_3} & \textcolor{#FDB777}{x_0} \textcolor{#ECFADC}{y_3} & & & \\[5pt] \hline & z_6 & z_5 & z_4 & z_3 & z_2 & z_1 & z_0 & \\ \end{array}
	// Split a field_t element into 4 68-bit limbs
    const auto split_into_limbs = [&composer, &shift_1, &shift_2, &shift_3](const field_ct& input) {
        const uint256_t value = input.get_value();

        const uint256_t t0 = value.slice(0, 68);
        const uint256_t t1 = value.slice(68, 136);
        const uint256_t t2 = value.slice(136, 204);
        const uint256_t t3 = value.slice(204, 272);

        std::array<field_ct, 4> limbs{
            witness_ct(&composer, t0),
            witness_ct(&composer, t1),
            witness_ct(&composer, t2),
            witness_ct(&composer, t3),
        };

        field_ct limb_sum_1 = limbs[0].add_two(limbs[1] * shift_1, limbs[2] * shift_2);
        field_ct limb_sum_2 = input - (limbs[3] * shift_3);
        limb_sum_1.assert_equal(limb_sum_2);

        limbs[0].create_range_constraint(68);
        limbs[1].create_range_constraint(68);
        limbs[2].create_range_constraint(68);
        limbs[3].create_range_constraint(68);

        return limbs;
    };

❗❗❗❗

Historical bugs: Integer Division

w \cdot D + \textcolor{red}{r} = W \cdot d
\begin{array}{lccr} [ \ d_3 & d_2 & d_1 & d_0 \ ] \\[3pt] [ \ w_3 & w_2 & w_1 & w_0 \ ] \\[5pt] [ \ D_3 & D_2 & D_1 & D_0 \ ] \\[5pt] [ \ W_3 & W_2 & W_1 & W_0 \ ] \\[5pt] [ \ r_3 & r_2 & r_1 & r_0 \ ] \end{array}
\begin{array}{rrrr} d \ \equiv \\[3pt] w \ \equiv \\[5pt] D \ \equiv \\[5pt] W \ \equiv \\[5pt] r \ \equiv \end{array}

Allowed to overflow

No range constraints at all!

(w + \textcolor{purple}{a' \cdot n}) \cdot (D + \textcolor{purple}{b' \cdot n}) + (\textcolor{red}{r} + \textcolor{purple}{c' \cdot n}) = (d + \textcolor{purple}{d' \cdot n}) \cdot (W + \textcolor{purple}{e' \cdot n})
w \cdot D + \textcolor{red}{r} = (W + \textcolor{purple}{n}) \cdot d
  • Let the user input be \(d=1\) USDT
\textcolor{red}{r} := (W + \textcolor{purple}{n}) - w \cdot D
\textcolor{red}{r} := (W + \textcolor{purple}{n-100 \cdot D}) - (w \textcolor{lightgreen}{+ 100}) \cdot D

Historical bugs: Integer Division

Learnings from Bigfield Audit

By Suyash Bagad

Learnings from Bigfield Audit

  • 29