Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: docs #8

Merged
merged 7 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ jobs:
run: |
export PROVIDER_URI=${{ secrets.PROVIDER_URI_SEPOLIA }}
cargo test -- --test-threads=1
- name: Test examples
run: |
export PROVIDER_URI=${{ secrets.PROVIDER_URI_SEPOLIA }}
mkdir data
cargo run --example keccak -- --input sdk/data/keccak_input.json -k 15 -c sdk/data/keccak_config.json keygen
cargo run --example keccak -- --input sdk/data/keccak_input.json -k 15 -c sdk/data/keccak_config.json run
cargo run --example rlc -- --input sdk/data/rlc_input.json -k 15 -c sdk/data/rlc_config.json keygen
cargo run --example rlc -- --input sdk/data/rlc_input.json -k 15 -c sdk/data/rlc_config.json run
cargo run --example account_age -- --input sdk/data/account_age_input.json -k 15 keygen
cargo run --example account_age -- --input sdk/data/account_age_input.json -k 15 run
cargo run --example quickstart -- --input sdk/data/quickstart_input.json -k 15 keygen
cargo run --example quickstart -- --input sdk/data/quickstart_input.json -k 15 run
116 changes: 114 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,118 @@

This repository is split into 3 components:

- `circuit`: Lower-level API for writing Axiom compute circuits
- `sdk`: User-friendly API for writing Axiom compute circuits
- `sdk-derive`: Procedural macros to for writing circuits with axiom-sdk
- `circuit`: Lower-level API for writing Axiom compute circuits
- `sdk-derive`: Procedural macros to for writing circuits with axiom-sdk

## axiom-sdk

### Installation

To install our Rust circuit SDK into a Cargo project, run:
```bash
cargo add axiom-sdk --git https://github.com/axiom-crypto/axiom-sdk-rs
```

### Overview

To implement an Axiom circuit using the Rust SDK you need to:

- Specify an input struct that consists of native Rust types and `ethers-rs` types (ie. `u64`, `Address`, `H256`, etc.). The struct name must end with `Input` (ie. `MyCircuitInput`).
- Implement the `AxiomComputeFn` trait on your input struct

### Input Specification

Your input struct can contain native Rust types (ie. `u64`, `[usize; N]`, etc.) and `ethers-rs` types (ie. `Address`, `H256`, etc.), and its name must end with `Input` (ie. `MyCircuitInput`). Additional types can be used if they implement the `RawInput` trait (see [here](./circuit/src/input/raw_input.rs)). The struct must be annotated with the #[AxiomComputeInput] attribute so that it implements the sufficient circuit traits. This attribute will also generate a new struct with `Input` replaced with `CircuitInput` (ie. `AccountAgeInput` -> `AccountAgeCircuitInput`), which has all the fields of the specified struct, but with `halo2-lib` types to be used inside your circuit (like `AssignedValue<Fr>`).

Here is an example:

```rust
#[AxiomComputeInput]
pub struct AccountAgeInput {
pub addr: Address,
pub claimed_block_number: u64,
}
```

### Compute Function Specification

You must implement the `AxiomComputeFn` on your input struct. There is only one trait function that you must implement:
```rust
fn compute(
api: &mut AxiomAPI,
assigned_inputs: AccountAgeCircuitInput<AssignedValue<Fr>>,
) -> Vec<AxiomResult>
```
where `AccountAgeCircuitInput` should be replaced with your derived circuit input struct.

The `AxiomAPI` struct gives you access to subquery calling functions in addition to a `RlcCircuitBuilder` to specify your circuit. Your compute function should then return any values that you wish to pass on-chain in the `Vec<AxiomResult>` -- an `AxiomResult` is either an enum of either `HiLo<AssignedValue<Fr>>` or `AssignedValue<Fr>` (in which case it is converted to hi-lo for you).

Here is an example:
```rust
impl AxiomComputeFn for AccountAgeInput {
fn compute(
api: &mut AxiomAPI,
assigned_inputs: AccountAgeCircuitInput<AssignedValue<Fr>>,
) -> Vec<AxiomResult> {
let gate = GateChip::new();
let zero = api.ctx().load_zero();
let one = api.ctx().load_constant(Fr::one());
let prev_block = gate.sub(api.ctx(), assigned_inputs.claimed_block_number, one);

let account_prev_block = api.get_account(prev_block, assigned_inputs.addr);
let prev_nonce = account_prev_block.call(AccountField::Nonce);
let prev_nonce = api.from_hi_lo(prev_nonce);
api.ctx().constrain_equal(&prev_nonce, &zero);

let account = api.get_account(assigned_inputs.claimed_block_number, assigned_inputs.addr);
let curr_nonce = account.call(AccountField::Nonce);
let curr_nonce = api.from_hi_lo(curr_nonce);

api.range.check_less_than(api.ctx(), zero, curr_nonce, 40);

vec![
assigned_inputs.addr.into(),
assigned_inputs.claimed_block_number.into(),
]
}
}
```

### Running The Circuit

To run your circuit, create a `main` function call the `run_cli` function with your input struct as the generic parameter:
```rust
fn main() {
env_logger::init();
run_cli::<AccountAgeInput>();
}
```
The `main` function will run a CLI that allows you to run mock proving, key generation, and proving of your circuit. The CLI has the following commands:

```
Commands:
mock Run the mock prover
keygen Generate new proving & verifying keys
prove Generate a new proof
run Generate an Axiom compute query
help Print this message or the help of the given subcommand(s)

Options:
-k, --degree <DEGREE> To determine the size of your circuit (12..25)
-p, --provider <PROVIDER> JSON RPC provider URI
-i, --input <INPUT_PATH> JSON inputs to feed into your circuit
-d, --data-path <DATA_PATH> For saving build artifacts (optional)
-c, --config <CONFIG> For custom advanced usage only (optional)
-h, --help Print help
-V, --version Print version
```

For example:

```bash
cargo run --example account_age -- --input data/account_age_input.json -k 12 -p <PROVIDER_URI> <CMD>
```

where `PROVIDER_URI` is a JSON-RPC URI, and `CMD` is `mock`, `prove`, `keygen`, or `run`.

2 changes: 1 addition & 1 deletion sdk-derive/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ pub fn impl_flatten_and_raw_input(ast: &DeriveInput) -> TokenStream {
}
}

impl #old_impl_generics axiom_sdk::compute::AxiomComputeInput for #raw_circuit_name_ident #old_ty_generics {
impl #old_impl_generics axiom_sdk::axiom::AxiomComputeInput for #raw_circuit_name_ident #old_ty_generics {
type LogicInput = #raw_circuit_name_ident #old_ty_generics;
type Input<T: Copy> = #name #ty_generics;
}
Expand Down
4 changes: 4 additions & 0 deletions sdk-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ mod input;

#[proc_macro_attribute]
#[allow(non_snake_case)]
/// Derive the `AxiomComputeInput` trait for a struct.
/// The struct must be named `_Input`. Ex: `ExampleInput`.
/// All the fields of the struct must implement `RawInput` from `axiom_circuit::input::raw_input`,
/// which has already been implemented for most primitive Rust types and Ethers types.
pub fn AxiomComputeInput(_args: TokenStream, input: TokenStream) -> TokenStream {
let input_clone = input.clone();
let ast = parse_macro_input!(input_clone as ItemStruct);
Expand Down
1 change: 0 additions & 1 deletion sdk/data/keccak_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"num_fixed": 1,
"num_lookup_advice_per_phase": [
5,
0,
0
],
"lookup_bits": 14,
Expand Down
3 changes: 1 addition & 2 deletions sdk/data/rlc_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
"num_fixed": 1,
"num_lookup_advice_per_phase": [
5,
1,
0
1
],
"lookup_bits": 14,
"num_rlc_columns": 1
Expand Down
14 changes: 8 additions & 6 deletions sdk/examples/account_age.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::fmt::Debug;

use axiom_sdk::{
axiom::{AxiomAPI, AxiomComputeFn, AxiomResult},
axiom::{AxiomAPI, AxiomComputeFn, AxiomComputeInput, AxiomResult},
cmd::run_cli,
ethers::types::Address,
halo2_base::{
gates::{GateChip, GateInstructions, RangeInstructions},
gates::{GateInstructions, RangeInstructions},
AssignedValue,
},
subquery::AccountField,
AxiomComputeInput, Fr,
Fr,
};
use ethers::types::Address;

#[AxiomComputeInput]
pub struct AccountAgeInput {
Expand All @@ -23,10 +23,12 @@ impl AxiomComputeFn for AccountAgeInput {
api: &mut AxiomAPI,
assigned_inputs: AccountAgeCircuitInput<AssignedValue<Fr>>,
) -> Vec<AxiomResult> {
let gate = GateChip::new();
let zero = api.ctx().load_zero();
let one = api.ctx().load_constant(Fr::one());
let prev_block = gate.sub(api.ctx(), assigned_inputs.claimed_block_number, one);
let prev_block = api
.range
.gate()
.sub(api.ctx(), assigned_inputs.claimed_block_number, one);

let account_prev_block = api.get_account(prev_block, assigned_inputs.addr);
let prev_nonce = account_prev_block.call(AccountField::Nonce);
Expand Down
4 changes: 2 additions & 2 deletions sdk/examples/keccak.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::fmt::Debug;

use axiom_sdk::{
axiom::{AxiomAPI, AxiomComputeFn, AxiomResult},
axiom::{AxiomAPI, AxiomComputeFn, AxiomComputeInput, AxiomResult},
cmd::run_cli,
halo2_base::AssignedValue,
AxiomComputeInput, Fr,
Fr,
};

#[AxiomComputeInput]
Expand Down
6 changes: 3 additions & 3 deletions sdk/examples/quickstart.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::{fmt::Debug, str::FromStr};

use axiom_sdk::{
axiom::{AxiomAPI, AxiomComputeFn, AxiomResult},
axiom::{AxiomAPI, AxiomComputeFn, AxiomComputeInput, AxiomResult},
cmd::run_cli,
ethers::types::{Address, H256},
halo2_base::AssignedValue,
subquery::{AccountField, HeaderField, TxField},
AxiomComputeInput, Fr, HiLo,
Fr, HiLo,
};
use ethers::types::{Address, H256};

#[AxiomComputeInput]
pub struct QuickstartInput {
Expand Down
14 changes: 10 additions & 4 deletions sdk/examples/rlc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use std::fmt::Debug;

use axiom_circuit::axiom_eth::rlc::circuit::builder::RlcCircuitBuilder;
use axiom_sdk::{
axiom::{AxiomAPI, AxiomComputeFn, AxiomResult},
axiom::{AxiomAPI, AxiomComputeFn, AxiomComputeInput, AxiomResult},
cmd::run_cli,
halo2_base::{
gates::{RangeChip, RangeInstructions},
gates::{GateInstructions, RangeChip, RangeInstructions},
AssignedValue,
},
AxiomComputeInput, Fr,
Fr,
};

#[AxiomComputeInput]
Expand Down Expand Up @@ -42,7 +42,13 @@ impl AxiomComputeFn for RlcInput {
) {
let gate = range.gate();
let rlc_chip = builder.rlc_chip(gate);
rlc_chip.compute_rlc_fixed_len(builder.base.main(1), payload);
let (ctx, rlc_ctx) = builder.rlc_ctx_pair();
gate.add(ctx, payload[0], payload[1]);
let x = vec![
ctx.load_constant(Fr::from(1)),
ctx.load_constant(Fr::from(2)),
];
rlc_chip.compute_rlc_fixed_len(rlc_ctx, x);
}
}

Expand Down
2 changes: 1 addition & 1 deletion sdk/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Usage

See [./examples/account_age.rs] for an example Axiom compute circuit. To run the `account_age` circuit:
See [./examples/account_age.rs](./examples/account_age.rs) for an example Axiom compute circuit. To run the `account_age` circuit:

```
cargo run --example account_age -- --input data/account_age_input.json -k 12 -p <PROVIDER_URI> <CMD>
Expand Down
43 changes: 42 additions & 1 deletion sdk/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ use crate::{
Fr,
};

/// Axiom Circuit API for making both subquery calls (e.g. `get_account`, `get_header`, etc.) and for more general ZK primitives (e.g. `add`, `mul`, etc.).
pub struct AxiomAPI<'a> {
/// The `halo2-lib` struct used to construct the circuit
pub builder: &'a mut RlcCircuitBuilder<Fr>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add basic comment this is an axiom-eth / halo2-lib struct

/// The main chip for ZK primitives
pub range: &'a RangeChip<Fr>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment this is main chip for zk primitives

pub subquery_caller: Arc<Mutex<SubqueryCaller<Http, Fr>>>,
/// The struct that manages all subquery calls
subquery_caller: Arc<Mutex<SubqueryCaller<Http, Fr>>>,
}

impl<'a> AxiomAPI<'a> {
Expand All @@ -43,24 +47,41 @@ impl<'a> AxiomAPI<'a> {
}
}

/// Returns a thread-safe [SubqueryCaller] object.
pub fn subquery_caller(&self) -> Arc<Mutex<SubqueryCaller<Http, Fr>>> {
self.subquery_caller.clone()
}

/// Returns a mutable reference to the [Context] of a gate thread. Spawns a new thread for the given phase, if none exists.
/// * `phase`: The challenge phase (as an index) of the gate thread.
pub fn ctx(&mut self) -> &mut Context<Fr> {
self.builder.base.main(0)
}

/// Returns an `AssignedValue<Fr>` from a `HiLo<AssignedValue<Fr>>`.
///
/// NOTE: this can fail if the hi-lo pair is greater than the `Fr` modulus. See `check_hi_lo` for what is constrained.
///
/// * `hilo` - The `HiLo<AssignedValue<Fr>>` object to convert.
pub fn from_hi_lo(&mut self, hilo: HiLo<AssignedValue<Fr>>) -> AssignedValue<Fr> {
let ctx = self.builder.base.main(0);
from_hi_lo(ctx, self.range, hilo)
}

/// Returns a 256-bit `HiLo<AssignedValue<Fr>>` from a `AssignedValue<Fr>`.
///
/// See `check_hi_lo` for what is constrained.
///
/// * `val` - The `AssignedValue<Fr>` object to convert.
pub fn to_hi_lo(&mut self, val: AssignedValue<Fr>) -> HiLo<AssignedValue<Fr>> {
let ctx = self.builder.base.main(0);
to_hi_lo(ctx, self.range, val)
}

/// Returns an [Account] builder given block number and address.
///
/// * `block_number` - The block number as an `AssignedValue<Fr>`.
/// * `addr` - The address as an `AssignedValue<Fr>`.
pub fn get_account(
&mut self,
block_number: AssignedValue<Fr>,
Expand All @@ -70,11 +91,19 @@ impl<'a> AxiomAPI<'a> {
get_account(ctx, self.subquery_caller.clone(), block_number, addr)
}

/// Returns a [Header] builder given block number.
///
/// * `block_number` - The block number as an `AssignedValue<Fr>`.
pub fn get_header(&mut self, block_number: AssignedValue<Fr>) -> Header {
let ctx = self.builder.base.main(0);
get_header(ctx, self.subquery_caller.clone(), block_number)
}

/// Returns a [SolidityMapping] builder given block number, address, and mapping slot.
///
/// * `block_number` - The block number as an `AssignedValue<Fr>`.
/// * `addr` - The address as an `AssignedValue<Fr>`.
/// * `mapping_slot` - The mapping slot as a `HiLo<AssignedValue<Fr>`.
pub fn get_mapping(
&mut self,
block_number: AssignedValue<Fr>,
Expand All @@ -91,6 +120,10 @@ impl<'a> AxiomAPI<'a> {
)
}

/// Returns a [Receipt] builder given block number and transaction index.
///
/// * `block_number` - The block number as an `AssignedValue<Fr>`.
/// * `tx_idx` - The transaction index as an `AssignedValue<Fr>`.
pub fn get_receipt(
&mut self,
block_number: AssignedValue<Fr>,
Expand All @@ -100,6 +133,10 @@ impl<'a> AxiomAPI<'a> {
get_receipt(ctx, self.subquery_caller.clone(), block_number, tx_idx)
}

/// Returns a [Storage] builder given block number and address.
///
/// * `block_number` - The block number as an `AssignedValue<Fr>`.
/// * `addr` - The address as an `AssignedValue<Fr>`.
pub fn get_storage(
&mut self,
block_number: AssignedValue<Fr>,
Expand All @@ -109,6 +146,10 @@ impl<'a> AxiomAPI<'a> {
get_storage(ctx, self.subquery_caller.clone(), block_number, addr)
}

/// Returns a [Tx] builder given block number and transaction index.
///
/// * `block_number` - The block number as an `AssignedValue<Fr>`.
/// * `tx_idx` - The transaction index as an `AssignedValue<Fr>`.
pub fn get_tx(&mut self, block_number: AssignedValue<Fr>, tx_idx: AssignedValue<Fr>) -> Tx {
let ctx = self.builder.base.main(0);
get_tx(ctx, self.subquery_caller.clone(), block_number, tx_idx)
Expand Down
Loading
Loading