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()

Adding Source Data

Source Data Types

type ReceiptData struct {
	BlockNum *big.Int
	TxHash   common.Hash
	Fields   [NumMaxLogFields]LogFieldData
}

// LogFieldData represents a single field of an event.
type LogFieldData struct {
	// The contract from which the event is emitted
	Contract common.Address
	// the index of the log in the receipt
	LogIndex uint
	// The event ID of the event to which the field belong (aka topics[0])
	EventID common.Hash
	// Whether the field is a topic (aka "indexed" as in solidity events)
	IsTopic bool
	// The index of the field in either a log's topics or data. For example, if a
	// field is the second topic of a log, then FieldIndex is 1; if a field is the
	// third field in the RLP decoded data, then FieldIndex is 2.
	FieldIndex uint
	// The value of the field in event, aka the actual thing we care about, only
	// 32-byte fixed length values are supported.
	Value common.Hash
}

type StorageData struct {
	BlockNum *big.Int
	Address  common.Address
	Key      common.Hash
	Value    common.Hash
}

type TransactionData struct {
	Hash     common.Hash
	ChainId  *big.Int
	BlockNum *big.Int
	Nonce    uint64
	// GasTipCapOrGasPrice is GasPrice for legacy tx (type 0) and GasTipCapOap for
	// dynamic-fee tx (type 2)
	GasTipCapOrGasPrice *big.Int
	// GasFeeCap is always 0 for legacy tx
	GasPriceOrFeeCap *big.Int
	GasLimit         uint64
	From             common.Address
	To               common.Address
	Value            *big.Int
}

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 AddReceipt 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 that you defined your Allocate function to allocate 1 data slot for Receipt, 2 for Storage, and 3 for Transaction.

func (c *AppCircuit) Allocate() (maxReceipts, maxStorage, maxTransaction) {
    return 1, 2, 3
}

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 3 slots for transactions, but only added two. We also fixated TransactionB at index 2, so the slot index 1 remains empty. TransactionB will always 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

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.

calldata, requestId, feeValue, err := app.PrepareRequest(
    vk, srcChainId, dstChainId, refundee, appContract)

srcChainId is the id of your data source chain. dstChainId is the chain that your app contract is deployed on and is where the proven data is used.

Currently, srcChainId can only be 1 (Ethereum Mainnet), and dstChainId can only be 11155111 (Sepolia Testnet). More options will be added as the project moves forward. Stay tuned!

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.

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.

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.

Last updated