# Native State Channels on Aztec Protocol ## Table of Contents 1. [Code] 2. [What is a State Channel?](#What-is-a-State-Channel?) 3. [The Aztec Stack](#The-Aztec-Stack) 3a. [Kernal Circuits and App Circuits](#Kernel-Circuits-and-App-Circuits) 3b. [Private eXecution Environment (PXE)](#Private-eXecution-Environment-PXE) 3c. [Notes](#Notes) 4. [Aztec State Channels](#Aztec-State-Channels) 4a. [Hijacking Transaction Proving](#Hijacking-Transaction-Proving) 4b. [The "Orchestrator"](#The-Orchestrator) 4c. [State Channel Compatible PXE](#State-Channel-Compatible-PXE) 4d. [Client-side Use](Client-side-Use) 4e, [Upgrades to Aztecs Proof System](#Upgrades-to-Aztecs-Proof-System) 5. [Edge Cases in Aztec State Channels](#Edge-Cases-in-Aztec-State-Channels) 5a. [Timeouts](#Timeouts) 5b. [Timeout Fraud](#Timeout-Fraud) 5c. ["Double-Spend" Fraud](#Double-Spend-Fraud) 6. [Why Aztec?](#Why-Aztec) 7. [Usecases](#Usecases) 7a. [Gaming](#Gaming) 7b. [Voting](#Voting) 7c. [Enterprise Resource Management](#Enterprise-Resource-Management) 7d. [Asynchronous Oracles](#Asynchronous-Oracles) ## Project ### Video Walkthrough (Embed youtube video of web app here) ### Code You can find the [Aztec State Channels Tic Tac Toe demo here](https://github.com/mach-34/aztec-state-channels). Additionally, you can find the [fork of Aztec's PXE here](https://github.com/mach-34/aztec-packages). Finally, you can find the [web app for the state channel demo here](https://github.com/mach-34/tic-tac-aztec-vite-app) and the [server here](https://github.com/mach-34/tic-tac-aztec-server). To install the state channel repo: ```console # Install the Aztec Packages repo (this may take a while) git clone https://github.com/mach-34/aztec-packages cd aztec-packages ./bootstrap.sh cd .. # Install the Aztec State Channels tic tac toe repository git clone https://github.com/mach-34/aztec-state-channels cd aztec-state-channels npm i # Run the aztec sandbox and one PXE npm run sandbox # Run a second PXE for the counterparty in a separate terminal window npm run pxe:secondary # Everything is now set up to run the web app or run tests npm run test tests/statechannel.test.ts ``` To run the web app: ```console # Do this in the same folder that aztec-packages and aztec-state-channels are located in mkdir aztec-channel-app && cd aztec-channel-app # Clone the server app git clone https://github.com/mach-34/tic-tac-aztec-server cd tic-tac-aztec-server npm i # Start a database used for minimal coordination in a separate terminal window docker compose up # Start the server in a separate terminal wind npm run server cd .. # Clone the web app git clone https://github.com/mach-34/tic-tac-aztec-vite-app npm i # Run the web app npm run dev ``` ## What is a State Channel? ### Traditional State Channels Smart contracts augment digital interactions by reducing the need for trust between parties. They enable secure and automatic execution of agreements based on pre-defined rules. However, blockchain computations come with a high cost, as every transaction must be executed and verified by each node in the network. This process ensures the transaction's correctness but incurs significant latency and requires transaction fees as compensation for the network's computational efforts. Even with advancements like zero-knowledge proofs, which allow for the verification of a transaction's correctness without revealing its details, the network still bears the burden of validating these proofs. Not all applications require the full validation power of the blockchain for every minor state change. State channels offer a solution by allowing a group of participants to conduct transactions and update states off-chain, where only the final state is recorded on the blockchain. This method drastically cuts down on transaction fees with much fewer transactions are executed on the blockchain itself. Additionally, interstitial interactions between channel participants are incredibly quick, incurring only latency from peer-to-peer communication. ### ZK State Channels Zero-knowledge proofs bring a notable enhancement to the functionality of state channels. In typical state channels, the integrity of finalized state relies on the consensus of the participants within the channel. However, this model assumes that all parties will not collude to bypass agreed-upon rules. Integrating zero-knowledge proofs allows the blockchain to confirm the accuracy of every interaction within the channel without needing to see the specifics of any state transition. This attribute is particularly valuable in complex ecosystems involving numerous subsets of participants engaging through various state channels. Relying solely on the consensus of channel participants might not be sufficient to ensure the integrity of the entire system when participants can collude to act against the protocol's interest. Zero-knowledge proofs mitigate this risk by providing a way to verify the correctness of each step within a channel in a privacy-preserving manner, thus ensuring the whole system can trust the outcome of channel interactions without compromising the privacy of individual transactions. ### Drawbacks to State Channels State channels are not without faults. The cheap and expeditious use of state channels is dependent on the honesty and liveliness of its participants. Channel participants may be unresponsive, and dApps implementing state channels must explicitly define recourse for handling liveliness faliures. This presents a weak point for abuse. Firstly, a malicious actor may engage in "timeout griefing", where the participant repeatedly triggers otherwise honest timeouts at every chance they get. This will force honest participants to respond onchain, destroying the efficiency gained by using state channels. Depending on the dApp, this may be mitigated by setting a threshold for timeouts that prevents overuse. In long-lived channels, alternative means for handling liveliness failures may be employed. Channel participants might agree to designate adjudicators who are responsible for attempting to make contact with channel participants out of band, or perhaps including a ZK Email proof that notification of liveliness failure was delivered before starting a timeout. Moreover, timeouts can be used fraudulently- a malicious channel participant could roll back the state of their channel to a "stale" state and trigger a timeout at that point. A dApp using state channels must define swift and decisive retribution for fraudulent behavior. Misbehaviour in single games can be remediated by providing honest parties the possibility of instantly terminating the channel onchain with a favorable outcome. In repeated games, sybil-resistant reputational/ credit systems can flag dishonest actors as undesirable counterparties, and a progressive slashing of financial stake scale punishment for repeat offenders. Additionally, channels come in two types: "finite" and "infinite". Finite state channels present conditions for termination of the channel; when met, a channel can be safely settled onchain without an optimistic waiting period. However, infinite channels have no set end condition. Simple payment channels where value is passed back and forth between two parties can go on indefinitely until one party chooses to withdraw their funds. A malicious party could send funds in the channel, then post a "stale" state onchain withdrawing their funds. This can only be remedied with an optimistic waiting period where the honest party can post the latest state proving the fraudulent channel closure. Firstly, this introduces a scenario where either an honest channel settlement must wait for the end of the challenge window, or all channel participants must be live to submit an onchain transaction asserting the channel closure is valid. Additionally, it burdens channel participants with constantly watching their chain to make sure a fraudulent channel closure wasn't posted. Fortunately, in our view, the most interesting applications of state channels, like gaming, voting, and business logic, are all finite. ## The Aztec Stack ### Kernel Circuits and App Circuits Aztec Protocol is a privacy-preserving L2 rollup for smart contracts. Each function in a smart contract is actually a distinct zero knowledge circuit, referred to as an "App Circuit". Invoking functions within these contracts might seem similar to traditional programming languages, but the underlying process is significantly more intricate.To facilitate a kind of virtual machine with state and call stacks, Aztec stitches together app circuits with "Kernel Circuits", as shown below: - ![Aztec3 Kernel Circuit Diagram](https://hackmd.io/_uploads/ByagRn-tp.png) Each kernel circuit verifies the previous kernel circuit and an inner app circuit, ultimately chaining together complex function call graphs while governing state across the execution. Kernel circuits aggregate state inclusion proofs, events to emit, and public function calls to enque for execution by the Aztec proving network. A transaction is essentially a finished kernel proof, supplying a proof of correct execution that publicly discloses the commitments to state changes. Aztec's state is tracked by a [set of trees](https://docs.aztec.network/learn/concepts/storage/trees/main). Contracts, nullifiers, and private state are all committed into respective trees, and constrained access to these trees facilitates private but verifiable general purpose computation. Trees are updated by a sequencer using an "Ordering Circuit" which packs many transactions (kernel proofs) together, verifying that each transaction correctly accesses and updates the respective state trees. ### Notes Notes are the atomic unit of state within the Aztec Protocol, acting as the building blocks for storing information in a way that smart contracts dictate. For example, take the Tic Tac Toe State Channel; a [`BoardNote`](https://github.com/Mach-34/aztec-state-channels/blob/main/contracts/tic_tac_toe/src/types/board.nr) is implemented to track players and board state, along with auxiliary information to drive logic. These notes are encrypted and stored across Aztec nodes, accessible only to those who have the decryption keys. Notes are also the means to private interaction on Aztec. Let's say we want to privately transfer tokens from one account to another using a [`ValueNote`](https://github.com/AztecProtocol/aztec-packages/blob/9ffe45719d7eee35c0241a5906e9d7c1ef7a162f/noir-projects/aztec-nr/value-note/src/value_note.nr#L4). Initially, account A holds a value note with 100 tokens. When account A decides to send those tokens to account B, a nullifier for the original note is inserted in the [nullifier tree](https://docs.aztec.network/learn/concepts/storage/trees/main#nullifier-tree) and the commitment to a new note for account B with 100 tokens is inserted into the [note hash tree](https://docs.aztec.network/learn/concepts/storage/trees/main#note-hash-tree). Additionally, account A generally ["broadcasts"](https://github.com/AztecProtocol/aztec-packages/blob/9ffe45719d7eee35c0241a5906e9d7c1ef7a162f/noir-projects/aztec-nr/value-note/src/value_note.nr#L47) this note, meaning send the encrypted note to the Aztec network for Account B to retrieve and decrypt. Private state is facilitated through a UTXO model, meaning that notes are nullified, not destroyed. If a spent note were removed, information about this state would leak. Instead, the nullifier on account A's spent note prevents it from being used again. If account A were to instead send 75 tokens, the note with 100 tokens would be destroyed, and two notes - one for account A with 25 tokens, and one for account B with 75 tokens - would be created with the same process. ### Private eXecution Environment (PXE) Aztec Protocol relies on a crucial tool called the "Private Execution Environment" (PXE), a local service that manages private interactions for a user. The PXE keeps synchronization with the connected Aztec node, fetching and trying to decrypt notes to identify which ones belong to its user before storing them for later use. The PXE also computes ZK proofs of Kernel and App circuits that comprise transactions. Despite essentially existing as a server in the sandbox, the PXE is not a remote third-party service. On the mainnet, the PXE can live in the browser, ensuring private notes and circuit execution remain in control of the client. A key feature the PXE are "oracles". Oracles are a way for systems that exist in a vacuum to access data from outside their frame of reference. On a blockchain like Ethereum, this might mean accessing data from a website that has no connection to the ledger. Zero knowledge circuits are even more isolated, and oracles provide the prover with private chain state it otherwise is unaware of. For instance, when executing a function like `transfer(from, to, value)`, the only direct inputs are the addresses and the amount. But what about the private key needed for authorization or accessing the specific notes being spent? Requiring such details as function inputs would incur a high cost to the DevEx of Noir contracting. Oracles abstract this complexity from Aztec dApp development. Need to use a note? The circuit can make the oracle call [`get_notes`](https://github.com/AztecProtocol/aztec-packages/blob/40202813cf1c5ba473dcb23e34c7dffc94187a1f/noir-projects/aztec-nr/aztec/src/oracle/notes.nr#L30) and the PXE will [fetch the relevant notes](https://github.com/AztecProtocol/aztec-packages/blob/40202813cf1c5ba473dcb23e34c7dffc94187a1f/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts#L143). Need to authorize the use of that note with a private key? A circuit can request (`get_nullifier_key_pair`)[https://github.com/AztecProtocol/aztec-packages/blob/40202813cf1c5ba473dcb23e34c7dffc94187a1f/noir-projects/aztec-nr/aztec/src/oracle/nullifier_key.nr#L10] with only a public address. Programming in zero knowledge is a shift in paradigm, and oracle calls to the PXE realign DevEx with what smart contract developers are expecting. The PXE offers an inconspicuous oracle call [`pop_capsule`](https://github.com/AztecProtocol/aztec-packages/blob/40202813cf1c5ba473dcb23e34c7dffc94187a1f/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/capsule.nr#L5) which is critical to the state channel architecture. While notes and keys are univerally reused primatives, capsules are a flexible interface for developer-defined oracle calls. Capsules are essentially data packets [directly stored in the PXE](https://github.com/AztecProtocol/aztec-packages/blob/40202813cf1c5ba473dcb23e34c7dffc94187a1f/yarn-project/aztec.js/src/wallet/base_wallet.ts#L65) by clients. Preloaded capsules can then be consumed by a Noir contract needing auxiliary state that couldn't be supplied as inputs to the entrypoint function. ## Aztec State Channels ### Hijacking Transaction Proving The recursive nature of Kernel Circuits means that we can do potentially infinite sequential computations inside of the Aztec VM. In the 1st party [distribution of the PXE](https://github.com/AztecProtocol/aztec-packages) shipped by Aztec, transactions are constructed by a single caller and the entire call stack of app circuits expanding from the entrypoint will be computed under the hood. However, in [Mach 34's fork of the PXE](https://github.com/Mach-34/aztec-packages), we provide additional endpoints for users to compute a single app circuit. [Originally proposed by Aztec's Jan Beneš](https://forum.aztec.network/t/abusing-private-kernel-to-create-state-channel-scheme/2550), Aztec State Channels takes advantage of the fact that we can prove different iterations Currently, the Aztec sandbox only "simulates" (computes the witness) of a given app circuit. Inputs and outputs of app circuits, as well as commitments to notes, are checked by the simulated Kernel Circuits. However, the finished version of the Aztec stack will return a proof of the proper execution of the app circuit which the Kernel circuit will verify as shown in the diagram above. ### The "Orchestrator" ![call_graph](https://hackmd.io/_uploads/SyuKZQEkC.png) Each app circuit verified by a kernel circuit can spawn a [maximum of four](https://github.com/AztecProtocol/aztec-packages/blob/9ffe45719d7eee35c0241a5906e9d7c1ef7a162f/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr#L28) child calls. Kernel Circuits have a [max call stack depth](https://github.com/AztecProtocol/aztec-packages/blob/9ffe45719d7eee35c0241a5906e9d7c1ef7a162f/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr#L47) that will quickly be encountered in a state channel if calls are not properly organized. If the orchestrator were to be organized in reverse order, such that the first orchestrator called (open_channel, turn, turn, orchestrator), all of the subsequent calls from the child orchestrator call would also pile up in the callstack. By ordering the orchestrator as the first call in the stack, calls do not pile up. (TODO: I don't actually understand why still) The orchestrator ### Why Capsules? Verification of Aztec kernel circuits presumes that the entire call graph has been simulated previously. While this makes sense in a normal transaction, we can't predict the length of our state channel. Furthermore, it would be impossible to pass information like rows and columns as function inputs as, at turn N, we have no clue what the inputs for turn N + 1 will be as our opponent would be providing this information. This is where the previously mentioned [`pop_capsule`](https://github.com/AztecProtocol/aztec-packages/blob/40202813cf1c5ba473dcb23e34c7dffc94187a1f/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/capsule.nr#L5) oracle call becomes useful. ### State Channel Compatible PXE When simulating/ proving a transaction, the PXE will start at the "entrypoint". Technically, the first function call is always to an account contract, which authorizes the execution of subsequent calls in the target contract. As shown in [the orchestrator](#The-Orchestrator) call graph, a state channel first hits an `orchestrator()` call, which dives deeper into orchestrator calls until the `turn_index` parameter informs the orchestrator to start calling logic functions. During normal execution, simulation of a parent function call is halted when it reaches a `call_private_function` oracle call. This informs the simulator to execute the child function and return the result before continuing on. However, we are not executing the logic in the order the simulator expects. We are executing `open_channel()` and sequential `turn()` calls manually. This has multiple considerations. Firstly, we need to track the results of each execution. When simulation of a function call occurs, it produces an [`ExecutionResult`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/circuit-types/src/execution_result.ts#L32). `ExecutionResult` objects are meant to be transient inside of the PXE while a full transaction is built, and thus contains more data than we need and includes a witness which would destroy the privacy if passed to counterparties. For this reason, we create an [`AppExecutionResult`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/circuit-types/src/app_execution_result.ts#L9) object which maintains only the necessary information for subsequent calls. The client-side [State Channel driver](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/base.ts#L34-L36) will maintain an ordered record of `AppExecutionResults` for the eventual construction of the single onchain transaction for the entire channel. Keep in mind that currently, the Aztec sandbox only simulates witnesses - it does not actually prove transactions. In future versions of Aztec, something like a `ProvenExecutionResult` will exist. This will be even more discreet for our usecase, as it only contains the ZK proof of the app circuit rather than auxiliary witness data. To produce an `AppExecutionResult`, we add [`simulateAppCircuit`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/circuit-types/src/interfaces/pxe.ts#L277-L285) to the PXE interface. This eventually hits an added simulator function [`runNested`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/acir-simulator/src/client/simulator.ts#L84-L92). `simulateNested` is quite similar to the vanilla [`run`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/acir-simulator/src/client/simulator.ts#L153) simulator, but it gives us granular control over the context of the simulation. One aspect of `simulateAppCircuit` is the ability to provide it with notes from previous executions. Adding notes directly to the PXE currently is dependent on transaction hashes, and while future versions of Aztec State Channels will likely add such an interface for interstitial notes, currently we can just supply them when simulating a specific function circuit. When a `getNotes` oracle call is made, the PXE will search both the PXE and the execution note cache for notes, and find the requisite note in the cache. By piping in our shared `BoardNote` for each incremental transaction, the PXE is able to simulate each step with advancing state despite not having any record onchain. Tic Tac Toe doesn't really deal with state that is private between the counterparties, and thus we essentially pipe the `BoardNote` from the previous `AppCircuitExecution` into the next one. However, `ExecutionResult` objects track a plaintext ExecutionNote cache separately from the commitments and nullifiers. Therefore, should a state channel depend on private notes as well as a shared note, we can simply not provide the private notes to the opponent, and they would have no issue computing an `AppExecutionResult` or the final transaction since they never employ said note. Equally important is the `cachedSimulations` parameter in `runNested`. As mentioned before, a function call with child function calls will halt simulation of the parent until the child has been simulated, just like in a traditional call stack. In order to stitch together our `AppExecutionResults` with the orchestrator, we need to intervene in this behavior by [providing the simulator a cache of existing `ExecutionResults`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/acir-simulator/src/client/client_execution_context.ts#L323-L346). When a child function is simulated, the simulator context provides information about the function being called. We can check the expected function call against that of the enqueued `AppExecutionResult` converted back to an `ExecutionResult`, and if it matches, instantly provide the result back for the parent function instead of running that function. Once we've constructed our `open_channel` and `turn()` calls and ordered them correctly with the `orchestrator()` via `simulateAppCircuit`, we're ready to build our final transaction. Like with cached simulations, a normal transaction call will attempt to simulate the entire call graph. Thus, we add another function to the PXE interface: [`proveSimulatedAppCircuits`](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/pxe/src/pxe_service/pxe_service.ts#L782). Traditional execution of a transaction will begin by [prompting the simulator at the entrypoint](https://github.com/Mach-34/aztec-packages/blob/125e05b6f9ed99f662e61c53a88f303f71f25379/yarn-project/pxe/src/pxe_service/pxe_service.ts#L625) to produce an `ExecutionResult`. We've already constructed the entire call graph up to the entrypoint of the first `orchestrator` calls, and `proveSimulatedAppCircuit` will supply this `ExecutionResult` instead. Now, just like normal, the PXE will execute the recursive kernel circuits verifying the entirety of our state channel and provide the full transaction to post onchain. ### Client-side Use The TicTacToe state channel is driven by a "State Channel" class. This is split into two types: [`BaseStateChannel`](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/base.ts#L32) and [`ContinuedStateChannel`](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/continued.ts#L38). In a polished version, these would inherit from a common abstract class, but this was not done due to time constraints. The `BaseChannel` is used at the start of a channel, and in a game where all parties are honest and maintain liveliness is the only instance used. The steps are as follows: 1. A host would create a new open game (purely in app). 2. A guest would join the game by signing an ["open channel message"](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L96-L100). 3. The host calls [`BaseStateChannel.openChannel()`](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L103), passing in the guest signature. This function creates the "open channel message" for the host and uses `PXE.simulateAppCircuit()` to execute the `open_channel()` contract function and storing it in the channel class. 4. The host provides the `AppExecutionResult` of `open_channel` to the guest, who calls [`BaseStateChannel.insertOpenChannel()`](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/base.ts#L339) to add the channel open to their channel class as well. 5. The host decides on a move and [signs that move](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L649-L650) before sending the move and signature to the guest. The guest should not respond without receiving a valid move signature since they can use it to prove malfeasance later on. 6. The guest [signs the hosts move](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L114) and transmits the signature back to the host 7. The host uses the move and opponent signature in [BaseStateChannel.turn()](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L117), which simulates the app circuit through the PXE and returns the `AppExecutionResult` for a `turn()` call. 8. The host transmits the `AppExecutionResult` for the turn back to the guest, who calls [`BaseStateChannel.insertTurn()`](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/base.ts#L330) to add the turn to their channel class as well. 9. Steps 5-8 are repeated, alternating with host and guest, until an end condition is reached. It does not matter whether the host or guest reaches the end condition; either can finalize the state channel onchain. 10. The winning player calls [`BaseStateChannel.finalize()`](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/base.ts#L303). This: a. Calls `BaseStateChannel.orchestrate()`](https://github.com/Mach-34/aztec-state-channels/blob/main/src/channel/base.ts#L215) to simulate the `AppExecutionResult` for each `orchestrator()` call, using the `open_channel()` and `turn()` results as cached simulations b. Supplies the entrypoint `orchestrator()` `AppExecutionResult` to `PXE.proveSimulatedAppCircuits` to get the transaction object to post onchain c. Posts the transaction onchain, waiting until finalization before returning the transaction hash to the client. A timeout can be triggered in either channel type inside of `BaseStateChannel.move()`, whether an opponent signature [is received](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L527) or [not received](https://github.com/Mach-34/aztec-state-channels/blob/main/tests/statechannel.test.ts#L411) by setting an optional third parameter to be true. An opponent signature is provided in the case where a live party makes a state channel transition and the counterparty fails to respond on their turn, while no signature is provided when the live party submits a move for opponent signature and does not receive a response. The timeout flag is passed through in the capsule and triggers [`set_timeout()`](https://github.com/Mach-34/aztec-state-channels/blob/main/contracts/tic_tac_toe/src/main.nr#L526) in the contract. From here, the live party must call [`answer_timeout()`](https://github.com/Mach-34/aztec-state-channels/blob/main/contracts/tic_tac_toe/src/main.nr#L69) directly onchain, and if they fail to do so within the timeout period the live party can then call [`claim_timeout_win()`](https://github.com/Mach-34/aztec-state-channels/blob/main/contracts/tic_tac_toe/src/main.nr#L215). If the timeout is answered, parties repeat the above process of steps 5-10 in the `ContinuedStateChannel`. This allows the rest of the game to be played inside of a state channel. ### Upgrades to Aztec's Proof System Aztec will eventually begin using [Protogalaxy](https://eprint.iacr.org/2023/1106) folding to improve the efficiency of proving many iterations of the Aztec kernel. This means that an accumulated witness will have to be shared from each app circuit execution. We have two solutions to this conundrum: 1. [Obfuscating folded witnesses](https://hackmd.io/@IQZ-5dJ4QGGu4K6oX71X7w/BJOZKIm9p) with one or more "chaff steps": Given access to iteration N-2 and N, it should not be difficult for an adversary to extract the witness for N-1. However, Each state transition could be performed in tandem with a "chaff step" which mixes in meaningless random data to the witness. Now, given witnesses for iteration N-3 and N, where N-2 contains confidential information, it should be infeasabile to extract the witness for N-2. This obfuscation becomes stronger the more chaff steps are folded in per logic step. While we believe this strategy will ultimately solve any issues arising from the integration of folding, it is unproven. 2. Folding together circuit verifications: If chaff-step obfuscation proves unacceptable, we could fold together many instances of a circuit that verifies another circuit. This is less desirable as it will incur a higher proving cost and will likey introduce other issues with total VM compatibility. We would have to specially construct a circuit that lines up with the expected inputs and outputs of the Aztec.nr kernel circuit, and verify that circuit inside an app circuit. ## Benefits of Kernel Circuit State Channels ## Edge Cases in Aztec State Channels ### Timeouts In rational cases, state channels never reach the chain until an end condition is reached. However, it is possible that a counterparty may fail to respond. Perhaps they negligently disconnect from the session, or they maliciously choose not to respond to a state increment that will result in an unfavorable outcome. For this reason, a channel party must be able to commit the progress in the channel to the chain at any given point. Griefing ### "Double-Spend" Fraud For many state channel applications, a party may submit a state increment that they later deem undesirable. For instance, in a game of chess on some turn, a player may miss a checkmate or blunder a piece. In this case, they may feel compelled to try to try to revise history by sending you a different state increment for the same state height. Each `AppExecutionResult` is built with notes and commitments from the previous increment, so it would be impossible to simply order two of these results together inside an orchestrator without complex verification logic to handle this. A simple solution is to take input data that is public to channel participants and have all parties sign it. In the case of Tic Tac Toe, this means both parties would sign the row, column, game id, and turn index for every turn. These signatures are kept by the client until the channel is finalized. Now, when a player tries to "double spend" a turn, their counterparty has signatures over two different inputs for a state increment at the same state height. Using [`claim_fraud_win`](https://github.com/Mach-34/aztec-state-channels/blob/main/contracts/tic_tac_toe/src/main.nr#L140) the aggreived party validates the signatures by their opponent, proving that they maliciously submitted two different turns. Assuming that the state channel for this game has not yet been finalized, the game is instantly terminated with the fraud prover being set as the winner. By providing quick, unilateral, and verifiable recourse to misbehavior, it is expected that channel participants would be highly disincentivized from acting fraudulently. ### Timeout Fraud Timeout fraud represents a craftier evolution of the simple "Double Spend" scheme. In the latter, a malicious actor's communication was limited to the peer-to-peer exchange with their counterparty. The twist in timeout fraud lies in the aggressor's decision to revert to a historical state, recompute the `AppExecutionResult` with an alternate input, and precipitously initiate a timeout at a past state height. Without providing recourse, this maneuver compels an honest party to employ the forged state when answer the timeout on the blockchain. When encountering "Double Spend" fraud, the victim had access to signatures over different states at the same increment. With timeout fraud, the perpetrator can withhold their duplicate signature from the opponent by executing an app circuit that initiates the timeout. This obviates a record of inputs for state increments in a note. For simple applications like Tic Tac Toe, this is done with minimal overhead by serializing all actions into a single field element. More complex applications may require hashing inputs which is then incorporated into hash chain. By invoking [`dispute_timeout`](https://github.com/Mach-34/aztec-state-channels/blob/main/contracts/tic_tac_toe/src/main.nr#L236), the honest party can present the original signature associated with the disputed state. Following the validation of the signature being from the malicious client over the row, column, game ID, and turn index, these inputs are compared to the corresponding move stored in the note. Once a discrepancy has been proven, immediate closure of the channel occurs with the defrauded party declared the winner. ## Why Aztec? ### What State Channels can do for Aztec Aztec is a unique contender in the world of L2 Rollups. While options like Mina exist for privacy-preserving general smart contracting, Aztec is the only protocol with native briding between the L2 and L1 Ethereum. However, while transactions will be far less expensive than an L1 block, they will never be as inexpensive as scalability-focused L2 networks. Drawing from metrics on the [0.31.0 release of Aztec-Packages](https://github.com/AztecProtocol/aztec-packages/pull/5340#issuecomment-2010196008), a block containing 64 transactions would cost ~585,000 gas. Taking an L1 gas price of 30 gwei and an Ether price of $4,000, we can construct the following equation to estimate the L1 cost of a transaction: $$ \dfrac{585,000\ gas}{1\ tx_{L1}} · \dfrac{1\ tx_{L1}}{64\ tx_{L1}} · \dfrac{30\ gwei}{1 gas} · \dfrac{1 ether}{1,000,000,000\ gwei} · \dfrac{\$4,000}{1\ ether} = \dfrac{\$1.096}{1\ tx_{L2}} $$ This is a rough estimation of the cost of an Aztec transaction and does not include the L2 fee. Furthermore, it is expected that the L1 fees increase. At an ether price of $16,000 during congestion, say 200 gwei, the cost for such a transaction could rise to $30. Therefore, it is important to consider how certain applications built on Aztec could utilize state channel architecture to minimize fees. ### What Aztec can do for State Channels ## Usecases ### Gaming As shown with a simple Tic Tac Toe game in the original [aztec-state-channels](https://github.com/mach-34/aztec-state-channels) repository, turn-based games work well with state channel architecture. By harnessing the Aztec VM, we can even facilitate games with hidden state like Poker all in a single transaction. ### Voting Voting: DAO's on Aztec may find themselves voting on a proposal with hundreds of participants. Take a voting scheme where voters are private but votes are public- instead of using a snapshot, a proposal could be triggered, voted on by all participants using [historical inclusion proofs](https://github.com/AztecProtocol/aztec-packages/blob/aztec-package-v0.31.0/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr), and decided publicly onchain in a single transaction. ### Enterprise Resource Management ERP systems have been tested onchain for years, and have suffered from a lack of privacy and scalability. Aztec natively supports the privacy, but with state channels, we can cut the cost of posting to Aztec considerably. A flow like: ``` 1. Purchase order 2. Confirmation of purchase order + invoice issuance 3. Seller releases order to transporter 4. Trasporter releases order to buyer 5. Buyer pays invoice ``` Can be handled with a single transaction. Additionally, ZK enables privacy-preserving third party adjudication of these steps by providing insight into what conditions were or were not met at any stage of this flow. ### Asynchronous Oracles Oracle calls in a dApp can require two transactions - one from the dApp paying an oracle provider and prompting them to retrieve some offchain information, and one from the oracle responding to the request. By structuring this call as a sort of state channel, we can: - Cut the cost of the interaction in half - "Escrow" the payment from the dApp to the oracle to only pay if the channel is finalized by a certain time/ block