BattleZips
  • Awesome Circom
  • 🔬Theory
    • Prerequisite Knowledge
    • Resources
      • White Papers & PDF's
      • Blogs and Writeups
      • Videos
      • Important Entities
      • Communities
    • Proving Schemes
    • Primitives
      • Hash Functions
      • Public Key Cryptosystems
        • Note on L1 key registry → L2 hot key + callback to circuit-optimized hash functions
        • ECDSA & secp256k1
        • EdDSA
      • Merkle Trees
        • What is a Merkle Tree?
        • What is a merkle proof of inclusion?
        • zk-kit
        • Incremental Merkle Trees
        • Sparse Merkle Trees
        • Tree Arity (Binary, Quinary)
      • Semaphore
      • Arithmetic Circuits
  • 🏗️Development
    • Circom Language
      • Installation
      • IDE
      • Signals and Variables
      • Signal Assignment and Constraint Generation
      • Conditional Statements
      • Components and Templates
      • Circuit Compilation
      • Syntax
    • SnarkJS
      • Proving Schemes
      • Powers of Tau
      • ZK Keys
      • Zero Knowledge Proofs
      • On-Chain ZKP
      • Page 2
    • circomlib
      • Basic Math Constraints
      • Multiplexing
      • Hashing
      • EdDSA
      • circomlibjs
    • circom-tester
    • hardhat-circom
    • SHIELD
    • Circomspect
  • 🌆Ecosystem
    • Circom vs Other Solutions
      • Domain-Specific Languages
      • ZK Virtual Machines
      • ZK Ethereum Virtual Machines
    • Communities to Join
    • Recorded Content
    • Projects
  • 🛳️Examples
    • BattleZips V1
      • On the BattleZips Project
      • Docs holder
        • Join Game UML Sequence Diagram
        • Play Game UML Sequence Diagram
        • End Game UML Sequence Diagram
      • ZK Privacy Stack
      • Deploying Artifacts to Prod
      • Browser Client
    • RollupNC
      • Smart Contracts
      • Account/ State Tree
      • Transaction Tree
      • Layer 1 Deposits to Layer 2
      • Layer 2 Transacting
      • Layer 2 Withdrawals to Layer 1
Powered by GitBook
On this page
  • What are Components / Templates?
  • The Main Component
  • Template Parameters
  • Working with Components
  • Anonymous Components
  1. Development
  2. Circom Language

Components and Templates

C++ has Classes. Solidity has Contracts. Circom has Components.

PreviousConditional StatementsNextCircuit Compilation

Last updated 2 years ago

What are Components / Templates?

Templates are the definition of generic, indivisible circuits in Circom. Components are the instantiation of these templates inside of other circuits. Templates provide the mechanism for creating API's using Signals in Circom. That is, by defining logic within templates, components can be extended to quickly specify a set logical pattern/ action within your circuits.

Though we draw the analogy to classes/ contracts, templates do not provide any sort of object-oriented programming.

The Main Component

The main component defines the circuit you intend to construct your Zero Knowledge proofs with. The main component can contain arbitrarily many components within its circuit, however there can only be one main component at the top level.

As mentioned in the Signals and Variables section, Circom developers specify their public inputs when they declare a template is the main component. This is shown below, where we want inPub to be a public input and inPriv to be a shielded, private input:

pragma circom 2.0.4;

template Multiplier2(){
   //Declaration of signals
   signal input inPub;
   signal input inPriv;
   signal output out <== inPub * inPriv; // inline output signal declaration
}

component main {public [inPub]} = Multiplier2(); 

Note that your entire code base is not limited to one main component. Your app's intended functionality may rely on multiple different proofs, in which each proof has its own main component. In BattleZips, both the and the are main components.

Template Parameters

Oftentimes you will find that your template's logic needs the Circom equivalent of a generic parameter. The values are still integers on the field, but during the execution of a component you can supply constant values that are parameterized at the template level.

template Num2Bits(n) { // n defines # of bits in number being bitified
    signal input in;
    signal output out[n]; // provide n bits out
    var lc1=0;

    var e2=1;
    for (var i = 0; i<n; i++) { // iterate n times to get each bit
        out[i] <-- (in >> i) & 1;
        out[i] * (out[i] -1 ) === 0;
        lc1 += out[i] * e2;
        e2 = e2+e2;
    }

    lc1 === in;
}

In this case, we provide the template parameter n to define the number of bits in the number we are decomposing into a bit array. We want to build modular tool that can be extensively reused with few barriers; by using template parameters we can make generic circuits that can be molded for their specific application.

template Main(balDepth, txDepth) {
    .
    .
    .
}

component main { public [txRoot, prevRoot, nextRoot] } = Main(4, 2);

This allows us to configure a specific constant to use in our Zero Knowledge proof computation while also maintaining a degree of modularity over our implementations. Practically, for the example of RollupNC, we use small account and transaction trees since we are using this code to run unit tests. However, we could very easily scale to production size by simply changing the Main component to hold millions of accounts with hundreds of transactions per batch by defining the main component with the parameters:

component main { public [txRoot, prevRoot, nextRoot] } = Main(24, 8);

Working with Components

pragma circom 2.0.3;

include "node_modules/circomlib/circuits/bitify.circom"; // import template

template PlaceShip() {
    signal input boardIn; // numerical representation of board bitmap
    component toBits = Num2Bits(100); // instantiate num2bits component with 100 bits
    toBits.in <== boardIn; // assign number to decompose to num2bits component
    var boardH[10][10]; 
    for (var i = 0; i < 100; i++) {
        boardH[i \ 10][i % 10] = toBits.out[i]; // utilise the output of num2bits
    }
    .
    .
    .
}

In the above case, we assign a variable with the component's output. However, you are just as able to use the <-- and <== operators to assign signals with a component's output.

When using a component, all inputs MUST be assigned before attempting to assign another signal using the output of the component. For instance, if in the above code we only assigned 99 of the 100 bits to toBits.in, then tried to access toBits.out, we would experience a compile-time error.

Anonymous Components

An anonymous component allows in a single instruction 1) the implicit declaration of a new component, 2) the assignment of every input signal and, finally, 3) the assignment of every output signal.

In simple terms, there are times when we can treat a component like a throwaway function where in one shot we want to extract an output given an input. In previous versions of Circom, whether you want to repeatedly access a component or only need it once, you must declare the component as shown below:

template A(n){
   signal input a, b;
   signal output c;
   c <== a*b;
}
template B(n){
   signal input in[n];
   signal out;
   component temp_a = A(n);
   temp_a.a <== in[0]; 
   temp_a.b <== in[1];
   out <== temp_a.c;
}
component main = B(2);

In template B, we do not care about the component temp_a and do not need such a verbose API to access it. Anonymous templates mean that we do not need to declare temp_a at all, streamlining the code to look like:

template A(n){
   signal input a, b;
   signal output c;
   c <== a*b;
}
template B(n){
   signal input in[n];
   signal out <== A(n)(in[0],in[1]);
}
component main = B(2);

Let's take the example of a decomposition component.

You can also use template parameters with the main component. This can be seen in the :

In the case of num2bits shown above, we can see how it is employed in BattleZips (code abridged here, )

Circom 2.1 included the addition of anonymous components. Much like the concept of , the as such:

🏗️
shot proof
board proof
num2bits
RollupNC code base
see full source
anonymous functions
Circom docs define anonymous components