TechnicalPreview.sol 6.62 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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./Interfaces/IElasticERC20.sol";
    
struct Transaction {
    bytes32 id;
    
    bytes32[16] commitments; 
    bytes32[16] nullifier_hashes; 
    bytes32[16] recipients; 
    bytes32[16] withdrawals; 
    bytes32 deposit; 
    bytes32[161] proof;
    bytes32[16] aggregation_object;
}

struct BatchPublicInputs {
    bytes32 key_hash;
    bytes32 oracle;
    bytes32 old_root;
    bytes32 new_root;
    Transaction[] transactions; 
}
interface IVerifier {
    function verify(
        bytes calldata,
        bytes32[] calldata
    ) external view returns (bool);
    function getVerificationKeyHash() external pure returns (bytes32);
}
contract TechnicalPreview {
    
    IVerifier public verifier;
    IElasticERC20 public token;
    bytes32[] public validRoots;
    bytes32 public keyHash;
    uint256 public MAX_FIELD_SIZE = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000;
    bytes32 public ZERO_VALUE = 0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720;
    mapping(bytes32 => bytes32) public utxoPrevRoots; 
    mapping(bytes32 => bool) public nullifierHashes; 
    mapping(bytes32 => bool) public commitments; 
    mapping(bytes32 => bytes32[]) public utxo;
    uint256 public MAX_ITEMS = 16; 
    mapping(uint256 => Transaction[]) public batch;
    uint256 public batchNumber = 0;    
    bytes32 public merkleRoot = ZERO_VALUE;
    constructor(IVerifier _verifier, address _token, bytes32 _keyHash) {
        verifier = _verifier;
        token = IElasticERC20(_token);
        validRoots.push(merkleRoot);
        keyHash = _keyHash;
    }
    function getLatestAggregationObject() public view returns (bytes32[16] memory) {
        return batch[batchNumber][batch[batchNumber].length - 1].aggregation_object;
    }
    function getSpentNullifiers(bytes32[] calldata _nullifierHashes) external view returns (bool[] memory spent) {
        uint256 nullifierHashesLength = _nullifierHashes.length;
        spent = new bool[](nullifierHashesLength);
        for (uint256 i; i < nullifierHashesLength; i++) {
            if (isSpent(_nullifierHashes[i])) {
                spent[i] = true;
            }
        }
    }
    
    function getCommitment(bytes32 _commitment) public view returns (bool) {
        return commitments[_commitment];
    }
    
    function getUtxoFromRoot(bytes32 _root) public view returns (bytes32[] memory) {
        return utxo[_root];
    }
    function getRootFromUtxo(bytes32 _utxo) public view returns (bytes32) {
        return utxoPrevRoots[_utxo];
    }
    function getValidRoots() public view returns (bytes32[] memory) {
        return validRoots;
    }
    function getCurrentBatch() public view returns (Transaction[] memory) {
        return batch[batchNumber];
    }
    
    
    
    function enqueue(Transaction calldata _tx) public {
        
        require(MAX_ITEMS > batch[batchNumber].length, "queue is full");
        batch[batchNumber].push(_tx);
        
        for (uint256 i = 0; i < 16; i++) {
            if (_tx.commitments[i] != ZERO_VALUE) {
                require(!commitments[_tx.commitments[i]], "commitment exists");
                commitments[_tx.commitments[i]] = true;
            }
            
        } 
    }
    function publish(
        bytes calldata _proof,
        BatchPublicInputs calldata _batch
    ) public payable {
        require(uint256(_batch.old_root) == uint256(merkleRoot), "invalid root");
        BatchPublicInputs memory batchPublicInputs = _batch; 
        
        batchPublicInputs.transactions = batch[batchNumber];
        for (uint256 i = 0; i < batchPublicInputs.transactions.length; i++) {
            for (uint256 j = 0; j < 16; j++) {
                if (batchPublicInputs.transactions[i].nullifier_hashes[j] != ZERO_VALUE) {
                    require(!nullifierHashes[batchPublicInputs.transactions[i].nullifier_hashes[j]], "nullifier spent");
                    nullifierHashes[batchPublicInputs.transactions[i].nullifier_hashes[j]] = true;
                }
                
                if (batchPublicInputs.transactions[i].commitments[j] != ZERO_VALUE) {
                    utxo[batchPublicInputs.old_root].push(batchPublicInputs.transactions[i].commitments[j]);
                    utxoPrevRoots[batchPublicInputs.transactions[i].commitments[j]] = batchPublicInputs.old_root; 
                }
                
                if (batchPublicInputs.transactions[i].recipients[j] != ZERO_VALUE) {
                    token.mint(address(uint160(uint256(batchPublicInputs.transactions[i].recipients[j]))), uint256(batchPublicInputs.transactions[i].withdrawals[j]));
                }
            }
        }
        validRoots.push(batchPublicInputs.new_root); 
        merkleRoot = batchPublicInputs.new_root; 
        batchNumber++; 
        
        require(verifier.verify(_proof, prepareBatchPublicInputs(_batch)), "invalid proof");
    }
    function dropQueue(bytes calldata _preimage) public {
        require(keccak256(_preimage) > 0xff00000000000000000000000000000000000000000000000000000000000000); 
        
        for (uint256 i = 0; i < MAX_ITEMS; i++) {
            for (uint256 j = 0; j < 16; j++) {
                commitments[batch[batchNumber][i].commitments[j]] = false;
            }
        }
        batchNumber++;
    }
    
    function isSpent(bytes32 _nullifierHash) public view returns (bool) {
        return nullifierHashes[_nullifierHash];
    }
    function verifyProof(
        bytes calldata _proof,
        bytes32[] memory _publicInputs
    ) public view returns (bool) {
        return verifier.verify(_proof, _publicInputs);
    }
    
    function prepareBatchPublicInputs(BatchPublicInputs memory input) public view returns (bytes32[] memory) {
        bytes32[] memory flatArray = new bytes32[](36);
        uint256 idx = 0;
        flatArray[idx++] = keyHash;
        for (uint256 i = 0; i < 16; i++) {
            
            if (i < input.transactions.length) {
                flatArray[idx++] = input.transactions[i].id;
            } else {
                flatArray[idx++] = ZERO_VALUE;
            }
            
        }
        flatArray[idx++] = input.old_root;
        flatArray[idx++] = input.new_root;
        flatArray[idx++] = input.oracle;
        for (uint256 i = 0; i < 16; i++ ) {
            
            flatArray[idx++] = input.transactions[(input.transactions.length - 1)].aggregation_object[i];
        }
        
        for (uint256 i = 0; i < flatArray.length; i++) {
            require(uint256(flatArray[i]) < 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000, "too large!");
        }
        return flatArray;
    }
}