From 8e7078d723711aa7af2c32818b777733bb6d4a94 Mon Sep 17 00:00:00 2001
From: Tom French <tom@tomfren.ch>
Date: Wed, 30 Aug 2023 20:06:25 +0100
Subject: [PATCH 1/3] chore: pull `acvm-backend-barretenberg` into main repo

---
 Cargo.lock                                    |   41 +-
 Cargo.toml                                    |    1 +
 crates/acvm_backend_barretenberg/CHANGELOG.md |  233 ++
 crates/acvm_backend_barretenberg/Cargo.toml   |   30 +
 crates/acvm_backend_barretenberg/build.rs     |   32 +
 .../src/1_mul.bytecode                        |    1 +
 .../src/bb/contract.rs                        |   75 +
 .../acvm_backend_barretenberg/src/bb/gates.rs |   71 +
 .../acvm_backend_barretenberg/src/bb/mod.rs   |  123 +
 .../acvm_backend_barretenberg/src/bb/prove.rs |   77 +
 .../src/bb/prove_and_verify.rs                |   68 +
 .../src/bb/verify.rs                          |   87 +
 .../src/bb/write_vk.rs                        |   66 +
 .../src/contract.sol                          | 2539 +++++++++++++++++
 crates/acvm_backend_barretenberg/src/lib.rs   |   40 +
 .../src/proof_system.rs                       |  249 ++
 .../src/smart_contract.rs                     |  108 +
 crates/nargo_cli/Cargo.toml                   |    2 +-
 18 files changed, 3807 insertions(+), 36 deletions(-)
 create mode 100644 crates/acvm_backend_barretenberg/CHANGELOG.md
 create mode 100644 crates/acvm_backend_barretenberg/Cargo.toml
 create mode 100644 crates/acvm_backend_barretenberg/build.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/1_mul.bytecode
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/contract.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/gates.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/mod.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/prove.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/verify.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/bb/write_vk.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/contract.sol
 create mode 100644 crates/acvm_backend_barretenberg/src/lib.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/proof_system.rs
 create mode 100644 crates/acvm_backend_barretenberg/src/smart_contract.rs

diff --git a/Cargo.lock b/Cargo.lock
index 00f51294521..02d8a2edcc8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -48,15 +48,13 @@ dependencies = [
 
 [[package]]
 name = "acvm-backend-barretenberg"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b2c96a9e4ea5b057e027b6f673dfd78260046aef67bdfcd867480575baa1d53"
+version = "0.11.0"
 dependencies = [
  "acvm",
  "base64",
  "build-target",
  "const_format",
- "dirs 5.0.1",
+ "dirs",
  "flate2",
  "reqwest",
  "tar",
@@ -1162,16 +1160,7 @@ version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
 dependencies = [
- "dirs-sys 0.3.7",
-]
-
-[[package]]
-name = "dirs"
-version = "5.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
-dependencies = [
- "dirs-sys 0.4.1",
+ "dirs-sys",
 ]
 
 [[package]]
@@ -1195,18 +1184,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "dirs-sys"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
-dependencies = [
- "libc",
- "option-ext",
- "redox_users",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "dirs-sys-next"
 version = "0.1.2"
@@ -2189,7 +2166,7 @@ dependencies = [
  "color-eyre",
  "const_format",
  "criterion",
- "dirs 4.0.0",
+ "dirs",
  "fm",
  "hex",
  "iai",
@@ -2221,7 +2198,7 @@ dependencies = [
 name = "nargo_toml"
 version = "0.10.5"
 dependencies = [
- "dirs 4.0.0",
+ "dirs",
  "fm",
  "nargo",
  "noirc_frontend",
@@ -2449,12 +2426,6 @@ version = "11.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
 
-[[package]]
-name = "option-ext"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
-
 [[package]]
 name = "owo-colors"
 version = "3.5.0"
@@ -3403,7 +3374,7 @@ version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4"
 dependencies = [
- "dirs 4.0.0",
+ "dirs",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index fe383867bed..5f6ff3fa1d2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,7 @@
 [workspace]
 
 members = [
+    "crates/acvm_backend_barretenberg",
     "crates/noirc_evaluator",
     "crates/noirc_frontend",
     "crates/noirc_errors",
diff --git a/crates/acvm_backend_barretenberg/CHANGELOG.md b/crates/acvm_backend_barretenberg/CHANGELOG.md
new file mode 100644
index 00000000000..4387d8ccb5f
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/CHANGELOG.md
@@ -0,0 +1,233 @@
+# Changelog
+
+## [0.11.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.10.1...v0.11.0) (2023-08-18)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update `acvm` to 0.22.0 ([#240](https://github.com/noir-lang/acvm-backend-barretenberg/issues/240))
+
+### Features
+
+* Update `acvm` to 0.22.0 ([#240](https://github.com/noir-lang/acvm-backend-barretenberg/issues/240)) ([d8342fd](https://github.com/noir-lang/acvm-backend-barretenberg/commit/d8342fd6da605ac3bbd889edf89cd122bc4689ce))
+
+## [0.10.1](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.10.0...v0.10.1) (2023-08-18)
+
+
+### Features
+
+* Migrate to `wasmer` 3.3.0 ([#236](https://github.com/noir-lang/acvm-backend-barretenberg/issues/236)) ([e115e38](https://github.com/noir-lang/acvm-backend-barretenberg/commit/e115e38856887c6b1eeead3534534ac7e6327ea9))
+
+## [0.10.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.9.1...v0.10.0) (2023-07-26)
+
+
+### ⚠ BREAKING CHANGES
+
+* Migrate to ACVM 0.21.0 ([#234](https://github.com/noir-lang/acvm-backend-barretenberg/issues/234))
+
+### Features
+
+* Migrate to ACVM 0.21.0 ([#234](https://github.com/noir-lang/acvm-backend-barretenberg/issues/234)) ([15c8676](https://github.com/noir-lang/acvm-backend-barretenberg/commit/15c86768685d2946a767c350f6ef5972c86677eb))
+
+## [0.9.1](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.9.0...v0.9.1) (2023-07-21)
+
+
+### Features
+
+* add support for atomic memory opcodes ([#232](https://github.com/noir-lang/acvm-backend-barretenberg/issues/232)) ([a7aa6e9](https://github.com/noir-lang/acvm-backend-barretenberg/commit/a7aa6e9505bb402c1b3db0a990845ed26928e7aa))
+
+## [0.9.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.8.0...v0.9.0) (2023-07-17)
+
+
+### ⚠ BREAKING CHANGES
+
+* update to ACVM 0.19.0 ([#230](https://github.com/noir-lang/acvm-backend-barretenberg/issues/230))
+
+### Miscellaneous Chores
+
+* update to ACVM 0.19.0 ([#230](https://github.com/noir-lang/acvm-backend-barretenberg/issues/230)) ([3f1d967](https://github.com/noir-lang/acvm-backend-barretenberg/commit/3f1d9674b904acb02c2a3e52481be8a6104c3a9d))
+
+## [0.8.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.7.0...v0.8.0) (2023-07-12)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update to acvm 0.18.1 ([#228](https://github.com/noir-lang/acvm-backend-barretenberg/issues/228))
+
+### Features
+
+* Update to acvm 0.18.1 ([#228](https://github.com/noir-lang/acvm-backend-barretenberg/issues/228)) ([397098b](https://github.com/noir-lang/acvm-backend-barretenberg/commit/397098b239efbe16785b1c9af108ca9fc4e24497))
+
+## [0.7.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.6.1...v0.7.0) (2023-07-08)
+
+
+### ⚠ BREAKING CHANGES
+
+* **bberg:** add secp256r1 builtin to barretenberg ([#223](https://github.com/noir-lang/acvm-backend-barretenberg/issues/223))
+
+### Features
+
+* **bberg:** add secp256r1 builtin to barretenberg ([#223](https://github.com/noir-lang/acvm-backend-barretenberg/issues/223)) ([ceb4770](https://github.com/noir-lang/acvm-backend-barretenberg/commit/ceb47705a492fcdcea1f3c098aaab42ea8edbf2e))
+
+## [0.6.1](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.6.0...v0.6.1) (2023-07-06)
+
+
+### Features
+
+* switch RecursiveAggregation support to true ([#225](https://github.com/noir-lang/acvm-backend-barretenberg/issues/225)) ([e9462ae](https://github.com/noir-lang/acvm-backend-barretenberg/commit/e9462ae015ec0dfb0a23ccbb89562071f87940f5))
+
+## [0.6.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.5.1...v0.6.0) (2023-07-06)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update to ACVM 0.16.0  ([#221](https://github.com/noir-lang/acvm-backend-barretenberg/issues/221))
+
+### Features
+
+* Update to ACVM 0.16.0  ([#221](https://github.com/noir-lang/acvm-backend-barretenberg/issues/221)) ([062d5ed](https://github.com/noir-lang/acvm-backend-barretenberg/commit/062d5ed9b476fab8ac8d3ca13371699fb2aac332))
+
+## [0.5.1](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.5.0...v0.5.1) (2023-06-20)
+
+
+### Bug Fixes
+
+* Remove wasm32 target ([#219](https://github.com/noir-lang/acvm-backend-barretenberg/issues/219)) ([e4cbb6d](https://github.com/noir-lang/acvm-backend-barretenberg/commit/e4cbb6d476e8746de33c38506e2fcb970f1c866a))
+
+## [0.5.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.4.0...v0.5.0) (2023-06-15)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update to target ACVM 0.15.0 ([#217](https://github.com/noir-lang/acvm-backend-barretenberg/issues/217))
+
+### Features
+
+* Update to target ACVM 0.15.0 ([#217](https://github.com/noir-lang/acvm-backend-barretenberg/issues/217)) ([9331898](https://github.com/noir-lang/acvm-backend-barretenberg/commit/9331898f161321c8b6a82d5ea850f197952b2ed2))
+
+## [0.4.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.3.0...v0.4.0) (2023-06-07)
+
+
+### ⚠ BREAKING CHANGES
+
+* Recursion ([#207](https://github.com/noir-lang/acvm-backend-barretenberg/issues/207))
+
+### Features
+
+* Recursion ([#207](https://github.com/noir-lang/acvm-backend-barretenberg/issues/207)) ([6fc479b](https://github.com/noir-lang/acvm-backend-barretenberg/commit/6fc479b9ae99d59bbfeb1b895d63cdbea469dcaa))
+
+## [0.3.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.2.0...v0.3.0) (2023-06-01)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update to ACVM 0.13.0 ([#205](https://github.com/noir-lang/acvm-backend-barretenberg/issues/205))
+* added keccakvar constraints ([#213](https://github.com/noir-lang/acvm-backend-barretenberg/issues/213))
+* update pedersen hashes for new implementation ([#212](https://github.com/noir-lang/acvm-backend-barretenberg/issues/212))
+
+### Features
+
+* added keccakvar constraints ([91ea65f](https://github.com/noir-lang/acvm-backend-barretenberg/commit/91ea65f6af7039095c7a3af7bc1e4ce302a68a8d))
+* added keccakvar constraints ([#213](https://github.com/noir-lang/acvm-backend-barretenberg/issues/213)) ([91ea65f](https://github.com/noir-lang/acvm-backend-barretenberg/commit/91ea65f6af7039095c7a3af7bc1e4ce302a68a8d))
+* Update to ACVM 0.13.0 ([#205](https://github.com/noir-lang/acvm-backend-barretenberg/issues/205)) ([298446e](https://github.com/noir-lang/acvm-backend-barretenberg/commit/298446ef8b69f528b6e2fd2abb2298d7b0a8118e))
+
+
+### Bug Fixes
+
+* Add or cleanup implementations for JS target ([#199](https://github.com/noir-lang/acvm-backend-barretenberg/issues/199)) ([f6134b7](https://github.com/noir-lang/acvm-backend-barretenberg/commit/f6134b7b502cb74882300b0046ab91ab000daf3c))
+* update pedersen hashes for new impl ([9a233ce](https://github.com/noir-lang/acvm-backend-barretenberg/commit/9a233ce8db9984b29b9cce0603f758d5281c89c9))
+* update pedersen hashes for new implementation ([#212](https://github.com/noir-lang/acvm-backend-barretenberg/issues/212)) ([9a233ce](https://github.com/noir-lang/acvm-backend-barretenberg/commit/9a233ce8db9984b29b9cce0603f758d5281c89c9))
+
+## [0.2.0](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.1.2...v0.2.0) (2023-05-22)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update to acvm 0.12.0 ([#165](https://github.com/noir-lang/acvm-backend-barretenberg/issues/165))
+* Add serialization logic for RAM and ROM opcodes ([#153](https://github.com/noir-lang/acvm-backend-barretenberg/issues/153))
+
+### Features
+
+* Add serde to `ConstraintSystem` types ([#196](https://github.com/noir-lang/acvm-backend-barretenberg/issues/196)) ([4c04a79](https://github.com/noir-lang/acvm-backend-barretenberg/commit/4c04a79e6d2b0115f3b4526c60f9f7dae8b464ae))
+* Add serialization logic for RAM and ROM opcodes ([#153](https://github.com/noir-lang/acvm-backend-barretenberg/issues/153)) ([3d3847d](https://github.com/noir-lang/acvm-backend-barretenberg/commit/3d3847de70e74a8f65c64e165ad15ae3d31f5350))
+* Update to acvm 0.12.0 ([#165](https://github.com/noir-lang/acvm-backend-barretenberg/issues/165)) ([d613c79](https://github.com/noir-lang/acvm-backend-barretenberg/commit/d613c79584a599f4adbd11d2ce3b61403c185b73))
+
+## [0.1.2](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.1.1...v0.1.2) (2023-05-11)
+
+
+### Bug Fixes
+
+* Remove star dependencies to allow publishing ([#182](https://github.com/noir-lang/acvm-backend-barretenberg/issues/182)) ([1727a79](https://github.com/noir-lang/acvm-backend-barretenberg/commit/1727a79ce7e66d95528f70c445cb4ec1b1ece636))
+
+## [0.1.1](https://github.com/noir-lang/acvm-backend-barretenberg/compare/v0.1.0...v0.1.1) (2023-05-11)
+
+
+### Bug Fixes
+
+* Add description so crate can be published ([#180](https://github.com/noir-lang/acvm-backend-barretenberg/issues/180)) ([caabf94](https://github.com/noir-lang/acvm-backend-barretenberg/commit/caabf9434031c6023a5e3a436c87fba0a1072539))
+
+## 0.1.0 (2023-05-10)
+
+
+### ⚠ BREAKING CHANGES
+
+* Update to ACVM v0.11.0 ([#151](https://github.com/noir-lang/acvm-backend-barretenberg/issues/151))
+* Add Keccak constraints ([#150](https://github.com/noir-lang/acvm-backend-barretenberg/issues/150))
+* migrate to ACVM 0.10.3 ([#148](https://github.com/noir-lang/acvm-backend-barretenberg/issues/148))
+* remove all crates other than `acvm-backend-barretenberg` and remove workspace ([#147](https://github.com/noir-lang/acvm-backend-barretenberg/issues/147))
+* merge `barretenberg_static_lib` and `barretenberg_wasm` ([#117](https://github.com/noir-lang/acvm-backend-barretenberg/issues/117))
+* remove dead blake2 code ([#137](https://github.com/noir-lang/acvm-backend-barretenberg/issues/137))
+* Implement pseudo-builder pattern for ConstraintSystem & hide struct fields ([#120](https://github.com/noir-lang/acvm-backend-barretenberg/issues/120))
+* return boolean rather than `FieldElement` from `verify_signature` ([#123](https://github.com/noir-lang/acvm-backend-barretenberg/issues/123))
+* avoid exposing internals of Assignments type ([#119](https://github.com/noir-lang/acvm-backend-barretenberg/issues/119))
+* update to acvm 0.9.0 ([#106](https://github.com/noir-lang/acvm-backend-barretenberg/issues/106))
+* Depend upon upstream barretenberg & switch to UltraPlonk ([#84](https://github.com/noir-lang/acvm-backend-barretenberg/issues/84))
+* update to ACVM 0.7.0 ([#90](https://github.com/noir-lang/acvm-backend-barretenberg/issues/90))
+* Remove create_proof and verify functions ([#82](https://github.com/noir-lang/acvm-backend-barretenberg/issues/82))
+* update to acvm v0.5.0 ([#60](https://github.com/noir-lang/acvm-backend-barretenberg/issues/60))
+
+### Features
+
+* **acvm_interop:** Updates to reflect new acvm methods using pk/vk ([#50](https://github.com/noir-lang/acvm-backend-barretenberg/issues/50)) ([cff757d](https://github.com/noir-lang/acvm-backend-barretenberg/commit/cff757dca7971161e4bd25e7a744d910c37c22be))
+* Add Keccak constraints ([#150](https://github.com/noir-lang/acvm-backend-barretenberg/issues/150)) ([ce2b9ed](https://github.com/noir-lang/acvm-backend-barretenberg/commit/ce2b9ed456bd8d2ad8357c15736d62c2a5812add))
+* allow overriding transcript location with BARRETENBERG_TRANSCRIPT env var ([#86](https://github.com/noir-lang/acvm-backend-barretenberg/issues/86)) ([af92b99](https://github.com/noir-lang/acvm-backend-barretenberg/commit/af92b99c7b5f37e9659931af378a851b3658a80b))
+* **ci:** add concurrency group for rust workflow ([#63](https://github.com/noir-lang/acvm-backend-barretenberg/issues/63)) ([5c936bc](https://github.com/noir-lang/acvm-backend-barretenberg/commit/5c936bc63cc3adcf9d43c9c4ce69053566089ad9))
+* Depend upon upstream barretenberg & switch to UltraPlonk ([#84](https://github.com/noir-lang/acvm-backend-barretenberg/issues/84)) ([8437bf7](https://github.com/noir-lang/acvm-backend-barretenberg/commit/8437bf7e08acadf43b55b307545336596a9fe766))
+* Implement pseudo-builder pattern for ConstraintSystem & hide struct fields ([#120](https://github.com/noir-lang/acvm-backend-barretenberg/issues/120)) ([8ed67d6](https://github.com/noir-lang/acvm-backend-barretenberg/commit/8ed67d68c71d655e1a6a5c38fa9ea1c3566f771d))
+* Leverage rustls when using downloader crate ([#46](https://github.com/noir-lang/acvm-backend-barretenberg/issues/46)) ([9de36b6](https://github.com/noir-lang/acvm-backend-barretenberg/commit/9de36b642d125d1fb4facd1bf60db67946be70ae))
+* merge `barretenberg_static_lib` and `barretenberg_wasm` ([#117](https://github.com/noir-lang/acvm-backend-barretenberg/issues/117)) ([ba1d0d6](https://github.com/noir-lang/acvm-backend-barretenberg/commit/ba1d0d61b94de91b15044d97608907c21bfb5299))
+* migrate to ACVM 0.10.3 ([#148](https://github.com/noir-lang/acvm-backend-barretenberg/issues/148)) ([c9fb9e8](https://github.com/noir-lang/acvm-backend-barretenberg/commit/c9fb9e806f1400a2ff7594a0669bec56025220bb))
+* remove all crates other than `acvm-backend-barretenberg` and remove workspace ([#147](https://github.com/noir-lang/acvm-backend-barretenberg/issues/147)) ([8fe7111](https://github.com/noir-lang/acvm-backend-barretenberg/commit/8fe7111ebdcb043764a83436744662e8c3ca5abc))
+* remove dead blake2 code ([#137](https://github.com/noir-lang/acvm-backend-barretenberg/issues/137)) ([14d8a5b](https://github.com/noir-lang/acvm-backend-barretenberg/commit/14d8a5b893eb1cb91d5bde908643b487b41809d6))
+* replace `downloader` dependency with `reqwest` ([#114](https://github.com/noir-lang/acvm-backend-barretenberg/issues/114)) ([dd62231](https://github.com/noir-lang/acvm-backend-barretenberg/commit/dd62231b8bfcee32e1029d31a07895b16159339c))
+* return boolean from `verify_signature` ([e560602](https://github.com/noir-lang/acvm-backend-barretenberg/commit/e560602ebbd547386ca4cab35735ffa92e98ac4b))
+* return boolean rather than `FieldElement` from `check_membership` ([#124](https://github.com/noir-lang/acvm-backend-barretenberg/issues/124)) ([a0a338e](https://github.com/noir-lang/acvm-backend-barretenberg/commit/a0a338e2295635a07f6b9e497c029160a5f323bc))
+* return boolean rather than `FieldElement` from `verify_signature` ([#123](https://github.com/noir-lang/acvm-backend-barretenberg/issues/123)) ([e560602](https://github.com/noir-lang/acvm-backend-barretenberg/commit/e560602ebbd547386ca4cab35735ffa92e98ac4b))
+* store transcript in `.nargo/backends` directory ([#91](https://github.com/noir-lang/acvm-backend-barretenberg/issues/91)) ([c6b5023](https://github.com/noir-lang/acvm-backend-barretenberg/commit/c6b50231da065e7550bfe8bddf8e46f4cd8002d7))
+* update `aztec_backend_wasm` to use new serialization ([#94](https://github.com/noir-lang/acvm-backend-barretenberg/issues/94)) ([28014d8](https://github.com/noir-lang/acvm-backend-barretenberg/commit/28014d803d052a7f459e03dbd7b5b9210449b1d0))
+* update to acvm 0.9.0 ([#106](https://github.com/noir-lang/acvm-backend-barretenberg/issues/106)) ([ff350fb](https://github.com/noir-lang/acvm-backend-barretenberg/commit/ff350fb111043964b8a14fc0df62508c87506423))
+* Update to ACVM v0.11.0 ([#151](https://github.com/noir-lang/acvm-backend-barretenberg/issues/151)) ([9202415](https://github.com/noir-lang/acvm-backend-barretenberg/commit/92024155532e15f25acb2f3ed8d5ca78da0fddd9))
+* update to acvm v0.5.0 ([#60](https://github.com/noir-lang/acvm-backend-barretenberg/issues/60)) ([74b4d8d](https://github.com/noir-lang/acvm-backend-barretenberg/commit/74b4d8d8b118e4477880c04149e5e9d93d388384))
+
+
+### Bug Fixes
+
+* Avoid exposing internals of Assignments type ([614c81b](https://github.com/noir-lang/acvm-backend-barretenberg/commit/614c81b0ea5e110bbf5a61a526bb0173f4fe377a))
+* avoid exposing internals of Assignments type ([#119](https://github.com/noir-lang/acvm-backend-barretenberg/issues/119)) ([614c81b](https://github.com/noir-lang/acvm-backend-barretenberg/commit/614c81b0ea5e110bbf5a61a526bb0173f4fe377a))
+* fix serialisation of arithmetic expressions ([#145](https://github.com/noir-lang/acvm-backend-barretenberg/issues/145)) ([7f42535](https://github.com/noir-lang/acvm-backend-barretenberg/commit/7f4253570257d9dedcfa8c8fb96b9d097ef06419))
+* Implement random_get for wasm backend ([#102](https://github.com/noir-lang/acvm-backend-barretenberg/issues/102)) ([9c0f06e](https://github.com/noir-lang/acvm-backend-barretenberg/commit/9c0f06ef56f23e2b5794e810f433e36ff2c5d6b5))
+* rename gates to opcodes ([#59](https://github.com/noir-lang/acvm-backend-barretenberg/issues/59)) ([6e05307](https://github.com/noir-lang/acvm-backend-barretenberg/commit/6e053072d8b9c5d93c296f10782251ccb597f902))
+* reorganize and ensure contracts can be compiled in Remix ([#112](https://github.com/noir-lang/acvm-backend-barretenberg/issues/112)) ([7ec5693](https://github.com/noir-lang/acvm-backend-barretenberg/commit/7ec5693f194a79c379ae2952bc17a31ee63a42b9))
+* replace `serialize_circuit` function with `from&lt;&Circuit&gt;` ([#118](https://github.com/noir-lang/acvm-backend-barretenberg/issues/118)) ([94f83a7](https://github.com/noir-lang/acvm-backend-barretenberg/commit/94f83a78e32d91dfb7ae9824923695d9b4c425b0))
+* Replace serialize_circuit function with `from&lt;&Circuit&gt;` ([94f83a7](https://github.com/noir-lang/acvm-backend-barretenberg/commit/94f83a78e32d91dfb7ae9824923695d9b4c425b0))
+* Update bb-sys to resolve bugs in some environments ([#129](https://github.com/noir-lang/acvm-backend-barretenberg/issues/129)) ([e3d4504](https://github.com/noir-lang/acvm-backend-barretenberg/commit/e3d4504f15e1295e637c4da80b1d08c87c267c45))
+* Update dependency containing pk write fix for large general circuits ([#78](https://github.com/noir-lang/acvm-backend-barretenberg/issues/78)) ([2cb523d](https://github.com/noir-lang/acvm-backend-barretenberg/commit/2cb523d2ab95249157b22e198d9dcd6841c3eed8))
+* Update to bb-sys 0.1.1 and update bb in lockfile ([00bb157](https://github.com/noir-lang/acvm-backend-barretenberg/commit/00bb15779dfb64539eeb3f3bb4c4deeba106f2fe))
+* update to bb-sys 0.1.1 and update bb in lockfile ([#111](https://github.com/noir-lang/acvm-backend-barretenberg/issues/111)) ([00bb157](https://github.com/noir-lang/acvm-backend-barretenberg/commit/00bb15779dfb64539eeb3f3bb4c4deeba106f2fe))
+* use `Barretenberg.call` to query circuit size from wasm ([#121](https://github.com/noir-lang/acvm-backend-barretenberg/issues/121)) ([a775af1](https://github.com/noir-lang/acvm-backend-barretenberg/commit/a775af14137cc7bc2e9d8a063fa718a5a9abe6cb))
+
+
+### Miscellaneous Chores
+
+* Remove create_proof and verify functions ([#82](https://github.com/noir-lang/acvm-backend-barretenberg/issues/82)) ([ad0c422](https://github.com/noir-lang/acvm-backend-barretenberg/commit/ad0c4228488457bd155ff381186ecf583f18bfac))
+* update to ACVM 0.7.0 ([#90](https://github.com/noir-lang/acvm-backend-barretenberg/issues/90)) ([6c03687](https://github.com/noir-lang/acvm-backend-barretenberg/commit/6c036870a6a8e26612ab8b4f90a162f7540b42e2))
diff --git a/crates/acvm_backend_barretenberg/Cargo.toml b/crates/acvm_backend_barretenberg/Cargo.toml
new file mode 100644
index 00000000000..302138bfe41
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "acvm-backend-barretenberg"
+description = "An ACVM backend which allows proving/verifying ACIR circuits against Aztec Lab's Barretenberg library."
+version = "0.11.0"
+authors.workspace = true
+edition.workspace = true
+rust-version = "1.66"
+license = "MIT OR Apache-2.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+acvm.workspace = true
+dirs.workspace = true
+thiserror.workspace = true
+base64.workspace = true
+
+tempfile = "3.6.0"
+
+## bb binary downloading
+const_format = "0.2.30"
+tar = "~0.4.15"
+flate2 = "~1.0.1"
+reqwest = { version = "0.11.16", default-features = false, features = [
+    "rustls-tls",
+    "blocking",
+] }
+
+[build-dependencies]
+build-target = "0.4.0"
diff --git a/crates/acvm_backend_barretenberg/build.rs b/crates/acvm_backend_barretenberg/build.rs
new file mode 100644
index 00000000000..b5995f27540
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/build.rs
@@ -0,0 +1,32 @@
+use build_target::{Arch, Os};
+
+// Useful for printing debugging messages during the build
+// macro_rules! p {
+//     ($($tokens: tt)*) => {
+//         println!("cargo:warning={}", format!($($tokens)*))
+//     }
+// }
+
+fn main() -> Result<(), String> {
+    // We need to inject which OS we're building for so that we can download the correct barretenberg binary.
+    let os = match build_target::target_os().unwrap() {
+        os @ (Os::Linux | Os::MacOs) => os,
+        Os::Windows => todo!("Windows is not currently supported"),
+        os_name => panic!("Unsupported OS {}", os_name),
+    };
+
+    let arch = match build_target::target_arch().unwrap() {
+        arch @ (Arch::X86_64 | Arch::AARCH64) => arch,
+        arch_name => panic!("Unsupported Architecture {}", arch_name),
+    };
+
+    // Arm builds of linux are not supported
+    if let (Os::Linux, Arch::AARCH64) = (&os, &arch) {
+        panic!("ARM64 builds of linux are not supported")
+    };
+
+    println!("cargo:rustc-env=TARGET_OS={os}");
+    println!("cargo:rustc-env=TARGET_ARCH={arch}");
+
+    Ok(())
+}
diff --git a/crates/acvm_backend_barretenberg/src/1_mul.bytecode b/crates/acvm_backend_barretenberg/src/1_mul.bytecode
new file mode 100644
index 00000000000..fc187410a4f
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/1_mul.bytecode
@@ -0,0 +1 @@
+H4sIAAAAAAAA/+2Z326CMBTGP2SIyCTLsmw3u+ARWv5ouZuPMjN8/0fYyFo5MHbFV6KJJyG1jf16/vT8NPoG4B2/Fvw8KzvmYr4azUM7D+0Dsb+zDzuqeabdeeDqKkzYTG3tUftyhszFgx0jsZbY0dWss7WoTSj2HsW+QIyB0DiKPVPvCf7RScSa258JX8DLiVqDfu9UJjTZDl8udVeEHH1TRXYO+GuksW6p9lXVHopWl/pTFc3J1KqqT3ujja5N/VWYsmxNZQ7NqTmoRldlq891U56t8BP8NGXI8bOwfuoHYswRsS7M/PmGcYQhbFh+Y8Jmai8OYwe2WKzdYczRXATGneM5ehjH8Adj10hsGD/jNmC8JsYcE+vCzJ9vGMcYwoblNyZspvbiMN7YUYLvDmOO5iIw7gqYo4dxAn8wdo3EhvELbgPGG2LMCbEuzPz5hnGCYWOz/MaEzdReHMZbO6Zi7Q5jjuYiMO4KmKOHcQp/MHaNxIbxK24DxltizCmxLleev0vMITHmlOjXI7gfZn+aHvxeZPos/d2J1+437NXEnfAATI3ROeM8egWqryLtPOhm4F1+X3Fn/BoN4HTNOZXfdtwfcmP7BvHx78jZGwAA
\ No newline at end of file
diff --git a/crates/acvm_backend_barretenberg/src/bb/contract.rs b/crates/acvm_backend_barretenberg/src/bb/contract.rs
new file mode 100644
index 00000000000..07131861aa6
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/contract.rs
@@ -0,0 +1,75 @@
+use super::{assert_binary_exists, get_binary_path, CliShimError};
+
+/// VerifyCommand will call the barretenberg binary
+/// to return a solidity library with the verification key
+/// that can be used to verify proofs on-chain.
+///
+/// This does not return a Solidity file that is able
+/// to verify a proof. See acvm_interop/contract.sol for the
+/// remaining logic that is missing.
+pub(crate) struct ContractCommand {
+    pub(crate) verbose: bool,
+    pub(crate) path_to_crs: String,
+    pub(crate) path_to_vk: String,
+    pub(crate) path_to_contract: String,
+}
+
+impl ContractCommand {
+    pub(crate) fn run(self) -> Result<(), CliShimError> {
+        assert_binary_exists();
+        let mut command = std::process::Command::new(get_binary_path());
+
+        command
+            .arg("contract")
+            .arg("-c")
+            .arg(self.path_to_crs)
+            .arg("-k")
+            .arg(self.path_to_vk)
+            .arg("-o")
+            .arg(self.path_to_contract);
+
+        if self.verbose {
+            command.arg("-v");
+        }
+
+        let output = command.output().expect("Failed to execute command");
+        if output.status.success() {
+            Ok(())
+        } else {
+            Err(CliShimError(String::from_utf8(output.stderr).unwrap()))
+        }
+    }
+}
+
+#[test]
+fn contract_command() {
+    use tempfile::tempdir;
+
+    let path_to_1_mul = "./src/1_mul.bytecode";
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    let temp_directory_path = temp_directory.path();
+    let path_to_crs = temp_directory_path.join("crs");
+    let path_to_vk = temp_directory_path.join("vk");
+    let path_to_contract = temp_directory_path.join("contract");
+
+    let write_vk_command = super::WriteVkCommand {
+        verbose: true,
+        path_to_bytecode: path_to_1_mul.to_string(),
+        path_to_vk_output: path_to_vk.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+    };
+
+    assert!(write_vk_command.run().is_ok());
+
+    let contract_command = ContractCommand {
+        verbose: true,
+        path_to_vk: path_to_vk.to_str().unwrap().to_string(),
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        path_to_contract: path_to_contract.to_str().unwrap().to_string(),
+    };
+
+    assert!(contract_command.run().is_ok());
+    drop(temp_directory);
+}
diff --git a/crates/acvm_backend_barretenberg/src/bb/gates.rs b/crates/acvm_backend_barretenberg/src/bb/gates.rs
new file mode 100644
index 00000000000..c9db70343fb
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/gates.rs
@@ -0,0 +1,71 @@
+use super::{assert_binary_exists, get_binary_path};
+
+/// GatesCommand will call the barretenberg binary
+/// to return the number of gates needed to create a proof
+/// for the given bytecode.
+pub(crate) struct GatesCommand {
+    pub(crate) path_to_crs: String,
+    pub(crate) path_to_bytecode: String,
+}
+
+impl GatesCommand {
+    pub(crate) fn run(self) -> u32 {
+        assert_binary_exists();
+        let output = std::process::Command::new(get_binary_path())
+            .arg("gates")
+            .arg("-c")
+            .arg(self.path_to_crs)
+            .arg("-b")
+            .arg(self.path_to_bytecode)
+            .output()
+            .expect("Failed to execute command");
+
+        if !output.status.success() {
+            panic!(
+                "gates command encountered an error: {}",
+                String::from_utf8_lossy(&output.stderr)
+            );
+        }
+        // Note: barretenberg includes the newline, so that subsequent prints to stdout
+        // are not on the same line as the gates output.
+
+        // Ensure we got the expected number of bytes
+        if output.stdout.len() != 8 {
+            panic!("Unexpected 8 bytes, received {}", output.stdout.len());
+        }
+
+        // Convert bytes to u64 in little-endian format
+        let value = u64::from_le_bytes([
+            output.stdout[0],
+            output.stdout[1],
+            output.stdout[2],
+            output.stdout[3],
+            output.stdout[4],
+            output.stdout[5],
+            output.stdout[6],
+            output.stdout[7],
+        ]);
+
+        value as u32
+    }
+}
+
+#[test]
+fn gate_command() {
+    use tempfile::tempdir;
+
+    let path_to_1_mul = "./src/1_mul.bytecode";
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    let temp_directory_path = temp_directory.path();
+    let path_to_crs = temp_directory_path.join("crs");
+
+    let gate_command = GatesCommand {
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        path_to_bytecode: path_to_1_mul.to_string(),
+    };
+
+    let output = gate_command.run();
+    assert_eq!(output, 2775);
+    drop(temp_directory);
+}
diff --git a/crates/acvm_backend_barretenberg/src/bb/mod.rs b/crates/acvm_backend_barretenberg/src/bb/mod.rs
new file mode 100644
index 00000000000..373e021a6c1
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/mod.rs
@@ -0,0 +1,123 @@
+// Reference: https://github.com/AztecProtocol/aztec-packages/blob/master/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp
+
+mod contract;
+mod gates;
+mod prove;
+mod prove_and_verify;
+mod verify;
+mod write_vk;
+
+use std::{io::Cursor, path::PathBuf};
+
+use const_format::formatcp;
+pub(crate) use contract::ContractCommand;
+pub(crate) use gates::GatesCommand;
+pub(crate) use prove::ProveCommand;
+pub(crate) use verify::VerifyCommand;
+pub(crate) use write_vk::WriteVkCommand;
+
+#[derive(Debug, thiserror::Error)]
+#[error("Error communicating with barretenberg binary {0}")]
+pub(crate) struct CliShimError(String);
+
+const USERNAME: &str = "AztecProtocol";
+const REPO: &str = "barretenberg";
+const VERSION: &str = "0.4.6";
+const TAG: &str = formatcp!("barretenberg-v{}", VERSION);
+const DEST_FOLDER: &str = ".nargo/backends/acvm-backend-barretenberg";
+const BINARY_NAME: &str = "backend_binary";
+
+const API_URL: &str = formatcp!(
+    "https://github.com/{}/{}/releases/download/{}",
+    USERNAME,
+    REPO,
+    TAG
+);
+
+fn get_bb_download_url() -> String {
+    if let Ok(path) = std::env::var("BB_BINARY_URL") {
+        return path;
+    }
+
+    let target_os = env!("TARGET_OS");
+    let target_arch = env!("TARGET_ARCH");
+
+    let archive_name = match target_os {
+        "linux" => "barretenberg-x86_64-linux-gnu.tar.gz",
+        "macos" => match target_arch {
+            "aarch64" => "barretenberg-aarch64-apple-darwin.tar.gz",
+            "x86_64" => "barretenberg-x86_64-apple-darwin.tar.gz",
+            arch => panic!("unsupported arch {arch}"),
+        },
+        os => panic!("Unsupported OS {os}"),
+    };
+
+    format!("{API_URL}/{archive_name}")
+}
+
+/// Returns the path to the binary that was set by the `NARGO_BINARIES_PATH` environment variable
+fn get_binary_path() -> PathBuf {
+    match std::env::var("BB_BINARY_PATH") {
+        Ok(path) => PathBuf::from(path),
+        Err(_) => dirs::home_dir()
+            .unwrap()
+            .join(formatcp!("{}/{}", DEST_FOLDER, BINARY_NAME)),
+    }
+}
+
+fn assert_binary_exists() {
+    if !get_binary_path().exists() {
+        download_bb_binary()
+    }
+}
+
+fn download_bb_binary() {
+    use flate2::read::GzDecoder;
+    use tar::Archive;
+    use tempfile::tempdir;
+
+    // Create directory to place binary in.
+    std::fs::create_dir_all(get_binary_path().parent().unwrap()).unwrap();
+
+    // Download sources
+    let compressed_file: Cursor<Vec<u8>> = download_binary_from_url(&get_bb_download_url())
+        .unwrap_or_else(|error| panic!("\n\nDownload error: {error}\n\n"));
+
+    // Unpack the tarball
+    let gz_decoder = GzDecoder::new(compressed_file);
+    let mut archive = Archive::new(gz_decoder);
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    archive.unpack(&temp_directory).unwrap();
+    let binary_path = temp_directory.path().join("bb");
+
+    // Rename the binary to the desired name
+    std::fs::copy(binary_path, get_binary_path()).unwrap();
+
+    drop(temp_directory);
+}
+
+/// Try to download the specified URL into a buffer which is returned.
+fn download_binary_from_url(url: &str) -> Result<Cursor<Vec<u8>>, String> {
+    let response = reqwest::blocking::get(url).map_err(|error| error.to_string())?;
+
+    let bytes = response.bytes().unwrap();
+
+    // TODO: Check SHA of downloaded binary
+
+    Ok(Cursor::new(bytes.to_vec()))
+}
+
+#[test]
+fn no_command_provided_works() {
+    // This is a simple test to check that the binaries work
+
+    assert_binary_exists();
+
+    let output = std::process::Command::new(get_binary_path())
+        .output()
+        .expect("Failed to execute command");
+
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    assert_eq!(stderr, "No command provided.\n");
+}
diff --git a/crates/acvm_backend_barretenberg/src/bb/prove.rs b/crates/acvm_backend_barretenberg/src/bb/prove.rs
new file mode 100644
index 00000000000..3c3dbd1f5a2
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/prove.rs
@@ -0,0 +1,77 @@
+use super::{assert_binary_exists, get_binary_path, CliShimError};
+
+/// ProveCommand will call the barretenberg binary
+/// to create a proof, given the witness and the bytecode.
+///
+/// Note:Internally barretenberg will create and discard the
+/// proving key, so this is not returned.
+///
+/// The proof will be written to the specified output file.
+pub(crate) struct ProveCommand {
+    pub(crate) verbose: bool,
+    pub(crate) path_to_crs: String,
+    pub(crate) is_recursive: bool,
+    pub(crate) path_to_bytecode: String,
+    pub(crate) path_to_witness: String,
+    pub(crate) path_to_proof: String,
+}
+
+impl ProveCommand {
+    pub(crate) fn run(self) -> Result<(), CliShimError> {
+        assert_binary_exists();
+        let mut command = std::process::Command::new(get_binary_path());
+
+        command
+            .arg("prove")
+            .arg("-c")
+            .arg(self.path_to_crs)
+            .arg("-b")
+            .arg(self.path_to_bytecode)
+            .arg("-w")
+            .arg(self.path_to_witness)
+            .arg("-o")
+            .arg(self.path_to_proof);
+
+        if self.verbose {
+            command.arg("-v");
+        }
+        if self.is_recursive {
+            command.arg("-r");
+        }
+
+        let output = command.output().expect("Failed to execute command");
+
+        if output.status.success() {
+            Ok(())
+        } else {
+            Err(CliShimError(String::from_utf8(output.stderr).unwrap()))
+        }
+    }
+}
+
+#[test]
+fn prove_command() {
+    use tempfile::tempdir;
+
+    let path_to_1_mul = "./src/1_mul.bytecode";
+    let path_to_1_mul_witness = "./src/witness.tr";
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    let temp_directory_path = temp_directory.path();
+
+    let path_to_crs = temp_directory_path.join("crs");
+    let path_to_proof = temp_directory_path.join("1_mul").with_extension("proof");
+
+    let prove_command = ProveCommand {
+        verbose: true,
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_bytecode: path_to_1_mul.to_string(),
+        path_to_witness: path_to_1_mul_witness.to_string(),
+        path_to_proof: path_to_proof.to_str().unwrap().to_string(),
+    };
+
+    let proof_created = prove_command.run();
+    assert!(proof_created.is_ok());
+    drop(temp_directory);
+}
diff --git a/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs b/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs
new file mode 100644
index 00000000000..f877909f588
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs
@@ -0,0 +1,68 @@
+use super::{assert_binary_exists, get_binary_path};
+
+/// ProveAndVerifyCommand will call the barretenberg binary
+/// to create a proof and then verify the proof once created.
+///
+/// Note: Functions like this are useful for testing. In a real workflow,
+/// ProveCommand and VerifyCommand will be used separately.
+#[allow(dead_code)]
+struct ProveAndVerifyCommand {
+    verbose: bool,
+    path_to_crs: String,
+    is_recursive: bool,
+    path_to_bytecode: String,
+    path_to_witness: String,
+}
+
+#[allow(dead_code)]
+impl ProveAndVerifyCommand {
+    fn run(self) -> bool {
+        assert_binary_exists();
+        let mut command = std::process::Command::new(get_binary_path());
+
+        command
+            .arg("prove_and_verify")
+            .arg("-c")
+            .arg(self.path_to_crs)
+            .arg("-b")
+            .arg(self.path_to_bytecode)
+            .arg("-w")
+            .arg(self.path_to_witness);
+        if self.verbose {
+            command.arg("-v");
+        }
+        if self.is_recursive {
+            command.arg("-r");
+        }
+
+        command
+            .output()
+            .expect("Failed to execute command")
+            .status
+            .success()
+    }
+}
+
+#[test]
+fn prove_and_verify_command() {
+    use tempfile::tempdir;
+
+    let path_to_1_mul = "./src/1_mul.bytecode";
+    let path_to_1_mul_witness = "./src/witness.tr";
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    let temp_directory_path = temp_directory.path();
+    let path_to_crs = temp_directory_path.join("crs");
+
+    let prove_and_verify_command = ProveAndVerifyCommand {
+        verbose: true,
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_bytecode: path_to_1_mul.to_string(),
+        path_to_witness: path_to_1_mul_witness.to_string(),
+    };
+
+    let output = prove_and_verify_command.run();
+    assert!(output);
+    drop(temp_directory);
+}
diff --git a/crates/acvm_backend_barretenberg/src/bb/verify.rs b/crates/acvm_backend_barretenberg/src/bb/verify.rs
new file mode 100644
index 00000000000..5ac40f810f4
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/verify.rs
@@ -0,0 +1,87 @@
+use super::{assert_binary_exists, get_binary_path};
+
+/// VerifyCommand will call the barretenberg binary
+/// to verify a proof
+pub(crate) struct VerifyCommand {
+    pub(crate) verbose: bool,
+    pub(crate) path_to_crs: String,
+    pub(crate) is_recursive: bool,
+    pub(crate) path_to_proof: String,
+    pub(crate) path_to_vk: String,
+}
+
+impl VerifyCommand {
+    pub(crate) fn run(self) -> bool {
+        assert_binary_exists();
+        let mut command = std::process::Command::new(get_binary_path());
+
+        command
+            .arg("verify")
+            .arg("-c")
+            .arg(self.path_to_crs)
+            .arg("-p")
+            .arg(self.path_to_proof)
+            .arg("-k")
+            .arg(self.path_to_vk);
+
+        if self.verbose {
+            command.arg("-v");
+        }
+        if self.is_recursive {
+            command.arg("-r");
+        }
+
+        let output = command.output().expect("Failed to execute command");
+        output.status.success()
+    }
+}
+
+#[test]
+fn verify_command() {
+    use tempfile::tempdir;
+
+    use crate::bb::{ProveCommand, WriteVkCommand};
+
+    let path_to_1_mul = "./src/1_mul.bytecode";
+    let path_to_1_mul_witness = "./src/witness.tr";
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    let temp_directory_path = temp_directory.path();
+
+    let path_to_crs = temp_directory_path.join("crs");
+    let path_to_proof = temp_directory_path.join("1_mul").with_extension("proof");
+    let path_to_vk = temp_directory_path.join("vk");
+
+    let write_vk_command = WriteVkCommand {
+        verbose: true,
+        path_to_bytecode: path_to_1_mul.to_string(),
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_vk_output: path_to_vk.to_str().unwrap().to_string(),
+    };
+
+    let vk_written = write_vk_command.run();
+    assert!(vk_written.is_ok());
+
+    let prove_command = ProveCommand {
+        verbose: true,
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_bytecode: path_to_1_mul.to_string(),
+        path_to_witness: path_to_1_mul_witness.to_string(),
+        path_to_proof: path_to_proof.to_str().unwrap().to_string(),
+    };
+    prove_command.run().unwrap();
+
+    let verify_command = VerifyCommand {
+        verbose: true,
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_proof: path_to_proof.to_str().unwrap().to_string(),
+        path_to_vk: path_to_vk.to_str().unwrap().to_string(),
+    };
+
+    let verified = verify_command.run();
+    assert!(verified);
+    drop(temp_directory);
+}
diff --git a/crates/acvm_backend_barretenberg/src/bb/write_vk.rs b/crates/acvm_backend_barretenberg/src/bb/write_vk.rs
new file mode 100644
index 00000000000..e330d58ba1e
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/bb/write_vk.rs
@@ -0,0 +1,66 @@
+use super::{assert_binary_exists, get_binary_path, CliShimError};
+
+/// WriteCommand will call the barretenberg binary
+/// to write a verification key to a file
+pub(crate) struct WriteVkCommand {
+    pub(crate) verbose: bool,
+    pub(crate) path_to_crs: String,
+    pub(crate) is_recursive: bool,
+    pub(crate) path_to_bytecode: String,
+    pub(crate) path_to_vk_output: String,
+}
+
+impl WriteVkCommand {
+    pub(crate) fn run(self) -> Result<(), CliShimError> {
+        assert_binary_exists();
+        let mut command = std::process::Command::new(get_binary_path());
+
+        command
+            .arg("write_vk")
+            .arg("-c")
+            .arg(self.path_to_crs)
+            .arg("-b")
+            .arg(self.path_to_bytecode)
+            .arg("-o")
+            .arg(self.path_to_vk_output);
+
+        if self.verbose {
+            command.arg("-v");
+        }
+        if self.is_recursive {
+            command.arg("-r");
+        }
+
+        let output = command.output().expect("Failed to execute command");
+
+        if output.status.success() {
+            Ok(())
+        } else {
+            Err(CliShimError(String::from_utf8(output.stderr).unwrap()))
+        }
+    }
+}
+
+#[test]
+fn write_vk_command() {
+    use tempfile::tempdir;
+
+    let path_to_1_mul = "./src/1_mul.bytecode";
+
+    let temp_directory = tempdir().expect("could not create a temporary directory");
+    let temp_directory_path = temp_directory.path();
+    let path_to_crs = temp_directory_path.join("crs");
+    let path_to_vk = temp_directory_path.join("vk");
+
+    let write_vk_command = WriteVkCommand {
+        verbose: true,
+        path_to_bytecode: path_to_1_mul.to_string(),
+        path_to_crs: path_to_crs.to_str().unwrap().to_string(),
+        is_recursive: false,
+        path_to_vk_output: path_to_vk.to_str().unwrap().to_string(),
+    };
+
+    let vk_written = write_vk_command.run();
+    assert!(vk_written.is_ok());
+    drop(temp_directory);
+}
diff --git a/crates/acvm_backend_barretenberg/src/contract.sol b/crates/acvm_backend_barretenberg/src/contract.sol
new file mode 100644
index 00000000000..abd45567969
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/contract.sol
@@ -0,0 +1,2539 @@
+
+/**
+ * @title Ultra Plonk proof verification contract
+ * @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified
+ */
+abstract contract BaseUltraVerifier {
+    // VERIFICATION KEY MEMORY LOCATIONS
+    uint256 internal constant N_LOC = 0x380;
+    uint256 internal constant NUM_INPUTS_LOC = 0x3a0;
+    uint256 internal constant OMEGA_LOC = 0x3c0;
+    uint256 internal constant DOMAIN_INVERSE_LOC = 0x3e0;
+    uint256 internal constant Q1_X_LOC = 0x400;
+    uint256 internal constant Q1_Y_LOC = 0x420;
+    uint256 internal constant Q2_X_LOC = 0x440;
+    uint256 internal constant Q2_Y_LOC = 0x460;
+    uint256 internal constant Q3_X_LOC = 0x480;
+    uint256 internal constant Q3_Y_LOC = 0x4a0;
+    uint256 internal constant Q4_X_LOC = 0x4c0;
+    uint256 internal constant Q4_Y_LOC = 0x4e0;
+    uint256 internal constant QM_X_LOC = 0x500;
+    uint256 internal constant QM_Y_LOC = 0x520;
+    uint256 internal constant QC_X_LOC = 0x540;
+    uint256 internal constant QC_Y_LOC = 0x560;
+    uint256 internal constant QARITH_X_LOC = 0x580;
+    uint256 internal constant QARITH_Y_LOC = 0x5a0;
+    uint256 internal constant QSORT_X_LOC = 0x5c0;
+    uint256 internal constant QSORT_Y_LOC = 0x5e0;
+    uint256 internal constant QELLIPTIC_X_LOC = 0x600;
+    uint256 internal constant QELLIPTIC_Y_LOC = 0x620;
+    uint256 internal constant QAUX_X_LOC = 0x640;
+    uint256 internal constant QAUX_Y_LOC = 0x660;
+    uint256 internal constant SIGMA1_X_LOC = 0x680;
+    uint256 internal constant SIGMA1_Y_LOC = 0x6a0;
+    uint256 internal constant SIGMA2_X_LOC = 0x6c0;
+    uint256 internal constant SIGMA2_Y_LOC = 0x6e0;
+    uint256 internal constant SIGMA3_X_LOC = 0x700;
+    uint256 internal constant SIGMA3_Y_LOC = 0x720;
+    uint256 internal constant SIGMA4_X_LOC = 0x740;
+    uint256 internal constant SIGMA4_Y_LOC = 0x760;
+    uint256 internal constant TABLE1_X_LOC = 0x780;
+    uint256 internal constant TABLE1_Y_LOC = 0x7a0;
+    uint256 internal constant TABLE2_X_LOC = 0x7c0;
+    uint256 internal constant TABLE2_Y_LOC = 0x7e0;
+    uint256 internal constant TABLE3_X_LOC = 0x800;
+    uint256 internal constant TABLE3_Y_LOC = 0x820;
+    uint256 internal constant TABLE4_X_LOC = 0x840;
+    uint256 internal constant TABLE4_Y_LOC = 0x860;
+    uint256 internal constant TABLE_TYPE_X_LOC = 0x880;
+    uint256 internal constant TABLE_TYPE_Y_LOC = 0x8a0;
+    uint256 internal constant ID1_X_LOC = 0x8c0;
+    uint256 internal constant ID1_Y_LOC = 0x8e0;
+    uint256 internal constant ID2_X_LOC = 0x900;
+    uint256 internal constant ID2_Y_LOC = 0x920;
+    uint256 internal constant ID3_X_LOC = 0x940;
+    uint256 internal constant ID3_Y_LOC = 0x960;
+    uint256 internal constant ID4_X_LOC = 0x980;
+    uint256 internal constant ID4_Y_LOC = 0x9a0;
+    uint256 internal constant CONTAINS_RECURSIVE_PROOF_LOC = 0x9c0;
+    uint256 internal constant RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC = 0x9e0;
+    uint256 internal constant G2X_X0_LOC = 0xa00;
+    uint256 internal constant G2X_X1_LOC = 0xa20;
+    uint256 internal constant G2X_Y0_LOC = 0xa40;
+    uint256 internal constant G2X_Y1_LOC = 0xa60;
+
+    // ### PROOF DATA MEMORY LOCATIONS
+    uint256 internal constant W1_X_LOC = 0x1200;
+    uint256 internal constant W1_Y_LOC = 0x1220;
+    uint256 internal constant W2_X_LOC = 0x1240;
+    uint256 internal constant W2_Y_LOC = 0x1260;
+    uint256 internal constant W3_X_LOC = 0x1280;
+    uint256 internal constant W3_Y_LOC = 0x12a0;
+    uint256 internal constant W4_X_LOC = 0x12c0;
+    uint256 internal constant W4_Y_LOC = 0x12e0;
+    uint256 internal constant S_X_LOC = 0x1300;
+    uint256 internal constant S_Y_LOC = 0x1320;
+    uint256 internal constant Z_X_LOC = 0x1340;
+    uint256 internal constant Z_Y_LOC = 0x1360;
+    uint256 internal constant Z_LOOKUP_X_LOC = 0x1380;
+    uint256 internal constant Z_LOOKUP_Y_LOC = 0x13a0;
+    uint256 internal constant T1_X_LOC = 0x13c0;
+    uint256 internal constant T1_Y_LOC = 0x13e0;
+    uint256 internal constant T2_X_LOC = 0x1400;
+    uint256 internal constant T2_Y_LOC = 0x1420;
+    uint256 internal constant T3_X_LOC = 0x1440;
+    uint256 internal constant T3_Y_LOC = 0x1460;
+    uint256 internal constant T4_X_LOC = 0x1480;
+    uint256 internal constant T4_Y_LOC = 0x14a0;
+
+    uint256 internal constant W1_EVAL_LOC = 0x1600;
+    uint256 internal constant W2_EVAL_LOC = 0x1620;
+    uint256 internal constant W3_EVAL_LOC = 0x1640;
+    uint256 internal constant W4_EVAL_LOC = 0x1660;
+    uint256 internal constant S_EVAL_LOC = 0x1680;
+    uint256 internal constant Z_EVAL_LOC = 0x16a0;
+    uint256 internal constant Z_LOOKUP_EVAL_LOC = 0x16c0;
+    uint256 internal constant Q1_EVAL_LOC = 0x16e0;
+    uint256 internal constant Q2_EVAL_LOC = 0x1700;
+    uint256 internal constant Q3_EVAL_LOC = 0x1720;
+    uint256 internal constant Q4_EVAL_LOC = 0x1740;
+    uint256 internal constant QM_EVAL_LOC = 0x1760;
+    uint256 internal constant QC_EVAL_LOC = 0x1780;
+    uint256 internal constant QARITH_EVAL_LOC = 0x17a0;
+    uint256 internal constant QSORT_EVAL_LOC = 0x17c0;
+    uint256 internal constant QELLIPTIC_EVAL_LOC = 0x17e0;
+    uint256 internal constant QAUX_EVAL_LOC = 0x1800;
+    uint256 internal constant TABLE1_EVAL_LOC = 0x1840;
+    uint256 internal constant TABLE2_EVAL_LOC = 0x1860;
+    uint256 internal constant TABLE3_EVAL_LOC = 0x1880;
+    uint256 internal constant TABLE4_EVAL_LOC = 0x18a0;
+    uint256 internal constant TABLE_TYPE_EVAL_LOC = 0x18c0;
+    uint256 internal constant ID1_EVAL_LOC = 0x18e0;
+    uint256 internal constant ID2_EVAL_LOC = 0x1900;
+    uint256 internal constant ID3_EVAL_LOC = 0x1920;
+    uint256 internal constant ID4_EVAL_LOC = 0x1940;
+    uint256 internal constant SIGMA1_EVAL_LOC = 0x1960;
+    uint256 internal constant SIGMA2_EVAL_LOC = 0x1980;
+    uint256 internal constant SIGMA3_EVAL_LOC = 0x19a0;
+    uint256 internal constant SIGMA4_EVAL_LOC = 0x19c0;
+    uint256 internal constant W1_OMEGA_EVAL_LOC = 0x19e0;
+    uint256 internal constant W2_OMEGA_EVAL_LOC = 0x2000;
+    uint256 internal constant W3_OMEGA_EVAL_LOC = 0x2020;
+    uint256 internal constant W4_OMEGA_EVAL_LOC = 0x2040;
+    uint256 internal constant S_OMEGA_EVAL_LOC = 0x2060;
+    uint256 internal constant Z_OMEGA_EVAL_LOC = 0x2080;
+    uint256 internal constant Z_LOOKUP_OMEGA_EVAL_LOC = 0x20a0;
+    uint256 internal constant TABLE1_OMEGA_EVAL_LOC = 0x20c0;
+    uint256 internal constant TABLE2_OMEGA_EVAL_LOC = 0x20e0;
+    uint256 internal constant TABLE3_OMEGA_EVAL_LOC = 0x2100;
+    uint256 internal constant TABLE4_OMEGA_EVAL_LOC = 0x2120;
+
+    uint256 internal constant PI_Z_X_LOC = 0x2300;
+    uint256 internal constant PI_Z_Y_LOC = 0x2320;
+    uint256 internal constant PI_Z_OMEGA_X_LOC = 0x2340;
+    uint256 internal constant PI_Z_OMEGA_Y_LOC = 0x2360;
+
+    // Used for elliptic widget. These are alias names for wire + shifted wire evaluations
+    uint256 internal constant X1_EVAL_LOC = W2_EVAL_LOC;
+    uint256 internal constant X2_EVAL_LOC = W1_OMEGA_EVAL_LOC;
+    uint256 internal constant X3_EVAL_LOC = W2_OMEGA_EVAL_LOC;
+    uint256 internal constant Y1_EVAL_LOC = W3_EVAL_LOC;
+    uint256 internal constant Y2_EVAL_LOC = W4_OMEGA_EVAL_LOC;
+    uint256 internal constant Y3_EVAL_LOC = W3_OMEGA_EVAL_LOC;
+    uint256 internal constant QBETA_LOC = Q3_EVAL_LOC;
+    uint256 internal constant QBETA_SQR_LOC = Q4_EVAL_LOC;
+    uint256 internal constant QSIGN_LOC = Q1_EVAL_LOC;
+
+    // ### CHALLENGES MEMORY OFFSETS
+
+    uint256 internal constant C_BETA_LOC = 0x2600;
+    uint256 internal constant C_GAMMA_LOC = 0x2620;
+    uint256 internal constant C_ALPHA_LOC = 0x2640;
+    uint256 internal constant C_ETA_LOC = 0x2660;
+    uint256 internal constant C_ETA_SQR_LOC = 0x2680;
+    uint256 internal constant C_ETA_CUBE_LOC = 0x26a0;
+
+    uint256 internal constant C_ZETA_LOC = 0x26c0;
+    uint256 internal constant C_CURRENT_LOC = 0x26e0;
+    uint256 internal constant C_V0_LOC = 0x2700;
+    uint256 internal constant C_V1_LOC = 0x2720;
+    uint256 internal constant C_V2_LOC = 0x2740;
+    uint256 internal constant C_V3_LOC = 0x2760;
+    uint256 internal constant C_V4_LOC = 0x2780;
+    uint256 internal constant C_V5_LOC = 0x27a0;
+    uint256 internal constant C_V6_LOC = 0x27c0;
+    uint256 internal constant C_V7_LOC = 0x27e0;
+    uint256 internal constant C_V8_LOC = 0x2800;
+    uint256 internal constant C_V9_LOC = 0x2820;
+    uint256 internal constant C_V10_LOC = 0x2840;
+    uint256 internal constant C_V11_LOC = 0x2860;
+    uint256 internal constant C_V12_LOC = 0x2880;
+    uint256 internal constant C_V13_LOC = 0x28a0;
+    uint256 internal constant C_V14_LOC = 0x28c0;
+    uint256 internal constant C_V15_LOC = 0x28e0;
+    uint256 internal constant C_V16_LOC = 0x2900;
+    uint256 internal constant C_V17_LOC = 0x2920;
+    uint256 internal constant C_V18_LOC = 0x2940;
+    uint256 internal constant C_V19_LOC = 0x2960;
+    uint256 internal constant C_V20_LOC = 0x2980;
+    uint256 internal constant C_V21_LOC = 0x29a0;
+    uint256 internal constant C_V22_LOC = 0x29c0;
+    uint256 internal constant C_V23_LOC = 0x29e0;
+    uint256 internal constant C_V24_LOC = 0x2a00;
+    uint256 internal constant C_V25_LOC = 0x2a20;
+    uint256 internal constant C_V26_LOC = 0x2a40;
+    uint256 internal constant C_V27_LOC = 0x2a60;
+    uint256 internal constant C_V28_LOC = 0x2a80;
+    uint256 internal constant C_V29_LOC = 0x2aa0;
+    uint256 internal constant C_V30_LOC = 0x2ac0;
+
+    uint256 internal constant C_U_LOC = 0x2b00;
+
+    // ### LOCAL VARIABLES MEMORY OFFSETS
+    uint256 internal constant DELTA_NUMERATOR_LOC = 0x3000;
+    uint256 internal constant DELTA_DENOMINATOR_LOC = 0x3020;
+    uint256 internal constant ZETA_POW_N_LOC = 0x3040;
+    uint256 internal constant PUBLIC_INPUT_DELTA_LOC = 0x3060;
+    uint256 internal constant ZERO_POLY_LOC = 0x3080;
+    uint256 internal constant L_START_LOC = 0x30a0;
+    uint256 internal constant L_END_LOC = 0x30c0;
+    uint256 internal constant R_ZERO_EVAL_LOC = 0x30e0;
+
+    uint256 internal constant PLOOKUP_DELTA_NUMERATOR_LOC = 0x3100;
+    uint256 internal constant PLOOKUP_DELTA_DENOMINATOR_LOC = 0x3120;
+    uint256 internal constant PLOOKUP_DELTA_LOC = 0x3140;
+
+    uint256 internal constant ACCUMULATOR_X_LOC = 0x3160;
+    uint256 internal constant ACCUMULATOR_Y_LOC = 0x3180;
+    uint256 internal constant ACCUMULATOR2_X_LOC = 0x31a0;
+    uint256 internal constant ACCUMULATOR2_Y_LOC = 0x31c0;
+    uint256 internal constant PAIRING_LHS_X_LOC = 0x31e0;
+    uint256 internal constant PAIRING_LHS_Y_LOC = 0x3200;
+    uint256 internal constant PAIRING_RHS_X_LOC = 0x3220;
+    uint256 internal constant PAIRING_RHS_Y_LOC = 0x3240;
+
+    // ### SUCCESS FLAG MEMORY LOCATIONS
+    uint256 internal constant GRAND_PRODUCT_SUCCESS_FLAG = 0x3300;
+    uint256 internal constant ARITHMETIC_TERM_SUCCESS_FLAG = 0x3020;
+    uint256 internal constant BATCH_OPENING_SUCCESS_FLAG = 0x3340;
+    uint256 internal constant OPENING_COMMITMENT_SUCCESS_FLAG = 0x3360;
+    uint256 internal constant PAIRING_PREAMBLE_SUCCESS_FLAG = 0x3380;
+    uint256 internal constant PAIRING_SUCCESS_FLAG = 0x33a0;
+    uint256 internal constant RESULT_FLAG = 0x33c0;
+
+    // misc stuff
+    uint256 internal constant OMEGA_INVERSE_LOC = 0x3400;
+    uint256 internal constant C_ALPHA_SQR_LOC = 0x3420;
+    uint256 internal constant C_ALPHA_CUBE_LOC = 0x3440;
+    uint256 internal constant C_ALPHA_QUAD_LOC = 0x3460;
+    uint256 internal constant C_ALPHA_BASE_LOC = 0x3480;
+
+    // ### RECURSION VARIABLE MEMORY LOCATIONS
+    uint256 internal constant RECURSIVE_P1_X_LOC = 0x3500;
+    uint256 internal constant RECURSIVE_P1_Y_LOC = 0x3520;
+    uint256 internal constant RECURSIVE_P2_X_LOC = 0x3540;
+    uint256 internal constant RECURSIVE_P2_Y_LOC = 0x3560;
+
+    uint256 internal constant PUBLIC_INPUTS_HASH_LOCATION = 0x3580;
+
+    // sub-identity storage
+    uint256 internal constant PERMUTATION_IDENTITY = 0x3600;
+    uint256 internal constant PLOOKUP_IDENTITY = 0x3620;
+    uint256 internal constant ARITHMETIC_IDENTITY = 0x3640;
+    uint256 internal constant SORT_IDENTITY = 0x3660;
+    uint256 internal constant ELLIPTIC_IDENTITY = 0x3680;
+    uint256 internal constant AUX_IDENTITY = 0x36a0;
+    uint256 internal constant AUX_NON_NATIVE_FIELD_EVALUATION = 0x36c0;
+    uint256 internal constant AUX_LIMB_ACCUMULATOR_EVALUATION = 0x36e0;
+    uint256 internal constant AUX_RAM_CONSISTENCY_EVALUATION = 0x3700;
+    uint256 internal constant AUX_ROM_CONSISTENCY_EVALUATION = 0x3720;
+    uint256 internal constant AUX_MEMORY_EVALUATION = 0x3740;
+
+    uint256 internal constant QUOTIENT_EVAL_LOC = 0x3760;
+    uint256 internal constant ZERO_POLY_INVERSE_LOC = 0x3780;
+
+    // when hashing public inputs we use memory at NU_CHALLENGE_INPUT_LOC_A, as the hash input size is unknown at compile time
+    uint256 internal constant NU_CHALLENGE_INPUT_LOC_A = 0x37a0;
+    uint256 internal constant NU_CHALLENGE_INPUT_LOC_B = 0x37c0;
+    uint256 internal constant NU_CHALLENGE_INPUT_LOC_C = 0x37e0;
+
+    bytes4 internal constant PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR = 0xeba9f4a6;
+    bytes4 internal constant PUBLIC_INPUT_GE_P_SELECTOR = 0x374a972f;
+    bytes4 internal constant MOD_EXP_FAILURE_SELECTOR = 0xf894a7bc;
+    bytes4 internal constant EC_SCALAR_MUL_FAILURE_SELECTOR = 0xf755f369;
+    bytes4 internal constant PROOF_FAILURE_SELECTOR = 0x0711fcec;
+
+    uint256 internal constant ETA_INPUT_LENGTH = 0xc0; // W1, W2, W3 = 6 * 0x20 bytes
+
+    // We need to hash 41 field elements when generating the NU challenge
+    // w1, w2, w3, w4, s, z, z_lookup, q1, q2, q3, q4, qm, qc, qarith (14)
+    // qsort, qelliptic, qaux, sigma1, sigma2, sigma, sigma4, (7)
+    // table1, table2, table3, table4, tabletype, id1, id2, id3, id4, (9)
+    // w1_omega, w2_omega, w3_omega, w4_omega, s_omega, z_omega, z_lookup_omega, (7)
+    // table1_omega, table2_omega, table3_omega, table4_omega (4)
+    uint256 internal constant NU_INPUT_LENGTH = 0x520; // 0x520 = 41 * 0x20
+
+    // There are ELEVEN G1 group elements added into the transcript in the `beta` round, that we need to skip over
+    // W1, W2, W3, W4, S, Z, Z_LOOKUP, T1, T2, T3, T4
+    uint256 internal constant NU_CALLDATA_SKIP_LENGTH = 0x2c0; // 11 * 0x40 = 0x2c0
+
+    uint256 internal constant NEGATIVE_INVERSE_OF_2_MODULO_P =
+        0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;
+    uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68
+    uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14
+
+    error PUBLIC_INPUT_COUNT_INVALID(uint256 expected, uint256 actual);
+    error PUBLIC_INPUT_INVALID_BN128_G1_POINT();
+    error PUBLIC_INPUT_GE_P();
+    error MOD_EXP_FAILURE();
+    error EC_SCALAR_MUL_FAILURE();
+    error PROOF_FAILURE();
+
+    function getVerificationKeyHash() public pure virtual returns (bytes32);
+
+    function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure virtual;
+
+    /**
+     * @notice Verify a Ultra Plonk proof
+     * @param _proof - The serialized proof
+     * @param _publicInputs - An array of the public inputs
+     * @return True if proof is valid, reverts otherwise
+     */
+    function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) {
+        loadVerificationKey(N_LOC, OMEGA_INVERSE_LOC);
+
+        uint256 requiredPublicInputCount;
+        assembly {
+            requiredPublicInputCount := mload(NUM_INPUTS_LOC)
+        }
+        if (requiredPublicInputCount != _publicInputs.length) {
+            revert PUBLIC_INPUT_COUNT_INVALID(requiredPublicInputCount, _publicInputs.length);
+        }
+
+        assembly {
+            let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // EC group order
+            let p := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // Prime field order
+
+            /**
+             * LOAD PROOF FROM CALLDATA
+             */
+            {
+                let data_ptr := add(calldataload(0x04), 0x24)
+
+                mstore(W1_Y_LOC, mod(calldataload(data_ptr), q))
+                mstore(W1_X_LOC, mod(calldataload(add(data_ptr, 0x20)), q))
+
+                mstore(W2_Y_LOC, mod(calldataload(add(data_ptr, 0x40)), q))
+                mstore(W2_X_LOC, mod(calldataload(add(data_ptr, 0x60)), q))
+
+                mstore(W3_Y_LOC, mod(calldataload(add(data_ptr, 0x80)), q))
+                mstore(W3_X_LOC, mod(calldataload(add(data_ptr, 0xa0)), q))
+
+                mstore(W4_Y_LOC, mod(calldataload(add(data_ptr, 0xc0)), q))
+                mstore(W4_X_LOC, mod(calldataload(add(data_ptr, 0xe0)), q))
+
+                mstore(S_Y_LOC, mod(calldataload(add(data_ptr, 0x100)), q))
+                mstore(S_X_LOC, mod(calldataload(add(data_ptr, 0x120)), q))
+                mstore(Z_Y_LOC, mod(calldataload(add(data_ptr, 0x140)), q))
+                mstore(Z_X_LOC, mod(calldataload(add(data_ptr, 0x160)), q))
+                mstore(Z_LOOKUP_Y_LOC, mod(calldataload(add(data_ptr, 0x180)), q))
+                mstore(Z_LOOKUP_X_LOC, mod(calldataload(add(data_ptr, 0x1a0)), q))
+                mstore(T1_Y_LOC, mod(calldataload(add(data_ptr, 0x1c0)), q))
+                mstore(T1_X_LOC, mod(calldataload(add(data_ptr, 0x1e0)), q))
+
+                mstore(T2_Y_LOC, mod(calldataload(add(data_ptr, 0x200)), q))
+                mstore(T2_X_LOC, mod(calldataload(add(data_ptr, 0x220)), q))
+
+                mstore(T3_Y_LOC, mod(calldataload(add(data_ptr, 0x240)), q))
+                mstore(T3_X_LOC, mod(calldataload(add(data_ptr, 0x260)), q))
+
+                mstore(T4_Y_LOC, mod(calldataload(add(data_ptr, 0x280)), q))
+                mstore(T4_X_LOC, mod(calldataload(add(data_ptr, 0x2a0)), q))
+
+                mstore(W1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2c0)), p))
+                mstore(W2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2e0)), p))
+                mstore(W3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x300)), p))
+                mstore(W4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x320)), p))
+                mstore(S_EVAL_LOC, mod(calldataload(add(data_ptr, 0x340)), p))
+                mstore(Z_EVAL_LOC, mod(calldataload(add(data_ptr, 0x360)), p))
+                mstore(Z_LOOKUP_EVAL_LOC, mod(calldataload(add(data_ptr, 0x380)), p))
+                mstore(Q1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3a0)), p))
+                mstore(Q2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3c0)), p))
+                mstore(Q3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3e0)), p))
+                mstore(Q4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x400)), p))
+                mstore(QM_EVAL_LOC, mod(calldataload(add(data_ptr, 0x420)), p))
+                mstore(QC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x440)), p))
+                mstore(QARITH_EVAL_LOC, mod(calldataload(add(data_ptr, 0x460)), p))
+                mstore(QSORT_EVAL_LOC, mod(calldataload(add(data_ptr, 0x480)), p))
+                mstore(QELLIPTIC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4a0)), p))
+                mstore(QAUX_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4c0)), p))
+
+                mstore(SIGMA1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4e0)), p))
+                mstore(SIGMA2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x500)), p))
+
+                mstore(SIGMA3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x520)), p))
+                mstore(SIGMA4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x540)), p))
+
+                mstore(TABLE1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x560)), p))
+                mstore(TABLE2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x580)), p))
+                mstore(TABLE3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5a0)), p))
+                mstore(TABLE4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5c0)), p))
+                mstore(TABLE_TYPE_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5e0)), p))
+
+                mstore(ID1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x600)), p))
+                mstore(ID2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x620)), p))
+                mstore(ID3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x640)), p))
+                mstore(ID4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x660)), p))
+
+                mstore(W1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x680)), p))
+                mstore(W2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6a0)), p))
+                mstore(W3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6c0)), p))
+                mstore(W4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6e0)), p))
+                mstore(S_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x700)), p))
+
+                mstore(Z_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x720)), p))
+
+                mstore(Z_LOOKUP_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x740)), p))
+                mstore(TABLE1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x760)), p))
+                mstore(TABLE2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x780)), p))
+                mstore(TABLE3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7a0)), p))
+                mstore(TABLE4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7c0)), p))
+
+                mstore(PI_Z_Y_LOC, mod(calldataload(add(data_ptr, 0x7e0)), q))
+                mstore(PI_Z_X_LOC, mod(calldataload(add(data_ptr, 0x800)), q))
+
+                mstore(PI_Z_OMEGA_Y_LOC, mod(calldataload(add(data_ptr, 0x820)), q))
+                mstore(PI_Z_OMEGA_X_LOC, mod(calldataload(add(data_ptr, 0x840)), q))
+            }
+
+            /**
+             * LOAD RECURSIVE PROOF INTO MEMORY
+             */
+            {
+                if mload(CONTAINS_RECURSIVE_PROOF_LOC) {
+                    let public_inputs_ptr := add(calldataload(0x24), 0x24)
+                    let index_counter := add(shl(5, mload(RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC)), public_inputs_ptr)
+
+                    let x0 := calldataload(index_counter)
+                    x0 := add(x0, shl(68, calldataload(add(index_counter, 0x20))))
+                    x0 := add(x0, shl(136, calldataload(add(index_counter, 0x40))))
+                    x0 := add(x0, shl(204, calldataload(add(index_counter, 0x60))))
+                    let y0 := calldataload(add(index_counter, 0x80))
+                    y0 := add(y0, shl(68, calldataload(add(index_counter, 0xa0))))
+                    y0 := add(y0, shl(136, calldataload(add(index_counter, 0xc0))))
+                    y0 := add(y0, shl(204, calldataload(add(index_counter, 0xe0))))
+                    let x1 := calldataload(add(index_counter, 0x100))
+                    x1 := add(x1, shl(68, calldataload(add(index_counter, 0x120))))
+                    x1 := add(x1, shl(136, calldataload(add(index_counter, 0x140))))
+                    x1 := add(x1, shl(204, calldataload(add(index_counter, 0x160))))
+                    let y1 := calldataload(add(index_counter, 0x180))
+                    y1 := add(y1, shl(68, calldataload(add(index_counter, 0x1a0))))
+                    y1 := add(y1, shl(136, calldataload(add(index_counter, 0x1c0))))
+                    y1 := add(y1, shl(204, calldataload(add(index_counter, 0x1e0))))
+                    mstore(RECURSIVE_P1_X_LOC, x0)
+                    mstore(RECURSIVE_P1_Y_LOC, y0)
+                    mstore(RECURSIVE_P2_X_LOC, x1)
+                    mstore(RECURSIVE_P2_Y_LOC, y1)
+
+                    // validate these are valid bn128 G1 points
+                    if iszero(and(and(lt(x0, q), lt(x1, q)), and(lt(y0, q), lt(y1, q)))) {
+                        mstore(0x00, PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR)
+                        revert(0x00, 0x04)
+                    }
+                }
+            }
+
+            {
+                /**
+                 * Generate initial challenge
+                 */
+                mstore(0x00, shl(224, mload(N_LOC)))
+                mstore(0x04, shl(224, mload(NUM_INPUTS_LOC)))
+                let challenge := keccak256(0x00, 0x08)
+
+                /**
+                 * Generate eta challenge
+                 */
+                mstore(PUBLIC_INPUTS_HASH_LOCATION, challenge)
+                // The public input location is stored at 0x24, we then add 0x24 to skip selector and the length of public inputs
+                let public_inputs_start := add(calldataload(0x24), 0x24)
+                // copy the public inputs over
+                let public_input_size := mul(mload(NUM_INPUTS_LOC), 0x20)
+                calldatacopy(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_inputs_start, public_input_size)
+
+                // copy W1, W2, W3 into challenge. Each point is 0x40 bytes, so load 0xc0 = 3 * 0x40 bytes (ETA input length)
+                let w_start := add(calldataload(0x04), 0x24)
+                calldatacopy(add(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_input_size), w_start, ETA_INPUT_LENGTH)
+
+                // Challenge is the old challenge + public inputs + W1, W2, W3 (0x20 + public_input_size + 0xc0)
+                let challenge_bytes_size := add(0x20, add(public_input_size, ETA_INPUT_LENGTH))
+
+                challenge := keccak256(PUBLIC_INPUTS_HASH_LOCATION, challenge_bytes_size)
+                {
+                    let eta := mod(challenge, p)
+                    mstore(C_ETA_LOC, eta)
+                    mstore(C_ETA_SQR_LOC, mulmod(eta, eta, p))
+                    mstore(C_ETA_CUBE_LOC, mulmod(mload(C_ETA_SQR_LOC), eta, p))
+                }
+
+                /**
+                 * Generate beta challenge
+                 */
+                mstore(0x00, challenge)
+                mstore(0x20, mload(W4_Y_LOC))
+                mstore(0x40, mload(W4_X_LOC))
+                mstore(0x60, mload(S_Y_LOC))
+                mstore(0x80, mload(S_X_LOC))
+                challenge := keccak256(0x00, 0xa0)
+                mstore(C_BETA_LOC, mod(challenge, p))
+
+                /**
+                 * Generate gamma challenge
+                 */
+                mstore(0x00, challenge)
+                mstore8(0x20, 0x01)
+                challenge := keccak256(0x00, 0x21)
+                mstore(C_GAMMA_LOC, mod(challenge, p))
+
+                /**
+                 * Generate alpha challenge
+                 */
+                mstore(0x00, challenge)
+                mstore(0x20, mload(Z_Y_LOC))
+                mstore(0x40, mload(Z_X_LOC))
+                mstore(0x60, mload(Z_LOOKUP_Y_LOC))
+                mstore(0x80, mload(Z_LOOKUP_X_LOC))
+                challenge := keccak256(0x00, 0xa0)
+                mstore(C_ALPHA_LOC, mod(challenge, p))
+
+                /**
+                 * Compute and store some powers of alpha for future computations
+                 */
+                let alpha := mload(C_ALPHA_LOC)
+                mstore(C_ALPHA_SQR_LOC, mulmod(alpha, alpha, p))
+                mstore(C_ALPHA_CUBE_LOC, mulmod(mload(C_ALPHA_SQR_LOC), alpha, p))
+                mstore(C_ALPHA_QUAD_LOC, mulmod(mload(C_ALPHA_CUBE_LOC), alpha, p))
+                mstore(C_ALPHA_BASE_LOC, alpha)
+
+                /**
+                 * Generate zeta challenge
+                 */
+                mstore(0x00, challenge)
+                mstore(0x20, mload(T1_Y_LOC))
+                mstore(0x40, mload(T1_X_LOC))
+                mstore(0x60, mload(T2_Y_LOC))
+                mstore(0x80, mload(T2_X_LOC))
+                mstore(0xa0, mload(T3_Y_LOC))
+                mstore(0xc0, mload(T3_X_LOC))
+                mstore(0xe0, mload(T4_Y_LOC))
+                mstore(0x100, mload(T4_X_LOC))
+
+                challenge := keccak256(0x00, 0x120)
+
+                mstore(C_ZETA_LOC, mod(challenge, p))
+                mstore(C_CURRENT_LOC, challenge)
+            }
+
+            /**
+             * EVALUATE FIELD OPERATIONS
+             */
+
+            /**
+             * COMPUTE PUBLIC INPUT DELTA
+             * ΔPI = ∏ᵢ∈ℓ(wᵢ + β σ(i) + γ) / ∏ᵢ∈ℓ(wᵢ + β σ'(i) + γ)
+             */
+            {
+                let beta := mload(C_BETA_LOC) // β
+                let gamma := mload(C_GAMMA_LOC) // γ
+                let work_root := mload(OMEGA_LOC) // ω
+                let numerator_value := 1
+                let denominator_value := 1
+
+                let p_clone := p // move p to the front of the stack
+                let valid_inputs := true
+
+                // Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24])
+                let public_inputs_ptr := add(calldataload(0x24), 0x24)
+
+                // endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes
+                let endpoint_ptr := add(public_inputs_ptr, mul(mload(NUM_INPUTS_LOC), 0x20))
+
+                // root_1 = β * 0x05
+                let root_1 := mulmod(beta, 0x05, p_clone) // k1.β
+                // root_2 = β * 0x0c
+                let root_2 := mulmod(beta, 0x0c, p_clone)
+                // @note 0x05 + 0x07 == 0x0c == external coset generator
+
+                for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } {
+                    /**
+                     * input = public_input[i]
+                     * valid_inputs &= input < p
+                     * temp = input + gamma
+                     * numerator_value *= (β.σ(i) + wᵢ + γ)  // σ(i) = 0x05.ωⁱ
+                     * denominator_value *= (β.σ'(i) + wᵢ + γ) // σ'(i) = 0x0c.ωⁱ
+                     * root_1 *= ω
+                     * root_2 *= ω
+                     */
+
+                    let input := calldataload(public_inputs_ptr)
+                    valid_inputs := and(valid_inputs, lt(input, p_clone))
+                    let temp := addmod(input, gamma, p_clone)
+
+                    numerator_value := mulmod(numerator_value, add(root_1, temp), p_clone)
+                    denominator_value := mulmod(denominator_value, add(root_2, temp), p_clone)
+
+                    root_1 := mulmod(root_1, work_root, p_clone)
+                    root_2 := mulmod(root_2, work_root, p_clone)
+                }
+
+                // Revert if not all public inputs are field elements (i.e. < p)
+                if iszero(valid_inputs) {
+                    mstore(0x00, PUBLIC_INPUT_GE_P_SELECTOR)
+                    revert(0x00, 0x04)
+                }
+
+                mstore(DELTA_NUMERATOR_LOC, numerator_value)
+                mstore(DELTA_DENOMINATOR_LOC, denominator_value)
+            }
+
+            /**
+             * Compute Plookup delta factor [γ(1 + β)]^{n-k}
+             * k = num roots cut out of Z_H = 4
+             */
+            {
+                let delta_base := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p)
+                let delta_numerator := delta_base
+                {
+                    let exponent := mload(N_LOC)
+                    let count := 1
+                    for {} lt(count, exponent) { count := add(count, count) } {
+                        delta_numerator := mulmod(delta_numerator, delta_numerator, p)
+                    }
+                }
+                mstore(PLOOKUP_DELTA_NUMERATOR_LOC, delta_numerator)
+
+                let delta_denominator := mulmod(delta_base, delta_base, p)
+                delta_denominator := mulmod(delta_denominator, delta_denominator, p)
+                mstore(PLOOKUP_DELTA_DENOMINATOR_LOC, delta_denominator)
+            }
+            /**
+             * Compute lagrange poly and vanishing poly fractions
+             */
+            {
+                /**
+                 * vanishing_numerator = zeta
+                 * ZETA_POW_N = zeta^n
+                 * vanishing_numerator -= 1
+                 * accumulating_root = omega_inverse
+                 * work_root = p - accumulating_root
+                 * domain_inverse = domain_inverse
+                 * vanishing_denominator = zeta + work_root
+                 * work_root *= accumulating_root
+                 * vanishing_denominator *= (zeta + work_root)
+                 * work_root *= accumulating_root
+                 * vanishing_denominator *= (zeta + work_root)
+                 * vanishing_denominator *= (zeta + (zeta + accumulating_root))
+                 * work_root = omega
+                 * lagrange_numerator = vanishing_numerator * domain_inverse
+                 * l_start_denominator = zeta - 1
+                 * accumulating_root = work_root^2
+                 * l_end_denominator = accumulating_root^2 * work_root * zeta - 1
+                 * Note: l_end_denominator term contains a term \omega^5 to cut out 5 roots of unity from vanishing poly
+                 */
+
+                let zeta := mload(C_ZETA_LOC)
+
+                // compute zeta^n, where n is a power of 2
+                let vanishing_numerator := zeta
+                {
+                    // pow_small
+                    let exponent := mload(N_LOC)
+                    let count := 1
+                    for {} lt(count, exponent) { count := add(count, count) } {
+                        vanishing_numerator := mulmod(vanishing_numerator, vanishing_numerator, p)
+                    }
+                }
+                mstore(ZETA_POW_N_LOC, vanishing_numerator)
+                vanishing_numerator := addmod(vanishing_numerator, sub(p, 1), p)
+
+                let accumulating_root := mload(OMEGA_INVERSE_LOC)
+                let work_root := sub(p, accumulating_root)
+                let domain_inverse := mload(DOMAIN_INVERSE_LOC)
+
+                let vanishing_denominator := addmod(zeta, work_root, p)
+                work_root := mulmod(work_root, accumulating_root, p)
+                vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p)
+                work_root := mulmod(work_root, accumulating_root, p)
+                vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p)
+                vanishing_denominator :=
+                    mulmod(vanishing_denominator, addmod(zeta, mulmod(work_root, accumulating_root, p), p), p)
+
+                work_root := mload(OMEGA_LOC)
+
+                let lagrange_numerator := mulmod(vanishing_numerator, domain_inverse, p)
+                let l_start_denominator := addmod(zeta, sub(p, 1), p)
+
+                accumulating_root := mulmod(work_root, work_root, p)
+
+                let l_end_denominator :=
+                    addmod(
+                        mulmod(mulmod(mulmod(accumulating_root, accumulating_root, p), work_root, p), zeta, p), sub(p, 1), p
+                    )
+
+                /**
+                 * Compute inversions using Montgomery's batch inversion trick
+                 */
+                let accumulator := mload(DELTA_DENOMINATOR_LOC)
+                let t0 := accumulator
+                accumulator := mulmod(accumulator, vanishing_denominator, p)
+                let t1 := accumulator
+                accumulator := mulmod(accumulator, vanishing_numerator, p)
+                let t2 := accumulator
+                accumulator := mulmod(accumulator, l_start_denominator, p)
+                let t3 := accumulator
+                accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p)
+                let t4 := accumulator
+                {
+                    mstore(0, 0x20)
+                    mstore(0x20, 0x20)
+                    mstore(0x40, 0x20)
+                    mstore(0x60, mulmod(accumulator, l_end_denominator, p))
+                    mstore(0x80, sub(p, 2))
+                    mstore(0xa0, p)
+                    if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) {
+                        mstore(0x0, MOD_EXP_FAILURE_SELECTOR)
+                        revert(0x00, 0x04)
+                    }
+                    accumulator := mload(0x00)
+                }
+
+                t4 := mulmod(accumulator, t4, p)
+                accumulator := mulmod(accumulator, l_end_denominator, p)
+
+                t3 := mulmod(accumulator, t3, p)
+                accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p)
+
+                t2 := mulmod(accumulator, t2, p)
+                accumulator := mulmod(accumulator, l_start_denominator, p)
+
+                t1 := mulmod(accumulator, t1, p)
+                accumulator := mulmod(accumulator, vanishing_numerator, p)
+
+                t0 := mulmod(accumulator, t0, p)
+                accumulator := mulmod(accumulator, vanishing_denominator, p)
+
+                accumulator := mulmod(mulmod(accumulator, accumulator, p), mload(DELTA_DENOMINATOR_LOC), p)
+
+                mstore(PUBLIC_INPUT_DELTA_LOC, mulmod(mload(DELTA_NUMERATOR_LOC), accumulator, p))
+                mstore(ZERO_POLY_LOC, mulmod(vanishing_numerator, t0, p))
+                mstore(ZERO_POLY_INVERSE_LOC, mulmod(vanishing_denominator, t1, p))
+                mstore(L_START_LOC, mulmod(lagrange_numerator, t2, p))
+                mstore(PLOOKUP_DELTA_LOC, mulmod(mload(PLOOKUP_DELTA_NUMERATOR_LOC), t3, p))
+                mstore(L_END_LOC, mulmod(lagrange_numerator, t4, p))
+            }
+
+            /**
+             * UltraPlonk Widget Ordering:
+             *
+             * 1. Permutation widget
+             * 2. Plookup widget
+             * 3. Arithmetic widget
+             * 4. Fixed base widget (?)
+             * 5. GenPermSort widget
+             * 6. Elliptic widget
+             * 7. Auxiliary widget
+             */
+
+            /**
+             * COMPUTE PERMUTATION WIDGET EVALUATION
+             */
+            {
+                let alpha := mload(C_ALPHA_LOC)
+                let beta := mload(C_BETA_LOC)
+                let gamma := mload(C_GAMMA_LOC)
+
+                /**
+                 * t1 = (W1 + gamma + beta * ID1) * (W2 + gamma + beta * ID2)
+                 * t2 = (W3 + gamma + beta * ID3) * (W4 + gamma + beta * ID4)
+                 * result = alpha_base * z_eval * t1 * t2
+                 * t1 = (W1 + gamma + beta * sigma_1_eval) * (W2 + gamma + beta * sigma_2_eval)
+                 * t2 = (W2 + gamma + beta * sigma_3_eval) * (W3 + gamma + beta * sigma_4_eval)
+                 * result -= (alpha_base * z_omega_eval * t1 * t2)
+                 */
+                let t1 :=
+                    mulmod(
+                        add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)),
+                        add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)),
+                        p
+                    )
+                let t2 :=
+                    mulmod(
+                        add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)),
+                        add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)),
+                        p
+                    )
+                let result := mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_EVAL_LOC), mulmod(t1, t2, p), p), p)
+                t1 :=
+                    mulmod(
+                        add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)),
+                        add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)),
+                        p
+                    )
+                t2 :=
+                    mulmod(
+                        add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)),
+                        add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)),
+                        p
+                    )
+                result :=
+                    addmod(
+                        result,
+                        sub(p, mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_OMEGA_EVAL_LOC), mulmod(t1, t2, p), p), p)),
+                        p
+                    )
+
+                /**
+                 * alpha_base *= alpha
+                 * result += alpha_base . (L_{n-k}(ʓ) . (z(ʓ.ω) - ∆_{PI}))
+                 * alpha_base *= alpha
+                 * result += alpha_base . (L_1(ʓ)(Z(ʓ) - 1))
+                 * alpha_Base *= alpha
+                 */
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
+                result :=
+                    addmod(
+                        result,
+                        mulmod(
+                            mload(C_ALPHA_BASE_LOC),
+                            mulmod(
+                                mload(L_END_LOC),
+                                addmod(mload(Z_OMEGA_EVAL_LOC), sub(p, mload(PUBLIC_INPUT_DELTA_LOC)), p),
+                                p
+                            ),
+                            p
+                        ),
+                        p
+                    )
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
+                mstore(
+                    PERMUTATION_IDENTITY,
+                    addmod(
+                        result,
+                        mulmod(
+                            mload(C_ALPHA_BASE_LOC),
+                            mulmod(mload(L_START_LOC), addmod(mload(Z_EVAL_LOC), sub(p, 1), p), p),
+                            p
+                        ),
+                        p
+                    )
+                )
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p))
+            }
+
+            /**
+             * COMPUTE PLOOKUP WIDGET EVALUATION
+             */
+            {
+                /**
+                 * Goal: f = (w1(z) + q2.w1(zω)) + η(w2(z) + qm.w2(zω)) + η²(w3(z) + qc.w_3(zω)) + q3(z).η³
+                 * f = η.q3(z)
+                 * f += (w3(z) + qc.w_3(zω))
+                 * f *= η
+                 * f += (w2(z) + qm.w2(zω))
+                 * f *= η
+                 * f += (w1(z) + q2.w1(zω))
+                 */
+                let f := mulmod(mload(C_ETA_LOC), mload(Q3_EVAL_LOC), p)
+                f :=
+                    addmod(f, addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_OMEGA_EVAL_LOC), p), p), p)
+                f := mulmod(f, mload(C_ETA_LOC), p)
+                f :=
+                    addmod(f, addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p), p)
+                f := mulmod(f, mload(C_ETA_LOC), p)
+                f :=
+                    addmod(f, addmod(mload(W1_EVAL_LOC), mulmod(mload(Q2_EVAL_LOC), mload(W1_OMEGA_EVAL_LOC), p), p), p)
+
+                // t(z) = table4(z).η³ + table3(z).η² + table2(z).η + table1(z)
+                let t :=
+                    addmod(
+                        addmod(
+                            addmod(
+                                mulmod(mload(TABLE4_EVAL_LOC), mload(C_ETA_CUBE_LOC), p),
+                                mulmod(mload(TABLE3_EVAL_LOC), mload(C_ETA_SQR_LOC), p),
+                                p
+                            ),
+                            mulmod(mload(TABLE2_EVAL_LOC), mload(C_ETA_LOC), p),
+                            p
+                        ),
+                        mload(TABLE1_EVAL_LOC),
+                        p
+                    )
+
+                // t(zw) = table4(zw).η³ + table3(zw).η² + table2(zw).η + table1(zw)
+                let t_omega :=
+                    addmod(
+                        addmod(
+                            addmod(
+                                mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_ETA_CUBE_LOC), p),
+                                mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_ETA_SQR_LOC), p),
+                                p
+                            ),
+                            mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p),
+                            p
+                        ),
+                        mload(TABLE1_OMEGA_EVAL_LOC),
+                        p
+                    )
+
+                /**
+                 * Goal: numerator = (TABLE_TYPE_EVAL * f(z) + γ) * (t(z) + βt(zω) + γ(β + 1)) * (β + 1)
+                 * gamma_beta_constant = γ(β + 1)
+                 * numerator = f * TABLE_TYPE_EVAL + gamma
+                 * temp0 = t(z) + t(zω) * β + gamma_beta_constant
+                 * numerator *= temp0
+                 * numerator *= (β + 1)
+                 * temp0 = alpha * l_1
+                 * numerator += temp0
+                 * numerator *= z_lookup(z)
+                 * numerator -= temp0
+                 */
+                let gamma_beta_constant := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p)
+                let numerator := addmod(mulmod(f, mload(TABLE_TYPE_EVAL_LOC), p), mload(C_GAMMA_LOC), p)
+                let temp0 := addmod(addmod(t, mulmod(t_omega, mload(C_BETA_LOC), p), p), gamma_beta_constant, p)
+                numerator := mulmod(numerator, temp0, p)
+                numerator := mulmod(numerator, addmod(mload(C_BETA_LOC), 1, p), p)
+                temp0 := mulmod(mload(C_ALPHA_LOC), mload(L_START_LOC), p)
+                numerator := addmod(numerator, temp0, p)
+                numerator := mulmod(numerator, mload(Z_LOOKUP_EVAL_LOC), p)
+                numerator := addmod(numerator, sub(p, temp0), p)
+
+                /**
+                 * Goal: denominator = z_lookup(zω)*[s(z) + βs(zω) + γ(1 + β)] - [z_lookup(zω) - [γ(1 + β)]^{n-k}]*α²L_end(z)
+                 * note: delta_factor = [γ(1 + β)]^{n-k}
+                 * denominator = s(z) + βs(zω) + γ(β + 1)
+                 * temp1 = α²L_end(z)
+                 * denominator -= temp1
+                 * denominator *= z_lookup(zω)
+                 * denominator += temp1 * delta_factor
+                 * PLOOKUP_IDENTITY = (numerator - denominator).alpha_base
+                 * alpha_base *= alpha^3
+                 */
+                let denominator :=
+                    addmod(
+                        addmod(mload(S_EVAL_LOC), mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_BETA_LOC), p), p),
+                        gamma_beta_constant,
+                        p
+                    )
+                let temp1 := mulmod(mload(C_ALPHA_SQR_LOC), mload(L_END_LOC), p)
+                denominator := addmod(denominator, sub(p, temp1), p)
+                denominator := mulmod(denominator, mload(Z_LOOKUP_OMEGA_EVAL_LOC), p)
+                denominator := addmod(denominator, mulmod(temp1, mload(PLOOKUP_DELTA_LOC), p), p)
+
+                mstore(PLOOKUP_IDENTITY, mulmod(addmod(numerator, sub(p, denominator), p), mload(C_ALPHA_BASE_LOC), p))
+
+                // update alpha
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p))
+            }
+
+            /**
+             * COMPUTE ARITHMETIC WIDGET EVALUATION
+             */
+            {
+                /**
+                 * The basic arithmetic gate identity in standard plonk is as follows.
+                 * (w_1 . w_2 . q_m) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c = 0
+                 * However, for Ultraplonk, we extend this to support "passing" wires between rows (shown without alpha scaling below):
+                 * q_arith * ( ( (-1/2) * (q_arith - 3) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c ) +
+                 * (q_arith - 1)*( α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + w_4_omega) ) = 0
+                 *
+                 * This formula results in several cases depending on q_arith:
+                 * 1. q_arith == 0: Arithmetic gate is completely disabled
+                 *
+                 * 2. q_arith == 1: Everything in the minigate on the right is disabled. The equation is just a standard plonk equation
+                 * with extra wires: q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c = 0
+                 *
+                 * 3. q_arith == 2: The (w_1 + w_4 - ...) term is disabled. THe equation is:
+                 * (1/2) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + w_4_omega = 0
+                 * It allows defining w_4 at next index (w_4_omega) in terms of current wire values
+                 *
+                 * 4. q_arith == 3: The product of w_1 and w_2 is disabled, but a mini addition gate is enabled. α allows us to split
+                 * the equation into two:
+                 *
+                 * q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0
+                 * and
+                 * w_1 + w_4 - w_1_omega + q_m = 0  (we are reusing q_m here)
+                 *
+                 * 5. q_arith > 3: The product of w_1 and w_2 is scaled by (q_arith - 3), while the w_4_omega term is scaled by (q_arith - 1).
+                 * The equation can be split into two:
+                 *
+                 * (q_arith - 3)* q_m * w_1 * w_ 2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + (q_arith - 1) * w_4_omega = 0
+                 * and
+                 * w_1 + w_4 - w_1_omega + q_m = 0
+                 *
+                 * The problem that q_m is used both in both equations can be dealt with by appropriately changing selector values at
+                 * the next gate. Then we can treat (q_arith - 1) as a simulated q_6 selector and scale q_m to handle (q_arith - 3) at
+                 * product.
+                 */
+
+                let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(Q1_EVAL_LOC), p)
+                let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(Q2_EVAL_LOC), p)
+                let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(Q3_EVAL_LOC), p)
+                let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p)
+
+                // @todo - Add a explicit test that hits QARITH == 3
+                // w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2
+                let w1w2qm :=
+                    mulmod(
+                        mulmod(
+                            mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p),
+                            addmod(mload(QARITH_EVAL_LOC), sub(p, 3), p),
+                            p
+                        ),
+                        NEGATIVE_INVERSE_OF_2_MODULO_P,
+                        p
+                    )
+
+                // (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c
+                let identity :=
+                    addmod(
+                        mload(QC_EVAL_LOC), addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p), p
+                    )
+
+                // if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where:
+                // w_1 + w_4 - w_1_omega + q_m = 0
+                // we use this gate to save an addition gate when adding or subtracting non-native field elements
+                // α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m)
+                let extra_small_addition_gate_identity :=
+                    mulmod(
+                        mload(C_ALPHA_LOC),
+                        mulmod(
+                            addmod(mload(QARITH_EVAL_LOC), sub(p, 2), p),
+                            addmod(
+                                mload(QM_EVAL_LOC),
+                                addmod(
+                                    sub(p, mload(W1_OMEGA_EVAL_LOC)), addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), p
+                                ),
+                                p
+                            ),
+                            p
+                        ),
+                        p
+                    )
+
+                // if q_arith == 2 OR q_arith == 3 we add the 4th wire of the NEXT gate into the arithmetic identity
+                // N.B. if q_arith > 2, this wire value will be scaled by (q_arith - 1) relative to the other gate wires!
+                // alpha_base * q_arith * (identity + (q_arith - 1) * (w_4_omega + extra_small_addition_gate_identity))
+                mstore(
+                    ARITHMETIC_IDENTITY,
+                    mulmod(
+                        mload(C_ALPHA_BASE_LOC),
+                        mulmod(
+                            mload(QARITH_EVAL_LOC),
+                            addmod(
+                                identity,
+                                mulmod(
+                                    addmod(mload(QARITH_EVAL_LOC), sub(p, 1), p),
+                                    addmod(mload(W4_OMEGA_EVAL_LOC), extra_small_addition_gate_identity, p),
+                                    p
+                                ),
+                                p
+                            ),
+                            p
+                        ),
+                        p
+                    )
+                )
+
+                // update alpha
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p))
+            }
+
+            /**
+             * COMPUTE GENPERMSORT WIDGET EVALUATION
+             */
+            {
+                /**
+                 * D1 = (w2 - w1)
+                 * D2 = (w3 - w2)
+                 * D3 = (w4 - w3)
+                 * D4 = (w1_omega - w4)
+                 *
+                 * α_a = alpha_base
+                 * α_b = alpha_base * α
+                 * α_c = alpha_base * α^2
+                 * α_d = alpha_base * α^3
+                 *
+                 * range_accumulator = (
+                 *   D1(D1 - 1)(D1 - 2)(D1 - 3).α_a +
+                 *   D2(D2 - 1)(D2 - 2)(D2 - 3).α_b +
+                 *   D3(D3 - 1)(D3 - 2)(D3 - 3).α_c +
+                 *   D4(D4 - 1)(D4 - 2)(D4 - 3).α_d +
+                 * ) . q_sort
+                 */
+                let minus_two := sub(p, 2)
+                let minus_three := sub(p, 3)
+                let d1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
+                let d2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
+                let d3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
+                let d4 := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
+
+                let range_accumulator :=
+                    mulmod(
+                        mulmod(
+                            mulmod(addmod(mulmod(d1, d1, p), sub(p, d1), p), addmod(d1, minus_two, p), p),
+                            addmod(d1, minus_three, p),
+                            p
+                        ),
+                        mload(C_ALPHA_BASE_LOC),
+                        p
+                    )
+                range_accumulator :=
+                    addmod(
+                        range_accumulator,
+                        mulmod(
+                            mulmod(
+                                mulmod(addmod(mulmod(d2, d2, p), sub(p, d2), p), addmod(d2, minus_two, p), p),
+                                addmod(d2, minus_three, p),
+                                p
+                            ),
+                            mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                range_accumulator :=
+                    addmod(
+                        range_accumulator,
+                        mulmod(
+                            mulmod(
+                                mulmod(addmod(mulmod(d3, d3, p), sub(p, d3), p), addmod(d3, minus_two, p), p),
+                                addmod(d3, minus_three, p),
+                                p
+                            ),
+                            mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                range_accumulator :=
+                    addmod(
+                        range_accumulator,
+                        mulmod(
+                            mulmod(
+                                mulmod(addmod(mulmod(d4, d4, p), sub(p, d4), p), addmod(d4, minus_two, p), p),
+                                addmod(d4, minus_three, p),
+                                p
+                            ),
+                            mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                range_accumulator := mulmod(range_accumulator, mload(QSORT_EVAL_LOC), p)
+
+                mstore(SORT_IDENTITY, range_accumulator)
+
+                // update alpha
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p))
+            }
+
+            /**
+             * COMPUTE ELLIPTIC WIDGET EVALUATION
+             */
+            {
+                /**
+                 * endo_term = (-x_2) * x_1 * (x_3 * 2 + x_1) * q_beta
+                 * endo_sqr_term = x_2^2
+                 * endo_sqr_term *= (x_3 - x_1)
+                 * endo_sqr_term *= q_beta^2
+                 * leftovers = x_2^2
+                 * leftovers *= x_2
+                 * leftovers += x_1^2 * (x_3 + x_1) @follow-up Invalid comment in BB widget
+                 * leftovers -= (y_2^2 + y_1^2)
+                 * sign_term = y_2 * y_1
+                 * sign_term += sign_term
+                 * sign_term *= q_sign
+                 */
+
+                let endo_term :=
+                    mulmod(
+                        mulmod(
+                            mulmod(sub(p, mload(X2_EVAL_LOC)), mload(X1_EVAL_LOC), p),
+                            addmod(addmod(mload(X3_EVAL_LOC), mload(X3_EVAL_LOC), p), mload(X1_EVAL_LOC), p),
+                            p
+                        ),
+                        mload(QBETA_LOC),
+                        p
+                    )
+
+                let endo_sqr_term := mulmod(mload(X2_EVAL_LOC), mload(X2_EVAL_LOC), p)
+                endo_sqr_term := mulmod(endo_sqr_term, addmod(mload(X3_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p), p)
+                endo_sqr_term := mulmod(endo_sqr_term, mload(QBETA_SQR_LOC), p)
+
+                let leftovers := mulmod(mload(X2_EVAL_LOC), mload(X2_EVAL_LOC), p)
+                leftovers := mulmod(leftovers, mload(X2_EVAL_LOC), p)
+                leftovers :=
+                    addmod(
+                        leftovers,
+                        mulmod(
+                            mulmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p),
+                            addmod(mload(X3_EVAL_LOC), mload(X1_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                leftovers :=
+                    addmod(
+                        leftovers,
+                        sub(
+                            p,
+                            addmod(
+                                mulmod(mload(Y2_EVAL_LOC), mload(Y2_EVAL_LOC), p),
+                                mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p),
+                                p
+                            )
+                        ),
+                        p
+                    )
+
+                let sign_term := mulmod(mload(Y2_EVAL_LOC), mload(Y1_EVAL_LOC), p)
+                sign_term := addmod(sign_term, sign_term, p)
+                sign_term := mulmod(sign_term, mload(QSIGN_LOC), p)
+
+                /**
+                 * x_identity = endo_term + endo_sqr_term + sign_term + leftovers
+                 * x_identity *= alpha_base
+                 * endo_term = (x_2 * q_beta) * (y_3 + y_1)
+                 * sign_term = -((y2 * q_sign) * (x_1 + x_3))
+                 * leftovers = - x1 * (y_3 + y_1) + y_1 * (x_1 - x_3)
+                 * y_identity = (endo_term + sign_term + leftovers) * (alpha_base * α)
+                 */
+
+                let x_identity := addmod(addmod(endo_term, endo_sqr_term, p), addmod(sign_term, leftovers, p), p)
+                x_identity := mulmod(x_identity, mload(C_ALPHA_BASE_LOC), p)
+                endo_term :=
+                    mulmod(
+                        mulmod(mload(X2_EVAL_LOC), mload(QBETA_LOC), p),
+                        addmod(mload(Y3_EVAL_LOC), mload(Y1_EVAL_LOC), p),
+                        p
+                    )
+                sign_term :=
+                    sub(
+                        p,
+                        mulmod(
+                            mulmod(mload(Y2_EVAL_LOC), mload(QSIGN_LOC), p),
+                            addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p),
+                            p
+                        )
+                    )
+                leftovers :=
+                    addmod(
+                        sub(p, mulmod(mload(X1_EVAL_LOC), addmod(mload(Y3_EVAL_LOC), mload(Y1_EVAL_LOC), p), p)),
+                        mulmod(mload(Y1_EVAL_LOC), addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p), p),
+                        p
+                    )
+                let y_identity :=
+                    mulmod(
+                        addmod(addmod(endo_term, sign_term, p), leftovers, p),
+                        mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p),
+                        p
+                    )
+
+                // ELLIPTIC_IDENTITY = (x_identity + y_identity) * Q_ELLIPTIC_EVAL
+                mstore(ELLIPTIC_IDENTITY, mulmod(addmod(x_identity, y_identity, p), mload(QELLIPTIC_EVAL_LOC), p))
+
+                // update alpha
+                // The paper says to use ALPHA^2, we use ALPHA^4 this is a small oversight in the prover protocol
+                mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p))
+            }
+
+            /**
+             * COMPUTE AUXILIARY WIDGET EVALUATION
+             */
+            {
+                {
+                    /**
+                     * Non native field arithmetic gate 2
+                     *             _                                                                               _
+                     *            /   _                   _                               _       14                \
+                     * q_2 . q_4 |   (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2    - w_3 - w_4   |
+                     *            \_                                                                               _/
+                     *
+                     * limb_subproduct = w_1 . w_2_omega + w_1_omega . w_2
+                     * non_native_field_gate_2 = w_1 * w_4 + w_4 * w_3 - w_3_omega
+                     * non_native_field_gate_2 = non_native_field_gate_2 * limb_size
+                     * non_native_field_gate_2 -= w_4_omega
+                     * non_native_field_gate_2 += limb_subproduct
+                     * non_native_field_gate_2 *= q_4
+                     * limb_subproduct *= limb_size
+                     * limb_subproduct += w_1_omega * w_2_omega
+                     * non_native_field_gate_1 = (limb_subproduct + w_3 + w_4) * q_3
+                     * non_native_field_gate_3 = (limb_subproduct + w_4 - (w_3_omega + w_4_omega)) * q_m
+                     * non_native_field_identity = (non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3) * q_2
+                     */
+
+                    let limb_subproduct :=
+                        addmod(
+                            mulmod(mload(W1_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p),
+                            mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_EVAL_LOC), p),
+                            p
+                        )
+
+                    let non_native_field_gate_2 :=
+                        addmod(
+                            addmod(
+                                mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p),
+                                mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p),
+                                p
+                            ),
+                            sub(p, mload(W3_OMEGA_EVAL_LOC)),
+                            p
+                        )
+                    non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p)
+                    non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p)
+                    non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p)
+                    non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p)
+                    limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p)
+                    limb_subproduct :=
+                        addmod(limb_subproduct, mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p)
+                    let non_native_field_gate_1 :=
+                        mulmod(
+                            addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p),
+                            mload(Q3_EVAL_LOC),
+                            p
+                        )
+                    let non_native_field_gate_3 :=
+                        mulmod(
+                            addmod(
+                                addmod(limb_subproduct, mload(W4_EVAL_LOC), p),
+                                sub(p, addmod(mload(W3_OMEGA_EVAL_LOC), mload(W4_OMEGA_EVAL_LOC), p)),
+                                p
+                            ),
+                            mload(QM_EVAL_LOC),
+                            p
+                        )
+                    let non_native_field_identity :=
+                        mulmod(
+                            addmod(addmod(non_native_field_gate_1, non_native_field_gate_2, p), non_native_field_gate_3, p),
+                            mload(Q2_EVAL_LOC),
+                            p
+                        )
+
+                    mstore(AUX_NON_NATIVE_FIELD_EVALUATION, non_native_field_identity)
+                }
+
+                {
+                    /**
+                     * limb_accumulator_1 = w_2_omega;
+                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_1 += w_1_omega;
+                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_1 += w_3;
+                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_1 += w_2;
+                     * limb_accumulator_1 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_1 += w_1;
+                     * limb_accumulator_1 -= w_4;
+                     * limb_accumulator_1 *= q_4;
+                     */
+                    let limb_accumulator_1 := mulmod(mload(W2_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p)
+                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_OMEGA_EVAL_LOC), p)
+                    limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
+                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p)
+                    limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
+                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p)
+                    limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p)
+                    limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p)
+                    limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p)
+                    limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p)
+
+                    /**
+                     * limb_accumulator_2 = w_3_omega;
+                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_2 += w_2_omega;
+                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_2 += w_1_omega;
+                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_2 += w_4;
+                     * limb_accumulator_2 *= SUBLIMB_SHIFT;
+                     * limb_accumulator_2 += w_3;
+                     * limb_accumulator_2 -= w_4_omega;
+                     * limb_accumulator_2 *= q_m;
+                     */
+                    let limb_accumulator_2 := mulmod(mload(W3_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p)
+                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_OMEGA_EVAL_LOC), p)
+                    limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
+                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_OMEGA_EVAL_LOC), p)
+                    limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
+                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p)
+                    limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p)
+                    limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p)
+                    limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p)
+                    limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p)
+
+                    mstore(
+                        AUX_LIMB_ACCUMULATOR_EVALUATION,
+                        mulmod(addmod(limb_accumulator_1, limb_accumulator_2, p), mload(Q3_EVAL_LOC), p)
+                    )
+                }
+
+                {
+                    /**
+                     * memory_record_check = w_3;
+                     * memory_record_check *= eta;
+                     * memory_record_check += w_2;
+                     * memory_record_check *= eta;
+                     * memory_record_check += w_1;
+                     * memory_record_check *= eta;
+                     * memory_record_check += q_c;
+                     *
+                     * partial_record_check = memory_record_check;
+                     *
+                     * memory_record_check -= w_4;
+                     */
+
+                    let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(C_ETA_LOC), p)
+                    memory_record_check := addmod(memory_record_check, mload(W2_EVAL_LOC), p)
+                    memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p)
+                    memory_record_check := addmod(memory_record_check, mload(W1_EVAL_LOC), p)
+                    memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p)
+                    memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p)
+
+                    let partial_record_check := memory_record_check
+                    memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p)
+
+                    mstore(AUX_MEMORY_EVALUATION, memory_record_check)
+
+                    // index_delta = w_1_omega - w_1
+                    let index_delta := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p)
+                    // record_delta = w_4_omega - w_4
+                    let record_delta := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p)
+                    // index_is_monotonically_increasing = index_delta * (index_delta - 1)
+                    let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, sub(p, 1), p), p)
+
+                    // adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta)
+                    let adjacent_values_match_if_adjacent_indices_match :=
+                        mulmod(record_delta, addmod(1, sub(p, index_delta), p), p)
+
+                    // AUX_ROM_CONSISTENCY_EVALUATION = ((adjacent_values_match_if_adjacent_indices_match * alpha) + index_is_monotonically_increasing) * alpha + partial_record_check
+                    mstore(
+                        AUX_ROM_CONSISTENCY_EVALUATION,
+                        addmod(
+                            mulmod(
+                                addmod(
+                                    mulmod(adjacent_values_match_if_adjacent_indices_match, mload(C_ALPHA_LOC), p),
+                                    index_is_monotonically_increasing,
+                                    p
+                                ),
+                                mload(C_ALPHA_LOC),
+                                p
+                            ),
+                            memory_record_check,
+                            p
+                        )
+                    )
+
+                    {
+                        /**
+                         * next_gate_access_type = w_3_omega;
+                         * next_gate_access_type *= eta;
+                         * next_gate_access_type += w_2_omega;
+                         * next_gate_access_type *= eta;
+                         * next_gate_access_type += w_1_omega;
+                         * next_gate_access_type *= eta;
+                         * next_gate_access_type = w_4_omega - next_gate_access_type;
+                         */
+                        let next_gate_access_type := mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p)
+                        next_gate_access_type := addmod(next_gate_access_type, mload(W2_OMEGA_EVAL_LOC), p)
+                        next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p)
+                        next_gate_access_type := addmod(next_gate_access_type, mload(W1_OMEGA_EVAL_LOC), p)
+                        next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p)
+                        next_gate_access_type := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, next_gate_access_type), p)
+
+                        // value_delta = w_3_omega - w_3
+                        let value_delta := addmod(mload(W3_OMEGA_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p)
+                        //  adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type);
+
+                        let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation :=
+                            mulmod(
+                                addmod(1, sub(p, index_delta), p),
+                                mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p),
+                                p
+                            )
+
+                        // AUX_RAM_CONSISTENCY_EVALUATION
+
+                        /**
+                         * access_type = w_4 - partial_record_check
+                         * access_check = access_type^2 - access_type
+                         * next_gate_access_type_is_boolean = next_gate_access_type^2 - next_gate_access_type
+                         * RAM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;
+                         * RAM_consistency_check_identity *= alpha;
+                         * RAM_consistency_check_identity += index_is_monotonically_increasing;
+                         * RAM_consistency_check_identity *= alpha;
+                         * RAM_consistency_check_identity += next_gate_access_type_is_boolean;
+                         * RAM_consistency_check_identity *= alpha;
+                         * RAM_consistency_check_identity += access_check;
+                         */
+
+                        let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p)
+                        let access_check := mulmod(access_type, addmod(access_type, sub(p, 1), p), p)
+                        let next_gate_access_type_is_boolean :=
+                            mulmod(next_gate_access_type, addmod(next_gate_access_type, sub(p, 1), p), p)
+                        let RAM_cci :=
+                            mulmod(
+                                adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation,
+                                mload(C_ALPHA_LOC),
+                                p
+                            )
+                        RAM_cci := addmod(RAM_cci, index_is_monotonically_increasing, p)
+                        RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p)
+                        RAM_cci := addmod(RAM_cci, next_gate_access_type_is_boolean, p)
+                        RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p)
+                        RAM_cci := addmod(RAM_cci, access_check, p)
+
+                        mstore(AUX_RAM_CONSISTENCY_EVALUATION, RAM_cci)
+                    }
+
+                    {
+                        // timestamp_delta = w_2_omega - w_2
+                        let timestamp_delta := addmod(mload(W2_OMEGA_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p)
+
+                        // RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3
+                        let RAM_timestamp_check_identity :=
+                            addmod(
+                                mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p), sub(p, mload(W3_EVAL_LOC)), p
+                            )
+
+                        /**
+                         * memory_identity = ROM_consistency_check_identity * q_2;
+                         * memory_identity += RAM_timestamp_check_identity * q_4;
+                         * memory_identity += memory_record_check * q_m;
+                         * memory_identity *= q_1;
+                         * memory_identity += (RAM_consistency_check_identity * q_arith);
+                         *
+                         * auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity;
+                         * auxiliary_identity *= q_aux;
+                         * auxiliary_identity *= alpha_base;
+                         */
+                        let memory_identity := mulmod(mload(AUX_ROM_CONSISTENCY_EVALUATION), mload(Q2_EVAL_LOC), p)
+                        memory_identity :=
+                            addmod(memory_identity, mulmod(RAM_timestamp_check_identity, mload(Q4_EVAL_LOC), p), p)
+                        memory_identity :=
+                            addmod(memory_identity, mulmod(mload(AUX_MEMORY_EVALUATION), mload(QM_EVAL_LOC), p), p)
+                        memory_identity := mulmod(memory_identity, mload(Q1_EVAL_LOC), p)
+                        memory_identity :=
+                            addmod(
+                                memory_identity, mulmod(mload(AUX_RAM_CONSISTENCY_EVALUATION), mload(QARITH_EVAL_LOC), p), p
+                            )
+
+                        let auxiliary_identity := addmod(memory_identity, mload(AUX_NON_NATIVE_FIELD_EVALUATION), p)
+                        auxiliary_identity := addmod(auxiliary_identity, mload(AUX_LIMB_ACCUMULATOR_EVALUATION), p)
+                        auxiliary_identity := mulmod(auxiliary_identity, mload(QAUX_EVAL_LOC), p)
+                        auxiliary_identity := mulmod(auxiliary_identity, mload(C_ALPHA_BASE_LOC), p)
+
+                        mstore(AUX_IDENTITY, auxiliary_identity)
+
+                        // update alpha
+                        mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p))
+                    }
+                }
+            }
+
+            {
+                /**
+                 * quotient = ARITHMETIC_IDENTITY
+                 * quotient += PERMUTATION_IDENTITY
+                 * quotient += PLOOKUP_IDENTITY
+                 * quotient += SORT_IDENTITY
+                 * quotient += ELLIPTIC_IDENTITY
+                 * quotient += AUX_IDENTITY
+                 * quotient *= ZERO_POLY_INVERSE
+                 */
+                mstore(
+                    QUOTIENT_EVAL_LOC,
+                    mulmod(
+                        addmod(
+                            addmod(
+                                addmod(
+                                    addmod(
+                                        addmod(mload(PERMUTATION_IDENTITY), mload(PLOOKUP_IDENTITY), p),
+                                        mload(ARITHMETIC_IDENTITY),
+                                        p
+                                    ),
+                                    mload(SORT_IDENTITY),
+                                    p
+                                ),
+                                mload(ELLIPTIC_IDENTITY),
+                                p
+                            ),
+                            mload(AUX_IDENTITY),
+                            p
+                        ),
+                        mload(ZERO_POLY_INVERSE_LOC),
+                        p
+                    )
+                )
+            }
+
+            /**
+             * GENERATE NU AND SEPARATOR CHALLENGES
+             */
+            {
+                let current_challenge := mload(C_CURRENT_LOC)
+                // get a calldata pointer that points to the start of the data we want to copy
+                let calldata_ptr := add(calldataload(0x04), 0x24)
+
+                calldata_ptr := add(calldata_ptr, NU_CALLDATA_SKIP_LENGTH)
+
+                mstore(NU_CHALLENGE_INPUT_LOC_A, current_challenge)
+                mstore(NU_CHALLENGE_INPUT_LOC_B, mload(QUOTIENT_EVAL_LOC))
+                calldatacopy(NU_CHALLENGE_INPUT_LOC_C, calldata_ptr, NU_INPUT_LENGTH)
+
+                // hash length = (0x20 + num field elements), we include the previous challenge in the hash
+                let challenge := keccak256(NU_CHALLENGE_INPUT_LOC_A, add(NU_INPUT_LENGTH, 0x40))
+
+                mstore(C_V0_LOC, mod(challenge, p))
+                // We need THIRTY-ONE independent nu challenges!
+                mstore(0x00, challenge)
+                mstore8(0x20, 0x01)
+                mstore(C_V1_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x02)
+                mstore(C_V2_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x03)
+                mstore(C_V3_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x04)
+                mstore(C_V4_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x05)
+                mstore(C_V5_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x06)
+                mstore(C_V6_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x07)
+                mstore(C_V7_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x08)
+                mstore(C_V8_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x09)
+                mstore(C_V9_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x0a)
+                mstore(C_V10_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x0b)
+                mstore(C_V11_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x0c)
+                mstore(C_V12_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x0d)
+                mstore(C_V13_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x0e)
+                mstore(C_V14_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x0f)
+                mstore(C_V15_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x10)
+                mstore(C_V16_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x11)
+                mstore(C_V17_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x12)
+                mstore(C_V18_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x13)
+                mstore(C_V19_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x14)
+                mstore(C_V20_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x15)
+                mstore(C_V21_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x16)
+                mstore(C_V22_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x17)
+                mstore(C_V23_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x18)
+                mstore(C_V24_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x19)
+                mstore(C_V25_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x1a)
+                mstore(C_V26_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x1b)
+                mstore(C_V27_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x1c)
+                mstore(C_V28_LOC, mod(keccak256(0x00, 0x21), p))
+                mstore8(0x20, 0x1d)
+                mstore(C_V29_LOC, mod(keccak256(0x00, 0x21), p))
+
+                // @follow-up - Why are both v29 and v30 using appending 0x1d to the prior challenge and hashing, should it not change?
+                mstore8(0x20, 0x1d)
+                challenge := keccak256(0x00, 0x21)
+                mstore(C_V30_LOC, mod(challenge, p))
+
+                // separator
+                mstore(0x00, challenge)
+                mstore(0x20, mload(PI_Z_Y_LOC))
+                mstore(0x40, mload(PI_Z_X_LOC))
+                mstore(0x60, mload(PI_Z_OMEGA_Y_LOC))
+                mstore(0x80, mload(PI_Z_OMEGA_X_LOC))
+
+                mstore(C_U_LOC, mod(keccak256(0x00, 0xa0), p))
+            }
+
+            let success := 0
+            // VALIDATE T1
+            {
+                let x := mload(T1_X_LOC)
+                let y := mload(T1_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))
+                mstore(ACCUMULATOR_X_LOC, x)
+                mstore(add(ACCUMULATOR_X_LOC, 0x20), y)
+            }
+            // VALIDATE T2
+            {
+                let x := mload(T2_X_LOC) // 0x1400
+                let y := mload(T2_Y_LOC) // 0x1420
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(ZETA_POW_N_LOC))
+            // accumulator_2 = [T2].zeta^n
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = [T1] + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE T3
+            {
+                let x := mload(T3_X_LOC)
+                let y := mload(T3_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p))
+            // accumulator_2 = [T3].zeta^{2n}
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE T4
+            {
+                let x := mload(T4_X_LOC)
+                let y := mload(T4_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p), mload(ZETA_POW_N_LOC), p))
+            // accumulator_2 = [T4].zeta^{3n}
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE W1
+            {
+                let x := mload(W1_X_LOC)
+                let y := mload(W1_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V0_LOC), p))
+            // accumulator_2 = v0.(u + 1).[W1]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE W2
+            {
+                let x := mload(W2_X_LOC)
+                let y := mload(W2_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V1_LOC), p))
+            // accumulator_2 = v1.(u + 1).[W2]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE W3
+            {
+                let x := mload(W3_X_LOC)
+                let y := mload(W3_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V2_LOC), p))
+            // accumulator_2 = v2.(u + 1).[W3]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE W4
+            {
+                let x := mload(W4_X_LOC)
+                let y := mload(W4_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V3_LOC), p))
+            // accumulator_2 = v3.(u + 1).[W4]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE S
+            {
+                let x := mload(S_X_LOC)
+                let y := mload(S_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V4_LOC), p))
+            // accumulator_2 = v4.(u + 1).[S]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE Z
+            {
+                let x := mload(Z_X_LOC)
+                let y := mload(Z_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V5_LOC), p))
+            // accumulator_2 = v5.(u + 1).[Z]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE Z_LOOKUP
+            {
+                let x := mload(Z_LOOKUP_X_LOC)
+                let y := mload(Z_LOOKUP_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V6_LOC), p))
+            // accumulator_2 = v6.(u + 1).[Z_LOOKUP]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE Q1
+            {
+                let x := mload(Q1_X_LOC)
+                let y := mload(Q1_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V7_LOC))
+            // accumulator_2 = v7.[Q1]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE Q2
+            {
+                let x := mload(Q2_X_LOC)
+                let y := mload(Q2_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V8_LOC))
+            // accumulator_2 = v8.[Q2]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE Q3
+            {
+                let x := mload(Q3_X_LOC)
+                let y := mload(Q3_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V9_LOC))
+            // accumulator_2 = v9.[Q3]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE Q4
+            {
+                let x := mload(Q4_X_LOC)
+                let y := mload(Q4_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V10_LOC))
+            // accumulator_2 = v10.[Q4]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE QM
+            {
+                let x := mload(QM_X_LOC)
+                let y := mload(QM_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V11_LOC))
+            // accumulator_2 = v11.[Q;]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE QC
+            {
+                let x := mload(QC_X_LOC)
+                let y := mload(QC_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V12_LOC))
+            // accumulator_2 = v12.[QC]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE QARITH
+            {
+                let x := mload(QARITH_X_LOC)
+                let y := mload(QARITH_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V13_LOC))
+            // accumulator_2 = v13.[QARITH]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE QSORT
+            {
+                let x := mload(QSORT_X_LOC)
+                let y := mload(QSORT_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V14_LOC))
+            // accumulator_2 = v14.[QSORT]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE QELLIPTIC
+            {
+                let x := mload(QELLIPTIC_X_LOC)
+                let y := mload(QELLIPTIC_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V15_LOC))
+            // accumulator_2 = v15.[QELLIPTIC]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE QAUX
+            {
+                let x := mload(QAUX_X_LOC)
+                let y := mload(QAUX_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V16_LOC))
+            // accumulator_2 = v15.[Q_AUX]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE SIGMA1
+            {
+                let x := mload(SIGMA1_X_LOC)
+                let y := mload(SIGMA1_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V17_LOC))
+            // accumulator_2 = v17.[sigma1]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE SIGMA2
+            {
+                let x := mload(SIGMA2_X_LOC)
+                let y := mload(SIGMA2_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V18_LOC))
+            // accumulator_2 = v18.[sigma2]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE SIGMA3
+            {
+                let x := mload(SIGMA3_X_LOC)
+                let y := mload(SIGMA3_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V19_LOC))
+            // accumulator_2 = v19.[sigma3]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE SIGMA4
+            {
+                let x := mload(SIGMA4_X_LOC)
+                let y := mload(SIGMA4_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V20_LOC))
+            // accumulator_2 = v20.[sigma4]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE TABLE1
+            {
+                let x := mload(TABLE1_X_LOC)
+                let y := mload(TABLE1_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V21_LOC), p))
+            // accumulator_2 = u.[table1]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE TABLE2
+            {
+                let x := mload(TABLE2_X_LOC)
+                let y := mload(TABLE2_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V22_LOC), p))
+            // accumulator_2 = u.[table2]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE TABLE3
+            {
+                let x := mload(TABLE3_X_LOC)
+                let y := mload(TABLE3_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V23_LOC), p))
+            // accumulator_2 = u.[table3]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE TABLE4
+            {
+                let x := mload(TABLE4_X_LOC)
+                let y := mload(TABLE4_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V24_LOC), p))
+            // accumulator_2 = u.[table4]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE TABLE_TYPE
+            {
+                let x := mload(TABLE_TYPE_X_LOC)
+                let y := mload(TABLE_TYPE_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V25_LOC))
+            // accumulator_2 = v25.[TableType]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE ID1
+            {
+                let x := mload(ID1_X_LOC)
+                let y := mload(ID1_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V26_LOC))
+            // accumulator_2 = v26.[ID1]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE ID2
+            {
+                let x := mload(ID2_X_LOC)
+                let y := mload(ID2_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V27_LOC))
+            // accumulator_2 = v27.[ID2]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE ID3
+            {
+                let x := mload(ID3_X_LOC)
+                let y := mload(ID3_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V28_LOC))
+            // accumulator_2 = v28.[ID3]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            // VALIDATE ID4
+            {
+                let x := mload(ID4_X_LOC)
+                let y := mload(ID4_Y_LOC)
+                let xx := mulmod(x, x, q)
+                // validate on curve
+                success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                mstore(0x00, x)
+                mstore(0x20, y)
+            }
+            mstore(0x40, mload(C_V29_LOC))
+            // accumulator_2 = v29.[ID4]
+            success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+            // accumulator = accumulator + accumulator_2
+            success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+            /**
+             * COMPUTE BATCH EVALUATION SCALAR MULTIPLIER
+             */
+            {
+                /**
+                 * batch_evaluation = v0 * (w_1_omega * u + w_1_eval)
+                 * batch_evaluation += v1 * (w_2_omega * u + w_2_eval)
+                 * batch_evaluation += v2 * (w_3_omega * u + w_3_eval)
+                 * batch_evaluation += v3 * (w_4_omega * u + w_4_eval)
+                 * batch_evaluation += v4 * (s_omega_eval * u + s_eval)
+                 * batch_evaluation += v5 * (z_omega_eval * u + z_eval)
+                 * batch_evaluation += v6 * (z_lookup_omega_eval * u + z_lookup_eval)
+                 */
+                let batch_evaluation :=
+                    mulmod(
+                        mload(C_V0_LOC),
+                        addmod(mulmod(mload(W1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W1_EVAL_LOC), p),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V1_LOC),
+                            addmod(mulmod(mload(W2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W2_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V2_LOC),
+                            addmod(mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W3_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V3_LOC),
+                            addmod(mulmod(mload(W4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W4_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V4_LOC),
+                            addmod(mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(S_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V5_LOC),
+                            addmod(mulmod(mload(Z_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V6_LOC),
+                            addmod(mulmod(mload(Z_LOOKUP_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_LOOKUP_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+
+                /**
+                 * batch_evaluation += v7 * Q1_EVAL
+                 * batch_evaluation += v8 * Q2_EVAL
+                 * batch_evaluation += v9 * Q3_EVAL
+                 * batch_evaluation += v10 * Q4_EVAL
+                 * batch_evaluation += v11 * QM_EVAL
+                 * batch_evaluation += v12 * QC_EVAL
+                 * batch_evaluation += v13 * QARITH_EVAL
+                 * batch_evaluation += v14 * QSORT_EVAL_LOC
+                 * batch_evaluation += v15 * QELLIPTIC_EVAL_LOC
+                 * batch_evaluation += v16 * QAUX_EVAL_LOC
+                 * batch_evaluation += v17 * SIGMA1_EVAL_LOC
+                 * batch_evaluation += v18 * SIGMA2_EVAL_LOC
+                 * batch_evaluation += v19 * SIGMA3_EVAL_LOC
+                 * batch_evaluation += v20 * SIGMA4_EVAL_LOC
+                 */
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V7_LOC), mload(Q1_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V8_LOC), mload(Q2_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V9_LOC), mload(Q3_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V10_LOC), mload(Q4_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V11_LOC), mload(QM_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V12_LOC), mload(QC_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V13_LOC), mload(QARITH_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V14_LOC), mload(QSORT_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V15_LOC), mload(QELLIPTIC_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V16_LOC), mload(QAUX_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V17_LOC), mload(SIGMA1_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V18_LOC), mload(SIGMA2_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V19_LOC), mload(SIGMA3_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V20_LOC), mload(SIGMA4_EVAL_LOC), p), p)
+
+                /**
+                 * batch_evaluation += v21 * (table1(zw) * u + table1(z))
+                 * batch_evaluation += v22 * (table2(zw) * u + table2(z))
+                 * batch_evaluation += v23 * (table3(zw) * u + table3(z))
+                 * batch_evaluation += v24 * (table4(zw) * u + table4(z))
+                 * batch_evaluation += v25 * table_type_eval
+                 * batch_evaluation += v26 * id1_eval
+                 * batch_evaluation += v27 * id2_eval
+                 * batch_evaluation += v28 * id3_eval
+                 * batch_evaluation += v29 * id4_eval
+                 * batch_evaluation += quotient_eval
+                 */
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V21_LOC),
+                            addmod(mulmod(mload(TABLE1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE1_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V22_LOC),
+                            addmod(mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE2_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V23_LOC),
+                            addmod(mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE3_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation :=
+                    addmod(
+                        batch_evaluation,
+                        mulmod(
+                            mload(C_V24_LOC),
+                            addmod(mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE4_EVAL_LOC), p),
+                            p
+                        ),
+                        p
+                    )
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V25_LOC), mload(TABLE_TYPE_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V26_LOC), mload(ID1_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V27_LOC), mload(ID2_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V28_LOC), mload(ID3_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V29_LOC), mload(ID4_EVAL_LOC), p), p)
+                batch_evaluation := addmod(batch_evaluation, mload(QUOTIENT_EVAL_LOC), p)
+
+                mstore(0x00, 0x01) // [1].x
+                mstore(0x20, 0x02) // [1].y
+                mstore(0x40, sub(p, batch_evaluation))
+                // accumulator_2 = -[1].(batch_evaluation)
+                success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+                // accumulator = accumulator + accumulator_2
+                success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+                mstore(OPENING_COMMITMENT_SUCCESS_FLAG, success)
+            }
+
+            /**
+             * PERFORM PAIRING PREAMBLE
+             */
+            {
+                let u := mload(C_U_LOC)
+                let zeta := mload(C_ZETA_LOC)
+                // VALIDATE PI_Z
+                {
+                    let x := mload(PI_Z_X_LOC)
+                    let y := mload(PI_Z_Y_LOC)
+                    let xx := mulmod(x, x, q)
+                    // validate on curve
+                    success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))
+                    mstore(0x00, x)
+                    mstore(0x20, y)
+                }
+                // compute zeta.[PI_Z] and add into accumulator
+                mstore(0x40, zeta)
+                success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+                // accumulator = accumulator + accumulator_2
+                success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40))
+
+                // VALIDATE PI_Z_OMEGA
+                {
+                    let x := mload(PI_Z_OMEGA_X_LOC)
+                    let y := mload(PI_Z_OMEGA_Y_LOC)
+                    let xx := mulmod(x, x, q)
+                    // validate on curve
+                    success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                    mstore(0x00, x)
+                    mstore(0x20, y)
+                }
+                mstore(0x40, mulmod(mulmod(u, zeta, p), mload(OMEGA_LOC), p))
+                // accumulator_2 = u.zeta.omega.[PI_Z_OMEGA]
+                success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40))
+                // PAIRING_RHS = accumulator + accumulator_2
+                success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, PAIRING_RHS_X_LOC, 0x40))
+
+                mstore(0x00, mload(PI_Z_X_LOC))
+                mstore(0x20, mload(PI_Z_Y_LOC))
+                mstore(0x40, mload(PI_Z_OMEGA_X_LOC))
+                mstore(0x60, mload(PI_Z_OMEGA_Y_LOC))
+                mstore(0x80, u)
+                success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40))
+                // PAIRING_LHS = [PI_Z] + [PI_Z_OMEGA] * u
+                success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40))
+                // negate lhs y-coordinate
+                mstore(PAIRING_LHS_Y_LOC, sub(q, mload(PAIRING_LHS_Y_LOC)))
+
+                if mload(CONTAINS_RECURSIVE_PROOF_LOC) {
+                    // VALIDATE RECURSIVE P1
+                    {
+                        let x := mload(RECURSIVE_P1_X_LOC)
+                        let y := mload(RECURSIVE_P1_Y_LOC)
+                        let xx := mulmod(x, x, q)
+                        // validate on curve
+                        success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                        mstore(0x00, x)
+                        mstore(0x20, y)
+                    }
+
+                    // compute u.u.[recursive_p1] and write into 0x60
+                    mstore(0x40, mulmod(u, u, p))
+                    success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x60, 0x40))
+                    // VALIDATE RECURSIVE P2
+                    {
+                        let x := mload(RECURSIVE_P2_X_LOC)
+                        let y := mload(RECURSIVE_P2_Y_LOC)
+                        let xx := mulmod(x, x, q)
+                        // validate on curve
+                        success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)))
+                        mstore(0x00, x)
+                        mstore(0x20, y)
+                    }
+                    // compute u.u.[recursive_p2] and write into 0x00
+                    // 0x40 still contains u*u
+                    success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x00, 0x40))
+
+                    // compute u.u.[recursiveP1] + rhs and write into rhs
+                    mstore(0xa0, mload(PAIRING_RHS_X_LOC))
+                    mstore(0xc0, mload(PAIRING_RHS_Y_LOC))
+                    success := and(success, staticcall(gas(), 6, 0x60, 0x80, PAIRING_RHS_X_LOC, 0x40))
+
+                    // compute u.u.[recursiveP2] + lhs and write into lhs
+                    mstore(0x40, mload(PAIRING_LHS_X_LOC))
+                    mstore(0x60, mload(PAIRING_LHS_Y_LOC))
+                    success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40))
+                }
+
+                if iszero(success) {
+                    mstore(0x0, EC_SCALAR_MUL_FAILURE_SELECTOR)
+                    revert(0x00, 0x04)
+                }
+                mstore(PAIRING_PREAMBLE_SUCCESS_FLAG, success)
+            }
+
+            /**
+             * PERFORM PAIRING
+             */
+            {
+                // rhs paired with [1]_2
+                // lhs paired with [x]_2
+
+                mstore(0x00, mload(PAIRING_RHS_X_LOC))
+                mstore(0x20, mload(PAIRING_RHS_Y_LOC))
+                mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) // this is [1]_2
+                mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed)
+                mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)
+                mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)
+
+                mstore(0xc0, mload(PAIRING_LHS_X_LOC))
+                mstore(0xe0, mload(PAIRING_LHS_Y_LOC))
+                mstore(0x100, mload(G2X_X0_LOC))
+                mstore(0x120, mload(G2X_X1_LOC))
+                mstore(0x140, mload(G2X_Y0_LOC))
+                mstore(0x160, mload(G2X_Y1_LOC))
+
+                success := staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20)
+                mstore(PAIRING_SUCCESS_FLAG, success)
+                mstore(RESULT_FLAG, mload(0x00))
+            }
+            if iszero(
+                and(
+                    and(and(mload(PAIRING_SUCCESS_FLAG), mload(RESULT_FLAG)), mload(PAIRING_PREAMBLE_SUCCESS_FLAG)),
+                    mload(OPENING_COMMITMENT_SUCCESS_FLAG)
+                )
+            ) {
+                mstore(0x0, PROOF_FAILURE_SELECTOR)
+                revert(0x00, 0x04)
+            }
+            {
+                mstore(0x00, 0x01)
+                return(0x00, 0x20) // Proof succeeded!
+            }
+        }
+    }
+}
+
+contract UltraVerifier is BaseUltraVerifier {
+    function getVerificationKeyHash() public pure override(BaseUltraVerifier) returns (bytes32) {
+        return UltraVerificationKey.verificationKeyHash();
+    }
+
+    function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BaseUltraVerifier) {
+        UltraVerificationKey.loadVerificationKey(vk, _omegaInverseLoc);
+    }
+}
diff --git a/crates/acvm_backend_barretenberg/src/lib.rs b/crates/acvm_backend_barretenberg/src/lib.rs
new file mode 100644
index 00000000000..46867a6aeb4
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/lib.rs
@@ -0,0 +1,40 @@
+#![warn(unused_crate_dependencies, unused_extern_crates)]
+#![warn(unreachable_pub)]
+
+// `acvm-backend-barretenberg` can either interact with the Barretenberg backend through a static library
+// or through an embedded wasm binary. It does not make sense to include both of these backends at the same time.
+// We then throw a compilation error if both flags are set.
+#[cfg(all(feature = "native", feature = "wasm"))]
+compile_error!("feature \"native\" and feature \"wasm\" cannot be enabled at the same time");
+
+#[cfg(all(feature = "native", target_arch = "wasm32"))]
+compile_error!("feature \"native\" cannot be enabled for a \"wasm32\" target");
+
+#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
+compile_error!("feature \"wasm\" cannot be enabled for a \"wasm32\" target");
+
+mod bb;
+mod proof_system;
+mod smart_contract;
+
+/// The number of bytes necessary to store a `FieldElement`.
+const FIELD_BYTES: usize = 32;
+
+#[derive(Debug, Default)]
+pub struct Barretenberg;
+
+impl Barretenberg {
+    pub fn new() -> Barretenberg {
+        Barretenberg
+    }
+}
+
+impl acvm::Backend for Barretenberg {}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct BackendError(#[from] Error);
+
+#[allow(clippy::upper_case_acronyms)]
+#[derive(Debug, thiserror::Error)]
+enum Error {}
diff --git a/crates/acvm_backend_barretenberg/src/proof_system.rs b/crates/acvm_backend_barretenberg/src/proof_system.rs
new file mode 100644
index 00000000000..33ec8457a43
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/proof_system.rs
@@ -0,0 +1,249 @@
+use std::fs::File;
+use std::io::{Read, Write};
+use std::path::Path;
+
+use acvm::acir::circuit::Opcode;
+use acvm::acir::{circuit::Circuit, native_types::WitnessMap, BlackBoxFunc};
+use acvm::FieldElement;
+use acvm::{Language, ProofSystemCompiler};
+use tempfile::tempdir;
+
+use crate::bb::{GatesCommand, ProveCommand, VerifyCommand, WriteVkCommand};
+use crate::{BackendError, Barretenberg, FIELD_BYTES};
+
+impl ProofSystemCompiler for Barretenberg {
+    type Error = BackendError;
+
+    fn np_language(&self) -> Language {
+        Language::PLONKCSat { width: 3 }
+    }
+
+    fn get_exact_circuit_size(&self, circuit: &Circuit) -> Result<u32, Self::Error> {
+        let temp_directory = tempdir().expect("could not create a temporary directory");
+        let temp_directory = temp_directory.path();
+        let temp_dir_path_str = temp_directory.to_str().unwrap();
+
+        // Create a temporary file for the circuit
+        //
+        let circuit_path = temp_directory.join("circuit").with_extension("bytecode");
+        let serialized_circuit = serialize_circuit(circuit);
+        write_to_file(serialized_circuit.as_bytes(), &circuit_path);
+
+        let number_of_gates_needed = GatesCommand {
+            path_to_crs: temp_dir_path_str.to_string(),
+            path_to_bytecode: circuit_path.as_os_str().to_str().unwrap().to_string(),
+        }
+        .run();
+
+        Ok(number_of_gates_needed)
+    }
+
+    fn supports_opcode(&self, opcode: &Opcode) -> bool {
+        match opcode {
+            Opcode::Arithmetic(_) => true,
+            Opcode::Directive(_) => true,
+            Opcode::Brillig(_) => true,
+            Opcode::MemoryInit { .. } => true,
+            Opcode::MemoryOp { .. } => true,
+            Opcode::BlackBoxFuncCall(func) => match func.get_black_box_func() {
+                BlackBoxFunc::AND
+                | BlackBoxFunc::XOR
+                | BlackBoxFunc::RANGE
+                | BlackBoxFunc::SHA256
+                | BlackBoxFunc::Blake2s
+                | BlackBoxFunc::Keccak256
+                | BlackBoxFunc::SchnorrVerify
+                | BlackBoxFunc::Pedersen
+                | BlackBoxFunc::HashToField128Security
+                | BlackBoxFunc::EcdsaSecp256k1
+                | BlackBoxFunc::EcdsaSecp256r1
+                | BlackBoxFunc::FixedBaseScalarMul
+                | BlackBoxFunc::RecursiveAggregation => true,
+            },
+        }
+    }
+
+    fn prove_with_pk(
+        &self,
+        _common_reference_string: &[u8],
+        circuit: &Circuit,
+        witness_values: WitnessMap,
+        _proving_key: &[u8],
+        is_recursive: bool,
+    ) -> Result<Vec<u8>, Self::Error> {
+        let temp_directory = tempdir().expect("could not create a temporary directory");
+        let temp_directory = temp_directory.path();
+        let temp_dir_path_str = temp_directory.to_str().unwrap();
+
+        // Create a temporary file for the witness
+        let serialized_witnesses: Vec<u8> = witness_values
+            .try_into()
+            .expect("could not serialize witness map");
+        let witness_path = temp_directory.join("witness").with_extension("tr");
+        write_to_file(&serialized_witnesses, &witness_path);
+
+        // Create a temporary file for the circuit
+        //
+        let circuit_path = temp_directory.join("circuit").with_extension("bytecode");
+        let serialized_circuit = serialize_circuit(circuit);
+        write_to_file(serialized_circuit.as_bytes(), &circuit_path);
+
+        let proof_path = temp_directory.join("proof").with_extension("proof");
+
+        // Create proof and store it in the specified path
+        ProveCommand {
+            verbose: true,
+            path_to_crs: temp_dir_path_str.to_string(),
+            is_recursive,
+            path_to_bytecode: circuit_path.as_os_str().to_str().unwrap().to_string(),
+            path_to_witness: witness_path.as_os_str().to_str().unwrap().to_string(),
+            path_to_proof: proof_path.as_os_str().to_str().unwrap().to_string(),
+        }
+        .run()
+        .expect("prove command failed");
+
+        let proof_with_public_inputs =
+            read_bytes_from_file(proof_path.as_os_str().to_str().unwrap()).unwrap();
+
+        // Barretenberg return the proof prepended with the public inputs.
+        //
+        // This is not how the API expects the proof to be formatted,
+        // so we remove the public inputs from the proof.
+        //
+        // TODO: As noted in the verification procedure, this is an abstraction leak
+        // TODO: and will need modifications to barretenberg
+        let proof =
+            remove_public_inputs(circuit.public_inputs().0.len(), &proof_with_public_inputs);
+        Ok(proof)
+    }
+
+    fn verify_with_vk(
+        &self,
+        _common_reference_string: &[u8],
+        proof: &[u8],
+        public_inputs: WitnessMap,
+        circuit: &Circuit,
+        _verification_key: &[u8],
+        is_recursive: bool,
+    ) -> Result<bool, Self::Error> {
+        let temp_directory = tempdir().expect("could not create a temporary directory");
+        let temp_directory = temp_directory.path();
+        let temp_dir_path = temp_directory.to_str().unwrap();
+
+        // Unlike when proving, we omit any unassigned witnesses.
+        // Witness values should be ordered by their index but we skip over any indices without an assignment.
+        let flattened_public_inputs: Vec<FieldElement> =
+            public_inputs.into_iter().map(|(_, el)| el).collect();
+
+        // Barretenberg expects the proof to be prepended with the public inputs.
+        //
+        // TODO: This is an abstraction leak and barretenberg's API should accept the public inputs
+        // TODO: separately and then prepend them internally
+        let proof_with_public_inputs =
+            prepend_public_inputs(proof.to_vec(), flattened_public_inputs.to_vec());
+
+        // Create a temporary file for the proof
+        let proof_path = temp_directory.join("proof").with_extension("proof");
+        write_to_file(&proof_with_public_inputs, &proof_path);
+
+        // Create a temporary file for the circuit
+        let circuit_path = temp_directory.join("circuit").with_extension("bytecode");
+        let serialized_circuit = serialize_circuit(circuit);
+        write_to_file(serialized_circuit.as_bytes(), &circuit_path);
+
+        // Create the verification key and write it to the specified path
+        let vk_path = temp_directory.join("vk");
+        WriteVkCommand {
+            verbose: false,
+            path_to_crs: temp_dir_path.to_string(),
+            is_recursive,
+            path_to_bytecode: circuit_path.as_os_str().to_str().unwrap().to_string(),
+            path_to_vk_output: vk_path.as_os_str().to_str().unwrap().to_string(),
+        }
+        .run()
+        .expect("write vk command failed");
+
+        // Verify the proof
+        Ok(VerifyCommand {
+            verbose: false,
+            path_to_crs: temp_dir_path.to_string(),
+            is_recursive,
+            path_to_proof: proof_path.as_os_str().to_str().unwrap().to_string(),
+            path_to_vk: vk_path.as_os_str().to_str().unwrap().to_string(),
+        }
+        .run())
+    }
+
+    fn proof_as_fields(
+        &self,
+        _proof: &[u8],
+        _public_inputs: WitnessMap,
+    ) -> Result<Vec<FieldElement>, Self::Error> {
+        panic!("vk_as_fields not supported in this backend");
+    }
+
+    fn vk_as_fields(
+        &self,
+        _common_reference_string: &[u8],
+        _verification_key: &[u8],
+    ) -> Result<(Vec<FieldElement>, FieldElement), Self::Error> {
+        panic!("vk_as_fields not supported in this backend");
+    }
+}
+
+pub(super) fn write_to_file(bytes: &[u8], path: &Path) -> String {
+    let display = path.display();
+
+    let mut file = match File::create(path) {
+        Err(why) => panic!("couldn't create {display}: {why}"),
+        Ok(file) => file,
+    };
+
+    match file.write_all(bytes) {
+        Err(why) => panic!("couldn't write to {display}: {why}"),
+        Ok(_) => display.to_string(),
+    }
+}
+
+pub(super) fn read_bytes_from_file(path: &str) -> std::io::Result<Vec<u8>> {
+    // Open the file for reading.
+    let mut file = File::open(path)?;
+
+    // Create a buffer to store the bytes.
+    let mut buffer = Vec::new();
+
+    // Read bytes from the file.
+    file.read_to_end(&mut buffer)?;
+
+    Ok(buffer)
+}
+
+/// Removes the public inputs which are prepended to a proof by Barretenberg.
+fn remove_public_inputs(num_pub_inputs: usize, proof: &[u8]) -> Vec<u8> {
+    // Barretenberg prepends the public inputs onto the proof so we need to remove
+    // the first `num_pub_inputs` field elements.
+    let num_bytes_to_remove = num_pub_inputs * FIELD_BYTES;
+    proof[num_bytes_to_remove..].to_vec()
+}
+
+/// Prepends a set of public inputs to a proof.
+fn prepend_public_inputs(proof: Vec<u8>, public_inputs: Vec<FieldElement>) -> Vec<u8> {
+    if public_inputs.is_empty() {
+        return proof;
+    }
+
+    let public_inputs_bytes = public_inputs
+        .into_iter()
+        .flat_map(|assignment| assignment.to_be_bytes());
+
+    public_inputs_bytes.chain(proof.into_iter()).collect()
+}
+
+// TODO: See nargo/src/artifacts/mod.rs
+// TODO: This method should live in ACVM and be the default method for serializing/deserializing circuits
+pub(super) fn serialize_circuit(circuit: &Circuit) -> String {
+    use base64::Engine;
+    let mut circuit_bytes: Vec<u8> = Vec::new();
+    circuit.write(&mut circuit_bytes).unwrap();
+    base64::engine::general_purpose::STANDARD.encode(circuit_bytes)
+}
diff --git a/crates/acvm_backend_barretenberg/src/smart_contract.rs b/crates/acvm_backend_barretenberg/src/smart_contract.rs
new file mode 100644
index 00000000000..b1a028f1e30
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/src/smart_contract.rs
@@ -0,0 +1,108 @@
+use super::proof_system::{serialize_circuit, write_to_file};
+use crate::{
+    bb::{ContractCommand, WriteVkCommand},
+    proof_system::read_bytes_from_file,
+    BackendError, Barretenberg,
+};
+use acvm::{acir::circuit::Circuit, SmartContract};
+use tempfile::tempdir;
+
+/// Embed the Solidity verifier file
+const ULTRA_VERIFIER_CONTRACT: &str = include_str!("contract.sol");
+
+impl SmartContract for Barretenberg {
+    type Error = BackendError;
+
+    fn eth_contract_from_vk(
+        &self,
+        _common_reference_string: &[u8],
+        circuit: &Circuit,
+        _verification_key: &[u8],
+    ) -> Result<String, Self::Error> {
+        let temp_directory = tempdir().expect("could not create a temporary directory");
+        let temp_directory_path = temp_directory.path();
+        let temp_dir_path = temp_directory_path.to_str().unwrap();
+
+        // Create a temporary file for the circuit
+        let circuit_path = temp_directory_path
+            .join("circuit")
+            .with_extension("bytecode");
+        let serialized_circuit = serialize_circuit(circuit);
+        write_to_file(serialized_circuit.as_bytes(), &circuit_path);
+
+        // Create the verification key and write it to the specified path
+        let vk_path = temp_directory_path.join("vk").to_str().unwrap().to_string();
+        WriteVkCommand {
+            verbose: false,
+            path_to_crs: temp_dir_path.to_string(),
+            is_recursive: false,
+            path_to_bytecode: circuit_path.as_os_str().to_str().unwrap().to_string(),
+            path_to_vk_output: vk_path.clone(),
+        }
+        .run()
+        .expect("write vk command failed");
+
+        let path_to_contract = temp_directory_path
+            .join("contract")
+            .to_str()
+            .unwrap()
+            .to_string();
+        ContractCommand {
+            verbose: false,
+            path_to_crs: temp_dir_path.to_string(),
+            path_to_vk: vk_path,
+            path_to_contract: path_to_contract.clone(),
+        }
+        .run()
+        .expect("contract command failed");
+
+        let verification_key_library_bytes = read_bytes_from_file(&path_to_contract).unwrap();
+        let verification_key_library = String::from_utf8(verification_key_library_bytes).unwrap();
+
+        drop(temp_directory);
+        Ok(format!(
+            "{verification_key_library}{ULTRA_VERIFIER_CONTRACT}"
+        ))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::collections::BTreeSet;
+
+    use acvm::{
+        acir::{
+            circuit::{Circuit, Opcode, PublicInputs},
+            native_types::{Expression, Witness},
+        },
+        SmartContract,
+    };
+
+    #[test]
+    fn test_smart_contract() {
+        use crate::Barretenberg;
+
+        let expression = &(Witness(1) + Witness(2)) - &Expression::from(Witness(3));
+        let constraint = Opcode::Arithmetic(expression);
+
+        let circuit = Circuit {
+            current_witness_index: 4,
+            opcodes: vec![constraint],
+            private_parameters: BTreeSet::from([Witness(1), Witness(2)]),
+            public_parameters: PublicInputs::default(),
+            return_values: PublicInputs::default(),
+        };
+
+        let bb = Barretenberg;
+
+        let common_reference_string = Vec::new();
+        let verification_key = Vec::new();
+        let contract = bb
+            .eth_contract_from_vk(&common_reference_string, &circuit, &verification_key)
+            .unwrap();
+
+        assert!(contract.contains("contract BaseUltraVerifier"));
+        assert!(contract.contains("contract UltraVerifier"));
+        assert!(contract.contains("library UltraVerificationKey"));
+    }
+}
diff --git a/crates/nargo_cli/Cargo.toml b/crates/nargo_cli/Cargo.toml
index a7a0ae5786c..da3eac653c5 100644
--- a/crates/nargo_cli/Cargo.toml
+++ b/crates/nargo_cli/Cargo.toml
@@ -49,7 +49,7 @@ tokio = { version = "1.0", features = ["io-std"] }
 tokio-util = { version = "0.7.8", features = ["compat"] }
 
 # Backends
-acvm-backend-barretenberg = "0.12.0"
+acvm-backend-barretenberg = { path = "../acvm_backend_barretenberg" }
 
 [dev-dependencies]
 tempdir = "0.3.7"

From 48663bfb83b86f107a8c8da3fe421731a7791d28 Mon Sep 17 00:00:00 2001
From: Tom French <tom@tomfren.ch>
Date: Thu, 31 Aug 2023 14:20:17 +0100
Subject: [PATCH 2/3] chore: run tests which interact with the backend in
 serial

---
 Cargo.lock                                    | 38 +++++++++++++++++++
 crates/acvm_backend_barretenberg/Cargo.toml   |  3 ++
 .../src/bb/contract.rs                        |  1 +
 .../acvm_backend_barretenberg/src/bb/gates.rs |  1 +
 .../acvm_backend_barretenberg/src/bb/mod.rs   | 18 +++------
 .../acvm_backend_barretenberg/src/bb/prove.rs |  1 +
 .../src/bb/prove_and_verify.rs                |  7 +---
 .../src/bb/verify.rs                          |  1 +
 .../src/bb/write_vk.rs                        |  1 +
 .../src/smart_contract.rs                     | 20 +++-------
 10 files changed, 60 insertions(+), 31 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 02d8a2edcc8..b3e4caf8fea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -57,6 +57,7 @@ dependencies = [
  "dirs",
  "flate2",
  "reqwest",
+ "serial_test",
  "tar",
  "tempfile",
  "thiserror",
@@ -1448,6 +1449,7 @@ checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
 dependencies = [
  "futures-channel",
  "futures-core",
+ "futures-executor",
  "futures-io",
  "futures-sink",
  "futures-task",
@@ -1470,6 +1472,17 @@ version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
 
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
 [[package]]
 name = "futures-io"
 version = "0.3.28"
@@ -3338,6 +3351,31 @@ dependencies = [
  "syn 2.0.26",
 ]
 
+[[package]]
+name = "serial_test"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d"
+dependencies = [
+ "dashmap",
+ "futures",
+ "lazy_static",
+ "log",
+ "parking_lot",
+ "serial_test_derive",
+]
+
+[[package]]
+name = "serial_test_derive"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.26",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.7"
diff --git a/crates/acvm_backend_barretenberg/Cargo.toml b/crates/acvm_backend_barretenberg/Cargo.toml
index 302138bfe41..55d71b54ba3 100644
--- a/crates/acvm_backend_barretenberg/Cargo.toml
+++ b/crates/acvm_backend_barretenberg/Cargo.toml
@@ -26,5 +26,8 @@ reqwest = { version = "0.11.16", default-features = false, features = [
     "blocking",
 ] }
 
+[dev-dependencies]
+serial_test = "2.0.0"
+
 [build-dependencies]
 build-target = "0.4.0"
diff --git a/crates/acvm_backend_barretenberg/src/bb/contract.rs b/crates/acvm_backend_barretenberg/src/bb/contract.rs
index 07131861aa6..d84a8daa44a 100644
--- a/crates/acvm_backend_barretenberg/src/bb/contract.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/contract.rs
@@ -42,6 +42,7 @@ impl ContractCommand {
 }
 
 #[test]
+#[serial_test::serial]
 fn contract_command() {
     use tempfile::tempdir;
 
diff --git a/crates/acvm_backend_barretenberg/src/bb/gates.rs b/crates/acvm_backend_barretenberg/src/bb/gates.rs
index c9db70343fb..38cfb9b135d 100644
--- a/crates/acvm_backend_barretenberg/src/bb/gates.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/gates.rs
@@ -51,6 +51,7 @@ impl GatesCommand {
 }
 
 #[test]
+#[serial_test::serial]
 fn gate_command() {
     use tempfile::tempdir;
 
diff --git a/crates/acvm_backend_barretenberg/src/bb/mod.rs b/crates/acvm_backend_barretenberg/src/bb/mod.rs
index 373e021a6c1..3f80c538bf5 100644
--- a/crates/acvm_backend_barretenberg/src/bb/mod.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/mod.rs
@@ -27,12 +27,8 @@ const TAG: &str = formatcp!("barretenberg-v{}", VERSION);
 const DEST_FOLDER: &str = ".nargo/backends/acvm-backend-barretenberg";
 const BINARY_NAME: &str = "backend_binary";
 
-const API_URL: &str = formatcp!(
-    "https://github.com/{}/{}/releases/download/{}",
-    USERNAME,
-    REPO,
-    TAG
-);
+const API_URL: &str =
+    formatcp!("https://github.com/{}/{}/releases/download/{}", USERNAME, REPO, TAG);
 
 fn get_bb_download_url() -> String {
     if let Ok(path) = std::env::var("BB_BINARY_URL") {
@@ -59,9 +55,7 @@ fn get_bb_download_url() -> String {
 fn get_binary_path() -> PathBuf {
     match std::env::var("BB_BINARY_PATH") {
         Ok(path) => PathBuf::from(path),
-        Err(_) => dirs::home_dir()
-            .unwrap()
-            .join(formatcp!("{}/{}", DEST_FOLDER, BINARY_NAME)),
+        Err(_) => dirs::home_dir().unwrap().join(formatcp!("{}/{}", DEST_FOLDER, BINARY_NAME)),
     }
 }
 
@@ -109,14 +103,14 @@ fn download_binary_from_url(url: &str) -> Result<Cursor<Vec<u8>>, String> {
 }
 
 #[test]
+#[serial_test::serial]
 fn no_command_provided_works() {
     // This is a simple test to check that the binaries work
 
     assert_binary_exists();
 
-    let output = std::process::Command::new(get_binary_path())
-        .output()
-        .expect("Failed to execute command");
+    let output =
+        std::process::Command::new(get_binary_path()).output().expect("Failed to execute command");
 
     let stderr = String::from_utf8_lossy(&output.stderr);
     assert_eq!(stderr, "No command provided.\n");
diff --git a/crates/acvm_backend_barretenberg/src/bb/prove.rs b/crates/acvm_backend_barretenberg/src/bb/prove.rs
index 3c3dbd1f5a2..481748e291a 100644
--- a/crates/acvm_backend_barretenberg/src/bb/prove.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/prove.rs
@@ -50,6 +50,7 @@ impl ProveCommand {
 }
 
 #[test]
+#[serial_test::serial]
 fn prove_command() {
     use tempfile::tempdir;
 
diff --git a/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs b/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs
index f877909f588..08fe0f10ea9 100644
--- a/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/prove_and_verify.rs
@@ -35,15 +35,12 @@ impl ProveAndVerifyCommand {
             command.arg("-r");
         }
 
-        command
-            .output()
-            .expect("Failed to execute command")
-            .status
-            .success()
+        command.output().expect("Failed to execute command").status.success()
     }
 }
 
 #[test]
+#[serial_test::serial]
 fn prove_and_verify_command() {
     use tempfile::tempdir;
 
diff --git a/crates/acvm_backend_barretenberg/src/bb/verify.rs b/crates/acvm_backend_barretenberg/src/bb/verify.rs
index 5ac40f810f4..f7352fa232f 100644
--- a/crates/acvm_backend_barretenberg/src/bb/verify.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/verify.rs
@@ -37,6 +37,7 @@ impl VerifyCommand {
 }
 
 #[test]
+#[serial_test::serial]
 fn verify_command() {
     use tempfile::tempdir;
 
diff --git a/crates/acvm_backend_barretenberg/src/bb/write_vk.rs b/crates/acvm_backend_barretenberg/src/bb/write_vk.rs
index e330d58ba1e..67706632185 100644
--- a/crates/acvm_backend_barretenberg/src/bb/write_vk.rs
+++ b/crates/acvm_backend_barretenberg/src/bb/write_vk.rs
@@ -42,6 +42,7 @@ impl WriteVkCommand {
 }
 
 #[test]
+#[serial_test::serial]
 fn write_vk_command() {
     use tempfile::tempdir;
 
diff --git a/crates/acvm_backend_barretenberg/src/smart_contract.rs b/crates/acvm_backend_barretenberg/src/smart_contract.rs
index b1a028f1e30..167a00e9cd2 100644
--- a/crates/acvm_backend_barretenberg/src/smart_contract.rs
+++ b/crates/acvm_backend_barretenberg/src/smart_contract.rs
@@ -24,9 +24,7 @@ impl SmartContract for Barretenberg {
         let temp_dir_path = temp_directory_path.to_str().unwrap();
 
         // Create a temporary file for the circuit
-        let circuit_path = temp_directory_path
-            .join("circuit")
-            .with_extension("bytecode");
+        let circuit_path = temp_directory_path.join("circuit").with_extension("bytecode");
         let serialized_circuit = serialize_circuit(circuit);
         write_to_file(serialized_circuit.as_bytes(), &circuit_path);
 
@@ -42,11 +40,7 @@ impl SmartContract for Barretenberg {
         .run()
         .expect("write vk command failed");
 
-        let path_to_contract = temp_directory_path
-            .join("contract")
-            .to_str()
-            .unwrap()
-            .to_string();
+        let path_to_contract = temp_directory_path.join("contract").to_str().unwrap().to_string();
         ContractCommand {
             verbose: false,
             path_to_crs: temp_dir_path.to_string(),
@@ -60,9 +54,7 @@ impl SmartContract for Barretenberg {
         let verification_key_library = String::from_utf8(verification_key_library_bytes).unwrap();
 
         drop(temp_directory);
-        Ok(format!(
-            "{verification_key_library}{ULTRA_VERIFIER_CONTRACT}"
-        ))
+        Ok(format!("{verification_key_library}{ULTRA_VERIFIER_CONTRACT}"))
     }
 }
 
@@ -79,6 +71,7 @@ mod tests {
     };
 
     #[test]
+    #[serial_test::serial]
     fn test_smart_contract() {
         use crate::Barretenberg;
 
@@ -97,9 +90,8 @@ mod tests {
 
         let common_reference_string = Vec::new();
         let verification_key = Vec::new();
-        let contract = bb
-            .eth_contract_from_vk(&common_reference_string, &circuit, &verification_key)
-            .unwrap();
+        let contract =
+            bb.eth_contract_from_vk(&common_reference_string, &circuit, &verification_key).unwrap();
 
         assert!(contract.contains("contract BaseUltraVerifier"));
         assert!(contract.contains("contract UltraVerifier"));

From b299e53737e36773c9b9102835e8ad6f80e8f431 Mon Sep 17 00:00:00 2001
From: Tom French <tom@tomfren.ch>
Date: Thu, 31 Aug 2023 14:35:03 +0100
Subject: [PATCH 3/3] chore: commit the witness file for
 acvm-backend-barretenberg tests

---
 crates/acvm_backend_barretenberg/.gitignore     |   1 +
 crates/acvm_backend_barretenberg/src/witness.tr | Bin 0 -> 114 bytes
 2 files changed, 1 insertion(+)
 create mode 100644 crates/acvm_backend_barretenberg/.gitignore
 create mode 100644 crates/acvm_backend_barretenberg/src/witness.tr

diff --git a/crates/acvm_backend_barretenberg/.gitignore b/crates/acvm_backend_barretenberg/.gitignore
new file mode 100644
index 00000000000..106a4f552a0
--- /dev/null
+++ b/crates/acvm_backend_barretenberg/.gitignore
@@ -0,0 +1 @@
+!src/witness.tr
diff --git a/crates/acvm_backend_barretenberg/src/witness.tr b/crates/acvm_backend_barretenberg/src/witness.tr
new file mode 100644
index 0000000000000000000000000000000000000000..e01c75d888ce2976c0dd635f2e1bbb759ee25b29
GIT binary patch
literal 114
zcmV-&0FD12iwFP!00002|E-eA34kyV0KIQTf>_5c>LWi}5&Q2U2}uW;g^+y>13RXO
zQ~LL&UGnCtIM!VQEVZA8zu~;3<ye0Qdi5B~$^d$e5bN=moItM`>}vtNR*<!EEZxEG
U4cO`d_H}|yI9L6gJ75I>0Ky0}dH?_b

literal 0
HcmV?d00001