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

Brevis App

sdk.BrevisApp is the framework around your custom circuit. It handles the conversion of your data to circuit inputs and interacting with Brevis’s system. To create a BrevisApp, use:

import "github.com/brevis-network/brevis-sdk/sdk"
app := sdk.NewBrevisApp(
    1, // data source chain id
    "RPC_URL", // corresponding chain RPC URL, you can find many here: https://chainlist.org/chain/1
    "OUTPUT_DIR", // brevis sdk will save source data into OUTPUT_DIR/input/data.json for future reference
)

Adding Source Data

Source Data Types

The Brevis application circuit supports proving receipt, storage value, and transaction by adding TransactionData, ReceiptData, and StorageData. Developers only need to set up the *required values and the Brevis app will prepare the rest automatically.

ReceiptData

NameTypeDescription
TxHashcommon.HashReceipt's transaction hash (*required)
BlockNumbig.IntReceipt's block number
BlockBaseFeebig.IntBlock base fee
MptKeyPathbig.IntRlp encoded receipt index using this
Fields[LogFieldData]Array of field information will be used in receipt (Usp to 4 fields in each receipt)

LogFieldData

NameTypeDescription
LogPosuintthe log’s position in the receipt (*required)
IsTopicboolWhether the field is a topic (*required)
FieldIndexuintThe index of the field in either a log’s topics or data. (*required)
Contractcommon.AddressThe contract from which the event is emitted
EventIDcommon.HashThe event ID of the event to which the field belong (aka topics[0])
Valuecommon.HashThe value of the field in event, aka the actual thing we care about, only 32-byte fixed length values are supported.

StorageData

NameTypeDescription
BlockNumbig.IntBlock number used for storage value (*required)
BlockBaseFeebig.IntBlock base fee
Addresscommon.AddressAddress used for storage value (*required)
Slotcommon.HashStorage slot (*required)
Valuecommon.HashStorage value

TransactionData

NameTypeDescription
Hashcommon.HashTransaction hash (*required)
BlockNumbig.IntReceipt's block number
BlockBaseFeebig.IntBlock base fee
MptKeyPathbig.IntRlp encoded receipt index using this
LeafHashcommon.Hash

Hash of transaction raw data

with rlp prefix.

Note: As of now, brevis will only prove the existence of a transaction, stay tuned for more tx information is usable

Adding Source Data

The data you add here will be available to process in your app circuit.

app.AddReceipt(sdk.ReceiptData{...})
app.AddStorage(sdk.StorageData{...})
app.AddTransaction(sdk.TransactionData{...})

The maximum amount of Receipt/Storage/Transaction data you can add to each type is restricted by the maximum amount you define in your circuit’s Allocate function. read more

Each of the three types of data has an index within its type. For example, if you call AddStorage twice:

app.AddStorage(sdk.StorageData{/* StorageA */})
app.AddStorage(sdk.StorageData{/* StorageB */})

Then StorageA will be at index 0, and StorageB will be at index 1.

Pin an Index

You can also pin a piece of data to a specific index. For example, this will pin TransactionA at index 2.

app.AddTransaction(sdk.TransactionData{/* TransactionA */}, 2)

Let’s see pinning in a more complete example. Let’s say  you defined your Allocate function to allocate 32 data for Receipt, 32 for Storage, and 64 for Transaction.

func (c *AppCircuit) Allocate() (maxReceipts, maxStorage, maxTransaction) {
    return 32, 32, 32
}

Then, you added data queries to your BrevisApp instance:

app.AddReceipt(sdk.ReceiptData{/* ReceiptA */})

app.AddStorage(sdk.StorageData{/* StorageA */})
app.AddStorage(sdk.StorageData{/* StorageB */})

app.AddTransaction(sdk.TransactionData{/* TransactionA */})
// this one is fixed at index 2
const MyFixedSpot = 2
app.AddTransaction(sdk.TransactionData{/* TransactionB */}, MyFixedSpot)

The mental model of this would be:

Notice how there is an empty slot in transactions because we allocated 64 slots for transactions, but only added two. We also fixated TransactionB at index 2, so the slot index 1 remains empty. TransactionB will always be at index 2.

Accessing Data by Index in Circuit

Accessing data by index is closely related to how you allocate data slots. read more about Allocate

func (c *AppCircuit) Define(api *sdk.CircuitAPI, input sdk.CircuitInput) {
    transactions := sdk.NewDataStream(input.Transaction)
    // access transactionB directly
    transactionB := transactions.Get(MyFixedSpot)
}

Building the CircuitInput

sdk.CircutiInput is the packaged data obtained from executing your data queries and converting them into circuit types. This is used in testing, compiling, and proving.

After you have added queries to your BrevisApp, call app.BuildCircuitInput with your circuit definition to build.

// if your circuit has custom inputs, you'll need to supply a correct assignment 
// of those custom inputs
appCircuit := &AppCircuit{MyCustomInput: someCorrectValue}
circuitInput, err := app.BuildCircuitInput(appCircuit)

Submitting the Proof to Brevis

Note: Proof generation relies on a separate set of functions, but once you have a proof, your BrevisApp instance can handle submitting it to Brevis.

To submit your proof to Brevis, you need to first query Brevis RPC for the fee amount and acquire a requestId.

PrepareRequest Input

NameTypeDescription
vkVerifyingKeyapplication circuit verifying key
witnesswitness.Witnessapplication circuit witness
srcChainIduint64the id of data source chain
dstChainIduint64the chain where the proven data is used
appContractcommon.Addressdeveloper’s contract callback address
callbackGasLimituint64Gas limit for contract callback
queryOptionqueryOptionZK_MODE: pure zk flow. recommended for developers. OP_MODE: supported by BVN. wip
brevisPartnerKeystringnot required. Developer can use empty string to skip this flow.
calldata, requestId, nonce, feeValue, err := app.PrepareRequest(
    vk, 
    witness,
    srcChainId, 
    dstChainId, 
    refundee, 
    appContract,
    callbackGasLimit,
    gwproto.QueryOption_ZK_MODE.Enum(),
    "")

PrepareRequest Output

NameTypeDescription
calldata[]bytetransaction calldata
requestIdcommon.Hashquery key for future reference
nonceuint64transaction parameter
feeValuebig.intproving fee

Submitting the Proof

err := app.SubmitProof(proof)

You can optionally supply success and error callbacks. Note that the option sdk.WithFinalProofSubmittedCallback makes SubmitProof non-blocking. If you want a blocking way to wait for final proof submission, use app.WaitFinalProofSubmitted.

// Choose one:

err := app.SubmitProof(proof, sdk.WithFinalProofSubmittedCallback(...)) // async
// Or
err := app.SubmitProof(proof)
tx, err := app.WaitFinalProofSubmitted(context.Background()) // blocks the routine

Paying the Fee

The provers in the Brevis network only start working after you pay the fee. To pay, call the sendRequest function on the BrevisRequest contract (address) with the feeValue you got from PrepareRequest.

Note: You can pay the fee any time after you acquire the requestId and feeValue from PrepareRequest. This process done in parallel with SubmitProof and WaitFinalProofSubmitted.

Note:

Tip: Reducing End-to-end Proof Generation Time

Once PrepareRequest is called AND Brevis receives the fee, Brevis starts proving the proofs that are independent from your proof. If your circuit is big and wants to minimize the proof generation time, you can call PrepareRequest first, then pay the fee. This allows your proof and Brevis’s proofs to be generated in parallel.