Circuit Data Types
DataInput
DataInput
CircuitInput
is a parameter of your circuit definition function Define
. The only thing you care about are the three lists of receipts/storages/transactions. You can treat the data in these three DataPoints
lists as proven to be valid. This is because when you submit your proof to Brevis, the provers there will check this data for you.
You can create a sdk.DataStream
around the list you want to process or you can directly access them.
Not every data point in the lists is necessarily valid. For example, if you allocate 3 slots for receipts but add only two receipts through app.AddReceipt
, then the last receipt item would remain empty. When using DataStream methods, e.g.sdk.NewDataStream(input.Receipts).Mean(...)
, empty checks are done for you so that when you do aggregation operations such as Mean
, you won't account for data located at those empty slots. If you access DataPoints
directly (e.g. inputs.Receipts[3]), you are forgoing this automatic empty check.
CircuitVariable
Data Types
CircuitVariable
Data TypesYou must use the data types specifically defined for circuit use. You can still use custom Go types for non-circuit operations, but anything that goes into CircuitAPI
and DataStream
functions are circuit data types. These variables can be casted to each other through casting (some are not convertible to others yet, please pay attention to the documentation of each casting function). The following pre-defined types all implement the CircuitVariable
interface.
Primitive Types
sdk.Uint248
sdk.Uint248
This type is the default and the lowest cost type to use in circuit because it's "native" to the underlying elliptic curve's scalar field. It represents an unsigned integer up to 248 bits. It is the return type of other type's operations (e.g. Bytes32
's ToBinary
operation returns a list of Uint248
). It is also used where boolean values are appropriate (e.g. the return type of IsEqual
should be boolean but is represented using a Uint248
0 or 1). You can perform arithmetics, comparisons, binary conversion, selection, and logic operations on it. You should always prefer using Uint248 to represent data if possible.
sdk.Int248
sdk.Int248
This type supports representing negative numbers. Uses 1 bit as the sign bit, so the absolute value can only be up to 247 bits.
sdk.Uint521
sdk.Uint521
This type supports arithmetics up to 521 bits. If you need to multiply two Uint248
s and know that they can overflow, cast them to this type before doing the calculation. Note that this type internally uses field emulation and has much higher cost than Uint248 arithmetics. You should only use this whenever necessary.
sdk.Bytes32
sdk.Bytes32
This type is equivalent to solidity's bytes32, and in turn, is also used where uint256 needs to be represented. The Value
fields in Receipt.LogField
, Transaction
, and Storage
are all this type. You can only perform comparison and selection over variables of this type. Bytes32
are used in many places instead of Uint521
for performance reasons because most of the time a Bytes32
can be down casted to Uint248
(e.g. when you know that Value
field in StorageSlot
that is actually a uint64
in a Solidity contract). Other times, we only use Bytes32
for equality checks (hashes, keys, etc...). You can always cast to Uint521
if you really need it.
Composite Types
sdk.List
sdk.List
Lists can hold CircuitVariable
s of a homogeneous type (e.g. sdk.List[sdk.Uint248]
). List itself also implements the CircuitVariable interface. List is simply a Go slice under the hood, so you can use append()
to add elements and list[i]
to access elements.
sdk.Tuple2 ... sdk.Tuple8
sdk.Tuple2 ... sdk.Tuple8
There are 7 pre-defined Tuple types from size 2 to 8. This is your go-to method of defining your custom data structures. You can use any type that implements CircuitVariable
in Tuple fields. Nested Tuples are also possible.
sdk.Receipt
/sdk.StorageSlot
/sdk.Transaction
sdk.Receipt
/sdk.StorageSlot
/sdk.Transaction
These are the types of the input data of your app circuit.
For each transaction receipt, you can choose to use up to NumMaxLogFields
fields. Currently this limit is set to 3.
Currently, only transactions of type 0 (legacy) and 2 (dynamic fee) are supported.
Defining Constant Variables
You may declare constant variables in your circuit. Consider those the "hardwires" of your circuit. The sdk
package contains some utility functions for this purpose. These functions are not a part of the Circuit API and should only be used outside of the circuit to initialize constant circuit variables.