// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; pragma experimental ABIEncoderV2; // import "./IElasticERC20.sol"; /* Offshift PureL1Rollup State Contract Technical Preview Testnet */ struct BatchPublicInputs { bytes32[16] tx_in; bytes32 amount_public_in; bytes32 amount_public_out; bytes32[16] commitment_out; bytes32 recipient; bytes32 oracle; bytes32 old_root; bytes32 new_root; bytes32[16] nullifier_hashes; } interface IVerifier { function verify( bytes calldata, bytes32[] calldata ) external view returns (bool); } contract TechnicalPreview { // IElasticIERC20 public token; IVerifier public verifier; bytes32[] public utxo; bytes32[] public validRoots; // Valid mapping(bytes32 => bool) public nullifierHashes; // Spent UTXO hashes mapping(bytes32 => bool) public commitments; // Individual commitments bytes32 public merkleRoot = 0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720; constructor(IVerifier _verifier) { // , address _token) { verifier = _verifier; // token = IElasticERC20(_token); } function publish( bytes calldata _proof, BatchPublicInputs calldata _batch ) public payable { require(uint256(_batch.old_root) == uint256(merkleRoot), "invalid root"); require(verifier.verify(_proof, flattenBatchPublicInputs(_batch)), "invalid proof"); for (uint256 i = 0; i < _batch.nullifier_hashes.length; i++) { if (_batch.nullifier_hashes[i] == 0) continue; assert(!nullifierHashes[_batch.nullifier_hashes[i]]); nullifierHashes[_batch.nullifier_hashes[i]] = true; } // Publish UTXO set for (uint256 i = 0; i < _batch.commitment_out.length; i++) { utxo.push(_batch.commitment_out[i]); } // token.mint(_batch.recipient, _batch.amount_public_out); // Process withdraw require(msg.value == uint256(_batch.amount_public_in), "Incorrect input amount"); validRoots.push(_batch.new_root); // Add to valid roots merkleRoot = _batch.new_root; // Update the root (bool sent, ) = address(uint160(uint256(_batch.recipient))).call{value: uint256(_batch.amount_public_out)}(""); require(sent, "unbalanced reserves or gas griefed"); } function verifyProof( bytes calldata _proof, bytes32[] memory _publicInputs ) public view returns (bool) { return verifier.verify(_proof, _publicInputs); } function flattenBatchPublicInputs( BatchPublicInputs memory input ) public pure returns (bytes32[] memory) { bytes32[] memory flatArray = new bytes32[](54); uint256 idx = 0; for (uint i = 0; i < 16; i++) { flatArray[idx++] = input.tx_in[i]; } flatArray[idx++] = input.amount_public_in; flatArray[idx++] = input.amount_public_out; for (uint i = 0; i < 16; i++) { flatArray[idx++] = input.commitment_out[i]; } flatArray[idx++] = input.recipient; flatArray[idx++] = input.oracle; flatArray[idx++] = input.old_root; flatArray[idx++] = input.new_root; for (uint i = 0; i < 16; i++) { flatArray[idx++] = input.nullifier_hashes[i]; } return flatArray; } }