TechnicalPreview.sol 3.36 KB
Newer Older
John Doe's avatar
John Doe committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
// 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;
    }
}