ETH Price: $2,001.30 (+1.17%)

Transaction Decoder

Block:
10034562 at May-09-2020 09:55:16 PM +UTC
Transaction Fee:
0.002977299 ETH $5.96
Gas Used:
330,811 Gas / 9 Gwei

Emitted Events:

38 TornadoCash_eth.Withdrawal( to=0xf5e852fcd4f1f928126c3aa0af7c795f4d1f773d, nullifierHash=177F71FAA92A4E69D9746E3C1700FDDC50A732AF2CCA8A41DFD63F690792B028, relayer=[Sender] 0x7d3bb46c78b0c4949639ce34896bfd875b97ad08, fee=54100000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Hiveon: Old Pool)
656.110977443301150692 Eth656.113954742301150692 Eth0.002977299
0x7d3BB46C...75B97aD08
37.829486699994 Eth
Nonce: 695
37.880609400994 Eth
Nonce: 696
0.051122701
0xA160cdAB...c3b53f291
(Tornado.Cash: 100 ETH)
4,700 Eth4,600 Eth100
0xF5e852fc...f4d1F773D 299.73675 Eth399.68265 Eth99.9459

Execution Trace

TornadoCash_eth.withdraw( _proof=0x03A1F283127EA59DD19B1B635611FD60540C053FF029D46D39094364D5DB470C212D5633F73C9B05E5787009240318ABA60C32FDBCD34FFA2477C26412F2BA610716C361B3650A683D2D97242EF41A385E8046DA5BC354CD9B034A210D34C0C92FE639DF5E18182E4ABF97E4DDD29450A4FD011F7C22D0CEEE396EDF61F23B0015DB6C095660CFE83194B4A0DDA9F34F3C1DD1495EDC441EF21307C2A2A4D64C1C4CC8B8803BFB89C3469ED27BC065987F3EF17A91992FED88D633ED628C7F401A947C6A3219E43E1B4BE5BDEE61E7405C066F1D1481E364F9D75A785485838D1BB513BE093B7D5B2CC28A746F8D12476283BDBEB6FA58D26EF3884DC9EE9AEB, _root=2DBD3D78840A293030E39803ED39C79534E744ADF353A86F143B306C52B930A3, _nullifierHash=177F71FAA92A4E69D9746E3C1700FDDC50A732AF2CCA8A41DFD63F690792B028, _recipient=0xF5e852fcD4F1F928126C3aa0AF7C795f4d1F773D, _relayer=0x7d3BB46C78b0c4949639ce34896bfD875B97aD08, _fee=54100000000000000, _refund=0 )
  • Verifier.verifyProof( proof=0x03A1F283127EA59DD19B1B635611FD60540C053FF029D46D39094364D5DB470C212D5633F73C9B05E5787009240318ABA60C32FDBCD34FFA2477C26412F2BA610716C361B3650A683D2D97242EF41A385E8046DA5BC354CD9B034A210D34C0C92FE639DF5E18182E4ABF97E4DDD29450A4FD011F7C22D0CEEE396EDF61F23B0015DB6C095660CFE83194B4A0DDA9F34F3C1DD1495EDC441EF21307C2A2A4D64C1C4CC8B8803BFB89C3469ED27BC065987F3EF17A91992FED88D633ED628C7F401A947C6A3219E43E1B4BE5BDEE61E7405C066F1D1481E364F9D75A785485838D1BB513BE093B7D5B2CC28A746F8D12476283BDBEB6FA58D26EF3884DC9EE9AEB, inputs=[20688436537116258846160944624302356719259949481918958610251533132564084109475, 10628371749781016959244611247633788452874605064432726946354251090294161453096, 1403883740979555388566802792007194711735303894845, 714955307442081782466079074720353396238707043592, 54100000000000000, 0] ) => ( r=True )
    • Null: 0x000...007.15ff890e( )
    • Null: 0x000...006.00000000( )
    • Null: 0x000...007.1e222744( )
    • Null: 0x000...006.25a67a62( )
    • Null: 0x000...007.085e72ea( )
    • Null: 0x000...006.15b970b6( )
    • Null: 0x000...007.1212d0f0( )
    • Null: 0x000...006.292d868a( )
    • Null: 0x000...007.155d5600( )
    • Null: 0x000...006.15852e82( )
    • Null: 0x000...007.161a52e2( )
    • Null: 0x000...006.0277d5b6( )
    • Null: 0x000...006.0277d5b6( )
    • Null: 0x000...008.03a1f283( )
    • ETH 99.9459 0xf5e852fcd4f1f928126c3aa0af7c795f4d1f773d.CALL( )
    • ETH 0.0541 0x7d3bb46c78b0c4949639ce34896bfd875b97ad08.CALL( )
      File 1 of 2: TornadoCash_eth
      // File: contracts/MerkleTreeWithHistory.sol
      
      // https://tornado.cash
      /*
      * d888888P                                           dP              a88888b.                   dP
      *    88                                              88             d8'   `88                   88
      *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
      *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
      *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
      *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
      * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
      */
      
      pragma solidity ^0.5.8;
      
      library Hasher {
        function MiMCSponge(uint256 in_xL, uint256 in_xR) public pure returns (uint256 xL, uint256 xR);
      }
      
      contract MerkleTreeWithHistory {
        uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
        uint256 public constant ZERO_VALUE = 21663839004416932945382355908790599225266501822907911457504978515578255421292; // = keccak256("tornado") % FIELD_SIZE
      
        uint32 public levels;
      
        // the following variables are made public for easier testing and debugging and
        // are not supposed to be accessed in regular code
        bytes32[] public filledSubtrees;
        bytes32[] public zeros;
        uint32 public currentRootIndex = 0;
        uint32 public nextIndex = 0;
        uint32 public constant ROOT_HISTORY_SIZE = 100;
        bytes32[ROOT_HISTORY_SIZE] public roots;
      
        constructor(uint32 _treeLevels) public {
          require(_treeLevels > 0, "_treeLevels should be greater than zero");
          require(_treeLevels < 32, "_treeLevels should be less than 32");
          levels = _treeLevels;
      
          bytes32 currentZero = bytes32(ZERO_VALUE);
          zeros.push(currentZero);
          filledSubtrees.push(currentZero);
      
          for (uint32 i = 1; i < levels; i++) {
            currentZero = hashLeftRight(currentZero, currentZero);
            zeros.push(currentZero);
            filledSubtrees.push(currentZero);
          }
      
          roots[0] = hashLeftRight(currentZero, currentZero);
        }
      
        /**
          @dev Hash 2 tree leaves, returns MiMC(_left, _right)
        */
        function hashLeftRight(bytes32 _left, bytes32 _right) public pure returns (bytes32) {
          require(uint256(_left) < FIELD_SIZE, "_left should be inside the field");
          require(uint256(_right) < FIELD_SIZE, "_right should be inside the field");
          uint256 R = uint256(_left);
          uint256 C = 0;
          (R, C) = Hasher.MiMCSponge(R, C);
          R = addmod(R, uint256(_right), FIELD_SIZE);
          (R, C) = Hasher.MiMCSponge(R, C);
          return bytes32(R);
        }
      
        function _insert(bytes32 _leaf) internal returns(uint32 index) {
          uint32 currentIndex = nextIndex;
          require(currentIndex != uint32(2)**levels, "Merkle tree is full. No more leafs can be added");
          nextIndex += 1;
          bytes32 currentLevelHash = _leaf;
          bytes32 left;
          bytes32 right;
      
          for (uint32 i = 0; i < levels; i++) {
            if (currentIndex % 2 == 0) {
              left = currentLevelHash;
              right = zeros[i];
      
              filledSubtrees[i] = currentLevelHash;
            } else {
              left = filledSubtrees[i];
              right = currentLevelHash;
            }
      
            currentLevelHash = hashLeftRight(left, right);
      
            currentIndex /= 2;
          }
      
          currentRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;
          roots[currentRootIndex] = currentLevelHash;
          return nextIndex - 1;
        }
      
        /**
          @dev Whether the root is present in the root history
        */
        function isKnownRoot(bytes32 _root) public view returns(bool) {
          if (_root == 0) {
            return false;
          }
          uint32 i = currentRootIndex;
          do {
            if (_root == roots[i]) {
              return true;
            }
            if (i == 0) {
              i = ROOT_HISTORY_SIZE;
            }
            i--;
          } while (i != currentRootIndex);
          return false;
        }
      
        /**
          @dev Returns the last root
        */
        function getLastRoot() public view returns(bytes32) {
          return roots[currentRootIndex];
        }
      }
      
      // File: @openzeppelin/contracts/utils/ReentrancyGuard.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       */
      contract ReentrancyGuard {
          // counter to allow mutex lock with only one SSTORE operation
          uint256 private _guardCounter;
      
          constructor () internal {
              // The counter starts at one to prevent changing it from zero to a non-zero
              // value, which is a more expensive operation.
              _guardCounter = 1;
          }
      
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and make it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              _guardCounter += 1;
              uint256 localCounter = _guardCounter;
              _;
              require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
          }
      }
      
      // File: contracts/Tornado.sol
      
      // https://tornado.cash
      /*
      * d888888P                                           dP              a88888b.                   dP
      *    88                                              88             d8'   `88                   88
      *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
      *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
      *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
      *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
      * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
      */
      
      pragma solidity ^0.5.8;
      
      
      
      contract IVerifier {
        function verifyProof(bytes memory _proof, uint256[6] memory _input) public returns(bool);
      }
      
      contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
        uint256 public denomination;
        mapping(bytes32 => bool) public nullifierHashes;
        // we store all commitments just to prevent accidental deposits with the same commitment
        mapping(bytes32 => bool) public commitments;
        IVerifier public verifier;
      
        // operator can update snark verification key
        // after the final trusted setup ceremony operator rights are supposed to be transferred to zero address
        address public operator;
        modifier onlyOperator {
          require(msg.sender == operator, "Only operator can call this function.");
          _;
        }
      
        event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp);
        event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee);
      
        /**
          @dev The constructor
          @param _verifier the address of SNARK verifier for this contract
          @param _denomination transfer amount for each deposit
          @param _merkleTreeHeight the height of deposits' Merkle Tree
          @param _operator operator address (see operator comment above)
        */
        constructor(
          IVerifier _verifier,
          uint256 _denomination,
          uint32 _merkleTreeHeight,
          address _operator
        ) MerkleTreeWithHistory(_merkleTreeHeight) public {
          require(_denomination > 0, "denomination should be greater than 0");
          verifier = _verifier;
          operator = _operator;
          denomination = _denomination;
        }
      
        /**
          @dev Deposit funds into the contract. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this instance.
          @param _commitment the note commitment, which is PedersenHash(nullifier + secret)
        */
        function deposit(bytes32 _commitment) external payable nonReentrant {
          require(!commitments[_commitment], "The commitment has been submitted");
      
          uint32 insertedIndex = _insert(_commitment);
          commitments[_commitment] = true;
          _processDeposit();
      
          emit Deposit(_commitment, insertedIndex, block.timestamp);
        }
      
        /** @dev this function is defined in a child contract */
        function _processDeposit() internal;
      
        /**
          @dev Withdraw a deposit from the contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
          `input` array consists of:
            - merkle root of all deposits in the contract
            - hash of unique deposit nullifier to prevent double spends
            - the recipient of funds
            - optional fee that goes to the transaction sender (usually a relay)
        */
        function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable nonReentrant {
          require(_fee <= denomination, "Fee exceeds transfer value");
          require(!nullifierHashes[_nullifierHash], "The note has been already spent");
          require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
          require(verifier.verifyProof(_proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]), "Invalid withdraw proof");
      
          nullifierHashes[_nullifierHash] = true;
          _processWithdraw(_recipient, _relayer, _fee, _refund);
          emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee);
        }
      
        /** @dev this function is defined in a child contract */
        function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal;
      
        /** @dev whether a note is already spent */
        function isSpent(bytes32 _nullifierHash) public view returns(bool) {
          return nullifierHashes[_nullifierHash];
        }
      
        /** @dev whether an array of notes is already spent */
        function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns(bool[] memory spent) {
          spent = new bool[](_nullifierHashes.length);
          for(uint i = 0; i < _nullifierHashes.length; i++) {
            if (isSpent(_nullifierHashes[i])) {
              spent[i] = true;
            }
          }
        }
      
        /**
          @dev allow operator to update SNARK verification keys. This is needed to update keys after the final trusted setup ceremony is held.
          After that operator rights are supposed to be transferred to zero address
        */
        function updateVerifier(address _newVerifier) external onlyOperator {
          verifier = IVerifier(_newVerifier);
        }
      
        /** @dev operator can change his address */
        function changeOperator(address _newOperator) external onlyOperator {
          operator = _newOperator;
        }
      }
      
      // File: contracts/ETHTornado.sol
      
      // https://tornado.cash
      /*
      * d888888P                                           dP              a88888b.                   dP
      *    88                                              88             d8'   `88                   88
      *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
      *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
      *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
      *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
      * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
      */
      
      pragma solidity ^0.5.8;
      
      
      contract TornadoCash_eth is Tornado {
        constructor(
          IVerifier _verifier,
          uint256 _denomination,
          uint32 _merkleTreeHeight,
          address _operator
        ) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator) public {
        }
      
        function _processDeposit() internal {
          require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction");
        }
      
        function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal {
          // sanity checks
          require(msg.value == 0, "Message value is supposed to be zero for ETH instance");
          require(_refund == 0, "Refund value is supposed to be zero for ETH instance");
      
          (bool success, ) = _recipient.call.value(denomination - _fee)("");
          require(success, "payment to _recipient did not go thru");
          if (_fee > 0) {
            (success, ) = _relayer.call.value(_fee)("");
            require(success, "payment to _relayer did not go thru");
          }
        }
      }

      File 2 of 2: Verifier
      // https://tornado.cash
      /*
      * d888888P                                           dP              a88888b.                   dP
      *    88                                              88             d8'   `88                   88
      *    88    .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b.    88        .d8888b. .d8888b. 88d888b.
      *    88    88'  `88 88'  `88 88'  `88 88'  `88 88'  `88 88'  `88    88        88'  `88 Y8ooooo. 88'  `88
      *    88    88.  .88 88       88    88 88.  .88 88.  .88 88.  .88 dP Y8.   .88 88.  .88       88 88    88
      *    dP    `88888P' dP       dP    dP `88888P8 `88888P8 `88888P' 88  Y88888P' `88888P8 `88888P' dP    dP
      * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
      */
      //
      // Copyright 2017 Christian Reitwiessner
      // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
      // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      //
      // 2019 OKIMS
      //      ported to solidity 0.5
      //      fixed linter warnings
      //      added requiere error messages
      //
      pragma solidity ^0.5.0;
      library Pairing {
          struct G1Point {
              uint X;
              uint Y;
          }
          // Encoding of field elements is: X[0] * z + X[1]
          struct G2Point {
              uint[2] X;
              uint[2] Y;
          }
          /// @return the generator of G1
          function P1() internal pure returns (G1Point memory) {
              return G1Point(1, 2);
          }
          /// @return the generator of G2
          function P2() internal pure returns (G2Point memory) {
              // Original code point
              return G2Point(
                  [11559732032986387107991004021392285783925812861821192530917403151452391805634,
                   10857046999023057135944570762232829481370756359578518086990519993285655852781],
                  [4082367875863433681332203403145435568316851327593401208105741076214120093531,
                   8495653923123431417604973247489272438418190587263600148770280649306958101930]
              );
      
      /*
              // Changed by Jordi point
              return G2Point(
                  [10857046999023057135944570762232829481370756359578518086990519993285655852781,
                   11559732032986387107991004021392285783925812861821192530917403151452391805634],
                  [8495653923123431417604973247489272438418190587263600148770280649306958101930,
                   4082367875863433681332203403145435568316851327593401208105741076214120093531]
              );
      */
          }
          /// @return the negation of p, i.e. p.addition(p.negate()) should be zero.
          function negate(G1Point memory p) internal pure returns (G1Point memory) {
              // The prime q in the base field F_q for G1
              uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
              if (p.X == 0 && p.Y == 0)
                  return G1Point(0, 0);
              return G1Point(p.X, q - (p.Y % q));
          }
          /// @return the sum of two points of G1
          function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
              uint[4] memory input;
              input[0] = p1.X;
              input[1] = p1.Y;
              input[2] = p2.X;
              input[3] = p2.Y;
              bool success;
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  success := staticcall(sub(gas, 2000), 6, input, 0xc0, r, 0x60)
                  // Use "invalid" to make gas estimation work
                  switch success case 0 { invalid() }
              }
              require(success,"pairing-add-failed");
          }
          /// @return the product of a point on G1 and a scalar, i.e.
          /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
          function scalar_mul(G1Point memory p, uint s) internal view returns (G1Point memory r) {
              uint[3] memory input;
              input[0] = p.X;
              input[1] = p.Y;
              input[2] = s;
              bool success;
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  success := staticcall(sub(gas, 2000), 7, input, 0x80, r, 0x60)
                  // Use "invalid" to make gas estimation work
                  switch success case 0 { invalid() }
              }
              require (success,"pairing-mul-failed");
          }
          /// @return the result of computing the pairing check
          /// e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
          /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
          /// return true.
          function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
              require(p1.length == p2.length,"pairing-lengths-failed");
              uint elements = p1.length;
              uint inputSize = elements * 6;
              uint[] memory input = new uint[](inputSize);
              for (uint i = 0; i < elements; i++)
              {
                  input[i * 6 + 0] = p1[i].X;
                  input[i * 6 + 1] = p1[i].Y;
                  input[i * 6 + 2] = p2[i].X[0];
                  input[i * 6 + 3] = p2[i].X[1];
                  input[i * 6 + 4] = p2[i].Y[0];
                  input[i * 6 + 5] = p2[i].Y[1];
              }
              uint[1] memory out;
              bool success;
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  success := staticcall(sub(gas, 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
                  // Use "invalid" to make gas estimation work
                  switch success case 0 { invalid() }
              }
              require(success,"pairing-opcode-failed");
              return out[0] != 0;
          }
          /// Convenience method for a pairing check for two pairs.
          function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal view returns (bool) {
              G1Point[] memory p1 = new G1Point[](2);
              G2Point[] memory p2 = new G2Point[](2);
              p1[0] = a1;
              p1[1] = b1;
              p2[0] = a2;
              p2[1] = b2;
              return pairing(p1, p2);
          }
          /// Convenience method for a pairing check for three pairs.
          function pairingProd3(
                  G1Point memory a1, G2Point memory a2,
                  G1Point memory b1, G2Point memory b2,
                  G1Point memory c1, G2Point memory c2
          ) internal view returns (bool) {
              G1Point[] memory p1 = new G1Point[](3);
              G2Point[] memory p2 = new G2Point[](3);
              p1[0] = a1;
              p1[1] = b1;
              p1[2] = c1;
              p2[0] = a2;
              p2[1] = b2;
              p2[2] = c2;
              return pairing(p1, p2);
          }
          /// Convenience method for a pairing check for four pairs.
          function pairingProd4(
                  G1Point memory a1, G2Point memory a2,
                  G1Point memory b1, G2Point memory b2,
                  G1Point memory c1, G2Point memory c2,
                  G1Point memory d1, G2Point memory d2
          ) internal view returns (bool) {
              G1Point[] memory p1 = new G1Point[](4);
              G2Point[] memory p2 = new G2Point[](4);
              p1[0] = a1;
              p1[1] = b1;
              p1[2] = c1;
              p1[3] = d1;
              p2[0] = a2;
              p2[1] = b2;
              p2[2] = c2;
              p2[3] = d2;
              return pairing(p1, p2);
          }
      }
      contract Verifier {
          using Pairing for *;
          struct VerifyingKey {
              Pairing.G1Point alfa1;
              Pairing.G2Point beta2;
              Pairing.G2Point gamma2;
              Pairing.G2Point delta2;
              Pairing.G1Point[] IC;
          }
          struct Proof {
              Pairing.G1Point A;
              Pairing.G2Point B;
              Pairing.G1Point C;
          }
          function verifyingKey() internal pure returns (VerifyingKey memory vk) {
              vk.alfa1 = Pairing.G1Point(14083060544450230494753274689662527554173093259052677053963403611543108812248,4706358536309759497515629700429717613728395864036029914779603000183617648928);
              vk.beta2 = Pairing.G2Point([16219376067261068540504773162427353135531573767414264044630641576986412148841,11226478626154379929692040045672180496625001440073645894895473453499733630608], [8662043286651336594833933468521973267084981586202736778519782026658563113819,5942204661444759280424140647067377540347290706910798648472025938632189112985]);
              vk.gamma2 = Pairing.G2Point([5626774539960215411062721749577193079747643943552704413404404262438418256316,9019372327661379039595737818991664049719738650451402651660959581913338994854], [21092692879600416126570996361460322936290640865421745471798870901476359011654,9562621902064186952244630067028615031672773927765582067796589601830914136863]);
              vk.delta2 = Pairing.G2Point([15895226675259918394214944850094992600763639881245050921639726020135193652688,8066794343152224768444030087741836548721200404739800154831732767644473461476], [2091512572979570673813987427579756573526730039480975604103592522881026646949,15957537187230612698014796603169934934345666788241949100809795704331873734963]);
              vk.IC = new Pairing.G1Point[](7);
              vk.IC[0] = Pairing.G1Point(14112255618296752664987088318060807163751325065279686739401083407169811107624,12125289624680902628733420759546824791467428117812768926606015515391099666551);
              vk.IC[1] = Pairing.G1Point(9950061763761971651364890451617100497514634553916860166198204152844612131077,21535612280842336626995215571028050552370291719642409660905444643494169974931);
              vk.IC[2] = Pairing.G1Point(13629729267373883091784437554800598646370914526466282534161591457204234303940,20428953590301985331608379836857790556503633933796007036040402171229917977521);
              vk.IC[3] = Pairing.G1Point(3785379534194287071601933141657029654649536871130067525982297509612327206924,20441720781313152301739076910621905177933653643381029091599841534154495485733);
              vk.IC[4] = Pairing.G1Point(8174876566887573867022571764285261688265531058998171958730684499818568021399,15364158076213537047194249827614090601580933367596700053241089406052131015891);
              vk.IC[5] = Pairing.G1Point(9663480153820773462766642876957017446277546738016922976831320957266750385069,11455348672789641736424763421225888127466400796123190592860299461638477062914);
              vk.IC[6] = Pairing.G1Point(9997392752408677018847679602311243591939016081244612283626393457143697191318,1284349834049952760295840793422778426148802044355256369588105110406727218438);
      
          }
          function verify(uint[] memory input, Proof memory proof) internal view returns (uint) {
              uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
              VerifyingKey memory vk = verifyingKey();
              require(input.length + 1 == vk.IC.length,"verifier-bad-input");
              // Compute the linear combination vk_x
              Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
              for (uint i = 0; i < input.length; i++) {
                  require(input[i] < snark_scalar_field,"verifier-gte-snark-scalar-field");
                  vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
              }
              vk_x = Pairing.addition(vk_x, vk.IC[0]);
              if (!Pairing.pairingProd4(
                  Pairing.negate(proof.A), proof.B,
                  vk.alfa1, vk.beta2,
                  vk_x, vk.gamma2,
                  proof.C, vk.delta2
              )) return 1;
              return 0;
          }
          function verifyProof(
                  uint[2] memory a,
                  uint[2][2] memory b,
                  uint[2] memory c,
                  uint[6] memory input
              ) public view returns (bool r) {
              Proof memory proof;
              proof.A = Pairing.G1Point(a[0], a[1]);
              proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
              proof.C = Pairing.G1Point(c[0], c[1]);
              uint[] memory inputValues = new uint[](input.length);
              for(uint i = 0; i < input.length; i++){
                  inputValues[i] = input[i];
              }
              if (verify(inputValues, proof) == 0) {
                  return true;
              } else {
                  return false;
              }
          }
          function verifyProof(bytes calldata proof, uint[6] calldata inputs) external view returns (bool r) {
              // solidity does not support decoding uint[2][2] yet
              (uint[2] memory a, uint[2] memory b1, uint[2] memory b2, uint[2] memory c) = abi.decode(proof, (uint[2], uint[2], uint[2], uint[2]));
              return verifyProof(a, [b1, b2], c, inputs);
          }
      }