Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Finite Fields

The values in a Circom circuit are not integers: every signal is an element of a finite field. Most of the time you will not notice the difference, but many of the bugs in Common Pitfalls come from forgetting it. So before writing any circuits, let us take a quick look at what these numbers are.

What Is a Field?

A field is a set of numbers where you can add, subtract, multiply, and divide by anything except zero, and the result always stays within the set. The usual rules apply, e.g. and . The rational numbers and the real numbers are fields, while the integers are not, as is not an integer.

A finite field has a finite number of elements. The one we use is made of the integers modulo a prime :

If you are interested in the formal definitions, which build up from groups to rings and then to fields, see Field and Finite field. They are not needed to write good Circom code, though.

Arithmetic Modulo

Every operation in the field is done modulo : we compute the result as usual, and then take the remainder after dividing by . Here are a few examples with the tiny prime :

OperationResult
, since
, since
, since

Why must be a prime? Because only then does every non-zero element have an inverse, so that division always works. Modulo 6, for example, is always even, so it can never be 1, and does not exist.

By default, Circom uses the prime of the BN254 elliptic curve, which is the one used by Ethereum’s precompiles:

You can choose a different prime with the --prime option of the compiler, such as bls12381 or goldilocks. Throughout this book we use BN254.

There Are No Negative Numbers

In the field, is simply , which is the largest element. So a “negative” number is really a huge number, and there is no notion of an element being smaller or larger than another that is preserved by arithmetic: with , we have , but while .

This is why comparing two signals is not trivial in a circuit: we must decompose them into bits, and make sure that they are small enough. See Comparators for how it is done, and Common Pitfalls for what happens when it is forgotten.

To make things more confusing, Circom’s relational operators such as < treat the elements larger than as negative numbers, so -1 < 0 is true in Circom code! This only affects the code that computes values, e.g. with var and <--; constraints can not compare at all. See nbits for an example where this matters.

Division

Division in the field is multiplication with the inverse: , where is the element such that . Computing an inverse is expensive, so in a circuit we compute it outside of the constraints, and only check that , as described in Inversion.

Circom has two division operators, and it is important to not mix them up:

  • / is the division in the field, so 1 / 2 is , the element that gives 1 when multiplied by 2.
  • \ is the integer division, along with % for the remainder, so 7 \ 2 is 3. These are not field operations, so they can only be used to compute values, and must be checked with constraints, as described in Integer Division.

The Size of

The BN254 prime has 254 bits, so every element fits in 254 bits. However, not every 254-bit number is an element: is larger than . This has a few consequences that come up throughout the book:

  • A number with up to 253 bits is always an element, so decomposing it into bits is safe. That is why Num2Bits(n) requires .
  • A 254-bit decomposition may have two solutions for the same element, and , called aliases. They are ruled out with an alias check.
  • Arithmetic wraps around at , so an equation such as only holds over the integers if the values are small enough, as in Integer Division.

Try It Yourself

You can see the field in action with log, which prints the values while computing a witness:

template Field() {
  signal input in;

  log("-1 =", -1);
  log("1 / 2 =", 1 / 2);
  log("7 \\ 2 =", 7 \ 2);
}

This prints:

-1 = 21888242871839275222246405745257275088548364400416034343698204186575808495616
1 / 2 = 10944121435919637611123202872628637544274182200208017171849102093287904247809
7 \ 2 = 3

Adding 1 to the first number gives you itself.